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.58 2026/09/30 18:00:30 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 void
    676 npf_ext_param_binary(nl_ext_t *ext, const char *key, const void *val, size_t len)
    677 {
    678 	nvlist_add_binary(ext->ext_dict, key, val, len);
    679 }
    680 
    681 /*
    682  * RULE INTERFACE.
    683  */
    684 
    685 nl_rule_t *
    686 npf_rule_create(const char *name, uint32_t attr, const char *ifname)
    687 {
    688 	nl_rule_t *rl;
    689 
    690 	rl = malloc(sizeof(nl_rule_t));
    691 	if (!rl) {
    692 		return NULL;
    693 	}
    694 	rl->rule_dict = nvlist_create(0);
    695 	nvlist_add_number(rl->rule_dict, "attr", attr);
    696 	if (name) {
    697 		nvlist_add_string(rl->rule_dict, "name", name);
    698 	}
    699 	if (ifname) {
    700 		nvlist_add_string(rl->rule_dict, "ifname", ifname);
    701 	}
    702 	return rl;
    703 }
    704 
    705 int
    706 npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t len)
    707 {
    708 	if (type != NPF_CODE_BPF) {
    709 		return ENOTSUP;
    710 	}
    711 	nvlist_add_number(rl->rule_dict, "code-type", (unsigned)type);
    712 	nvlist_add_binary(rl->rule_dict, "code", code, len);
    713 	return nvlist_error(rl->rule_dict);
    714 }
    715 
    716 int
    717 npf_rule_setkey(nl_rule_t *rl, const void *key, size_t len)
    718 {
    719 	nvlist_add_binary(rl->rule_dict, "key", key, len);
    720 	return nvlist_error(rl->rule_dict);
    721 }
    722 
    723 int
    724 npf_rule_setinfo(nl_rule_t *rl, const void *info, size_t len)
    725 {
    726 	nvlist_add_binary(rl->rule_dict, "info", info, len);
    727 	return nvlist_error(rl->rule_dict);
    728 }
    729 
    730 int
    731 npf_rule_setprio(nl_rule_t *rl, int pri)
    732 {
    733 	nvlist_add_number(rl->rule_dict, "prio", (uint64_t)pri);
    734 	return nvlist_error(rl->rule_dict);
    735 }
    736 
    737 int
    738 npf_rule_setproc(nl_rule_t *rl, const char *name)
    739 {
    740 	nvlist_add_string(rl->rule_dict, "rproc", name);
    741 	return nvlist_error(rl->rule_dict);
    742 }
    743 
    744 /* both user and group */
    745 int
    746 npf_rule_setrid(nl_rule_t *rl, struct r_id rid, const char *name)
    747 {
    748 	uint64_t uid_element[3] = { rid.id[0], rid.id[1], rid.op };
    749 	nvlist_add_number_array(rl->rule_dict, name, uid_element, 3);
    750 	return nvlist_error(rl->rule_dict);
    751 }
    752 
    753 void *
    754 npf_rule_export(nl_rule_t *rl, size_t *length)
    755 {
    756 	return nvlist_pack(rl->rule_dict, length);
    757 }
    758 
    759 bool
    760 npf_rule_exists_p(nl_config_t *ncf, const char *name)
    761 {
    762 	const char *key = nvlist_exists_nvlist_array(ncf->ncf_dict,
    763 	    "rules") ? "rules" : "__rules"; // config may not be built yet
    764 	return _npf_dataset_lookup(ncf->ncf_dict, key, "name", name);
    765 }
    766 
    767 int
    768 npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl)
    769 {
    770 	nvlist_t *rule_dict = rl->rule_dict;
    771 	nvlist_t *target;
    772 	const char *key;
    773 
    774 	if (parent) {
    775 		/* Subrule of the parent. */
    776 		target = parent->rule_dict;
    777 		key = "subrules";
    778 	} else {
    779 		/* Global ruleset. */
    780 		target = ncf->ncf_dict;
    781 		key = "__rules";
    782 	}
    783 	nvlist_append_nvlist_array(target, key, rule_dict);
    784 	nvlist_destroy(rule_dict);
    785 	free(rl);
    786 	return 0;
    787 }
    788 
    789 static nl_rule_t *
    790 _npf_rule_iterate1(nl_config_t *ncf, const char *key,
    791     nl_iter_t *iter, unsigned *level)
    792 {
    793 	unsigned i = *iter;
    794 	const nvlist_t *rule_dict;
    795 	uint32_t skipto;
    796 
    797 	if (i == 0) {
    798 		/* Initialise the iterator. */
    799 		ncf->ncf_nlevel = 0;
    800 		ncf->ncf_reduce[0] = 0;
    801 	}
    802 
    803 	rule_dict = _npf_dataset_getelement(ncf->ncf_dict, key, i);
    804 	if (!rule_dict) {
    805 		*iter = NPF_ITER_BEGIN;
    806 		return NULL;
    807 	}
    808 	*iter = i + 1; // next
    809 	*level = ncf->ncf_nlevel;
    810 
    811 	skipto = dnvlist_get_number(rule_dict, "skip-to", 0);
    812 	if (skipto) {
    813 		ncf->ncf_nlevel++;
    814 		ncf->ncf_reduce[ncf->ncf_nlevel] = skipto;
    815 	}
    816 	if (ncf->ncf_reduce[ncf->ncf_nlevel] == (i + 1)) {
    817 		assert(ncf->ncf_nlevel > 0);
    818 		ncf->ncf_nlevel--;
    819 	}
    820 
    821 	ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX
    822 	return &ncf->ncf_cur_rule;
    823 }
    824 
    825 nl_rule_t *
    826 npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level)
    827 {
    828 	return _npf_rule_iterate1(ncf, "rules", iter, level);
    829 }
    830 
    831 const char *
    832 npf_rule_getname(nl_rule_t *rl)
    833 {
    834 	return dnvlist_get_string(rl->rule_dict, "name", NULL);
    835 }
    836 
    837 uint32_t
    838 npf_rule_getattr(nl_rule_t *rl)
    839 {
    840 	return dnvlist_get_number(rl->rule_dict, "attr", 0);
    841 }
    842 
    843 const char *
    844 npf_rule_getinterface(nl_rule_t *rl)
    845 {
    846 	return dnvlist_get_string(rl->rule_dict, "ifname", NULL);
    847 }
    848 
    849 const void *
    850 npf_rule_getinfo(nl_rule_t *rl, size_t *len)
    851 {
    852 	return dnvlist_get_binary(rl->rule_dict, "info", len, NULL, 0);
    853 }
    854 
    855 const char *
    856 npf_rule_getproc(nl_rule_t *rl)
    857 {
    858 	return dnvlist_get_string(rl->rule_dict, "rproc", NULL);
    859 }
    860 
    861 int
    862 npf_rule_getrid(struct r_id *r_id, nl_rule_t *rl, const char *key)
    863 {
    864 	if (nvlist_exists_number_array(rl->rule_dict, key)) {
    865 		size_t nitems;
    866 		const uint64_t *rid = nvlist_get_number_array(rl->rule_dict, key, &nitems);
    867 		assert(nitems == 3);
    868 
    869 		r_id->id[0] = (uint32_t)rid[0];
    870 		r_id->id[1] = (uint32_t)rid[1];
    871 		r_id->op = (uint8_t)rid[2];
    872 		return 0;
    873 	}
    874 	return -1;
    875 }
    876 
    877 uint64_t
    878 npf_rule_getid(nl_rule_t *rl)
    879 {
    880 	return dnvlist_get_number(rl->rule_dict, "id", 0);
    881 }
    882 
    883 const void *
    884 npf_rule_getcode(nl_rule_t *rl, int *type, size_t *len)
    885 {
    886 	*type = (int)dnvlist_get_number(rl->rule_dict, "code-type", 0);
    887 	return dnvlist_get_binary(rl->rule_dict, "code", len, NULL, 0);
    888 }
    889 
    890 int
    891 _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf)
    892 {
    893 	const bool natset = _npf_nat_ruleset_p(rname);
    894 	nvlist_t *req, *resp;
    895 	int error;
    896 
    897 	req = nvlist_create(0);
    898 	nvlist_add_string(req, "ruleset-name", rname);
    899 	nvlist_add_bool(req, "nat-ruleset", natset);
    900 	nvlist_add_number(req, "command", NPF_CMD_RULE_LIST);
    901 
    902 	error = _npf_xfer_fd(fd, IOC_NPF_RULE, req, &resp);
    903 	nvlist_destroy(req);
    904 	if (error) {
    905 		return error;
    906 	}
    907 
    908 	if (nvlist_exists_nvlist_array(resp, "rules")) {
    909 		nvlist_t **rules;
    910 		size_t n;
    911 
    912 		rules = nvlist_take_nvlist_array(resp, "rules", &n);
    913 		nvlist_move_nvlist_array(ncf->ncf_dict, "rules", rules, n);
    914 	}
    915 	nvlist_destroy(resp);
    916 	return 0;
    917 }
    918 
    919 void
    920 npf_rule_destroy(nl_rule_t *rl)
    921 {
    922 	nvlist_destroy(rl->rule_dict);
    923 	free(rl);
    924 }
    925 
    926 /*
    927  * RULE PROCEDURE INTERFACE.
    928  */
    929 
    930 nl_rproc_t *
    931 npf_rproc_create(const char *name)
    932 {
    933 	nl_rproc_t *rp;
    934 
    935 	rp = malloc(sizeof(nl_rproc_t));
    936 	if (!rp) {
    937 		return NULL;
    938 	}
    939 	rp->rproc_dict = nvlist_create(0);
    940 	nvlist_add_string(rp->rproc_dict, "name", name);
    941 	return rp;
    942 }
    943 
    944 int
    945 npf_rproc_extcall(nl_rproc_t *rp, nl_ext_t *ext)
    946 {
    947 	nvlist_t *rproc_dict = rp->rproc_dict;
    948 	const char *name = dnvlist_get_string(ext->ext_dict, "name", NULL);
    949 
    950 	if (_npf_dataset_lookup(rproc_dict, "extcalls", "name", name)) {
    951 		return EEXIST;
    952 	}
    953 	nvlist_append_nvlist_array(rproc_dict, "extcalls", ext->ext_dict);
    954 	nvlist_destroy(ext->ext_dict);
    955 	free(ext);
    956 	return 0;
    957 }
    958 
    959 bool
    960 npf_rproc_exists_p(nl_config_t *ncf, const char *name)
    961 {
    962 	return _npf_dataset_lookup(ncf->ncf_dict, "rprocs", "name", name);
    963 }
    964 
    965 int
    966 npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp)
    967 {
    968 	const char *name;
    969 
    970 	name = dnvlist_get_string(rp->rproc_dict, "name", NULL);
    971 	if (!name) {
    972 		return EINVAL;
    973 	}
    974 	if (npf_rproc_exists_p(ncf, name)) {
    975 		return EEXIST;
    976 	}
    977 	nvlist_append_nvlist_array(ncf->ncf_dict, "rprocs", rp->rproc_dict);
    978 	nvlist_destroy(rp->rproc_dict);
    979 	free(rp);
    980 	return 0;
    981 }
    982 
    983 nl_rproc_t *
    984 npf_rproc_iterate(nl_config_t *ncf, nl_iter_t *iter)
    985 {
    986 	const nvlist_t *rproc_dict;
    987 	unsigned i = *iter;
    988 
    989 	rproc_dict = _npf_dataset_getelement(ncf->ncf_dict, "rprocs", i);
    990 	if (!rproc_dict) {
    991 		*iter = NPF_ITER_BEGIN;
    992 		return NULL;
    993 	}
    994 	*iter = i + 1; // next
    995 	ncf->ncf_cur_rproc.rproc_dict = __UNCONST(rproc_dict); // XXX
    996 	return &ncf->ncf_cur_rproc;
    997 }
    998 
    999 const char *
   1000 npf_rproc_getname(nl_rproc_t *rp)
   1001 {
   1002 	return dnvlist_get_string(rp->rproc_dict, "name", NULL);
   1003 }
   1004 
   1005 /*
   1006  * NAT INTERFACE.
   1007  */
   1008 
   1009 nl_nat_t *
   1010 npf_nat_create(int type, unsigned flags, const char *ifname)
   1011 {
   1012 	nl_rule_t *rl;
   1013 	nvlist_t *rule_dict;
   1014 	uint32_t attr;
   1015 
   1016 	attr = NPF_RULE_PASS | NPF_RULE_FINAL | NPF_RULE_LAYER_3 |
   1017 	    (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN);
   1018 
   1019 	/* Create a rule for NAT policy.  Next, will add NAT data. */
   1020 	rl = npf_rule_create(NULL, attr, ifname);
   1021 	if (!rl) {
   1022 		return NULL;
   1023 	}
   1024 	rule_dict = rl->rule_dict;
   1025 
   1026 	/* Translation type and flags. */
   1027 	nvlist_add_number(rule_dict, "type", type);
   1028 	nvlist_add_number(rule_dict, "flags", flags);
   1029 	nvlist_add_bool(rule_dict, "nat-rule", true);
   1030 	return (nl_nat_t *)rl;
   1031 }
   1032 
   1033 int
   1034 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt)
   1035 {
   1036 	nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict);
   1037 	nvlist_destroy(nt->rule_dict);
   1038 	free(nt);
   1039 	return 0;
   1040 }
   1041 
   1042 nl_nat_t *
   1043 npf_nat_iterate(nl_config_t *ncf, nl_iter_t *iter)
   1044 {
   1045 	unsigned level;
   1046 	return _npf_rule_iterate1(ncf, "nat", iter, &level);
   1047 }
   1048 
   1049 int
   1050 npf_nat_setaddr(nl_nat_t *nt, int af, npf_addr_t *addr, npf_netmask_t mask)
   1051 {
   1052 	/* Translation IP and mask. */
   1053 	if (!_npf_add_addr(nt->rule_dict, "nat-addr", af, addr)) {
   1054 		return nvlist_error(nt->rule_dict);
   1055 	}
   1056 	nvlist_add_number(nt->rule_dict, "nat-mask", (uint32_t)mask);
   1057 	return nvlist_error(nt->rule_dict);
   1058 }
   1059 
   1060 int
   1061 npf_nat_setport(nl_nat_t *nt, in_port_t port)
   1062 {
   1063 	/* Translation port (for redirect case). */
   1064 	nvlist_add_number(nt->rule_dict, "nat-port", port);
   1065 	return nvlist_error(nt->rule_dict);
   1066 }
   1067 
   1068 int
   1069 npf_nat_settable(nl_nat_t *nt, unsigned tid)
   1070 {
   1071 	/*
   1072 	 * Translation table ID; the address/mask will then serve as a filter.
   1073 	 */
   1074 	nvlist_add_number(nt->rule_dict, "nat-table-id", tid);
   1075 	return nvlist_error(nt->rule_dict);
   1076 }
   1077 
   1078 int
   1079 npf_nat_setalgo(nl_nat_t *nt, unsigned algo)
   1080 {
   1081 	nvlist_add_number(nt->rule_dict, "nat-algo", algo);
   1082 	return nvlist_error(nt->rule_dict);
   1083 }
   1084 
   1085 int
   1086 npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj)
   1087 {
   1088 	int error;
   1089 
   1090 	if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) {
   1091 		return error;
   1092 	}
   1093 	nvlist_add_number(nt->rule_dict, "npt66-adj", adj);
   1094 	return nvlist_error(nt->rule_dict);
   1095 }
   1096 
   1097 int
   1098 npf_nat_gettype(nl_nat_t *nt)
   1099 {
   1100 	return dnvlist_get_number(nt->rule_dict, "type", 0);
   1101 }
   1102 
   1103 unsigned
   1104 npf_nat_getflags(nl_nat_t *nt)
   1105 {
   1106 	return dnvlist_get_number(nt->rule_dict, "flags", 0);
   1107 }
   1108 
   1109 unsigned
   1110 npf_nat_getalgo(nl_nat_t *nt)
   1111 {
   1112 	return dnvlist_get_number(nt->rule_dict, "nat-algo", 0);
   1113 }
   1114 
   1115 const npf_addr_t *
   1116 npf_nat_getaddr(nl_nat_t *nt, size_t *alen, npf_netmask_t *mask)
   1117 {
   1118 	const void *data;
   1119 
   1120 	if (nvlist_exists(nt->rule_dict, "nat-addr")) {
   1121 		data = nvlist_get_binary(nt->rule_dict, "nat-addr", alen);
   1122 		*mask = nvlist_get_number(nt->rule_dict, "nat-mask");
   1123 	} else {
   1124 		data = NULL;
   1125 		*alen = 0;
   1126 		*mask = NPF_NO_NETMASK;
   1127 	}
   1128 	return data;
   1129 }
   1130 
   1131 in_port_t
   1132 npf_nat_getport(nl_nat_t *nt)
   1133 {
   1134 	return (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0);
   1135 }
   1136 
   1137 unsigned
   1138 npf_nat_gettable(nl_nat_t *nt)
   1139 {
   1140 	return dnvlist_get_number(nt->rule_dict, "nat-table-id", 0);
   1141 }
   1142 
   1143 /*
   1144  * TABLE INTERFACE.
   1145  */
   1146 
   1147 nl_table_t *
   1148 npf_table_create(const char *name, unsigned id, int type)
   1149 {
   1150 	nl_table_t *tl;
   1151 
   1152 	tl = malloc(sizeof(*tl));
   1153 	if (!tl) {
   1154 		return NULL;
   1155 	}
   1156 	tl->table_dict = nvlist_create(0);
   1157 	nvlist_add_string(tl->table_dict, "name", name);
   1158 	nvlist_add_number(tl->table_dict, "id", id);
   1159 	nvlist_add_number(tl->table_dict, "type", type);
   1160 	return tl;
   1161 }
   1162 
   1163 int
   1164 npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr,
   1165     const npf_netmask_t mask)
   1166 {
   1167 	nvlist_t *entry;
   1168 
   1169 	entry = nvlist_create(0);
   1170 	if (!entry) {
   1171 		return ENOMEM;
   1172 	}
   1173 	if (!_npf_add_addr(entry, "addr", af, addr)) {
   1174 		nvlist_destroy(entry);
   1175 		return EINVAL;
   1176 	}
   1177 	nvlist_add_number(entry, "mask", mask);
   1178 	nvlist_append_nvlist_array(tl->table_dict, "entries", entry);
   1179 	nvlist_destroy(entry);
   1180 	return 0;
   1181 }
   1182 
   1183 static inline int
   1184 _npf_table_build_const(nl_table_t *tl)
   1185 {
   1186 	struct cdbw *cdbw;
   1187 	const nvlist_t * const *entries;
   1188 	int error = 0, fd = -1;
   1189 	size_t nitems, len;
   1190 	void *cdb, *buf;
   1191 	struct stat sb;
   1192 	char sfn[32];
   1193 
   1194 	if (dnvlist_get_number(tl->table_dict, "type", 0) != NPF_TABLE_CONST) {
   1195 		return 0;
   1196 	}
   1197 
   1198 	if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) {
   1199 		return 0;
   1200 	}
   1201 
   1202 	/*
   1203 	 * Create a constant database and put all the entries.
   1204 	 */
   1205 	if ((cdbw = cdbw_open()) == NULL) {
   1206 		return errno;
   1207 	}
   1208 	entries = nvlist_get_nvlist_array(tl->table_dict, "entries", &nitems);
   1209 	for (unsigned i = 0; i < nitems; i++) {
   1210 		const nvlist_t *entry = entries[i];
   1211 		const npf_addr_t *addr;
   1212 		size_t alen;
   1213 
   1214 		addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0);
   1215 		if (addr == NULL || alen == 0 || alen > sizeof(npf_addr_t)) {
   1216 			error = EINVAL;
   1217 			goto out;
   1218 		}
   1219 		if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) {
   1220 			error = errno;
   1221 			goto out;
   1222 		}
   1223 	}
   1224 
   1225 	/*
   1226 	 * Write the constant database into a temporary file.
   1227 	 */
   1228 	strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn));
   1229 	sfn[sizeof(sfn) - 1] = '\0';
   1230 
   1231 	if ((fd = mkstemp(sfn)) == -1) {
   1232 		error = errno;
   1233 		goto out;
   1234 	}
   1235 	unlink(sfn);
   1236 
   1237 	if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
   1238 		error = errno;
   1239 		goto out;
   1240 	}
   1241 	if (fstat(fd, &sb) == -1) {
   1242 		error = errno;
   1243 		goto out;
   1244 	}
   1245 	len = sb.st_size;
   1246 
   1247 	/*
   1248 	 * Memory-map the database and copy it into a buffer.
   1249 	 */
   1250 	buf = malloc(len);
   1251 	if (!buf) {
   1252 		error = ENOMEM;
   1253 		goto out;
   1254 	}
   1255 	cdb = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
   1256 	if (cdb == MAP_FAILED) {
   1257 		error = errno;
   1258 		free(buf);
   1259 		goto out;
   1260 	}
   1261 	memcpy(buf, cdb, len);
   1262 	munmap(cdb, len);
   1263 
   1264 	/*
   1265 	 * Move the data buffer to the nvlist.
   1266 	 */
   1267 	nvlist_move_binary(tl->table_dict, "data", buf, len);
   1268 	error = nvlist_error(tl->table_dict);
   1269 	if (!error) {
   1270 		nvlist_free(tl->table_dict, "entries");
   1271 	}
   1272 out:
   1273 	if (fd != -1) {
   1274 		close(fd);
   1275 	}
   1276 	cdbw_close(cdbw);
   1277 	return error;
   1278 }
   1279 
   1280 int
   1281 npf_table_insert(nl_config_t *ncf, nl_table_t *tl)
   1282 {
   1283 	const char *name;
   1284 	int error;
   1285 
   1286 	name = dnvlist_get_string(tl->table_dict, "name", NULL);
   1287 	if (!name) {
   1288 		return EINVAL;
   1289 	}
   1290 	if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) {
   1291 		return EEXIST;
   1292 	}
   1293 	if ((error = _npf_table_build_const(tl)) != 0) {
   1294 		return error;
   1295 	}
   1296 	nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict);
   1297 	nvlist_destroy(tl->table_dict);
   1298 	free(tl);
   1299 	return 0;
   1300 }
   1301 
   1302 int
   1303 npf_table_replace(int fd, nl_table_t *tl, npf_error_t *errinfo)
   1304 {
   1305 	nvlist_t *resp = NULL;
   1306 	int error;
   1307 
   1308 	/* Ensure const tables are built. */
   1309 	if ((error = _npf_table_build_const(tl)) != 0) {
   1310 		return _npf_init_error(errno, errinfo);
   1311 	}
   1312 	error = _npf_xfer_fd(fd, IOC_NPF_TABLE_REPLACE, tl->table_dict, &resp);
   1313 	if (error) {
   1314 		assert(resp == NULL);
   1315 		return _npf_init_error(errno, errinfo);
   1316 	}
   1317 	error = _npf_extract_error(resp, errinfo);
   1318 	nvlist_destroy(resp);
   1319 	return error;
   1320 }
   1321 
   1322 nl_table_t *
   1323 npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter)
   1324 {
   1325 	const nvlist_t *table_dict;
   1326 	unsigned i = *iter;
   1327 
   1328 	table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i);
   1329 	if (!table_dict) {
   1330 		*iter = NPF_ITER_BEGIN;
   1331 		return NULL;
   1332 	}
   1333 	*iter = i + 1; // next
   1334 	ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX
   1335 	return &ncf->ncf_cur_table;
   1336 }
   1337 
   1338 unsigned
   1339 npf_table_getid(nl_table_t *tl)
   1340 {
   1341 	return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1);
   1342 }
   1343 
   1344 const char *
   1345 npf_table_getname(nl_table_t *tl)
   1346 {
   1347 	return dnvlist_get_string(tl->table_dict, "name", NULL);
   1348 }
   1349 
   1350 int
   1351 npf_table_gettype(nl_table_t *tl)
   1352 {
   1353 	return dnvlist_get_number(tl->table_dict, "type", 0);
   1354 }
   1355 
   1356 void
   1357 npf_table_destroy(nl_table_t *tl)
   1358 {
   1359 	nvlist_destroy(tl->table_dict);
   1360 	free(tl);
   1361 }
   1362 
   1363 /*
   1364  * ALG INTERFACE.
   1365  */
   1366 
   1367 int
   1368 npf_alg_load(nl_config_t *ncf, const char *name)
   1369 {
   1370 	nvlist_t *alg_dict;
   1371 
   1372 	if (_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) {
   1373 		return EEXIST;
   1374 	}
   1375 	alg_dict = nvlist_create(0);
   1376 	nvlist_add_string(alg_dict, "name", name);
   1377 	nvlist_append_nvlist_array(ncf->ncf_dict, "algs", alg_dict);
   1378 	nvlist_destroy(alg_dict);
   1379 	return 0;
   1380 }
   1381 
   1382 /*
   1383  * CONNECTION / NAT ENTRY INTERFACE.
   1384  */
   1385 
   1386 typedef struct {
   1387 	unsigned	alen;
   1388 	unsigned	proto;
   1389 	npf_addr_t	addr[3];
   1390 	in_port_t	port[3];
   1391 } npf_connpoint_t;
   1392 
   1393 static int
   1394 _npf_conn_lookup(int fd, const int af, npf_addr_t *addr[2], in_port_t port[2],
   1395     unsigned proto, const char *ifname, unsigned di)
   1396 {
   1397 	nvlist_t *req = NULL, *resp = NULL, *key_nv;
   1398 	const nvlist_t *nat;
   1399 	int error = EINVAL;
   1400 
   1401 	/*
   1402 	 * Setup the connection lookup key.
   1403 	 */
   1404 	if ((key_nv = nvlist_create(0)) == NULL) {
   1405 		return ENOMEM;
   1406 	}
   1407 	if (!_npf_add_addr(key_nv, "saddr", af, addr[0])) {
   1408 		nvlist_destroy(key_nv);
   1409 		goto out;
   1410 	}
   1411 	if (!_npf_add_addr(key_nv, "daddr", af, addr[1])) {
   1412 		nvlist_destroy(key_nv);
   1413 		goto out;
   1414 	}
   1415 	nvlist_add_number(key_nv, "sport", htons(port[0]));
   1416 	nvlist_add_number(key_nv, "dport", htons(port[1]));
   1417 	nvlist_add_number(key_nv, "proto", proto);
   1418 	if (ifname) {
   1419 		nvlist_add_string(key_nv, "ifname", ifname);
   1420 	}
   1421 	if (di) {
   1422 		nvlist_add_number(key_nv, "di", di);
   1423 	}
   1424 
   1425 	/*
   1426 	 * Setup the request.
   1427 	 */
   1428 	if ((req = nvlist_create(0)) == NULL) {
   1429 		error = ENOMEM;
   1430 		goto out;
   1431 	}
   1432 	nvlist_move_nvlist(req, "key", key_nv);
   1433 
   1434 	/* Lookup: retrieve the connection entry. */
   1435 	error = _npf_xfer_fd(fd, IOC_NPF_CONN_LOOKUP, req, &resp);
   1436 	if (error) {
   1437 		goto out;
   1438 	}
   1439 
   1440 	/*
   1441 	 * Get the NAT entry and extract the translated pair.
   1442 	 */
   1443 	if ((nat = dnvlist_get_nvlist(resp, "nat", NULL)) == NULL) {
   1444 		error = ENOENT;
   1445 		goto out;
   1446 	}
   1447 	if (_npf_get_addr(nat, "oaddr", addr[0]) == 0 ||
   1448 	    _npf_get_addr(nat, "taddr", addr[1]) == 0) {
   1449 		error = EINVAL;
   1450 		goto out;
   1451 	}
   1452 	port[0] = ntohs(nvlist_get_number(nat, "oport"));
   1453 	port[1] = ntohs(nvlist_get_number(nat, "tport"));
   1454 out:
   1455 	if (resp) {
   1456 		nvlist_destroy(resp);
   1457 	}
   1458 	if (req) {
   1459 		nvlist_destroy(req);
   1460 	}
   1461 	return error;
   1462 }
   1463 
   1464 int
   1465 npf_nat_lookup(int fd, int af, npf_addr_t *addr[2], in_port_t port[2],
   1466     int proto, int di __unused)
   1467 {
   1468 	int error;
   1469 
   1470 	port[0] = ntohs(port[0]); port[1] = ntohs(port[1]);
   1471 	error = _npf_conn_lookup(fd, af, addr, port, proto, NULL, 0);
   1472 	port[0] = htons(port[0]); port[1] = htons(port[1]);
   1473 	return error;
   1474 }
   1475 
   1476 static bool
   1477 npf_connkey_handle(const nvlist_t *key_nv, npf_connpoint_t *ep)
   1478 {
   1479 	unsigned alen1, alen2;
   1480 
   1481 	alen1 = _npf_get_addr(key_nv, "saddr", &ep->addr[0]);
   1482 	alen2 = _npf_get_addr(key_nv, "daddr", &ep->addr[1]);
   1483 	if (alen1 == 0 || alen1 != alen2) {
   1484 		return false;
   1485 	}
   1486 	ep->alen = alen1;
   1487 	ep->port[0] = ntohs(nvlist_get_number(key_nv, "sport"));
   1488 	ep->port[1] = ntohs(nvlist_get_number(key_nv, "dport"));
   1489 	ep->proto = nvlist_get_number(key_nv, "proto");
   1490 	return true;
   1491 }
   1492 
   1493 static void
   1494 npf_conn_handle(const nvlist_t *conn, npf_conn_func_t func, void *arg)
   1495 {
   1496 	const nvlist_t *key_nv, *nat_nv;
   1497 	const char *ifname;
   1498 	npf_connpoint_t ep;
   1499 
   1500 	memset(&ep, 0, sizeof(npf_connpoint_t));
   1501 
   1502 	ifname = dnvlist_get_string(conn, "ifname", NULL);
   1503 	key_nv = dnvlist_get_nvlist(conn, "forw-key", NULL);
   1504 	if (!npf_connkey_handle(key_nv, &ep)) {
   1505 		goto err;
   1506 	}
   1507 	if ((nat_nv = dnvlist_get_nvlist(conn, "nat", NULL)) != NULL) {
   1508 		if (_npf_get_addr(nat_nv, "taddr", &ep.addr[2]) != ep.alen) {
   1509 			goto err;
   1510 		}
   1511 		ep.port[2] = ntohs(nvlist_get_number(nat_nv, "tport"));
   1512 	}
   1513 	/*
   1514 	 * XXX: add 'proto' and 'flow'; perhaps expand and pass the
   1515 	 * whole to npf_connpoint_t?
   1516 	 */
   1517 	(*func)((unsigned)ep.alen, ep.addr, ep.port, ifname, arg);
   1518 err:
   1519 	return;
   1520 }
   1521 
   1522 int
   1523 npf_conn_list(int fd, npf_conn_func_t func, void *arg)
   1524 {
   1525 	nl_config_t *ncf;
   1526 	const nvlist_t * const *conns;
   1527 	size_t nitems;
   1528 
   1529 	ncf = npf_config_retrieve(fd);
   1530 	if (!ncf) {
   1531 		return errno;
   1532 	}
   1533 	if (!nvlist_exists_nvlist_array(ncf->ncf_dict, "conn-list")) {
   1534 		return 0;
   1535 	}
   1536 	conns = nvlist_get_nvlist_array(ncf->ncf_dict, "conn-list", &nitems);
   1537 	for (unsigned i = 0; i < nitems; i++) {
   1538 		const nvlist_t *conn = conns[i];
   1539 		npf_conn_handle(conn, func, arg);
   1540 	}
   1541 	npf_config_destroy(ncf);
   1542 	return 0;
   1543 }
   1544 
   1545 /*
   1546  * MISC.
   1547  */
   1548 
   1549 void
   1550 _npf_debug_addif(nl_config_t *ncf, const char *ifname)
   1551 {
   1552 	nvlist_t *debug;
   1553 
   1554 	/*
   1555 	 * Initialise the debug dictionary on the first call.
   1556 	 */
   1557 	debug = dnvlist_take_nvlist(ncf->ncf_dict, "debug", NULL);
   1558 	if (debug == NULL) {
   1559 		debug = nvlist_create(0);
   1560 	}
   1561 	if (!_npf_dataset_lookup(debug, "interfaces", "name", ifname)) {
   1562 		nvlist_t *ifdict = nvlist_create(0);
   1563 		nvlist_add_string(ifdict, "name", ifname);
   1564 		nvlist_add_number(ifdict, "index", if_nametoindex(ifname));
   1565 		nvlist_append_nvlist_array(debug, "interfaces", ifdict);
   1566 		nvlist_destroy(ifdict);
   1567 	}
   1568 	nvlist_move_nvlist(ncf->ncf_dict, "debug", debug);
   1569 }
   1570 
   1571 void
   1572 _npf_config_dump(nl_config_t *ncf, int fd)
   1573 {
   1574 	(void)npf_config_build(ncf);
   1575 	nvlist_dump(ncf->ncf_dict, fd);
   1576 }
   1577