1 /*- 2 * Copyright (c) 2009-2015 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 /* 31 * NPF ruleset module. 32 */ 33 34 #ifdef _KERNEL 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.47 2018/09/29 14:41:36 rmind Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/types.h> 40 41 #include <sys/atomic.h> 42 #include <sys/kmem.h> 43 #include <sys/queue.h> 44 #include <sys/mbuf.h> 45 #include <sys/types.h> 46 47 #include <net/bpf.h> 48 #include <net/bpfjit.h> 49 #include <net/pfil.h> 50 #include <net/if.h> 51 #endif 52 53 #include "npf_impl.h" 54 55 struct npf_ruleset { 56 /* 57 * - List of all rules. 58 * - Dynamic (i.e. named) rules. 59 * - G/C list for convenience. 60 */ 61 LIST_HEAD(, npf_rule) rs_all; 62 LIST_HEAD(, npf_rule) rs_dynamic; 63 LIST_HEAD(, npf_rule) rs_gc; 64 65 /* Unique ID counter. */ 66 uint64_t rs_idcnt; 67 68 /* Number of array slots and active rules. */ 69 u_int rs_slots; 70 u_int rs_nitems; 71 72 /* Array of ordered rules. */ 73 npf_rule_t * rs_rules[]; 74 }; 75 76 struct npf_rule { 77 /* Attributes, interface and skip slot. */ 78 uint32_t r_attr; 79 u_int r_ifid; 80 u_int r_skip_to; 81 82 /* Code to process, if any. */ 83 int r_type; 84 bpfjit_func_t r_jcode; 85 void * r_code; 86 u_int r_clen; 87 88 /* NAT policy (optional), rule procedure and subset. */ 89 npf_natpolicy_t * r_natp; 90 npf_rproc_t * r_rproc; 91 92 union { 93 /* 94 * Dynamic group: rule subset and a group list entry. 95 */ 96 struct { 97 npf_rule_t * r_subset; 98 LIST_ENTRY(npf_rule) r_dentry; 99 }; 100 101 /* 102 * Dynamic rule: priority, parent group and next rule. 103 */ 104 struct { 105 int r_priority; 106 npf_rule_t * r_parent; 107 npf_rule_t * r_next; 108 }; 109 }; 110 111 /* Rule ID, name and the optional key. */ 112 uint64_t r_id; 113 char r_name[NPF_RULE_MAXNAMELEN]; 114 uint8_t r_key[NPF_RULE_MAXKEYLEN]; 115 116 /* All-list entry and the auxiliary info. */ 117 LIST_ENTRY(npf_rule) r_aentry; 118 nvlist_t * r_info; 119 size_t r_info_len; 120 }; 121 122 #define SKIPTO_ADJ_FLAG (1U << 31) 123 #define SKIPTO_MASK (SKIPTO_ADJ_FLAG - 1) 124 125 static nvlist_t * npf_rule_export(npf_t *, const npf_rule_t *); 126 127 /* 128 * Private attributes - must be in the NPF_RULE_PRIVMASK range. 129 */ 130 #define NPF_RULE_KEEPNAT (0x01000000 & NPF_RULE_PRIVMASK) 131 132 #define NPF_DYNAMIC_GROUP_P(attr) \ 133 (((attr) & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP) 134 135 #define NPF_DYNAMIC_RULE_P(attr) \ 136 (((attr) & NPF_DYNAMIC_GROUP) == NPF_RULE_DYNAMIC) 137 138 npf_ruleset_t * 139 npf_ruleset_create(size_t slots) 140 { 141 size_t len = offsetof(npf_ruleset_t, rs_rules[slots]); 142 npf_ruleset_t *rlset; 143 144 rlset = kmem_zalloc(len, KM_SLEEP); 145 LIST_INIT(&rlset->rs_dynamic); 146 LIST_INIT(&rlset->rs_all); 147 LIST_INIT(&rlset->rs_gc); 148 rlset->rs_slots = slots; 149 150 return rlset; 151 } 152 153 void 154 npf_ruleset_destroy(npf_ruleset_t *rlset) 155 { 156 size_t len = offsetof(npf_ruleset_t, rs_rules[rlset->rs_slots]); 157 npf_rule_t *rl; 158 159 while ((rl = LIST_FIRST(&rlset->rs_all)) != NULL) { 160 if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) { 161 /* 162 * Note: r_subset may point to the rules which 163 * were inherited by a new ruleset. 164 */ 165 rl->r_subset = NULL; 166 LIST_REMOVE(rl, r_dentry); 167 } 168 if (NPF_DYNAMIC_RULE_P(rl->r_attr)) { 169 /* Not removing from r_subset, see above. */ 170 KASSERT(rl->r_parent != NULL); 171 } 172 LIST_REMOVE(rl, r_aentry); 173 npf_rule_free(rl); 174 } 175 KASSERT(LIST_EMPTY(&rlset->rs_dynamic)); 176 177 npf_ruleset_gc(rlset); 178 KASSERT(LIST_EMPTY(&rlset->rs_gc)); 179 kmem_free(rlset, len); 180 } 181 182 /* 183 * npf_ruleset_insert: insert the rule into the specified ruleset. 184 */ 185 void 186 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl) 187 { 188 u_int n = rlset->rs_nitems; 189 190 KASSERT(n < rlset->rs_slots); 191 192 LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry); 193 if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) { 194 LIST_INSERT_HEAD(&rlset->rs_dynamic, rl, r_dentry); 195 } else { 196 KASSERTMSG(rl->r_parent == NULL, "cannot be dynamic rule"); 197 rl->r_attr &= ~NPF_RULE_DYNAMIC; 198 } 199 200 rlset->rs_rules[n] = rl; 201 rlset->rs_nitems++; 202 rl->r_id = ++rlset->rs_idcnt; 203 204 if (rl->r_skip_to < ++n) { 205 rl->r_skip_to = SKIPTO_ADJ_FLAG | n; 206 } 207 } 208 209 npf_rule_t * 210 npf_ruleset_lookup(npf_ruleset_t *rlset, const char *name) 211 { 212 npf_rule_t *rl; 213 214 LIST_FOREACH(rl, &rlset->rs_dynamic, r_dentry) { 215 KASSERT(NPF_DYNAMIC_GROUP_P(rl->r_attr)); 216 if (strncmp(rl->r_name, name, NPF_RULE_MAXNAMELEN) == 0) 217 break; 218 } 219 return rl; 220 } 221 222 /* 223 * npf_ruleset_add: insert dynamic rule into the (active) ruleset. 224 */ 225 int 226 npf_ruleset_add(npf_ruleset_t *rlset, const char *rname, npf_rule_t *rl) 227 { 228 npf_rule_t *rg, *it, *target; 229 int priocmd; 230 231 if (!NPF_DYNAMIC_RULE_P(rl->r_attr)) { 232 return EINVAL; 233 } 234 rg = npf_ruleset_lookup(rlset, rname); 235 if (rg == NULL) { 236 return ESRCH; 237 } 238 239 /* Dynamic rule - assign a unique ID and save the parent. */ 240 rl->r_id = ++rlset->rs_idcnt; 241 rl->r_parent = rg; 242 243 /* 244 * Rule priority: (highest) 1, 2 ... n (lowest). 245 * Negative priority indicates an operation and is reset to zero. 246 */ 247 if ((priocmd = rl->r_priority) < 0) { 248 rl->r_priority = 0; 249 } 250 251 /* 252 * WARNING: once rg->subset or target->r_next of an *active* 253 * rule is set, then our rule becomes globally visible and active. 254 * Must issue a load fence to ensure rl->r_next visibility first. 255 */ 256 switch (priocmd) { 257 case NPF_PRI_LAST: 258 default: 259 target = NULL; 260 it = rg->r_subset; 261 while (it && it->r_priority <= rl->r_priority) { 262 target = it; 263 it = it->r_next; 264 } 265 if (target) { 266 rl->r_next = target->r_next; 267 membar_producer(); 268 target->r_next = rl; 269 break; 270 } 271 /* FALLTHROUGH */ 272 273 case NPF_PRI_FIRST: 274 rl->r_next = rg->r_subset; 275 membar_producer(); 276 rg->r_subset = rl; 277 break; 278 } 279 280 /* Finally, add into the all-list. */ 281 LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry); 282 return 0; 283 } 284 285 static void 286 npf_ruleset_unlink(npf_rule_t *rl, npf_rule_t *prev) 287 { 288 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 289 if (prev) { 290 prev->r_next = rl->r_next; 291 } else { 292 npf_rule_t *rg = rl->r_parent; 293 rg->r_subset = rl->r_next; 294 } 295 LIST_REMOVE(rl, r_aentry); 296 } 297 298 /* 299 * npf_ruleset_remove: remove the dynamic rule given the rule ID. 300 */ 301 int 302 npf_ruleset_remove(npf_ruleset_t *rlset, const char *rname, uint64_t id) 303 { 304 npf_rule_t *rg, *prev = NULL; 305 306 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 307 return ESRCH; 308 } 309 for (npf_rule_t *rl = rg->r_subset; rl; rl = rl->r_next) { 310 KASSERT(rl->r_parent == rg); 311 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 312 313 /* Compare ID. On match, remove and return. */ 314 if (rl->r_id == id) { 315 npf_ruleset_unlink(rl, prev); 316 LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry); 317 return 0; 318 } 319 prev = rl; 320 } 321 return ENOENT; 322 } 323 324 /* 325 * npf_ruleset_remkey: remove the dynamic rule given the rule key. 326 */ 327 int 328 npf_ruleset_remkey(npf_ruleset_t *rlset, const char *rname, 329 const void *key, size_t len) 330 { 331 npf_rule_t *rg, *rlast = NULL, *prev = NULL, *lastprev = NULL; 332 333 KASSERT(len && len <= NPF_RULE_MAXKEYLEN); 334 335 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 336 return ESRCH; 337 } 338 339 /* Compare the key and find the last in the list. */ 340 for (npf_rule_t *rl = rg->r_subset; rl; rl = rl->r_next) { 341 KASSERT(rl->r_parent == rg); 342 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 343 if (memcmp(rl->r_key, key, len) == 0) { 344 lastprev = prev; 345 rlast = rl; 346 } 347 prev = rl; 348 } 349 if (!rlast) { 350 return ENOENT; 351 } 352 npf_ruleset_unlink(rlast, lastprev); 353 LIST_INSERT_HEAD(&rlset->rs_gc, rlast, r_aentry); 354 return 0; 355 } 356 357 /* 358 * npf_ruleset_list: serialise and return the dynamic rules. 359 */ 360 nvlist_t * 361 npf_ruleset_list(npf_t *npf, npf_ruleset_t *rlset, const char *rname) 362 { 363 nvlist_t *rgroup; 364 npf_rule_t *rg; 365 366 KASSERT(npf_config_locked_p(npf)); 367 368 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 369 return NULL; 370 } 371 if ((rgroup = nvlist_create(0)) == NULL) { 372 return NULL; 373 } 374 for (npf_rule_t *rl = rg->r_subset; rl; rl = rl->r_next) { 375 nvlist_t *rule; 376 377 KASSERT(rl->r_parent == rg); 378 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 379 380 rule = npf_rule_export(npf, rl); 381 if (!rule) { 382 nvlist_destroy(rgroup); 383 return NULL; 384 } 385 nvlist_append_nvlist_array(rgroup, "rules", rule); 386 nvlist_destroy(rule); 387 } 388 return rgroup; 389 } 390 391 /* 392 * npf_ruleset_flush: flush the dynamic rules in the ruleset by inserting 393 * them into the G/C list. 394 */ 395 int 396 npf_ruleset_flush(npf_ruleset_t *rlset, const char *rname) 397 { 398 npf_rule_t *rg, *rl; 399 400 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 401 return ESRCH; 402 } 403 404 rl = atomic_swap_ptr(&rg->r_subset, NULL); 405 membar_producer(); 406 407 while (rl) { 408 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 409 KASSERT(rl->r_parent == rg); 410 411 LIST_REMOVE(rl, r_aentry); 412 LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry); 413 rl = rl->r_next; 414 } 415 rlset->rs_idcnt = 0; 416 return 0; 417 } 418 419 /* 420 * npf_ruleset_gc: destroy the rules in G/C list. 421 */ 422 void 423 npf_ruleset_gc(npf_ruleset_t *rlset) 424 { 425 npf_rule_t *rl; 426 427 while ((rl = LIST_FIRST(&rlset->rs_gc)) != NULL) { 428 LIST_REMOVE(rl, r_aentry); 429 npf_rule_free(rl); 430 } 431 } 432 433 /* 434 * npf_ruleset_export: serialise and return the static rules. 435 */ 436 int 437 npf_ruleset_export(npf_t *npf, const npf_ruleset_t *rlset, 438 const char *key, nvlist_t *npf_dict) 439 { 440 const unsigned nitems = rlset->rs_nitems; 441 unsigned n = 0; 442 int error = 0; 443 444 KASSERT(npf_config_locked_p(npf)); 445 446 while (n < nitems) { 447 const npf_rule_t *rl = rlset->rs_rules[n]; 448 const npf_natpolicy_t *natp = rl->r_natp; 449 nvlist_t *rule; 450 451 rule = npf_rule_export(npf, rl); 452 if (!rule) { 453 error = ENOMEM; 454 break; 455 } 456 if (natp && (error = npf_nat_policyexport(natp, rule)) != 0) { 457 nvlist_destroy(rule); 458 break; 459 } 460 nvlist_append_nvlist_array(npf_dict, key, rule); 461 nvlist_destroy(rule); 462 n++; 463 } 464 return error; 465 } 466 467 /* 468 * npf_ruleset_reload: prepare the new ruleset by scanning the active 469 * ruleset and: 1) sharing the dynamic rules 2) sharing NAT policies. 470 * 471 * => The active (old) ruleset should be exclusively locked. 472 */ 473 void 474 npf_ruleset_reload(npf_t *npf, npf_ruleset_t *newset, 475 npf_ruleset_t *oldset, bool load) 476 { 477 npf_rule_t *rg, *rl; 478 uint64_t nid = 0; 479 480 KASSERT(npf_config_locked_p(npf)); 481 482 /* 483 * Scan the dynamic rules and share (migrate) if needed. 484 */ 485 LIST_FOREACH(rg, &newset->rs_dynamic, r_dentry) { 486 npf_rule_t *active_rgroup; 487 488 /* Look for a dynamic ruleset group with such name. */ 489 active_rgroup = npf_ruleset_lookup(oldset, rg->r_name); 490 if (active_rgroup == NULL) { 491 continue; 492 } 493 494 /* 495 * ATOMICITY: Copy the head pointer of the linked-list, 496 * but do not remove the rules from the active r_subset. 497 * This is necessary because the rules are still active 498 * and therefore are accessible for inspection via the 499 * old ruleset. 500 */ 501 rg->r_subset = active_rgroup->r_subset; 502 503 /* 504 * We can safely migrate to the new all-rule list and 505 * reset the parent rule, though. 506 */ 507 for (rl = rg->r_subset; rl; rl = rl->r_next) { 508 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 509 LIST_REMOVE(rl, r_aentry); 510 LIST_INSERT_HEAD(&newset->rs_all, rl, r_aentry); 511 512 KASSERT(rl->r_parent == active_rgroup); 513 rl->r_parent = rg; 514 } 515 } 516 517 /* 518 * If performing the load of connections then NAT policies may 519 * already have translated connections associated with them and 520 * we should not share or inherit anything. 521 */ 522 if (load) 523 return; 524 525 /* 526 * Scan all rules in the new ruleset and share NAT policies. 527 * Also, assign a unique ID for each policy here. 528 */ 529 LIST_FOREACH(rl, &newset->rs_all, r_aentry) { 530 npf_natpolicy_t *np; 531 npf_rule_t *actrl; 532 533 /* Does the rule have a NAT policy associated? */ 534 if ((np = rl->r_natp) == NULL) { 535 continue; 536 } 537 538 /* 539 * First, try to share the active port map. If this 540 * policy will be unused, npf_nat_freepolicy() will 541 * drop the reference. 542 */ 543 npf_ruleset_sharepm(oldset, np); 544 545 /* Does it match with any policy in the active ruleset? */ 546 LIST_FOREACH(actrl, &oldset->rs_all, r_aentry) { 547 if (!actrl->r_natp) 548 continue; 549 if ((actrl->r_attr & NPF_RULE_KEEPNAT) != 0) 550 continue; 551 if (npf_nat_cmppolicy(actrl->r_natp, np)) 552 break; 553 } 554 if (!actrl) { 555 /* No: just set the ID and continue. */ 556 npf_nat_setid(np, ++nid); 557 continue; 558 } 559 560 /* Yes: inherit the matching NAT policy. */ 561 rl->r_natp = actrl->r_natp; 562 npf_nat_setid(rl->r_natp, ++nid); 563 564 /* 565 * Finally, mark the active rule to not destroy its NAT 566 * policy later as we inherited it (but the rule must be 567 * kept active for now). Destroy the new/unused policy. 568 */ 569 actrl->r_attr |= NPF_RULE_KEEPNAT; 570 npf_nat_freepolicy(np); 571 } 572 573 /* Inherit the ID counter. */ 574 newset->rs_idcnt = oldset->rs_idcnt; 575 } 576 577 /* 578 * npf_ruleset_sharepm: attempt to share the active NAT portmap. 579 */ 580 npf_rule_t * 581 npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 582 { 583 npf_natpolicy_t *np; 584 npf_rule_t *rl; 585 586 /* 587 * Scan the NAT policies in the ruleset and match with the 588 * given policy based on the translation IP address. If they 589 * match - adjust the given NAT policy to use the active NAT 590 * portmap. In such case the reference on the old portmap is 591 * dropped and acquired on the active one. 592 */ 593 LIST_FOREACH(rl, &rlset->rs_all, r_aentry) { 594 np = rl->r_natp; 595 if (np == NULL || np == mnp) 596 continue; 597 if (npf_nat_sharepm(np, mnp)) 598 break; 599 } 600 return rl; 601 } 602 603 npf_natpolicy_t * 604 npf_ruleset_findnat(npf_ruleset_t *rlset, uint64_t id) 605 { 606 npf_rule_t *rl; 607 608 LIST_FOREACH(rl, &rlset->rs_all, r_aentry) { 609 npf_natpolicy_t *np = rl->r_natp; 610 if (np && npf_nat_getid(np) == id) { 611 return np; 612 } 613 } 614 return NULL; 615 } 616 617 /* 618 * npf_ruleset_freealg: inspect the ruleset and disassociate specified 619 * ALG from all NAT entries using it. 620 */ 621 void 622 npf_ruleset_freealg(npf_ruleset_t *rlset, npf_alg_t *alg) 623 { 624 npf_rule_t *rl; 625 npf_natpolicy_t *np; 626 627 LIST_FOREACH(rl, &rlset->rs_all, r_aentry) { 628 if ((np = rl->r_natp) != NULL) { 629 npf_nat_freealg(np, alg); 630 } 631 } 632 } 633 634 /* 635 * npf_rule_alloc: allocate a rule and initialise it. 636 */ 637 npf_rule_t * 638 npf_rule_alloc(npf_t *npf, const nvlist_t *rule) 639 { 640 npf_rule_t *rl; 641 const char *rname; 642 const void *key, *info; 643 size_t len; 644 645 /* Allocate a rule structure and keep the information. */ 646 rl = kmem_zalloc(sizeof(npf_rule_t), KM_SLEEP); 647 info = dnvlist_get_binary(rule, "info", &rl->r_info_len, NULL, 0); 648 if (info) { 649 rl->r_info = kmem_alloc(rl->r_info_len, KM_SLEEP); 650 memcpy(rl->r_info, info, rl->r_info_len); 651 } 652 rl->r_natp = NULL; 653 654 /* Name (optional) */ 655 if ((rname = dnvlist_get_string(rule, "name", NULL)) != NULL) { 656 strlcpy(rl->r_name, rname, NPF_RULE_MAXNAMELEN); 657 } else { 658 rl->r_name[0] = '\0'; 659 } 660 661 /* Attributes, priority and interface ID (optional). */ 662 rl->r_attr = dnvlist_get_number(rule, "attr", 0); 663 rl->r_attr &= ~NPF_RULE_PRIVMASK; 664 665 if (NPF_DYNAMIC_RULE_P(rl->r_attr)) { 666 /* Priority of the dynamic rule. */ 667 rl->r_priority = dnvlist_get_number(rule, "prio", 0); 668 } else { 669 /* The skip-to index. No need to validate it. */ 670 rl->r_skip_to = dnvlist_get_number(rule, "skip-to", 0); 671 } 672 673 /* Interface name; register and get the npf-if-id. */ 674 if ((rname = dnvlist_get_string(rule, "ifname", NULL)) != NULL) { 675 if ((rl->r_ifid = npf_ifmap_register(npf, rname)) == 0) { 676 kmem_free(rl, sizeof(npf_rule_t)); 677 return NULL; 678 } 679 } else { 680 rl->r_ifid = 0; 681 } 682 683 /* Key (optional). */ 684 if ((key = dnvlist_get_binary(rule, "key", &len, NULL, 0)) != NULL) { 685 if (len > NPF_RULE_MAXKEYLEN) { 686 kmem_free(rl, sizeof(npf_rule_t)); 687 return NULL; 688 } 689 memcpy(rl->r_key, key, len); 690 } 691 return rl; 692 } 693 694 static nvlist_t * 695 npf_rule_export(npf_t *npf, const npf_rule_t *rl) 696 { 697 nvlist_t *rule = nvlist_create(0); 698 unsigned skip_to = 0; 699 npf_rproc_t *rp; 700 701 nvlist_add_number(rule, "attr", rl->r_attr); 702 nvlist_add_number(rule, "prio", rl->r_priority); 703 if ((rl->r_skip_to & SKIPTO_ADJ_FLAG) == 0) { 704 skip_to = rl->r_skip_to & SKIPTO_MASK; 705 } 706 nvlist_add_number(rule, "skip-to", skip_to); 707 nvlist_add_number(rule, "code-type", rl->r_type); 708 if (rl->r_code) { 709 nvlist_add_binary(rule, "code", rl->r_code, rl->r_clen); 710 } 711 if (rl->r_ifid) { 712 const char *ifname = npf_ifmap_getname(npf, rl->r_ifid); 713 nvlist_add_string(rule, "ifname", ifname); 714 } 715 nvlist_add_number(rule, "id", rl->r_id); 716 717 if (rl->r_name[0]) { 718 nvlist_add_string(rule, "name", rl->r_name); 719 } 720 if (NPF_DYNAMIC_RULE_P(rl->r_attr)) { 721 nvlist_add_binary(rule, "key", rl->r_key, NPF_RULE_MAXKEYLEN); 722 } 723 if (rl->r_info) { 724 nvlist_add_binary(rule, "info", rl->r_info, rl->r_info_len); 725 } 726 if ((rp = npf_rule_getrproc(rl)) != NULL) { 727 const char *rname = npf_rproc_getname(rp); 728 nvlist_add_string(rule, "rproc", rname); 729 npf_rproc_release(rp); 730 } 731 return rule; 732 } 733 734 /* 735 * npf_rule_setcode: assign filter code to the rule. 736 * 737 * => The code must be validated by the caller. 738 * => JIT compilation may be performed here. 739 */ 740 void 741 npf_rule_setcode(npf_rule_t *rl, const int type, void *code, size_t size) 742 { 743 KASSERT(type == NPF_CODE_BPF); 744 745 rl->r_type = type; 746 rl->r_code = code; 747 rl->r_clen = size; 748 rl->r_jcode = npf_bpf_compile(code, size); 749 } 750 751 /* 752 * npf_rule_setrproc: assign a rule procedure and hold a reference on it. 753 */ 754 void 755 npf_rule_setrproc(npf_rule_t *rl, npf_rproc_t *rp) 756 { 757 npf_rproc_acquire(rp); 758 rl->r_rproc = rp; 759 } 760 761 /* 762 * npf_rule_free: free the specified rule. 763 */ 764 void 765 npf_rule_free(npf_rule_t *rl) 766 { 767 npf_natpolicy_t *np = rl->r_natp; 768 npf_rproc_t *rp = rl->r_rproc; 769 770 if (np && (rl->r_attr & NPF_RULE_KEEPNAT) == 0) { 771 /* Free NAT policy. */ 772 npf_nat_freepolicy(np); 773 } 774 if (rp) { 775 /* Release rule procedure. */ 776 npf_rproc_release(rp); 777 } 778 if (rl->r_code) { 779 /* Free byte-code. */ 780 kmem_free(rl->r_code, rl->r_clen); 781 } 782 if (rl->r_jcode) { 783 /* Free JIT code. */ 784 bpf_jit_freecode(rl->r_jcode); 785 } 786 if (rl->r_info) { 787 kmem_free(rl->r_info, rl->r_info_len); 788 } 789 kmem_free(rl, sizeof(npf_rule_t)); 790 } 791 792 /* 793 * npf_rule_getid: return the unique ID of a rule. 794 * npf_rule_getrproc: acquire a reference and return rule procedure, if any. 795 * npf_rule_getnat: get NAT policy assigned to the rule. 796 */ 797 798 uint64_t 799 npf_rule_getid(const npf_rule_t *rl) 800 { 801 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 802 return rl->r_id; 803 } 804 805 npf_rproc_t * 806 npf_rule_getrproc(const npf_rule_t *rl) 807 { 808 npf_rproc_t *rp = rl->r_rproc; 809 810 if (rp) { 811 npf_rproc_acquire(rp); 812 } 813 return rp; 814 } 815 816 npf_natpolicy_t * 817 npf_rule_getnat(const npf_rule_t *rl) 818 { 819 return rl->r_natp; 820 } 821 822 /* 823 * npf_rule_setnat: assign NAT policy to the rule and insert into the 824 * NAT policy list in the ruleset. 825 */ 826 void 827 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np) 828 { 829 KASSERT(rl->r_natp == NULL); 830 rl->r_natp = np; 831 } 832 833 /* 834 * npf_rule_inspect: match the interface, direction and run the filter code. 835 * Returns true if rule matches and false otherwise. 836 */ 837 static inline bool 838 npf_rule_inspect(const npf_rule_t *rl, bpf_args_t *bc_args, 839 const int di_mask, const u_int ifid) 840 { 841 /* Match the interface. */ 842 if (rl->r_ifid && rl->r_ifid != ifid) { 843 return false; 844 } 845 846 /* Match the direction. */ 847 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) { 848 if ((rl->r_attr & di_mask) == 0) 849 return false; 850 } 851 852 /* Any code? */ 853 if (!rl->r_code) { 854 KASSERT(rl->r_jcode == NULL); 855 return true; 856 } 857 KASSERT(rl->r_type == NPF_CODE_BPF); 858 return npf_bpf_filter(bc_args, rl->r_code, rl->r_jcode) != 0; 859 } 860 861 /* 862 * npf_rule_reinspect: re-inspect the dynamic rule by iterating its list. 863 * This is only for the dynamic rules. Subrules cannot have nested rules. 864 */ 865 static inline npf_rule_t * 866 npf_rule_reinspect(const npf_rule_t *rg, bpf_args_t *bc_args, 867 const int di_mask, const u_int ifid) 868 { 869 npf_rule_t *final_rl = NULL, *rl; 870 871 KASSERT(NPF_DYNAMIC_GROUP_P(rg->r_attr)); 872 873 for (rl = rg->r_subset; rl; rl = rl->r_next) { 874 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority); 875 if (!npf_rule_inspect(rl, bc_args, di_mask, ifid)) { 876 continue; 877 } 878 if (rl->r_attr & NPF_RULE_FINAL) { 879 return rl; 880 } 881 final_rl = rl; 882 } 883 return final_rl; 884 } 885 886 /* 887 * npf_ruleset_inspect: inspect the packet against the given ruleset. 888 * 889 * Loop through the rules in the set and run the byte-code of each rule 890 * against the packet (nbuf chain). If sub-ruleset is found, inspect it. 891 */ 892 npf_rule_t * 893 npf_ruleset_inspect(npf_cache_t *npc, const npf_ruleset_t *rlset, 894 const int di, const int layer) 895 { 896 nbuf_t *nbuf = npc->npc_nbuf; 897 const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT; 898 const u_int nitems = rlset->rs_nitems; 899 const u_int ifid = nbuf->nb_ifid; 900 npf_rule_t *final_rl = NULL; 901 bpf_args_t bc_args; 902 u_int n = 0; 903 904 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0)); 905 906 /* 907 * Prepare the external memory store and the arguments for 908 * the BPF programs to be executed. Reset mbuf before taking 909 * any pointers for the BPF. 910 */ 911 uint32_t bc_words[NPF_BPF_NWORDS]; 912 913 nbuf_reset(nbuf); 914 npf_bpf_prepare(npc, &bc_args, bc_words); 915 916 while (n < nitems) { 917 npf_rule_t *rl = rlset->rs_rules[n]; 918 const u_int skip_to = rl->r_skip_to & SKIPTO_MASK; 919 const uint32_t attr = rl->r_attr; 920 921 KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)); 922 KASSERT(n < skip_to); 923 924 /* Group is a barrier: return a matching if found any. */ 925 if ((attr & NPF_RULE_GROUP) != 0 && final_rl) { 926 break; 927 } 928 929 /* Main inspection of the rule. */ 930 if (!npf_rule_inspect(rl, &bc_args, di_mask, ifid)) { 931 n = skip_to; 932 continue; 933 } 934 935 if (NPF_DYNAMIC_GROUP_P(attr)) { 936 /* 937 * If this is a dynamic rule, re-inspect the subrules. 938 * If it has any matching rule, then it is final. 939 */ 940 rl = npf_rule_reinspect(rl, &bc_args, di_mask, ifid); 941 if (rl != NULL) { 942 final_rl = rl; 943 break; 944 } 945 } else if ((attr & NPF_RULE_GROUP) == 0) { 946 /* 947 * Groups themselves are not matching. 948 */ 949 final_rl = rl; 950 } 951 952 /* Set the matching rule and check for "final". */ 953 if (attr & NPF_RULE_FINAL) { 954 break; 955 } 956 n++; 957 } 958 959 KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)); 960 return final_rl; 961 } 962 963 /* 964 * npf_rule_conclude: return decision and the flags for conclusion. 965 * 966 * => Returns ENETUNREACH if "block" and 0 if "pass". 967 */ 968 int 969 npf_rule_conclude(const npf_rule_t *rl, npf_match_info_t *mi) 970 { 971 /* If not passing - drop the packet. */ 972 mi->mi_retfl = rl->r_attr; 973 mi->mi_rid = rl->r_id; 974 return (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH; 975 } 976 977 978 #if defined(DDB) || defined(_NPF_TESTING) 979 980 void 981 npf_ruleset_dump(npf_t *npf, const char *name) 982 { 983 npf_ruleset_t *rlset = npf_config_ruleset(npf); 984 npf_rule_t *rg, *rl; 985 986 LIST_FOREACH(rg, &rlset->rs_dynamic, r_dentry) { 987 printf("ruleset '%s':\n", rg->r_name); 988 for (rl = rg->r_subset; rl; rl = rl->r_next) { 989 printf("\tid %"PRIu64", key: ", rl->r_id); 990 for (u_int i = 0; i < NPF_RULE_MAXKEYLEN; i++) 991 printf("%x", rl->r_key[i]); 992 printf("\n"); 993 } 994 } 995 } 996 997 #endif 998