npf.c revision 1.43.14.2 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.43.14.2 2020/04/13 08:03:14 martin 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