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