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