1 /* $NetBSD: npf_alg.c,v 1.16 2016/12/26 23:05:06 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2010-2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This material is based upon work partially supported by The 8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * NPF interface for the Application Level Gateways (ALGs). 34 */ 35 36 #ifdef _KERNEL 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.16 2016/12/26 23:05:06 christos Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/types.h> 42 43 #include <sys/kmem.h> 44 #include <sys/pserialize.h> 45 #include <sys/mutex.h> 46 #include <net/pfil.h> 47 #include <sys/module.h> 48 #endif 49 50 #include "npf_impl.h" 51 52 /* 53 * NAT ALG description structure. For more compact use of cache, 54 * the functions are separated in their own arrays. The number of 55 * ALGs is expected to be very small. 56 */ 57 58 struct npf_alg { 59 const char * na_name; 60 u_int na_slot; 61 }; 62 63 struct npf_algset { 64 /* List of ALGs and the count. */ 65 npf_alg_t alg_list[NPF_MAX_ALGS]; 66 u_int alg_count; 67 68 /* Matching, inspection and translation functions. */ 69 npfa_funcs_t alg_funcs[NPF_MAX_ALGS]; 70 }; 71 72 static const char alg_prefix[] = "npf_alg_"; 73 #define NPF_EXT_PREFLEN (sizeof(alg_prefix) - 1) 74 75 void 76 npf_alg_init(npf_t *npf) 77 { 78 npf_algset_t *aset; 79 80 aset = kmem_zalloc(sizeof(npf_algset_t), KM_SLEEP); 81 npf->algset = aset; 82 } 83 84 void 85 npf_alg_fini(npf_t *npf) 86 { 87 npf_algset_t *aset = npf->algset; 88 89 kmem_free(aset, sizeof(npf_algset_t)); 90 } 91 92 static npf_alg_t * 93 npf_alg_lookup(npf_t *npf, const char *name) 94 { 95 npf_algset_t *aset = npf->algset; 96 97 KASSERT(npf_config_locked_p(npf)); 98 99 for (u_int i = 0; i < aset->alg_count; i++) { 100 npf_alg_t *alg = &aset->alg_list[i]; 101 const char *aname = alg->na_name; 102 103 if (aname && strcmp(aname, name) == 0) 104 return alg; 105 } 106 return NULL; 107 } 108 109 npf_alg_t * 110 npf_alg_construct(npf_t *npf, const char *name) 111 { 112 npf_alg_t *alg; 113 114 npf_config_enter(npf); 115 if ((alg = npf_alg_lookup(npf, name)) == NULL) { 116 char modname[NPF_EXT_PREFLEN + 64]; 117 snprintf(modname, sizeof(modname), "%s%s", alg_prefix, name); 118 npf_config_exit(npf); 119 120 if (module_autoload(modname, MODULE_CLASS_MISC) != 0) { 121 return NULL; 122 } 123 npf_config_enter(npf); 124 alg = npf_alg_lookup(npf, name); 125 } 126 npf_config_exit(npf); 127 return alg; 128 } 129 130 /* 131 * npf_alg_register: register application-level gateway. 132 */ 133 npf_alg_t * 134 npf_alg_register(npf_t *npf, const char *name, const npfa_funcs_t *funcs) 135 { 136 npf_algset_t *aset = npf->algset; 137 npfa_funcs_t *afuncs; 138 npf_alg_t *alg; 139 u_int i; 140 141 npf_config_enter(npf); 142 if (npf_alg_lookup(npf, name) != NULL) { 143 npf_config_exit(npf); 144 return NULL; 145 } 146 147 /* Find a spare slot. */ 148 for (i = 0; i < NPF_MAX_ALGS; i++) { 149 alg = &aset->alg_list[i]; 150 if (alg->na_name == NULL) { 151 break; 152 } 153 } 154 if (i == NPF_MAX_ALGS) { 155 npf_config_exit(npf); 156 return NULL; 157 } 158 159 /* Register the ALG. */ 160 alg->na_name = name; 161 alg->na_slot = i; 162 163 /* Assign the functions. */ 164 afuncs = &aset->alg_funcs[i]; 165 afuncs->match = funcs->match; 166 afuncs->translate = funcs->translate; 167 afuncs->inspect = funcs->inspect; 168 169 aset->alg_count = MAX(aset->alg_count, i + 1); 170 npf_config_exit(npf); 171 172 return alg; 173 } 174 175 /* 176 * npf_alg_unregister: unregister application-level gateway. 177 */ 178 int 179 npf_alg_unregister(npf_t *npf, npf_alg_t *alg) 180 { 181 npf_algset_t *aset = npf->algset; 182 u_int i = alg->na_slot; 183 npfa_funcs_t *afuncs; 184 185 /* Deactivate the functions first. */ 186 npf_config_enter(npf); 187 afuncs = &aset->alg_funcs[i]; 188 afuncs->match = NULL; 189 afuncs->translate = NULL; 190 afuncs->inspect = NULL; 191 pserialize_perform(npf->qsbr); 192 193 /* Finally, unregister the ALG. */ 194 npf_ruleset_freealg(npf_config_natset(npf), alg); 195 alg->na_name = NULL; 196 npf_config_exit(npf); 197 198 return 0; 199 } 200 201 /* 202 * npf_alg_match: call ALG matching inspectors, determine if any ALG matches. 203 */ 204 bool 205 npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di) 206 { 207 npf_algset_t *aset = npc->npc_ctx->algset; 208 bool match = false; 209 int s; 210 211 s = pserialize_read_enter(); 212 for (u_int i = 0; i < aset->alg_count; i++) { 213 const npfa_funcs_t *f = &aset->alg_funcs[i]; 214 215 if (f->match && f->match(npc, nt, di)) { 216 match = true; 217 break; 218 } 219 } 220 pserialize_read_exit(s); 221 return match; 222 } 223 224 /* 225 * npf_alg_exec: execute ALG hooks for translation. 226 */ 227 void 228 npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, bool forw) 229 { 230 npf_algset_t *aset = npc->npc_ctx->algset; 231 int s; 232 233 s = pserialize_read_enter(); 234 for (u_int i = 0; i < aset->alg_count; i++) { 235 const npfa_funcs_t *f = &aset->alg_funcs[i]; 236 237 if (f->translate) { 238 f->translate(npc, nt, forw); 239 } 240 } 241 pserialize_read_exit(s); 242 } 243 244 npf_conn_t * 245 npf_alg_conn(npf_cache_t *npc, int di) 246 { 247 npf_algset_t *aset = npc->npc_ctx->algset; 248 npf_conn_t *con = NULL; 249 int s; 250 251 s = pserialize_read_enter(); 252 for (u_int i = 0; i < aset->alg_count; i++) { 253 const npfa_funcs_t *f = &aset->alg_funcs[i]; 254 255 if (!f->inspect) 256 continue; 257 if ((con = f->inspect(npc, di)) != NULL) 258 break; 259 } 260 pserialize_read_exit(s); 261 return con; 262 } 263 264 prop_array_t 265 npf_alg_export(npf_t *npf) 266 { 267 prop_array_t alglist = prop_array_create(); 268 npf_algset_t *aset = npf->algset; 269 270 KASSERT(npf_config_locked_p(npf)); 271 272 for (u_int i = 0; i < aset->alg_count; i++) { 273 const npf_alg_t *alg = &aset->alg_list[i]; 274 275 if (alg->na_name == NULL) { 276 continue; 277 } 278 prop_dictionary_t algdict = prop_dictionary_create(); 279 prop_dictionary_set_cstring(algdict, "name", alg->na_name); 280 prop_array_add(alglist, algdict); 281 prop_object_release(algdict); 282 } 283 return alglist; 284 } 285