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.44 2018/09/29 14:41:36 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 int af, npf_addr_t *addr, npf_netmask_t mask, in_port_t port) 741 { 742 nl_rule_t *rl; 743 nvlist_t *rule_dict; 744 uint32_t attr; 745 746 attr = NPF_RULE_PASS | NPF_RULE_FINAL | 747 (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN); 748 749 /* Create a rule for NAT policy. Next, will add NAT data. */ 750 rl = npf_rule_create(NULL, attr, ifname); 751 if (!rl) { 752 return NULL; 753 } 754 rule_dict = rl->rule_dict; 755 756 /* Translation type and flags. */ 757 nvlist_add_number(rule_dict, "type", type); 758 nvlist_add_number(rule_dict, "flags", flags); 759 760 /* Translation IP and mask. */ 761 if (!_npf_add_addr(rule_dict, "nat-ip", af, addr)) { 762 npf_rule_destroy(rl); 763 return NULL; 764 } 765 nvlist_add_number(rule_dict, "nat-mask", (uint32_t)mask); 766 767 /* Translation port (for redirect case). */ 768 nvlist_add_number(rule_dict, "nat-port", port); 769 770 return (nl_nat_t *)rl; 771 } 772 773 int 774 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt, int pri __unused) 775 { 776 nvlist_add_number(nt->rule_dict, "prio", (uint64_t)NPF_PRI_LAST); 777 nvlist_append_nvlist_array(ncf->ncf_dict, "nat", nt->rule_dict); 778 nvlist_destroy(nt->rule_dict); 779 free(nt); 780 return 0; 781 } 782 783 nl_nat_t * 784 npf_nat_iterate(nl_config_t *ncf) 785 { 786 unsigned level; 787 return _npf_rule_iterate1(ncf, "nat", &level); 788 } 789 790 int 791 npf_nat_setalgo(nl_nat_t *nt, unsigned algo) 792 { 793 nvlist_add_number(nt->rule_dict, "nat-algo", algo); 794 return nvlist_error(nt->rule_dict); 795 } 796 797 int 798 npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj) 799 { 800 int error; 801 802 if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) { 803 return error; 804 } 805 nvlist_add_number(nt->rule_dict, "npt66-adj", adj); 806 return nvlist_error(nt->rule_dict); 807 } 808 809 int 810 npf_nat_gettype(nl_nat_t *nt) 811 { 812 return dnvlist_get_number(nt->rule_dict, "type", 0); 813 } 814 815 unsigned 816 npf_nat_getflags(nl_nat_t *nt) 817 { 818 return dnvlist_get_number(nt->rule_dict, "flags", 0); 819 } 820 821 void 822 npf_nat_getmap(nl_nat_t *nt, npf_addr_t *addr, size_t *alen, in_port_t *port) 823 { 824 const void *data = nvlist_get_binary(nt->rule_dict, "nat-ip", alen); 825 memcpy(addr, data, *alen); 826 *port = (uint16_t)dnvlist_get_number(nt->rule_dict, "nat-port", 0); 827 } 828 829 /* 830 * TABLE INTERFACE. 831 */ 832 833 nl_table_t * 834 npf_table_create(const char *name, unsigned id, int type) 835 { 836 nl_table_t *tl; 837 838 tl = malloc(sizeof(*tl)); 839 if (!tl) { 840 return NULL; 841 } 842 tl->table_dict = nvlist_create(0); 843 nvlist_add_string(tl->table_dict, "name", name); 844 nvlist_add_number(tl->table_dict, "id", id); 845 nvlist_add_number(tl->table_dict, "type", type); 846 return tl; 847 } 848 849 int 850 npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr, 851 const npf_netmask_t mask) 852 { 853 nvlist_t *entry; 854 855 /* Create the table entry. */ 856 entry = nvlist_create(0); 857 if (!entry) { 858 return ENOMEM; 859 } 860 if (!_npf_add_addr(entry, "addr", af, addr)) { 861 nvlist_destroy(entry); 862 return EINVAL; 863 } 864 nvlist_add_number(entry, "mask", mask); 865 nvlist_append_nvlist_array(tl->table_dict, "entries", entry); 866 nvlist_destroy(entry); 867 return 0; 868 } 869 870 static inline int 871 _npf_table_build(nl_table_t *tl) 872 { 873 struct cdbw *cdbw; 874 const nvlist_t * const *entries; 875 int error = 0, fd = -1; 876 size_t nitems, len; 877 void *cdb, *buf; 878 struct stat sb; 879 char sfn[32]; 880 881 if (!nvlist_exists_nvlist_array(tl->table_dict, "entries")) { 882 return 0; 883 } 884 885 /* 886 * Create a constant database and put all the entries. 887 */ 888 if ((cdbw = cdbw_open()) == NULL) { 889 return errno; 890 } 891 entries = nvlist_get_nvlist_array(tl->table_dict, "entries", &nitems); 892 for (unsigned i = 0; i < nitems; i++) { 893 const nvlist_t *entry = entries[i]; 894 const npf_addr_t *addr; 895 size_t alen; 896 897 addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0); 898 if (addr == NULL || alen == 0 || alen > sizeof(npf_addr_t)) { 899 error = EINVAL; 900 goto out; 901 } 902 if (cdbw_put(cdbw, addr, alen, addr, alen) == -1) { 903 error = errno; 904 goto out; 905 } 906 } 907 908 /* 909 * Produce the constant database into a temporary file. 910 */ 911 strncpy(sfn, "/tmp/npfcdb.XXXXXX", sizeof(sfn)); 912 sfn[sizeof(sfn) - 1] = '\0'; 913 914 if ((fd = mkstemp(sfn)) == -1) { 915 error = errno; 916 goto out; 917 } 918 unlink(sfn); 919 920 if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) { 921 error = errno; 922 goto out; 923 } 924 if (fstat(fd, &sb) == -1) { 925 error = errno; 926 goto out; 927 } 928 len = sb.st_size; 929 930 /* 931 * Memory-map the database and copy it into a buffer. 932 */ 933 buf = malloc(len); 934 if (!buf) { 935 error = ENOMEM; 936 goto out; 937 } 938 cdb = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); 939 if (cdb == MAP_FAILED) { 940 error = errno; 941 free(buf); 942 goto out; 943 } 944 munmap(cdb, len); 945 946 /* 947 * Move the data buffer to the nvlist. 948 */ 949 nvlist_move_binary(tl->table_dict, "data", buf, len); 950 error = nvlist_error(tl->table_dict); 951 out: 952 if (fd != -1) { 953 close(fd); 954 } 955 cdbw_close(cdbw); 956 return error; 957 } 958 959 int 960 npf_table_insert(nl_config_t *ncf, nl_table_t *tl) 961 { 962 const char *name; 963 int error; 964 965 name = dnvlist_get_string(tl->table_dict, "name", NULL); 966 if (!name) { 967 return EINVAL; 968 } 969 if (_npf_dataset_lookup(ncf->ncf_dict, "tables", "name", name)) { 970 return EEXIST; 971 } 972 if (dnvlist_get_number(tl->table_dict, "type", 0) == NPF_TABLE_CDB) { 973 if ((error = _npf_table_build(tl)) != 0) { 974 return error; 975 } 976 } 977 nvlist_append_nvlist_array(ncf->ncf_dict, "tables", tl->table_dict); 978 nvlist_destroy(tl->table_dict); 979 free(tl); 980 return 0; 981 } 982 983 nl_table_t * 984 npf_table_iterate(nl_config_t *ncf) 985 { 986 const nvlist_t *table_dict; 987 unsigned i = ncf->ncf_table_iter++; 988 989 table_dict = _npf_dataset_getelement(ncf->ncf_dict, "tables", i); 990 if (!table_dict) { 991 /* Reset the iterator. */ 992 ncf->ncf_table_iter = 0; 993 return NULL; 994 } 995 ncf->ncf_cur_table.table_dict = __UNCONST(table_dict); // XXX 996 return &ncf->ncf_cur_table; 997 } 998 999 unsigned 1000 npf_table_getid(nl_table_t *tl) 1001 { 1002 return dnvlist_get_number(tl->table_dict, "id", (unsigned)-1); 1003 } 1004 1005 const char * 1006 npf_table_getname(nl_table_t *tl) 1007 { 1008 return dnvlist_get_string(tl->table_dict, "name", NULL); 1009 } 1010 1011 int 1012 npf_table_gettype(nl_table_t *tl) 1013 { 1014 return dnvlist_get_number(tl->table_dict, "type", 0); 1015 } 1016 1017 void 1018 npf_table_destroy(nl_table_t *tl) 1019 { 1020 nvlist_destroy(tl->table_dict); 1021 free(tl); 1022 } 1023 1024 /* 1025 * ALG INTERFACE. 1026 */ 1027 1028 int 1029 _npf_alg_load(nl_config_t *ncf, const char *name) 1030 { 1031 nvlist_t *alg_dict; 1032 1033 if (_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) { 1034 return EEXIST; 1035 } 1036 alg_dict = nvlist_create(0); 1037 nvlist_add_string(alg_dict, "name", name); 1038 nvlist_append_nvlist_array(ncf->ncf_dict, "algs", alg_dict); 1039 nvlist_destroy(alg_dict); 1040 return 0; 1041 } 1042 1043 int 1044 _npf_alg_unload(nl_config_t *ncf, const char *name) 1045 { 1046 if (!_npf_dataset_lookup(ncf->ncf_dict, "algs", "name", name)) { 1047 return ENOENT; 1048 } 1049 return ENOTSUP; 1050 } 1051 1052 /* 1053 * CONNECTION / NAT ENTRY INTERFACE. 1054 */ 1055 1056 int 1057 npf_nat_lookup(int fd, int af, npf_addr_t *addr[2], in_port_t port[2], 1058 int proto, int dir) 1059 { 1060 nvlist_t *req = NULL, *conn_res; 1061 const nvlist_t *nat; 1062 int error = EINVAL; 1063 1064 /* 1065 * Setup the connection lookup key. 1066 */ 1067 conn_res = nvlist_create(0); 1068 if (!conn_res) { 1069 return ENOMEM; 1070 } 1071 if (!_npf_add_addr(conn_res, "saddr", af, addr[0])) 1072 goto out; 1073 if (!_npf_add_addr(conn_res, "daddr", af, addr[1])) 1074 goto out; 1075 nvlist_add_number(conn_res, "sport", port[0]); 1076 nvlist_add_number(conn_res, "dport", port[1]); 1077 nvlist_add_number(conn_res, "proto", proto); 1078 1079 /* 1080 * Setup the request. 1081 */ 1082 req = nvlist_create(0); 1083 if (!req) { 1084 error = ENOMEM; 1085 goto out; 1086 } 1087 nvlist_add_number(req, "direction", dir); 1088 nvlist_move_nvlist(req, "key", conn_res); 1089 conn_res = NULL; 1090 1091 /* Lookup: retrieve the connection entry. */ 1092 if (nvlist_xfer_ioctl(fd, IOC_NPF_CONN_LOOKUP, req, &conn_res) == -1) { 1093 error = errno; 1094 goto out; 1095 } 1096 1097 /* 1098 * Get the NAT entry and extract the translated pair. 1099 */ 1100 nat = dnvlist_get_nvlist(conn_res, "nat", NULL); 1101 if (!nat) { 1102 errno = ENOENT; 1103 goto out; 1104 } 1105 if (!_npf_get_addr(nat, "oaddr", addr[0])) { 1106 error = EINVAL; 1107 goto out; 1108 } 1109 port[0] = nvlist_get_number(nat, "oport"); 1110 port[1] = nvlist_get_number(nat, "tport"); 1111 out: 1112 if (conn_res) { 1113 nvlist_destroy(conn_res); 1114 } 1115 if (req) { 1116 nvlist_destroy(req); 1117 } 1118 return error; 1119 } 1120 1121 typedef struct { 1122 npf_addr_t addr[2]; 1123 in_port_t port[2]; 1124 uint16_t alen; 1125 uint16_t proto; 1126 } npf_endpoint_t; 1127 1128 static bool 1129 npf_endpoint_load(const nvlist_t *conn, const char *name, npf_endpoint_t *ep) 1130 { 1131 const nvlist_t *ed = dnvlist_get_nvlist(conn, name, NULL); 1132 1133 if (!ed) 1134 return false; 1135 if (!(ep->alen = _npf_get_addr(ed, "saddr", &ep->addr[0]))) 1136 return false; 1137 if (ep->alen != _npf_get_addr(ed, "daddr", &ep->addr[1])) 1138 return false; 1139 ep->port[0] = nvlist_get_number(ed, "sport"); 1140 ep->port[1] = nvlist_get_number(ed, "dport"); 1141 ep->proto = nvlist_get_number(ed, "proto"); 1142 return true; 1143 } 1144 1145 static void 1146 npf_conn_handle(const nvlist_t *conn, npf_conn_func_t func, void *arg) 1147 { 1148 const nvlist_t *nat; 1149 npf_endpoint_t ep; 1150 uint16_t tport; 1151 const char *ifname; 1152 1153 ifname = dnvlist_get_string(conn, "ifname", NULL); 1154 if (!ifname) 1155 goto err; 1156 1157 if ((nat = dnvlist_get_nvlist(conn, "nat", NULL)) != NULL) { 1158 tport = nvlist_get_number(nat, "tport"); 1159 } else { 1160 tport = 0; 1161 } 1162 if (!npf_endpoint_load(conn, "forw-key", &ep)) { 1163 goto err; 1164 } 1165 1166 in_port_t p[] = { 1167 ntohs(ep.port[0]), 1168 ntohs(ep.port[1]), 1169 ntohs(tport) 1170 }; 1171 (*func)((unsigned)ep.alen, ep.addr, p, ifname, arg); 1172 err: 1173 return; 1174 } 1175 1176 int 1177 npf_conn_list(int fd, npf_conn_func_t func, void *arg) 1178 { 1179 nl_config_t *ncf; 1180 const nvlist_t * const *conns; 1181 size_t nitems; 1182 1183 ncf = npf_config_retrieve(fd); 1184 if (!ncf) { 1185 return errno; 1186 } 1187 if (!nvlist_exists_nvlist_array(ncf->ncf_dict, "conn-list")) { 1188 return 0; 1189 } 1190 conns = nvlist_get_nvlist_array(ncf->ncf_dict, "conn-list", &nitems); 1191 for (unsigned i = 0; i < nitems; i++) { 1192 const nvlist_t *conn = conns[i]; 1193 npf_conn_handle(conn, func, arg); 1194 } 1195 return 0; 1196 } 1197 1198 /* 1199 * MISC. 1200 */ 1201 1202 void 1203 _npf_debug_addif(nl_config_t *ncf, const char *ifname) 1204 { 1205 nvlist_t *debug; 1206 1207 /* 1208 * Initialise the debug dictionary on the first call. 1209 */ 1210 debug = dnvlist_take_nvlist(ncf->ncf_dict, "debug", NULL); 1211 if (debug == NULL) { 1212 debug = nvlist_create(0); 1213 } 1214 if (!_npf_dataset_lookup(debug, "interfaces", "name", ifname)) { 1215 nvlist_t *ifdict = nvlist_create(0); 1216 nvlist_add_string(ifdict, "name", ifname); 1217 nvlist_add_number(ifdict, "index", if_nametoindex(ifname)); 1218 nvlist_append_nvlist_array(debug, "interfaces", ifdict); 1219 nvlist_destroy(ifdict); 1220 } 1221 nvlist_move_nvlist(ncf->ncf_dict, "debug", debug); 1222 } 1223 1224 void 1225 _npf_config_dump(nl_config_t *ncf, int fd) 1226 { 1227 (void)npf_config_build(ncf); 1228 nvlist_dump(ncf->ncf_dict, fd); 1229 } 1230