Home | History | Annotate | Line # | Download | only in libnpf
      1 /*-
      2  * Copyright (c) 2010-2025 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 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.56 2025/07/01 19:55:15 joe Exp $");
     32 
     33 #include <sys/types.h>
     34 #include <sys/mman.h>
     35 #include <sys/stat.h>
     36 #if !defined(_NPF_STANDALONE)
     37 #include <sys/ioctl.h>
     38 #endif
     39 #include <netinet/in_systm.h>
     40 #include <netinet/in.h>
     41 #include <net/if.h>
     42 
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <assert.h>
     46 #include <unistd.h>
     47 #include <errno.h>
     48 #include <err.h>
     49 
     50 #include <nv.h>
     51 #include <dnv.h>
     52 
     53 #include <cdbw.h>
     54 
     55 #define	_NPF_PRIVATE
     56 #include "npf.h"
     57 
     58 struct nl_rule {
     59 	nvlist_t *	rule_dict;
     60 };
     61 
     62 struct nl_rproc {
     63 	nvlist_t *	rproc_dict;
     64 };
     65 
     66 struct nl_table {
     67 	nvlist_t *	table_dict;
     68 };
     69 
     70 struct nl_alg {
     71 	nvlist_t *	alg_dict;
     72 };
     73 
     74 struct nl_ext {
     75 	nvlist_t *	ext_dict;
     76 };
     77 
     78 struct nl_config {
     79 	nvlist_t *	ncf_dict;
     80 
     81 	/* Temporary rule list. */
     82 	nvlist_t **	ncf_rule_list;
     83 	unsigned	ncf_rule_count;
     84 
     85 	/* Iterators. */
     86 	unsigned	ncf_reduce[16];
     87 	unsigned	ncf_nlevel;
     88 
     89 	nl_rule_t	ncf_cur_rule;
     90 	nl_table_t	ncf_cur_table;
     91 	nl_rproc_t	ncf_cur_rproc;
     92 };
     93 
     94 /*
     95  * Various helper routines.
     96  */
     97 
     98 static bool
     99 _npf_add_addr(nvlist_t *nvl, const char *name, int af, const npf_addr_t *addr)
    100 {
    101 	size_t sz;
    102 
    103 	if (af == AF_INET) {
    104 		sz = sizeof(struct in_addr);
    105 	} else if (af == AF_INET6) {
    106 		sz = sizeof(struct in6_addr);
    107 	} else {
    108 		return false;
    109 	}
    110 	nvlist_add_binary(nvl, name, addr, sz);
    111 	return nvlist_error(nvl) == 0;
    112 }
    113 
    114 static unsigned
    115 _npf_get_addr(const nvlist_t *nvl, const char *name, npf_addr_t *addr)
    116 {
    117 	const void *d;
    118 	size_t sz = 0;
    119 
    120 	d = nvlist_get_binary(nvl, name, &sz);
    121 	switch (sz) {
    122 	case sizeof(struct in_addr):
    123 	case sizeof(struct in6_addr):
    124 		memcpy(addr, d, sz);
    125 		return (unsigned)sz;
    126 	}
    127 	return 0;
    128 }
    129 
    130 static bool
    131 _npf_dataset_lookup(const nvlist_t *dict, const char *dataset,
    132     const char *key, const char *name)
    133 {
    134 	const nvlist_t * const *items;
    135 	size_t nitems;
    136 
    137 	if (!nvlist_exists_nvlist_array(dict, dataset)) {
    138 		return false;
    139 	}
    140 	items = nvlist_get_nvlist_array(dict, dataset, &nitems);
    141 	for (unsigned i = 0; i < nitems; i++) {
    142 		const char *item_name;
    143 
    144 		item_name = dnvlist_get_string(items[i], key, NULL);
    145 		if (item_name && strcmp(item_name, name) == 0) {
    146 			return true;
    147 		}
    148 	}
    149 	return false;
    150 }
    151 
    152 static const nvlist_t *
    153 _npf_dataset_getelement(nvlist_t *dict, const char *dataset, unsigned i)
    154 {
    155 	const nvlist_t * const *items;
    156 	size_t nitems;
    157 
    158 	if (!nvlist_exists_nvlist_array(dict, dataset)) {
    159 		return NULL;
    160 	}
    161 	items = nvlist_get_nvlist_array(dict, dataset, &nitems);
    162 	if (i < nitems) {
    163 		return items[i];
    164 	}
    165 	return NULL;
    166 }
    167 
    168 /*
    169  * _npf_rules_process: transform the ruleset representing nested rules
    170  * with sublists into a single array with skip-to marks.
    171  */
    172 static void
    173 _npf_rules_process(nl_config_t *ncf, nvlist_t *dict, const char *key)
    174 {
    175 	nvlist_t **items;
    176 	size_t nitems;
    177 
    178 	if (!nvlist_exists_nvlist_array(dict, key)) {
    179 		return;
    180 	}
    181 	items = nvlist_take_nvlist_array(dict, key, &nitems);
    182 	for (unsigned i = 0; i < nitems; i++) {
    183 		nvlist_t *rule_dict = items[i];
    184 		size_t len = (ncf->ncf_rule_count + 1) * sizeof(nvlist_t *);
    185 		void *p = realloc(ncf->ncf_rule_list, len);
    186 
    187 		/*
    188 		 * - Add rule to the transformed array.
    189 		 * - Process subrules recursively.
    190 		 * - Add the skip-to position.
    191 		 */
    192 		ncf->ncf_rule_list = p;
    193 		ncf->ncf_rule_list[ncf->ncf_rule_count] = rule_dict;
    194 		ncf->ncf_rule_count++;
    195 
    196 		if (nvlist_exists_nvlist_array(rule_dict, "subrules")) {
    197 			unsigned idx;
    198 
    199 			_npf_rules_process(ncf, rule_dict, "subrules");
    200 			idx = ncf->ncf_rule_count; // post-recursion index
    201 			nvlist_add_number(rule_dict, "skip-to", idx);
    202 		}
    203 		assert(nvlist_error(rule_dict) == 0);
    204 	}
    205 	free(items);
    206 }
    207 
    208 /*
    209  * _npf_init_error: initialize the error structure with the message
    210  * from the current error number
    211  */
    212 static int
    213 _npf_init_error(int error, npf_error_t *errinfo)
    214 {
    215 	if (error && errinfo) {
    216 		memset(errinfo, 0, sizeof(*errinfo));
    217 		errinfo->error_msg = strerror(error);
    218 	}
    219 	return error;
    220 }
    221 
    222 /*
    223  * _npf_extract_error: check the error number field and extract the
    224  * error details into the npf_error_t structure.
    225  */
    226 static int
    227 _npf_extract_error(nvlist_t *resp, npf_error_t *errinfo)
    228 {
    229 	int error;
    230 
    231 	error = dnvlist_get_number(resp, "errno", 0);
    232 	if (error && errinfo) {
    233 		memset(errinfo, 0, sizeof(npf_error_t));
    234 
    235 		errinfo->id = dnvlist_get_number(resp, "id", 0);
    236 		errinfo->error_msg =
    237 		    dnvlist_take_string(resp, "error-msg", NULL);
    238 		errinfo->source_file =
    239 		    dnvlist_take_string(resp, "source-file", NULL);
    240 		errinfo->source_line =
    241 		    dnvlist_take_number(resp, "source-line", 0);
    242 	}
    243 	return error;
    244 }
    245 
    246 /*
    247  * npf_xfer_fd: transfer the given request and receive a response.
    248  *
    249  * => Sets the 'operation' key on the 'req' dictionary.
    250  * => On success: returns 0 and valid nvlist in 'resp'.
    251  * => On failure: returns an error number.
    252  */
    253 static int
    254 _npf_xfer_fd(int fd, unsigned long cmd, nvlist_t *req, nvlist_t **resp)
    255 {
    256 	struct stat st;
    257 	int kernver;
    258 
    259 	/*
    260 	 * Set the NPF version and operation.
    261 	 */
    262 	if (!nvlist_exists(req, "version")) {
    263 		nvlist_add_number(req, "version", NPF_VERSION);
    264 	}
    265 	nvlist_add_number(req, "operation", cmd);
    266 
    267 	/*
    268 	 * Determine the type of file descriptor:
    269 	 * - If socket, then perform nvlist_send()/nvlist_recv().
    270 	 * - If a character device, then use ioctl.
    271 	 */
    272 	if (fstat(fd, &st) == -1) {
    273 		goto err;
    274 	}
    275 	switch (st.st_mode & S_IFMT) {
    276 #if !defined(__NetBSD__)
    277 	case S_IFSOCK:
    278 		if (nvlist_send(fd, req) == -1) {
    279 			goto err;
    280 		}
    281 		if (resp && (*resp = nvlist_recv(fd, 0)) == NULL) {
    282 			goto err;
    283 		}
    284 		break;
    285 #endif
    286 #if !defined(_NPF_STANDALONE)
    287 	case S_IFBLK:
    288 	case S_IFCHR:
    289 		if (ioctl(fd, IOC_NPF_VERSION, &kernver) == -1) {
    290 			goto err;
    291 		}
    292 		if (kernver != NPF_VERSION) {
    293 			errno = EPROGMISMATCH;
    294 			goto err;
    295 		}
    296 		if (nvlist_xfer_ioctl(fd, cmd, req, resp) == -1) {
    297 			goto err;
    298 		}
    299 		break;
    300 #else
    301 		(void)kernver;
    302 #endif
    303 	default:
    304 		errno = ENOTSUP;
    305 		goto err;
    306 	}
    307 	return 0;
    308 err:
    309 	return errno ? errno : EIO;
    310 }
    311 
    312 /*
    313  * npf_xfer_fd_errno: same as npf_xfer_fd(), but:
    314  *
    315  * => After successful retrieval of the response, inspects it, extracts
    316  *    the 'errno' value (if any) and returns it.
    317  * => Destroys the response.
    318  */
    319 static int
    320 _npf_xfer_fd_errno(int fd, unsigned long cmd, nvlist_t *req)
    321 {
    322 	nvlist_t *resp;
    323 	int error;
    324 
    325 	error = _npf_xfer_fd(fd, cmd, req, &resp);
    326 	if (error) {
    327 		return error;
    328 	}
    329 	error = _npf_extract_error(resp, NULL);
    330 	nvlist_destroy(resp);
    331 	return error;
    332 }
    333 
    334 /*
    335  * CONFIGURATION INTERFACE.
    336  */
    337 
    338 nl_config_t *
    339 npf_config_create(void)
    340 {
    341 	nl_config_t *ncf;
    342 
    343 	ncf = calloc(1, sizeof(nl_config_t));
    344 	if (!ncf) {
    345 		return NULL;
    346 	}
    347 	ncf->ncf_dict = nvlist_create(0);
    348 	nvlist_add_number(ncf->ncf_dict, "version", NPF_VERSION);
    349 	return ncf;
    350 }
    351 
    352 int
    353 npf_config_submit(nl_config_t *ncf, int fd, npf_error_t *errinfo)
    354 {
    355 	nvlist_t *resp = NULL;
    356 	int error;
    357 
    358 	/* Ensure the config is built. */
    359 	(void)npf_config_build(ncf);
    360 
    361 	error = _npf_xfer_fd(fd, IOC_NPF_LOAD, ncf->ncf_dict, &resp);
    362 	if (error) {
    363 		return _npf_init_error(errno, errinfo);
    364 	}
    365 	error = _npf_extract_error(resp, errinfo);
    366 	nvlist_destroy(resp);
    367 	return error;
    368 }
    369 
    370 nl_config_t *
    371 npf_config_retrieve(int fd)
    372 {
    373 	nl_config_t *ncf;
    374 	nvlist_t *req, *resp = NULL;
    375 	int error;
    376 
    377 	ncf = calloc(1, sizeof(nl_config_t));
    378 	if (!ncf) {
    379 		return NULL;
    380 	}
    381 
    382 	req = nvlist_create(0);
    383 	error = _npf_xfer_fd(fd, IOC_NPF_SAVE, req, &resp);
    384 	nvlist_destroy(req);
    385 
    386 	if (error || _npf_extract_error(resp, NULL) != 0) {
    387 		nvlist_destroy(resp);
    388 		free(ncf);
    389 		return NULL;
    390 	}
    391 	ncf->ncf_dict = resp;
    392 	return ncf;
    393 }
    394 
    395 void *
    396 npf_config_export(nl_config_t *ncf, size_t *length)
    397 {
    398 	/* Ensure the config is built. */
    399 	(void)npf_config_build(ncf);
    400 	return nvlist_pack(ncf->ncf_dict, length);
    401 }
    402 
    403 nl_config_t *
    404 npf_config_import(const void *blob, size_t len)
    405 {
    406 	nl_config_t *ncf;
    407 
    408 	ncf = calloc(1, sizeof(nl_config_t));
    409 	if (!ncf) {
    410 		return NULL;
    411 	}
    412 	ncf->ncf_dict = nvlist_unpack(blob, len, 0);
    413 	if (!ncf->ncf_dict) {
    414 		free(ncf);
    415 		return NULL;
    416 	}
    417 	return ncf;
    418 }
    419 
    420 int
    421 npf_config_flush(int fd)
    422 {
    423 	nl_config_t *ncf;
    424 	npf_error_t errinfo;
    425 	int error;
    426 
    427 	ncf = npf_config_create();
    428 	if (!ncf) {
    429 		return ENOMEM;
    430 	}
    431 	nvlist_add_bool(ncf->ncf_dict, "flush", true);
    432 	error = npf_config_submit(ncf, fd, &errinfo);
    433 	npf_config_destroy(ncf);
    434 	return error;
    435 }
    436 
    437 bool
    438 npf_config_active_p(nl_config_t *ncf)
    439 {
    440 	return dnvlist_get_bool(ncf->ncf_dict, "active", false);
    441 }
    442 
    443 bool
    444 npf_config_loaded_p(nl_config_t *ncf)
    445 {
    446 	return nvlist_exists_nvlist_array(ncf->ncf_dict, "rules");
    447 }
    448 
    449 const void *
    450 npf_config_build(nl_config_t *ncf)
    451 {
    452 	_npf_rules_process(ncf, ncf->ncf_dict, "__rules");
    453 	if (ncf->ncf_rule_list) {
    454 		/* Set the transformed ruleset. */
    455 		nvlist_move_nvlist_array(ncf->ncf_dict, "rules",
    456 		    ncf->ncf_rule_list, ncf->ncf_rule_count);
    457 
    458 		/* Clear the temporary list. */
    459 		ncf->ncf_rule_list = NULL;
    460 		ncf->ncf_rule_count = 0;
    461 	}
    462 	assert(nvlist_error(ncf->ncf_dict) == 0);
    463 	return (void *)ncf->ncf_dict;
    464 }
    465 
    466 void
    467 npf_config_destroy(nl_config_t *ncf)
    468 {
    469 	nvlist_destroy(ncf->ncf_dict);
    470 	free(ncf);
    471 }
    472 
    473 /*
    474  * PARAMETERS.
    475  */
    476 
    477 int
    478 npf_param_get(nl_config_t *ncf, const char *name, int *valp)
    479 {
    480 	const nvlist_t *params;
    481 
    482 	params = dnvlist_get_nvlist(ncf->ncf_dict, "params", NULL);
    483 	if (params == NULL || !nvlist_exists(params, name)) {
    484 		return ENOENT;
    485 	}
    486 	*valp = (int)dnvlist_get_number(params, name, 0);
    487 	return 0;
    488 }
    489 
    490 int
    491 npf_param_set(nl_config_t *ncf, const char *name, int val)
    492 {
    493 	nvlist_t *params;
    494 
    495 	/* Ensure params dictionary. */
    496 	if (nvlist_exists(ncf->ncf_dict, "params")) {
    497 		params = nvlist_take_nvlist(ncf->ncf_dict, "params");
    498 	} else {
    499 		params = nvlist_create(0);
    500 	}
    501 
    502 	/*
    503 	 * If the parameter is already set, then free it first.
    504 	 * Set the parameter.  Note: values can be negative.
    505 	 */
    506 	if (nvlist_exists(params, name)) {
    507 		nvlist_free_number(params, name);
    508 	}
    509 	nvlist_add_number(params, name, (uint64_t)val);
    510 	nvlist_add_nvlist(ncf->ncf_dict, "params", params);
    511 	return 0;
    512 }
    513 
    514 const char *
    515 npf_param_iterate(nl_config_t *ncf, nl_iter_t *iter, int *val, int *defval)
    516 {
    517 	void *cookie = (void *)(intptr_t)*iter;
    518 	const nvlist_t *params, *dparams;
    519 	const char *name;
    520 	int type;
    521 
    522 	assert(sizeof(nl_iter_t) >= sizeof(void *));
    523 
    524 	params = dnvlist_get_nvlist(ncf->ncf_dict, "params", NULL);
    525 	if (params == NULL) {
    526 		return NULL;
    527 	}
    528 skip:
    529 	if ((name = nvlist_next(params, &type, &cookie)) == NULL) {
    530 		*iter = NPF_ITER_BEGIN;
    531 		return NULL;
    532 	}
    533 	if (type != NV_TYPE_NUMBER) {
    534 		goto skip; // should never happen, though
    535 	}
    536 	if (defval) {
    537 		dparams = dnvlist_get_nvlist(ncf->ncf_dict,
    538 		    "params-defaults", NULL);
    539 		if (dparams == NULL) {
    540 			errno = EINVAL;
    541 			return NULL;
    542 		}
    543 		*defval = (int)nvlist_get_number(dparams, name);
    544 	}
    545 
    546 	*val = (int)nvlist_get_number(params, name);
    547 	*iter = (intptr_t)cookie;
    548 	return name;
    549 }
    550 
    551 /*
    552  * DYNAMIC RULESET INTERFACE.
    553  */
    554 
    555 static inline bool
    556 _npf_nat_ruleset_p(const char *name)
    557 {
    558 	return strncmp(name, NPF_RULESET_MAP_PREF,
    559 	    sizeof(NPF_RULESET_MAP_PREF) - 1) == 0;
    560 }
    561 
    562 int
    563 npf_ruleset_add(int fd, const char *rname, nl_rule_t *rl, uint64_t *id)
    564 {
    565 	const bool natset = _npf_nat_ruleset_p(rname);
    566 	nvlist_t *rule_nvl = rl->rule_dict, *resp;
    567 	int error;
    568 
    569 	nvlist_add_number(rule_nvl, "attr",
    570 	    NPF_RULE_DYNAMIC | nvlist_take_number(rule_nvl, "attr"));
    571 
    572 	if (natset && !dnvlist_get_bool(rule_nvl, "nat-rule", false)) {
    573 		errno = EINVAL;
    574 		return errno;
    575 	}
    576 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
    577 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
    578 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_ADD);
    579 
    580 	error = _npf_xfer_fd(fd, IOC_NPF_RULE, rule_nvl, &resp);
    581 	if (error) {
    582 		return error;
    583 	}
    584 	*id = nvlist_get_number(resp, "id");
    585 	nvlist_destroy(resp);
    586 	return 0;
    587 }
    588 
    589 int
    590 npf_ruleset_remove(int fd, const char *rname, uint64_t id)
    591 {
    592 	const bool natset = _npf_nat_ruleset_p(rname);
    593 	nvlist_t *rule_nvl = nvlist_create(0);
    594 	int error;
    595 
    596 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
    597 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
    598 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_REMOVE);
    599 	nvlist_add_number(rule_nvl, "id", id);
    600 
    601 	error = _npf_xfer_fd_errno(fd, IOC_NPF_RULE, rule_nvl);
    602 	nvlist_destroy(rule_nvl);
    603 	return error;
    604 }
    605 
    606 int
    607 npf_ruleset_remkey(int fd, const char *rname, const void *key, size_t len)
    608 {
    609 	const bool natset = _npf_nat_ruleset_p(rname);
    610 	nvlist_t *rule_nvl = nvlist_create(0);
    611 	int error;
    612 
    613 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
    614 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
    615 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_REMKEY);
    616 	nvlist_add_binary(rule_nvl, "key", key, len);
    617 
    618 	error = _npf_xfer_fd_errno(fd, IOC_NPF_RULE, rule_nvl);
    619 	nvlist_destroy(rule_nvl);
    620 	return error;
    621 }
    622 
    623 int
    624 npf_ruleset_flush(int fd, const char *rname)
    625 {
    626 	const bool natset = _npf_nat_ruleset_p(rname);
    627 	nvlist_t *rule_nvl = nvlist_create(0);
    628 	int error;
    629 
    630 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
    631 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
    632 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_FLUSH);
    633 
    634 	error = _npf_xfer_fd_errno(fd, IOC_NPF_RULE, rule_nvl);
    635 	nvlist_destroy(rule_nvl);
    636 	return error;
    637 }
    638 
    639 /*
    640  * NPF EXTENSION INTERFACE.
    641  */
    642 
    643 nl_ext_t *
    644 npf_ext_construct(const char *name)
    645 {
    646 	nl_ext_t *ext;
    647 
    648 	ext = malloc(sizeof(*ext));
    649 	if (!ext) {
    650 		return NULL;
    651 	}
    652 	ext->ext_dict = nvlist_create(0);
    653 	nvlist_add_string(ext->ext_dict, "name", name);
    654 	return ext;
    655 }
    656 
    657 void
    658 npf_ext_param_u32(nl_ext_t *ext, const char *key, uint32_t val)
    659 {
    660 	nvlist_add_number(ext->ext_dict, key, val);
    661 }
    662 
    663 void
    664 npf_ext_param_bool(nl_ext_t *ext, const char *key, bool val)
    665 {
    666 	nvlist_add_bool(ext->ext_dict, key, val);
    667 }
    668 
    669 void
    670 npf_ext_param_string(nl_ext_t *ext, const char *key, const char *val)
    671 {
    672 	nvlist_add_string(ext->ext_dict, key, val);
    673 }
    674 
    675 /*
    676  * RULE INTERFACE.
    677  */
    678 
    679 nl_rule_t *
    680 npf_rule_create(const char *name, uint32_t attr, const char *ifname)
    681 {
    682 	nl_rule_t *rl;
    683 
    684 	rl = malloc(sizeof(nl_rule_t));
    685 	if (!rl) {
    686 		return NULL;
    687 	}
    688 	rl->rule_dict = nvlist_create(0);
    689 	nvlist_add_number(rl->rule_dict, "attr", attr);
    690 	if (name) {
    691 		nvlist_add_string(rl->rule_dict, "name", name);
    692 	}
    693 	if (ifname) {
    694 		nvlist_add_string(rl->rule_dict, "ifname", ifname);
    695 	}
    696 	return rl;
    697 }
    698 
    699 int
    700 npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t len)
    701 {
    702 	if (type != NPF_CODE_BPF) {
    703 		return ENOTSUP;
    704 	}
    705 	nvlist_add_number(rl->rule_dict, "code-type", (unsigned)type);
    706 	nvlist_add_binary(rl->rule_dict, "code", code, len);
    707 	return nvlist_error(rl->rule_dict);
    708 }
    709 
    710 int
    711 npf_rule_setkey(nl_rule_t *rl, const void *key, size_t len)
    712 {
    713 	nvlist_add_binary(rl->rule_dict, "key", key, len);
    714 	return nvlist_error(rl->rule_dict);
    715 }
    716 
    717 int
    718 npf_rule_setinfo(nl_rule_t *rl, const void *info, size_t len)
    719 {
    720 	nvlist_add_binary(rl->rule_dict, "info", info, len);
    721 	return nvlist_error(rl->rule_dict);
    722 }
    723 
    724 int
    725 npf_rule_setprio(nl_rule_t *rl, int pri)
    726 {
    727 	nvlist_add_number(rl->rule_dict, "prio", (uint64_t)pri);
    728 	return nvlist_error(rl->rule_dict);
    729 }
    730 
    731 int
    732 npf_rule_setproc(nl_rule_t *rl, const char *name)
    733 {
    734 	nvlist_add_string(rl->rule_dict, "rproc", name);
    735 	return nvlist_error(rl->rule_dict);
    736 }
    737 
    738 /* both user and group */
    739 int
    740 npf_rule_setrid(nl_rule_t *rl, struct r_id rid, const char *name)
    741 {
    742 	uint64_t uid_element[3] = { rid.id[0], rid.id[1], rid.op };
    743 	nvlist_add_number_array(rl->rule_dict, name, uid_element, 3);
    744 	return nvlist_error(rl->rule_dict);
    745 }
    746 
    747 void *
    748 npf_rule_export(nl_rule_t *rl, size_t *length)
    749 {
    750 	return nvlist_pack(rl->rule_dict, length);
    751 }
    752 
    753 bool
    754 npf_rule_exists_p(nl_config_t *ncf, const char *name)
    755 {
    756 	const char *key = nvlist_exists_nvlist_array(ncf->ncf_dict,
    757 	    "rules") ? "rules" : "__rules"; // config may not be built yet
    758 	return _npf_dataset_lookup(ncf->ncf_dict, key, "name", name);
    759 }
    760 
    761 int
    762 npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl)
    763 {
    764 	nvlist_t *rule_dict = rl->rule_dict;
    765 	nvlist_t *target;
    766 	const char *key;
    767 
    768 	if (parent) {
    769 		/* Subrule of the parent. */
    770 		target = parent->rule_dict;
    771 		key = "subrules";
    772 	} else {
    773 		/* Global ruleset. */
    774 		target = ncf->ncf_dict;
    775 		key = "__rules";
    776 	}
    777 	nvlist_append_nvlist_array(target, key, rule_dict);
    778 	nvlist_destroy(rule_dict);
    779 	free(rl);
    780 	return 0;
    781 }
    782 
    783 static nl_rule_t *
    784 _npf_rule_iterate1(nl_config_t *ncf, const char *key,
    785     nl_iter_t *iter, unsigned *level)
    786 {
    787 	unsigned i = *iter;
    788 	const nvlist_t *rule_dict;
    789 	uint32_t skipto;
    790 
    791 	if (i == 0) {
    792 		/* Initialise the iterator. */
    793 		ncf->ncf_nlevel = 0;
    794 		ncf->ncf_reduce[0] = 0;
    795 	}
    796 
    797 	rule_dict = _npf_dataset_getelement(ncf->ncf_dict, key, i);
    798 	if (!rule_dict) {
    799 		*iter = NPF_ITER_BEGIN;
    800 		return NULL;
    801 	}
    802 	*iter = i + 1; // next
    803 	*level = ncf->ncf_nlevel;
    804 
    805 	skipto = dnvlist_get_number(rule_dict, "skip-to", 0);
    806 	if (skipto) {
    807 		ncf->ncf_nlevel++;
    808 		ncf->ncf_reduce[ncf->ncf_nlevel] = skipto;
    809 	}
    810 	if (ncf->ncf_reduce[ncf->ncf_nlevel] == (i + 1)) {
    811 		assert(ncf->ncf_nlevel > 0);
    812 		ncf->ncf_nlevel--;
    813 	}
    814 
    815 	ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX
    816 	return &ncf->ncf_cur_rule;
    817 }
    818 
    819 nl_rule_t *
    820 npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level)
    821 {
    822 	return _npf_rule_iterate1(ncf, "rules", iter, level);
    823 }
    824 
    825 const char *
    826 npf_rule_getname(nl_rule_t *rl)
    827 {
    828 	return dnvlist_get_string(rl->rule_dict, "name", NULL);
    829 }
    830 
    831 uint32_t
    832 npf_rule_getattr(nl_rule_t *rl)
    833 {
    834 	return dnvlist_get_number(rl->rule_dict, "attr", 0);
    835 }
    836 
    837 const char *
    838 npf_rule_getinterface(nl_rule_t *rl)
    839 {
    840 	return dnvlist_get_string(rl->rule_dict, "ifname", NULL);
    841 }
    842 
    843 const void *
    844 npf_rule_getinfo(nl_rule_t *rl, size_t *len)
    845 {
    846 	return dnvlist_get_binary(rl->rule_dict, "info", len, NULL, 0);
    847 }
    848 
    849 const char *
    850 npf_rule_getproc(nl_rule_t *rl)
    851 {
    852 	return dnvlist_get_string(rl->rule_dict, "rproc", NULL);
    853 }
    854 
    855 int
    856 npf_rule_getrid(struct r_id *r_id, nl_rule_t *rl, const char *key)
    857 {
    858 	if (nvlist_exists_number_array(rl->rule_dict, key)) {
    859 		size_t nitems;
    860 		const uint64_t *rid = nvlist_get_number_array(rl->rule_dict, key, &nitems);
    861 		assert(nitems == 3);
    862 
    863 		r_id->id[0] = (uint32_t)rid[0];
    864 		r_id->id[1] = (uint32_t)rid[1];
    865 		r_id->op = (uint8_t)rid[2];
    866 		return 0;
    867 	}
    868 	return -1;
    869 }
    870 
    871 uint64_t
    872 npf_rule_getid(nl_rule_t *rl)
    873 {
    874 	return dnvlist_get_number(rl->rule_dict, "id", 0);
    875 }
    876 
    877 const void *
    878 npf_rule_getcode(nl_rule_t *rl, int *type, size_t *len)
    879 {
    880 	*type = (int)dnvlist_get_number(rl->rule_dict, "code-type", 0);
    881 	return dnvlist_get_binary(rl->rule_dict, "code", len, NULL, 0);
    882 }
    883 
    884 int
    885 _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf)
    886 {
    887 	const bool natset = _npf_nat_ruleset_p(rname);
    888 	nvlist_t *req, *resp;
    889 	int error;
    890 
    891 	req = nvlist_create(0);
    892 	nvlist_add_string(req, "ruleset-name", rname);
    893 	nvlist_add_bool(req, "nat-ruleset", natset);
    894 	nvlist_add_number(req, "command", NPF_CMD_RULE_LIST);
    895 
    896 	error = _npf_xfer_fd(fd, IOC_NPF_RULE, req, &resp);
    897 	nvlist_destroy(req);
    898 	if (error) {
    899 		return error;
    900 	}
    901 
    902 	if (nvlist_exists_nvlist_array(resp, "rules")) {
    903 		nvlist_t **rules;
    904 		size_t n;
    905 
    906 		rules = nvlist_take_nvlist_array(resp, "rules", &n);
    907 		nvlist_move_nvlist_array(ncf->ncf_dict, "rules", rules, n);
    908 	}
    909 	nvlist_destroy(resp);
    910 	return 0;
    911 }
    912 
    913 void
    914 npf_rule_destroy(nl_rule_t *rl)
    915 {
    916 	nvlist_destroy(rl->rule_dict);
    917 	free(rl);
    918 }
    919 
    920 /*
    921  * RULE PROCEDURE INTERFACE.
    922  */
    923 
    924 nl_rproc_t *
    925 npf_rproc_create(const char *name)
    926 {
    927 	nl_rproc_t *rp;
    928 
    929 	rp = malloc(sizeof(nl_rproc_t));
    930 	if (!rp) {
    931 		return NULL;
    932 	}
    933 	rp->rproc_dict = nvlist_create(0);
    934 	nvlist_add_string(rp->rproc_dict, "name", name);
    935 	return rp;
    936 }
    937 
    938 int
    939 npf_rproc_extcall(nl_rproc_t *rp, nl_ext_t *ext)
    940 {
    941 	nvlist_t *rproc_dict = rp->rproc_dict;
    942 	const char *name = dnvlist_get_string(ext->ext_dict, "name", NULL);
    943 
    944 	if (_npf_dataset_lookup(rproc_dict, "extcalls", "name", name)) {
    945 		return EEXIST;
    946 	}
    947 	nvlist_append_nvlist_array(rproc_dict, "extcalls", ext->ext_dict);
    948 	nvlist_destroy(ext->ext_dict);
    949 	free(ext);
    950 	return 0;
    951 }
    952 
    953 bool
    954 npf_rproc_exists_p(nl_config_t *ncf, const char *name)
    955 {
    956 	return _npf_dataset_lookup(ncf->ncf_dict, "rprocs", "name", name);
    957 }
    958 
    959 int
    960 npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp)
    961 {
    962 	const char *name;
    963 
    964 	name = dnvlist_get_string(rp->rproc_dict, "name", NULL);
    965 	if (!name) {
    966 		return EINVAL;
    967 	}
    968 	if (npf_rproc_exists_p(ncf, name)) {
    969 		return EEXIST;
    970 	}
    971 	nvlist_append_nvlist_array(ncf->ncf_dict, "rprocs", rp->rproc_dict);
    972 	nvlist_destroy(rp->rproc_dict);
    973 	free(rp);
    974 	return 0;
    975 }
    976 
    977 nl_rproc_t *
    978 npf_rproc_iterate(nl_config_t *ncf, nl_iter_t *iter)
    979 {
    980 	const nvlist_t *rproc_dict;
    981 	unsigned i = *iter;
    982 
    983 	rproc_dict = _npf_dataset_getelement(ncf->ncf_dict, "rprocs", i);
    984 	if (!rproc_dict) {
    985 		*iter = NPF_ITER_BEGIN;
    986 		return NULL;
    987 	}
    988 	*iter = i + 1; // next
    989 	ncf->ncf_cur_rproc.rproc_dict = __UNCONST(rproc_dict); // XXX
    990 	return &ncf->ncf_cur_rproc;
    991 }
    992 
    993 const char *
    994 npf_rproc_getname(nl_rproc_t *rp)
    995 {
    996 	return dnvlist_get_string(rp->rproc_dict, "name", NULL);
    997 }
    998 
    999 /*
   1000  * NAT INTERFACE.
   1001  */
   1002 
   1003 nl_nat_t *
   1004 npf_nat_create(int type, unsigned flags, const char *ifname)
   1005 {
   1006 	nl_rule_t *rl;
   1007 	nvlist_t *rule_dict;
   1008 	uint32_t attr;
   1009 
   1010 	attr = NPF_RULE_PASS | NPF_RULE_FINAL | NPF_RULE_LAYER_3 |
   1011 	    (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN);
   1012 
   1013 	/* Create a rule for NAT policy.  Next, will add NAT data. */
   1014 	rl = npf_rule_create(NULL, attr, ifname);
   1015 	if (!rl) {
   1016 		return NULL;
   1017 	}
   1018 	rule_dict = rl->rule_dict;
   1019 
   1020 	/* Translation type and flags. */
   1021 	nvlist_add_number(rule_dict, "type", type);
   1022 	nvlist_add_number(rule_dict, "flags", flags);
   1023 	nvlist_add_bool(rule_dict, "nat-rule", true);
   1024 	return (nl_nat_t *)rl;
   1025 }
   1026 
   1027 int
   1028 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt)
   1029 {
   1030 	nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict);
   1031 	nvlist_destroy(nt->rule_dict);
   1032 	free(nt);
   1033 	return 0;
   1034 }
   1035 
   1036 nl_nat_t *
   1037 npf_nat_iterate(nl_config_t *ncf, nl_iter_t *iter)
   1038 {
   1039 	unsigned level;
   1040 	return _npf_rule_iterate1(ncf, "nat", iter, &level);
   1041 }
   1042 
   1043 int
   1044 npf_nat_setaddr(nl_nat_t *nt, int af, npf_addr_t *addr, npf_netmask_t mask)
   1045 {
   1046 	/* Translation IP and mask. */
   1047 	if (!_npf_add_addr(nt->rule_dict, "nat-addr", af, addr)) {
   1048 		return nvlist_error(nt->rule_dict);
   1049 	}
   1050 	nvlist_add_number(nt->rule_dict, "nat-mask", (uint32_t)mask);
   1051 	return nvlist_error(nt->rule_dict);
   1052 }
   1053 
   1054 int
   1055 npf_nat_setport(nl_nat_t *nt, in_port_t port)
   1056 {
   1057 	/* Translation port (for redirect case). */
   1058 	nvlist_add_number(nt->rule_dict, "nat-port", port);
   1059 	return nvlist_error(nt->rule_dict);
   1060 }
   1061 
   1062 int
   1063 npf_nat_settable(nl_nat_t *nt, unsigned tid)
   1064 {
   1065 	/*
   1066 	 * Translation table ID; the address/mask will then serve as a filter.
   1067 	 */
   1068 	nvlist_add_number(nt->rule_dict, "nat-table-id", tid);
   1069 	return nvlist_error(nt->rule_dict);
   1070 }
   1071 
   1072 int
   1073 npf_nat_setalgo(nl_nat_t *nt, unsigned algo)
   1074 {
   1075 	nvlist_add_number(nt->rule_dict, "nat-algo", algo);
   1076 	return nvlist_error(nt->rule_dict);
   1077 }
   1078 
   1079 int
   1080 npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj)
   1081 {
   1082 	int error;
   1083 
   1084 	if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) {
   1085 		return error;
   1086 	}
   1087 	nvlist_add_number(nt->rule_dict, "npt66-adj", adj);
   1088 	return nvlist_error(nt->rule_dict);
   1089 }
   1090 
   1091 int
   1092 npf_nat_gettype(nl_nat_t *nt)
   1093 {
   1094 	return dnvlist_get_number(nt->rule_dict, "type", 0);
   1095 }
   1096 
   1097 unsigned
   1098 npf_nat_getflags(nl_nat_t *nt)
   1099 {
   1100 	return dnvlist_get_number(nt->rule_dict, "flags", 0);
   1101 }
   1102 
   1103 unsigned
   1104 npf_nat_getalgo(nl_nat_t *nt)
   1105 {
   1106 	return dnvlist_get_number(nt->rule_dict, "nat-algo", 0);
   1107 }
   1108 
   1109 const npf_addr_t *
   1110 npf_nat_getaddr(nl_nat_t *nt, size_t *alen, npf_netmask_t *mask)
   1111 {
   1112 	const void *data;
   1113 
   1114 	if (nvlist_exists(nt->rule_dict, "nat-addr")) {
   1115 		data = nvlist_get_binary(nt->rule_dict, "nat-addr", alen);
   1116 		*mask = nvlist_get_number(nt->rule_dict, "nat-mask");
   1117 	} else {
   1118 		data = NULL;
   1119 		*alen = 0;
   1120 		*mask = NPF_NO_NETMASK;
   1121 	}
   1122 	return data;
   1123 }
   1124 
   1125 in_port_t
   1126 npf_nat_getport(nl_nat_t *nt)
   1127 {
   1128 	return (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0);
   1129 }
   1130 
   1131 unsigned
   1132 npf_nat_gettable(nl_nat_t *nt)
   1133 {
   1134 	return dnvlist_get_number(nt->rule_dict, "nat-table-id", 0);
   1135 }
   1136 
   1137 /*
   1138  * TABLE INTERFACE.
   1139  */
   1140 
   1141 nl_table_t *
   1142 npf_table_create(const char *name, unsigned id, int type)
   1143 {
   1144 	nl_table_t *tl;
   1145 
   1146 	tl = malloc(sizeof(*tl));
   1147 	if (!tl) {
   1148 		return NULL;
   1149 	}
   1150 	tl->table_dict = nvlist_create(0);
   1151 	nvlist_add_string(tl->table_dict, "name", name);
   1152 	nvlist_add_number(tl->table_dict, "id", id);
   1153 	nvlist_add_number(tl->table_dict, "type", type);
   1154 	return tl;
   1155 }
   1156 
   1157 int
   1158 npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr,
   1159     const npf_netmask_t mask)
   1160 {
   1161 	nvlist_t *entry;
   1162 
   1163 	entry = nvlist_create(0);
   1164 	if (!entry) {
   1165 		return ENOMEM;
   1166 	}
   1167 	if (!_npf_add_addr(entry, "addr", af, addr)) {
   1168 		nvlist_destroy(entry);
   1169 		return EINVAL;
   1170 	}
   1171 	nvlist_add_number(entry, "mask", mask);
   1172 	nvlist_append_nvlist_array(tl->table_dict, "entries", entry);
   1173 	nvlist_destroy(entry);
   1174 	return 0;
   1175 }
   1176 
   1177 static inline int
   1178 _npf_table_build_const(nl_table_t *tl)
   1179 {
   1180 	struct cdbw *cdbw;
   1181 	const nvlist_t * const *entries;
   1182 	int error = 0, fd = -1;
   1183 	size_t nitems, len;
   1184 	void *cdb, *buf;
   1185 	struct stat sb;
   1186 	char sfn[32];
   1187 
   1188 	if (dnvlist_get_number(tl->table_dict, "type", 0) != NPF_TABLE_CONST) {
   1189 		return 0;
   1190 	}
   1191 
   1192 	if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) {
   1193 		return 0;
   1194 	}
   1195 
   1196 	/*
   1197 	 * Create a constant database and put all the entries.
   1198 	 */
   1199 	if ((cdbw = cdbw_open()) == NULL) {
   1200 		return errno;
   1201 	}
   1202 	entries = nvlist_get_nvlist_array(tl->table_dict, "entries", &nitems);
   1203 	for (unsigned i = 0; i < nitems; i++) {
   1204 		const nvlist_t *entry = entries[i];
   1205 		const npf_addr_t *addr;
   1206 		size_t alen;
   1207 
   1208 		addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0);
   1209 		if (addr == NULL || alen == 0 || alen > sizeof(npf_addr_t)) {
   1210 			error = EINVAL;
   1211 			goto out;
   1212 		}
   1213 		if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) {
   1214 			error = errno;
   1215 			goto out;
   1216 		}
   1217 	}
   1218 
   1219 	/*
   1220 	 * Write the constant database into a temporary file.
   1221 	 */
   1222 	strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn));
   1223 	sfn[sizeof(sfn) - 1] = '\0';
   1224 
   1225 	if ((fd = mkstemp(sfn)) == -1) {
   1226 		error = errno;
   1227 		goto out;
   1228 	}
   1229 	unlink(sfn);
   1230 
   1231 	if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
   1232 		error = errno;
   1233 		goto out;
   1234 	}
   1235 	if (fstat(fd, &sb) == -1) {
   1236 		error = errno;
   1237 		goto out;
   1238 	}
   1239 	len = sb.st_size;
   1240 
   1241 	/*
   1242 	 * Memory-map the database and copy it into a buffer.
   1243 	 */
   1244 	buf = malloc(len);
   1245 	if (!buf) {
   1246 		error = ENOMEM;
   1247 		goto out;
   1248 	}
   1249 	cdb = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
   1250 	if (cdb == MAP_FAILED) {
   1251 		error = errno;
   1252 		free(buf);
   1253 		goto out;
   1254 	}
   1255 	munmap(cdb, len);
   1256 
   1257 	/*
   1258 	 * Move the data buffer to the nvlist.
   1259 	 */
   1260 	nvlist_move_binary(tl->table_dict, "data", buf, len);
   1261 	error = nvlist_error(tl->table_dict);
   1262 out:
   1263 	if (fd != -1) {
   1264 		close(fd);
   1265 	}
   1266 	cdbw_close(cdbw);
   1267 	return error;
   1268 }
   1269 
   1270 int
   1271 npf_table_insert(nl_config_t *ncf, nl_table_t *tl)
   1272 {
   1273 	const char *name;
   1274 	int error;
   1275 
   1276 	name = dnvlist_get_string(tl->table_dict, "name", NULL);
   1277 	if (!name) {
   1278 		return EINVAL;
   1279 	}
   1280 	if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) {
   1281 		return EEXIST;
   1282 	}
   1283 	if ((error = _npf_table_build_const(tl)) != 0) {
   1284 		return error;
   1285 	}
   1286 	nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict);
   1287 	nvlist_destroy(tl->table_dict);
   1288 	free(tl);
   1289 	return 0;
   1290 }
   1291 
   1292 int
   1293 npf_table_replace(int fd, nl_table_t *tl, npf_error_t *errinfo)
   1294 {
   1295 	nvlist_t *resp = NULL;
   1296 	int error;
   1297 
   1298 	/* Ensure const tables are built. */
   1299 	if ((error = _npf_table_build_const(tl)) != 0) {
   1300 		return _npf_init_error(errno, errinfo);
   1301 	}
   1302 	error = _npf_xfer_fd(fd, IOC_NPF_TABLE_REPLACE, tl->table_dict, &resp);
   1303 	if (error) {
   1304 		assert(resp == NULL);
   1305 		return _npf_init_error(errno, errinfo);
   1306 	}
   1307 	error = _npf_extract_error(resp, errinfo);
   1308 	nvlist_destroy(resp);
   1309 	return error;
   1310 }
   1311 
   1312 nl_table_t *
   1313 npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter)
   1314 {
   1315 	const nvlist_t *table_dict;
   1316 	unsigned i = *iter;
   1317 
   1318 	table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i);
   1319 	if (!table_dict) {
   1320 		*iter = NPF_ITER_BEGIN;
   1321 		return NULL;
   1322 	}
   1323 	*iter = i + 1; // next
   1324 	ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX
   1325 	return &ncf->ncf_cur_table;
   1326 }
   1327 
   1328 unsigned
   1329 npf_table_getid(nl_table_t *tl)
   1330 {
   1331 	return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1);
   1332 }
   1333 
   1334 const char *
   1335 npf_table_getname(nl_table_t *tl)
   1336 {
   1337 	return dnvlist_get_string(tl->table_dict, "name", NULL);
   1338 }
   1339 
   1340 int
   1341 npf_table_gettype(nl_table_t *tl)
   1342 {
   1343 	return dnvlist_get_number(tl->table_dict, "type", 0);
   1344 }
   1345 
   1346 void
   1347 npf_table_destroy(nl_table_t *tl)
   1348 {
   1349 	nvlist_destroy(tl->table_dict);
   1350 	free(tl);
   1351 }
   1352 
   1353 /*
   1354  * ALG INTERFACE.
   1355  */
   1356 
   1357 int
   1358 npf_alg_load(nl_config_t *ncf, const char *name)
   1359 {
   1360 	nvlist_t *alg_dict;
   1361 
   1362 	if (_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) {
   1363 		return EEXIST;
   1364 	}
   1365 	alg_dict = nvlist_create(0);
   1366 	nvlist_add_string(alg_dict, "name", name);
   1367 	nvlist_append_nvlist_array(ncf->ncf_dict, "algs", alg_dict);
   1368 	nvlist_destroy(alg_dict);
   1369 	return 0;
   1370 }
   1371 
   1372 /*
   1373  * CONNECTION / NAT ENTRY INTERFACE.
   1374  */
   1375 
   1376 typedef struct {
   1377 	unsigned	alen;
   1378 	unsigned	proto;
   1379 	npf_addr_t	addr[3];
   1380 	in_port_t	port[3];
   1381 } npf_connpoint_t;
   1382 
   1383 static int
   1384 _npf_conn_lookup(int fd, const int af, npf_addr_t *addr[2], in_port_t port[2],
   1385     unsigned proto, const char *ifname, unsigned di)
   1386 {
   1387 	nvlist_t *req = NULL, *resp = NULL, *key_nv;
   1388 	const nvlist_t *nat;
   1389 	int error = EINVAL;
   1390 
   1391 	/*
   1392 	 * Setup the connection lookup key.
   1393 	 */
   1394 	if ((key_nv = nvlist_create(0)) == NULL) {
   1395 		return ENOMEM;
   1396 	}
   1397 	if (!_npf_add_addr(key_nv, "saddr", af, addr[0])) {
   1398 		nvlist_destroy(key_nv);
   1399 		goto out;
   1400 	}
   1401 	if (!_npf_add_addr(key_nv, "daddr", af, addr[1])) {
   1402 		nvlist_destroy(key_nv);
   1403 		goto out;
   1404 	}
   1405 	nvlist_add_number(key_nv, "sport", htons(port[0]));
   1406 	nvlist_add_number(key_nv, "dport", htons(port[1]));
   1407 	nvlist_add_number(key_nv, "proto", proto);
   1408 	if (ifname) {
   1409 		nvlist_add_string(key_nv, "ifname", ifname);
   1410 	}
   1411 	if (di) {
   1412 		nvlist_add_number(key_nv, "di", di);
   1413 	}
   1414 
   1415 	/*
   1416 	 * Setup the request.
   1417 	 */
   1418 	if ((req = nvlist_create(0)) == NULL) {
   1419 		error = ENOMEM;
   1420 		goto out;
   1421 	}
   1422 	nvlist_move_nvlist(req, "key", key_nv);
   1423 
   1424 	/* Lookup: retrieve the connection entry. */
   1425 	error = _npf_xfer_fd(fd, IOC_NPF_CONN_LOOKUP, req, &resp);
   1426 	if (error) {
   1427 		goto out;
   1428 	}
   1429 
   1430 	/*
   1431 	 * Get the NAT entry and extract the translated pair.
   1432 	 */
   1433 	if ((nat = dnvlist_get_nvlist(resp, "nat", NULL)) == NULL) {
   1434 		error = ENOENT;
   1435 		goto out;
   1436 	}
   1437 	if (_npf_get_addr(nat, "oaddr", addr[0]) == 0 ||
   1438 	    _npf_get_addr(nat, "taddr", addr[1]) == 0) {
   1439 		error = EINVAL;
   1440 		goto out;
   1441 	}
   1442 	port[0] = ntohs(nvlist_get_number(nat, "oport"));
   1443 	port[1] = ntohs(nvlist_get_number(nat, "tport"));
   1444 out:
   1445 	if (resp) {
   1446 		nvlist_destroy(resp);
   1447 	}
   1448 	if (req) {
   1449 		nvlist_destroy(req);
   1450 	}
   1451 	return error;
   1452 }
   1453 
   1454 int
   1455 npf_nat_lookup(int fd, int af, npf_addr_t *addr[2], in_port_t port[2],
   1456     int proto, int di __unused)
   1457 {
   1458 	int error;
   1459 
   1460 	port[0] = ntohs(port[0]); port[1] = ntohs(port[1]);
   1461 	error = _npf_conn_lookup(fd, af, addr, port, proto, NULL, 0);
   1462 	port[0] = htons(port[0]); port[1] = htons(port[1]);
   1463 	return error;
   1464 }
   1465 
   1466 static bool
   1467 npf_connkey_handle(const nvlist_t *key_nv, npf_connpoint_t *ep)
   1468 {
   1469 	unsigned alen1, alen2;
   1470 
   1471 	alen1 = _npf_get_addr(key_nv, "saddr", &ep->addr[0]);
   1472 	alen2 = _npf_get_addr(key_nv, "daddr", &ep->addr[1]);
   1473 	if (alen1 == 0 || alen1 != alen2) {
   1474 		return false;
   1475 	}
   1476 	ep->alen = alen1;
   1477 	ep->port[0] = ntohs(nvlist_get_number(key_nv, "sport"));
   1478 	ep->port[1] = ntohs(nvlist_get_number(key_nv, "dport"));
   1479 	ep->proto = nvlist_get_number(key_nv, "proto");
   1480 	return true;
   1481 }
   1482 
   1483 static void
   1484 npf_conn_handle(const nvlist_t *conn, npf_conn_func_t func, void *arg)
   1485 {
   1486 	const nvlist_t *key_nv, *nat_nv;
   1487 	const char *ifname;
   1488 	npf_connpoint_t ep;
   1489 
   1490 	memset(&ep, 0, sizeof(npf_connpoint_t));
   1491 
   1492 	ifname = dnvlist_get_string(conn, "ifname", NULL);
   1493 	key_nv = dnvlist_get_nvlist(conn, "forw-key", NULL);
   1494 	if (!npf_connkey_handle(key_nv, &ep)) {
   1495 		goto err;
   1496 	}
   1497 	if ((nat_nv = dnvlist_get_nvlist(conn, "nat", NULL)) != NULL) {
   1498 		if (_npf_get_addr(nat_nv, "taddr", &ep.addr[2]) != ep.alen) {
   1499 			goto err;
   1500 		}
   1501 		ep.port[2] = ntohs(nvlist_get_number(nat_nv, "tport"));
   1502 	}
   1503 	/*
   1504 	 * XXX: add 'proto' and 'flow'; perhaps expand and pass the
   1505 	 * whole to npf_connpoint_t?
   1506 	 */
   1507 	(*func)((unsigned)ep.alen, ep.addr, ep.port, ifname, arg);
   1508 err:
   1509 	return;
   1510 }
   1511 
   1512 int
   1513 npf_conn_list(int fd, npf_conn_func_t func, void *arg)
   1514 {
   1515 	nl_config_t *ncf;
   1516 	const nvlist_t * const *conns;
   1517 	size_t nitems;
   1518 
   1519 	ncf = npf_config_retrieve(fd);
   1520 	if (!ncf) {
   1521 		return errno;
   1522 	}
   1523 	if (!nvlist_exists_nvlist_array(ncf->ncf_dict, "conn-list")) {
   1524 		return 0;
   1525 	}
   1526 	conns = nvlist_get_nvlist_array(ncf->ncf_dict, "conn-list", &nitems);
   1527 	for (unsigned i = 0; i < nitems; i++) {
   1528 		const nvlist_t *conn = conns[i];
   1529 		npf_conn_handle(conn, func, arg);
   1530 	}
   1531 	npf_config_destroy(ncf);
   1532 	return 0;
   1533 }
   1534 
   1535 /*
   1536  * MISC.
   1537  */
   1538 
   1539 void
   1540 _npf_debug_addif(nl_config_t *ncf, const char *ifname)
   1541 {
   1542 	nvlist_t *debug;
   1543 
   1544 	/*
   1545 	 * Initialise the debug dictionary on the first call.
   1546 	 */
   1547 	debug = dnvlist_take_nvlist(ncf->ncf_dict, "debug", NULL);
   1548 	if (debug == NULL) {
   1549 		debug = nvlist_create(0);
   1550 	}
   1551 	if (!_npf_dataset_lookup(debug, "interfaces", "name", ifname)) {
   1552 		nvlist_t *ifdict = nvlist_create(0);
   1553 		nvlist_add_string(ifdict, "name", ifname);
   1554 		nvlist_add_number(ifdict, "index", if_nametoindex(ifname));
   1555 		nvlist_append_nvlist_array(debug, "interfaces", ifdict);
   1556 		nvlist_destroy(ifdict);
   1557 	}
   1558 	nvlist_move_nvlist(ncf->ncf_dict, "debug", debug);
   1559 }
   1560 
   1561 void
   1562 _npf_config_dump(nl_config_t *ncf, int fd)
   1563 {
   1564 	(void)npf_config_build(ncf);
   1565 	nvlist_dump(ncf->ncf_dict, fd);
   1566 }
   1567