npf_alg.c revision 1.21
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.21 2019/08/25 13:21:03 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 /* Assign the functions. */ 160 afuncs = &aset->alg_funcs[i]; 161 afuncs->match = funcs->match; 162 afuncs->translate = funcs->translate; 163 afuncs->inspect = funcs->inspect; 164 165 aset->alg_count = MAX(aset->alg_count, i + 1); 166 npf_config_exit(npf); 167 168 return alg; 169} 170 171/* 172 * npf_alg_unregister: unregister application-level gateway. 173 */ 174int 175npf_alg_unregister(npf_t *npf, npf_alg_t *alg) 176{ 177 npf_algset_t *aset = npf->algset; 178 unsigned i = alg->na_slot; 179 npfa_funcs_t *afuncs; 180 181 /* Deactivate the functions first. */ 182 npf_config_enter(npf); 183 afuncs = &aset->alg_funcs[i]; 184 afuncs->match = NULL; 185 afuncs->translate = NULL; 186 afuncs->inspect = NULL; 187 npf_ebr_full_sync(npf->ebr); 188 189 /* Finally, unregister the ALG. */ 190 npf_ruleset_freealg(npf_config_natset(npf), alg); 191 alg->na_name = NULL; 192 npf_config_exit(npf); 193 194 return 0; 195} 196 197/* 198 * npf_alg_match: call the ALG matching inspectors. 199 * 200 * The purpose of the "matching" inspector function in the ALG API 201 * is to determine whether this connection matches the ALG criteria 202 * i.e. is concerning the ALG. If yes, ALG can associate itself with 203 * the given NAT state structure and set/save an arbitrary parameter. 204 * This is done using the using the npf_nat_setalg() function. 205 * 206 * => This is called when the packet matches the dynamic NAT policy 207 * and the NAT state entry is being created for it [NAT-ESTABLISH]. 208 */ 209bool 210npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di) 211{ 212 npf_t *npf = npc->npc_ctx; 213 npf_algset_t *aset = npf->algset; 214 bool match = false; 215 int s; 216 217 KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number"); 218 219 s = npf_ebr_enter(npf->ebr); 220 for (unsigned i = 0; i < aset->alg_count; i++) { 221 const npfa_funcs_t *f = &aset->alg_funcs[i]; 222 223 if (f->match && f->match(npc, nt, di)) { 224 match = true; 225 break; 226 } 227 } 228 npf_ebr_exit(npf->ebr, s); 229 return match; 230} 231 232/* 233 * npf_alg_exec: execute the ALG translation processors. 234 * 235 * The ALG function would perform any additional packet translation 236 * or manipulation here. The translate function will be called by 237 * once the ALG has been associated with the NAT state through the 238 * npf_alg_match() inspector. 239 * 240 * => This is called when the packet is being translated according 241 * to the dynamic NAT logic [NAT-TRANSLATE]. 242 */ 243void 244npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, bool forw) 245{ 246 npf_t *npf = npc->npc_ctx; 247 npf_algset_t *aset = npf->algset; 248 int s; 249 250 KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number"); 251 252 s = npf_ebr_enter(npf->ebr); 253 for (unsigned i = 0; i < aset->alg_count; i++) { 254 const npfa_funcs_t *f = &aset->alg_funcs[i]; 255 256 if (f->translate) { 257 f->translate(npc, nt, forw); 258 } 259 } 260 npf_ebr_exit(npf->ebr, s); 261} 262 263/* 264 * npf_alg_conn: query ALGs which may perform a custom state lookup. 265 * 266 * The purpose of ALG connection inspection function is to provide 267 * ALGs with a mechanism to override the regular connection state 268 * lookup, if they need to. For example, some ALGs may want to 269 * extract and use a different 5-tuple to perform a lookup. 270 * 271 * => This is called at the beginning of the connection state lookup 272 * function [CONN-LOOKUP]. 273 * 274 * => Must use the npf_conn_lookup() function to perform the custom 275 * connection state lookup and return the result. 276 * 277 * => Returning NULL will result in NPF performing a regular state 278 * lookup for the packet. 279 */ 280npf_conn_t * 281npf_alg_conn(npf_cache_t *npc, int di) 282{ 283 npf_t *npf = npc->npc_ctx; 284 npf_algset_t *aset = npf->algset; 285 npf_conn_t *con = NULL; 286 int s; 287 288 s = npf_ebr_enter(npf->ebr); 289 for (unsigned i = 0; i < aset->alg_count; i++) { 290 const npfa_funcs_t *f = &aset->alg_funcs[i]; 291 292 if (!f->inspect) 293 continue; 294 if ((con = f->inspect(npc, di)) != NULL) 295 break; 296 } 297 npf_ebr_exit(npf->ebr, s); 298 return con; 299} 300 301/* 302 * npf_alg_export: serialise the configuration of ALGs. 303 */ 304int 305npf_alg_export(npf_t *npf, nvlist_t *npf_dict) 306{ 307 npf_algset_t *aset = npf->algset; 308 309 KASSERT(npf_config_locked_p(npf)); 310 311 for (unsigned i = 0; i < aset->alg_count; i++) { 312 const npf_alg_t *alg = &aset->alg_list[i]; 313 nvlist_t *algdict; 314 315 if (alg->na_name == NULL) { 316 continue; 317 } 318 algdict = nvlist_create(0); 319 nvlist_add_string(algdict, "name", alg->na_name); 320 nvlist_append_nvlist_array(npf_dict, "algs", algdict); 321 nvlist_destroy(algdict); 322 } 323 return 0; 324} 325