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