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