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