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