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 58struct nl_rule { 59 nvlist_t * rule_dict; 60}; 61 62struct nl_rproc { 63 nvlist_t * rproc_dict; 64}; 65 66struct nl_table { 67 nvlist_t * table_dict; 68}; 69 70struct nl_alg { 71 nvlist_t * alg_dict; 72}; 73 74struct nl_ext { 75 nvlist_t * ext_dict; 76}; 77 78struct 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 98static bool 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 114static unsigned 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 130static bool 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 152static const nvlist_t * 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 */ 172static void 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 */ 212static int 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 */ 226static int 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 */ 253static int 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; 308err: 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 */ 319static int 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 338nl_config_t * 339npf_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 352int 353npf_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 370nl_config_t * 371npf_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 395void * 396npf_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 403nl_config_t * 404npf_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 420int 421npf_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 437bool 438npf_config_active_p(nl_config_t *ncf) 439{ 440 return dnvlist_get_bool(ncf->ncf_dict, "active", false); 441} 442 443bool 444npf_config_loaded_p(nl_config_t *ncf) 445{ 446 return nvlist_exists_nvlist_array(ncf->ncf_dict, "rules"); 447} 448 449const void * 450npf_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 466void 467npf_config_destroy(nl_config_t *ncf) 468{ 469 nvlist_destroy(ncf->ncf_dict); 470 free(ncf); 471} 472 473/* 474 * PARAMETERS. 475 */ 476 477int 478npf_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 490int 491npf_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 514const char * 515npf_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 } 528skip: 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 555static inline bool 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 562int 563npf_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 589int 590npf_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 606int 607npf_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 623int 624npf_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 643nl_ext_t * 644npf_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 657void 658npf_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 663void 664npf_ext_param_bool(nl_ext_t *ext, const char *key, bool val) 665{ 666 nvlist_add_bool(ext->ext_dict, key, val); 667} 668 669void 670npf_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 679nl_rule_t * 680npf_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 699int 700npf_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 710int 711npf_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 717int 718npf_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 724int 725npf_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 731int 732npf_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 738void * 739npf_rule_export(nl_rule_t *rl, size_t *length) 740{ 741 return nvlist_pack(rl->rule_dict, length); 742} 743 744bool 745npf_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 752int 753npf_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 774static nl_rule_t * 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 810nl_rule_t * 811npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level) 812{ 813 return _npf_rule_iterate1(ncf, "rules", iter, level); 814} 815 816const char * 817npf_rule_getname(nl_rule_t *rl) 818{ 819 return dnvlist_get_string(rl->rule_dict, "name", NULL); 820} 821 822uint32_t 823npf_rule_getattr(nl_rule_t *rl) 824{ 825 return dnvlist_get_number(rl->rule_dict, "attr", 0); 826} 827 828const char * 829npf_rule_getinterface(nl_rule_t *rl) 830{ 831 return dnvlist_get_string(rl->rule_dict, "ifname", NULL); 832} 833 834const void * 835npf_rule_getinfo(nl_rule_t *rl, size_t *len) 836{ 837 return dnvlist_get_binary(rl->rule_dict, "info", len, NULL, 0); 838} 839 840const char * 841npf_rule_getproc(nl_rule_t *rl) 842{ 843 return dnvlist_get_string(rl->rule_dict, "rproc", NULL); 844} 845 846uint64_t 847npf_rule_getid(nl_rule_t *rl) 848{ 849 return dnvlist_get_number(rl->rule_dict, "id", 0); 850} 851 852const void * 853npf_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 859int 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 888void 889npf_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 899nl_rproc_t * 900npf_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 913int 914npf_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 928bool 929npf_rproc_exists_p(nl_config_t *ncf, const char *name) 930{ 931 return _npf_dataset_lookup(ncf->ncf_dict, "rprocs", "name", name); 932} 933 934int 935npf_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 952nl_rproc_t * 953npf_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 968const char * 969npf_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 978nl_nat_t * 979npf_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 1002int 1003npf_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 1011nl_nat_t * 1012npf_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 1018int 1019npf_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 1029int 1030npf_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 1037int 1038npf_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 1047int 1048npf_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 1054int 1055npf_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 1066int 1067npf_nat_gettype(nl_nat_t *nt) 1068{ 1069 return dnvlist_get_number(nt->rule_dict, "type", 0); 1070} 1071 1072unsigned 1073npf_nat_getflags(nl_nat_t *nt) 1074{ 1075 return dnvlist_get_number(nt->rule_dict, "flags", 0); 1076} 1077 1078unsigned 1079npf_nat_getalgo(nl_nat_t *nt) 1080{ 1081 return dnvlist_get_number(nt->rule_dict, "nat-algo", 0); 1082} 1083 1084const npf_addr_t * 1085npf_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 1100in_port_t 1101npf_nat_getport(nl_nat_t *nt) 1102{ 1103 return (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0); 1104} 1105 1106unsigned 1107npf_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 1116nl_table_t * 1117npf_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 1132int 1133npf_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 1152static inline int 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); 1237out: 1238 if (fd != -1) { 1239 close(fd); 1240 } 1241 cdbw_close(cdbw); 1242 return error; 1243} 1244 1245int 1246npf_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 1267int 1268npf_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 1287nl_table_t * 1288npf_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 1303unsigned 1304npf_table_getid(nl_table_t *tl) 1305{ 1306 return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1); 1307} 1308 1309const char * 1310npf_table_getname(nl_table_t *tl) 1311{ 1312 return dnvlist_get_string(tl->table_dict, "name", NULL); 1313} 1314 1315int 1316npf_table_gettype(nl_table_t *tl) 1317{ 1318 return dnvlist_get_number(tl->table_dict, "type", 0); 1319} 1320 1321void 1322npf_table_destroy(nl_table_t *tl) 1323{ 1324 nvlist_destroy(tl->table_dict); 1325 free(tl); 1326} 1327 1328/* 1329 * ALG INTERFACE. 1330 */ 1331 1332int 1333npf_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 1351typedef struct { 1352 unsigned alen; 1353 unsigned proto; 1354 npf_addr_t addr[3]; 1355 in_port_t port[3]; 1356} npf_connpoint_t; 1357 1358static int 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")); 1419out: 1420 if (resp) { 1421 nvlist_destroy(resp); 1422 } 1423 if (req) { 1424 nvlist_destroy(req); 1425 } 1426 return error; 1427} 1428 1429int 1430npf_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 1441static bool 1442npf_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 1458static void 1459npf_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); 1483err: 1484 return; 1485} 1486 1487int 1488npf_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 1514void 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 1536void 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