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