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