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