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