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