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