xref: /netbsd/lib/libnpf/npf.c (revision 8af4b3eb)
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.50 2022/06/07 16:27:24 christos 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
_npf_add_addr(nvlist_t * nvl,const char * name,int af,const npf_addr_t * addr)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
_npf_get_addr(const nvlist_t * nvl,const char * name,npf_addr_t * addr)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
_npf_dataset_lookup(const nvlist_t * dict,const char * dataset,const char * key,const char * name)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 *
_npf_dataset_getelement(nvlist_t * dict,const char * dataset,unsigned i)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
_npf_rules_process(nl_config_t * ncf,nvlist_t * dict,const char * key)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
_npf_init_error(int error,npf_error_t * errinfo)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
_npf_extract_error(nvlist_t * resp,npf_error_t * errinfo)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
_npf_xfer_fd(int fd,unsigned long cmd,nvlist_t * req,nvlist_t ** resp)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
_npf_xfer_fd_errno(int fd,unsigned long cmd,nvlist_t * req)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 *
npf_config_create(void)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
npf_config_submit(nl_config_t * ncf,int fd,npf_error_t * errinfo)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 *
npf_config_retrieve(int fd)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 *
npf_config_export(nl_config_t * ncf,size_t * length)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 *
npf_config_import(const void * blob,size_t len)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
npf_config_flush(int fd)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
npf_config_active_p(nl_config_t * ncf)438 npf_config_active_p(nl_config_t *ncf)
439 {
440 	return dnvlist_get_bool(ncf->ncf_dict, "active", false);
441 }
442 
443 bool
npf_config_loaded_p(nl_config_t * ncf)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 *
npf_config_build(nl_config_t * ncf)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
npf_config_destroy(nl_config_t * ncf)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
npf_param_get(nl_config_t * ncf,const char * name,int * valp)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
npf_param_set(nl_config_t * ncf,const char * name,int val)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 *
npf_param_iterate(nl_config_t * ncf,nl_iter_t * iter,int * val,int * defval)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
_npf_nat_ruleset_p(const char * name)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
npf_ruleset_add(int fd,const char * rname,nl_rule_t * rl,uint64_t * id)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
npf_ruleset_remove(int fd,const char * rname,uint64_t id)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
npf_ruleset_remkey(int fd,const char * rname,const void * key,size_t len)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
npf_ruleset_flush(int fd,const char * rname)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 *
npf_ext_construct(const char * name)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
npf_ext_param_u32(nl_ext_t * ext,const char * key,uint32_t val)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
npf_ext_param_bool(nl_ext_t * ext,const char * key,bool val)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
npf_ext_param_string(nl_ext_t * ext,const char * key,const char * val)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 *
npf_rule_create(const char * name,uint32_t attr,const char * ifname)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
npf_rule_setcode(nl_rule_t * rl,int type,const void * code,size_t len)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
npf_rule_setkey(nl_rule_t * rl,const void * key,size_t len)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
npf_rule_setinfo(nl_rule_t * rl,const void * info,size_t len)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
npf_rule_setprio(nl_rule_t * rl,int pri)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
npf_rule_setproc(nl_rule_t * rl,const char * name)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 *
npf_rule_export(nl_rule_t * rl,size_t * length)739 npf_rule_export(nl_rule_t *rl, size_t *length)
740 {
741 	return nvlist_pack(rl->rule_dict, length);
742 }
743 
744 bool
npf_rule_exists_p(nl_config_t * ncf,const char * name)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
npf_rule_insert(nl_config_t * ncf,nl_rule_t * parent,nl_rule_t * rl)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 *
_npf_rule_iterate1(nl_config_t * ncf,const char * key,nl_iter_t * iter,unsigned * level)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 *
npf_rule_iterate(nl_config_t * ncf,nl_iter_t * iter,unsigned * level)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 *
npf_rule_getname(nl_rule_t * rl)817 npf_rule_getname(nl_rule_t *rl)
818 {
819 	return dnvlist_get_string(rl->rule_dict, "name", NULL);
820 }
821 
822 uint32_t
npf_rule_getattr(nl_rule_t * rl)823 npf_rule_getattr(nl_rule_t *rl)
824 {
825 	return dnvlist_get_number(rl->rule_dict, "attr", 0);
826 }
827 
828 const char *
npf_rule_getinterface(nl_rule_t * rl)829 npf_rule_getinterface(nl_rule_t *rl)
830 {
831 	return dnvlist_get_string(rl->rule_dict, "ifname", NULL);
832 }
833 
834 const void *
npf_rule_getinfo(nl_rule_t * rl,size_t * len)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 *
npf_rule_getproc(nl_rule_t * rl)841 npf_rule_getproc(nl_rule_t *rl)
842 {
843 	return dnvlist_get_string(rl->rule_dict, "rproc", NULL);
844 }
845 
846 uint64_t
npf_rule_getid(nl_rule_t * rl)847 npf_rule_getid(nl_rule_t *rl)
848 {
849 	return dnvlist_get_number(rl->rule_dict, "id", 0);
850 }
851 
852 const void *
npf_rule_getcode(nl_rule_t * rl,int * type,size_t * len)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
_npf_ruleset_list(int fd,const char * rname,nl_config_t * ncf)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
npf_rule_destroy(nl_rule_t * rl)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 *
npf_rproc_create(const char * name)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
npf_rproc_extcall(nl_rproc_t * rp,nl_ext_t * ext)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
npf_rproc_exists_p(nl_config_t * ncf,const char * name)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
npf_rproc_insert(nl_config_t * ncf,nl_rproc_t * rp)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 *
npf_rproc_iterate(nl_config_t * ncf,nl_iter_t * iter)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 *
npf_rproc_getname(nl_rproc_t * rp)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 *
npf_nat_create(int type,unsigned flags,const char * ifname)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
npf_nat_insert(nl_config_t * ncf,nl_nat_t * nt)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 *
npf_nat_iterate(nl_config_t * ncf,nl_iter_t * iter)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
npf_nat_setaddr(nl_nat_t * nt,int af,npf_addr_t * addr,npf_netmask_t mask)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
npf_nat_setport(nl_nat_t * nt,in_port_t port)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
npf_nat_settable(nl_nat_t * nt,unsigned tid)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
npf_nat_setalgo(nl_nat_t * nt,unsigned algo)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
npf_nat_setnpt66(nl_nat_t * nt,uint16_t adj)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
npf_nat_gettype(nl_nat_t * nt)1067 npf_nat_gettype(nl_nat_t *nt)
1068 {
1069 	return dnvlist_get_number(nt->rule_dict, "type", 0);
1070 }
1071 
1072 unsigned
npf_nat_getflags(nl_nat_t * nt)1073 npf_nat_getflags(nl_nat_t *nt)
1074 {
1075 	return dnvlist_get_number(nt->rule_dict, "flags", 0);
1076 }
1077 
1078 unsigned
npf_nat_getalgo(nl_nat_t * nt)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 *
npf_nat_getaddr(nl_nat_t * nt,size_t * alen,npf_netmask_t * mask)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
npf_nat_getport(nl_nat_t * nt)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
npf_nat_gettable(nl_nat_t * nt)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 *
npf_table_create(const char * name,unsigned id,int type)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
npf_table_add_entry(nl_table_t * tl,int af,const npf_addr_t * addr,const npf_netmask_t mask)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
_npf_table_build_const(nl_table_t * tl)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", 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
npf_table_insert(nl_config_t * ncf,nl_table_t * tl)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
npf_table_replace(int fd,nl_table_t * tl,npf_error_t * errinfo)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 *
npf_table_iterate(nl_config_t * ncf,nl_iter_t * iter)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
npf_table_getid(nl_table_t * tl)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 *
npf_table_getname(nl_table_t * tl)1310 npf_table_getname(nl_table_t *tl)
1311 {
1312 	return dnvlist_get_string(tl->table_dict, "name", NULL);
1313 }
1314 
1315 int
npf_table_gettype(nl_table_t * tl)1316 npf_table_gettype(nl_table_t *tl)
1317 {
1318 	return dnvlist_get_number(tl->table_dict, "type", 0);
1319 }
1320 
1321 void
npf_table_destroy(nl_table_t * tl)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
npf_alg_load(nl_config_t * ncf,const char * name)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
_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)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
npf_nat_lookup(int fd,int af,npf_addr_t * addr[2],in_port_t port[2],int proto,int di __unused)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
npf_connkey_handle(const nvlist_t * key_nv,npf_connpoint_t * ep)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
npf_conn_handle(const nvlist_t * conn,npf_conn_func_t func,void * arg)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
npf_conn_list(int fd,npf_conn_func_t func,void * arg)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
_npf_debug_addif(nl_config_t * ncf,const char * ifname)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
_npf_config_dump(nl_config_t * ncf,int fd)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