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