Home | History | Annotate | Line # | Download | only in npf
npf_ctl.c revision 1.38
      1 /*	$NetBSD: npf_ctl.c,v 1.38 2014/08/11 01:54:12 rmind Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009-2014 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.38 2014/08/11 01:54:12 rmind Exp $");
     41 
     42 #include <sys/param.h>
     43 #include <sys/conf.h>
     44 #include <sys/kmem.h>
     45 #include <net/bpf.h>
     46 
     47 #include <prop/proplib.h>
     48 
     49 #include "npf_impl.h"
     50 #include "npf_conn.h"
     51 
     52 #if defined(DEBUG) || defined(DIAGNOSTIC)
     53 #define	NPF_ERR_DEBUG(e) \
     54 	prop_dictionary_set_cstring_nocopy((e), "source-file", __FILE__); \
     55 	prop_dictionary_set_uint32((e), "source-line", __LINE__);
     56 #else
     57 #define	NPF_ERR_DEBUG(e)
     58 #endif
     59 
     60 /*
     61  * npfctl_switch: enable or disable packet inspection.
     62  */
     63 int
     64 npfctl_switch(void *data)
     65 {
     66 	const bool onoff = *(int *)data ? true : false;
     67 	int error;
     68 
     69 	if (onoff) {
     70 		/* Enable: add pfil hooks. */
     71 		error = npf_pfil_register(false);
     72 	} else {
     73 		/* Disable: remove pfil hooks. */
     74 		npf_pfil_unregister(false);
     75 		error = 0;
     76 	}
     77 	return error;
     78 }
     79 
     80 static int __noinline
     81 npf_mk_table_entries(npf_table_t *t, prop_array_t entries)
     82 {
     83 	prop_object_iterator_t eit;
     84 	prop_dictionary_t ent;
     85 	int error = 0;
     86 
     87 	/* Fill all the entries. */
     88 	eit = prop_array_iterator(entries);
     89 	while ((ent = prop_object_iterator_next(eit)) != NULL) {
     90 		const npf_addr_t *addr;
     91 		npf_netmask_t mask;
     92 		int alen;
     93 
     94 		/* Get address and mask.  Add a table entry. */
     95 		prop_object_t obj = prop_dictionary_get(ent, "addr");
     96 		addr = (const npf_addr_t *)prop_data_data_nocopy(obj);
     97 		prop_dictionary_get_uint8(ent, "mask", &mask);
     98 		alen = prop_data_size(obj);
     99 
    100 		error = npf_table_insert(t, alen, addr, mask);
    101 		if (error)
    102 			break;
    103 	}
    104 	prop_object_iterator_release(eit);
    105 	return error;
    106 }
    107 
    108 static int __noinline
    109 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables,
    110     prop_dictionary_t errdict)
    111 {
    112 	prop_object_iterator_t it;
    113 	prop_dictionary_t tbldict;
    114 	int error = 0;
    115 
    116 	/* Tables - array. */
    117 	if (prop_object_type(tables) != PROP_TYPE_ARRAY) {
    118 		NPF_ERR_DEBUG(errdict);
    119 		return EINVAL;
    120 	}
    121 
    122 	it = prop_array_iterator(tables);
    123 	while ((tbldict = prop_object_iterator_next(it)) != NULL) {
    124 		const char *name;
    125 		npf_table_t *t;
    126 		u_int tid;
    127 		int type;
    128 
    129 		/* Table - dictionary. */
    130 		if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) {
    131 			NPF_ERR_DEBUG(errdict);
    132 			error = EINVAL;
    133 			break;
    134 		}
    135 
    136 		/* Table name, ID and type.  Validate them. */
    137 		if (!prop_dictionary_get_cstring_nocopy(tbldict, "name", &name)) {
    138 			NPF_ERR_DEBUG(errdict);
    139 			error = EINVAL;
    140 			break;
    141 		}
    142 		prop_dictionary_get_uint32(tbldict, "id", &tid);
    143 		prop_dictionary_get_int32(tbldict, "type", &type);
    144 		error = npf_table_check(tblset, name, tid, type);
    145 		if (error) {
    146 			NPF_ERR_DEBUG(errdict);
    147 			break;
    148 		}
    149 
    150 		/* Get the entries or binary data. */
    151 		prop_array_t entries = prop_dictionary_get(tbldict, "entries");
    152 		if (prop_object_type(entries) != PROP_TYPE_ARRAY) {
    153 			NPF_ERR_DEBUG(errdict);
    154 			error = EINVAL;
    155 			break;
    156 		}
    157 		prop_object_t obj = prop_dictionary_get(tbldict, "data");
    158 		void *blob = prop_data_data(obj);
    159 		size_t size = prop_data_size(obj);
    160 
    161 		if (type == NPF_TABLE_CDB && (blob == NULL || size == 0)) {
    162 			NPF_ERR_DEBUG(errdict);
    163 			error = EINVAL;
    164 			break;
    165 		}
    166 		if (type == NPF_TABLE_HASH) {
    167 			size = 1024; /* XXX */
    168 		}
    169 
    170 		/* Create and insert the table. */
    171 		t = npf_table_create(name, tid, type, blob, size);
    172 		if (t == NULL) {
    173 			NPF_ERR_DEBUG(errdict);
    174 			error = ENOMEM;
    175 			break;
    176 		}
    177 		error = npf_tableset_insert(tblset, t);
    178 		KASSERT(error == 0);
    179 
    180 		if ((error = npf_mk_table_entries(t, entries)) != 0) {
    181 			NPF_ERR_DEBUG(errdict);
    182 			break;
    183 		}
    184 	}
    185 	prop_object_iterator_release(it);
    186 	/*
    187 	 * Note: in a case of error, caller will free the tableset.
    188 	 */
    189 	return error;
    190 }
    191 
    192 static npf_rproc_t *
    193 npf_mk_singlerproc(prop_dictionary_t rpdict)
    194 {
    195 	prop_object_iterator_t it;
    196 	prop_dictionary_t extdict;
    197 	prop_array_t extlist;
    198 	npf_rproc_t *rp;
    199 
    200 	extlist = prop_dictionary_get(rpdict, "extcalls");
    201 	if (prop_object_type(extlist) != PROP_TYPE_ARRAY) {
    202 		return NULL;
    203 	}
    204 
    205 	rp = npf_rproc_create(rpdict);
    206 	if (rp == NULL) {
    207 		return NULL;
    208 	}
    209 
    210 	it = prop_array_iterator(extlist);
    211 	while ((extdict = prop_object_iterator_next(it)) != NULL) {
    212 		const char *name;
    213 
    214 		if (!prop_dictionary_get_cstring_nocopy(extdict,
    215 		    "name", &name) || npf_ext_construct(name, rp, extdict)) {
    216 			npf_rproc_release(rp);
    217 			rp = NULL;
    218 			break;
    219 		}
    220 	}
    221 	prop_object_iterator_release(it);
    222 	return rp;
    223 }
    224 
    225 static int __noinline
    226 npf_mk_rprocs(npf_rprocset_t *rpset, prop_array_t rprocs,
    227     prop_dictionary_t errdict)
    228 {
    229 	prop_object_iterator_t it;
    230 	prop_dictionary_t rpdict;
    231 	int error = 0;
    232 
    233 	it = prop_array_iterator(rprocs);
    234 	while ((rpdict = prop_object_iterator_next(it)) != NULL) {
    235 		npf_rproc_t *rp;
    236 
    237 		if ((rp = npf_mk_singlerproc(rpdict)) == NULL) {
    238 			NPF_ERR_DEBUG(errdict);
    239 			error = EINVAL;
    240 			break;
    241 		}
    242 		npf_rprocset_insert(rpset, rp);
    243 	}
    244 	prop_object_iterator_release(it);
    245 	return error;
    246 }
    247 
    248 static npf_alg_t *
    249 npf_mk_singlealg(prop_dictionary_t aldict)
    250 {
    251 	const char *name;
    252 
    253 	if (!prop_dictionary_get_cstring_nocopy(aldict, "name", &name))
    254 		return NULL;
    255 	return npf_alg_construct(name);
    256 }
    257 
    258 static int __noinline
    259 npf_mk_algs(prop_array_t alglist, prop_dictionary_t errdict)
    260 {
    261 	prop_object_iterator_t it;
    262 	prop_dictionary_t nadict;
    263 	int error = 0;
    264 
    265 	it = prop_array_iterator(alglist);
    266 	while ((nadict = prop_object_iterator_next(it)) != NULL) {
    267 		if (npf_mk_singlealg(nadict) == NULL) {
    268 			NPF_ERR_DEBUG(errdict);
    269 			error = EINVAL;
    270 			break;
    271 		}
    272 	}
    273 	prop_object_iterator_release(it);
    274 	return error;
    275 }
    276 
    277 static int __noinline
    278 npf_mk_code(prop_object_t obj, int type, void **code, size_t *csize,
    279     prop_dictionary_t errdict)
    280 {
    281 	const void *cptr;
    282 	size_t clen;
    283 	void *bc;
    284 
    285 	if (type != NPF_CODE_BPF) {
    286 		return ENOTSUP;
    287 	}
    288 	cptr = prop_data_data_nocopy(obj);
    289 	if (cptr == NULL || (clen = prop_data_size(obj)) == 0) {
    290 		NPF_ERR_DEBUG(errdict);
    291 		return EINVAL;
    292 	}
    293 	if (!npf_bpf_validate(cptr, clen)) {
    294 		NPF_ERR_DEBUG(errdict);
    295 		return EINVAL;
    296 	}
    297 	bc = kmem_alloc(clen, KM_SLEEP);
    298 	memcpy(bc, cptr, clen);
    299 
    300 	*code = bc;
    301 	*csize = clen;
    302 	return 0;
    303 }
    304 
    305 static int __noinline
    306 npf_mk_singlerule(prop_dictionary_t rldict, npf_rprocset_t *rpset,
    307     npf_rule_t **rlret, prop_dictionary_t errdict)
    308 {
    309 	npf_rule_t *rl;
    310 	const char *rname;
    311 	prop_object_t obj;
    312 	int p, error = 0;
    313 
    314 	/* Rule - dictionary. */
    315 	if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) {
    316 		NPF_ERR_DEBUG(errdict);
    317 		return EINVAL;
    318 	}
    319 	if ((rl = npf_rule_alloc(rldict)) == NULL) {
    320 		NPF_ERR_DEBUG(errdict);
    321 		return EINVAL;
    322 	}
    323 
    324 	/* Assign rule procedure, if any. */
    325 	if (prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rname)) {
    326 		npf_rproc_t *rp;
    327 
    328 		if (rpset == NULL) {
    329 			error = EINVAL;
    330 			goto err;
    331 		}
    332 		if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) {
    333 			NPF_ERR_DEBUG(errdict);
    334 			error = EINVAL;
    335 			goto err;
    336 		}
    337 		npf_rule_setrproc(rl, rp);
    338 	}
    339 
    340 	/* Filter code (binary data). */
    341 	if ((obj = prop_dictionary_get(rldict, "code")) != NULL) {
    342 		int type;
    343 		size_t len;
    344 		void *code;
    345 
    346 		prop_dictionary_get_int32(rldict, "code-type", &type);
    347 		error = npf_mk_code(obj, type, &code, &len, errdict);
    348 		if (error) {
    349 			goto err;
    350 		}
    351 		npf_rule_setcode(rl, type, code, len);
    352 	}
    353 
    354 	*rlret = rl;
    355 	return 0;
    356 err:
    357 	npf_rule_free(rl);
    358 	prop_dictionary_get_int32(rldict, "prio", &p); /* XXX */
    359 	prop_dictionary_set_int32(errdict, "id", p);
    360 	return error;
    361 }
    362 
    363 static int __noinline
    364 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules, npf_rprocset_t *rpset,
    365     prop_dictionary_t errdict)
    366 {
    367 	prop_object_iterator_t it;
    368 	prop_dictionary_t rldict;
    369 	int error;
    370 
    371 	if (prop_object_type(rules) != PROP_TYPE_ARRAY) {
    372 		NPF_ERR_DEBUG(errdict);
    373 		return EINVAL;
    374 	}
    375 
    376 	error = 0;
    377 	it = prop_array_iterator(rules);
    378 	while ((rldict = prop_object_iterator_next(it)) != NULL) {
    379 		npf_rule_t *rl = NULL;
    380 
    381 		/* Generate a single rule. */
    382 		error = npf_mk_singlerule(rldict, rpset, &rl, errdict);
    383 		if (error) {
    384 			break;
    385 		}
    386 		npf_ruleset_insert(rlset, rl);
    387 	}
    388 	prop_object_iterator_release(it);
    389 	/*
    390 	 * Note: in a case of error, caller will free the ruleset.
    391 	 */
    392 	return error;
    393 }
    394 
    395 static int __noinline
    396 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist,
    397     prop_dictionary_t errdict)
    398 {
    399 	prop_object_iterator_t it;
    400 	prop_dictionary_t natdict;
    401 	int error;
    402 
    403 	/* NAT policies - array. */
    404 	if (prop_object_type(natlist) != PROP_TYPE_ARRAY) {
    405 		NPF_ERR_DEBUG(errdict);
    406 		return EINVAL;
    407 	}
    408 
    409 	error = 0;
    410 	it = prop_array_iterator(natlist);
    411 	while ((natdict = prop_object_iterator_next(it)) != NULL) {
    412 		npf_rule_t *rl = NULL;
    413 		npf_natpolicy_t *np;
    414 
    415 		/* NAT policy - dictionary. */
    416 		if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
    417 			NPF_ERR_DEBUG(errdict);
    418 			error = EINVAL;
    419 			break;
    420 		}
    421 
    422 		/*
    423 		 * NAT policies are standard rules, plus additional
    424 		 * information for translation.  Make a rule.
    425 		 */
    426 		error = npf_mk_singlerule(natdict, NULL, &rl, errdict);
    427 		if (error) {
    428 			break;
    429 		}
    430 		npf_ruleset_insert(nset, rl);
    431 
    432 		/* If rule is named, it is a group with NAT policies. */
    433 		if (prop_dictionary_get(natdict, "name") &&
    434 		    prop_dictionary_get(natdict, "subrules")) {
    435 			continue;
    436 		}
    437 
    438 		/* Allocate a new NAT policy and assign to the rule. */
    439 		np = npf_nat_newpolicy(natdict, nset);
    440 		if (np == NULL) {
    441 			NPF_ERR_DEBUG(errdict);
    442 			error = ENOMEM;
    443 			break;
    444 		}
    445 		npf_rule_setnat(rl, np);
    446 	}
    447 	prop_object_iterator_release(it);
    448 	/*
    449 	 * Note: in a case of error, caller will free entire NAT ruleset
    450 	 * with assigned NAT policies.
    451 	 */
    452 	return error;
    453 }
    454 
    455 /*
    456  * npf_mk_connlist: import a list of connections and load them.
    457  */
    458 static int __noinline
    459 npf_mk_connlist(prop_array_t conlist, npf_ruleset_t *natlist,
    460     npf_conndb_t **conndb, prop_dictionary_t errdict)
    461 {
    462 	prop_dictionary_t condict;
    463 	prop_object_iterator_t it;
    464 	npf_conndb_t *cd;
    465 	int error;
    466 
    467 	/* Connection list - array */
    468 	if (prop_object_type(conlist) != PROP_TYPE_ARRAY) {
    469 		NPF_ERR_DEBUG(errdict);
    470 		return EINVAL;
    471 	}
    472 
    473 	/* Create a connection database. */
    474 	cd = npf_conndb_create();
    475 
    476 	error = 0;
    477 	it = prop_array_iterator(conlist);
    478 	while ((condict = prop_object_iterator_next(it)) != NULL) {
    479 		/* Connection - dictionary. */
    480 		if (prop_object_type(condict) != PROP_TYPE_DICTIONARY) {
    481 			NPF_ERR_DEBUG(errdict);
    482 			error = EINVAL;
    483 			break;
    484 		}
    485 		/* Construct and insert real connection structure. */
    486 		error = npf_conn_import(cd, condict, natlist);
    487 		if (error) {
    488 			NPF_ERR_DEBUG(errdict);
    489 			break;
    490 		}
    491 	}
    492 	prop_object_iterator_release(it);
    493 	if (error) {
    494 		npf_conn_gc(cd, true, false);
    495 		npf_conndb_destroy(cd);
    496 	} else {
    497 		*conndb = cd;
    498 	}
    499 	return error;
    500 }
    501 
    502 /*
    503  * npfctl_load: store passed data i.e. update settings, create passed
    504  * tables, rules and atomically activate all them.
    505  */
    506 int
    507 npfctl_load(u_long cmd, void *data)
    508 {
    509 	struct plistref *pref = data;
    510 	prop_dictionary_t npf_dict, errdict;
    511 	prop_array_t alglist, natlist, tables, rprocs, rules, conlist;
    512 	npf_tableset_t *tblset = NULL;
    513 	npf_rprocset_t *rpset = NULL;
    514 	npf_ruleset_t *rlset = NULL;
    515 	npf_ruleset_t *nset = NULL;
    516 	npf_conndb_t *conndb = NULL;
    517 	uint32_t ver = 0;
    518 	size_t nitems;
    519 	bool flush;
    520 	int error;
    521 
    522 	/* Retrieve the dictionary. */
    523 #ifndef _NPF_TESTING
    524 	error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_dict);
    525 	if (error)
    526 		return error;
    527 #else
    528 	npf_dict = (prop_dictionary_t)pref;
    529 #endif
    530 
    531 	/* Dictionary for error reporting and version check. */
    532 	errdict = prop_dictionary_create();
    533 	prop_dictionary_get_uint32(npf_dict, "version", &ver);
    534 	if (ver != NPF_VERSION) {
    535 		error = EPROGMISMATCH;
    536 		goto fail;
    537 	}
    538 
    539 	/* ALGs. */
    540 	alglist = prop_dictionary_get(npf_dict, "algs");
    541 	error = npf_mk_algs(alglist, errdict);
    542 	if (error) {
    543 		goto fail;
    544 	}
    545 
    546 	/* NAT policies. */
    547 	natlist = prop_dictionary_get(npf_dict, "nat");
    548 	if ((nitems = prop_array_count(natlist)) > NPF_MAX_RULES) {
    549 		goto fail;
    550 	}
    551 
    552 	nset = npf_ruleset_create(nitems);
    553 	error = npf_mk_natlist(nset, natlist, errdict);
    554 	if (error) {
    555 		goto fail;
    556 	}
    557 
    558 	/* Tables. */
    559 	tables = prop_dictionary_get(npf_dict, "tables");
    560 	if ((nitems = prop_array_count(tables)) > NPF_MAX_TABLES) {
    561 		goto fail;
    562 	}
    563 	tblset = npf_tableset_create(nitems);
    564 	error = npf_mk_tables(tblset, tables, errdict);
    565 	if (error) {
    566 		goto fail;
    567 	}
    568 
    569 	/* Rule procedures. */
    570 	rprocs = prop_dictionary_get(npf_dict, "rprocs");
    571 	if ((nitems = prop_array_count(rprocs)) > NPF_MAX_RPROCS) {
    572 		goto fail;
    573 	}
    574 	rpset = npf_rprocset_create();
    575 	error = npf_mk_rprocs(rpset, rprocs, errdict);
    576 	if (error) {
    577 		goto fail;
    578 	}
    579 
    580 	/* Rules. */
    581 	rules = prop_dictionary_get(npf_dict, "rules");
    582 	if ((nitems = prop_array_count(rules)) > NPF_MAX_RULES) {
    583 		goto fail;
    584 	}
    585 
    586 	rlset = npf_ruleset_create(nitems);
    587 	error = npf_mk_rules(rlset, rules, rpset, errdict);
    588 	if (error) {
    589 		goto fail;
    590 	}
    591 
    592 	/* Connections (if loading any). */
    593 	if ((conlist = prop_dictionary_get(npf_dict, "conn-list")) != NULL) {
    594 		error = npf_mk_connlist(conlist, nset, &conndb, errdict);
    595 		if (error) {
    596 			goto fail;
    597 		}
    598 	}
    599 
    600 	flush = false;
    601 	prop_dictionary_get_bool(npf_dict, "flush", &flush);
    602 
    603 	/*
    604 	 * Finally - perform the load.
    605 	 */
    606 	npf_config_load(rlset, tblset, nset, rpset, conndb, flush);
    607 
    608 	/* Done.  Since data is consumed now, we shall not destroy it. */
    609 	tblset = NULL;
    610 	rpset = NULL;
    611 	rlset = NULL;
    612 	nset = NULL;
    613 fail:
    614 	/*
    615 	 * Note: destroy rulesets first, to drop references to the tableset.
    616 	 */
    617 	KASSERT(error == 0 || (nset || rpset || rlset || tblset));
    618 	if (nset) {
    619 		npf_ruleset_destroy(nset);
    620 	}
    621 	if (rlset) {
    622 		npf_ruleset_destroy(rlset);
    623 	}
    624 	if (rpset) {
    625 		npf_rprocset_destroy(rpset);
    626 	}
    627 	if (tblset) {
    628 		npf_tableset_destroy(tblset);
    629 	}
    630 	prop_object_release(npf_dict);
    631 
    632 	/* Error report. */
    633 #ifndef _NPF_TESTING
    634 	prop_dictionary_set_int32(errdict, "errno", error);
    635 	prop_dictionary_copyout_ioctl(pref, cmd, errdict);
    636 	prop_object_release(errdict);
    637 	error = 0;
    638 #endif
    639 	return error;
    640 }
    641 
    642 /*
    643  * npfctl_save: export the config dictionary as it was submitted,
    644  * including the current snapshot of the connections.  Additionally,
    645  * indicate whether the ruleset is currently active.
    646  */
    647 int
    648 npfctl_save(u_long cmd, void *data)
    649 {
    650 	struct plistref *pref = data;
    651 	prop_array_t rulelist, natlist, tables, rprocs, conlist;
    652 	prop_dictionary_t npf_dict = NULL;
    653 	int error;
    654 
    655 	rulelist = prop_array_create();
    656 	natlist = prop_array_create();
    657 	tables = prop_array_create();
    658 	rprocs = prop_array_create();
    659 	conlist = prop_array_create();
    660 
    661 	/*
    662 	 * Serialise the connections and NAT policies.
    663 	 */
    664 	npf_config_enter();
    665 	error = npf_conndb_export(conlist);
    666 	if (error) {
    667 		goto out;
    668 	}
    669 	error = npf_ruleset_export(npf_config_ruleset(), rulelist);
    670 	if (error) {
    671 		goto out;
    672 	}
    673 	error = npf_ruleset_export(npf_config_natset(), natlist);
    674 	if (error) {
    675 		goto out;
    676 	}
    677 	error = npf_tableset_export(npf_config_tableset(), tables);
    678 	if (error) {
    679 		goto out;
    680 	}
    681 	error = npf_rprocset_export(npf_config_rprocs(), rprocs);
    682 	if (error) {
    683 		goto out;
    684 	}
    685 	npf_dict = prop_dictionary_create();
    686 	prop_dictionary_set_uint32(npf_dict, "version", NPF_VERSION);
    687 	prop_dictionary_set_and_rel(npf_dict, "rules", rulelist);
    688 	prop_dictionary_set_and_rel(npf_dict, "nat", natlist);
    689 	prop_dictionary_set_and_rel(npf_dict, "tables", tables);
    690 	prop_dictionary_set_and_rel(npf_dict, "rprocs", rprocs);
    691 	prop_dictionary_set_and_rel(npf_dict, "conn-list", conlist);
    692 	prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p());
    693 	error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict);
    694 out:
    695 	npf_config_exit();
    696 
    697 	if (!npf_dict) {
    698 		prop_object_release(rulelist);
    699 		prop_object_release(natlist);
    700 		prop_object_release(tables);
    701 		prop_object_release(rprocs);
    702 		prop_object_release(conlist);
    703 	} else {
    704 		prop_object_release(npf_dict);
    705 	}
    706 	return error;
    707 }
    708 
    709 /*
    710  * npfctl_rule: add or remove dynamic rules in the specified ruleset.
    711  */
    712 int
    713 npfctl_rule(u_long cmd, void *data)
    714 {
    715 	struct plistref *pref = data;
    716 	prop_dictionary_t npf_rule, retdict = NULL;
    717 	npf_ruleset_t *rlset;
    718 	npf_rule_t *rl = NULL;
    719 	const char *ruleset_name;
    720 	uint32_t rcmd = 0;
    721 	int error;
    722 
    723 	error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_rule);
    724 	if (error) {
    725 		return error;
    726 	}
    727 	prop_dictionary_get_uint32(npf_rule, "command", &rcmd);
    728 	if (!prop_dictionary_get_cstring_nocopy(npf_rule,
    729 	    "ruleset-name", &ruleset_name)) {
    730 		error = EINVAL;
    731 		goto out;
    732 	}
    733 
    734 	if (rcmd == NPF_CMD_RULE_ADD) {
    735 		retdict = prop_dictionary_create();
    736 		if (npf_mk_singlerule(npf_rule, NULL, &rl, retdict) != 0) {
    737 			error = EINVAL;
    738 			goto out;
    739 		}
    740 	}
    741 
    742 	npf_config_enter();
    743 	rlset = npf_config_ruleset();
    744 
    745 	switch (rcmd) {
    746 	case NPF_CMD_RULE_ADD: {
    747 		if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) {
    748 			/* Success. */
    749 			uint64_t id = npf_rule_getid(rl);
    750 			prop_dictionary_set_uint64(retdict, "id", id);
    751 			rl = NULL;
    752 		}
    753 		break;
    754 	}
    755 	case NPF_CMD_RULE_REMOVE: {
    756 		uint64_t id;
    757 
    758 		if (!prop_dictionary_get_uint64(npf_rule, "id", &id)) {
    759 			error = EINVAL;
    760 			break;
    761 		}
    762 		error = npf_ruleset_remove(rlset, ruleset_name, id);
    763 		break;
    764 	}
    765 	case NPF_CMD_RULE_REMKEY: {
    766 		prop_object_t obj = prop_dictionary_get(npf_rule, "key");
    767 		const void *key = prop_data_data_nocopy(obj);
    768 		size_t len = prop_data_size(obj);
    769 
    770 		if (len == 0 || len > NPF_RULE_MAXKEYLEN) {
    771 			error = EINVAL;
    772 			break;
    773 		}
    774 		error = npf_ruleset_remkey(rlset, ruleset_name, key, len);
    775 		break;
    776 	}
    777 	case NPF_CMD_RULE_LIST: {
    778 		retdict = npf_ruleset_list(rlset, ruleset_name);
    779 		break;
    780 	}
    781 	case NPF_CMD_RULE_FLUSH: {
    782 		error = npf_ruleset_flush(rlset, ruleset_name);
    783 		break;
    784 	}
    785 	default:
    786 		error = EINVAL;
    787 		break;
    788 	}
    789 
    790 	/* Destroy any removed rules. */
    791 	if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) {
    792 		npf_config_sync();
    793 		npf_ruleset_gc(rlset);
    794 	}
    795 	npf_config_exit();
    796 
    797 	if (rl) {
    798 		npf_rule_free(rl);
    799 	}
    800 out:
    801 	if (retdict) {
    802 		prop_object_release(npf_rule);
    803 		prop_dictionary_copyout_ioctl(pref, cmd, retdict);
    804 		prop_object_release(retdict);
    805 	}
    806 	return error;
    807 }
    808 
    809 /*
    810  * npfctl_table: add, remove or query entries in the specified table.
    811  *
    812  * For maximum performance, interface is avoiding proplib(3)'s overhead.
    813  */
    814 int
    815 npfctl_table(void *data)
    816 {
    817 	const npf_ioctl_table_t *nct = data;
    818 	char tname[NPF_TABLE_MAXNAMELEN];
    819 	npf_tableset_t *ts;
    820 	npf_table_t *t;
    821 	int s, error;
    822 
    823 	error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL);
    824 	if (error) {
    825 		return error;
    826 	}
    827 
    828 	s = npf_config_read_enter(); /* XXX */
    829 	ts = npf_config_tableset();
    830 	if ((t = npf_tableset_getbyname(ts, tname)) == NULL) {
    831 		npf_config_read_exit(s);
    832 		return EINVAL;
    833 	}
    834 
    835 	switch (nct->nct_cmd) {
    836 	case NPF_CMD_TABLE_LOOKUP:
    837 		error = npf_table_lookup(t, nct->nct_data.ent.alen,
    838 		    &nct->nct_data.ent.addr);
    839 		break;
    840 	case NPF_CMD_TABLE_ADD:
    841 		error = npf_table_insert(t, nct->nct_data.ent.alen,
    842 		    &nct->nct_data.ent.addr, nct->nct_data.ent.mask);
    843 		break;
    844 	case NPF_CMD_TABLE_REMOVE:
    845 		error = npf_table_remove(t, nct->nct_data.ent.alen,
    846 		    &nct->nct_data.ent.addr, nct->nct_data.ent.mask);
    847 		break;
    848 	case NPF_CMD_TABLE_LIST:
    849 		error = npf_table_list(t, nct->nct_data.buf.buf,
    850 		    nct->nct_data.buf.len);
    851 		break;
    852 	case NPF_CMD_TABLE_FLUSH:
    853 		error = npf_table_flush(t);
    854 		break;
    855 	default:
    856 		error = EINVAL;
    857 		break;
    858 	}
    859 	npf_config_read_exit(s);
    860 
    861 	return error;
    862 }
    863