Home | History | Annotate | Line # | Download | only in npf
npf_ctl.c revision 1.3
      1 /*	$NetBSD: npf_ctl.c,v 1.3 2010/11/11 06:30:39 rmind Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009-2010 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 device control.
     34  *
     35  * Implementation of (re)loading, construction of tables and rules.
     36  * NPF proplib(9) dictionary consumer.
     37  *
     38  * TODO:
     39  * - Consider implementing 'sync' functionality.
     40  */
     41 
     42 #include <sys/cdefs.h>
     43 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.3 2010/11/11 06:30:39 rmind Exp $");
     44 
     45 #include <sys/param.h>
     46 #include <sys/conf.h>
     47 #include <sys/kernel.h>
     48 
     49 #include <prop/proplib.h>
     50 
     51 #include "npf_ncode.h"
     52 #include "npf_impl.h"
     53 
     54 /*
     55  * npfctl_switch: enable or disable packet inspection.
     56  */
     57 int
     58 npfctl_switch(void *data)
     59 {
     60 	const bool onoff = *(int *)data ? true : false;
     61 	int error;
     62 
     63 	if (onoff) {
     64 		/* Enable: add pfil hooks. */
     65 		error = npf_register_pfil();
     66 	} else {
     67 		/* Disable: remove pfil hooks. */
     68 		npf_unregister_pfil();
     69 		error = 0;
     70 	}
     71 	return error;
     72 }
     73 
     74 static int
     75 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables)
     76 {
     77 	prop_object_iterator_t it;
     78 	prop_dictionary_t tbldict;
     79 	prop_object_t obj;
     80 	int error = 0;
     81 
     82 	/* Tables - array. */
     83 	if (prop_object_type(tables) != PROP_TYPE_ARRAY)
     84 		return EINVAL;
     85 
     86 	it = prop_array_iterator(tables);
     87 	if (it == NULL)
     88 		return ENOMEM;
     89 
     90 	while ((tbldict = prop_object_iterator_next(it)) != NULL) {
     91 		prop_dictionary_t ent;
     92 		prop_object_iterator_t eit;
     93 		prop_array_t entries;
     94 		npf_table_t *t;
     95 		u_int tid;
     96 		int type;
     97 
     98 		/* Table - dictionary. */
     99 		if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) {
    100 			error = EINVAL;
    101 			break;
    102 		}
    103 
    104 		/* Table ID and type. */
    105 		obj = prop_dictionary_get(tbldict, "id");
    106 		tid = (u_int)prop_number_integer_value(obj);
    107 		obj = prop_dictionary_get(tbldict, "type");
    108 		type = (int)prop_number_integer_value(obj);
    109 		/* Validate them. */
    110 		error = npf_table_check(tblset, tid, type);
    111 		if (error)
    112 			break;
    113 
    114 		/* Create and insert the table. */
    115 		t = npf_table_create(tid, type, 1024);	/* XXX */
    116 		if (t == NULL) {
    117 			error = ENOMEM;
    118 			break;
    119 		}
    120 		error = npf_tableset_insert(tblset, t);
    121 		KASSERT(error == 0);
    122 
    123 		/* Entries. */
    124 		entries = prop_dictionary_get(tbldict, "entries");
    125 		if (prop_object_type(entries) != PROP_TYPE_ARRAY) {
    126 			error = EINVAL;
    127 			break;
    128 		}
    129 		eit = prop_array_iterator(entries);
    130 		if (eit == NULL) {
    131 			error = ENOMEM;
    132 			break;
    133 		}
    134 		while ((ent = prop_object_iterator_next(eit)) != NULL) {
    135 			in_addr_t addr, mask;	/* XXX: IPv6 */
    136 
    137 			/* Address. */
    138 			obj = prop_dictionary_get(ent, "addr");
    139 			addr = (in_addr_t)prop_number_integer_value(obj);
    140 			/* Mask. */
    141 			obj = prop_dictionary_get(ent, "mask");
    142 			mask = (in_addr_t)prop_number_integer_value(obj);
    143 			/* Add a table entry. */
    144 			error = npf_table_add_v4cidr(tblset, tid, addr, mask);
    145 			if (error)
    146 				break;
    147 		}
    148 		prop_object_iterator_release(eit);
    149 		if (error)
    150 			break;
    151 	}
    152 	prop_object_iterator_release(it);
    153 	/*
    154 	 * Note: in a case of error, caller will free entire tableset.
    155 	 */
    156 	return error;
    157 }
    158 
    159 static void *
    160 npf_mk_ncode(const void *ncptr, size_t nc_size)
    161 {
    162 	int npf_err, errat;
    163 	void *nc;
    164 
    165 	/*
    166 	 * Allocate and copy n-code.
    167 	 *
    168 	 * XXX: Inefficient; consider extending proplib(9) to provide
    169 	 * interface for custom allocator and avoid copy.
    170 	 */
    171 	nc = npf_ncode_alloc(nc_size);
    172 	if (nc == NULL) {
    173 		return NULL;
    174 	}
    175 	memcpy(nc, ncptr, nc_size);
    176 	npf_err = npf_ncode_validate(nc, nc_size, &errat);
    177 	if (npf_err) {
    178 		npf_ncode_free(nc, nc_size);
    179 		/* TODO: return error details via proplib */
    180 		return NULL;
    181 	}
    182 	return nc;
    183 }
    184 
    185 static int
    186 npf_mk_singlerule(prop_dictionary_t rldict,
    187     npf_ruleset_t *rlset, npf_rule_t **parent)
    188 {
    189 	npf_rule_t *rl;
    190 	prop_object_t obj;
    191 	int attr, ifidx, minttl, maxmss;
    192 	pri_t pri;
    193 	bool rnd_ipid;
    194 	size_t nc_size;
    195 	void *nc;
    196 
    197 	/* Rule - dictionary. */
    198 	if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY)
    199 		return EINVAL;
    200 
    201 	/* Attributes (integer). */
    202 	obj = prop_dictionary_get(rldict, "attributes");
    203 	attr = prop_number_integer_value(obj);
    204 
    205 	/* Priority (integer). */
    206 	obj = prop_dictionary_get(rldict, "priority");
    207 	pri = prop_number_integer_value(obj);
    208 
    209 	/* Interface ID (integer). */
    210 	obj = prop_dictionary_get(rldict, "interface");
    211 	ifidx = prop_number_integer_value(obj);
    212 
    213 	/* Randomize IP ID (bool). */
    214 	obj = prop_dictionary_get(rldict, "randomize-id");
    215 	rnd_ipid = prop_bool_true(obj);
    216 
    217 	/* Minimum IP TTL (integer). */
    218 	obj = prop_dictionary_get(rldict, "min-ttl");
    219 	minttl = prop_number_integer_value(obj);
    220 
    221 	/* Maximum TCP MSS (integer). */
    222 	obj = prop_dictionary_get(rldict, "max-mss");
    223 	maxmss = prop_number_integer_value(obj);
    224 
    225 	/* N-code (binary data). */
    226 	obj = prop_dictionary_get(rldict, "ncode");
    227 	if (obj) {
    228 		const void *ncptr;
    229 
    230 		/* Perform n-code validation. */
    231 		nc_size = prop_data_size(obj);
    232 		ncptr = prop_data_data_nocopy(obj);
    233 		if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) {
    234 			return EINVAL;
    235 		}
    236 		nc = npf_mk_ncode(ncptr, nc_size);
    237 		if (nc == NULL) {
    238 			return EINVAL;
    239 		}
    240 	} else {
    241 		/* No n-code. */
    242 		nc = NULL;
    243 		nc_size = 0;
    244 	}
    245 
    246 	/* Allocate and setup NPF rule. */
    247 	rl = npf_rule_alloc(attr, pri, ifidx, nc, nc_size,
    248 	    rnd_ipid, minttl, maxmss);
    249 	if (rl == NULL) {
    250 		if (nc) {
    251 			npf_ncode_free(nc, nc_size);	/* XXX */
    252 		}
    253 		return ENOMEM;
    254 	}
    255 	npf_ruleset_insert(rlset, rl);
    256 	if (parent) {
    257 		*parent = rl;
    258 	}
    259 	return 0;
    260 }
    261 
    262 static int
    263 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules)
    264 {
    265 	prop_object_iterator_t it;
    266 	prop_dictionary_t rldict;
    267 	int error;
    268 
    269 	/* Ruleset - array. */
    270 	if (prop_object_type(rules) != PROP_TYPE_ARRAY)
    271 		return EINVAL;
    272 
    273 	it = prop_array_iterator(rules);
    274 	if (it == NULL)
    275 		return ENOMEM;
    276 
    277 	error = 0;
    278 	while ((rldict = prop_object_iterator_next(it)) != NULL) {
    279 		prop_object_iterator_t sit;
    280 		prop_array_t subrules;
    281 		prop_dictionary_t srldict;
    282 		npf_rule_t *myrl;
    283 
    284 		/* Generate a single rule. */
    285 		error = npf_mk_singlerule(rldict, rlset, &myrl);
    286 		if (error)
    287 			break;
    288 
    289 		/* Check for subrules. */
    290 		subrules = prop_dictionary_get(rldict, "subrules");
    291 		if (subrules == NULL) {
    292 			/* No subrules, next.. */
    293 			continue;
    294 		}
    295 		/* Generate subrules, if any. */
    296 		if (prop_object_type(subrules) != PROP_TYPE_ARRAY) {
    297 			error = EINVAL;
    298 			break;
    299 		}
    300 		sit = prop_array_iterator(subrules);
    301 		if (sit == NULL) {
    302 			error = ENOMEM;
    303 			break;
    304 		}
    305 		while ((srldict = prop_object_iterator_next(sit)) != NULL) {
    306 			/* For subrule, pass ruleset pointer of parent. */
    307 			error = npf_mk_singlerule(srldict,
    308 			    npf_rule_subset(myrl), NULL);
    309 			if (error)
    310 				break;
    311 		}
    312 		prop_object_iterator_release(sit);
    313 		if (error)
    314 			break;
    315 	}
    316 	prop_object_iterator_release(it);
    317 	/*
    318 	 * Note: in a case of error, caller will free entire ruleset.
    319 	 */
    320 	return error;
    321 }
    322 
    323 static int
    324 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist)
    325 {
    326 	prop_object_iterator_t it;
    327 	prop_dictionary_t natdict;
    328 	int error;
    329 
    330 	/* NAT policies - array. */
    331 	if (prop_object_type(natlist) != PROP_TYPE_ARRAY)
    332 		return EINVAL;
    333 
    334 	it = prop_array_iterator(natlist);
    335 	if (it == NULL)
    336 		return ENOMEM;
    337 
    338 	error = 0;
    339 	while ((natdict = prop_object_iterator_next(it)) != NULL) {
    340 		prop_object_t obj;
    341 		npf_natpolicy_t *np;
    342 		npf_rule_t *rl;
    343 		const npf_addr_t *taddr;
    344 		size_t taddr_sz;
    345 		in_port_t tport;
    346 		int type, flags;
    347 
    348 		/* NAT policy - dictionary. */
    349 		if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
    350 			error = EINVAL;
    351 			break;
    352 		}
    353 
    354 		/* Translation type. */
    355 		obj = prop_dictionary_get(natdict, "type");
    356 		type = prop_number_integer_value(obj);
    357 
    358 		/* Translation type. */
    359 		obj = prop_dictionary_get(natdict, "flags");
    360 		flags = prop_number_integer_value(obj);
    361 
    362 		/* Translation IP. */
    363 		obj = prop_dictionary_get(natdict, "translation-ip");
    364 		taddr_sz = prop_data_size(obj);
    365 		taddr = (const npf_addr_t *)prop_data_data_nocopy(obj);
    366 
    367 		/* Translation port (for redirect case). */
    368 		obj = prop_dictionary_get(natdict, "translation-port");
    369 		tport = (in_port_t)prop_number_integer_value(obj);
    370 
    371 		/*
    372 		 * NAT policies are standard rules, plus additional
    373 		 * information for translation.  Make a rule.
    374 		 */
    375 		error = npf_mk_singlerule(natdict, nset, &rl);
    376 		if (error)
    377 			break;
    378 
    379 		/* Allocate a new NAT policy and assign to the rule. */
    380 		np = npf_nat_newpolicy(type, flags, taddr, taddr_sz, tport);
    381 		if (np == NULL) {
    382 			error = ENOMEM;
    383 			break;
    384 		}
    385 		npf_rule_setnat(rl, np);
    386 	}
    387 	prop_object_iterator_release(it);
    388 	/*
    389 	 * Note: in a case of error, caller will free entire NAT ruleset
    390 	 * with assigned NAT policies.
    391 	 */
    392 	return error;
    393 }
    394 
    395 /*
    396  * npfctl_reload: store passed data i.e. update settings, create passed
    397  * tables, rules and atomically activate all them.
    398  */
    399 int
    400 npfctl_reload(u_long cmd, void *data)
    401 {
    402 	const struct plistref *pref = data;
    403 	npf_tableset_t *tblset = NULL;
    404 	npf_ruleset_t *rlset = NULL;
    405 	npf_ruleset_t *nset = NULL;
    406 	prop_dictionary_t dict;
    407 	prop_array_t natlist, tables, rules;
    408 	prop_object_t ver;
    409 	int error;
    410 
    411 	/* Retrieve the dictionary. */
    412 #ifdef _KERNEL
    413 	error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
    414 	if (error)
    415 		return error;
    416 #else
    417 	dict = prop_dictionary_internalize_from_file(data);
    418 	if (dict == NULL)
    419 		return EINVAL;
    420 #endif
    421 	/* Version. */
    422 	ver = prop_dictionary_get(dict, "version");
    423 	if (ver == NULL || prop_number_integer_value(ver) != NPF_VERSION) {
    424 		error = EINVAL;
    425 		goto fail;
    426 	}
    427 
    428 	/* XXX: Hard way for now. */
    429 	(void)npf_session_tracking(false);
    430 
    431 	/* NAT policies. */
    432 	nset = npf_ruleset_create();
    433 	natlist = prop_dictionary_get(dict, "translation");
    434 	error = npf_mk_natlist(nset, natlist);
    435 	if (error)
    436 		goto fail;
    437 
    438 	/* Tables. */
    439 	tblset = npf_tableset_create();
    440 	tables = prop_dictionary_get(dict, "tables");
    441 	error = npf_mk_tables(tblset, tables);
    442 	if (error)
    443 		goto fail;
    444 
    445 	/* Rules. */
    446 	rlset = npf_ruleset_create();
    447 	rules = prop_dictionary_get(dict, "rules");
    448 	error = npf_mk_rules(rlset, rules);
    449 	if (error)
    450 		goto fail;
    451 
    452 	/* Flush and reload NAT policies. */
    453 	npf_nat_reload(nset);
    454 
    455 	/*
    456 	 * Finally, reload the ruleset.  It will also reload the tableset.
    457 	 * Operation will be performed as a single transaction.
    458 	 */
    459 	npf_ruleset_reload(rlset, tblset);
    460 
    461 	(void)npf_session_tracking(true);
    462 
    463 	/* Done.  Since data is consumed now, we shall not destroy it. */
    464 	tblset = NULL;
    465 	rlset = NULL;
    466 	nset = NULL;
    467 fail:
    468 	prop_object_release(dict);
    469 	/*
    470 	 * Note: destroy rulesets first, to drop references to the tableset.
    471 	 */
    472 	KASSERT(error == 0 || (nset || rlset || tblset));
    473 	if (nset) {
    474 		npf_ruleset_destroy(nset);
    475 	}
    476 	if (rlset) {
    477 		npf_ruleset_destroy(rlset);
    478 	}
    479 	if (tblset) {
    480 		npf_tableset_destroy(tblset);
    481 	}
    482 	return error;
    483 }
    484 
    485 /*
    486  * npfctl_table: add, remove or query entries in the specified table.
    487  *
    488  * For maximum performance, interface is avoiding proplib(3)'s overhead.
    489  */
    490 int
    491 npfctl_table(void *data)
    492 {
    493 	npf_ioctl_table_t *nct = data;
    494 	int error;
    495 
    496 	switch (nct->nct_action) {
    497 	case NPF_IOCTL_TBLENT_ADD:
    498 		error = npf_table_add_v4cidr(NULL, nct->nct_tid,
    499 		    nct->nct_addr, nct->nct_mask);
    500 		break;
    501 	case NPF_IOCTL_TBLENT_REM:
    502 		error = npf_table_rem_v4cidr(NULL, nct->nct_tid,
    503 		    nct->nct_addr, nct->nct_mask);
    504 		break;
    505 	default:
    506 		/* XXX */
    507 		error = npf_table_match_v4addr(nct->nct_tid, nct->nct_addr);
    508 		if (error) {
    509 			error = EINVAL;
    510 		}
    511 	}
    512 	return error;
    513 }
    514