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