npf_alg.c revision 1.20
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.20 2019/07/23 00:52:01 rmind Exp $"); 37 38#include <sys/param.h> 39#include <sys/types.h> 40 41#include <sys/kmem.h> 42#include <sys/pserialize.h> 43#include <sys/module.h> 44#endif 45 46#include "npf_impl.h" 47 48/* 49 * NAT ALG description structure. For more compact use of cache, 50 * the functions are separated in their own arrays. The number of 51 * ALGs is expected to be very small. 52 */ 53 54struct npf_alg { 55 const char * na_name; 56 unsigned na_slot; 57}; 58 59struct npf_algset { 60 /* List of ALGs and the count. */ 61 npf_alg_t alg_list[NPF_MAX_ALGS]; 62 unsigned alg_count; 63 64 /* Matching, inspection and translation functions. */ 65 npfa_funcs_t alg_funcs[NPF_MAX_ALGS]; 66}; 67 68#define NPF_ALG_PREF "npf_alg_" 69#define NPF_ALG_PREFLEN (sizeof(NPF_ALG_PREF) - 1) 70 71void 72npf_alg_init(npf_t *npf) 73{ 74 npf_algset_t *aset; 75 76 aset = kmem_zalloc(sizeof(npf_algset_t), KM_SLEEP); 77 npf->algset = aset; 78} 79 80void 81npf_alg_fini(npf_t *npf) 82{ 83 npf_algset_t *aset = npf->algset; 84 85 kmem_free(aset, sizeof(npf_algset_t)); 86} 87 88static npf_alg_t * 89npf_alg_lookup(npf_t *npf, const char *name) 90{ 91 npf_algset_t *aset = npf->algset; 92 93 KASSERT(npf_config_locked_p(npf)); 94 95 for (unsigned i = 0; i < aset->alg_count; i++) { 96 npf_alg_t *alg = &aset->alg_list[i]; 97 const char *aname = alg->na_name; 98 99 if (aname && strcmp(aname, name) == 0) 100 return alg; 101 } 102 return NULL; 103} 104 105npf_alg_t * 106npf_alg_construct(npf_t *npf, const char *name) 107{ 108 npf_alg_t *alg; 109 110 npf_config_enter(npf); 111 if ((alg = npf_alg_lookup(npf, name)) == NULL) { 112 char modname[NPF_ALG_PREFLEN + 64]; 113 114 snprintf(modname, sizeof(modname), "%s%s", NPF_ALG_PREF, name); 115 npf_config_exit(npf); 116 117 if (module_autoload(modname, MODULE_CLASS_MISC) != 0) { 118 return NULL; 119 } 120 npf_config_enter(npf); 121 alg = npf_alg_lookup(npf, name); 122 } 123 npf_config_exit(npf); 124 return alg; 125} 126 127/* 128 * npf_alg_register: register application-level gateway. 129 */ 130npf_alg_t * 131npf_alg_register(npf_t *npf, const char *name, const npfa_funcs_t *funcs) 132{ 133 npf_algset_t *aset = npf->algset; 134 npfa_funcs_t *afuncs; 135 npf_alg_t *alg; 136 unsigned i; 137 138 npf_config_enter(npf); 139 if (npf_alg_lookup(npf, name) != NULL) { 140 npf_config_exit(npf); 141 return NULL; 142 } 143 144 /* Find a spare slot. */ 145 for (i = 0; i < NPF_MAX_ALGS; i++) { 146 alg = &aset->alg_list[i]; 147 if (alg->na_name == NULL) { 148 break; 149 } 150 } 151 if (i == NPF_MAX_ALGS) { 152 npf_config_exit(npf); 153 return NULL; 154 } 155 156 /* Register the ALG. */ 157 alg->na_name = name; 158 alg->na_slot = i; 159 160 /* Assign the functions. */ 161 afuncs = &aset->alg_funcs[i]; 162 afuncs->match = funcs->match; 163 afuncs->translate = funcs->translate; 164 afuncs->inspect = funcs->inspect; 165 166 aset->alg_count = MAX(aset->alg_count, i + 1); 167 npf_config_exit(npf); 168 169 return alg; 170} 171 172/* 173 * npf_alg_unregister: unregister application-level gateway. 174 */ 175int 176npf_alg_unregister(npf_t *npf, npf_alg_t *alg) 177{ 178 npf_algset_t *aset = npf->algset; 179 unsigned i = alg->na_slot; 180 npfa_funcs_t *afuncs; 181 182 /* Deactivate the functions first. */ 183 npf_config_enter(npf); 184 afuncs = &aset->alg_funcs[i]; 185 afuncs->match = NULL; 186 afuncs->translate = NULL; 187 afuncs->inspect = NULL; 188 pserialize_perform(npf->qsbr); 189 190 /* Finally, unregister the ALG. */ 191 npf_ruleset_freealg(npf_config_natset(npf), alg); 192 alg->na_name = NULL; 193 npf_config_exit(npf); 194 195 return 0; 196} 197 198/* 199 * npf_alg_match: call the ALG matching inspectors. 200 * 201 * The purpose of the "matching" inspector function in the ALG API 202 * is to determine whether this connection matches the ALG criteria 203 * i.e. is concerning the ALG. If yes, ALG can associate itself with 204 * the given NAT state structure and set/save an arbitrary parameter. 205 * This is done using the using the npf_nat_setalg() function. 206 * 207 * => This is called when the packet matches the dynamic NAT policy 208 * and the NAT state entry is being created for it [NAT-ESTABLISH]. 209 */ 210bool 211npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di) 212{ 213 npf_algset_t *aset = npc->npc_ctx->algset; 214 bool match = false; 215 int s; 216 217 KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number"); 218 219 s = pserialize_read_enter(); 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 pserialize_read_exit(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_algset_t *aset = npc->npc_ctx->algset; 247 int s; 248 249 KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number"); 250 251 s = pserialize_read_enter(); 252 for (unsigned i = 0; i < aset->alg_count; i++) { 253 const npfa_funcs_t *f = &aset->alg_funcs[i]; 254 255 if (f->translate) { 256 f->translate(npc, nt, forw); 257 } 258 } 259 pserialize_read_exit(s); 260} 261 262/* 263 * npf_alg_conn: query ALGs giving which may perform a custom state lookup. 264 * 265 * The purpose of ALG connection inspection function is to provide 266 * ALGs with a mechanism to override the regular connection state 267 * lookup, if they need to. For example, some ALGs may want to 268 * extract and use a different 5-tuple to perform a lookup. 269 * 270 * => This is called at the beginning of the connection state lookup 271 * function [CONN-LOOKUP]. 272 * 273 * => Must use the npf_conn_lookup() function to perform the custom 274 * connection state lookup and return the result. 275 * 276 * => Returning NULL will result in NPF performing a regular state 277 * lookup for the packet. 278 */ 279npf_conn_t * 280npf_alg_conn(npf_cache_t *npc, int di) 281{ 282 npf_algset_t *aset = npc->npc_ctx->algset; 283 npf_conn_t *con = NULL; 284 int s; 285 286 s = pserialize_read_enter(); 287 for (unsigned i = 0; i < aset->alg_count; i++) { 288 const npfa_funcs_t *f = &aset->alg_funcs[i]; 289 290 if (!f->inspect) 291 continue; 292 if ((con = f->inspect(npc, di)) != NULL) 293 break; 294 } 295 pserialize_read_exit(s); 296 return con; 297} 298 299/* 300 * npf_alg_export: serialise the configuration of ALGs. 301 */ 302int 303npf_alg_export(npf_t *npf, nvlist_t *npf_dict) 304{ 305 npf_algset_t *aset = npf->algset; 306 307 KASSERT(npf_config_locked_p(npf)); 308 309 for (unsigned i = 0; i < aset->alg_count; i++) { 310 const npf_alg_t *alg = &aset->alg_list[i]; 311 nvlist_t *algdict; 312 313 if (alg->na_name == NULL) { 314 continue; 315 } 316 algdict = nvlist_create(0); 317 nvlist_add_string(algdict, "name", alg->na_name); 318 nvlist_append_nvlist_array(npf_dict, "algs", algdict); 319 nvlist_destroy(algdict); 320 } 321 return 0; 322} 323