xref: /freebsd/sys/netpfil/pf/pf_ruleset.c (revision 54a547fcb47c9fce54789a8b091e900b291fd1ba)
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 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("pf_find_or_create_ruleset: RB_INSERT1 "
263 		    "'%s' '%s' collides with '%s' '%s'\n",
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("pf_find_or_create_ruleset: "
274 			    "RB_INSERT2 '%s' '%s' collides with "
275 			    "'%s' '%s'\n", anchor->path, anchor->name,
276 			    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 || ruleset->anchor == NULL ||
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->anchor == NULL) {
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->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 			    anchor_call_len);
445 		}
446 		if (strncmp(a, r->anchor->path, strlen(a))) {
447 			printf("pf_anchor_copyout: '%s' '%s'\n", 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_kanchor_remove(struct pf_krule * r)528 pf_kanchor_remove(struct pf_krule *r)
529 {
530 	if (r->anchor == NULL)
531 		return;
532 	if (r->anchor->refcnt <= 0) {
533 		printf("pf_anchor_remove: broken refcount\n");
534 		r->anchor = NULL;
535 		return;
536 	}
537 	if (!--r->anchor->refcnt)
538 		pf_remove_if_empty_kruleset(&r->anchor->ruleset);
539 	r->anchor = NULL;
540 }
541 
542 struct pf_keth_ruleset *
pf_find_keth_ruleset(const char * path)543 pf_find_keth_ruleset(const char *path)
544 {
545 	struct pf_keth_anchor	*anchor;
546 
547 	while (*path == '/')
548 		path++;
549 	if (!*path)
550 		return (V_pf_keth);
551 	anchor = pf_find_keth_anchor(path);
552 	if (anchor == NULL)
553 		return (NULL);
554 	else
555 		return (&anchor->ruleset);
556 }
557 
558 static struct pf_keth_anchor *
_pf_find_keth_anchor(struct pf_keth_ruleset * rs,const char * path)559 _pf_find_keth_anchor(struct pf_keth_ruleset *rs, const char *path)
560 {
561 	struct pf_keth_anchor	*key, *found;
562 
563 	key = (struct pf_keth_anchor *)rs_malloc(sizeof(*key));
564 	if (key == NULL)
565 		return (NULL);
566 	strlcpy(key->path, path, sizeof(key->path));
567 	found = RB_FIND(pf_keth_anchor_global, &V_pf_keth_anchors, key);
568 	rs_free(key);
569 	return (found);
570 }
571 
572 struct pf_keth_anchor *
pf_find_keth_anchor(const char * path)573 pf_find_keth_anchor(const char *path)
574 {
575 	return (_pf_find_keth_anchor(V_pf_keth, path));
576 }
577 
578 struct pf_keth_ruleset *
pf_find_or_create_keth_ruleset(const char * path)579 pf_find_or_create_keth_ruleset(const char *path)
580 {
581 	char			*p, *q, *r;
582 	struct pf_keth_anchor	*anchor = NULL, *dup = NULL, *parent = NULL;
583 	struct pf_keth_ruleset	*ruleset;
584 
585 	if (path[0] == 0)
586 		return (V_pf_keth);
587 	while (*path == '/')
588 		path++;
589 	ruleset = pf_find_keth_ruleset(path);
590 	if (ruleset != NULL)
591 		return (ruleset);
592 	p = (char *)rs_malloc(MAXPATHLEN);
593 	if (p == NULL)
594 		return (NULL);
595 	strlcpy(p, path, MAXPATHLEN);
596 	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
597 		*q = 0;
598 		if ((ruleset = pf_find_keth_ruleset(p)) != NULL) {
599 			parent = ruleset->anchor;
600 			break;
601 		}
602 	}
603 	if (q == NULL)
604 		q = p;
605 	else
606 		q++;
607 	strlcpy(p, path, MAXPATHLEN);
608 	if (!*q) {
609 		rs_free(p);
610 		return (NULL);
611 	}
612 	while ((r = strchr(q, '/')) != NULL || *q) {
613 		if (r != NULL)
614 			*r = 0;
615 		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
616 		    (parent != NULL && strlen(parent->path) >=
617 		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
618 			rs_free(p);
619 			return (NULL);
620 		}
621 		anchor = (struct pf_keth_anchor *)rs_malloc(sizeof(*anchor));
622 		if (anchor == NULL) {
623 			rs_free(p);
624 			return (NULL);
625 		}
626 		RB_INIT(&anchor->children);
627 		strlcpy(anchor->name, q, sizeof(anchor->name));
628 		if (parent != NULL) {
629 			strlcpy(anchor->path, parent->path,
630 			    sizeof(anchor->path));
631 			strlcat(anchor->path, "/", sizeof(anchor->path));
632 		}
633 		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
634 		if ((dup = RB_INSERT(pf_keth_anchor_global, &V_pf_keth_anchors, anchor)) !=
635 		    NULL) {
636 			printf("%s: RB_INSERT1 "
637 			    "'%s' '%s' collides with '%s' '%s'\n", __func__,
638 			    anchor->path, anchor->name, dup->path, dup->name);
639 			rs_free(anchor);
640 			rs_free(p);
641 			return (NULL);
642 		}
643 		if (parent != NULL) {
644 			anchor->parent = parent;
645 			if ((dup = RB_INSERT(pf_keth_anchor_node, &parent->children,
646 			    anchor)) != NULL) {
647 				printf("%s: "
648 				    "RB_INSERT2 '%s' '%s' collides with "
649 				    "'%s' '%s'\n", __func__, anchor->path,
650 				    anchor->name, dup->path, dup->name);
651 				RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors,
652 				    anchor);
653 				rs_free(anchor);
654 				rs_free(p);
655 				return (NULL);
656 			}
657 		}
658 		pf_init_keth(&anchor->ruleset);
659 		anchor->ruleset.anchor = anchor;
660 		parent = anchor;
661 		if (r != NULL)
662 			q = r + 1;
663 		else
664 			*q = 0;
665 	}
666 	rs_free(p);
667 	return (&anchor->ruleset);
668 }
669 
670 int
pf_keth_anchor_setup(struct pf_keth_rule * r,const struct pf_keth_ruleset * s,const char * name)671 pf_keth_anchor_setup(struct pf_keth_rule *r, const struct pf_keth_ruleset *s,
672     const char *name)
673 {
674 	char			*p, *path;
675 	struct pf_keth_ruleset	*ruleset;
676 
677 	r->anchor = NULL;
678 	r->anchor_relative = 0;
679 	r->anchor_wildcard = 0;
680 	if (!name[0])
681 		return (0);
682 	path = (char *)rs_malloc(MAXPATHLEN);
683 	if (path == NULL)
684 		return (1);
685 	if (name[0] == '/')
686 		strlcpy(path, name + 1, MAXPATHLEN);
687 	else {
688 		/* relative path */
689 		r->anchor_relative = 1;
690 		if (s->anchor == NULL || !s->anchor->path[0])
691 			path[0] = 0;
692 		else
693 			strlcpy(path, s->anchor->path, MAXPATHLEN);
694 		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
695 			if (!path[0]) {
696 				DPFPRINTF("%s: .. beyond root\n", __func__);
697 				rs_free(path);
698 				return (1);
699 			}
700 			if ((p = strrchr(path, '/')) != NULL)
701 				*p = 0;
702 			else
703 				path[0] = 0;
704 			r->anchor_relative++;
705 			name += 3;
706 		}
707 		if (path[0])
708 			strlcat(path, "/", MAXPATHLEN);
709 		strlcat(path, name, MAXPATHLEN);
710 	}
711 	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
712 		r->anchor_wildcard = 1;
713 		*p = 0;
714 	}
715 	ruleset = pf_find_or_create_keth_ruleset(path);
716 	rs_free(path);
717 	if (ruleset == NULL || ruleset->anchor == NULL) {
718 		DPFPRINTF("%s: ruleset\n", __func__);
719 		return (1);
720 	}
721 	r->anchor = ruleset->anchor;
722 	r->anchor->refcnt++;
723 	return (0);
724 }
725 
726 void
pf_keth_anchor_remove(struct pf_keth_rule * r)727 pf_keth_anchor_remove(struct pf_keth_rule *r)
728 {
729 	if (r->anchor == NULL)
730 		return;
731 	if (r->anchor->refcnt <= 0) {
732 		printf("%s: broken refcount\n", __func__);
733 		r->anchor = NULL;
734 		return;
735 	}
736 	if (!--r->anchor->refcnt)
737 		pf_remove_if_empty_keth_ruleset(&r->anchor->ruleset);
738 	r->anchor = NULL;
739 }
740 
741 void
pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset * ruleset)742 pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset *ruleset)
743 {
744 	struct pf_keth_anchor	*parent;
745 	int			 i;
746 
747 	while (ruleset != NULL) {
748 		if (ruleset == V_pf_keth || ruleset->anchor == NULL ||
749 		    !RB_EMPTY(&ruleset->anchor->children) ||
750 		    ruleset->anchor->refcnt > 0)
751 			return;
752 		for (i = 0; i < PF_RULESET_MAX; ++i)
753 			if (!TAILQ_EMPTY(ruleset->active.rules) ||
754 			    !TAILQ_EMPTY(ruleset->inactive.rules) ||
755 			    ruleset->inactive.open)
756 				return;
757 		RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, ruleset->anchor);
758 		if ((parent = ruleset->anchor->parent) != NULL)
759 			RB_REMOVE(pf_keth_anchor_node, &parent->children,
760 			    ruleset->anchor);
761 		rs_free(ruleset->anchor);
762 		if (parent == NULL)
763 			return;
764 		ruleset = &parent->ruleset;
765 	}
766 }
767