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
68VNET_DEFINE(struct pf_kanchor_global,	pf_anchors);
69VNET_DEFINE(struct pf_kanchor,		pf_main_anchor);
70VNET_DEFINE(struct pf_keth_ruleset*,	pf_keth);
71VNET_DEFINE(struct pf_keth_anchor,	pf_main_keth_anchor);
72VNET_DEFINE(struct pf_keth_anchor_global,	 pf_keth_anchors);
73
74static __inline int		pf_kanchor_compare(struct pf_kanchor *,
75				    struct pf_kanchor *);
76static __inline int		pf_keth_anchor_compare(struct pf_keth_anchor *,
77				    struct pf_keth_anchor *);
78static struct pf_kanchor	*pf_find_kanchor(const char *);
79
80RB_GENERATE(pf_kanchor_global, pf_kanchor, entry_global, pf_kanchor_compare);
81RB_GENERATE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare);
82RB_GENERATE(pf_keth_anchor_global, pf_keth_anchor, entry_global,
83    pf_keth_anchor_compare);
84RB_GENERATE(pf_keth_anchor_node, pf_keth_anchor, entry_node,
85    pf_keth_anchor_compare);
86
87static __inline int
88pf_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
95static __inline int
96pf_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
103int
104pf_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
134static struct pf_kanchor *
135pf_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
148void
149pf_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
162void
163pf_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
177struct pf_kruleset *
178pf_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
193struct pf_kruleset *
194pf_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
285void
286pf_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
313int
314pf_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
369int
370pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
371    char *anchor_call, size_t anchor_call_len)
372{
373	anchor_call[0] = 0;
374
375	if (r->anchor == NULL)
376		goto done;
377	if (!r->anchor_relative) {
378		strlcpy(anchor_call, "/", anchor_call_len);
379		strlcat(anchor_call, r->anchor->path,
380		    anchor_call_len);
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			    anchor_call_len);
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), anchor_call_len);
404
405	}
406	if (r->anchor_wildcard)
407		strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
408		    anchor_call_len);
409
410done:
411
412	return (0);
413}
414
415int
416pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r,
417    nvlist_t *nvl)
418{
419	char anchor_call[MAXPATHLEN] = { 0 };
420	int ret;
421
422	ret = pf_kanchor_copyout(rs, r, anchor_call, sizeof(anchor_call));
423	MPASS(ret == 0);
424
425	nvlist_add_string(nvl, "anchor_call", anchor_call);
426
427	return (ret);
428}
429
430int
431pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs,
432    const struct pf_keth_rule *r, nvlist_t *nvl)
433{
434	char anchor_call[MAXPATHLEN] = { 0 };
435
436	if (r->anchor == NULL)
437		goto done;
438	if (!r->anchor_relative) {
439		strlcpy(anchor_call, "/", sizeof(anchor_call));
440		strlcat(anchor_call, r->anchor->path,
441		    sizeof(anchor_call));
442	} else {
443		char	 a[MAXPATHLEN];
444		char	*p;
445		int	 i;
446		if (rs->anchor == NULL)
447			a[0] = 0;
448		else
449			strlcpy(a, rs->anchor->path, MAXPATHLEN);
450		for (i = 1; i < r->anchor_relative; ++i) {
451			if ((p = strrchr(a, '/')) == NULL)
452				p = a;
453			*p = 0;
454			strlcat(anchor_call, "../",
455			    sizeof(anchor_call));
456		}
457		if (strncmp(a, r->anchor->path, strlen(a))) {
458			printf("%s(): '%s' '%s'\n", __func__, a,
459			    r->anchor->path);
460			return (1);
461		}
462		if (strlen(r->anchor->path) > strlen(a))
463			strlcat(anchor_call, r->anchor->path + (a[0] ?
464			    strlen(a) + 1 : 0), sizeof(anchor_call));
465
466	}
467	if (r->anchor_wildcard)
468		strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
469		    sizeof(anchor_call));
470
471done:
472	nvlist_add_string(nvl, "anchor_call", anchor_call);
473
474	return (0);
475}
476
477void
478pf_kanchor_remove(struct pf_krule *r)
479{
480	if (r->anchor == NULL)
481		return;
482	if (r->anchor->refcnt <= 0) {
483		printf("pf_anchor_remove: broken refcount\n");
484		r->anchor = NULL;
485		return;
486	}
487	if (!--r->anchor->refcnt)
488		pf_remove_if_empty_kruleset(&r->anchor->ruleset);
489	r->anchor = NULL;
490}
491
492struct pf_keth_ruleset *
493pf_find_keth_ruleset(const char *path)
494{
495	struct pf_keth_anchor	*anchor;
496
497	while (*path == '/')
498		path++;
499	if (!*path)
500		return (V_pf_keth);
501	anchor = pf_find_keth_anchor(path);
502	if (anchor == NULL)
503		return (NULL);
504	else
505		return (&anchor->ruleset);
506}
507
508static struct pf_keth_anchor *
509_pf_find_keth_anchor(struct pf_keth_ruleset *rs, const char *path)
510{
511	struct pf_keth_anchor	*key, *found;
512
513	key = (struct pf_keth_anchor *)rs_malloc(sizeof(*key));
514	if (key == NULL)
515		return (NULL);
516	strlcpy(key->path, path, sizeof(key->path));
517	found = RB_FIND(pf_keth_anchor_global, &V_pf_keth_anchors, key);
518	rs_free(key);
519	return (found);
520}
521
522struct pf_keth_anchor *
523pf_find_keth_anchor(const char *path)
524{
525	return (_pf_find_keth_anchor(V_pf_keth, path));
526}
527
528struct pf_keth_ruleset *
529pf_find_or_create_keth_ruleset(const char *path)
530{
531	char			*p, *q, *r;
532	struct pf_keth_anchor	*anchor = NULL, *dup = NULL, *parent = NULL;
533	struct pf_keth_ruleset	*ruleset;
534
535	if (path[0] == 0)
536		return (V_pf_keth);
537	while (*path == '/')
538		path++;
539	ruleset = pf_find_keth_ruleset(path);
540	if (ruleset != NULL)
541		return (ruleset);
542	p = (char *)rs_malloc(MAXPATHLEN);
543	if (p == NULL)
544		return (NULL);
545	strlcpy(p, path, MAXPATHLEN);
546	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
547		*q = 0;
548		if ((ruleset = pf_find_keth_ruleset(p)) != NULL) {
549			parent = ruleset->anchor;
550			break;
551		}
552	}
553	if (q == NULL)
554		q = p;
555	else
556		q++;
557	strlcpy(p, path, MAXPATHLEN);
558	if (!*q) {
559		rs_free(p);
560		return (NULL);
561	}
562	while ((r = strchr(q, '/')) != NULL || *q) {
563		if (r != NULL)
564			*r = 0;
565		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
566		    (parent != NULL && strlen(parent->path) >=
567		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
568			rs_free(p);
569			return (NULL);
570		}
571		anchor = (struct pf_keth_anchor *)rs_malloc(sizeof(*anchor));
572		if (anchor == NULL) {
573			rs_free(p);
574			return (NULL);
575		}
576		RB_INIT(&anchor->children);
577		strlcpy(anchor->name, q, sizeof(anchor->name));
578		if (parent != NULL) {
579			strlcpy(anchor->path, parent->path,
580			    sizeof(anchor->path));
581			strlcat(anchor->path, "/", sizeof(anchor->path));
582		}
583		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
584		if ((dup = RB_INSERT(pf_keth_anchor_global, &V_pf_keth_anchors, anchor)) !=
585		    NULL) {
586			printf("%s: RB_INSERT1 "
587			    "'%s' '%s' collides with '%s' '%s'\n", __func__,
588			    anchor->path, anchor->name, dup->path, dup->name);
589			rs_free(anchor);
590			rs_free(p);
591			return (NULL);
592		}
593		if (parent != NULL) {
594			anchor->parent = parent;
595			if ((dup = RB_INSERT(pf_keth_anchor_node, &parent->children,
596			    anchor)) != NULL) {
597				printf("%s: "
598				    "RB_INSERT2 '%s' '%s' collides with "
599				    "'%s' '%s'\n", __func__, anchor->path,
600				    anchor->name, dup->path, dup->name);
601				RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors,
602				    anchor);
603				rs_free(anchor);
604				rs_free(p);
605				return (NULL);
606			}
607		}
608		pf_init_keth(&anchor->ruleset);
609		anchor->ruleset.anchor = anchor;
610		parent = anchor;
611		if (r != NULL)
612			q = r + 1;
613		else
614			*q = 0;
615	}
616	rs_free(p);
617	return (&anchor->ruleset);
618}
619
620int
621pf_keth_anchor_setup(struct pf_keth_rule *r, const struct pf_keth_ruleset *s,
622    const char *name)
623{
624	char			*p, *path;
625	struct pf_keth_ruleset	*ruleset;
626
627	r->anchor = NULL;
628	r->anchor_relative = 0;
629	r->anchor_wildcard = 0;
630	if (!name[0])
631		return (0);
632	path = (char *)rs_malloc(MAXPATHLEN);
633	if (path == NULL)
634		return (1);
635	if (name[0] == '/')
636		strlcpy(path, name + 1, MAXPATHLEN);
637	else {
638		/* relative path */
639		r->anchor_relative = 1;
640		if (s->anchor == NULL || !s->anchor->path[0])
641			path[0] = 0;
642		else
643			strlcpy(path, s->anchor->path, MAXPATHLEN);
644		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
645			if (!path[0]) {
646				DPFPRINTF("pf_anchor_setup: .. beyond root\n");
647				rs_free(path);
648				return (1);
649			}
650			if ((p = strrchr(path, '/')) != NULL)
651				*p = 0;
652			else
653				path[0] = 0;
654			r->anchor_relative++;
655			name += 3;
656		}
657		if (path[0])
658			strlcat(path, "/", MAXPATHLEN);
659		strlcat(path, name, MAXPATHLEN);
660	}
661	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
662		r->anchor_wildcard = 1;
663		*p = 0;
664	}
665	ruleset = pf_find_or_create_keth_ruleset(path);
666	rs_free(path);
667	if (ruleset == NULL || ruleset->anchor == NULL) {
668		DPFPRINTF("pf_anchor_setup: ruleset\n");
669		return (1);
670	}
671	r->anchor = ruleset->anchor;
672	r->anchor->refcnt++;
673	return (0);
674}
675
676void
677pf_keth_anchor_remove(struct pf_keth_rule *r)
678{
679	if (r->anchor == NULL)
680		return;
681	if (r->anchor->refcnt <= 0) {
682		printf("%s: broken refcount\n", __func__);
683		r->anchor = NULL;
684		return;
685	}
686	if (!--r->anchor->refcnt)
687		pf_remove_if_empty_keth_ruleset(&r->anchor->ruleset);
688	r->anchor = NULL;
689}
690
691void
692pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset *ruleset)
693{
694	struct pf_keth_anchor	*parent;
695	int			 i;
696
697	while (ruleset != NULL) {
698		if (ruleset == V_pf_keth || ruleset->anchor == NULL ||
699		    !RB_EMPTY(&ruleset->anchor->children) ||
700		    ruleset->anchor->refcnt > 0)
701			return;
702		for (i = 0; i < PF_RULESET_MAX; ++i)
703			if (!TAILQ_EMPTY(ruleset->active.rules) ||
704			    !TAILQ_EMPTY(ruleset->inactive.rules) ||
705			    ruleset->inactive.open)
706				return;
707		RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, ruleset->anchor);
708		if ((parent = ruleset->anchor->parent) != NULL)
709			RB_REMOVE(pf_keth_anchor_node, &parent->children,
710			    ruleset->anchor);
711		rs_free(ruleset->anchor);
712		if (parent == NULL)
713			return;
714		ruleset = &parent->ruleset;
715	}
716}
717