1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2001 Daniel Hartmeier 5 * Copyright (c) 2002,2003 Henning Brauer 6 * All rights reserved. 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 * 12 * - Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * - Redistributions in binary form must reproduce the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer in the documentation and/or other materials provided 17 * with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 * 32 * Effort sponsored in part by the Defense Advanced Research Projects 33 * Agency (DARPA) and Air Force Research Laboratory, Air Force 34 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 35 * 36 * $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ 37 */ 38 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 41 42 #include <sys/param.h> 43 #include <sys/socket.h> 44 #include <sys/systm.h> 45 #include <sys/refcount.h> 46 #include <sys/mbuf.h> 47 48 #include <netinet/in.h> 49 #include <netinet/in_systm.h> 50 #include <netinet/ip.h> 51 #include <netinet/tcp.h> 52 53 #include <net/if.h> 54 #include <net/vnet.h> 55 #include <net/pfvar.h> 56 57 #ifdef INET6 58 #include <netinet/ip6.h> 59 #endif /* INET6 */ 60 61 #ifndef _KERNEL 62 #error "Kernel only file. Please use sbin/pfctl/pf_ruleset.c instead." 63 #endif 64 65 #define DPFPRINTF(format, x...) \ 66 if (V_pf_status.debug >= PF_DEBUG_NOISY) \ 67 printf(format , ##x) 68 #define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO) 69 #define rs_free(x) free(x, M_TEMP) 70 71 VNET_DEFINE(struct pf_kanchor_global, pf_anchors); 72 VNET_DEFINE(struct pf_kanchor, pf_main_anchor); 73 VNET_DEFINE(struct pf_keth_ruleset*, pf_keth); 74 VNET_DEFINE(struct pf_keth_anchor, pf_main_keth_anchor); 75 VNET_DEFINE(struct pf_keth_anchor_global, pf_keth_anchors); 76 77 static __inline int pf_kanchor_compare(struct pf_kanchor *, 78 struct pf_kanchor *); 79 static __inline int pf_keth_anchor_compare(struct pf_keth_anchor *, 80 struct pf_keth_anchor *); 81 static struct pf_kanchor *pf_find_kanchor(const char *); 82 83 RB_GENERATE(pf_kanchor_global, pf_kanchor, entry_global, pf_kanchor_compare); 84 RB_GENERATE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare); 85 RB_GENERATE(pf_keth_anchor_global, pf_keth_anchor, entry_global, 86 pf_keth_anchor_compare); 87 RB_GENERATE(pf_keth_anchor_node, pf_keth_anchor, entry_node, 88 pf_keth_anchor_compare); 89 90 static __inline int 91 pf_kanchor_compare(struct pf_kanchor *a, struct pf_kanchor *b) 92 { 93 int c = strcmp(a->path, b->path); 94 95 return (c ? (c < 0 ? -1 : 1) : 0); 96 } 97 98 static __inline int 99 pf_keth_anchor_compare(struct pf_keth_anchor *a, struct pf_keth_anchor *b) 100 { 101 int c = strcmp(a->path, b->path); 102 103 return (c ? (c < 0 ? -1 : 1) : 0); 104 } 105 106 int 107 pf_get_ruleset_number(u_int8_t action) 108 { 109 switch (action) { 110 case PF_SCRUB: 111 case PF_NOSCRUB: 112 return (PF_RULESET_SCRUB); 113 break; 114 case PF_PASS: 115 case PF_MATCH: 116 case PF_DROP: 117 return (PF_RULESET_FILTER); 118 break; 119 case PF_NAT: 120 case PF_NONAT: 121 return (PF_RULESET_NAT); 122 break; 123 case PF_BINAT: 124 case PF_NOBINAT: 125 return (PF_RULESET_BINAT); 126 break; 127 case PF_RDR: 128 case PF_NORDR: 129 return (PF_RULESET_RDR); 130 break; 131 default: 132 return (PF_RULESET_MAX); 133 break; 134 } 135 } 136 137 static struct pf_kanchor * 138 pf_find_kanchor(const char *path) 139 { 140 struct pf_kanchor *key, *found; 141 142 key = (struct pf_kanchor *)rs_malloc(sizeof(*key)); 143 if (key == NULL) 144 return (NULL); 145 strlcpy(key->path, path, sizeof(key->path)); 146 found = RB_FIND(pf_kanchor_global, &V_pf_anchors, key); 147 rs_free(key); 148 return (found); 149 } 150 151 void 152 pf_init_kruleset(struct pf_kruleset *ruleset) 153 { 154 int i; 155 156 memset(ruleset, 0, sizeof(struct pf_kruleset)); 157 for (i = 0; i < PF_RULESET_MAX; i++) { 158 TAILQ_INIT(&ruleset->rules[i].queues[0]); 159 TAILQ_INIT(&ruleset->rules[i].queues[1]); 160 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 161 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 162 } 163 } 164 165 void 166 pf_init_keth(struct pf_keth_ruleset *rs) 167 { 168 169 bzero(rs, sizeof(*rs)); 170 TAILQ_INIT(&rs->rules[0]); 171 TAILQ_INIT(&rs->rules[1]); 172 rs->active.rules = &rs->rules[0]; 173 rs->active.open = 0; 174 rs->inactive.rules = &rs->rules[1]; 175 rs->inactive.open = 0; 176 177 rs->vnet = curvnet; 178 } 179 180 struct pf_kruleset * 181 pf_find_kruleset(const char *path) 182 { 183 struct pf_kanchor *anchor; 184 185 while (*path == '/') 186 path++; 187 if (!*path) 188 return (&pf_main_ruleset); 189 anchor = pf_find_kanchor(path); 190 if (anchor == NULL) 191 return (NULL); 192 else 193 return (&anchor->ruleset); 194 } 195 196 struct pf_kruleset * 197 pf_find_or_create_kruleset(const char *path) 198 { 199 char *p, *q, *r; 200 struct pf_kruleset *ruleset; 201 struct pf_kanchor *anchor = NULL, *dup, *parent = NULL; 202 203 if (path[0] == 0) 204 return (&pf_main_ruleset); 205 while (*path == '/') 206 path++; 207 ruleset = pf_find_kruleset(path); 208 if (ruleset != NULL) 209 return (ruleset); 210 p = (char *)rs_malloc(MAXPATHLEN); 211 if (p == NULL) 212 return (NULL); 213 strlcpy(p, path, MAXPATHLEN); 214 while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 215 *q = 0; 216 if ((ruleset = pf_find_kruleset(p)) != NULL) { 217 parent = ruleset->anchor; 218 break; 219 } 220 } 221 if (q == NULL) 222 q = p; 223 else 224 q++; 225 strlcpy(p, path, MAXPATHLEN); 226 if (!*q) { 227 rs_free(p); 228 return (NULL); 229 } 230 while ((r = strchr(q, '/')) != NULL || *q) { 231 if (r != NULL) 232 *r = 0; 233 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 234 (parent != NULL && strlen(parent->path) >= 235 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 236 rs_free(p); 237 return (NULL); 238 } 239 anchor = (struct pf_kanchor *)rs_malloc(sizeof(*anchor)); 240 if (anchor == NULL) { 241 rs_free(p); 242 return (NULL); 243 } 244 RB_INIT(&anchor->children); 245 strlcpy(anchor->name, q, sizeof(anchor->name)); 246 if (parent != NULL) { 247 strlcpy(anchor->path, parent->path, 248 sizeof(anchor->path)); 249 strlcat(anchor->path, "/", sizeof(anchor->path)); 250 } 251 strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 252 if ((dup = RB_INSERT(pf_kanchor_global, &V_pf_anchors, anchor)) != 253 NULL) { 254 printf("pf_find_or_create_ruleset: RB_INSERT1 " 255 "'%s' '%s' collides with '%s' '%s'\n", 256 anchor->path, anchor->name, dup->path, dup->name); 257 rs_free(anchor); 258 rs_free(p); 259 return (NULL); 260 } 261 if (parent != NULL) { 262 anchor->parent = parent; 263 if ((dup = RB_INSERT(pf_kanchor_node, &parent->children, 264 anchor)) != NULL) { 265 printf("pf_find_or_create_ruleset: " 266 "RB_INSERT2 '%s' '%s' collides with " 267 "'%s' '%s'\n", anchor->path, anchor->name, 268 dup->path, dup->name); 269 RB_REMOVE(pf_kanchor_global, &V_pf_anchors, 270 anchor); 271 rs_free(anchor); 272 rs_free(p); 273 return (NULL); 274 } 275 } 276 pf_init_kruleset(&anchor->ruleset); 277 anchor->ruleset.anchor = anchor; 278 parent = anchor; 279 if (r != NULL) 280 q = r + 1; 281 else 282 *q = 0; 283 } 284 rs_free(p); 285 return (&anchor->ruleset); 286 } 287 288 void 289 pf_remove_if_empty_kruleset(struct pf_kruleset *ruleset) 290 { 291 struct pf_kanchor *parent; 292 int i; 293 294 while (ruleset != NULL) { 295 if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || 296 !RB_EMPTY(&ruleset->anchor->children) || 297 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 298 ruleset->topen) 299 return; 300 for (i = 0; i < PF_RULESET_MAX; ++i) 301 if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 302 !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || 303 ruleset->rules[i].inactive.open) 304 return; 305 RB_REMOVE(pf_kanchor_global, &V_pf_anchors, ruleset->anchor); 306 if ((parent = ruleset->anchor->parent) != NULL) 307 RB_REMOVE(pf_kanchor_node, &parent->children, 308 ruleset->anchor); 309 rs_free(ruleset->anchor); 310 if (parent == NULL) 311 return; 312 ruleset = &parent->ruleset; 313 } 314 } 315 316 int 317 pf_kanchor_setup(struct pf_krule *r, const struct pf_kruleset *s, 318 const char *name) 319 { 320 char *p, *path; 321 struct pf_kruleset *ruleset; 322 323 r->anchor = NULL; 324 r->anchor_relative = 0; 325 r->anchor_wildcard = 0; 326 if (!name[0]) 327 return (0); 328 path = (char *)rs_malloc(MAXPATHLEN); 329 if (path == NULL) 330 return (1); 331 if (name[0] == '/') 332 strlcpy(path, name + 1, MAXPATHLEN); 333 else { 334 /* relative path */ 335 r->anchor_relative = 1; 336 if (s->anchor == NULL || !s->anchor->path[0]) 337 path[0] = 0; 338 else 339 strlcpy(path, s->anchor->path, MAXPATHLEN); 340 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 341 if (!path[0]) { 342 DPFPRINTF("pf_anchor_setup: .. beyond root\n"); 343 rs_free(path); 344 return (1); 345 } 346 if ((p = strrchr(path, '/')) != NULL) 347 *p = 0; 348 else 349 path[0] = 0; 350 r->anchor_relative++; 351 name += 3; 352 } 353 if (path[0]) 354 strlcat(path, "/", MAXPATHLEN); 355 strlcat(path, name, MAXPATHLEN); 356 } 357 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 358 r->anchor_wildcard = 1; 359 *p = 0; 360 } 361 ruleset = pf_find_or_create_kruleset(path); 362 rs_free(path); 363 if (ruleset == NULL || ruleset->anchor == NULL) { 364 DPFPRINTF("pf_anchor_setup: ruleset\n"); 365 return (1); 366 } 367 r->anchor = ruleset->anchor; 368 r->anchor->refcnt++; 369 return (0); 370 } 371 372 int 373 pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r, 374 nvlist_t *nvl) 375 { 376 char anchor_call[MAXPATHLEN] = { 0 }; 377 378 if (r->anchor == NULL) 379 goto done; 380 if (!r->anchor_relative) { 381 strlcpy(anchor_call, "/", sizeof(anchor_call)); 382 strlcat(anchor_call, r->anchor->path, 383 sizeof(anchor_call)); 384 } else { 385 char a[MAXPATHLEN]; 386 char *p; 387 int i; 388 if (rs->anchor == NULL) 389 a[0] = 0; 390 else 391 strlcpy(a, rs->anchor->path, MAXPATHLEN); 392 for (i = 1; i < r->anchor_relative; ++i) { 393 if ((p = strrchr(a, '/')) == NULL) 394 p = a; 395 *p = 0; 396 strlcat(anchor_call, "../", 397 sizeof(anchor_call)); 398 } 399 if (strncmp(a, r->anchor->path, strlen(a))) { 400 printf("pf_anchor_copyout: '%s' '%s'\n", a, 401 r->anchor->path); 402 return (1); 403 } 404 if (strlen(r->anchor->path) > strlen(a)) 405 strlcat(anchor_call, r->anchor->path + (a[0] ? 406 strlen(a) + 1 : 0), sizeof(anchor_call)); 407 408 } 409 if (r->anchor_wildcard) 410 strlcat(anchor_call, anchor_call[0] ? "/*" : "*", 411 sizeof(anchor_call)); 412 413 done: 414 nvlist_add_string(nvl, "anchor_call", anchor_call); 415 416 return (0); 417 } 418 419 int 420 pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs, 421 const struct pf_keth_rule *r, nvlist_t *nvl) 422 { 423 char anchor_call[MAXPATHLEN] = { 0 }; 424 425 if (r->anchor == NULL) 426 goto done; 427 if (!r->anchor_relative) { 428 strlcpy(anchor_call, "/", sizeof(anchor_call)); 429 strlcat(anchor_call, r->anchor->path, 430 sizeof(anchor_call)); 431 } else { 432 char a[MAXPATHLEN]; 433 char *p; 434 int i; 435 if (rs->anchor == NULL) 436 a[0] = 0; 437 else 438 strlcpy(a, rs->anchor->path, MAXPATHLEN); 439 for (i = 1; i < r->anchor_relative; ++i) { 440 if ((p = strrchr(a, '/')) == NULL) 441 p = a; 442 *p = 0; 443 strlcat(anchor_call, "../", 444 sizeof(anchor_call)); 445 } 446 if (strncmp(a, r->anchor->path, strlen(a))) { 447 printf("%s(): '%s' '%s'\n", __func__, a, 448 r->anchor->path); 449 return (1); 450 } 451 if (strlen(r->anchor->path) > strlen(a)) 452 strlcat(anchor_call, r->anchor->path + (a[0] ? 453 strlen(a) + 1 : 0), sizeof(anchor_call)); 454 455 } 456 if (r->anchor_wildcard) 457 strlcat(anchor_call, anchor_call[0] ? "/*" : "*", 458 sizeof(anchor_call)); 459 460 done: 461 nvlist_add_string(nvl, "anchor_call", anchor_call); 462 463 return (0); 464 } 465 466 int 467 pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r, 468 struct pfioc_rule *pr) 469 { 470 pr->anchor_call[0] = 0; 471 if (r->anchor == NULL) 472 return (0); 473 if (!r->anchor_relative) { 474 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 475 strlcat(pr->anchor_call, r->anchor->path, 476 sizeof(pr->anchor_call)); 477 } else { 478 char *a, *p; 479 int i; 480 481 a = (char *)rs_malloc(MAXPATHLEN); 482 if (a == NULL) 483 return (1); 484 if (rs->anchor == NULL) 485 a[0] = 0; 486 else 487 strlcpy(a, rs->anchor->path, MAXPATHLEN); 488 for (i = 1; i < r->anchor_relative; ++i) { 489 if ((p = strrchr(a, '/')) == NULL) 490 p = a; 491 *p = 0; 492 strlcat(pr->anchor_call, "../", 493 sizeof(pr->anchor_call)); 494 } 495 if (strncmp(a, r->anchor->path, strlen(a))) { 496 printf("pf_anchor_copyout: '%s' '%s'\n", a, 497 r->anchor->path); 498 rs_free(a); 499 return (1); 500 } 501 if (strlen(r->anchor->path) > strlen(a)) 502 strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 503 strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 504 rs_free(a); 505 } 506 if (r->anchor_wildcard) 507 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 508 sizeof(pr->anchor_call)); 509 return (0); 510 } 511 512 void 513 pf_kanchor_remove(struct pf_krule *r) 514 { 515 if (r->anchor == NULL) 516 return; 517 if (r->anchor->refcnt <= 0) { 518 printf("pf_anchor_remove: broken refcount\n"); 519 r->anchor = NULL; 520 return; 521 } 522 if (!--r->anchor->refcnt) 523 pf_remove_if_empty_kruleset(&r->anchor->ruleset); 524 r->anchor = NULL; 525 } 526 527 struct pf_keth_ruleset * 528 pf_find_keth_ruleset(const char *path) 529 { 530 struct pf_keth_anchor *anchor; 531 532 while (*path == '/') 533 path++; 534 if (!*path) 535 return (V_pf_keth); 536 anchor = pf_find_keth_anchor(path); 537 if (anchor == NULL) 538 return (NULL); 539 else 540 return (&anchor->ruleset); 541 } 542 543 static struct pf_keth_anchor * 544 _pf_find_keth_anchor(struct pf_keth_ruleset *rs, const char *path) 545 { 546 struct pf_keth_anchor *key, *found; 547 548 key = (struct pf_keth_anchor *)rs_malloc(sizeof(*key)); 549 if (key == NULL) 550 return (NULL); 551 strlcpy(key->path, path, sizeof(key->path)); 552 found = RB_FIND(pf_keth_anchor_global, &V_pf_keth_anchors, key); 553 rs_free(key); 554 return (found); 555 } 556 557 struct pf_keth_anchor * 558 pf_find_keth_anchor(const char *path) 559 { 560 return (_pf_find_keth_anchor(V_pf_keth, path)); 561 } 562 563 struct pf_keth_ruleset * 564 pf_find_or_create_keth_ruleset(const char *path) 565 { 566 char *p, *q, *r; 567 struct pf_keth_anchor *anchor = NULL, *dup = NULL, *parent = NULL; 568 struct pf_keth_ruleset *ruleset; 569 570 if (path[0] == 0) 571 return (V_pf_keth); 572 while (*path == '/') 573 path++; 574 ruleset = pf_find_keth_ruleset(path); 575 if (ruleset != NULL) 576 return (ruleset); 577 p = (char *)rs_malloc(MAXPATHLEN); 578 if (p == NULL) 579 return (NULL); 580 strlcpy(p, path, MAXPATHLEN); 581 while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 582 *q = 0; 583 if ((ruleset = pf_find_keth_ruleset(p)) != NULL) { 584 parent = ruleset->anchor; 585 break; 586 } 587 } 588 if (q == NULL) 589 q = p; 590 else 591 q++; 592 strlcpy(p, path, MAXPATHLEN); 593 if (!*q) { 594 rs_free(p); 595 return (NULL); 596 } 597 while ((r = strchr(q, '/')) != NULL || *q) { 598 if (r != NULL) 599 *r = 0; 600 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 601 (parent != NULL && strlen(parent->path) >= 602 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 603 rs_free(p); 604 return (NULL); 605 } 606 anchor = (struct pf_keth_anchor *)rs_malloc(sizeof(*anchor)); 607 if (anchor == NULL) { 608 rs_free(p); 609 return (NULL); 610 } 611 RB_INIT(&anchor->children); 612 strlcpy(anchor->name, q, sizeof(anchor->name)); 613 if (parent != NULL) { 614 strlcpy(anchor->path, parent->path, 615 sizeof(anchor->path)); 616 strlcat(anchor->path, "/", sizeof(anchor->path)); 617 } 618 strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 619 if ((dup = RB_INSERT(pf_keth_anchor_global, &V_pf_keth_anchors, anchor)) != 620 NULL) { 621 printf("%s: RB_INSERT1 " 622 "'%s' '%s' collides with '%s' '%s'\n", __func__, 623 anchor->path, anchor->name, dup->path, dup->name); 624 rs_free(anchor); 625 rs_free(p); 626 return (NULL); 627 } 628 if (parent != NULL) { 629 anchor->parent = parent; 630 if ((dup = RB_INSERT(pf_keth_anchor_node, &parent->children, 631 anchor)) != NULL) { 632 printf("%s: " 633 "RB_INSERT2 '%s' '%s' collides with " 634 "'%s' '%s'\n", __func__, anchor->path, 635 anchor->name, dup->path, dup->name); 636 RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, 637 anchor); 638 rs_free(anchor); 639 rs_free(p); 640 return (NULL); 641 } 642 } 643 pf_init_keth(&anchor->ruleset); 644 anchor->ruleset.anchor = anchor; 645 parent = anchor; 646 if (r != NULL) 647 q = r + 1; 648 else 649 *q = 0; 650 } 651 rs_free(p); 652 return (&anchor->ruleset); 653 } 654 655 int 656 pf_keth_anchor_setup(struct pf_keth_rule *r, const struct pf_keth_ruleset *s, 657 const char *name) 658 { 659 char *p, *path; 660 struct pf_keth_ruleset *ruleset; 661 662 r->anchor = NULL; 663 r->anchor_relative = 0; 664 r->anchor_wildcard = 0; 665 if (!name[0]) 666 return (0); 667 path = (char *)rs_malloc(MAXPATHLEN); 668 if (path == NULL) 669 return (1); 670 if (name[0] == '/') 671 strlcpy(path, name + 1, MAXPATHLEN); 672 else { 673 /* relative path */ 674 r->anchor_relative = 1; 675 if (s->anchor == NULL || !s->anchor->path[0]) 676 path[0] = 0; 677 else 678 strlcpy(path, s->anchor->path, MAXPATHLEN); 679 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 680 if (!path[0]) { 681 DPFPRINTF("pf_anchor_setup: .. beyond root\n"); 682 rs_free(path); 683 return (1); 684 } 685 if ((p = strrchr(path, '/')) != NULL) 686 *p = 0; 687 else 688 path[0] = 0; 689 r->anchor_relative++; 690 name += 3; 691 } 692 if (path[0]) 693 strlcat(path, "/", MAXPATHLEN); 694 strlcat(path, name, MAXPATHLEN); 695 } 696 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 697 r->anchor_wildcard = 1; 698 *p = 0; 699 } 700 ruleset = pf_find_or_create_keth_ruleset(path); 701 rs_free(path); 702 if (ruleset == NULL || ruleset->anchor == NULL) { 703 DPFPRINTF("pf_anchor_setup: ruleset\n"); 704 return (1); 705 } 706 r->anchor = ruleset->anchor; 707 r->anchor->refcnt++; 708 return (0); 709 } 710 711 void 712 pf_keth_anchor_remove(struct pf_keth_rule *r) 713 { 714 if (r->anchor == NULL) 715 return; 716 if (r->anchor->refcnt <= 0) { 717 printf("%s: broken refcount\n", __func__); 718 r->anchor = NULL; 719 return; 720 } 721 if (!--r->anchor->refcnt) 722 pf_remove_if_empty_keth_ruleset(&r->anchor->ruleset); 723 r->anchor = NULL; 724 } 725 726 void 727 pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset *ruleset) 728 { 729 struct pf_keth_anchor *parent; 730 int i; 731 732 while (ruleset != NULL) { 733 if (ruleset == V_pf_keth || ruleset->anchor == NULL || 734 !RB_EMPTY(&ruleset->anchor->children) || 735 ruleset->anchor->refcnt > 0) 736 return; 737 for (i = 0; i < PF_RULESET_MAX; ++i) 738 if (!TAILQ_EMPTY(ruleset->active.rules) || 739 !TAILQ_EMPTY(ruleset->inactive.rules) || 740 ruleset->inactive.open) 741 return; 742 RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, ruleset->anchor); 743 if ((parent = ruleset->anchor->parent) != NULL) 744 RB_REMOVE(pf_keth_anchor_node, &parent->children, 745 ruleset->anchor); 746 rs_free(ruleset->anchor); 747 if (parent == NULL) 748 return; 749 ruleset = &parent->ruleset; 750 } 751 } 752