1 /*- 2 * Copyright (c) 2010-2020 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.50 2022/06/07 16:27:24 christos Exp $"); 32 33 #include <sys/types.h> 34 #include <sys/mman.h> 35 #include <sys/stat.h> 36 #if !defined(_NPF_STANDALONE) 37 #include <sys/ioctl.h> 38 #endif 39 #include <netinet/in_systm.h> 40 #include <netinet/in.h> 41 #include <net/if.h> 42 43 #include <stdlib.h> 44 #include <string.h> 45 #include <assert.h> 46 #include <unistd.h> 47 #include <errno.h> 48 #include <err.h> 49 50 #include <nv.h> 51 #include <dnv.h> 52 53 #include <cdbw.h> 54 55 #define _NPF_PRIVATE 56 #include "npf.h" 57 58 struct nl_rule { 59 nvlist_t * rule_dict; 60 }; 61 62 struct nl_rproc { 63 nvlist_t * rproc_dict; 64 }; 65 66 struct nl_table { 67 nvlist_t * table_dict; 68 }; 69 70 struct nl_alg { 71 nvlist_t * alg_dict; 72 }; 73 74 struct nl_ext { 75 nvlist_t * ext_dict; 76 }; 77 78 struct nl_config { 79 nvlist_t * ncf_dict; 80 81 /* Temporary rule list. */ 82 nvlist_t ** ncf_rule_list; 83 unsigned ncf_rule_count; 84 85 /* Iterators. */ 86 unsigned ncf_reduce[16]; 87 unsigned ncf_nlevel; 88 89 nl_rule_t ncf_cur_rule; 90 nl_table_t ncf_cur_table; 91 nl_rproc_t ncf_cur_rproc; 92 }; 93 94 /* 95 * Various helper routines. 96 */ 97 98 static bool 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_init_error: initialize the error structure with the message 210 * from the current error number 211 */ 212 static 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 */ 226 static 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 */ 253 static 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; 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 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 * 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 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 * 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 * 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 * 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 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 438 npf_config_active_p(nl_config_t *ncf) 439 { 440 return dnvlist_get_bool(ncf->ncf_dict, "active", false); 441 } 442 443 bool 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 * 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 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 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 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 * 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 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 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 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 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 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 * 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 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 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 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 * 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 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 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 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 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 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 * 739 npf_rule_export(nl_rule_t *rl, size_t *length) 740 { 741 return nvlist_pack(rl->rule_dict, length); 742 } 743 744 bool 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 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 * 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 * 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 * 817 npf_rule_getname(nl_rule_t *rl) 818 { 819 return dnvlist_get_string(rl->rule_dict, "name", NULL); 820 } 821 822 uint32_t 823 npf_rule_getattr(nl_rule_t *rl) 824 { 825 return dnvlist_get_number(rl->rule_dict, "attr", 0); 826 } 827 828 const char * 829 npf_rule_getinterface(nl_rule_t *rl) 830 { 831 return dnvlist_get_string(rl->rule_dict, "ifname", NULL); 832 } 833 834 const void * 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 * 841 npf_rule_getproc(nl_rule_t *rl) 842 { 843 return dnvlist_get_string(rl->rule_dict, "rproc", NULL); 844 } 845 846 uint64_t 847 npf_rule_getid(nl_rule_t *rl) 848 { 849 return dnvlist_get_number(rl->rule_dict, "id", 0); 850 } 851 852 const void * 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 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 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 * 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 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 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 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 * 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 * 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 * 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 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 * 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 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 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 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 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 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 1067 npf_nat_gettype(nl_nat_t *nt) 1068 { 1069 return dnvlist_get_number(nt->rule_dict, "type", 0); 1070 } 1071 1072 unsigned 1073 npf_nat_getflags(nl_nat_t *nt) 1074 { 1075 return dnvlist_get_number(nt->rule_dict, "flags", 0); 1076 } 1077 1078 unsigned 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 * 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 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 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 * 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 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 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 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 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 * 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 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 * 1310 npf_table_getname(nl_table_t *tl) 1311 { 1312 return dnvlist_get_string(tl->table_dict, "name", NULL); 1313 } 1314 1315 int 1316 npf_table_gettype(nl_table_t *tl) 1317 { 1318 return dnvlist_get_number(tl->table_dict, "type", 0); 1319 } 1320 1321 void 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 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 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 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 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 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 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 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 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