1/*- 2 * Copyright (c) 2010-2019 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* 31 * NPF interface for the Application Level Gateways (ALGs). 32 */ 33 34#ifdef _KERNEL 35#include <sys/cdefs.h> 36__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.22 2020/05/30 14:16:56 rmind Exp $"); 37 38#include <sys/param.h> 39#include <sys/types.h> 40 41#include <sys/kmem.h> 42#include <sys/module.h> 43#endif 44 45#include "npf_impl.h" 46 47/* 48 * NAT ALG description structure. For more compact use of cache, 49 * the functions are separated in their own arrays. The number of 50 * ALGs is expected to be very small. 51 */ 52 53struct npf_alg { 54 const char * na_name; 55 unsigned na_slot; 56}; 57 58struct npf_algset { 59 /* List of ALGs and the count. */ 60 npf_alg_t alg_list[NPF_MAX_ALGS]; 61 unsigned alg_count; 62 63 /* Matching, inspection and translation functions. */ 64 npfa_funcs_t alg_funcs[NPF_MAX_ALGS]; 65}; 66 67#define NPF_ALG_PREF "npf_alg_" 68#define NPF_ALG_PREFLEN (sizeof(NPF_ALG_PREF) - 1) 69 70void 71npf_alg_init(npf_t *npf) 72{ 73 npf_algset_t *aset; 74 75 aset = kmem_zalloc(sizeof(npf_algset_t), KM_SLEEP); 76 npf->algset = aset; 77} 78 79void 80npf_alg_fini(npf_t *npf) 81{ 82 npf_algset_t *aset = npf->algset; 83 84 kmem_free(aset, sizeof(npf_algset_t)); 85} 86 87static npf_alg_t * 88npf_alg_lookup(npf_t *npf, const char *name) 89{ 90 npf_algset_t *aset = npf->algset; 91 92 KASSERT(npf_config_locked_p(npf)); 93 94 for (unsigned i = 0; i < aset->alg_count; i++) { 95 npf_alg_t *alg = &aset->alg_list[i]; 96 const char *aname = alg->na_name; 97 98 if (aname && strcmp(aname, name) == 0) 99 return alg; 100 } 101 return NULL; 102} 103 104npf_alg_t * 105npf_alg_construct(npf_t *npf, const char *name) 106{ 107 npf_alg_t *alg; 108 109 npf_config_enter(npf); 110 if ((alg = npf_alg_lookup(npf, name)) == NULL) { 111 char modname[NPF_ALG_PREFLEN + 64]; 112 113 snprintf(modname, sizeof(modname), "%s%s", NPF_ALG_PREF, name); 114 npf_config_exit(npf); 115 116 if (module_autoload(modname, MODULE_CLASS_MISC) != 0) { 117 return NULL; 118 } 119 npf_config_enter(npf); 120 alg = npf_alg_lookup(npf, name); 121 } 122 npf_config_exit(npf); 123 return alg; 124} 125 126/* 127 * npf_alg_register: register application-level gateway. 128 */ 129npf_alg_t * 130npf_alg_register(npf_t *npf, const char *name, const npfa_funcs_t *funcs) 131{ 132 npf_algset_t *aset = npf->algset; 133 npfa_funcs_t *afuncs; 134 npf_alg_t *alg; 135 unsigned i; 136 137 npf_config_enter(npf); 138 if (npf_alg_lookup(npf, name) != NULL) { 139 npf_config_exit(npf); 140 return NULL; 141 } 142 143 /* Find a spare slot. */ 144 for (i = 0; i < NPF_MAX_ALGS; i++) { 145 alg = &aset->alg_list[i]; 146 if (alg->na_name == NULL) { 147 break; 148 } 149 } 150 if (i == NPF_MAX_ALGS) { 151 npf_config_exit(npf); 152 return NULL; 153 } 154 155 /* Register the ALG. */ 156 alg->na_name = name; 157 alg->na_slot = i; 158 159 /* 160 * Assign the functions. Make sure the 'destroy' gets visible first. 161 */ 162 afuncs = &aset->alg_funcs[i]; 163 atomic_store_relaxed(&afuncs->destroy, funcs->destroy); 164 membar_producer(); 165 atomic_store_relaxed(&afuncs->translate, funcs->translate); 166 atomic_store_relaxed(&afuncs->inspect, funcs->inspect); 167 atomic_store_relaxed(&afuncs->match, funcs->match); 168 membar_producer(); 169 170 atomic_store_relaxed(&aset->alg_count, MAX(aset->alg_count, i + 1)); 171 npf_config_exit(npf); 172 173 return alg; 174} 175 176/* 177 * npf_alg_unregister: unregister application-level gateway. 178 */ 179int 180npf_alg_unregister(npf_t *npf, npf_alg_t *alg) 181{ 182 npf_algset_t *aset = npf->algset; 183 unsigned i = alg->na_slot; 184 npfa_funcs_t *afuncs; 185 186 /* Deactivate the functions first. */ 187 npf_config_enter(npf); 188 afuncs = &aset->alg_funcs[i]; 189 atomic_store_relaxed(&afuncs->match, NULL); 190 atomic_store_relaxed(&afuncs->translate, NULL); 191 atomic_store_relaxed(&afuncs->inspect, NULL); 192 npf_config_sync(npf); 193 194 /* 195 * Finally, unregister the ALG. We leave the 'destroy' callback 196 * as the following will invoke it for the relevant connections. 197 */ 198 npf_ruleset_freealg(npf_config_natset(npf), alg); 199 atomic_store_relaxed(&afuncs->destroy, NULL); 200 alg->na_name = NULL; 201 npf_config_exit(npf); 202 203 return 0; 204} 205 206/* 207 * npf_alg_match: call the ALG matching inspectors. 208 * 209 * The purpose of the "matching" inspector function in the ALG API 210 * is to determine whether this connection matches the ALG criteria 211 * i.e. is concerning the ALG. If yes, ALG can associate itself with 212 * the given NAT state structure and set/save an arbitrary parameter. 213 * This is done using the using the npf_nat_setalg() function. 214 * 215 * => This is called when the packet matches the dynamic NAT policy 216 * and the NAT state entry is being created for it [NAT-ESTABLISH]. 217 */ 218bool 219npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di) 220{ 221 npf_t *npf = npc->npc_ctx; 222 npf_algset_t *aset = npf->algset; 223 bool match = false; 224 unsigned count; 225 int s; 226 227 KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number"); 228 229 s = npf_config_read_enter(npf); 230 count = atomic_load_relaxed(&aset->alg_count); 231 for (unsigned i = 0; i < count; i++) { 232 const npfa_funcs_t *f = &aset->alg_funcs[i]; 233 bool (*match_func)(npf_cache_t *, npf_nat_t *, int); 234 235 match_func = atomic_load_relaxed(&f->match); 236 if (match_func && match_func(npc, nt, di)) { 237 match = true; 238 break; 239 } 240 } 241 npf_config_read_exit(npf, s); 242 return match; 243} 244 245/* 246 * npf_alg_exec: execute the ALG translation processors. 247 * 248 * The ALG function would perform any additional packet translation 249 * or manipulation here. 250 * 251 * => This is called when the packet is being translated according 252 * to the dynamic NAT logic [NAT-TRANSLATE]. 253 */ 254void 255npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, const npf_flow_t flow) 256{ 257 npf_t *npf = npc->npc_ctx; 258 npf_algset_t *aset = npf->algset; 259 unsigned count; 260 int s; 261 262 s = npf_config_read_enter(npf); 263 count = atomic_load_relaxed(&aset->alg_count); 264 for (unsigned i = 0; i < count; i++) { 265 const npfa_funcs_t *f = &aset->alg_funcs[i]; 266 bool (*translate_func)(npf_cache_t *, npf_nat_t *, npf_flow_t); 267 268 translate_func = atomic_load_relaxed(&f->translate); 269 if (translate_func) { 270 translate_func(npc, nt, flow); 271 } 272 } 273 npf_config_read_exit(npf, s); 274} 275 276/* 277 * npf_alg_conn: query ALGs which may perform a custom state lookup. 278 * 279 * The purpose of ALG connection inspection function is to provide 280 * ALGs with a mechanism to override the regular connection state 281 * lookup, if they need to. For example, some ALGs may want to 282 * extract and use a different n-tuple to perform a lookup. 283 * 284 * => This is called at the beginning of the connection state lookup 285 * function [CONN-LOOKUP]. 286 * 287 * => Must use the npf_conn_lookup() function to perform the custom 288 * connection state lookup and return the result. 289 * 290 * => Returning NULL will result in NPF performing a regular state 291 * lookup for the packet. 292 */ 293npf_conn_t * 294npf_alg_conn(npf_cache_t *npc, int di) 295{ 296 npf_t *npf = npc->npc_ctx; 297 npf_algset_t *aset = npf->algset; 298 npf_conn_t *con = NULL; 299 unsigned count; 300 int s; 301 302 s = npf_config_read_enter(npf); 303 count = atomic_load_relaxed(&aset->alg_count); 304 for (unsigned i = 0; i < count; i++) { 305 const npfa_funcs_t *f = &aset->alg_funcs[i]; 306 npf_conn_t *(*inspect_func)(npf_cache_t *, int); 307 308 inspect_func = atomic_load_relaxed(&f->inspect); 309 if (inspect_func && (con = inspect_func(npc, di)) != NULL) { 310 break; 311 } 312 } 313 npf_config_read_exit(npf, s); 314 return con; 315} 316 317/* 318 * npf_alg_destroy: free the ALG structure associated with the NAT entry. 319 */ 320void 321npf_alg_destroy(npf_t *npf, npf_alg_t *alg, npf_nat_t *nat, npf_conn_t *con) 322{ 323 npf_algset_t *aset = npf->algset; 324 const npfa_funcs_t *f = &aset->alg_funcs[alg->na_slot]; 325 void (*destroy_func)(npf_t *, npf_nat_t *, npf_conn_t *); 326 327 if ((destroy_func = atomic_load_relaxed(&f->destroy)) != NULL) { 328 destroy_func(npf, nat, con); 329 } 330} 331 332/* 333 * npf_alg_export: serialise the configuration of ALGs. 334 */ 335int 336npf_alg_export(npf_t *npf, nvlist_t *nvl) 337{ 338 npf_algset_t *aset = npf->algset; 339 340 KASSERT(npf_config_locked_p(npf)); 341 342 for (unsigned i = 0; i < aset->alg_count; i++) { 343 const npf_alg_t *alg = &aset->alg_list[i]; 344 nvlist_t *algdict; 345 346 if (alg->na_name == NULL) { 347 continue; 348 } 349 algdict = nvlist_create(0); 350 nvlist_add_string(algdict, "name", alg->na_name); 351 nvlist_append_nvlist_array(nvl, "algs", algdict); 352 nvlist_destroy(algdict); 353 } 354 return 0; 355} 356