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