1 /*- 2 * Copyright (c) 2010-2019 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.48 2019/09/30 00:37:11 rmind Exp $"); 32 33 #include <sys/types.h> 34 #include <sys/mman.h> 35 #include <sys/stat.h> 36 #include <netinet/in_systm.h> 37 #include <netinet/in.h> 38 #include <net/if.h> 39 40 #include <stdlib.h> 41 #include <string.h> 42 #include <assert.h> 43 #include <unistd.h> 44 #include <errno.h> 45 #include <err.h> 46 47 #include <nv.h> 48 #include <dnv.h> 49 50 #include <cdbw.h> 51 52 #define _NPF_PRIVATE 53 #include "npf.h" 54 55 struct nl_rule { 56 nvlist_t * rule_dict; 57 }; 58 59 struct nl_rproc { 60 nvlist_t * rproc_dict; 61 }; 62 63 struct nl_table { 64 nvlist_t * table_dict; 65 }; 66 67 struct nl_alg { 68 nvlist_t * alg_dict; 69 }; 70 71 struct nl_ext { 72 nvlist_t * ext_dict; 73 }; 74 75 struct nl_config { 76 nvlist_t * ncf_dict; 77 78 /* Temporary rule list. */ 79 nvlist_t ** ncf_rule_list; 80 unsigned ncf_rule_count; 81 82 /* Iterators. */ 83 unsigned ncf_reduce[16]; 84 unsigned ncf_nlevel; 85 86 nl_rule_t ncf_cur_rule; 87 nl_table_t ncf_cur_table; 88 nl_rproc_t ncf_cur_rproc; 89 }; 90 91 /* 92 * Various helper routines. 93 */ 94 95 static bool 96 _npf_add_addr(nvlist_t *nvl, const char *name, int af, const npf_addr_t *addr) 97 { 98 size_t sz; 99 100 if (af == AF_INET) { 101 sz = sizeof(struct in_addr); 102 } else if (af == AF_INET6) { 103 sz = sizeof(struct in6_addr); 104 } else { 105 return false; 106 } 107 nvlist_add_binary(nvl, name, addr, sz); 108 return nvlist_error(nvl) == 0; 109 } 110 111 static unsigned 112 _npf_get_addr(const nvlist_t *nvl, const char *name, npf_addr_t *addr) 113 { 114 const void *d; 115 size_t sz = 0; 116 117 d = nvlist_get_binary(nvl, name, &sz); 118 switch (sz) { 119 case sizeof(struct in_addr): 120 case sizeof(struct in6_addr): 121 memcpy(addr, d, sz); 122 return (unsigned)sz; 123 } 124 return 0; 125 } 126 127 static bool 128 _npf_dataset_lookup(const nvlist_t *dict, const char *dataset, 129 const char *key, const char *name) 130 { 131 const nvlist_t * const *items; 132 size_t nitems; 133 134 if (!nvlist_exists_nvlist_array(dict, dataset)) { 135 return false; 136 } 137 items = nvlist_get_nvlist_array(dict, dataset, &nitems); 138 for (unsigned i = 0; i < nitems; i++) { 139 const char *item_name; 140 141 item_name = dnvlist_get_string(items[i], key, NULL); 142 if (item_name && strcmp(item_name, name) == 0) { 143 return true; 144 } 145 } 146 return false; 147 } 148 149 static const nvlist_t * 150 _npf_dataset_getelement(nvlist_t *dict, const char *dataset, unsigned i) 151 { 152 const nvlist_t * const *items; 153 size_t nitems; 154 155 if (!nvlist_exists_nvlist_array(dict, dataset)) { 156 return NULL; 157 } 158 items = nvlist_get_nvlist_array(dict, dataset, &nitems); 159 if (i < nitems) { 160 return items[i]; 161 } 162 return NULL; 163 } 164 165 /* 166 * _npf_rules_process: transform the ruleset representing nested rules 167 * with sublists into a single array with skip-to marks. 168 */ 169 static void 170 _npf_rules_process(nl_config_t *ncf, nvlist_t *dict, const char *key) 171 { 172 nvlist_t **items; 173 size_t nitems; 174 175 if (!nvlist_exists_nvlist_array(dict, key)) { 176 return; 177 } 178 items = nvlist_take_nvlist_array(dict, key, &nitems); 179 for (unsigned i = 0; i < nitems; i++) { 180 nvlist_t *rule_dict = items[i]; 181 size_t len = (ncf->ncf_rule_count + 1) * sizeof(nvlist_t *); 182 void *p = realloc(ncf->ncf_rule_list, len); 183 184 /* 185 * - Add rule to the transformed array. 186 * - Process subrules recursively. 187 * - Add the skip-to position. 188 */ 189 ncf->ncf_rule_list = p; 190 ncf->ncf_rule_list[ncf->ncf_rule_count] = rule_dict; 191 ncf->ncf_rule_count++; 192 193 if (nvlist_exists_nvlist_array(rule_dict, "subrules")) { 194 unsigned idx; 195 196 _npf_rules_process(ncf, rule_dict, "subrules"); 197 idx = ncf->ncf_rule_count; // post-recursion index 198 nvlist_add_number(rule_dict, "skip-to", idx); 199 } 200 assert(nvlist_error(rule_dict) == 0); 201 } 202 free(items); 203 } 204 205 /* 206 * _npf_extract_error: check the error number field and extract the 207 * error details into the npf_error_t structure. 208 */ 209 static int 210 _npf_extract_error(nvlist_t *resp, npf_error_t *errinfo) 211 { 212 int error; 213 214 error = dnvlist_get_number(resp, "errno", 0); 215 if (error && errinfo) { 216 memset(errinfo, 0, sizeof(npf_error_t)); 217 218 errinfo->id = dnvlist_get_number(resp, "id", 0); 219 errinfo->error_msg = 220 dnvlist_take_string(resp, "error-msg", NULL); 221 errinfo->source_file = 222 dnvlist_take_string(resp, "source-file", NULL); 223 errinfo->source_line = 224 dnvlist_take_number(resp, "source-line", 0); 225 } 226 return error; 227 } 228 229 /* 230 * CONFIGURATION INTERFACE. 231 */ 232 233 nl_config_t * 234 npf_config_create(void) 235 { 236 nl_config_t *ncf; 237 238 ncf = calloc(1, sizeof(nl_config_t)); 239 if (!ncf) { 240 return NULL; 241 } 242 ncf->ncf_dict = nvlist_create(0); 243 nvlist_add_number(ncf->ncf_dict, "version", NPF_VERSION); 244 return ncf; 245 } 246 247 int 248 npf_config_submit(nl_config_t *ncf, int fd, npf_error_t *errinfo) 249 { 250 nvlist_t *errnv = NULL; 251 int error; 252 253 /* Ensure the config is built. */ 254 (void)npf_config_build(ncf); 255 256 if (nvlist_xfer_ioctl(fd, IOC_NPF_LOAD, ncf->ncf_dict, &errnv) == -1) { 257 assert(errnv == NULL); 258 return errno; 259 } 260 error = _npf_extract_error(errnv, errinfo); 261 nvlist_destroy(errnv); 262 return error; 263 } 264 265 nl_config_t * 266 npf_config_retrieve(int fd) 267 { 268 nl_config_t *ncf; 269 270 ncf = calloc(1, sizeof(nl_config_t)); 271 if (!ncf) { 272 return NULL; 273 } 274 if (nvlist_recv_ioctl(fd, IOC_NPF_SAVE, &ncf->ncf_dict) == -1) { 275 free(ncf); 276 return NULL; 277 } 278 return ncf; 279 } 280 281 void * 282 npf_config_export(nl_config_t *ncf, size_t *length) 283 { 284 /* Ensure the config is built. */ 285 (void)npf_config_build(ncf); 286 return nvlist_pack(ncf->ncf_dict, length); 287 } 288 289 nl_config_t * 290 npf_config_import(const void *blob, size_t len) 291 { 292 nl_config_t *ncf; 293 294 ncf = calloc(1, sizeof(nl_config_t)); 295 if (!ncf) { 296 return NULL; 297 } 298 ncf->ncf_dict = nvlist_unpack(blob, len, 0); 299 if (!ncf->ncf_dict) { 300 free(ncf); 301 return NULL; 302 } 303 return ncf; 304 } 305 306 int 307 npf_config_flush(int fd) 308 { 309 nl_config_t *ncf; 310 npf_error_t errinfo; 311 int error; 312 313 ncf = npf_config_create(); 314 if (!ncf) { 315 return ENOMEM; 316 } 317 nvlist_add_bool(ncf->ncf_dict, "flush", true); 318 error = npf_config_submit(ncf, fd, &errinfo); 319 npf_config_destroy(ncf); 320 return error; 321 } 322 323 bool 324 npf_config_active_p(nl_config_t *ncf) 325 { 326 return dnvlist_get_bool(ncf->ncf_dict, "active", false); 327 } 328 329 bool 330 npf_config_loaded_p(nl_config_t *ncf) 331 { 332 return nvlist_exists_nvlist_array(ncf->ncf_dict, "rules"); 333 } 334 335 void * 336 npf_config_build(nl_config_t *ncf) 337 { 338 _npf_rules_process(ncf, ncf->ncf_dict, "__rules"); 339 if (ncf->ncf_rule_list) { 340 /* Set the transformed ruleset. */ 341 nvlist_move_nvlist_array(ncf->ncf_dict, "rules", 342 ncf->ncf_rule_list, ncf->ncf_rule_count); 343 344 /* Clear the temporary list. */ 345 ncf->ncf_rule_list = NULL; 346 ncf->ncf_rule_count = 0; 347 } 348 assert(nvlist_error(ncf->ncf_dict) == 0); 349 return (void *)ncf->ncf_dict; 350 } 351 352 void 353 npf_config_destroy(nl_config_t *ncf) 354 { 355 nvlist_destroy(ncf->ncf_dict); 356 free(ncf); 357 } 358 359 /* 360 * PARAMETERS. 361 */ 362 363 int 364 npf_param_get(nl_config_t *ncf, const char *name, int *valp) 365 { 366 const nvlist_t *params; 367 368 params = dnvlist_get_nvlist(ncf->ncf_dict, "params", NULL); 369 if (params == NULL || !nvlist_exists(params, name)) { 370 return ENOENT; 371 } 372 *valp = (int)dnvlist_get_number(params, name, 0); 373 return 0; 374 } 375 376 int 377 npf_param_set(nl_config_t *ncf, const char *name, int val) 378 { 379 nvlist_t *params; 380 381 /* Ensure params dictionary. */ 382 if (nvlist_exists(ncf->ncf_dict, "params")) { 383 params = nvlist_take_nvlist(ncf->ncf_dict, "params"); 384 } else { 385 params = nvlist_create(0); 386 } 387 388 /* 389 * If the parameter is already set, then free it first. 390 * Set the parameter. Note: values can be negative. 391 */ 392 if (nvlist_exists(params, name)) { 393 nvlist_free_number(params, name); 394 } 395 nvlist_add_number(params, name, (uint64_t)val); 396 nvlist_add_nvlist(ncf->ncf_dict, "params", params); 397 return 0; 398 } 399 400 /* 401 * DYNAMIC RULESET INTERFACE. 402 */ 403 404 static inline bool 405 _npf_nat_ruleset_p(const char *name) 406 { 407 return strncmp(name, NPF_RULESET_MAP_PREF, 408 sizeof(NPF_RULESET_MAP_PREF) - 1) == 0; 409 } 410 411 int 412 npf_ruleset_add(int fd, const char *rname, nl_rule_t *rl, uint64_t *id) 413 { 414 const bool natset = _npf_nat_ruleset_p(rname); 415 nvlist_t *rule_dict = rl->rule_dict; 416 nvlist_t *ret_dict; 417 418 nvlist_add_number(rule_dict, "attr", 419 NPF_RULE_DYNAMIC | nvlist_take_number(rule_dict, "attr")); 420 421 if (natset && !dnvlist_get_bool(rule_dict, "nat-rule", false)) { 422 errno = EINVAL; 423 return errno; 424 } 425 nvlist_add_string(rule_dict, "ruleset-name", rname); 426 nvlist_add_bool(rule_dict, "nat-ruleset", natset); 427 nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_ADD); 428 429 if (nvlist_xfer_ioctl(fd, IOC_NPF_RULE, rule_dict, &ret_dict) == -1) { 430 return errno; 431 } 432 *id = nvlist_get_number(ret_dict, "id"); 433 return 0; 434 } 435 436 int 437 npf_ruleset_remove(int fd, const char *rname, uint64_t id) 438 { 439 const bool natset = _npf_nat_ruleset_p(rname); 440 nvlist_t *rule_dict = nvlist_create(0); 441 442 nvlist_add_string(rule_dict, "ruleset-name", rname); 443 nvlist_add_bool(rule_dict, "nat-ruleset", natset); 444 nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_REMOVE); 445 nvlist_add_number(rule_dict, "id", id); 446 447 if (nvlist_send_ioctl(fd, IOC_NPF_RULE, rule_dict) == -1) { 448 return errno; 449 } 450 return 0; 451 } 452 453 int 454 npf_ruleset_remkey(int fd, const char *rname, const void *key, size_t len) 455 { 456 const bool natset = _npf_nat_ruleset_p(rname); 457 nvlist_t *rule_dict = nvlist_create(0); 458 459 nvlist_add_string(rule_dict, "ruleset-name", rname); 460 nvlist_add_bool(rule_dict, "nat-ruleset", natset); 461 nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_REMKEY); 462 nvlist_add_binary(rule_dict, "key", key, len); 463 464 if (nvlist_send_ioctl(fd, IOC_NPF_RULE, rule_dict) == -1) { 465 return errno; 466 } 467 return 0; 468 } 469 470 int 471 npf_ruleset_flush(int fd, const char *rname) 472 { 473 const bool natset = _npf_nat_ruleset_p(rname); 474 nvlist_t *rule_dict = nvlist_create(0); 475 476 nvlist_add_string(rule_dict, "ruleset-name", rname); 477 nvlist_add_bool(rule_dict, "nat-ruleset", natset); 478 nvlist_add_number(rule_dict, "command", NPF_CMD_RULE_FLUSH); 479 480 if (nvlist_send_ioctl(fd, IOC_NPF_RULE, rule_dict) == -1) { 481 return errno; 482 } 483 return 0; 484 } 485 486 /* 487 * NPF EXTENSION INTERFACE. 488 */ 489 490 nl_ext_t * 491 npf_ext_construct(const char *name) 492 { 493 nl_ext_t *ext; 494 495 ext = malloc(sizeof(*ext)); 496 if (!ext) { 497 return NULL; 498 } 499 ext->ext_dict = nvlist_create(0); 500 nvlist_add_string(ext->ext_dict, "name", name); 501 return ext; 502 } 503 504 void 505 npf_ext_param_u32(nl_ext_t *ext, const char *key, uint32_t val) 506 { 507 nvlist_add_number(ext->ext_dict, key, val); 508 } 509 510 void 511 npf_ext_param_bool(nl_ext_t *ext, const char *key, bool val) 512 { 513 nvlist_add_bool(ext->ext_dict, key, val); 514 } 515 516 void 517 npf_ext_param_string(nl_ext_t *ext, const char *key, const char *val) 518 { 519 nvlist_add_string(ext->ext_dict, key, val); 520 } 521 522 /* 523 * RULE INTERFACE. 524 */ 525 526 nl_rule_t * 527 npf_rule_create(const char *name, uint32_t attr, const char *ifname) 528 { 529 nl_rule_t *rl; 530 531 rl = malloc(sizeof(nl_rule_t)); 532 if (!rl) { 533 return NULL; 534 } 535 rl->rule_dict = nvlist_create(0); 536 nvlist_add_number(rl->rule_dict, "attr", attr); 537 if (name) { 538 nvlist_add_string(rl->rule_dict, "name", name); 539 } 540 if (ifname) { 541 nvlist_add_string(rl->rule_dict, "ifname", ifname); 542 } 543 return rl; 544 } 545 546 int 547 npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t len) 548 { 549 if (type != NPF_CODE_BPF) { 550 return ENOTSUP; 551 } 552 nvlist_add_number(rl->rule_dict, "code-type", (unsigned)type); 553 nvlist_add_binary(rl->rule_dict, "code", code, len); 554 return nvlist_error(rl->rule_dict); 555 } 556 557 int 558 npf_rule_setkey(nl_rule_t *rl, const void *key, size_t len) 559 { 560 nvlist_add_binary(rl->rule_dict, "key", key, len); 561 return nvlist_error(rl->rule_dict); 562 } 563 564 int 565 npf_rule_setinfo(nl_rule_t *rl, const void *info, size_t len) 566 { 567 nvlist_add_binary(rl->rule_dict, "info", info, len); 568 return nvlist_error(rl->rule_dict); 569 } 570 571 int 572 npf_rule_setprio(nl_rule_t *rl, int pri) 573 { 574 nvlist_add_number(rl->rule_dict, "prio", (uint64_t)pri); 575 return nvlist_error(rl->rule_dict); 576 } 577 578 int 579 npf_rule_setproc(nl_rule_t *rl, const char *name) 580 { 581 nvlist_add_string(rl->rule_dict, "rproc", name); 582 return nvlist_error(rl->rule_dict); 583 } 584 585 void * 586 npf_rule_export(nl_rule_t *rl, size_t *length) 587 { 588 return nvlist_pack(rl->rule_dict, length); 589 } 590 591 bool 592 npf_rule_exists_p(nl_config_t *ncf, const char *name) 593 { 594 return _npf_dataset_lookup(ncf->ncf_dict, "rules", "name", name); 595 } 596 597 int 598 npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl) 599 { 600 nvlist_t *rule_dict = rl->rule_dict; 601 nvlist_t *target; 602 const char *key; 603 604 if (parent) { 605 /* Subrule of the parent. */ 606 target = parent->rule_dict; 607 key = "subrules"; 608 } else { 609 /* Global ruleset. */ 610 target = ncf->ncf_dict; 611 key = "__rules"; 612 } 613 nvlist_append_nvlist_array(target, key, rule_dict); 614 nvlist_destroy(rule_dict); 615 free(rl); 616 return 0; 617 } 618 619 static nl_rule_t * 620 _npf_rule_iterate1(nl_config_t *ncf, const char *key, 621 nl_iter_t *iter, unsigned *level) 622 { 623 unsigned i = *iter; 624 const nvlist_t *rule_dict; 625 uint32_t skipto; 626 627 if (i == 0) { 628 /* Initialise the iterator. */ 629 ncf->ncf_nlevel = 0; 630 ncf->ncf_reduce[0] = 0; 631 } 632 633 rule_dict = _npf_dataset_getelement(ncf->ncf_dict, key, i); 634 if (!rule_dict) { 635 *iter = NPF_ITER_BEGIN; 636 return NULL; 637 } 638 *iter = i + 1; // next 639 *level = ncf->ncf_nlevel; 640 641 skipto = dnvlist_get_number(rule_dict, "skip-to", 0); 642 if (skipto) { 643 ncf->ncf_nlevel++; 644 ncf->ncf_reduce[ncf->ncf_nlevel] = skipto; 645 } 646 if (ncf->ncf_reduce[ncf->ncf_nlevel] == (i + 1)) { 647 assert(ncf->ncf_nlevel > 0); 648 ncf->ncf_nlevel--; 649 } 650 651 ncf->ncf_cur_rule.rule_dict = __UNCONST(rule_dict); // XXX 652 return &ncf->ncf_cur_rule; 653 } 654 655 nl_rule_t * 656 npf_rule_iterate(nl_config_t *ncf, nl_iter_t *iter, unsigned *level) 657 { 658 return _npf_rule_iterate1(ncf, "rules", iter, level); 659 } 660 661 const char * 662 npf_rule_getname(nl_rule_t *rl) 663 { 664 return dnvlist_get_string(rl->rule_dict, "name", NULL); 665 } 666 667 uint32_t 668 npf_rule_getattr(nl_rule_t *rl) 669 { 670 return dnvlist_get_number(rl->rule_dict, "attr", 0); 671 } 672 673 const char * 674 npf_rule_getinterface(nl_rule_t *rl) 675 { 676 return dnvlist_get_string(rl->rule_dict, "ifname", NULL); 677 } 678 679 const void * 680 npf_rule_getinfo(nl_rule_t *rl, size_t *len) 681 { 682 return dnvlist_get_binary(rl->rule_dict, "info", len, NULL, 0); 683 } 684 685 const char * 686 npf_rule_getproc(nl_rule_t *rl) 687 { 688 return dnvlist_get_string(rl->rule_dict, "rproc", NULL); 689 } 690 691 uint64_t 692 npf_rule_getid(nl_rule_t *rl) 693 { 694 return dnvlist_get_number(rl->rule_dict, "id", 0); 695 } 696 697 const void * 698 npf_rule_getcode(nl_rule_t *rl, int *type, size_t *len) 699 { 700 *type = (int)dnvlist_get_number(rl->rule_dict, "code-type", 0); 701 return dnvlist_get_binary(rl->rule_dict, "code", len, NULL, 0); 702 } 703 704 int 705 _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf) 706 { 707 const bool natset = _npf_nat_ruleset_p(rname); 708 nvlist_t *req, *ret; 709 710 req = nvlist_create(0); 711 nvlist_add_string(req, "ruleset-name", rname); 712 nvlist_add_bool(req, "nat-ruleset", natset); 713 nvlist_add_number(req, "command", NPF_CMD_RULE_LIST); 714 715 if (nvlist_xfer_ioctl(fd, IOC_NPF_RULE, req, &ret) == -1) { 716 return errno; 717 } 718 if (nvlist_exists_nvlist_array(ret, "rules")) { 719 nvlist_t **rules; 720 size_t n; 721 722 rules = nvlist_take_nvlist_array(ret, "rules", &n); 723 nvlist_move_nvlist_array(ncf->ncf_dict, "rules", rules, n); 724 } 725 nvlist_destroy(ret); 726 return 0; 727 } 728 729 void 730 npf_rule_destroy(nl_rule_t *rl) 731 { 732 nvlist_destroy(rl->rule_dict); 733 free(rl); 734 } 735 736 /* 737 * RULE PROCEDURE INTERFACE. 738 */ 739 740 nl_rproc_t * 741 npf_rproc_create(const char *name) 742 { 743 nl_rproc_t *rp; 744 745 rp = malloc(sizeof(nl_rproc_t)); 746 if (!rp) { 747 return NULL; 748 } 749 rp->rproc_dict = nvlist_create(0); 750 nvlist_add_string(rp->rproc_dict, "name", name); 751 return rp; 752 } 753 754 int 755 npf_rproc_extcall(nl_rproc_t *rp, nl_ext_t *ext) 756 { 757 nvlist_t *rproc_dict = rp->rproc_dict; 758 const char *name = dnvlist_get_string(ext->ext_dict, "name", NULL); 759 760 if (_npf_dataset_lookup(rproc_dict, "extcalls", "name", name)) { 761 return EEXIST; 762 } 763 nvlist_append_nvlist_array(rproc_dict, "extcalls", ext->ext_dict); 764 nvlist_destroy(ext->ext_dict); 765 free(ext); 766 return 0; 767 } 768 769 bool 770 npf_rproc_exists_p(nl_config_t *ncf, const char *name) 771 { 772 return _npf_dataset_lookup(ncf->ncf_dict, "rprocs", "name", name); 773 } 774 775 int 776 npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp) 777 { 778 const char *name; 779 780 name = dnvlist_get_string(rp->rproc_dict, "name", NULL); 781 if (!name) { 782 return EINVAL; 783 } 784 if (npf_rproc_exists_p(ncf, name)) { 785 return EEXIST; 786 } 787 nvlist_append_nvlist_array(ncf->ncf_dict, "rprocs", rp->rproc_dict); 788 nvlist_destroy(rp->rproc_dict); 789 free(rp); 790 return 0; 791 } 792 793 nl_rproc_t * 794 npf_rproc_iterate(nl_config_t *ncf, nl_iter_t *iter) 795 { 796 const nvlist_t *rproc_dict; 797 unsigned i = *iter; 798 799 rproc_dict = _npf_dataset_getelement(ncf->ncf_dict, "rprocs", i); 800 if (!rproc_dict) { 801 *iter = NPF_ITER_BEGIN; 802 return NULL; 803 } 804 *iter = i + 1; // next 805 ncf->ncf_cur_rproc.rproc_dict = __UNCONST(rproc_dict); // XXX 806 return &ncf->ncf_cur_rproc; 807 } 808 809 const char * 810 npf_rproc_getname(nl_rproc_t *rp) 811 { 812 return dnvlist_get_string(rp->rproc_dict, "name", NULL); 813 } 814 815 /* 816 * NAT INTERFACE. 817 */ 818 819 nl_nat_t * 820 npf_nat_create(int type, unsigned flags, const char *ifname) 821 { 822 nl_rule_t *rl; 823 nvlist_t *rule_dict; 824 uint32_t attr; 825 826 attr = NPF_RULE_PASS | NPF_RULE_FINAL | 827 (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN); 828 829 /* Create a rule for NAT policy. Next, will add NAT data. */ 830 rl = npf_rule_create(NULL, attr, ifname); 831 if (!rl) { 832 return NULL; 833 } 834 rule_dict = rl->rule_dict; 835 836 /* Translation type and flags. */ 837 nvlist_add_number(rule_dict, "type", type); 838 nvlist_add_number(rule_dict, "flags", flags); 839 nvlist_add_bool(rule_dict, "nat-rule", true); 840 return (nl_nat_t *)rl; 841 } 842 843 int 844 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt) 845 { 846 nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict); 847 nvlist_destroy(nt->rule_dict); 848 free(nt); 849 return 0; 850 } 851 852 nl_nat_t * 853 npf_nat_iterate(nl_config_t *ncf, nl_iter_t *iter) 854 { 855 unsigned level; 856 return _npf_rule_iterate1(ncf, "nat", iter, &level); 857 } 858 859 int 860 npf_nat_setaddr(nl_nat_t *nt, int af, npf_addr_t *addr, npf_netmask_t mask) 861 { 862 /* Translation IP and mask. */ 863 if (!_npf_add_addr(nt->rule_dict, "nat-addr", af, addr)) { 864 return nvlist_error(nt->rule_dict); 865 } 866 nvlist_add_number(nt->rule_dict, "nat-mask", (uint32_t)mask); 867 return nvlist_error(nt->rule_dict); 868 } 869 870 int 871 npf_nat_setport(nl_nat_t *nt, in_port_t port) 872 { 873 /* Translation port (for redirect case). */ 874 nvlist_add_number(nt->rule_dict, "nat-port", port); 875 return nvlist_error(nt->rule_dict); 876 } 877 878 int 879 npf_nat_settable(nl_nat_t *nt, unsigned tid) 880 { 881 /* 882 * Translation table ID; the address/mask will then serve as a filter. 883 */ 884 nvlist_add_number(nt->rule_dict, "nat-table-id", tid); 885 return nvlist_error(nt->rule_dict); 886 } 887 888 int 889 npf_nat_setalgo(nl_nat_t *nt, unsigned algo) 890 { 891 nvlist_add_number(nt->rule_dict, "nat-algo", algo); 892 return nvlist_error(nt->rule_dict); 893 } 894 895 int 896 npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj) 897 { 898 int error; 899 900 if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) { 901 return error; 902 } 903 nvlist_add_number(nt->rule_dict, "npt66-adj", adj); 904 return nvlist_error(nt->rule_dict); 905 } 906 907 int 908 npf_nat_gettype(nl_nat_t *nt) 909 { 910 return dnvlist_get_number(nt->rule_dict, "type", 0); 911 } 912 913 unsigned 914 npf_nat_getflags(nl_nat_t *nt) 915 { 916 return dnvlist_get_number(nt->rule_dict, "flags", 0); 917 } 918 919 unsigned 920 npf_nat_getalgo(nl_nat_t *nt) 921 { 922 return dnvlist_get_number(nt->rule_dict, "nat-algo", 0); 923 } 924 925 const npf_addr_t * 926 npf_nat_getaddr(nl_nat_t *nt, size_t *alen, npf_netmask_t *mask) 927 { 928 const void *data; 929 930 if (nvlist_exists(nt->rule_dict, "nat-addr")) { 931 data = nvlist_get_binary(nt->rule_dict, "nat-addr", alen); 932 *mask = nvlist_get_number(nt->rule_dict, "nat-mask"); 933 } else { 934 data = NULL; 935 *alen = 0; 936 *mask = NPF_NO_NETMASK; 937 } 938 return data; 939 } 940 941 in_port_t 942 npf_nat_getport(nl_nat_t *nt) 943 { 944 return (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0); 945 } 946 947 unsigned 948 npf_nat_gettable(nl_nat_t *nt) 949 { 950 return dnvlist_get_number(nt->rule_dict, "nat-table-id", 0); 951 } 952 953 /* 954 * TABLE INTERFACE. 955 */ 956 957 nl_table_t * 958 npf_table_create(const char *name, unsigned id, int type) 959 { 960 nl_table_t *tl; 961 962 tl = malloc(sizeof(*tl)); 963 if (!tl) { 964 return NULL; 965 } 966 tl->table_dict = nvlist_create(0); 967 nvlist_add_string(tl->table_dict, "name", name); 968 nvlist_add_number(tl->table_dict, "id", id); 969 nvlist_add_number(tl->table_dict, "type", type); 970 return tl; 971 } 972 973 int 974 npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr, 975 const npf_netmask_t mask) 976 { 977 nvlist_t *entry; 978 979 entry = nvlist_create(0); 980 if (!entry) { 981 return ENOMEM; 982 } 983 if (!_npf_add_addr(entry, "addr", af, addr)) { 984 nvlist_destroy(entry); 985 return EINVAL; 986 } 987 nvlist_add_number(entry, "mask", mask); 988 nvlist_append_nvlist_array(tl->table_dict, "entries", entry); 989 nvlist_destroy(entry); 990 return 0; 991 } 992 993 static inline int 994 _npf_table_build_const(nl_table_t *tl) 995 { 996 struct cdbw *cdbw; 997 const nvlist_t * const *entries; 998 int error = 0, fd = -1; 999 size_t nitems, len; 1000 void *cdb, *buf; 1001 struct stat sb; 1002 char sfn[32]; 1003 1004 if (dnvlist_get_number(tl->table_dict, "type", 0) != NPF_TABLE_CONST) { 1005 return 0; 1006 } 1007 1008 if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) { 1009 return 0; 1010 } 1011 1012 /* 1013 * Create a constant database and put all the entries. 1014 */ 1015 if ((cdbw = cdbw_open()) == NULL) { 1016 return errno; 1017 } 1018 entries = nvlist_get_nvlist_array(tl->table_dict, "entries", &nitems); 1019 for (unsigned i = 0; i < nitems; i++) { 1020 const nvlist_t *entry = entries[i]; 1021 const npf_addr_t *addr; 1022 size_t alen; 1023 1024 addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0); 1025 if (addr == NULL || alen == 0 || alen > sizeof(npf_addr_t)) { 1026 error = EINVAL; 1027 goto out; 1028 } 1029 if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) { 1030 error = errno; 1031 goto out; 1032 } 1033 } 1034 1035 /* 1036 * Write the constant database into a temporary file. 1037 */ 1038 strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn)); 1039 sfn[sizeof(sfn) - 1] = '\0'; 1040 1041 if ((fd = mkstemp(sfn)) == -1) { 1042 error = errno; 1043 goto out; 1044 } 1045 unlink(sfn); 1046 1047 if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) { 1048 error = errno; 1049 goto out; 1050 } 1051 if (fstat(fd, &sb) == -1) { 1052 error = errno; 1053 goto out; 1054 } 1055 len = sb.st_size; 1056 1057 /* 1058 * Memory-map the database and copy it into a buffer. 1059 */ 1060 buf = malloc(len); 1061 if (!buf) { 1062 error = ENOMEM; 1063 goto out; 1064 } 1065 cdb = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); 1066 if (cdb == MAP_FAILED) { 1067 error = errno; 1068 free(buf); 1069 goto out; 1070 } 1071 munmap(cdb, len); 1072 1073 /* 1074 * Move the data buffer to the nvlist. 1075 */ 1076 nvlist_move_binary(tl->table_dict, "data", buf, len); 1077 error = nvlist_error(tl->table_dict); 1078 out: 1079 if (fd != -1) { 1080 close(fd); 1081 } 1082 cdbw_close(cdbw); 1083 return error; 1084 } 1085 1086 int 1087 npf_table_insert(nl_config_t *ncf, nl_table_t *tl) 1088 { 1089 const char *name; 1090 int error; 1091 1092 name = dnvlist_get_string(tl->table_dict, "name", NULL); 1093 if (!name) { 1094 return EINVAL; 1095 } 1096 if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) { 1097 return EEXIST; 1098 } 1099 if ((error = _npf_table_build_const(tl)) != 0) { 1100 return error; 1101 } 1102 nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict); 1103 nvlist_destroy(tl->table_dict); 1104 free(tl); 1105 return 0; 1106 } 1107 1108 int 1109 npf_table_replace(int fd, nl_table_t *tl, npf_error_t *errinfo) 1110 { 1111 nvlist_t *errnv = NULL; 1112 int error; 1113 1114 /* Ensure const tables are built. */ 1115 if ((error = _npf_table_build_const(tl)) != 0) { 1116 return error; 1117 } 1118 1119 if (nvlist_xfer_ioctl(fd, IOC_NPF_TABLE_REPLACE, 1120 tl->table_dict, &errnv) == -1) { 1121 assert(errnv == NULL); 1122 return errno; 1123 } 1124 error = _npf_extract_error(errnv, errinfo); 1125 nvlist_destroy(errnv); 1126 return error; 1127 } 1128 1129 nl_table_t * 1130 npf_table_iterate(nl_config_t *ncf, nl_iter_t *iter) 1131 { 1132 const nvlist_t *table_dict; 1133 unsigned i = *iter; 1134 1135 table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i); 1136 if (!table_dict) { 1137 *iter = NPF_ITER_BEGIN; 1138 return NULL; 1139 } 1140 *iter = i + 1; // next 1141 ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX 1142 return &ncf->ncf_cur_table; 1143 } 1144 1145 unsigned 1146 npf_table_getid(nl_table_t *tl) 1147 { 1148 return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1); 1149 } 1150 1151 const char * 1152 npf_table_getname(nl_table_t *tl) 1153 { 1154 return dnvlist_get_string(tl->table_dict, "name", NULL); 1155 } 1156 1157 int 1158 npf_table_gettype(nl_table_t *tl) 1159 { 1160 return dnvlist_get_number(tl->table_dict, "type", 0); 1161 } 1162 1163 void 1164 npf_table_destroy(nl_table_t *tl) 1165 { 1166 nvlist_destroy(tl->table_dict); 1167 free(tl); 1168 } 1169 1170 /* 1171 * ALG INTERFACE. 1172 */ 1173 1174 int 1175 npf_alg_load(nl_config_t *ncf, const char *name) 1176 { 1177 nvlist_t *alg_dict; 1178 1179 if (_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) { 1180 return EEXIST; 1181 } 1182 alg_dict = nvlist_create(0); 1183 nvlist_add_string(alg_dict, "name", name); 1184 nvlist_append_nvlist_array(ncf->ncf_dict, "algs", alg_dict); 1185 nvlist_destroy(alg_dict); 1186 return 0; 1187 } 1188 1189 /* 1190 * CONNECTION / NAT ENTRY INTERFACE. 1191 */ 1192 1193 int 1194 npf_nat_lookup(int fd, int af, npf_addr_t *addr[2], in_port_t port[2], 1195 int proto, int dir) 1196 { 1197 nvlist_t *req = NULL, *conn_res; 1198 const nvlist_t *nat; 1199 int error = EINVAL; 1200 1201 /* 1202 * Setup the connection lookup key. 1203 */ 1204 conn_res = nvlist_create(0); 1205 if (!conn_res) { 1206 return ENOMEM; 1207 } 1208 if (!_npf_add_addr(conn_res, "saddr", af, addr[0])) 1209 goto out; 1210 if (!_npf_add_addr(conn_res, "daddr", af, addr[1])) 1211 goto out; 1212 nvlist_add_number(conn_res, "sport", port[0]); 1213 nvlist_add_number(conn_res, "dport", port[1]); 1214 nvlist_add_number(conn_res, "proto", proto); 1215 1216 /* 1217 * Setup the request. 1218 */ 1219 req = nvlist_create(0); 1220 if (!req) { 1221 error = ENOMEM; 1222 goto out; 1223 } 1224 nvlist_add_number(req, "direction", dir); 1225 nvlist_move_nvlist(req, "key", conn_res); 1226 conn_res = NULL; 1227 1228 /* Lookup: retrieve the connection entry. */ 1229 if (nvlist_xfer_ioctl(fd, IOC_NPF_CONN_LOOKUP, req, &conn_res) == -1) { 1230 error = errno; 1231 goto out; 1232 } 1233 1234 /* 1235 * Get the NAT entry and extract the translated pair. 1236 */ 1237 nat = dnvlist_get_nvlist(conn_res, "nat", NULL); 1238 if (!nat) { 1239 errno = ENOENT; 1240 goto out; 1241 } 1242 if (!_npf_get_addr(nat, "oaddr", addr[0])) { 1243 error = EINVAL; 1244 goto out; 1245 } 1246 port[0] = nvlist_get_number(nat, "oport"); 1247 port[1] = nvlist_get_number(nat, "tport"); 1248 out: 1249 if (conn_res) { 1250 nvlist_destroy(conn_res); 1251 } 1252 if (req) { 1253 nvlist_destroy(req); 1254 } 1255 return error; 1256 } 1257 1258 typedef struct { 1259 npf_addr_t addr[2]; 1260 in_port_t port[2]; 1261 uint16_t alen; 1262 uint16_t proto; 1263 } npf_endpoint_t; 1264 1265 static bool 1266 npf_endpoint_load(const nvlist_t *conn, const char *name, npf_endpoint_t *ep) 1267 { 1268 const nvlist_t *ed = dnvlist_get_nvlist(conn, name, NULL); 1269 1270 if (!ed) 1271 return false; 1272 if (!(ep->alen = _npf_get_addr(ed, "saddr", &ep->addr[0]))) 1273 return false; 1274 if (ep->alen != _npf_get_addr(ed, "daddr", &ep->addr[1])) 1275 return false; 1276 ep->port[0] = nvlist_get_number(ed, "sport"); 1277 ep->port[1] = nvlist_get_number(ed, "dport"); 1278 ep->proto = nvlist_get_number(ed, "proto"); 1279 return true; 1280 } 1281 1282 static void 1283 npf_conn_handle(const nvlist_t *conn, npf_conn_func_t func, void *arg) 1284 { 1285 const nvlist_t *nat; 1286 npf_endpoint_t ep; 1287 uint16_t tport; 1288 const char *ifname; 1289 1290 ifname = dnvlist_get_string(conn, "ifname", NULL); 1291 if (!ifname) 1292 goto err; 1293 1294 if ((nat = dnvlist_get_nvlist(conn, "nat", NULL)) != NULL) { 1295 tport = nvlist_get_number(nat, "tport"); 1296 } else { 1297 tport = 0; 1298 } 1299 if (!npf_endpoint_load(conn, "forw-key", &ep)) { 1300 goto err; 1301 } 1302 1303 in_port_t p[] = { 1304 ntohs(ep.port[0]), 1305 ntohs(ep.port[1]), 1306 ntohs(tport) 1307 }; 1308 (*func)((unsigned)ep.alen, ep.addr, p, ifname, arg); 1309 err: 1310 return; 1311 } 1312 1313 int 1314 npf_conn_list(int fd, npf_conn_func_t func, void *arg) 1315 { 1316 nl_config_t *ncf; 1317 const nvlist_t * const *conns; 1318 size_t nitems; 1319 1320 ncf = npf_config_retrieve(fd); 1321 if (!ncf) { 1322 return errno; 1323 } 1324 if (!nvlist_exists_nvlist_array(ncf->ncf_dict, "conn-list")) { 1325 return 0; 1326 } 1327 conns = nvlist_get_nvlist_array(ncf->ncf_dict, "conn-list", &nitems); 1328 for (unsigned i = 0; i < nitems; i++) { 1329 const nvlist_t *conn = conns[i]; 1330 npf_conn_handle(conn, func, arg); 1331 } 1332 return 0; 1333 } 1334 1335 /* 1336 * MISC. 1337 */ 1338 1339 void 1340 _npf_debug_addif(nl_config_t *ncf, const char *ifname) 1341 { 1342 nvlist_t *debug; 1343 1344 /* 1345 * Initialise the debug dictionary on the first call. 1346 */ 1347 debug = dnvlist_take_nvlist(ncf->ncf_dict, "debug", NULL); 1348 if (debug == NULL) { 1349 debug = nvlist_create(0); 1350 } 1351 if (!_npf_dataset_lookup(debug, "interfaces", "name", ifname)) { 1352 nvlist_t *ifdict = nvlist_create(0); 1353 nvlist_add_string(ifdict, "name", ifname); 1354 nvlist_add_number(ifdict, "index", if_nametoindex(ifname)); 1355 nvlist_append_nvlist_array(debug, "interfaces", ifdict); 1356 nvlist_destroy(ifdict); 1357 } 1358 nvlist_move_nvlist(ncf->ncf_dict, "debug", debug); 1359 } 1360 1361 void 1362 _npf_config_dump(nl_config_t *ncf, int fd) 1363 { 1364 (void)npf_config_build(ncf); 1365 nvlist_dump(ncf->ncf_dict, fd); 1366 } 1367