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.52 2023/08/08 10:36:04 riastradh 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