npf_alg.c revision 1.19
1/*- 2 * Copyright (c) 2010-2013 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.19 2019/01/19 21:19:31 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/mutex.h> 44#include <net/pfil.h> 45#include <sys/module.h> 46#endif 47 48#include "npf_impl.h" 49 50/* 51 * NAT ALG description structure. For more compact use of cache, 52 * the functions are separated in their own arrays. The number of 53 * ALGs is expected to be very small. 54 */ 55 56struct npf_alg { 57 const char * na_name; 58 u_int na_slot; 59}; 60 61struct npf_algset { 62 /* List of ALGs and the count. */ 63 npf_alg_t alg_list[NPF_MAX_ALGS]; 64 u_int alg_count; 65 66 /* Matching, inspection and translation functions. */ 67 npfa_funcs_t alg_funcs[NPF_MAX_ALGS]; 68}; 69 70static const char alg_prefix[] = "npf_alg_"; 71#define NPF_EXT_PREFLEN (sizeof(alg_prefix) - 1) 72 73void 74npf_alg_init(npf_t *npf) 75{ 76 npf_algset_t *aset; 77 78 aset = kmem_zalloc(sizeof(npf_algset_t), KM_SLEEP); 79 npf->algset = aset; 80} 81 82void 83npf_alg_fini(npf_t *npf) 84{ 85 npf_algset_t *aset = npf->algset; 86 87 kmem_free(aset, sizeof(npf_algset_t)); 88} 89 90static npf_alg_t * 91npf_alg_lookup(npf_t *npf, const char *name) 92{ 93 npf_algset_t *aset = npf->algset; 94 95 KASSERT(npf_config_locked_p(npf)); 96 97 for (u_int i = 0; i < aset->alg_count; i++) { 98 npf_alg_t *alg = &aset->alg_list[i]; 99 const char *aname = alg->na_name; 100 101 if (aname && strcmp(aname, name) == 0) 102 return alg; 103 } 104 return NULL; 105} 106 107npf_alg_t * 108npf_alg_construct(npf_t *npf, const char *name) 109{ 110 npf_alg_t *alg; 111 112 npf_config_enter(npf); 113 if ((alg = npf_alg_lookup(npf, name)) == NULL) { 114 char modname[NPF_EXT_PREFLEN + 64]; 115 116 snprintf(modname, sizeof(modname), "%s%s", alg_prefix, name); 117 npf_config_exit(npf); 118 119 if (module_autoload(modname, MODULE_CLASS_MISC) != 0) { 120 return NULL; 121 } 122 npf_config_enter(npf); 123 alg = npf_alg_lookup(npf, name); 124 } 125 npf_config_exit(npf); 126 return alg; 127} 128 129/* 130 * npf_alg_register: register application-level gateway. 131 */ 132npf_alg_t * 133npf_alg_register(npf_t *npf, const char *name, const npfa_funcs_t *funcs) 134{ 135 npf_algset_t *aset = npf->algset; 136 npfa_funcs_t *afuncs; 137 npf_alg_t *alg; 138 u_int i; 139 140 npf_config_enter(npf); 141 if (npf_alg_lookup(npf, name) != NULL) { 142 npf_config_exit(npf); 143 return NULL; 144 } 145 146 /* Find a spare slot. */ 147 for (i = 0; i < NPF_MAX_ALGS; i++) { 148 alg = &aset->alg_list[i]; 149 if (alg->na_name == NULL) { 150 break; 151 } 152 } 153 if (i == NPF_MAX_ALGS) { 154 npf_config_exit(npf); 155 return NULL; 156 } 157 158 /* Register the ALG. */ 159 alg->na_name = name; 160 alg->na_slot = i; 161 162 /* Assign the functions. */ 163 afuncs = &aset->alg_funcs[i]; 164 afuncs->match = funcs->match; 165 afuncs->translate = funcs->translate; 166 afuncs->inspect = funcs->inspect; 167 168 aset->alg_count = MAX(aset->alg_count, i + 1); 169 npf_config_exit(npf); 170 171 return alg; 172} 173 174/* 175 * npf_alg_unregister: unregister application-level gateway. 176 */ 177int 178npf_alg_unregister(npf_t *npf, npf_alg_t *alg) 179{ 180 npf_algset_t *aset = npf->algset; 181 u_int i = alg->na_slot; 182 npfa_funcs_t *afuncs; 183 184 /* Deactivate the functions first. */ 185 npf_config_enter(npf); 186 afuncs = &aset->alg_funcs[i]; 187 afuncs->match = NULL; 188 afuncs->translate = NULL; 189 afuncs->inspect = NULL; 190 pserialize_perform(npf->qsbr); 191 192 /* Finally, unregister the ALG. */ 193 npf_ruleset_freealg(npf_config_natset(npf), alg); 194 alg->na_name = NULL; 195 npf_config_exit(npf); 196 197 return 0; 198} 199 200/* 201 * npf_alg_match: call ALG matching inspectors, determine if any ALG matches. 202 */ 203bool 204npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di) 205{ 206 npf_algset_t *aset = npc->npc_ctx->algset; 207 bool match = false; 208 int s; 209 210 s = pserialize_read_enter(); 211 for (u_int i = 0; i < aset->alg_count; i++) { 212 const npfa_funcs_t *f = &aset->alg_funcs[i]; 213 214 if (f->match && f->match(npc, nt, di)) { 215 match = true; 216 break; 217 } 218 } 219 pserialize_read_exit(s); 220 return match; 221} 222 223/* 224 * npf_alg_exec: execute ALG hooks for translation. 225 */ 226void 227npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, bool forw) 228{ 229 npf_algset_t *aset = npc->npc_ctx->algset; 230 int s; 231 232 s = pserialize_read_enter(); 233 for (u_int i = 0; i < aset->alg_count; i++) { 234 const npfa_funcs_t *f = &aset->alg_funcs[i]; 235 236 if (f->translate) { 237 f->translate(npc, nt, forw); 238 } 239 } 240 pserialize_read_exit(s); 241} 242 243npf_conn_t * 244npf_alg_conn(npf_cache_t *npc, int di) 245{ 246 npf_algset_t *aset = npc->npc_ctx->algset; 247 npf_conn_t *con = NULL; 248 int s; 249 250 s = pserialize_read_enter(); 251 for (u_int i = 0; i < aset->alg_count; i++) { 252 const npfa_funcs_t *f = &aset->alg_funcs[i]; 253 254 if (!f->inspect) 255 continue; 256 if ((con = f->inspect(npc, di)) != NULL) 257 break; 258 } 259 pserialize_read_exit(s); 260 return con; 261} 262 263int 264npf_alg_export(npf_t *npf, nvlist_t *npf_dict) 265{ 266 npf_algset_t *aset = npf->algset; 267 268 KASSERT(npf_config_locked_p(npf)); 269 270 for (u_int i = 0; i < aset->alg_count; i++) { 271 const npf_alg_t *alg = &aset->alg_list[i]; 272 nvlist_t *algdict; 273 274 if (alg->na_name == NULL) { 275 continue; 276 } 277 algdict = nvlist_create(0); 278 nvlist_add_string(algdict, "name", alg->na_name); 279 nvlist_append_nvlist_array(npf_dict, "algs", algdict); 280 nvlist_destroy(algdict); 281 } 282 return 0; 283} 284