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