xref: /freebsd/sys/netpfil/pf/pf_ruleset.c (revision 1d386b48)
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