xref: /netbsd-src/lib/libnpf/npf.c (revision b3b6b684ebcae901a84fa7c62c5b737fb3c45ce5)
107ac07d3Srmind /*-
2b899bfd9Srmind  * Copyright (c) 2010-2020 The NetBSD Foundation, Inc.
307ac07d3Srmind  * All rights reserved.
407ac07d3Srmind  *
507ac07d3Srmind  * This material is based upon work partially supported by The
607ac07d3Srmind  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
707ac07d3Srmind  *
807ac07d3Srmind  * Redistribution and use in source and binary forms, with or without
907ac07d3Srmind  * modification, are permitted provided that the following conditions
1007ac07d3Srmind  * are met:
1107ac07d3Srmind  * 1. Redistributions of source code must retain the above copyright
1207ac07d3Srmind  *    notice, this list of conditions and the following disclaimer.
1307ac07d3Srmind  * 2. Redistributions in binary form must reproduce the above copyright
1407ac07d3Srmind  *    notice, this list of conditions and the following disclaimer in the
1507ac07d3Srmind  *    documentation and/or other materials provided with the distribution.
1607ac07d3Srmind  *
1707ac07d3Srmind  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1807ac07d3Srmind  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1907ac07d3Srmind  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2007ac07d3Srmind  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2107ac07d3Srmind  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2207ac07d3Srmind  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2307ac07d3Srmind  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2407ac07d3Srmind  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2507ac07d3Srmind  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2607ac07d3Srmind  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2707ac07d3Srmind  * POSSIBILITY OF SUCH DAMAGE.
2807ac07d3Srmind  */
2907ac07d3Srmind 
3007ac07d3Srmind #include <sys/cdefs.h>
31*b3b6b684Sriastradh __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.52 2023/08/08 10:36:04 riastradh Exp $");
3207ac07d3Srmind 
3307ac07d3Srmind #include <sys/types.h>
3439013e66Srmind #include <sys/mman.h>
3539013e66Srmind #include <sys/stat.h>
36b899bfd9Srmind #if !defined(_NPF_STANDALONE)
37b899bfd9Srmind #include <sys/ioctl.h>
38b899bfd9Srmind #endif
3907ac07d3Srmind #include <netinet/in_systm.h>
4007ac07d3Srmind #include <netinet/in.h>
4163f44833Srmind #include <net/if.h>
4207ac07d3Srmind 
4307ac07d3Srmind #include <stdlib.h>
4407ac07d3Srmind #include <string.h>
45f7fec0d2Srmind #include <assert.h>
4639013e66Srmind #include <unistd.h>
4707ac07d3Srmind #include <errno.h>
4807ac07d3Srmind #include <err.h>
4907ac07d3Srmind 
5039013e66Srmind #include <nv.h>
5139013e66Srmind #include <dnv.h>
5239013e66Srmind 
5339013e66Srmind #include <cdbw.h>
5439013e66Srmind 
5507ac07d3Srmind #define	_NPF_PRIVATE
5607ac07d3Srmind #include "npf.h"
5707ac07d3Srmind 
5807ac07d3Srmind struct nl_rule {
5939013e66Srmind 	nvlist_t *	rule_dict;
6007ac07d3Srmind };
6107ac07d3Srmind 
6207ac07d3Srmind struct nl_rproc {
6339013e66Srmind 	nvlist_t *	rproc_dict;
6407ac07d3Srmind };
6507ac07d3Srmind 
6607ac07d3Srmind struct nl_table {
6739013e66Srmind 	nvlist_t *	table_dict;
6807ac07d3Srmind };
6907ac07d3Srmind 
70bc0f55deSchristos struct nl_alg {
7139013e66Srmind 	nvlist_t *	alg_dict;
72bc0f55deSchristos };
73bc0f55deSchristos 
748c6e21bfSrmind struct nl_ext {
7539013e66Srmind 	nvlist_t *	ext_dict;
768c6e21bfSrmind };
778c6e21bfSrmind 
784e592132Srmind struct nl_config {
7939013e66Srmind 	nvlist_t *	ncf_dict;
8039013e66Srmind 
8139013e66Srmind 	/* Temporary rule list. */
8239013e66Srmind 	nvlist_t **	ncf_rule_list;
8339013e66Srmind 	unsigned	ncf_rule_count;
844e592132Srmind 
854e592132Srmind 	/* Iterators. */
864e592132Srmind 	unsigned	ncf_reduce[16];
874e592132Srmind 	unsigned	ncf_nlevel;
88dadc88e3Srmind 
894e592132Srmind 	nl_rule_t	ncf_cur_rule;
904e592132Srmind 	nl_table_t	ncf_cur_table;
914e592132Srmind 	nl_rproc_t	ncf_cur_rproc;
924e592132Srmind };
934e592132Srmind 
9439013e66Srmind /*
9539013e66Srmind  * Various helper routines.
9639013e66Srmind  */
970e218254Srmind 
983d5a430cSchristos static bool
_npf_add_addr(nvlist_t * nvl,const char * name,int af,const npf_addr_t * addr)9939013e66Srmind _npf_add_addr(nvlist_t *nvl, const char *name, int af, const npf_addr_t *addr)
1003d5a430cSchristos {
1013d5a430cSchristos 	size_t sz;
1023d5a430cSchristos 
1033d5a430cSchristos 	if (af == AF_INET) {
1043d5a430cSchristos 		sz = sizeof(struct in_addr);
1053d5a430cSchristos 	} else if (af == AF_INET6) {
1063d5a430cSchristos 		sz = sizeof(struct in6_addr);
1073d5a430cSchristos 	} else {
1083d5a430cSchristos 		return false;
1093d5a430cSchristos 	}
11039013e66Srmind 	nvlist_add_binary(nvl, name, addr, sz);
11139013e66Srmind 	return nvlist_error(nvl) == 0;
1123d5a430cSchristos }
1133d5a430cSchristos 
11452d8bce5Schristos static unsigned
_npf_get_addr(const nvlist_t * nvl,const char * name,npf_addr_t * addr)11539013e66Srmind _npf_get_addr(const nvlist_t *nvl, const char *name, npf_addr_t *addr)
1163d5a430cSchristos {
11739013e66Srmind 	const void *d;
11839013e66Srmind 	size_t sz = 0;
1193d5a430cSchristos 
12039013e66Srmind 	d = nvlist_get_binary(nvl, name, &sz);
1213d5a430cSchristos 	switch (sz) {
1223d5a430cSchristos 	case sizeof(struct in_addr):
1233d5a430cSchristos 	case sizeof(struct in6_addr):
1243d5a430cSchristos 		memcpy(addr, d, sz);
12552d8bce5Schristos 		return (unsigned)sz;
12639013e66Srmind 	}
12752d8bce5Schristos 	return 0;
1283d5a430cSchristos }
12939013e66Srmind 
13039013e66Srmind static bool
_npf_dataset_lookup(const nvlist_t * dict,const char * dataset,const char * key,const char * name)13139013e66Srmind _npf_dataset_lookup(const nvlist_t *dict, const char *dataset,
13239013e66Srmind     const char *key, const char *name)
13339013e66Srmind {
13439013e66Srmind 	const nvlist_t * const *items;
13539013e66Srmind 	size_t nitems;
13639013e66Srmind 
13739013e66Srmind 	if (!nvlist_exists_nvlist_array(dict, dataset)) {
13839013e66Srmind 		return false;
13939013e66Srmind 	}
14039013e66Srmind 	items = nvlist_get_nvlist_array(dict, dataset, &nitems);
14139013e66Srmind 	for (unsigned i = 0; i < nitems; i++) {
14239013e66Srmind 		const char *item_name;
14339013e66Srmind 
14439013e66Srmind 		item_name = dnvlist_get_string(items[i], key, NULL);
14539013e66Srmind 		if (item_name && strcmp(item_name, name) == 0) {
14639013e66Srmind 			return true;
14739013e66Srmind 		}
14839013e66Srmind 	}
14939013e66Srmind 	return false;
15039013e66Srmind }
15139013e66Srmind 
15239013e66Srmind static const nvlist_t *
_npf_dataset_getelement(nvlist_t * dict,const char * dataset,unsigned i)15339013e66Srmind _npf_dataset_getelement(nvlist_t *dict, const char *dataset, unsigned i)
15439013e66Srmind {
15539013e66Srmind 	const nvlist_t * const *items;
15639013e66Srmind 	size_t nitems;
15739013e66Srmind 
15839013e66Srmind 	if (!nvlist_exists_nvlist_array(dict, dataset)) {
15939013e66Srmind 		return NULL;
16039013e66Srmind 	}
16139013e66Srmind 	items = nvlist_get_nvlist_array(dict, dataset, &nitems);
16239013e66Srmind 	if (i < nitems) {
16339013e66Srmind 		return items[i];
16439013e66Srmind 	}
16539013e66Srmind 	return NULL;
16639013e66Srmind }
16739013e66Srmind 
16839013e66Srmind /*
16939013e66Srmind  * _npf_rules_process: transform the ruleset representing nested rules
17039013e66Srmind  * with sublists into a single array with skip-to marks.
17139013e66Srmind  */
17239013e66Srmind static void
_npf_rules_process(nl_config_t * ncf,nvlist_t * dict,const char * key)17339013e66Srmind _npf_rules_process(nl_config_t *ncf, nvlist_t *dict, const char *key)
17439013e66Srmind {
17539013e66Srmind 	nvlist_t **items;
17639013e66Srmind 	size_t nitems;
17739013e66Srmind 
17839013e66Srmind 	if (!nvlist_exists_nvlist_array(dict, key)) {
17939013e66Srmind 		return;
18039013e66Srmind 	}
18139013e66Srmind 	items = nvlist_take_nvlist_array(dict, key, &nitems);
18239013e66Srmind 	for (unsigned i = 0; i < nitems; i++) {
18339013e66Srmind 		nvlist_t *rule_dict = items[i];
18439013e66Srmind 		size_t len = (ncf->ncf_rule_count + 1) * sizeof(nvlist_t *);
18539013e66Srmind 		void *p = realloc(ncf->ncf_rule_list, len);
18639013e66Srmind 
18739013e66Srmind 		/*
18839013e66Srmind 		 * - Add rule to the transformed array.
18939013e66Srmind 		 * - Process subrules recursively.
19039013e66Srmind 		 * - Add the skip-to position.
19139013e66Srmind 		 */
19239013e66Srmind 		ncf->ncf_rule_list = p;
19339013e66Srmind 		ncf->ncf_rule_list[ncf->ncf_rule_count] = rule_dict;
19439013e66Srmind 		ncf->ncf_rule_count++;
19539013e66Srmind 
19639013e66Srmind 		if (nvlist_exists_nvlist_array(rule_dict, "subrules")) {
19739013e66Srmind 			unsigned idx;
19839013e66Srmind 
19939013e66Srmind 			_npf_rules_process(ncf, rule_dict, "subrules");
20039013e66Srmind 			idx = ncf->ncf_rule_count; // post-recursion index
20139013e66Srmind 			nvlist_add_number(rule_dict, "skip-to", idx);
20239013e66Srmind 		}
20339013e66Srmind 		assert(nvlist_error(rule_dict) == 0);
20439013e66Srmind 	}
20539013e66Srmind 	free(items);
2063d5a430cSchristos }
2073d5a430cSchristos 
20807ac07d3Srmind /*
209712e653aSchristos  * _npf_init_error: initialize the error structure with the message
210712e653aSchristos  * from the current error number
211712e653aSchristos  */
212712e653aSchristos static int
_npf_init_error(int error,npf_error_t * errinfo)213712e653aSchristos _npf_init_error(int error, npf_error_t *errinfo)
214712e653aSchristos {
215712e653aSchristos 	if (error && errinfo) {
216712e653aSchristos 		memset(errinfo, 0, sizeof(*errinfo));
217712e653aSchristos 		errinfo->error_msg = strerror(error);
218712e653aSchristos 	}
219712e653aSchristos 	return error;
220712e653aSchristos }
221712e653aSchristos 
222712e653aSchristos /*
2230e1944daSrmind  * _npf_extract_error: check the error number field and extract the
2240e1944daSrmind  * error details into the npf_error_t structure.
2250e1944daSrmind  */
2260e1944daSrmind static int
_npf_extract_error(nvlist_t * resp,npf_error_t * errinfo)2270e1944daSrmind _npf_extract_error(nvlist_t *resp, npf_error_t *errinfo)
2280e1944daSrmind {
2290e1944daSrmind 	int error;
2300e1944daSrmind 
2310e1944daSrmind 	error = dnvlist_get_number(resp, "errno", 0);
2320e1944daSrmind 	if (error && errinfo) {
2330e1944daSrmind 		memset(errinfo, 0, sizeof(npf_error_t));
2340e1944daSrmind 
2350e1944daSrmind 		errinfo->id = dnvlist_get_number(resp, "id", 0);
2360e1944daSrmind 		errinfo->error_msg =
2370e1944daSrmind 		    dnvlist_take_string(resp, "error-msg", NULL);
2380e1944daSrmind 		errinfo->source_file =
2390e1944daSrmind 		    dnvlist_take_string(resp, "source-file", NULL);
2400e1944daSrmind 		errinfo->source_line =
2410e1944daSrmind 		    dnvlist_take_number(resp, "source-line", 0);
2420e1944daSrmind 	}
2430e1944daSrmind 	return error;
2440e1944daSrmind }
2450e1944daSrmind 
2460e1944daSrmind /*
247b899bfd9Srmind  * npf_xfer_fd: transfer the given request and receive a response.
248b899bfd9Srmind  *
249b899bfd9Srmind  * => Sets the 'operation' key on the 'req' dictionary.
250b899bfd9Srmind  * => On success: returns 0 and valid nvlist in 'resp'.
251b899bfd9Srmind  * => On failure: returns an error number.
252b899bfd9Srmind  */
253b899bfd9Srmind static int
_npf_xfer_fd(int fd,unsigned long cmd,nvlist_t * req,nvlist_t ** resp)254b899bfd9Srmind _npf_xfer_fd(int fd, unsigned long cmd, nvlist_t *req, nvlist_t **resp)
255b899bfd9Srmind {
256b899bfd9Srmind 	struct stat st;
257b899bfd9Srmind 	int kernver;
258b899bfd9Srmind 
259b899bfd9Srmind 	/*
260b899bfd9Srmind 	 * Set the NPF version and operation.
261b899bfd9Srmind 	 */
262b899bfd9Srmind 	if (!nvlist_exists(req, "version")) {
263b899bfd9Srmind 		nvlist_add_number(req, "version", NPF_VERSION);
264b899bfd9Srmind 	}
265b899bfd9Srmind 	nvlist_add_number(req, "operation", cmd);
266b899bfd9Srmind 
267b899bfd9Srmind 	/*
268b899bfd9Srmind 	 * Determine the type of file descriptor:
269b899bfd9Srmind 	 * - If socket, then perform nvlist_send()/nvlist_recv().
270b899bfd9Srmind 	 * - If a character device, then use ioctl.
271b899bfd9Srmind 	 */
272b899bfd9Srmind 	if (fstat(fd, &st) == -1) {
273b899bfd9Srmind 		goto err;
274b899bfd9Srmind 	}
275b899bfd9Srmind 	switch (st.st_mode & S_IFMT) {
276b899bfd9Srmind #if !defined(__NetBSD__)
277b899bfd9Srmind 	case S_IFSOCK:
278b899bfd9Srmind 		if (nvlist_send(fd, req) == -1) {
279b899bfd9Srmind 			goto err;
280b899bfd9Srmind 		}
281b899bfd9Srmind 		if (resp && (*resp = nvlist_recv(fd, 0)) == NULL) {
282b899bfd9Srmind 			goto err;
283b899bfd9Srmind 		}
284b899bfd9Srmind 		break;
285b899bfd9Srmind #endif
286b899bfd9Srmind #if !defined(_NPF_STANDALONE)
287b899bfd9Srmind 	case S_IFBLK:
288b899bfd9Srmind 	case S_IFCHR:
289b899bfd9Srmind 		if (ioctl(fd, IOC_NPF_VERSION, &kernver) == -1) {
290b899bfd9Srmind 			goto err;
291b899bfd9Srmind 		}
292b899bfd9Srmind 		if (kernver != NPF_VERSION) {
293b899bfd9Srmind 			errno = EPROGMISMATCH;
294b899bfd9Srmind 			goto err;
295b899bfd9Srmind 		}
296b899bfd9Srmind 		if (nvlist_xfer_ioctl(fd, cmd, req, resp) == -1) {
297b899bfd9Srmind 			goto err;
298b899bfd9Srmind 		}
299b899bfd9Srmind 		break;
300b899bfd9Srmind #else
301b899bfd9Srmind 		(void)kernver;
302b899bfd9Srmind #endif
303b899bfd9Srmind 	default:
304b899bfd9Srmind 		errno = ENOTSUP;
305b899bfd9Srmind 		goto err;
306b899bfd9Srmind 	}
307b899bfd9Srmind 	return 0;
308b899bfd9Srmind err:
309b899bfd9Srmind 	return errno ? errno : EIO;
310b899bfd9Srmind }
311b899bfd9Srmind 
312b899bfd9Srmind /*
313b899bfd9Srmind  * npf_xfer_fd_errno: same as npf_xfer_fd(), but:
314b899bfd9Srmind  *
315b899bfd9Srmind  * => After successful retrieval of the response, inspects it, extracts
316b899bfd9Srmind  *    the 'errno' value (if any) and returns it.
317b899bfd9Srmind  * => Destroys the response.
318b899bfd9Srmind  */
319b899bfd9Srmind static int
_npf_xfer_fd_errno(int fd,unsigned long cmd,nvlist_t * req)320b899bfd9Srmind _npf_xfer_fd_errno(int fd, unsigned long cmd, nvlist_t *req)
321b899bfd9Srmind {
322b899bfd9Srmind 	nvlist_t *resp;
323b899bfd9Srmind 	int error;
324b899bfd9Srmind 
325b899bfd9Srmind 	error = _npf_xfer_fd(fd, cmd, req, &resp);
326b899bfd9Srmind 	if (error) {
327b899bfd9Srmind 		return error;
328b899bfd9Srmind 	}
329b899bfd9Srmind 	error = _npf_extract_error(resp, NULL);
330b899bfd9Srmind 	nvlist_destroy(resp);
331b899bfd9Srmind 	return error;
332b899bfd9Srmind }
333b899bfd9Srmind 
334b899bfd9Srmind /*
33507ac07d3Srmind  * CONFIGURATION INTERFACE.
33607ac07d3Srmind  */
33707ac07d3Srmind 
33807ac07d3Srmind nl_config_t *
npf_config_create(void)33907ac07d3Srmind npf_config_create(void)
34007ac07d3Srmind {
34107ac07d3Srmind 	nl_config_t *ncf;
34207ac07d3Srmind 
34339013e66Srmind 	ncf = calloc(1, sizeof(nl_config_t));
34439013e66Srmind 	if (!ncf) {
34507ac07d3Srmind 		return NULL;
34607ac07d3Srmind 	}
34739013e66Srmind 	ncf->ncf_dict = nvlist_create(0);
34839013e66Srmind 	nvlist_add_number(ncf->ncf_dict, "version", NPF_VERSION);
34907ac07d3Srmind 	return ncf;
35007ac07d3Srmind }
35107ac07d3Srmind 
352f75d79ebSchristos int
npf_config_submit(nl_config_t * ncf,int fd,npf_error_t * errinfo)353f75d79ebSchristos npf_config_submit(nl_config_t *ncf, int fd, npf_error_t *errinfo)
354f75d79ebSchristos {
355b899bfd9Srmind 	nvlist_t *resp = NULL;
35639013e66Srmind 	int error;
357f75d79ebSchristos 
35839013e66Srmind 	/* Ensure the config is built. */
35939013e66Srmind 	(void)npf_config_build(ncf);
36039013e66Srmind 
361b899bfd9Srmind 	error = _npf_xfer_fd(fd, IOC_NPF_LOAD, ncf->ncf_dict, &resp);
362b899bfd9Srmind 	if (error) {
363712e653aSchristos 		return _npf_init_error(errno, errinfo);
36407ac07d3Srmind 	}
365b899bfd9Srmind 	error = _npf_extract_error(resp, errinfo);
366b899bfd9Srmind 	nvlist_destroy(resp);
367f7fec0d2Srmind 	return error;
368f7fec0d2Srmind }
3695f6fa3d5Srmind 
370a02b7176Srmind nl_config_t *
npf_config_retrieve(int fd)371f75d79ebSchristos npf_config_retrieve(int fd)
372a02b7176Srmind {
373a02b7176Srmind 	nl_config_t *ncf;
374b899bfd9Srmind 	nvlist_t *req, *resp = NULL;
375b899bfd9Srmind 	int error;
376a02b7176Srmind 
37739013e66Srmind 	ncf = calloc(1, sizeof(nl_config_t));
37839013e66Srmind 	if (!ncf) {
379a02b7176Srmind 		return NULL;
380a02b7176Srmind 	}
381b899bfd9Srmind 
382b899bfd9Srmind 	req = nvlist_create(0);
383b899bfd9Srmind 	error = _npf_xfer_fd(fd, IOC_NPF_SAVE, req, &resp);
384b899bfd9Srmind 	nvlist_destroy(req);
385b899bfd9Srmind 
386b899bfd9Srmind 	if (error || _npf_extract_error(resp, NULL) != 0) {
387b899bfd9Srmind 		nvlist_destroy(resp);
38839013e66Srmind 		free(ncf);
389a02b7176Srmind 		return NULL;
390a02b7176Srmind 	}
391b899bfd9Srmind 	ncf->ncf_dict = resp;
3925f6fa3d5Srmind 	return ncf;
3935f6fa3d5Srmind }
3945f6fa3d5Srmind 
395f75d79ebSchristos void *
npf_config_export(nl_config_t * ncf,size_t * length)396f75d79ebSchristos npf_config_export(nl_config_t *ncf, size_t *length)
397a02b7176Srmind {
39839013e66Srmind 	/* Ensure the config is built. */
39939013e66Srmind 	(void)npf_config_build(ncf);
40039013e66Srmind 	return nvlist_pack(ncf->ncf_dict, length);
401a02b7176Srmind }
402a02b7176Srmind 
403a02b7176Srmind nl_config_t *
npf_config_import(const void * blob,size_t len)40439013e66Srmind npf_config_import(const void *blob, size_t len)
405a02b7176Srmind {
406a02b7176Srmind 	nl_config_t *ncf;
407a02b7176Srmind 
40839013e66Srmind 	ncf = calloc(1, sizeof(nl_config_t));
40939013e66Srmind 	if (!ncf) {
410a02b7176Srmind 		return NULL;
411a02b7176Srmind 	}
41239013e66Srmind 	ncf->ncf_dict = nvlist_unpack(blob, len, 0);
41339013e66Srmind 	if (!ncf->ncf_dict) {
41439013e66Srmind 		free(ncf);
415a02b7176Srmind 		return NULL;
416a02b7176Srmind 	}
417a02b7176Srmind 	return ncf;
418a02b7176Srmind }
419a02b7176Srmind 
420a02b7176Srmind int
npf_config_flush(int fd)4214b85474bSrmind npf_config_flush(int fd)
4224b85474bSrmind {
4234b85474bSrmind 	nl_config_t *ncf;
424f75d79ebSchristos 	npf_error_t errinfo;
4254b85474bSrmind 	int error;
4264b85474bSrmind 
4274b85474bSrmind 	ncf = npf_config_create();
42839013e66Srmind 	if (!ncf) {
4294b85474bSrmind 		return ENOMEM;
4304b85474bSrmind 	}
43139013e66Srmind 	nvlist_add_bool(ncf->ncf_dict, "flush", true);
432f75d79ebSchristos 	error = npf_config_submit(ncf, fd, &errinfo);
4334b85474bSrmind 	npf_config_destroy(ncf);
4344b85474bSrmind 	return error;
4354b85474bSrmind }
4364b85474bSrmind 
437f75d79ebSchristos bool
npf_config_active_p(nl_config_t * ncf)438f75d79ebSchristos npf_config_active_p(nl_config_t *ncf)
439f7fec0d2Srmind {
44039013e66Srmind 	return dnvlist_get_bool(ncf->ncf_dict, "active", false);
441f75d79ebSchristos }
442f75d79ebSchristos 
443f75d79ebSchristos bool
npf_config_loaded_p(nl_config_t * ncf)444f75d79ebSchristos npf_config_loaded_p(nl_config_t *ncf)
445f75d79ebSchristos {
44639013e66Srmind 	return nvlist_exists_nvlist_array(ncf->ncf_dict, "rules");
447f75d79ebSchristos }
448f75d79ebSchristos 
449b899bfd9Srmind const void *
npf_config_build(nl_config_t * ncf)450f75d79ebSchristos npf_config_build(nl_config_t *ncf)
451f75d79ebSchristos {
45239013e66Srmind 	_npf_rules_process(ncf, ncf->ncf_dict, "__rules");
45339013e66Srmind 	if (ncf->ncf_rule_list) {
45439013e66Srmind 		/* Set the transformed ruleset. */
45539013e66Srmind 		nvlist_move_nvlist_array(ncf->ncf_dict, "rules",
45639013e66Srmind 		    ncf->ncf_rule_list, ncf->ncf_rule_count);
45739013e66Srmind 
45839013e66Srmind 		/* Clear the temporary list. */
45939013e66Srmind 		ncf->ncf_rule_list = NULL;
46039013e66Srmind 		ncf->ncf_rule_count = 0;
461f75d79ebSchristos 	}
46239013e66Srmind 	assert(nvlist_error(ncf->ncf_dict) == 0);
463f75d79ebSchristos 	return (void *)ncf->ncf_dict;
464f7fec0d2Srmind }
465f7fec0d2Srmind 
466f7fec0d2Srmind void
npf_config_destroy(nl_config_t * ncf)46707ac07d3Srmind npf_config_destroy(nl_config_t *ncf)
46807ac07d3Srmind {
46939013e66Srmind 	nvlist_destroy(ncf->ncf_dict);
47007ac07d3Srmind 	free(ncf);
47107ac07d3Srmind }
47207ac07d3Srmind 
47307ac07d3Srmind /*
474dadc88e3Srmind  * PARAMETERS.
475dadc88e3Srmind  */
476dadc88e3Srmind 
477dadc88e3Srmind int
npf_param_get(nl_config_t * ncf,const char * name,int * valp)478dadc88e3Srmind npf_param_get(nl_config_t *ncf, const char *name, int *valp)
479dadc88e3Srmind {
480dadc88e3Srmind 	const nvlist_t *params;
481dadc88e3Srmind 
482dadc88e3Srmind 	params = dnvlist_get_nvlist(ncf->ncf_dict, "params", NULL);
483dadc88e3Srmind 	if (params == NULL || !nvlist_exists(params, name)) {
484dadc88e3Srmind 		return ENOENT;
485dadc88e3Srmind 	}
486dadc88e3Srmind 	*valp = (int)dnvlist_get_number(params, name, 0);
487dadc88e3Srmind 	return 0;
488dadc88e3Srmind }
489dadc88e3Srmind 
490dadc88e3Srmind int
npf_param_set(nl_config_t * ncf,const char * name,int val)491dadc88e3Srmind npf_param_set(nl_config_t *ncf, const char *name, int val)
492dadc88e3Srmind {
493dadc88e3Srmind 	nvlist_t *params;
494dadc88e3Srmind 
495dadc88e3Srmind 	/* Ensure params dictionary. */
496dadc88e3Srmind 	if (nvlist_exists(ncf->ncf_dict, "params")) {
497dadc88e3Srmind 		params = nvlist_take_nvlist(ncf->ncf_dict, "params");
498dadc88e3Srmind 	} else {
499dadc88e3Srmind 		params = nvlist_create(0);
500dadc88e3Srmind 	}
501dadc88e3Srmind 
502dadc88e3Srmind 	/*
503dadc88e3Srmind 	 * If the parameter is already set, then free it first.
504dadc88e3Srmind 	 * Set the parameter.  Note: values can be negative.
505dadc88e3Srmind 	 */
506dadc88e3Srmind 	if (nvlist_exists(params, name)) {
507dadc88e3Srmind 		nvlist_free_number(params, name);
508dadc88e3Srmind 	}
509dadc88e3Srmind 	nvlist_add_number(params, name, (uint64_t)val);
510dadc88e3Srmind 	nvlist_add_nvlist(ncf->ncf_dict, "params", params);
511dadc88e3Srmind 	return 0;
512dadc88e3Srmind }
513dadc88e3Srmind 
514b899bfd9Srmind const char *
npf_param_iterate(nl_config_t * ncf,nl_iter_t * iter,int * val,int * defval)515b899bfd9Srmind npf_param_iterate(nl_config_t *ncf, nl_iter_t *iter, int *val, int *defval)
516b899bfd9Srmind {
517b899bfd9Srmind 	void *cookie = (void *)(intptr_t)*iter;
518b899bfd9Srmind 	const nvlist_t *params, *dparams;
519b899bfd9Srmind 	const char *name;
520b899bfd9Srmind 	int type;
521b899bfd9Srmind 
522b899bfd9Srmind 	assert(sizeof(nl_iter_t) >= sizeof(void *));
523b899bfd9Srmind 
524b899bfd9Srmind 	params = dnvlist_get_nvlist(ncf->ncf_dict, "params", NULL);
525b899bfd9Srmind 	if (params == NULL) {
526b899bfd9Srmind 		return NULL;
527b899bfd9Srmind 	}
528b899bfd9Srmind skip:
529b899bfd9Srmind 	if ((name = nvlist_next(params, &type, &cookie)) == NULL) {
530b899bfd9Srmind 		*iter = NPF_ITER_BEGIN;
531b899bfd9Srmind 		return NULL;
532b899bfd9Srmind 	}
533b899bfd9Srmind 	if (type != NV_TYPE_NUMBER) {
534b899bfd9Srmind 		goto skip; // should never happen, though
535b899bfd9Srmind 	}
536b899bfd9Srmind 	if (defval) {
537b899bfd9Srmind 		dparams = dnvlist_get_nvlist(ncf->ncf_dict,
538b899bfd9Srmind 		    "params-defaults", NULL);
539b899bfd9Srmind 		if (dparams == NULL) {
540b899bfd9Srmind 			errno = EINVAL;
541b899bfd9Srmind 			return NULL;
542b899bfd9Srmind 		}
543b899bfd9Srmind 		*defval = (int)nvlist_get_number(dparams, name);
544b899bfd9Srmind 	}
545b899bfd9Srmind 
546b899bfd9Srmind 	*val = (int)nvlist_get_number(params, name);
547b899bfd9Srmind 	*iter = (intptr_t)cookie;
548b899bfd9Srmind 	return name;
549b899bfd9Srmind }
550b899bfd9Srmind 
551dadc88e3Srmind /*
5520e218254Srmind  * DYNAMIC RULESET INTERFACE.
5530e218254Srmind  */
5540e218254Srmind 
55504cb50acSrmind static inline bool
_npf_nat_ruleset_p(const char * name)55604cb50acSrmind _npf_nat_ruleset_p(const char *name)
55704cb50acSrmind {
55804cb50acSrmind 	return strncmp(name, NPF_RULESET_MAP_PREF,
55904cb50acSrmind 	    sizeof(NPF_RULESET_MAP_PREF) - 1) == 0;
56004cb50acSrmind }
56104cb50acSrmind 
5620e218254Srmind int
npf_ruleset_add(int fd,const char * rname,nl_rule_t * rl,uint64_t * id)56356910be7Srmind npf_ruleset_add(int fd, const char *rname, nl_rule_t *rl, uint64_t *id)
5640e218254Srmind {
56504cb50acSrmind 	const bool natset = _npf_nat_ruleset_p(rname);
566b899bfd9Srmind 	nvlist_t *rule_nvl = rl->rule_dict, *resp;
567b899bfd9Srmind 	int error;
5680e218254Srmind 
569b899bfd9Srmind 	nvlist_add_number(rule_nvl, "attr",
570b899bfd9Srmind 	    NPF_RULE_DYNAMIC | nvlist_take_number(rule_nvl, "attr"));
57104cb50acSrmind 
572b899bfd9Srmind 	if (natset && !dnvlist_get_bool(rule_nvl, "nat-rule", false)) {
57304cb50acSrmind 		errno = EINVAL;
57404cb50acSrmind 		return errno;
57504cb50acSrmind 	}
576b899bfd9Srmind 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
577b899bfd9Srmind 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
578b899bfd9Srmind 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_ADD);
57904cb50acSrmind 
580b899bfd9Srmind 	error = _npf_xfer_fd(fd, IOC_NPF_RULE, rule_nvl, &resp);
581b899bfd9Srmind 	if (error) {
582b899bfd9Srmind 		return error;
5830e218254Srmind 	}
584b899bfd9Srmind 	*id = nvlist_get_number(resp, "id");
585b899bfd9Srmind 	nvlist_destroy(resp);
58639013e66Srmind 	return 0;
5870e218254Srmind }
5880e218254Srmind 
5890e218254Srmind int
npf_ruleset_remove(int fd,const char * rname,uint64_t id)59056910be7Srmind npf_ruleset_remove(int fd, const char *rname, uint64_t id)
5910e218254Srmind {
59204cb50acSrmind 	const bool natset = _npf_nat_ruleset_p(rname);
593b899bfd9Srmind 	nvlist_t *rule_nvl = nvlist_create(0);
594b899bfd9Srmind 	int error;
5950e218254Srmind 
596b899bfd9Srmind 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
597b899bfd9Srmind 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
598b899bfd9Srmind 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_REMOVE);
599b899bfd9Srmind 	nvlist_add_number(rule_nvl, "id", id);
60004cb50acSrmind 
601b899bfd9Srmind 	error = _npf_xfer_fd_errno(fd, IOC_NPF_RULE, rule_nvl);
602b899bfd9Srmind 	nvlist_destroy(rule_nvl);
603b899bfd9Srmind 	return error;
6040e218254Srmind }
6050e218254Srmind 
6060e218254Srmind int
npf_ruleset_remkey(int fd,const char * rname,const void * key,size_t len)6070e218254Srmind npf_ruleset_remkey(int fd, const char *rname, const void *key, size_t len)
6080e218254Srmind {
60904cb50acSrmind 	const bool natset = _npf_nat_ruleset_p(rname);
610b899bfd9Srmind 	nvlist_t *rule_nvl = nvlist_create(0);
611b899bfd9Srmind 	int error;
6120e218254Srmind 
613b899bfd9Srmind 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
614b899bfd9Srmind 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
615b899bfd9Srmind 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_REMKEY);
616b899bfd9Srmind 	nvlist_add_binary(rule_nvl, "key", key, len);
61704cb50acSrmind 
618b899bfd9Srmind 	error = _npf_xfer_fd_errno(fd, IOC_NPF_RULE, rule_nvl);
619b899bfd9Srmind 	nvlist_destroy(rule_nvl);
620b899bfd9Srmind 	return error;
6210e218254Srmind }
6220e218254Srmind 
62350c5afcaSrmind int
npf_ruleset_flush(int fd,const char * rname)62450c5afcaSrmind npf_ruleset_flush(int fd, const char *rname)
62550c5afcaSrmind {
62604cb50acSrmind 	const bool natset = _npf_nat_ruleset_p(rname);
627b899bfd9Srmind 	nvlist_t *rule_nvl = nvlist_create(0);
628b899bfd9Srmind 	int error;
62950c5afcaSrmind 
630b899bfd9Srmind 	nvlist_add_string(rule_nvl, "ruleset-name", rname);
631b899bfd9Srmind 	nvlist_add_bool(rule_nvl, "nat-ruleset", natset);
632b899bfd9Srmind 	nvlist_add_number(rule_nvl, "command", NPF_CMD_RULE_FLUSH);
63304cb50acSrmind 
634b899bfd9Srmind 	error = _npf_xfer_fd_errno(fd, IOC_NPF_RULE, rule_nvl);
635b899bfd9Srmind 	nvlist_destroy(rule_nvl);
636b899bfd9Srmind 	return error;
6370e218254Srmind }
6380e218254Srmind 
6390e218254Srmind /*
6408c6e21bfSrmind  * NPF EXTENSION INTERFACE.
6418c6e21bfSrmind  */
6428c6e21bfSrmind 
6438c6e21bfSrmind nl_ext_t *
npf_ext_construct(const char * name)6448c6e21bfSrmind npf_ext_construct(const char *name)
6458c6e21bfSrmind {
6468c6e21bfSrmind 	nl_ext_t *ext;
6478c6e21bfSrmind 
6488c6e21bfSrmind 	ext = malloc(sizeof(*ext));
64939013e66Srmind 	if (!ext) {
6508c6e21bfSrmind 		return NULL;
6518c6e21bfSrmind 	}
65239013e66Srmind 	ext->ext_dict = nvlist_create(0);
65339013e66Srmind 	nvlist_add_string(ext->ext_dict, "name", name);
6548c6e21bfSrmind 	return ext;
6558c6e21bfSrmind }
6568c6e21bfSrmind 
6578c6e21bfSrmind void
npf_ext_param_u32(nl_ext_t * ext,const char * key,uint32_t val)6588c6e21bfSrmind npf_ext_param_u32(nl_ext_t *ext, const char *key, uint32_t val)
6598c6e21bfSrmind {
66039013e66Srmind 	nvlist_add_number(ext->ext_dict, key, val);
6618c6e21bfSrmind }
6628c6e21bfSrmind 
6638c6e21bfSrmind void
npf_ext_param_bool(nl_ext_t * ext,const char * key,bool val)6648c6e21bfSrmind npf_ext_param_bool(nl_ext_t *ext, const char *key, bool val)
6658c6e21bfSrmind {
66639013e66Srmind 	nvlist_add_bool(ext->ext_dict, key, val);
6678c6e21bfSrmind }
6688c6e21bfSrmind 
669c628b578Sjakllsch void
npf_ext_param_string(nl_ext_t * ext,const char * key,const char * val)670c628b578Sjakllsch npf_ext_param_string(nl_ext_t *ext, const char *key, const char *val)
671c628b578Sjakllsch {
67239013e66Srmind 	nvlist_add_string(ext->ext_dict, key, val);
673c628b578Sjakllsch }
674c628b578Sjakllsch 
6758c6e21bfSrmind /*
67607ac07d3Srmind  * RULE INTERFACE.
67707ac07d3Srmind  */
67807ac07d3Srmind 
67907ac07d3Srmind nl_rule_t *
npf_rule_create(const char * name,uint32_t attr,const char * ifname)680a79812eaSrmind npf_rule_create(const char *name, uint32_t attr, const char *ifname)
68107ac07d3Srmind {
68207ac07d3Srmind 	nl_rule_t *rl;
68307ac07d3Srmind 
68439013e66Srmind 	rl = malloc(sizeof(nl_rule_t));
68539013e66Srmind 	if (!rl) {
68607ac07d3Srmind 		return NULL;
68707ac07d3Srmind 	}
68839013e66Srmind 	rl->rule_dict = nvlist_create(0);
68939013e66Srmind 	nvlist_add_number(rl->rule_dict, "attr", attr);
69007ac07d3Srmind 	if (name) {
69139013e66Srmind 		nvlist_add_string(rl->rule_dict, "name", name);
69207ac07d3Srmind 	}
693a79812eaSrmind 	if (ifname) {
69439013e66Srmind 		nvlist_add_string(rl->rule_dict, "ifname", ifname);
69507ac07d3Srmind 	}
69607ac07d3Srmind 	return rl;
69707ac07d3Srmind }
69807ac07d3Srmind 
69907ac07d3Srmind int
npf_rule_setcode(nl_rule_t * rl,int type,const void * code,size_t len)7000e218254Srmind npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t len)
70107ac07d3Srmind {
70239013e66Srmind 	if (type != NPF_CODE_BPF) {
70307ac07d3Srmind 		return ENOTSUP;
70407ac07d3Srmind 	}
70539013e66Srmind 	nvlist_add_number(rl->rule_dict, "code-type", (unsigned)type);
70639013e66Srmind 	nvlist_add_binary(rl->rule_dict, "code", code, len);
70739013e66Srmind 	return nvlist_error(rl->rule_dict);
70807ac07d3Srmind }
70907ac07d3Srmind 
71007ac07d3Srmind int
npf_rule_setkey(nl_rule_t * rl,const void * key,size_t len)7110e218254Srmind npf_rule_setkey(nl_rule_t *rl, const void *key, size_t len)
7120e218254Srmind {
71339013e66Srmind 	nvlist_add_binary(rl->rule_dict, "key", key, len);
71439013e66Srmind 	return nvlist_error(rl->rule_dict);
7150e218254Srmind }
7160e218254Srmind 
7170e218254Srmind int
npf_rule_setinfo(nl_rule_t * rl,const void * info,size_t len)7184e592132Srmind npf_rule_setinfo(nl_rule_t *rl, const void *info, size_t len)
7194e592132Srmind {
72039013e66Srmind 	nvlist_add_binary(rl->rule_dict, "info", info, len);
72139013e66Srmind 	return nvlist_error(rl->rule_dict);
7224e592132Srmind }
7234e592132Srmind 
7244e592132Srmind int
npf_rule_setprio(nl_rule_t * rl,int pri)725f75d79ebSchristos npf_rule_setprio(nl_rule_t *rl, int pri)
72607ac07d3Srmind {
72739013e66Srmind 	nvlist_add_number(rl->rule_dict, "prio", (uint64_t)pri);
72839013e66Srmind 	return nvlist_error(rl->rule_dict);
72907ac07d3Srmind }
7300e218254Srmind 
7310e218254Srmind int
npf_rule_setproc(nl_rule_t * rl,const char * name)7320e218254Srmind npf_rule_setproc(nl_rule_t *rl, const char *name)
7330e218254Srmind {
73439013e66Srmind 	nvlist_add_string(rl->rule_dict, "rproc", name);
73539013e66Srmind 	return nvlist_error(rl->rule_dict);
73607ac07d3Srmind }
73707ac07d3Srmind 
7380e218254Srmind void *
npf_rule_export(nl_rule_t * rl,size_t * length)7390e218254Srmind npf_rule_export(nl_rule_t *rl, size_t *length)
7400e218254Srmind {
74139013e66Srmind 	return nvlist_pack(rl->rule_dict, length);
7420e218254Srmind }
7430e218254Srmind 
74407ac07d3Srmind bool
npf_rule_exists_p(nl_config_t * ncf,const char * name)74507ac07d3Srmind npf_rule_exists_p(nl_config_t *ncf, const char *name)
74607ac07d3Srmind {
747b899bfd9Srmind 	const char *key = nvlist_exists_nvlist_array(ncf->ncf_dict,
748b899bfd9Srmind 	    "rules") ? "rules" : "__rules"; // config may not be built yet
749b899bfd9Srmind 	return _npf_dataset_lookup(ncf->ncf_dict, key, "name", name);
75007ac07d3Srmind }
75107ac07d3Srmind 
75207ac07d3Srmind int
npf_rule_insert(nl_config_t * ncf,nl_rule_t * parent,nl_rule_t * rl)7530e218254Srmind npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl)
75407ac07d3Srmind {
75539013e66Srmind 	nvlist_t *rule_dict = rl->rule_dict;
75639013e66Srmind 	nvlist_t *target;
75739013e66Srmind 	const char *key;
75807ac07d3Srmind 
75907ac07d3Srmind 	if (parent) {
76039013e66Srmind 		/* Subrule of the parent. */
76139013e66Srmind 		target = parent->rule_dict;
76239013e66Srmind 		key = "subrules";
76307ac07d3Srmind 	} else {
76439013e66Srmind 		/* Global ruleset. */
76539013e66Srmind 		target = ncf->ncf_dict;
76639013e66Srmind 		key = "__rules";
76707ac07d3Srmind 	}
76839013e66Srmind 	nvlist_append_nvlist_array(target, key, rule_dict);
76939013e66Srmind 	nvlist_destroy(rule_dict);
77039013e66Srmind 	free(rl);
77107ac07d3Srmind 	return 0;
77207ac07d3Srmind }
77307ac07d3Srmind 
7744e592132Srmind static nl_rule_t *
_npf_rule_iterate1(nl_config_t * ncf,const char * key,nl_iter_t * iter,unsigned * level)775dadc88e3Srmind _npf_rule_iterate1(nl_config_t *ncf, const char *key,
776dadc88e3Srmind     nl_iter_t *iter, unsigned *level)
7774e592132Srmind {
778dadc88e3Srmind 	unsigned i = *iter;
77939013e66Srmind 	const nvlist_t *rule_dict;
78039013e66Srmind 	uint32_t skipto;
7814e592132Srmind 
78239013e66Srmind 	if (i == 0) {
7834e592132Srmind 		/* Initialise the iterator. */
7844e592132Srmind 		ncf->ncf_nlevel = 0;
7854e592132Srmind 		ncf->ncf_reduce[0] = 0;
7864e592132Srmind 	}
7874e592132Srmind 
78839013e66Srmind 	rule_dict = _npf_dataset_getelement(ncf->ncf_dict, key, i);
78939013e66Srmind 	if (!rule_dict) {
790dadc88e3Srmind 		*iter = NPF_ITER_BEGIN;
7914e592132Srmind 		return NULL;
7924e592132Srmind 	}
793dadc88e3Srmind 	*iter = i + 1; // next
7944e592132Srmind 	*level = ncf->ncf_nlevel;
7954e592132Srmind 
79639013e66Srmind 	skipto = dnvlist_get_number(rule_dict, "skip-to", 0);
7974e592132Srmind 	if (skipto) {
7984e592132Srmind 		ncf->ncf_nlevel++;
7994e592132Srmind 		ncf->ncf_reduce[ncf->ncf_nlevel] = skipto;
8004e592132Srmind 	}
801dadc88e3Srmind 	if (ncf->ncf_reduce[ncf->ncf_nlevel] == (i + 1)) {
8024e592132Srmind 		assert(ncf->ncf_nlevel > 0);
8034e592132Srmind 		ncf->ncf_nlevel--;
8044e592132Srmind 	}
805dadc88e3Srmind 
806dadc88e3Srmind 	ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX
8074e592132Srmind 	return &ncf->ncf_cur_rule;
8084e592132Srmind }
8094e592132Srmind 
8104e592132Srmind nl_rule_t *
npf_rule_iterate(nl_config_t * ncf,nl_iter_t * iter,unsigned * level)811dadc88e3Srmind npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level)
8124e592132Srmind {
813dadc88e3Srmind 	return _npf_rule_iterate1(ncf, "rules", iter, level);
8144e592132Srmind }
8154e592132Srmind 
8164e592132Srmind const char *
npf_rule_getname(nl_rule_t * rl)8174e592132Srmind npf_rule_getname(nl_rule_t *rl)
8184e592132Srmind {
81939013e66Srmind 	return dnvlist_get_string(rl->rule_dict, "name", NULL);
8204e592132Srmind }
8214e592132Srmind 
8224e592132Srmind uint32_t
npf_rule_getattr(nl_rule_t * rl)8234e592132Srmind npf_rule_getattr(nl_rule_t *rl)
8244e592132Srmind {
82539013e66Srmind 	return dnvlist_get_number(rl->rule_dict, "attr", 0);
8264e592132Srmind }
8274e592132Srmind 
828a79812eaSrmind const char *
npf_rule_getinterface(nl_rule_t * rl)8294e592132Srmind npf_rule_getinterface(nl_rule_t *rl)
8304e592132Srmind {
83139013e66Srmind 	return dnvlist_get_string(rl->rule_dict, "ifname", NULL);
8324e592132Srmind }
8334e592132Srmind 
8344e592132Srmind const void *
npf_rule_getinfo(nl_rule_t * rl,size_t * len)8354e592132Srmind npf_rule_getinfo(nl_rule_t *rl, size_t *len)
8364e592132Srmind {
83739013e66Srmind 	return dnvlist_get_binary(rl->rule_dict, "info", len, NULL, 0);
8384e592132Srmind }
8394e592132Srmind 
8404e592132Srmind const char *
npf_rule_getproc(nl_rule_t * rl)8414e592132Srmind npf_rule_getproc(nl_rule_t *rl)
8424e592132Srmind {
84339013e66Srmind 	return dnvlist_get_string(rl->rule_dict, "rproc", NULL);
8444e592132Srmind }
8454e592132Srmind 
8463e73e7e6Srmind uint64_t
npf_rule_getid(nl_rule_t * rl)8473e73e7e6Srmind npf_rule_getid(nl_rule_t *rl)
8483e73e7e6Srmind {
84939013e66Srmind 	return dnvlist_get_number(rl->rule_dict, "id", 0);
8503e73e7e6Srmind }
8513e73e7e6Srmind 
8523e73e7e6Srmind const void *
npf_rule_getcode(nl_rule_t * rl,int * type,size_t * len)8533e73e7e6Srmind npf_rule_getcode(nl_rule_t *rl, int *type, size_t *len)
8543e73e7e6Srmind {
85539013e66Srmind 	*type = (int)dnvlist_get_number(rl->rule_dict, "code-type", 0);
85639013e66Srmind 	return dnvlist_get_binary(rl->rule_dict, "code", len, NULL, 0);
8573e73e7e6Srmind }
8583e73e7e6Srmind 
85950c5afcaSrmind int
_npf_ruleset_list(int fd,const char * rname,nl_config_t * ncf)86050c5afcaSrmind _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf)
86150c5afcaSrmind {
86204cb50acSrmind 	const bool natset = _npf_nat_ruleset_p(rname);
863b899bfd9Srmind 	nvlist_t *req, *resp;
864b899bfd9Srmind 	int error;
86550c5afcaSrmind 
86639013e66Srmind 	req = nvlist_create(0);
86739013e66Srmind 	nvlist_add_string(req, "ruleset-name", rname);
86804cb50acSrmind 	nvlist_add_bool(req, "nat-ruleset", natset);
86939013e66Srmind 	nvlist_add_number(req, "command", NPF_CMD_RULE_LIST);
87050c5afcaSrmind 
871b899bfd9Srmind 	error = _npf_xfer_fd(fd, IOC_NPF_RULE, req, &resp);
872b899bfd9Srmind 	nvlist_destroy(req);
873b899bfd9Srmind 	if (error) {
874b899bfd9Srmind 		return error;
87550c5afcaSrmind 	}
876b899bfd9Srmind 
877b899bfd9Srmind 	if (nvlist_exists_nvlist_array(resp, "rules")) {
87839013e66Srmind 		nvlist_t **rules;
87939013e66Srmind 		size_t n;
88039013e66Srmind 
881b899bfd9Srmind 		rules = nvlist_take_nvlist_array(resp, "rules", &n);
88239013e66Srmind 		nvlist_move_nvlist_array(ncf->ncf_dict, "rules", rules, n);
88350c5afcaSrmind 	}
884b899bfd9Srmind 	nvlist_destroy(resp);
88539013e66Srmind 	return 0;
88650c5afcaSrmind }
88750c5afcaSrmind 
88807ac07d3Srmind void
npf_rule_destroy(nl_rule_t * rl)88907ac07d3Srmind npf_rule_destroy(nl_rule_t *rl)
89007ac07d3Srmind {
89139013e66Srmind 	nvlist_destroy(rl->rule_dict);
89207ac07d3Srmind 	free(rl);
89307ac07d3Srmind }
89407ac07d3Srmind 
89507ac07d3Srmind /*
89607ac07d3Srmind  * RULE PROCEDURE INTERFACE.
89707ac07d3Srmind  */
89807ac07d3Srmind 
89907ac07d3Srmind nl_rproc_t *
npf_rproc_create(const char * name)90007ac07d3Srmind npf_rproc_create(const char *name)
90107ac07d3Srmind {
90239013e66Srmind 	nl_rproc_t *rp;
90307ac07d3Srmind 
90439013e66Srmind 	rp = malloc(sizeof(nl_rproc_t));
90539013e66Srmind 	if (!rp) {
90607ac07d3Srmind 		return NULL;
90707ac07d3Srmind 	}
90839013e66Srmind 	rp->rproc_dict = nvlist_create(0);
90939013e66Srmind 	nvlist_add_string(rp->rproc_dict, "name", name);
91039013e66Srmind 	return rp;
91107ac07d3Srmind }
91207ac07d3Srmind 
9138c6e21bfSrmind int
npf_rproc_extcall(nl_rproc_t * rp,nl_ext_t * ext)9148c6e21bfSrmind npf_rproc_extcall(nl_rproc_t *rp, nl_ext_t *ext)
9158c6e21bfSrmind {
91639013e66Srmind 	nvlist_t *rproc_dict = rp->rproc_dict;
91739013e66Srmind 	const char *name = dnvlist_get_string(ext->ext_dict, "name", NULL);
9188c6e21bfSrmind 
91939013e66Srmind 	if (_npf_dataset_lookup(rproc_dict, "extcalls", "name", name)) {
9208c6e21bfSrmind 		return EEXIST;
9218c6e21bfSrmind 	}
92239013e66Srmind 	nvlist_append_nvlist_array(rproc_dict, "extcalls", ext->ext_dict);
92339013e66Srmind 	nvlist_destroy(ext->ext_dict);
92439013e66Srmind 	free(ext);
9258c6e21bfSrmind 	return 0;
9268c6e21bfSrmind }
9278c6e21bfSrmind 
92807ac07d3Srmind bool
npf_rproc_exists_p(nl_config_t * ncf,const char * name)92907ac07d3Srmind npf_rproc_exists_p(nl_config_t *ncf, const char *name)
93007ac07d3Srmind {
93139013e66Srmind 	return _npf_dataset_lookup(ncf->ncf_dict, "rprocs", "name", name);
93207ac07d3Srmind }
93307ac07d3Srmind 
93407ac07d3Srmind int
npf_rproc_insert(nl_config_t * ncf,nl_rproc_t * rp)93507ac07d3Srmind npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp)
93607ac07d3Srmind {
93707ac07d3Srmind 	const char *name;
93807ac07d3Srmind 
93939013e66Srmind 	name = dnvlist_get_string(rp->rproc_dict, "name", NULL);
94039013e66Srmind 	if (!name) {
94107ac07d3Srmind 		return EINVAL;
94207ac07d3Srmind 	}
94307ac07d3Srmind 	if (npf_rproc_exists_p(ncf, name)) {
94407ac07d3Srmind 		return EEXIST;
94507ac07d3Srmind 	}
94639013e66Srmind 	nvlist_append_nvlist_array(ncf->ncf_dict, "rprocs", rp->rproc_dict);
94739013e66Srmind 	nvlist_destroy(rp->rproc_dict);
94839013e66Srmind 	free(rp);
94907ac07d3Srmind 	return 0;
95007ac07d3Srmind }
95107ac07d3Srmind 
9524e592132Srmind nl_rproc_t *
npf_rproc_iterate(nl_config_t * ncf,nl_iter_t * iter)953dadc88e3Srmind npf_rproc_iterate(nl_config_t *ncf, nl_iter_t *iter)
9544e592132Srmind {
95539013e66Srmind 	const nvlist_t *rproc_dict;
956dadc88e3Srmind 	unsigned i = *iter;
9574e592132Srmind 
95839013e66Srmind 	rproc_dict = _npf_dataset_getelement(ncf->ncf_dict, "rprocs", i);
95939013e66Srmind 	if (!rproc_dict) {
960dadc88e3Srmind 		*iter = NPF_ITER_BEGIN;
9614e592132Srmind 		return NULL;
9624e592132Srmind 	}
963dadc88e3Srmind 	*iter = i + 1; // next
96439013e66Srmind 	ncf->ncf_cur_rproc.rproc_dict = __UNCONST(rproc_dict); // XXX
9654e592132Srmind 	return &ncf->ncf_cur_rproc;
9664e592132Srmind }
9674e592132Srmind 
9684e592132Srmind const char *
npf_rproc_getname(nl_rproc_t * rp)9694e592132Srmind npf_rproc_getname(nl_rproc_t *rp)
9704e592132Srmind {
97139013e66Srmind 	return dnvlist_get_string(rp->rproc_dict, "name", NULL);
9724e592132Srmind }
9734e592132Srmind 
97407ac07d3Srmind /*
975d0850273Srmind  * NAT INTERFACE.
97607ac07d3Srmind  */
97707ac07d3Srmind 
97807ac07d3Srmind nl_nat_t *
npf_nat_create(int type,unsigned flags,const char * ifname)9793d9a792dSrmind npf_nat_create(int type, unsigned flags, const char *ifname)
98007ac07d3Srmind {
98107ac07d3Srmind 	nl_rule_t *rl;
98239013e66Srmind 	nvlist_t *rule_dict;
98307ac07d3Srmind 	uint32_t attr;
98407ac07d3Srmind 
98507ac07d3Srmind 	attr = NPF_RULE_PASS | NPF_RULE_FINAL |
9864a8954ecSrmind 	    (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN);
98707ac07d3Srmind 
988d0850273Srmind 	/* Create a rule for NAT policy.  Next, will add NAT data. */
989a79812eaSrmind 	rl = npf_rule_create(NULL, attr, ifname);
99039013e66Srmind 	if (!rl) {
99107ac07d3Srmind 		return NULL;
99207ac07d3Srmind 	}
99339013e66Srmind 	rule_dict = rl->rule_dict;
99407ac07d3Srmind 
99507ac07d3Srmind 	/* Translation type and flags. */
99639013e66Srmind 	nvlist_add_number(rule_dict, "type", type);
99739013e66Srmind 	nvlist_add_number(rule_dict, "flags", flags);
998dadc88e3Srmind 	nvlist_add_bool(rule_dict, "nat-rule", true);
99907ac07d3Srmind 	return (nl_nat_t *)rl;
100007ac07d3Srmind }
100107ac07d3Srmind 
100207ac07d3Srmind int
npf_nat_insert(nl_config_t * ncf,nl_nat_t * nt)1003dadc88e3Srmind npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt)
100407ac07d3Srmind {
100539013e66Srmind 	nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict);
100639013e66Srmind 	nvlist_destroy(nt->rule_dict);
100739013e66Srmind 	free(nt);
100807ac07d3Srmind 	return 0;
100907ac07d3Srmind }
101007ac07d3Srmind 
10114e592132Srmind nl_nat_t *
npf_nat_iterate(nl_config_t * ncf,nl_iter_t * iter)1012dadc88e3Srmind npf_nat_iterate(nl_config_t *ncf, nl_iter_t *iter)
10134e592132Srmind {
101439013e66Srmind 	unsigned level;
1015dadc88e3Srmind 	return _npf_rule_iterate1(ncf, "nat", iter, &level);
10164e592132Srmind }
10174e592132Srmind 
10184e592132Srmind int
npf_nat_setaddr(nl_nat_t * nt,int af,npf_addr_t * addr,npf_netmask_t mask)10193d9a792dSrmind npf_nat_setaddr(nl_nat_t *nt, int af, npf_addr_t *addr, npf_netmask_t mask)
10203d9a792dSrmind {
10213d9a792dSrmind 	/* Translation IP and mask. */
1022dadc88e3Srmind 	if (!_npf_add_addr(nt->rule_dict, "nat-addr", af, addr)) {
10233d9a792dSrmind 		return nvlist_error(nt->rule_dict);
10243d9a792dSrmind 	}
10253d9a792dSrmind 	nvlist_add_number(nt->rule_dict, "nat-mask", (uint32_t)mask);
10263d9a792dSrmind 	return nvlist_error(nt->rule_dict);
10273d9a792dSrmind }
10283d9a792dSrmind 
10293d9a792dSrmind int
npf_nat_setport(nl_nat_t * nt,in_port_t port)10303d9a792dSrmind npf_nat_setport(nl_nat_t *nt, in_port_t port)
10313d9a792dSrmind {
10323d9a792dSrmind 	/* Translation port (for redirect case). */
10333d9a792dSrmind 	nvlist_add_number(nt->rule_dict, "nat-port", port);
10343d9a792dSrmind 	return nvlist_error(nt->rule_dict);
10353d9a792dSrmind }
10363d9a792dSrmind 
10373d9a792dSrmind int
npf_nat_settable(nl_nat_t * nt,unsigned tid)10383d9a792dSrmind npf_nat_settable(nl_nat_t *nt, unsigned tid)
10393d9a792dSrmind {
1040dadc88e3Srmind 	/*
1041dadc88e3Srmind 	 * Translation table ID; the address/mask will then serve as a filter.
1042dadc88e3Srmind 	 */
10433d9a792dSrmind 	nvlist_add_number(nt->rule_dict, "nat-table-id", tid);
10443d9a792dSrmind 	return nvlist_error(nt->rule_dict);
10453d9a792dSrmind }
10463d9a792dSrmind 
10473d9a792dSrmind int
npf_nat_setalgo(nl_nat_t * nt,unsigned algo)104839013e66Srmind npf_nat_setalgo(nl_nat_t *nt, unsigned algo)
1049068cee29Srmind {
105039013e66Srmind 	nvlist_add_number(nt->rule_dict, "nat-algo", algo);
105139013e66Srmind 	return nvlist_error(nt->rule_dict);
1052068cee29Srmind }
1053068cee29Srmind 
1054068cee29Srmind int
npf_nat_setnpt66(nl_nat_t * nt,uint16_t adj)1055068cee29Srmind npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj)
1056068cee29Srmind {
1057068cee29Srmind 	int error;
1058068cee29Srmind 
1059068cee29Srmind 	if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) {
1060068cee29Srmind 		return error;
1061068cee29Srmind 	}
106239013e66Srmind 	nvlist_add_number(nt->rule_dict, "npt66-adj", adj);
106339013e66Srmind 	return nvlist_error(nt->rule_dict);
1064068cee29Srmind }
1065068cee29Srmind 
1066068cee29Srmind int
npf_nat_gettype(nl_nat_t * nt)10674e592132Srmind npf_nat_gettype(nl_nat_t *nt)
10684e592132Srmind {
106939013e66Srmind 	return dnvlist_get_number(nt->rule_dict, "type", 0);
10704e592132Srmind }
10714e592132Srmind 
107239013e66Srmind unsigned
npf_nat_getflags(nl_nat_t * nt)10738274d601Srmind npf_nat_getflags(nl_nat_t *nt)
10748274d601Srmind {
107539013e66Srmind 	return dnvlist_get_number(nt->rule_dict, "flags", 0);
10768274d601Srmind }
10778274d601Srmind 
10783d9a792dSrmind unsigned
npf_nat_getalgo(nl_nat_t * nt)10793d9a792dSrmind npf_nat_getalgo(nl_nat_t *nt)
10804e592132Srmind {
10813d9a792dSrmind 	return dnvlist_get_number(nt->rule_dict, "nat-algo", 0);
10823d9a792dSrmind }
10833d9a792dSrmind 
10843d9a792dSrmind const npf_addr_t *
npf_nat_getaddr(nl_nat_t * nt,size_t * alen,npf_netmask_t * mask)10853d9a792dSrmind npf_nat_getaddr(nl_nat_t *nt, size_t *alen, npf_netmask_t *mask)
10863d9a792dSrmind {
10873d9a792dSrmind 	const void *data;
10883d9a792dSrmind 
1089dadc88e3Srmind 	if (nvlist_exists(nt->rule_dict, "nat-addr")) {
1090dadc88e3Srmind 		data = nvlist_get_binary(nt->rule_dict, "nat-addr", alen);
10913d9a792dSrmind 		*mask = nvlist_get_number(nt->rule_dict, "nat-mask");
10923d9a792dSrmind 	} else {
10933d9a792dSrmind 		data = NULL;
10943d9a792dSrmind 		*alen = 0;
10953d9a792dSrmind 		*mask = NPF_NO_NETMASK;
10963d9a792dSrmind 	}
10973d9a792dSrmind 	return data;
10983d9a792dSrmind }
10993d9a792dSrmind 
11003d9a792dSrmind in_port_t
npf_nat_getport(nl_nat_t * nt)11013d9a792dSrmind npf_nat_getport(nl_nat_t *nt)
11023d9a792dSrmind {
11033d9a792dSrmind 	return (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0);
11043d9a792dSrmind }
11053d9a792dSrmind 
11063d9a792dSrmind unsigned
npf_nat_gettable(nl_nat_t * nt)11073d9a792dSrmind npf_nat_gettable(nl_nat_t *nt)
11083d9a792dSrmind {
11093d9a792dSrmind 	return dnvlist_get_number(nt->rule_dict, "nat-table-id", 0);
11104e592132Srmind }
11114e592132Srmind 
111207ac07d3Srmind /*
111307ac07d3Srmind  * TABLE INTERFACE.
111407ac07d3Srmind  */
111507ac07d3Srmind 
111607ac07d3Srmind nl_table_t *
npf_table_create(const char * name,unsigned id,int type)111739013e66Srmind npf_table_create(const char *name, unsigned id, int type)
111807ac07d3Srmind {
111907ac07d3Srmind 	nl_table_t *tl;
112007ac07d3Srmind 
11213552fa1eSchristos 	tl = malloc(sizeof(*tl));
112239013e66Srmind 	if (!tl) {
112307ac07d3Srmind 		return NULL;
112407ac07d3Srmind 	}
112539013e66Srmind 	tl->table_dict = nvlist_create(0);
112639013e66Srmind 	nvlist_add_string(tl->table_dict, "name", name);
112739013e66Srmind 	nvlist_add_number(tl->table_dict, "id", id);
112839013e66Srmind 	nvlist_add_number(tl->table_dict, "type", type);
112907ac07d3Srmind 	return tl;
113007ac07d3Srmind }
113107ac07d3Srmind 
113207ac07d3Srmind int
npf_table_add_entry(nl_table_t * tl,int af,const npf_addr_t * addr,const npf_netmask_t mask)113357ff5416Srmind npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr,
113457ff5416Srmind     const npf_netmask_t mask)
113507ac07d3Srmind {
113639013e66Srmind 	nvlist_t *entry;
113707ac07d3Srmind 
113839013e66Srmind 	entry = nvlist_create(0);
113939013e66Srmind 	if (!entry) {
114007ac07d3Srmind 		return ENOMEM;
114107ac07d3Srmind 	}
114239013e66Srmind 	if (!_npf_add_addr(entry, "addr", af, addr)) {
114339013e66Srmind 		nvlist_destroy(entry);
114457ff5416Srmind 		return EINVAL;
114557ff5416Srmind 	}
114639013e66Srmind 	nvlist_add_number(entry, "mask", mask);
114739013e66Srmind 	nvlist_append_nvlist_array(tl->table_dict, "entries", entry);
114839013e66Srmind 	nvlist_destroy(entry);
114907ac07d3Srmind 	return 0;
115007ac07d3Srmind }
115107ac07d3Srmind 
115239013e66Srmind static inline int
_npf_table_build_const(nl_table_t * tl)11530e1944daSrmind _npf_table_build_const(nl_table_t *tl)
1154ffcdc4afSrmind {
115539013e66Srmind 	struct cdbw *cdbw;
115639013e66Srmind 	const nvlist_t * const *entries;
115739013e66Srmind 	int error = 0, fd = -1;
115839013e66Srmind 	size_t nitems, len;
115939013e66Srmind 	void *cdb, *buf;
116039013e66Srmind 	struct stat sb;
116139013e66Srmind 	char sfn[32];
1162ffcdc4afSrmind 
11630e1944daSrmind 	if (dnvlist_get_number(tl->table_dict, "type", 0) != NPF_TABLE_CONST) {
11640e1944daSrmind 		return 0;
11650e1944daSrmind 	}
11660e1944daSrmind 
116739013e66Srmind 	if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) {
1168ffcdc4afSrmind 		return 0;
1169ffcdc4afSrmind 	}
1170ffcdc4afSrmind 
117139013e66Srmind 	/*
117239013e66Srmind 	 * Create a constant database and put all the entries.
117339013e66Srmind 	 */
117439013e66Srmind 	if ((cdbw = cdbw_open()) == NULL) {
117539013e66Srmind 		return errno;
117607ac07d3Srmind 	}
117739013e66Srmind 	entries = nvlist_get_nvlist_array(tl->table_dict, "entries", &nitems);
117839013e66Srmind 	for (unsigned i = 0; i < nitems; i++) {
117939013e66Srmind 		const nvlist_t *entry = entries[i];
118039013e66Srmind 		const npf_addr_t *addr;
118139013e66Srmind 		size_t alen;
118239013e66Srmind 
118339013e66Srmind 		addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0);
118439013e66Srmind 		if (addr == NULL || alen == 0 || alen > sizeof(npf_addr_t)) {
118539013e66Srmind 			error = EINVAL;
118639013e66Srmind 			goto out;
118739013e66Srmind 		}
118839013e66Srmind 		if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) {
118939013e66Srmind 			error = errno;
119039013e66Srmind 			goto out;
119139013e66Srmind 		}
119239013e66Srmind 	}
119339013e66Srmind 
119439013e66Srmind 	/*
1195dadc88e3Srmind 	 * Write the constant database into a temporary file.
119639013e66Srmind 	 */
119739013e66Srmind 	strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn));
119839013e66Srmind 	sfn[sizeof(sfn) - 1] = '\0';
119939013e66Srmind 
120039013e66Srmind 	if ((fd = mkstemp(sfn)) == -1) {
120139013e66Srmind 		error = errno;
120239013e66Srmind 		goto out;
120339013e66Srmind 	}
120439013e66Srmind 	unlink(sfn);
120539013e66Srmind 
1206*b3b6b684Sriastradh 	if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
120739013e66Srmind 		error = errno;
120839013e66Srmind 		goto out;
120939013e66Srmind 	}
121039013e66Srmind 	if (fstat(fd, &sb) == -1) {
121139013e66Srmind 		error = errno;
121239013e66Srmind 		goto out;
121339013e66Srmind 	}
121439013e66Srmind 	len = sb.st_size;
121539013e66Srmind 
121639013e66Srmind 	/*
121739013e66Srmind 	 * Memory-map the database and copy it into a buffer.
121839013e66Srmind 	 */
121939013e66Srmind 	buf = malloc(len);
122039013e66Srmind 	if (!buf) {
122139013e66Srmind 		error = ENOMEM;
122239013e66Srmind 		goto out;
122339013e66Srmind 	}
122439013e66Srmind 	cdb = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
122539013e66Srmind 	if (cdb == MAP_FAILED) {
122639013e66Srmind 		error = errno;
122739013e66Srmind 		free(buf);
122839013e66Srmind 		goto out;
122939013e66Srmind 	}
123039013e66Srmind 	munmap(cdb, len);
123139013e66Srmind 
123239013e66Srmind 	/*
123339013e66Srmind 	 * Move the data buffer to the nvlist.
123439013e66Srmind 	 */
123539013e66Srmind 	nvlist_move_binary(tl->table_dict, "data", buf, len);
123639013e66Srmind 	error = nvlist_error(tl->table_dict);
123739013e66Srmind out:
123839013e66Srmind 	if (fd != -1) {
123939013e66Srmind 		close(fd);
124039013e66Srmind 	}
124139013e66Srmind 	cdbw_close(cdbw);
124239013e66Srmind 	return error;
124307ac07d3Srmind }
124407ac07d3Srmind 
124507ac07d3Srmind int
npf_table_insert(nl_config_t * ncf,nl_table_t * tl)124607ac07d3Srmind npf_table_insert(nl_config_t *ncf, nl_table_t *tl)
124707ac07d3Srmind {
124839013e66Srmind 	const char *name;
124939013e66Srmind 	int error;
125007ac07d3Srmind 
125139013e66Srmind 	name = dnvlist_get_string(tl->table_dict, "name", NULL);
125239013e66Srmind 	if (!name) {
125307ac07d3Srmind 		return EINVAL;
125407ac07d3Srmind 	}
125539013e66Srmind 	if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) {
125607ac07d3Srmind 		return EEXIST;
125707ac07d3Srmind 	}
12580e1944daSrmind 	if ((error = _npf_table_build_const(tl)) != 0) {
125939013e66Srmind 		return error;
126039013e66Srmind 	}
126139013e66Srmind 	nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict);
126239013e66Srmind 	nvlist_destroy(tl->table_dict);
126339013e66Srmind 	free(tl);
126407ac07d3Srmind 	return 0;
126507ac07d3Srmind }
126607ac07d3Srmind 
12670e1944daSrmind int
npf_table_replace(int fd,nl_table_t * tl,npf_error_t * errinfo)12680e1944daSrmind npf_table_replace(int fd, nl_table_t *tl, npf_error_t *errinfo)
12690e1944daSrmind {
1270b899bfd9Srmind 	nvlist_t *resp = NULL;
12710e1944daSrmind 	int error;
12720e1944daSrmind 
12730e1944daSrmind 	/* Ensure const tables are built. */
12740e1944daSrmind 	if ((error = _npf_table_build_const(tl)) != 0) {
1275712e653aSchristos 		return _npf_init_error(errno, errinfo);
12760e1944daSrmind 	}
1277b899bfd9Srmind 	error = _npf_xfer_fd(fd, IOC_NPF_TABLE_REPLACE, tl->table_dict, &resp);
1278b899bfd9Srmind 	if (error) {
1279b899bfd9Srmind 		assert(resp == NULL);
1280712e653aSchristos 		return _npf_init_error(errno, errinfo);
12810e1944daSrmind 	}
1282b899bfd9Srmind 	error = _npf_extract_error(resp, errinfo);
1283b899bfd9Srmind 	nvlist_destroy(resp);
12840e1944daSrmind 	return error;
12850e1944daSrmind }
12860e1944daSrmind 
12874e592132Srmind nl_table_t *
npf_table_iterate(nl_config_t * ncf,nl_iter_t * iter)1288dadc88e3Srmind npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter)
12894e592132Srmind {
129039013e66Srmind 	const nvlist_t *table_dict;
1291dadc88e3Srmind 	unsigned i = *iter;
12924e592132Srmind 
129339013e66Srmind 	table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i);
129439013e66Srmind 	if (!table_dict) {
1295dadc88e3Srmind 		*iter = NPF_ITER_BEGIN;
12964e592132Srmind 		return NULL;
12974e592132Srmind 	}
1298dadc88e3Srmind 	*iter = i + 1; // next
129939013e66Srmind 	ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX
13004e592132Srmind 	return &ncf->ncf_cur_table;
13014e592132Srmind }
13024e592132Srmind 
13034e592132Srmind unsigned
npf_table_getid(nl_table_t * tl)13044e592132Srmind npf_table_getid(nl_table_t *tl)
13054e592132Srmind {
130639013e66Srmind 	return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1);
13074e592132Srmind }
13084e592132Srmind 
13091e7342c1Srmind const char *
npf_table_getname(nl_table_t * tl)13101e7342c1Srmind npf_table_getname(nl_table_t *tl)
13111e7342c1Srmind {
131239013e66Srmind 	return dnvlist_get_string(tl->table_dict, "name", NULL);
13131e7342c1Srmind }
13141e7342c1Srmind 
13154e592132Srmind int
npf_table_gettype(nl_table_t * tl)13164e592132Srmind npf_table_gettype(nl_table_t *tl)
13174e592132Srmind {
131839013e66Srmind 	return dnvlist_get_number(tl->table_dict, "type", 0);
13194e592132Srmind }
13204e592132Srmind 
132107ac07d3Srmind void
npf_table_destroy(nl_table_t * tl)132207ac07d3Srmind npf_table_destroy(nl_table_t *tl)
132307ac07d3Srmind {
132439013e66Srmind 	nvlist_destroy(tl->table_dict);
132507ac07d3Srmind 	free(tl);
132607ac07d3Srmind }
132707ac07d3Srmind 
132807ac07d3Srmind /*
1329bc0f55deSchristos  * ALG INTERFACE.
1330bc0f55deSchristos  */
1331bc0f55deSchristos 
1332bc0f55deSchristos int
npf_alg_load(nl_config_t * ncf,const char * name)1333dadc88e3Srmind npf_alg_load(nl_config_t *ncf, const char *name)
1334bc0f55deSchristos {
133539013e66Srmind 	nvlist_t *alg_dict;
1336bc0f55deSchristos 
133739013e66Srmind 	if (_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) {
1338bc0f55deSchristos 		return EEXIST;
133939013e66Srmind 	}
134039013e66Srmind 	alg_dict = nvlist_create(0);
134139013e66Srmind 	nvlist_add_string(alg_dict, "name", name);
134239013e66Srmind 	nvlist_append_nvlist_array(ncf->ncf_dict, "algs", alg_dict);
134339013e66Srmind 	nvlist_destroy(alg_dict);
1344bc0f55deSchristos 	return 0;
1345bc0f55deSchristos }
1346bc0f55deSchristos 
1347bc0f55deSchristos /*
134839013e66Srmind  * CONNECTION / NAT ENTRY INTERFACE.
134907ac07d3Srmind  */
135007ac07d3Srmind 
1351b899bfd9Srmind typedef struct {
1352b899bfd9Srmind 	unsigned	alen;
1353b899bfd9Srmind 	unsigned	proto;
1354b899bfd9Srmind 	npf_addr_t	addr[3];
1355b899bfd9Srmind 	in_port_t	port[3];
1356b899bfd9Srmind } npf_connpoint_t;
1357b899bfd9Srmind 
1358b899bfd9Srmind static int
_npf_conn_lookup(int fd,const int af,npf_addr_t * addr[2],in_port_t port[2],unsigned proto,const char * ifname,unsigned di)1359b899bfd9Srmind _npf_conn_lookup(int fd, const int af, npf_addr_t *addr[2], in_port_t port[2],
1360b899bfd9Srmind     unsigned proto, const char *ifname, unsigned di)
13613d5a430cSchristos {
1362b899bfd9Srmind 	nvlist_t *req = NULL, *resp = NULL, *key_nv;
136339013e66Srmind 	const nvlist_t *nat;
13643d5a430cSchristos 	int error = EINVAL;
13653d5a430cSchristos 
136639013e66Srmind 	/*
136739013e66Srmind 	 * Setup the connection lookup key.
136839013e66Srmind 	 */
1369b899bfd9Srmind 	if ((key_nv = nvlist_create(0)) == NULL) {
13703d5a430cSchristos 		return ENOMEM;
137139013e66Srmind 	}
1372b899bfd9Srmind 	if (!_npf_add_addr(key_nv, "saddr", af, addr[0])) {
1373b899bfd9Srmind 		nvlist_destroy(key_nv);
1374431fde0eSchristos 		goto out;
1375b899bfd9Srmind 	}
1376b899bfd9Srmind 	if (!_npf_add_addr(key_nv, "daddr", af, addr[1])) {
1377b899bfd9Srmind 		nvlist_destroy(key_nv);
1378431fde0eSchristos 		goto out;
1379b899bfd9Srmind 	}
1380b899bfd9Srmind 	nvlist_add_number(key_nv, "sport", htons(port[0]));
1381b899bfd9Srmind 	nvlist_add_number(key_nv, "dport", htons(port[1]));
1382b899bfd9Srmind 	nvlist_add_number(key_nv, "proto", proto);
1383b899bfd9Srmind 	if (ifname) {
1384b899bfd9Srmind 		nvlist_add_string(key_nv, "ifname", ifname);
1385b899bfd9Srmind 	}
1386b899bfd9Srmind 	if (di) {
1387b899bfd9Srmind 		nvlist_add_number(key_nv, "di", di);
1388b899bfd9Srmind 	}
13893d5a430cSchristos 
139039013e66Srmind 	/*
139139013e66Srmind 	 * Setup the request.
139239013e66Srmind 	 */
1393b899bfd9Srmind 	if ((req = nvlist_create(0)) == NULL) {
139439013e66Srmind 		error = ENOMEM;
139539013e66Srmind 		goto out;
139639013e66Srmind 	}
1397b899bfd9Srmind 	nvlist_move_nvlist(req, "key", key_nv);
1398431fde0eSchristos 
139939013e66Srmind 	/* Lookup: retrieve the connection entry. */
1400b899bfd9Srmind 	error = _npf_xfer_fd(fd, IOC_NPF_CONN_LOOKUP, req, &resp);
1401b899bfd9Srmind 	if (error) {
14023d5a430cSchristos 		goto out;
14033d5a430cSchristos 	}
1404431fde0eSchristos 
140539013e66Srmind 	/*
140639013e66Srmind 	 * Get the NAT entry and extract the translated pair.
140739013e66Srmind 	 */
1408b899bfd9Srmind 	if ((nat = dnvlist_get_nvlist(resp, "nat", NULL)) == NULL) {
1409b899bfd9Srmind 		error = ENOENT;
141039013e66Srmind 		goto out;
141139013e66Srmind 	}
1412b899bfd9Srmind 	if (_npf_get_addr(nat, "oaddr", addr[0]) == 0 ||
1413b899bfd9Srmind 	    _npf_get_addr(nat, "taddr", addr[1]) == 0) {
14143d5a430cSchristos 		error = EINVAL;
14153d5a430cSchristos 		goto out;
14163d5a430cSchristos 	}
1417b899bfd9Srmind 	port[0] = ntohs(nvlist_get_number(nat, "oport"));
1418b899bfd9Srmind 	port[1] = ntohs(nvlist_get_number(nat, "tport"));
14193d5a430cSchristos out:
1420b899bfd9Srmind 	if (resp) {
1421b899bfd9Srmind 		nvlist_destroy(resp);
142239013e66Srmind 	}
142339013e66Srmind 	if (req) {
142439013e66Srmind 		nvlist_destroy(req);
142539013e66Srmind 	}
14263d5a430cSchristos 	return error;
14273d5a430cSchristos }
142852d8bce5Schristos 
1429b899bfd9Srmind int
npf_nat_lookup(int fd,int af,npf_addr_t * addr[2],in_port_t port[2],int proto,int di __unused)1430b899bfd9Srmind npf_nat_lookup(int fd, int af, npf_addr_t *addr[2], in_port_t port[2],
1431b899bfd9Srmind     int proto, int di __unused)
1432b899bfd9Srmind {
1433b899bfd9Srmind 	int error;
1434b899bfd9Srmind 
1435b899bfd9Srmind 	port[0] = ntohs(port[0]); port[1] = ntohs(port[1]);
1436b899bfd9Srmind 	error = _npf_conn_lookup(fd, af, addr, port, proto, NULL, 0);
1437b899bfd9Srmind 	port[0] = htons(port[0]); port[1] = htons(port[1]);
1438b899bfd9Srmind 	return error;
1439b899bfd9Srmind }
144052d8bce5Schristos 
144152d8bce5Schristos static bool
npf_connkey_handle(const nvlist_t * key_nv,npf_connpoint_t * ep)1442b899bfd9Srmind npf_connkey_handle(const nvlist_t *key_nv, npf_connpoint_t *ep)
144352d8bce5Schristos {
1444b899bfd9Srmind 	unsigned alen1, alen2;
144539013e66Srmind 
1446b899bfd9Srmind 	alen1 = _npf_get_addr(key_nv, "saddr", &ep->addr[0]);
1447b899bfd9Srmind 	alen2 = _npf_get_addr(key_nv, "daddr", &ep->addr[1]);
1448b899bfd9Srmind 	if (alen1 == 0 || alen1 != alen2) {
144952d8bce5Schristos 		return false;
1450b899bfd9Srmind 	}
1451b899bfd9Srmind 	ep->alen = alen1;
1452b899bfd9Srmind 	ep->port[0] = ntohs(nvlist_get_number(key_nv, "sport"));
1453b899bfd9Srmind 	ep->port[1] = ntohs(nvlist_get_number(key_nv, "dport"));
1454b899bfd9Srmind 	ep->proto = nvlist_get_number(key_nv, "proto");
145552d8bce5Schristos 	return true;
145652d8bce5Schristos }
145752d8bce5Schristos 
145852d8bce5Schristos static void
npf_conn_handle(const nvlist_t * conn,npf_conn_func_t func,void * arg)145939013e66Srmind npf_conn_handle(const nvlist_t *conn, npf_conn_func_t func, void *arg)
146052d8bce5Schristos {
1461b899bfd9Srmind 	const nvlist_t *key_nv, *nat_nv;
146252d8bce5Schristos 	const char *ifname;
1463b899bfd9Srmind 	npf_connpoint_t ep;
1464b899bfd9Srmind 
1465b899bfd9Srmind 	memset(&ep, 0, sizeof(npf_connpoint_t));
146652d8bce5Schristos 
146739013e66Srmind 	ifname = dnvlist_get_string(conn, "ifname", NULL);
1468b899bfd9Srmind 	key_nv = dnvlist_get_nvlist(conn, "forw-key", NULL);
1469b899bfd9Srmind 	if (!npf_connkey_handle(key_nv, &ep)) {
147052d8bce5Schristos 		goto err;
147139013e66Srmind 	}
1472b899bfd9Srmind 	if ((nat_nv = dnvlist_get_nvlist(conn, "nat", NULL)) != NULL) {
1473b899bfd9Srmind 		if (_npf_get_addr(nat_nv, "taddr", &ep.addr[2]) != ep.alen) {
1474b899bfd9Srmind 			goto err;
1475b899bfd9Srmind 		}
1476b899bfd9Srmind 		ep.port[2] = ntohs(nvlist_get_number(nat_nv, "tport"));
1477b899bfd9Srmind 	}
1478b899bfd9Srmind 	/*
1479b899bfd9Srmind 	 * XXX: add 'proto' and 'flow'; perhaps expand and pass the
1480b899bfd9Srmind 	 * whole to npf_connpoint_t?
1481b899bfd9Srmind 	 */
1482b899bfd9Srmind 	(*func)((unsigned)ep.alen, ep.addr, ep.port, ifname, arg);
148352d8bce5Schristos err:
148452d8bce5Schristos 	return;
148552d8bce5Schristos }
148652d8bce5Schristos 
148752d8bce5Schristos int
npf_conn_list(int fd,npf_conn_func_t func,void * arg)148839013e66Srmind npf_conn_list(int fd, npf_conn_func_t func, void *arg)
148952d8bce5Schristos {
149052d8bce5Schristos 	nl_config_t *ncf;
149139013e66Srmind 	const nvlist_t * const *conns;
149239013e66Srmind 	size_t nitems;
149352d8bce5Schristos 
149452d8bce5Schristos 	ncf = npf_config_retrieve(fd);
149539013e66Srmind 	if (!ncf) {
149652d8bce5Schristos 		return errno;
149752d8bce5Schristos 	}
149839013e66Srmind 	if (!nvlist_exists_nvlist_array(ncf->ncf_dict, "conn-list")) {
149939013e66Srmind 		return 0;
150052d8bce5Schristos 	}
150139013e66Srmind 	conns = nvlist_get_nvlist_array(ncf->ncf_dict, "conn-list", &nitems);
150239013e66Srmind 	for (unsigned i = 0; i < nitems; i++) {
150339013e66Srmind 		const nvlist_t *conn = conns[i];
150439013e66Srmind 		npf_conn_handle(conn, func, arg);
150552d8bce5Schristos 	}
1506b899bfd9Srmind 	npf_config_destroy(ncf);
150752d8bce5Schristos 	return 0;
150852d8bce5Schristos }
150939013e66Srmind 
151039013e66Srmind /*
151139013e66Srmind  * MISC.
151239013e66Srmind  */
151339013e66Srmind 
151439013e66Srmind void
_npf_debug_addif(nl_config_t * ncf,const char * ifname)151539013e66Srmind _npf_debug_addif(nl_config_t *ncf, const char *ifname)
151639013e66Srmind {
151739013e66Srmind 	nvlist_t *debug;
151839013e66Srmind 
151939013e66Srmind 	/*
152039013e66Srmind 	 * Initialise the debug dictionary on the first call.
152139013e66Srmind 	 */
152239013e66Srmind 	debug = dnvlist_take_nvlist(ncf->ncf_dict, "debug", NULL);
152339013e66Srmind 	if (debug == NULL) {
152439013e66Srmind 		debug = nvlist_create(0);
152539013e66Srmind 	}
152639013e66Srmind 	if (!_npf_dataset_lookup(debug, "interfaces", "name", ifname)) {
152739013e66Srmind 		nvlist_t *ifdict = nvlist_create(0);
152839013e66Srmind 		nvlist_add_string(ifdict, "name", ifname);
152939013e66Srmind 		nvlist_add_number(ifdict, "index", if_nametoindex(ifname));
153039013e66Srmind 		nvlist_append_nvlist_array(debug, "interfaces", ifdict);
153139013e66Srmind 		nvlist_destroy(ifdict);
153239013e66Srmind 	}
153339013e66Srmind 	nvlist_move_nvlist(ncf->ncf_dict, "debug", debug);
153439013e66Srmind }
153539013e66Srmind 
153639013e66Srmind void
_npf_config_dump(nl_config_t * ncf,int fd)153739013e66Srmind _npf_config_dump(nl_config_t *ncf, int fd)
153839013e66Srmind {
153939013e66Srmind 	(void)npf_config_build(ncf);
154039013e66Srmind 	nvlist_dump(ncf->ncf_dict, fd);
154139013e66Srmind }
1542