xref: /freebsd/sys/netpfil/pf/pf_ruleset.c (revision 3494f7c0)
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
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
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
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 *
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
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
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 *
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 
193 struct pf_kruleset *
194 pf_find_or_create_kruleset(const char *path)
195 {
196 	char			*p, *q, *r;
197 	struct pf_kruleset	*ruleset;
198 	struct pf_kanchor	*anchor = NULL, *dup, *parent = NULL;
199 
200 	if (path[0] == 0)
201 		return (&pf_main_ruleset);
202 	while (*path == '/')
203 		path++;
204 	ruleset = pf_find_kruleset(path);
205 	if (ruleset != NULL)
206 		return (ruleset);
207 	p = (char *)rs_malloc(MAXPATHLEN);
208 	if (p == NULL)
209 		return (NULL);
210 	strlcpy(p, path, MAXPATHLEN);
211 	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
212 		*q = 0;
213 		if ((ruleset = pf_find_kruleset(p)) != NULL) {
214 			parent = ruleset->anchor;
215 			break;
216 		}
217 	}
218 	if (q == NULL)
219 		q = p;
220 	else
221 		q++;
222 	strlcpy(p, path, MAXPATHLEN);
223 	if (!*q) {
224 		rs_free(p);
225 		return (NULL);
226 	}
227 	while ((r = strchr(q, '/')) != NULL || *q) {
228 		if (r != NULL)
229 			*r = 0;
230 		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
231 		    (parent != NULL && strlen(parent->path) >=
232 		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
233 			rs_free(p);
234 			return (NULL);
235 		}
236 		anchor = (struct pf_kanchor *)rs_malloc(sizeof(*anchor));
237 		if (anchor == NULL) {
238 			rs_free(p);
239 			return (NULL);
240 		}
241 		RB_INIT(&anchor->children);
242 		strlcpy(anchor->name, q, sizeof(anchor->name));
243 		if (parent != NULL) {
244 			strlcpy(anchor->path, parent->path,
245 			    sizeof(anchor->path));
246 			strlcat(anchor->path, "/", sizeof(anchor->path));
247 		}
248 		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
249 		if ((dup = RB_INSERT(pf_kanchor_global, &V_pf_anchors, anchor)) !=
250 		    NULL) {
251 			printf("pf_find_or_create_ruleset: RB_INSERT1 "
252 			    "'%s' '%s' collides with '%s' '%s'\n",
253 			    anchor->path, anchor->name, dup->path, dup->name);
254 			rs_free(anchor);
255 			rs_free(p);
256 			return (NULL);
257 		}
258 		if (parent != NULL) {
259 			anchor->parent = parent;
260 			if ((dup = RB_INSERT(pf_kanchor_node, &parent->children,
261 			    anchor)) != NULL) {
262 				printf("pf_find_or_create_ruleset: "
263 				    "RB_INSERT2 '%s' '%s' collides with "
264 				    "'%s' '%s'\n", anchor->path, anchor->name,
265 				    dup->path, dup->name);
266 				RB_REMOVE(pf_kanchor_global, &V_pf_anchors,
267 				    anchor);
268 				rs_free(anchor);
269 				rs_free(p);
270 				return (NULL);
271 			}
272 		}
273 		pf_init_kruleset(&anchor->ruleset);
274 		anchor->ruleset.anchor = anchor;
275 		parent = anchor;
276 		if (r != NULL)
277 			q = r + 1;
278 		else
279 			*q = 0;
280 	}
281 	rs_free(p);
282 	return (&anchor->ruleset);
283 }
284 
285 void
286 pf_remove_if_empty_kruleset(struct pf_kruleset *ruleset)
287 {
288 	struct pf_kanchor	*parent;
289 	int			 i;
290 
291 	while (ruleset != NULL) {
292 		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
293 		    !RB_EMPTY(&ruleset->anchor->children) ||
294 		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
295 		    ruleset->topen)
296 			return;
297 		for (i = 0; i < PF_RULESET_MAX; ++i)
298 			if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
299 			    !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
300 			    ruleset->rules[i].inactive.open)
301 				return;
302 		RB_REMOVE(pf_kanchor_global, &V_pf_anchors, ruleset->anchor);
303 		if ((parent = ruleset->anchor->parent) != NULL)
304 			RB_REMOVE(pf_kanchor_node, &parent->children,
305 			    ruleset->anchor);
306 		rs_free(ruleset->anchor);
307 		if (parent == NULL)
308 			return;
309 		ruleset = &parent->ruleset;
310 	}
311 }
312 
313 int
314 pf_kanchor_setup(struct pf_krule *r, const struct pf_kruleset *s,
315     const char *name)
316 {
317 	char			*p, *path;
318 	struct pf_kruleset	*ruleset;
319 
320 	r->anchor = NULL;
321 	r->anchor_relative = 0;
322 	r->anchor_wildcard = 0;
323 	if (!name[0])
324 		return (0);
325 	path = (char *)rs_malloc(MAXPATHLEN);
326 	if (path == NULL)
327 		return (1);
328 	if (name[0] == '/')
329 		strlcpy(path, name + 1, MAXPATHLEN);
330 	else {
331 		/* relative path */
332 		r->anchor_relative = 1;
333 		if (s->anchor == NULL || !s->anchor->path[0])
334 			path[0] = 0;
335 		else
336 			strlcpy(path, s->anchor->path, MAXPATHLEN);
337 		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
338 			if (!path[0]) {
339 				DPFPRINTF("pf_anchor_setup: .. beyond root\n");
340 				rs_free(path);
341 				return (1);
342 			}
343 			if ((p = strrchr(path, '/')) != NULL)
344 				*p = 0;
345 			else
346 				path[0] = 0;
347 			r->anchor_relative++;
348 			name += 3;
349 		}
350 		if (path[0])
351 			strlcat(path, "/", MAXPATHLEN);
352 		strlcat(path, name, MAXPATHLEN);
353 	}
354 	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
355 		r->anchor_wildcard = 1;
356 		*p = 0;
357 	}
358 	ruleset = pf_find_or_create_kruleset(path);
359 	rs_free(path);
360 	if (ruleset == NULL || ruleset->anchor == NULL) {
361 		DPFPRINTF("pf_anchor_setup: ruleset\n");
362 		return (1);
363 	}
364 	r->anchor = ruleset->anchor;
365 	r->anchor->refcnt++;
366 	return (0);
367 }
368 
369 int
370 pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r,
371     nvlist_t *nvl)
372 {
373 	char anchor_call[MAXPATHLEN] = { 0 };
374 
375 	if (r->anchor == NULL)
376 		goto done;
377 	if (!r->anchor_relative) {
378 		strlcpy(anchor_call, "/", sizeof(anchor_call));
379 		strlcat(anchor_call, r->anchor->path,
380 		    sizeof(anchor_call));
381 	} else {
382 		char	 a[MAXPATHLEN];
383 		char	*p;
384 		int	 i;
385 		if (rs->anchor == NULL)
386 			a[0] = 0;
387 		else
388 			strlcpy(a, rs->anchor->path, MAXPATHLEN);
389 		for (i = 1; i < r->anchor_relative; ++i) {
390 			if ((p = strrchr(a, '/')) == NULL)
391 				p = a;
392 			*p = 0;
393 			strlcat(anchor_call, "../",
394 			    sizeof(anchor_call));
395 		}
396 		if (strncmp(a, r->anchor->path, strlen(a))) {
397 			printf("pf_anchor_copyout: '%s' '%s'\n", a,
398 			    r->anchor->path);
399 			return (1);
400 		}
401 		if (strlen(r->anchor->path) > strlen(a))
402 			strlcat(anchor_call, r->anchor->path + (a[0] ?
403 			    strlen(a) + 1 : 0), sizeof(anchor_call));
404 
405 	}
406 	if (r->anchor_wildcard)
407 		strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
408 		    sizeof(anchor_call));
409 
410 done:
411 	nvlist_add_string(nvl, "anchor_call", anchor_call);
412 
413 	return (0);
414 }
415 
416 int
417 pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs,
418     const struct pf_keth_rule *r, nvlist_t *nvl)
419 {
420 	char anchor_call[MAXPATHLEN] = { 0 };
421 
422 	if (r->anchor == NULL)
423 		goto done;
424 	if (!r->anchor_relative) {
425 		strlcpy(anchor_call, "/", sizeof(anchor_call));
426 		strlcat(anchor_call, r->anchor->path,
427 		    sizeof(anchor_call));
428 	} else {
429 		char	 a[MAXPATHLEN];
430 		char	*p;
431 		int	 i;
432 		if (rs->anchor == NULL)
433 			a[0] = 0;
434 		else
435 			strlcpy(a, rs->anchor->path, MAXPATHLEN);
436 		for (i = 1; i < r->anchor_relative; ++i) {
437 			if ((p = strrchr(a, '/')) == NULL)
438 				p = a;
439 			*p = 0;
440 			strlcat(anchor_call, "../",
441 			    sizeof(anchor_call));
442 		}
443 		if (strncmp(a, r->anchor->path, strlen(a))) {
444 			printf("%s(): '%s' '%s'\n", __func__, a,
445 			    r->anchor->path);
446 			return (1);
447 		}
448 		if (strlen(r->anchor->path) > strlen(a))
449 			strlcat(anchor_call, r->anchor->path + (a[0] ?
450 			    strlen(a) + 1 : 0), sizeof(anchor_call));
451 
452 	}
453 	if (r->anchor_wildcard)
454 		strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
455 		    sizeof(anchor_call));
456 
457 done:
458 	nvlist_add_string(nvl, "anchor_call", anchor_call);
459 
460 	return (0);
461 }
462 
463 int
464 pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
465     struct pfioc_rule *pr)
466 {
467 	pr->anchor_call[0] = 0;
468 	if (r->anchor == NULL)
469 		return (0);
470 	if (!r->anchor_relative) {
471 		strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
472 		strlcat(pr->anchor_call, r->anchor->path,
473 		    sizeof(pr->anchor_call));
474 	} else {
475 		char	*a, *p;
476 		int	 i;
477 
478 		a = (char *)rs_malloc(MAXPATHLEN);
479 		if (a == NULL)
480 			return (1);
481 		if (rs->anchor == NULL)
482 			a[0] = 0;
483 		else
484 			strlcpy(a, rs->anchor->path, MAXPATHLEN);
485 		for (i = 1; i < r->anchor_relative; ++i) {
486 			if ((p = strrchr(a, '/')) == NULL)
487 				p = a;
488 			*p = 0;
489 			strlcat(pr->anchor_call, "../",
490 			    sizeof(pr->anchor_call));
491 		}
492 		if (strncmp(a, r->anchor->path, strlen(a))) {
493 			printf("pf_anchor_copyout: '%s' '%s'\n", a,
494 			    r->anchor->path);
495 			rs_free(a);
496 			return (1);
497 		}
498 		if (strlen(r->anchor->path) > strlen(a))
499 			strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
500 			    strlen(a) + 1 : 0), sizeof(pr->anchor_call));
501 		rs_free(a);
502 	}
503 	if (r->anchor_wildcard)
504 		strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
505 		    sizeof(pr->anchor_call));
506 	return (0);
507 }
508 
509 void
510 pf_kanchor_remove(struct pf_krule *r)
511 {
512 	if (r->anchor == NULL)
513 		return;
514 	if (r->anchor->refcnt <= 0) {
515 		printf("pf_anchor_remove: broken refcount\n");
516 		r->anchor = NULL;
517 		return;
518 	}
519 	if (!--r->anchor->refcnt)
520 		pf_remove_if_empty_kruleset(&r->anchor->ruleset);
521 	r->anchor = NULL;
522 }
523 
524 struct pf_keth_ruleset *
525 pf_find_keth_ruleset(const char *path)
526 {
527 	struct pf_keth_anchor	*anchor;
528 
529 	while (*path == '/')
530 		path++;
531 	if (!*path)
532 		return (V_pf_keth);
533 	anchor = pf_find_keth_anchor(path);
534 	if (anchor == NULL)
535 		return (NULL);
536 	else
537 		return (&anchor->ruleset);
538 }
539 
540 static struct pf_keth_anchor *
541 _pf_find_keth_anchor(struct pf_keth_ruleset *rs, const char *path)
542 {
543 	struct pf_keth_anchor	*key, *found;
544 
545 	key = (struct pf_keth_anchor *)rs_malloc(sizeof(*key));
546 	if (key == NULL)
547 		return (NULL);
548 	strlcpy(key->path, path, sizeof(key->path));
549 	found = RB_FIND(pf_keth_anchor_global, &V_pf_keth_anchors, key);
550 	rs_free(key);
551 	return (found);
552 }
553 
554 struct pf_keth_anchor *
555 pf_find_keth_anchor(const char *path)
556 {
557 	return (_pf_find_keth_anchor(V_pf_keth, path));
558 }
559 
560 struct pf_keth_ruleset *
561 pf_find_or_create_keth_ruleset(const char *path)
562 {
563 	char			*p, *q, *r;
564 	struct pf_keth_anchor	*anchor = NULL, *dup = NULL, *parent = NULL;
565 	struct pf_keth_ruleset	*ruleset;
566 
567 	if (path[0] == 0)
568 		return (V_pf_keth);
569 	while (*path == '/')
570 		path++;
571 	ruleset = pf_find_keth_ruleset(path);
572 	if (ruleset != NULL)
573 		return (ruleset);
574 	p = (char *)rs_malloc(MAXPATHLEN);
575 	if (p == NULL)
576 		return (NULL);
577 	strlcpy(p, path, MAXPATHLEN);
578 	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
579 		*q = 0;
580 		if ((ruleset = pf_find_keth_ruleset(p)) != NULL) {
581 			parent = ruleset->anchor;
582 			break;
583 		}
584 	}
585 	if (q == NULL)
586 		q = p;
587 	else
588 		q++;
589 	strlcpy(p, path, MAXPATHLEN);
590 	if (!*q) {
591 		rs_free(p);
592 		return (NULL);
593 	}
594 	while ((r = strchr(q, '/')) != NULL || *q) {
595 		if (r != NULL)
596 			*r = 0;
597 		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
598 		    (parent != NULL && strlen(parent->path) >=
599 		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
600 			rs_free(p);
601 			return (NULL);
602 		}
603 		anchor = (struct pf_keth_anchor *)rs_malloc(sizeof(*anchor));
604 		if (anchor == NULL) {
605 			rs_free(p);
606 			return (NULL);
607 		}
608 		RB_INIT(&anchor->children);
609 		strlcpy(anchor->name, q, sizeof(anchor->name));
610 		if (parent != NULL) {
611 			strlcpy(anchor->path, parent->path,
612 			    sizeof(anchor->path));
613 			strlcat(anchor->path, "/", sizeof(anchor->path));
614 		}
615 		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
616 		if ((dup = RB_INSERT(pf_keth_anchor_global, &V_pf_keth_anchors, anchor)) !=
617 		    NULL) {
618 			printf("%s: RB_INSERT1 "
619 			    "'%s' '%s' collides with '%s' '%s'\n", __func__,
620 			    anchor->path, anchor->name, dup->path, dup->name);
621 			rs_free(anchor);
622 			rs_free(p);
623 			return (NULL);
624 		}
625 		if (parent != NULL) {
626 			anchor->parent = parent;
627 			if ((dup = RB_INSERT(pf_keth_anchor_node, &parent->children,
628 			    anchor)) != NULL) {
629 				printf("%s: "
630 				    "RB_INSERT2 '%s' '%s' collides with "
631 				    "'%s' '%s'\n", __func__, anchor->path,
632 				    anchor->name, dup->path, dup->name);
633 				RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors,
634 				    anchor);
635 				rs_free(anchor);
636 				rs_free(p);
637 				return (NULL);
638 			}
639 		}
640 		pf_init_keth(&anchor->ruleset);
641 		anchor->ruleset.anchor = anchor;
642 		parent = anchor;
643 		if (r != NULL)
644 			q = r + 1;
645 		else
646 			*q = 0;
647 	}
648 	rs_free(p);
649 	return (&anchor->ruleset);
650 }
651 
652 int
653 pf_keth_anchor_setup(struct pf_keth_rule *r, const struct pf_keth_ruleset *s,
654     const char *name)
655 {
656 	char			*p, *path;
657 	struct pf_keth_ruleset	*ruleset;
658 
659 	r->anchor = NULL;
660 	r->anchor_relative = 0;
661 	r->anchor_wildcard = 0;
662 	if (!name[0])
663 		return (0);
664 	path = (char *)rs_malloc(MAXPATHLEN);
665 	if (path == NULL)
666 		return (1);
667 	if (name[0] == '/')
668 		strlcpy(path, name + 1, MAXPATHLEN);
669 	else {
670 		/* relative path */
671 		r->anchor_relative = 1;
672 		if (s->anchor == NULL || !s->anchor->path[0])
673 			path[0] = 0;
674 		else
675 			strlcpy(path, s->anchor->path, MAXPATHLEN);
676 		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
677 			if (!path[0]) {
678 				DPFPRINTF("pf_anchor_setup: .. beyond root\n");
679 				rs_free(path);
680 				return (1);
681 			}
682 			if ((p = strrchr(path, '/')) != NULL)
683 				*p = 0;
684 			else
685 				path[0] = 0;
686 			r->anchor_relative++;
687 			name += 3;
688 		}
689 		if (path[0])
690 			strlcat(path, "/", MAXPATHLEN);
691 		strlcat(path, name, MAXPATHLEN);
692 	}
693 	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
694 		r->anchor_wildcard = 1;
695 		*p = 0;
696 	}
697 	ruleset = pf_find_or_create_keth_ruleset(path);
698 	rs_free(path);
699 	if (ruleset == NULL || ruleset->anchor == NULL) {
700 		DPFPRINTF("pf_anchor_setup: ruleset\n");
701 		return (1);
702 	}
703 	r->anchor = ruleset->anchor;
704 	r->anchor->refcnt++;
705 	return (0);
706 }
707 
708 void
709 pf_keth_anchor_remove(struct pf_keth_rule *r)
710 {
711 	if (r->anchor == NULL)
712 		return;
713 	if (r->anchor->refcnt <= 0) {
714 		printf("%s: broken refcount\n", __func__);
715 		r->anchor = NULL;
716 		return;
717 	}
718 	if (!--r->anchor->refcnt)
719 		pf_remove_if_empty_keth_ruleset(&r->anchor->ruleset);
720 	r->anchor = NULL;
721 }
722 
723 void
724 pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset *ruleset)
725 {
726 	struct pf_keth_anchor	*parent;
727 	int			 i;
728 
729 	while (ruleset != NULL) {
730 		if (ruleset == V_pf_keth || ruleset->anchor == NULL ||
731 		    !RB_EMPTY(&ruleset->anchor->children) ||
732 		    ruleset->anchor->refcnt > 0)
733 			return;
734 		for (i = 0; i < PF_RULESET_MAX; ++i)
735 			if (!TAILQ_EMPTY(ruleset->active.rules) ||
736 			    !TAILQ_EMPTY(ruleset->inactive.rules) ||
737 			    ruleset->inactive.open)
738 				return;
739 		RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, ruleset->anchor);
740 		if ((parent = ruleset->anchor->parent) != NULL)
741 			RB_REMOVE(pf_keth_anchor_node, &parent->children,
742 			    ruleset->anchor);
743 		rs_free(ruleset->anchor);
744 		if (parent == NULL)
745 			return;
746 		ruleset = &parent->ruleset;
747 	}
748 }
749