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