Home | History | Annotate | Line # | Download | only in npf
      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.22 2020/05/30 14:16:56 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 
     53 struct npf_alg {
     54 	const char *	na_name;
     55 	unsigned	na_slot;
     56 };
     57 
     58 struct 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 
     70 void
     71 npf_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 
     79 void
     80 npf_alg_fini(npf_t *npf)
     81 {
     82 	npf_algset_t *aset = npf->algset;
     83 
     84 	kmem_free(aset, sizeof(npf_algset_t));
     85 }
     86 
     87 static npf_alg_t *
     88 npf_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 
    104 npf_alg_t *
    105 npf_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  */
    129 npf_alg_t *
    130 npf_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 	/*
    160 	 * Assign the functions.  Make sure the 'destroy' gets visible first.
    161 	 */
    162 	afuncs = &aset->alg_funcs[i];
    163 	atomic_store_relaxed(&afuncs->destroy, funcs->destroy);
    164 	membar_producer();
    165 	atomic_store_relaxed(&afuncs->translate, funcs->translate);
    166 	atomic_store_relaxed(&afuncs->inspect, funcs->inspect);
    167 	atomic_store_relaxed(&afuncs->match, funcs->match);
    168 	membar_producer();
    169 
    170 	atomic_store_relaxed(&aset->alg_count, MAX(aset->alg_count, i + 1));
    171 	npf_config_exit(npf);
    172 
    173 	return alg;
    174 }
    175 
    176 /*
    177  * npf_alg_unregister: unregister application-level gateway.
    178  */
    179 int
    180 npf_alg_unregister(npf_t *npf, npf_alg_t *alg)
    181 {
    182 	npf_algset_t *aset = npf->algset;
    183 	unsigned i = alg->na_slot;
    184 	npfa_funcs_t *afuncs;
    185 
    186 	/* Deactivate the functions first. */
    187 	npf_config_enter(npf);
    188 	afuncs = &aset->alg_funcs[i];
    189 	atomic_store_relaxed(&afuncs->match, NULL);
    190 	atomic_store_relaxed(&afuncs->translate, NULL);
    191 	atomic_store_relaxed(&afuncs->inspect, NULL);
    192 	npf_config_sync(npf);
    193 
    194 	/*
    195 	 * Finally, unregister the ALG.  We leave the 'destroy' callback
    196 	 * as the following will invoke it for the relevant connections.
    197 	 */
    198 	npf_ruleset_freealg(npf_config_natset(npf), alg);
    199 	atomic_store_relaxed(&afuncs->destroy, NULL);
    200 	alg->na_name = NULL;
    201 	npf_config_exit(npf);
    202 
    203 	return 0;
    204 }
    205 
    206 /*
    207  * npf_alg_match: call the ALG matching inspectors.
    208  *
    209  *	The purpose of the "matching" inspector function in the ALG API
    210  *	is to determine whether this connection matches the ALG criteria
    211  *	i.e. is concerning the ALG.  If yes, ALG can associate itself with
    212  *	the given NAT state structure and set/save an arbitrary parameter.
    213  *	This is done using the using the npf_nat_setalg() function.
    214  *
    215  *	=> This is called when the packet matches the dynamic NAT policy
    216  *	   and the NAT state entry is being created for it [NAT-ESTABLISH].
    217  */
    218 bool
    219 npf_alg_match(npf_cache_t *npc, npf_nat_t *nt, int di)
    220 {
    221 	npf_t *npf = npc->npc_ctx;
    222 	npf_algset_t *aset = npf->algset;
    223 	bool match = false;
    224 	unsigned count;
    225 	int s;
    226 
    227 	KASSERTMSG(npf_iscached(npc, NPC_IP46), "expecting protocol number");
    228 
    229 	s = npf_config_read_enter(npf);
    230 	count = atomic_load_relaxed(&aset->alg_count);
    231 	for (unsigned i = 0; i < count; i++) {
    232 		const npfa_funcs_t *f = &aset->alg_funcs[i];
    233 		bool (*match_func)(npf_cache_t *, npf_nat_t *, int);
    234 
    235 		match_func = atomic_load_relaxed(&f->match);
    236 		if (match_func && match_func(npc, nt, di)) {
    237 			match = true;
    238 			break;
    239 		}
    240 	}
    241 	npf_config_read_exit(npf, s);
    242 	return match;
    243 }
    244 
    245 /*
    246  * npf_alg_exec: execute the ALG translation processors.
    247  *
    248  *	The ALG function would perform any additional packet translation
    249  *	or manipulation here.
    250  *
    251  *	=> This is called when the packet is being translated according
    252  *	   to the dynamic NAT logic [NAT-TRANSLATE].
    253  */
    254 void
    255 npf_alg_exec(npf_cache_t *npc, npf_nat_t *nt, const npf_flow_t flow)
    256 {
    257 	npf_t *npf = npc->npc_ctx;
    258 	npf_algset_t *aset = npf->algset;
    259 	unsigned count;
    260 	int s;
    261 
    262 	s = npf_config_read_enter(npf);
    263 	count = atomic_load_relaxed(&aset->alg_count);
    264 	for (unsigned i = 0; i < count; i++) {
    265 		const npfa_funcs_t *f = &aset->alg_funcs[i];
    266 		bool (*translate_func)(npf_cache_t *, npf_nat_t *, npf_flow_t);
    267 
    268 		translate_func = atomic_load_relaxed(&f->translate);
    269 		if (translate_func) {
    270 			translate_func(npc, nt, flow);
    271 		}
    272 	}
    273 	npf_config_read_exit(npf, s);
    274 }
    275 
    276 /*
    277  * npf_alg_conn: query ALGs which may perform a custom state lookup.
    278  *
    279  *	The purpose of ALG connection inspection function is to provide
    280  *	ALGs with a mechanism to override the regular connection state
    281  *	lookup, if they need to.  For example, some ALGs may want to
    282  *	extract and use a different n-tuple to perform a lookup.
    283  *
    284  *	=> This is called at the beginning of the connection state lookup
    285  *	   function [CONN-LOOKUP].
    286  *
    287  *	=> Must use the npf_conn_lookup() function to perform the custom
    288  *	   connection state lookup and return the result.
    289  *
    290  *	=> Returning NULL will result in NPF performing a regular state
    291  *	   lookup for the packet.
    292  */
    293 npf_conn_t *
    294 npf_alg_conn(npf_cache_t *npc, int di)
    295 {
    296 	npf_t *npf = npc->npc_ctx;
    297 	npf_algset_t *aset = npf->algset;
    298 	npf_conn_t *con = NULL;
    299 	unsigned count;
    300 	int s;
    301 
    302 	s = npf_config_read_enter(npf);
    303 	count = atomic_load_relaxed(&aset->alg_count);
    304 	for (unsigned i = 0; i < count; i++) {
    305 		const npfa_funcs_t *f = &aset->alg_funcs[i];
    306 		npf_conn_t *(*inspect_func)(npf_cache_t *, int);
    307 
    308 		inspect_func = atomic_load_relaxed(&f->inspect);
    309 		if (inspect_func && (con = inspect_func(npc, di)) != NULL) {
    310 			break;
    311 		}
    312 	}
    313 	npf_config_read_exit(npf, s);
    314 	return con;
    315 }
    316 
    317 /*
    318  * npf_alg_destroy: free the ALG structure associated with the NAT entry.
    319  */
    320 void
    321 npf_alg_destroy(npf_t *npf, npf_alg_t *alg, npf_nat_t *nat, npf_conn_t *con)
    322 {
    323 	npf_algset_t *aset = npf->algset;
    324 	const npfa_funcs_t *f = &aset->alg_funcs[alg->na_slot];
    325 	void (*destroy_func)(npf_t *, npf_nat_t *, npf_conn_t *);
    326 
    327 	if ((destroy_func = atomic_load_relaxed(&f->destroy)) != NULL) {
    328 		destroy_func(npf, nat, con);
    329 	}
    330 }
    331 
    332 /*
    333  * npf_alg_export: serialise the configuration of ALGs.
    334  */
    335 int
    336 npf_alg_export(npf_t *npf, nvlist_t *nvl)
    337 {
    338 	npf_algset_t *aset = npf->algset;
    339 
    340 	KASSERT(npf_config_locked_p(npf));
    341 
    342 	for (unsigned i = 0; i < aset->alg_count; i++) {
    343 		const npf_alg_t *alg = &aset->alg_list[i];
    344 		nvlist_t *algdict;
    345 
    346 		if (alg->na_name == NULL) {
    347 			continue;
    348 		}
    349 		algdict = nvlist_create(0);
    350 		nvlist_add_string(algdict, "name", alg->na_name);
    351 		nvlist_append_nvlist_array(nvl, "algs", algdict);
    352 		nvlist_destroy(algdict);
    353 	}
    354 	return 0;
    355 }
    356