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