1 /*- 2 * Copyright (c) 2010-2025 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.56 2025/07/01 19:55:15 joe 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 /* both user and group */ 739 int 740 npf_rule_setrid(nl_rule_t *rl, struct r_id rid, const char *name) 741 { 742 uint64_t uid_element[3] = { rid.id[0], rid.id[1], rid.op }; 743 nvlist_add_number_array(rl->rule_dict, name, uid_element, 3); 744 return nvlist_error(rl->rule_dict); 745 } 746 747 void * 748 npf_rule_export(nl_rule_t *rl, size_t *length) 749 { 750 return nvlist_pack(rl->rule_dict, length); 751 } 752 753 bool 754 npf_rule_exists_p(nl_config_t *ncf, const char *name) 755 { 756 const char *key = nvlist_exists_nvlist_array(ncf->ncf_dict, 757 "rules") ? "rules" : "__rules"; // config may not be built yet 758 return _npf_dataset_lookup(ncf->ncf_dict, key, "name", name); 759 } 760 761 int 762 npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl) 763 { 764 nvlist_t *rule_dict = rl->rule_dict; 765 nvlist_t *target; 766 const char *key; 767 768 if (parent) { 769 /* Subrule of the parent. */ 770 target = parent->rule_dict; 771 key = "subrules"; 772 } else { 773 /* Global ruleset. */ 774 target = ncf->ncf_dict; 775 key = "__rules"; 776 } 777 nvlist_append_nvlist_array(target, key, rule_dict); 778 nvlist_destroy(rule_dict); 779 free(rl); 780 return 0; 781 } 782 783 static nl_rule_t * 784 _npf_rule_iterate1(nl_config_t *ncf, const char *key, 785 nl_iter_t *iter, unsigned *level) 786 { 787 unsigned i = *iter; 788 const nvlist_t *rule_dict; 789 uint32_t skipto; 790 791 if (i == 0) { 792 /* Initialise the iterator. */ 793 ncf->ncf_nlevel = 0; 794 ncf->ncf_reduce[0] = 0; 795 } 796 797 rule_dict = _npf_dataset_getelement(ncf->ncf_dict, key, i); 798 if (!rule_dict) { 799 *iter = NPF_ITER_BEGIN; 800 return NULL; 801 } 802 *iter = i + 1; // next 803 *level = ncf->ncf_nlevel; 804 805 skipto = dnvlist_get_number(rule_dict, "skip-to", 0); 806 if (skipto) { 807 ncf->ncf_nlevel++; 808 ncf->ncf_reduce[ncf->ncf_nlevel] = skipto; 809 } 810 if (ncf->ncf_reduce[ncf->ncf_nlevel] == (i + 1)) { 811 assert(ncf->ncf_nlevel > 0); 812 ncf->ncf_nlevel--; 813 } 814 815 ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX 816 return &ncf->ncf_cur_rule; 817 } 818 819 nl_rule_t * 820 npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level) 821 { 822 return _npf_rule_iterate1(ncf, "rules", iter, level); 823 } 824 825 const char * 826 npf_rule_getname(nl_rule_t *rl) 827 { 828 return dnvlist_get_string(rl->rule_dict, "name", NULL); 829 } 830 831 uint32_t 832 npf_rule_getattr(nl_rule_t *rl) 833 { 834 return dnvlist_get_number(rl->rule_dict, "attr", 0); 835 } 836 837 const char * 838 npf_rule_getinterface(nl_rule_t *rl) 839 { 840 return dnvlist_get_string(rl->rule_dict, "ifname", NULL); 841 } 842 843 const void * 844 npf_rule_getinfo(nl_rule_t *rl, size_t *len) 845 { 846 return dnvlist_get_binary(rl->rule_dict, "info", len, NULL, 0); 847 } 848 849 const char * 850 npf_rule_getproc(nl_rule_t *rl) 851 { 852 return dnvlist_get_string(rl->rule_dict, "rproc", NULL); 853 } 854 855 int 856 npf_rule_getrid(struct r_id *r_id, nl_rule_t *rl, const char *key) 857 { 858 if (nvlist_exists_number_array(rl->rule_dict, key)) { 859 size_t nitems; 860 const uint64_t *rid = nvlist_get_number_array(rl->rule_dict, key, &nitems); 861 assert(nitems == 3); 862 863 r_id->id[0] = (uint32_t)rid[0]; 864 r_id->id[1] = (uint32_t)rid[1]; 865 r_id->op = (uint8_t)rid[2]; 866 return 0; 867 } 868 return -1; 869 } 870 871 uint64_t 872 npf_rule_getid(nl_rule_t *rl) 873 { 874 return dnvlist_get_number(rl->rule_dict, "id", 0); 875 } 876 877 const void * 878 npf_rule_getcode(nl_rule_t *rl, int *type, size_t *len) 879 { 880 *type = (int)dnvlist_get_number(rl->rule_dict, "code-type", 0); 881 return dnvlist_get_binary(rl->rule_dict, "code", len, NULL, 0); 882 } 883 884 int 885 _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf) 886 { 887 const bool natset = _npf_nat_ruleset_p(rname); 888 nvlist_t *req, *resp; 889 int error; 890 891 req = nvlist_create(0); 892 nvlist_add_string(req, "ruleset-name", rname); 893 nvlist_add_bool(req, "nat-ruleset", natset); 894 nvlist_add_number(req, "command", NPF_CMD_RULE_LIST); 895 896 error = _npf_xfer_fd(fd, IOC_NPF_RULE, req, &resp); 897 nvlist_destroy(req); 898 if (error) { 899 return error; 900 } 901 902 if (nvlist_exists_nvlist_array(resp, "rules")) { 903 nvlist_t **rules; 904 size_t n; 905 906 rules = nvlist_take_nvlist_array(resp, "rules", &n); 907 nvlist_move_nvlist_array(ncf->ncf_dict, "rules", rules, n); 908 } 909 nvlist_destroy(resp); 910 return 0; 911 } 912 913 void 914 npf_rule_destroy(nl_rule_t *rl) 915 { 916 nvlist_destroy(rl->rule_dict); 917 free(rl); 918 } 919 920 /* 921 * RULE PROCEDURE INTERFACE. 922 */ 923 924 nl_rproc_t * 925 npf_rproc_create(const char *name) 926 { 927 nl_rproc_t *rp; 928 929 rp = malloc(sizeof(nl_rproc_t)); 930 if (!rp) { 931 return NULL; 932 } 933 rp->rproc_dict = nvlist_create(0); 934 nvlist_add_string(rp->rproc_dict, "name", name); 935 return rp; 936 } 937 938 int 939 npf_rproc_extcall(nl_rproc_t *rp, nl_ext_t *ext) 940 { 941 nvlist_t *rproc_dict = rp->rproc_dict; 942 const char *name = dnvlist_get_string(ext->ext_dict, "name", NULL); 943 944 if (_npf_dataset_lookup(rproc_dict, "extcalls", "name", name)) { 945 return EEXIST; 946 } 947 nvlist_append_nvlist_array(rproc_dict, "extcalls", ext->ext_dict); 948 nvlist_destroy(ext->ext_dict); 949 free(ext); 950 return 0; 951 } 952 953 bool 954 npf_rproc_exists_p(nl_config_t *ncf, const char *name) 955 { 956 return _npf_dataset_lookup(ncf->ncf_dict, "rprocs", "name", name); 957 } 958 959 int 960 npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp) 961 { 962 const char *name; 963 964 name = dnvlist_get_string(rp->rproc_dict, "name", NULL); 965 if (!name) { 966 return EINVAL; 967 } 968 if (npf_rproc_exists_p(ncf, name)) { 969 return EEXIST; 970 } 971 nvlist_append_nvlist_array(ncf->ncf_dict, "rprocs", rp->rproc_dict); 972 nvlist_destroy(rp->rproc_dict); 973 free(rp); 974 return 0; 975 } 976 977 nl_rproc_t * 978 npf_rproc_iterate(nl_config_t *ncf, nl_iter_t *iter) 979 { 980 const nvlist_t *rproc_dict; 981 unsigned i = *iter; 982 983 rproc_dict = _npf_dataset_getelement(ncf->ncf_dict, "rprocs", i); 984 if (!rproc_dict) { 985 *iter = NPF_ITER_BEGIN; 986 return NULL; 987 } 988 *iter = i + 1; // next 989 ncf->ncf_cur_rproc.rproc_dict = __UNCONST(rproc_dict); // XXX 990 return &ncf->ncf_cur_rproc; 991 } 992 993 const char * 994 npf_rproc_getname(nl_rproc_t *rp) 995 { 996 return dnvlist_get_string(rp->rproc_dict, "name", NULL); 997 } 998 999 /* 1000 * NAT INTERFACE. 1001 */ 1002 1003 nl_nat_t * 1004 npf_nat_create(int type, unsigned flags, const char *ifname) 1005 { 1006 nl_rule_t *rl; 1007 nvlist_t *rule_dict; 1008 uint32_t attr; 1009 1010 attr = NPF_RULE_PASS | NPF_RULE_FINAL | NPF_RULE_LAYER_3 | 1011 (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN); 1012 1013 /* Create a rule for NAT policy. Next, will add NAT data. */ 1014 rl = npf_rule_create(NULL, attr, ifname); 1015 if (!rl) { 1016 return NULL; 1017 } 1018 rule_dict = rl->rule_dict; 1019 1020 /* Translation type and flags. */ 1021 nvlist_add_number(rule_dict, "type", type); 1022 nvlist_add_number(rule_dict, "flags", flags); 1023 nvlist_add_bool(rule_dict, "nat-rule", true); 1024 return (nl_nat_t *)rl; 1025 } 1026 1027 int 1028 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt) 1029 { 1030 nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict); 1031 nvlist_destroy(nt->rule_dict); 1032 free(nt); 1033 return 0; 1034 } 1035 1036 nl_nat_t * 1037 npf_nat_iterate(nl_config_t *ncf, nl_iter_t *iter) 1038 { 1039 unsigned level; 1040 return _npf_rule_iterate1(ncf, "nat", iter, &level); 1041 } 1042 1043 int 1044 npf_nat_setaddr(nl_nat_t *nt, int af, npf_addr_t *addr, npf_netmask_t mask) 1045 { 1046 /* Translation IP and mask. */ 1047 if (!_npf_add_addr(nt->rule_dict, "nat-addr", af, addr)) { 1048 return nvlist_error(nt->rule_dict); 1049 } 1050 nvlist_add_number(nt->rule_dict, "nat-mask", (uint32_t)mask); 1051 return nvlist_error(nt->rule_dict); 1052 } 1053 1054 int 1055 npf_nat_setport(nl_nat_t *nt, in_port_t port) 1056 { 1057 /* Translation port (for redirect case). */ 1058 nvlist_add_number(nt->rule_dict, "nat-port", port); 1059 return nvlist_error(nt->rule_dict); 1060 } 1061 1062 int 1063 npf_nat_settable(nl_nat_t *nt, unsigned tid) 1064 { 1065 /* 1066 * Translation table ID; the address/mask will then serve as a filter. 1067 */ 1068 nvlist_add_number(nt->rule_dict, "nat-table-id", tid); 1069 return nvlist_error(nt->rule_dict); 1070 } 1071 1072 int 1073 npf_nat_setalgo(nl_nat_t *nt, unsigned algo) 1074 { 1075 nvlist_add_number(nt->rule_dict, "nat-algo", algo); 1076 return nvlist_error(nt->rule_dict); 1077 } 1078 1079 int 1080 npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj) 1081 { 1082 int error; 1083 1084 if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) { 1085 return error; 1086 } 1087 nvlist_add_number(nt->rule_dict, "npt66-adj", adj); 1088 return nvlist_error(nt->rule_dict); 1089 } 1090 1091 int 1092 npf_nat_gettype(nl_nat_t *nt) 1093 { 1094 return dnvlist_get_number(nt->rule_dict, "type", 0); 1095 } 1096 1097 unsigned 1098 npf_nat_getflags(nl_nat_t *nt) 1099 { 1100 return dnvlist_get_number(nt->rule_dict, "flags", 0); 1101 } 1102 1103 unsigned 1104 npf_nat_getalgo(nl_nat_t *nt) 1105 { 1106 return dnvlist_get_number(nt->rule_dict, "nat-algo", 0); 1107 } 1108 1109 const npf_addr_t * 1110 npf_nat_getaddr(nl_nat_t *nt, size_t *alen, npf_netmask_t *mask) 1111 { 1112 const void *data; 1113 1114 if (nvlist_exists(nt->rule_dict, "nat-addr")) { 1115 data = nvlist_get_binary(nt->rule_dict, "nat-addr", alen); 1116 *mask = nvlist_get_number(nt->rule_dict, "nat-mask"); 1117 } else { 1118 data = NULL; 1119 *alen = 0; 1120 *mask = NPF_NO_NETMASK; 1121 } 1122 return data; 1123 } 1124 1125 in_port_t 1126 npf_nat_getport(nl_nat_t *nt) 1127 { 1128 return (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0); 1129 } 1130 1131 unsigned 1132 npf_nat_gettable(nl_nat_t *nt) 1133 { 1134 return dnvlist_get_number(nt->rule_dict, "nat-table-id", 0); 1135 } 1136 1137 /* 1138 * TABLE INTERFACE. 1139 */ 1140 1141 nl_table_t * 1142 npf_table_create(const char *name, unsigned id, int type) 1143 { 1144 nl_table_t *tl; 1145 1146 tl = malloc(sizeof(*tl)); 1147 if (!tl) { 1148 return NULL; 1149 } 1150 tl->table_dict = nvlist_create(0); 1151 nvlist_add_string(tl->table_dict, "name", name); 1152 nvlist_add_number(tl->table_dict, "id", id); 1153 nvlist_add_number(tl->table_dict, "type", type); 1154 return tl; 1155 } 1156 1157 int 1158 npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr, 1159 const npf_netmask_t mask) 1160 { 1161 nvlist_t *entry; 1162 1163 entry = nvlist_create(0); 1164 if (!entry) { 1165 return ENOMEM; 1166 } 1167 if (!_npf_add_addr(entry, "addr", af, addr)) { 1168 nvlist_destroy(entry); 1169 return EINVAL; 1170 } 1171 nvlist_add_number(entry, "mask", mask); 1172 nvlist_append_nvlist_array(tl->table_dict, "entries", entry); 1173 nvlist_destroy(entry); 1174 return 0; 1175 } 1176 1177 static inline int 1178 _npf_table_build_const(nl_table_t *tl) 1179 { 1180 struct cdbw *cdbw; 1181 const nvlist_t * const *entries; 1182 int error = 0, fd = -1; 1183 size_t nitems, len; 1184 void *cdb, *buf; 1185 struct stat sb; 1186 char sfn[32]; 1187 1188 if (dnvlist_get_number(tl->table_dict, "type", 0) != NPF_TABLE_CONST) { 1189 return 0; 1190 } 1191 1192 if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) { 1193 return 0; 1194 } 1195 1196 /* 1197 * Create a constant database and put all the entries. 1198 */ 1199 if ((cdbw = cdbw_open()) == NULL) { 1200 return errno; 1201 } 1202 entries = nvlist_get_nvlist_array(tl->table_dict, "entries", &nitems); 1203 for (unsigned i = 0; i < nitems; i++) { 1204 const nvlist_t *entry = entries[i]; 1205 const npf_addr_t *addr; 1206 size_t alen; 1207 1208 addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0); 1209 if (addr == NULL || alen == 0 || alen > sizeof(npf_addr_t)) { 1210 error = EINVAL; 1211 goto out; 1212 } 1213 if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) { 1214 error = errno; 1215 goto out; 1216 } 1217 } 1218 1219 /* 1220 * Write the constant database into a temporary file. 1221 */ 1222 strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn)); 1223 sfn[sizeof(sfn) - 1] = '\0'; 1224 1225 if ((fd = mkstemp(sfn)) == -1) { 1226 error = errno; 1227 goto out; 1228 } 1229 unlink(sfn); 1230 1231 if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) { 1232 error = errno; 1233 goto out; 1234 } 1235 if (fstat(fd, &sb) == -1) { 1236 error = errno; 1237 goto out; 1238 } 1239 len = sb.st_size; 1240 1241 /* 1242 * Memory-map the database and copy it into a buffer. 1243 */ 1244 buf = malloc(len); 1245 if (!buf) { 1246 error = ENOMEM; 1247 goto out; 1248 } 1249 cdb = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); 1250 if (cdb == MAP_FAILED) { 1251 error = errno; 1252 free(buf); 1253 goto out; 1254 } 1255 munmap(cdb, len); 1256 1257 /* 1258 * Move the data buffer to the nvlist. 1259 */ 1260 nvlist_move_binary(tl->table_dict, "data", buf, len); 1261 error = nvlist_error(tl->table_dict); 1262 out: 1263 if (fd != -1) { 1264 close(fd); 1265 } 1266 cdbw_close(cdbw); 1267 return error; 1268 } 1269 1270 int 1271 npf_table_insert(nl_config_t *ncf, nl_table_t *tl) 1272 { 1273 const char *name; 1274 int error; 1275 1276 name = dnvlist_get_string(tl->table_dict, "name", NULL); 1277 if (!name) { 1278 return EINVAL; 1279 } 1280 if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) { 1281 return EEXIST; 1282 } 1283 if ((error = _npf_table_build_const(tl)) != 0) { 1284 return error; 1285 } 1286 nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict); 1287 nvlist_destroy(tl->table_dict); 1288 free(tl); 1289 return 0; 1290 } 1291 1292 int 1293 npf_table_replace(int fd, nl_table_t *tl, npf_error_t *errinfo) 1294 { 1295 nvlist_t *resp = NULL; 1296 int error; 1297 1298 /* Ensure const tables are built. */ 1299 if ((error = _npf_table_build_const(tl)) != 0) { 1300 return _npf_init_error(errno, errinfo); 1301 } 1302 error = _npf_xfer_fd(fd, IOC_NPF_TABLE_REPLACE, tl->table_dict, &resp); 1303 if (error) { 1304 assert(resp == NULL); 1305 return _npf_init_error(errno, errinfo); 1306 } 1307 error = _npf_extract_error(resp, errinfo); 1308 nvlist_destroy(resp); 1309 return error; 1310 } 1311 1312 nl_table_t * 1313 npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter) 1314 { 1315 const nvlist_t *table_dict; 1316 unsigned i = *iter; 1317 1318 table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i); 1319 if (!table_dict) { 1320 *iter = NPF_ITER_BEGIN; 1321 return NULL; 1322 } 1323 *iter = i + 1; // next 1324 ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX 1325 return &ncf->ncf_cur_table; 1326 } 1327 1328 unsigned 1329 npf_table_getid(nl_table_t *tl) 1330 { 1331 return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1); 1332 } 1333 1334 const char * 1335 npf_table_getname(nl_table_t *tl) 1336 { 1337 return dnvlist_get_string(tl->table_dict, "name", NULL); 1338 } 1339 1340 int 1341 npf_table_gettype(nl_table_t *tl) 1342 { 1343 return dnvlist_get_number(tl->table_dict, "type", 0); 1344 } 1345 1346 void 1347 npf_table_destroy(nl_table_t *tl) 1348 { 1349 nvlist_destroy(tl->table_dict); 1350 free(tl); 1351 } 1352 1353 /* 1354 * ALG INTERFACE. 1355 */ 1356 1357 int 1358 npf_alg_load(nl_config_t *ncf, const char *name) 1359 { 1360 nvlist_t *alg_dict; 1361 1362 if (_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) { 1363 return EEXIST; 1364 } 1365 alg_dict = nvlist_create(0); 1366 nvlist_add_string(alg_dict, "name", name); 1367 nvlist_append_nvlist_array(ncf->ncf_dict, "algs", alg_dict); 1368 nvlist_destroy(alg_dict); 1369 return 0; 1370 } 1371 1372 /* 1373 * CONNECTION / NAT ENTRY INTERFACE. 1374 */ 1375 1376 typedef struct { 1377 unsigned alen; 1378 unsigned proto; 1379 npf_addr_t addr[3]; 1380 in_port_t port[3]; 1381 } npf_connpoint_t; 1382 1383 static int 1384 _npf_conn_lookup(int fd, const int af, npf_addr_t *addr[2], in_port_t port[2], 1385 unsigned proto, const char *ifname, unsigned di) 1386 { 1387 nvlist_t *req = NULL, *resp = NULL, *key_nv; 1388 const nvlist_t *nat; 1389 int error = EINVAL; 1390 1391 /* 1392 * Setup the connection lookup key. 1393 */ 1394 if ((key_nv = nvlist_create(0)) == NULL) { 1395 return ENOMEM; 1396 } 1397 if (!_npf_add_addr(key_nv, "saddr", af, addr[0])) { 1398 nvlist_destroy(key_nv); 1399 goto out; 1400 } 1401 if (!_npf_add_addr(key_nv, "daddr", af, addr[1])) { 1402 nvlist_destroy(key_nv); 1403 goto out; 1404 } 1405 nvlist_add_number(key_nv, "sport", htons(port[0])); 1406 nvlist_add_number(key_nv, "dport", htons(port[1])); 1407 nvlist_add_number(key_nv, "proto", proto); 1408 if (ifname) { 1409 nvlist_add_string(key_nv, "ifname", ifname); 1410 } 1411 if (di) { 1412 nvlist_add_number(key_nv, "di", di); 1413 } 1414 1415 /* 1416 * Setup the request. 1417 */ 1418 if ((req = nvlist_create(0)) == NULL) { 1419 error = ENOMEM; 1420 goto out; 1421 } 1422 nvlist_move_nvlist(req, "key", key_nv); 1423 1424 /* Lookup: retrieve the connection entry. */ 1425 error = _npf_xfer_fd(fd, IOC_NPF_CONN_LOOKUP, req, &resp); 1426 if (error) { 1427 goto out; 1428 } 1429 1430 /* 1431 * Get the NAT entry and extract the translated pair. 1432 */ 1433 if ((nat = dnvlist_get_nvlist(resp, "nat", NULL)) == NULL) { 1434 error = ENOENT; 1435 goto out; 1436 } 1437 if (_npf_get_addr(nat, "oaddr", addr[0]) == 0 || 1438 _npf_get_addr(nat, "taddr", addr[1]) == 0) { 1439 error = EINVAL; 1440 goto out; 1441 } 1442 port[0] = ntohs(nvlist_get_number(nat, "oport")); 1443 port[1] = ntohs(nvlist_get_number(nat, "tport")); 1444 out: 1445 if (resp) { 1446 nvlist_destroy(resp); 1447 } 1448 if (req) { 1449 nvlist_destroy(req); 1450 } 1451 return error; 1452 } 1453 1454 int 1455 npf_nat_lookup(int fd, int af, npf_addr_t *addr[2], in_port_t port[2], 1456 int proto, int di __unused) 1457 { 1458 int error; 1459 1460 port[0] = ntohs(port[0]); port[1] = ntohs(port[1]); 1461 error = _npf_conn_lookup(fd, af, addr, port, proto, NULL, 0); 1462 port[0] = htons(port[0]); port[1] = htons(port[1]); 1463 return error; 1464 } 1465 1466 static bool 1467 npf_connkey_handle(const nvlist_t *key_nv, npf_connpoint_t *ep) 1468 { 1469 unsigned alen1, alen2; 1470 1471 alen1 = _npf_get_addr(key_nv, "saddr", &ep->addr[0]); 1472 alen2 = _npf_get_addr(key_nv, "daddr", &ep->addr[1]); 1473 if (alen1 == 0 || alen1 != alen2) { 1474 return false; 1475 } 1476 ep->alen = alen1; 1477 ep->port[0] = ntohs(nvlist_get_number(key_nv, "sport")); 1478 ep->port[1] = ntohs(nvlist_get_number(key_nv, "dport")); 1479 ep->proto = nvlist_get_number(key_nv, "proto"); 1480 return true; 1481 } 1482 1483 static void 1484 npf_conn_handle(const nvlist_t *conn, npf_conn_func_t func, void *arg) 1485 { 1486 const nvlist_t *key_nv, *nat_nv; 1487 const char *ifname; 1488 npf_connpoint_t ep; 1489 1490 memset(&ep, 0, sizeof(npf_connpoint_t)); 1491 1492 ifname = dnvlist_get_string(conn, "ifname", NULL); 1493 key_nv = dnvlist_get_nvlist(conn, "forw-key", NULL); 1494 if (!npf_connkey_handle(key_nv, &ep)) { 1495 goto err; 1496 } 1497 if ((nat_nv = dnvlist_get_nvlist(conn, "nat", NULL)) != NULL) { 1498 if (_npf_get_addr(nat_nv, "taddr", &ep.addr[2]) != ep.alen) { 1499 goto err; 1500 } 1501 ep.port[2] = ntohs(nvlist_get_number(nat_nv, "tport")); 1502 } 1503 /* 1504 * XXX: add 'proto' and 'flow'; perhaps expand and pass the 1505 * whole to npf_connpoint_t? 1506 */ 1507 (*func)((unsigned)ep.alen, ep.addr, ep.port, ifname, arg); 1508 err: 1509 return; 1510 } 1511 1512 int 1513 npf_conn_list(int fd, npf_conn_func_t func, void *arg) 1514 { 1515 nl_config_t *ncf; 1516 const nvlist_t * const *conns; 1517 size_t nitems; 1518 1519 ncf = npf_config_retrieve(fd); 1520 if (!ncf) { 1521 return errno; 1522 } 1523 if (!nvlist_exists_nvlist_array(ncf->ncf_dict, "conn-list")) { 1524 return 0; 1525 } 1526 conns = nvlist_get_nvlist_array(ncf->ncf_dict, "conn-list", &nitems); 1527 for (unsigned i = 0; i < nitems; i++) { 1528 const nvlist_t *conn = conns[i]; 1529 npf_conn_handle(conn, func, arg); 1530 } 1531 npf_config_destroy(ncf); 1532 return 0; 1533 } 1534 1535 /* 1536 * MISC. 1537 */ 1538 1539 void 1540 _npf_debug_addif(nl_config_t *ncf, const char *ifname) 1541 { 1542 nvlist_t *debug; 1543 1544 /* 1545 * Initialise the debug dictionary on the first call. 1546 */ 1547 debug = dnvlist_take_nvlist(ncf->ncf_dict, "debug", NULL); 1548 if (debug == NULL) { 1549 debug = nvlist_create(0); 1550 } 1551 if (!_npf_dataset_lookup(debug, "interfaces", "name", ifname)) { 1552 nvlist_t *ifdict = nvlist_create(0); 1553 nvlist_add_string(ifdict, "name", ifname); 1554 nvlist_add_number(ifdict, "index", if_nametoindex(ifname)); 1555 nvlist_append_nvlist_array(debug, "interfaces", ifdict); 1556 nvlist_destroy(ifdict); 1557 } 1558 nvlist_move_nvlist(ncf->ncf_dict, "debug", debug); 1559 } 1560 1561 void 1562 _npf_config_dump(nl_config_t *ncf, int fd) 1563 { 1564 (void)npf_config_build(ncf); 1565 nvlist_dump(ncf->ncf_dict, fd); 1566 } 1567