1 /* $NetBSD: npf_ruleset.c,v 1.33 2014/06/25 00:20:06 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This material is based upon work partially supported by The 8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * NPF ruleset module. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.33 2014/06/25 00:20:06 rmind Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 42 #include <sys/atomic.h> 43 #include <sys/kmem.h> 44 #include <sys/queue.h> 45 #include <sys/mbuf.h> 46 #include <sys/types.h> 47 48 #include <net/bpf.h> 49 #include <net/bpfjit.h> 50 #include <net/pfil.h> 51 #include <net/if.h> 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 size_t 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 /* Rule priority: (highest) 1, 2 ... n (lowest). */ 93 pri_t r_priority; 94 95 /* 96 * Dynamic group: subset queue and a dynamic group list entry. 97 * Dynamic rule: entry and the parent rule (the group). 98 */ 99 union { 100 TAILQ_HEAD(npf_ruleq, npf_rule) r_subset; 101 TAILQ_ENTRY(npf_rule) r_entry; 102 } /* C11 */; 103 union { 104 LIST_ENTRY(npf_rule) r_dentry; 105 npf_rule_t * r_parent; 106 } /* C11 */; 107 108 /* Rule ID and the original dictionary. */ 109 uint64_t r_id; 110 prop_dictionary_t r_dict; 111 112 /* Rule name and all-list entry. */ 113 char r_name[NPF_RULE_MAXNAMELEN]; 114 LIST_ENTRY(npf_rule) r_aentry; 115 116 /* Key (optional). */ 117 uint8_t r_key[NPF_RULE_MAXKEYLEN]; 118 }; 119 120 /* 121 * Private attributes - must be in the NPF_RULE_PRIVMASK range. 122 */ 123 #define NPF_RULE_KEEPNAT (0x01000000 & NPF_RULE_PRIVMASK) 124 125 #define NPF_DYNAMIC_GROUP_P(attr) \ 126 (((attr) & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP) 127 128 #define NPF_DYNAMIC_RULE_P(attr) \ 129 (((attr) & NPF_DYNAMIC_GROUP) == NPF_RULE_DYNAMIC) 130 131 npf_ruleset_t * 132 npf_ruleset_create(size_t slots) 133 { 134 size_t len = offsetof(npf_ruleset_t, rs_rules[slots]); 135 npf_ruleset_t *rlset; 136 137 rlset = kmem_zalloc(len, KM_SLEEP); 138 LIST_INIT(&rlset->rs_dynamic); 139 LIST_INIT(&rlset->rs_all); 140 LIST_INIT(&rlset->rs_gc); 141 rlset->rs_slots = slots; 142 143 return rlset; 144 } 145 146 static void 147 npf_ruleset_unlink(npf_ruleset_t *rlset, npf_rule_t *rl) 148 { 149 if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) { 150 LIST_REMOVE(rl, r_dentry); 151 } 152 if (NPF_DYNAMIC_RULE_P(rl->r_attr)) { 153 npf_rule_t *rg = rl->r_parent; 154 TAILQ_REMOVE(&rg->r_subset, rl, r_entry); 155 } 156 LIST_REMOVE(rl, r_aentry); 157 } 158 159 void 160 npf_ruleset_destroy(npf_ruleset_t *rlset) 161 { 162 size_t len = offsetof(npf_ruleset_t, rs_rules[rlset->rs_slots]); 163 npf_rule_t *rl; 164 165 while ((rl = LIST_FIRST(&rlset->rs_all)) != NULL) { 166 npf_ruleset_unlink(rlset, rl); 167 npf_rule_free(rl); 168 } 169 KASSERT(LIST_EMPTY(&rlset->rs_dynamic)); 170 KASSERT(LIST_EMPTY(&rlset->rs_gc)); 171 kmem_free(rlset, len); 172 } 173 174 /* 175 * npf_ruleset_insert: insert the rule into the specified ruleset. 176 */ 177 void 178 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl) 179 { 180 u_int n = rlset->rs_nitems; 181 182 KASSERT(n < rlset->rs_slots); 183 184 LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry); 185 if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) { 186 LIST_INSERT_HEAD(&rlset->rs_dynamic, rl, r_dentry); 187 } else { 188 KASSERTMSG(rl->r_parent == NULL, "cannot be dynamic rule"); 189 rl->r_attr &= ~NPF_RULE_DYNAMIC; 190 } 191 192 rlset->rs_rules[n] = rl; 193 rlset->rs_nitems++; 194 195 if (rl->r_skip_to < ++n) { 196 rl->r_skip_to = n; 197 } 198 } 199 200 static npf_rule_t * 201 npf_ruleset_lookup(npf_ruleset_t *rlset, const char *name) 202 { 203 npf_rule_t *rl; 204 205 KASSERT(npf_config_locked_p()); 206 207 LIST_FOREACH(rl, &rlset->rs_dynamic, r_dentry) { 208 KASSERT(NPF_DYNAMIC_GROUP_P(rl->r_attr)); 209 if (strncmp(rl->r_name, name, NPF_RULE_MAXNAMELEN) == 0) 210 break; 211 } 212 return rl; 213 } 214 215 int 216 npf_ruleset_add(npf_ruleset_t *rlset, const char *rname, npf_rule_t *rl) 217 { 218 npf_rule_t *rg, *it; 219 pri_t priocmd; 220 221 rg = npf_ruleset_lookup(rlset, rname); 222 if (rg == NULL) { 223 return ESRCH; 224 } 225 if (!NPF_DYNAMIC_RULE_P(rl->r_attr)) { 226 return EINVAL; 227 } 228 229 /* Dynamic rule - assign a unique ID and save the parent. */ 230 rl->r_id = ++rlset->rs_idcnt; 231 rl->r_parent = rg; 232 233 /* 234 * Rule priority: (highest) 1, 2 ... n (lowest). 235 * Negative priority indicates an operation and is reset to zero. 236 */ 237 if ((priocmd = rl->r_priority) < 0) { 238 rl->r_priority = 0; 239 } 240 241 switch (priocmd) { 242 case NPF_PRI_FIRST: 243 TAILQ_FOREACH(it, &rg->r_subset, r_entry) { 244 if (rl->r_priority <= it->r_priority) 245 break; 246 } 247 if (it) { 248 TAILQ_INSERT_BEFORE(it, rl, r_entry); 249 } else { 250 TAILQ_INSERT_HEAD(&rg->r_subset, rl, r_entry); 251 } 252 break; 253 case NPF_PRI_LAST: 254 default: 255 TAILQ_FOREACH(it, &rg->r_subset, r_entry) { 256 if (rl->r_priority < it->r_priority) 257 break; 258 } 259 if (it) { 260 TAILQ_INSERT_BEFORE(it, rl, r_entry); 261 } else { 262 TAILQ_INSERT_TAIL(&rg->r_subset, rl, r_entry); 263 } 264 break; 265 } 266 267 /* Finally, add into the all-list. */ 268 LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry); 269 return 0; 270 } 271 272 int 273 npf_ruleset_remove(npf_ruleset_t *rlset, const char *rname, uint64_t id) 274 { 275 npf_rule_t *rg, *rl; 276 277 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 278 return ESRCH; 279 } 280 TAILQ_FOREACH(rl, &rg->r_subset, r_entry) { 281 KASSERT(rl->r_parent == rg); 282 283 /* Compare ID. On match, remove and return. */ 284 if (rl->r_id == id) { 285 npf_ruleset_unlink(rlset, rl); 286 LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry); 287 return 0; 288 } 289 } 290 return ENOENT; 291 } 292 293 int 294 npf_ruleset_remkey(npf_ruleset_t *rlset, const char *rname, 295 const void *key, size_t len) 296 { 297 npf_rule_t *rg, *rl; 298 299 KASSERT(len && len <= NPF_RULE_MAXKEYLEN); 300 301 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 302 return ESRCH; 303 } 304 305 /* Find the last in the list. */ 306 TAILQ_FOREACH_REVERSE(rl, &rg->r_subset, npf_ruleq, r_entry) { 307 KASSERT(rl->r_parent == rg); 308 309 /* Compare the key. On match, remove and return. */ 310 if (memcmp(rl->r_key, key, len) == 0) { 311 npf_ruleset_unlink(rlset, rl); 312 LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry); 313 return 0; 314 } 315 } 316 return ENOENT; 317 } 318 319 prop_dictionary_t 320 npf_ruleset_list(npf_ruleset_t *rlset, const char *rname) 321 { 322 prop_dictionary_t rldict; 323 prop_array_t rules; 324 npf_rule_t *rg, *rl; 325 326 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 327 return NULL; 328 } 329 if ((rldict = prop_dictionary_create()) == NULL) { 330 return NULL; 331 } 332 if ((rules = prop_array_create()) == NULL) { 333 prop_object_release(rldict); 334 return NULL; 335 } 336 337 TAILQ_FOREACH(rl, &rg->r_subset, r_entry) { 338 KASSERT(rl->r_parent == rg); 339 if (rl->r_dict && !prop_array_add(rules, rl->r_dict)) { 340 prop_object_release(rldict); 341 prop_object_release(rules); 342 return NULL; 343 } 344 } 345 346 if (!prop_dictionary_set(rldict, "rules", rules)) { 347 prop_object_release(rldict); 348 rldict = NULL; 349 } 350 prop_object_release(rules); 351 return rldict; 352 } 353 354 int 355 npf_ruleset_flush(npf_ruleset_t *rlset, const char *rname) 356 { 357 npf_rule_t *rg, *rl; 358 359 if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) { 360 return ESRCH; 361 } 362 while ((rl = TAILQ_FIRST(&rg->r_subset)) != NULL) { 363 KASSERT(rl->r_parent == rg); 364 npf_ruleset_unlink(rlset, rl); 365 LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry); 366 } 367 return 0; 368 } 369 370 void 371 npf_ruleset_gc(npf_ruleset_t *rlset) 372 { 373 npf_rule_t *rl; 374 375 while ((rl = LIST_FIRST(&rlset->rs_gc)) != NULL) { 376 LIST_REMOVE(rl, r_aentry); 377 npf_rule_free(rl); 378 } 379 } 380 381 /* 382 * npf_ruleset_reload: prepare the new ruleset by scanning the active 383 * ruleset and 1) sharing the dynamic rules 2) sharing NAT policies. 384 * 385 * => The active (old) ruleset should be exclusively locked. 386 */ 387 void 388 npf_ruleset_reload(npf_ruleset_t *newset, npf_ruleset_t *oldset) 389 { 390 npf_rule_t *rg, *rl; 391 392 KASSERT(npf_config_locked_p()); 393 394 /* 395 * Scan the dynamic rules and share (migrate) if needed. 396 */ 397 LIST_FOREACH(rg, &newset->rs_dynamic, r_dentry) { 398 npf_rule_t *actrg; 399 400 /* Look for a dynamic ruleset group with such name. */ 401 actrg = npf_ruleset_lookup(oldset, rg->r_name); 402 if (actrg == NULL) { 403 continue; 404 } 405 406 /* 407 * Copy the list-head structure. This is necessary because 408 * the rules are still active and therefore accessible for 409 * inspection via the old ruleset. 410 */ 411 memcpy(&rg->r_subset, &actrg->r_subset, sizeof(rg->r_subset)); 412 TAILQ_FOREACH(rl, &rg->r_subset, r_entry) { 413 /* 414 * We can safely migrate to the new all-rule list 415 * and re-set the parent rule, though. 416 */ 417 LIST_REMOVE(rl, r_aentry); 418 LIST_INSERT_HEAD(&newset->rs_all, rl, r_aentry); 419 rl->r_parent = rg; 420 } 421 } 422 423 /* 424 * Scan all rules in the new ruleset and share NAT policies. 425 */ 426 LIST_FOREACH(rl, &newset->rs_all, r_aentry) { 427 npf_natpolicy_t *np; 428 npf_rule_t *actrl; 429 430 /* Does the rule have a NAT policy associated? */ 431 if ((np = rl->r_natp) == NULL) { 432 continue; 433 } 434 /* Does it match with any policy in the active ruleset? */ 435 if ((actrl = npf_ruleset_matchnat(oldset, np)) == NULL) { 436 continue; 437 } 438 439 /* 440 * Inherit the matching NAT policy and check other ones 441 * in the new ruleset for sharing the portmap. 442 */ 443 rl->r_natp = actrl->r_natp; 444 npf_ruleset_sharepm(newset, rl->r_natp); 445 446 /* 447 * Finally, mark the active rule to not destroy its NAT 448 * policy later as we inherited it (but the rule must be 449 * kept active for now). Destroy the new/unused policy. 450 */ 451 actrl->r_attr |= NPF_RULE_KEEPNAT; 452 npf_nat_freepolicy(np); 453 } 454 455 /* Inherit the ID counter. */ 456 newset->rs_idcnt = oldset->rs_idcnt; 457 } 458 459 /* 460 * npf_ruleset_matchnat: find a matching NAT policy in the ruleset. 461 */ 462 npf_rule_t * 463 npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 464 { 465 npf_rule_t *rl; 466 467 /* Find a matching NAT policy in the old ruleset. */ 468 LIST_FOREACH(rl, &rlset->rs_all, r_aentry) { 469 if (rl->r_natp && npf_nat_matchpolicy(rl->r_natp, mnp)) 470 break; 471 } 472 return rl; 473 } 474 475 npf_rule_t * 476 npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp) 477 { 478 npf_natpolicy_t *np; 479 npf_rule_t *rl; 480 481 /* Find a matching NAT policy in the old ruleset. */ 482 LIST_FOREACH(rl, &rlset->rs_all, r_aentry) { 483 /* 484 * NAT policy might not yet be set during the creation of 485 * the ruleset (in such case, rule is for our policy), or 486 * policies might be equal due to rule exchange on reload. 487 */ 488 np = rl->r_natp; 489 if (np == NULL || np == mnp) 490 continue; 491 if (npf_nat_sharepm(np, mnp)) 492 break; 493 } 494 return rl; 495 } 496 497 /* 498 * npf_ruleset_freealg: inspect the ruleset and disassociate specified 499 * ALG from all NAT entries using it. 500 */ 501 void 502 npf_ruleset_freealg(npf_ruleset_t *rlset, npf_alg_t *alg) 503 { 504 npf_rule_t *rl; 505 npf_natpolicy_t *np; 506 507 LIST_FOREACH(rl, &rlset->rs_all, r_aentry) { 508 if ((np = rl->r_natp) != NULL) { 509 npf_nat_freealg(np, alg); 510 } 511 } 512 } 513 514 /* 515 * npf_rule_alloc: allocate a rule and initialise it. 516 */ 517 npf_rule_t * 518 npf_rule_alloc(prop_dictionary_t rldict) 519 { 520 npf_rule_t *rl; 521 const char *rname; 522 523 /* Allocate a rule structure. */ 524 rl = kmem_zalloc(sizeof(npf_rule_t), KM_SLEEP); 525 TAILQ_INIT(&rl->r_subset); 526 rl->r_natp = NULL; 527 528 /* Name (optional) */ 529 if (prop_dictionary_get_cstring_nocopy(rldict, "name", &rname)) { 530 strlcpy(rl->r_name, rname, NPF_RULE_MAXNAMELEN); 531 } else { 532 rl->r_name[0] = '\0'; 533 } 534 535 /* Attributes, priority and interface ID (optional). */ 536 prop_dictionary_get_uint32(rldict, "attributes", &rl->r_attr); 537 prop_dictionary_get_int32(rldict, "priority", &rl->r_priority); 538 rl->r_attr &= ~NPF_RULE_PRIVMASK; 539 540 if (prop_dictionary_get_cstring_nocopy(rldict, "interface", &rname)) { 541 if ((rl->r_ifid = npf_ifmap_register(rname)) == 0) { 542 kmem_free(rl, sizeof(npf_rule_t)); 543 return NULL; 544 } 545 } else { 546 rl->r_ifid = 0; 547 } 548 549 /* Get the skip-to index. No need to validate it. */ 550 prop_dictionary_get_uint32(rldict, "skip-to", &rl->r_skip_to); 551 552 /* Key (optional). */ 553 prop_object_t obj = prop_dictionary_get(rldict, "key"); 554 const void *key = prop_data_data_nocopy(obj); 555 556 if (key) { 557 size_t len = prop_data_size(obj); 558 if (len > NPF_RULE_MAXKEYLEN) { 559 kmem_free(rl, sizeof(npf_rule_t)); 560 return NULL; 561 } 562 memcpy(rl->r_key, key, len); 563 } 564 565 if (NPF_DYNAMIC_RULE_P(rl->r_attr)) { 566 rl->r_dict = prop_dictionary_copy(rldict); 567 } 568 569 return rl; 570 } 571 572 /* 573 * npf_rule_setcode: assign filter code to the rule. 574 * 575 * => The code must be validated by the caller. 576 * => JIT compilation may be performed here. 577 */ 578 void 579 npf_rule_setcode(npf_rule_t *rl, const int type, void *code, size_t size) 580 { 581 KASSERT(type == NPF_CODE_BPF); 582 583 if ((rl->r_jcode = npf_bpf_compile(code, size)) == NULL) { 584 rl->r_code = code; 585 rl->r_clen = size; 586 } else { 587 rl->r_code = NULL; 588 } 589 rl->r_type = type; 590 } 591 592 /* 593 * npf_rule_setrproc: assign a rule procedure and hold a reference on it. 594 */ 595 void 596 npf_rule_setrproc(npf_rule_t *rl, npf_rproc_t *rp) 597 { 598 npf_rproc_acquire(rp); 599 rl->r_rproc = rp; 600 } 601 602 /* 603 * npf_rule_free: free the specified rule. 604 */ 605 void 606 npf_rule_free(npf_rule_t *rl) 607 { 608 npf_natpolicy_t *np = rl->r_natp; 609 npf_rproc_t *rp = rl->r_rproc; 610 611 if (np && (rl->r_attr & NPF_RULE_KEEPNAT) == 0) { 612 /* Free NAT policy. */ 613 npf_nat_freepolicy(np); 614 } 615 if (rp) { 616 /* Release rule procedure. */ 617 npf_rproc_release(rp); 618 } 619 if (rl->r_code) { 620 /* Free byte-code. */ 621 kmem_free(rl->r_code, rl->r_clen); 622 } 623 if (rl->r_jcode) { 624 /* Free JIT code. */ 625 bpf_jit_freecode(rl->r_jcode); 626 } 627 if (rl->r_dict) { 628 /* Destroy the dictionary. */ 629 prop_object_release(rl->r_dict); 630 } 631 kmem_free(rl, sizeof(npf_rule_t)); 632 } 633 634 /* 635 * npf_rule_getid: return the unique ID of a rule. 636 * npf_rule_getrproc: acquire a reference and return rule procedure, if any. 637 * npf_rule_getnat: get NAT policy assigned to the rule. 638 */ 639 640 uint64_t 641 npf_rule_getid(const npf_rule_t *rl) 642 { 643 KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr)); 644 return rl->r_id; 645 } 646 647 npf_rproc_t * 648 npf_rule_getrproc(const npf_rule_t *rl) 649 { 650 npf_rproc_t *rp = rl->r_rproc; 651 652 if (rp) { 653 npf_rproc_acquire(rp); 654 } 655 return rp; 656 } 657 658 npf_natpolicy_t * 659 npf_rule_getnat(const npf_rule_t *rl) 660 { 661 return rl->r_natp; 662 } 663 664 /* 665 * npf_rule_setnat: assign NAT policy to the rule and insert into the 666 * NAT policy list in the ruleset. 667 */ 668 void 669 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np) 670 { 671 KASSERT(rl->r_natp == NULL); 672 rl->r_natp = np; 673 } 674 675 /* 676 * npf_rule_inspect: match the interface, direction and run the filter code. 677 * Returns true if rule matches and false otherwise. 678 */ 679 static inline bool 680 npf_rule_inspect(const npf_rule_t *rl, bpf_args_t *bc_args, 681 const int di_mask, const u_int ifid) 682 { 683 /* Match the interface. */ 684 if (rl->r_ifid && rl->r_ifid != ifid) { 685 return false; 686 } 687 688 /* Match the direction. */ 689 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) { 690 if ((rl->r_attr & di_mask) == 0) 691 return false; 692 } 693 694 /* Any code? */ 695 if (rl->r_jcode == rl->r_code) { 696 KASSERT(rl->r_jcode == NULL); 697 KASSERT(rl->r_code == NULL); 698 return true; 699 } 700 KASSERT(rl->r_type == NPF_CODE_BPF); 701 return npf_bpf_filter(bc_args, rl->r_code, rl->r_jcode) != 0; 702 } 703 704 /* 705 * npf_rule_reinspect: re-inspect the dynamic rule by iterating its list. 706 * This is only for the dynamic rules. Subrules cannot have nested rules. 707 */ 708 static npf_rule_t * 709 npf_rule_reinspect(const npf_rule_t *drl, bpf_args_t *bc_args, 710 const int di_mask, const u_int ifid) 711 { 712 npf_rule_t *final_rl = NULL, *rl; 713 714 KASSERT(NPF_DYNAMIC_GROUP_P(drl->r_attr)); 715 716 TAILQ_FOREACH(rl, &drl->r_subset, r_entry) { 717 if (!npf_rule_inspect(rl, bc_args, di_mask, ifid)) { 718 continue; 719 } 720 if (rl->r_attr & NPF_RULE_FINAL) { 721 return rl; 722 } 723 final_rl = rl; 724 } 725 return final_rl; 726 } 727 728 /* 729 * npf_ruleset_inspect: inspect the packet against the given ruleset. 730 * 731 * Loop through the rules in the set and run the byte-code of each rule 732 * against the packet (nbuf chain). If sub-ruleset is found, inspect it. 733 * 734 * => Caller is responsible for nbuf chain protection. 735 */ 736 npf_rule_t * 737 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, 738 const npf_ruleset_t *rlset, const int di, const int layer) 739 { 740 const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT; 741 const u_int nitems = rlset->rs_nitems; 742 const u_int ifid = nbuf->nb_ifid; 743 npf_rule_t *final_rl = NULL; 744 bpf_args_t bc_args; 745 u_int n = 0; 746 747 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0)); 748 749 /* 750 * Prepare the external memory store and the arguments for 751 * the BPF programs to be executed. 752 */ 753 uint32_t bc_words[NPF_BPF_NWORDS]; 754 npf_bpf_prepare(npc, nbuf, &bc_args, bc_words); 755 756 while (n < nitems) { 757 npf_rule_t *rl = rlset->rs_rules[n]; 758 const u_int skip_to = rl->r_skip_to; 759 const uint32_t attr = rl->r_attr; 760 761 KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)); 762 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority); 763 KASSERT(n < skip_to); 764 765 /* Group is a barrier: return a matching if found any. */ 766 if ((attr & NPF_RULE_GROUP) != 0 && final_rl) { 767 break; 768 } 769 770 /* Main inspection of the rule. */ 771 if (!npf_rule_inspect(rl, &bc_args, di_mask, ifid)) { 772 n = skip_to; 773 continue; 774 } 775 776 if (NPF_DYNAMIC_GROUP_P(attr)) { 777 /* 778 * If this is a dynamic rule, re-inspect the subrules. 779 * If it has any matching rule, then it is final. 780 */ 781 rl = npf_rule_reinspect(rl, &bc_args, di_mask, ifid); 782 if (rl != NULL) { 783 final_rl = rl; 784 break; 785 } 786 } else if ((attr & NPF_RULE_GROUP) == 0) { 787 /* 788 * Groups themselves are not matching. 789 */ 790 final_rl = rl; 791 } 792 793 /* Set the matching rule and check for "final". */ 794 if (attr & NPF_RULE_FINAL) { 795 break; 796 } 797 n++; 798 } 799 800 KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)); 801 return final_rl; 802 } 803 804 /* 805 * npf_rule_conclude: return decision and the flags for conclusion. 806 * 807 * => Returns ENETUNREACH if "block" and 0 if "pass". 808 */ 809 int 810 npf_rule_conclude(const npf_rule_t *rl, int *retfl) 811 { 812 /* If not passing - drop the packet. */ 813 *retfl = rl->r_attr; 814 return (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH; 815 } 816