1244769Sglebius/*-
2171164Smlaier * Copyright (c) 2001 Daniel Hartmeier
3171164Smlaier * Copyright (c) 2002,2003 Henning Brauer
4171164Smlaier * All rights reserved.
5171164Smlaier *
6171164Smlaier * Redistribution and use in source and binary forms, with or without
7171164Smlaier * modification, are permitted provided that the following conditions
8171164Smlaier * are met:
9171164Smlaier *
10171164Smlaier *    - Redistributions of source code must retain the above copyright
11171164Smlaier *      notice, this list of conditions and the following disclaimer.
12171164Smlaier *    - Redistributions in binary form must reproduce the above
13171164Smlaier *      copyright notice, this list of conditions and the following
14171164Smlaier *      disclaimer in the documentation and/or other materials provided
15171164Smlaier *      with the distribution.
16171164Smlaier *
17171164Smlaier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18171164Smlaier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19171164Smlaier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20171164Smlaier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21171164Smlaier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22171164Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23171164Smlaier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24171164Smlaier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25171164Smlaier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26171164Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27171164Smlaier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28171164Smlaier * POSSIBILITY OF SUCH DAMAGE.
29171164Smlaier *
30171164Smlaier * Effort sponsored in part by the Defense Advanced Research Projects
31171164Smlaier * Agency (DARPA) and Air Force Research Laboratory, Air Force
32171164Smlaier * Materiel Command, USAF, under agreement number F30602-01-2-0537.
33171164Smlaier *
34244769Sglebius *	$OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $
35171164Smlaier */
36171164Smlaier
37171168Smlaier#include <sys/cdefs.h>
38171168Smlaier__FBSDID("$FreeBSD: releng/11.0/sys/netpfil/pf/pf_ruleset.c 257179 2013-10-26 18:18:50Z glebius $");
39171168Smlaier
40171164Smlaier#include <sys/param.h>
41171164Smlaier#include <sys/socket.h>
42171164Smlaier#ifdef _KERNEL
43171164Smlaier# include <sys/systm.h>
44240233Sglebius# include <sys/refcount.h>
45171164Smlaier#endif /* _KERNEL */
46171164Smlaier#include <sys/mbuf.h>
47171164Smlaier
48171164Smlaier#include <netinet/in.h>
49171164Smlaier#include <netinet/in_systm.h>
50171164Smlaier#include <netinet/ip.h>
51171164Smlaier#include <netinet/tcp.h>
52171164Smlaier
53171164Smlaier#include <net/if.h>
54257179Sglebius#include <net/vnet.h>
55171164Smlaier#include <net/pfvar.h>
56171164Smlaier
57171164Smlaier#ifdef INET6
58171164Smlaier#include <netinet/ip6.h>
59171164Smlaier#endif /* INET6 */
60171164Smlaier
61171164Smlaier
62171164Smlaier#ifdef _KERNEL
63223637Sbz#define DPFPRINTF(format, x...)				\
64223637Sbz	if (V_pf_status.debug >= PF_DEBUG_NOISY)	\
65171164Smlaier		printf(format , ##x)
66223637Sbz#define rs_malloc(x)		malloc(x, M_TEMP, M_NOWAIT|M_ZERO)
67171164Smlaier#define rs_free(x)		free(x, M_TEMP)
68171164Smlaier
69171164Smlaier#else
70171164Smlaier/* Userland equivalents so we can lend code to pfctl et al. */
71171164Smlaier
72223637Sbz#include <arpa/inet.h>
73223637Sbz#include <errno.h>
74223637Sbz#include <stdio.h>
75223637Sbz#include <stdlib.h>
76223637Sbz#include <string.h>
77223637Sbz#define rs_malloc(x)		 calloc(1, x)
78223637Sbz#define rs_free(x)		 free(x)
79171164Smlaier
80223637Sbz#ifdef PFDEBUG
81223637Sbz#include <sys/stdarg.h>
82223637Sbz#define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
83223637Sbz#else
84223637Sbz#define DPFPRINTF(format, x...)	((void)0)
85223637Sbz#endif /* PFDEBUG */
86171164Smlaier#endif /* _KERNEL */
87171164Smlaier
88240233Sglebius#ifdef _KERNEL
89240233SglebiusVNET_DEFINE(struct pf_anchor_global,	pf_anchors);
90240233SglebiusVNET_DEFINE(struct pf_anchor,		pf_main_anchor);
91240233Sglebius#else /* ! _KERNEL */
92240233Sglebiusstruct pf_anchor_global	 pf_anchors;
93240233Sglebiusstruct pf_anchor	 pf_main_anchor;
94223637Sbz#undef V_pf_anchors
95223637Sbz#define V_pf_anchors		 pf_anchors
96223637Sbz#undef pf_main_ruleset
97223637Sbz#define pf_main_ruleset		 pf_main_anchor.ruleset
98240233Sglebius#endif /* _KERNEL */
99223637Sbz
100171164Smlaierstatic __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
101171164Smlaier
102240233Sglebiusstatic struct pf_anchor		*pf_find_anchor(const char *);
103240233Sglebius
104171164SmlaierRB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
105171164SmlaierRB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
106171164Smlaier
107171164Smlaierstatic __inline int
108171164Smlaierpf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
109171164Smlaier{
110171164Smlaier	int c = strcmp(a->path, b->path);
111171164Smlaier
112171164Smlaier	return (c ? (c < 0 ? -1 : 1) : 0);
113171164Smlaier}
114171164Smlaier
115171164Smlaierint
116171164Smlaierpf_get_ruleset_number(u_int8_t action)
117171164Smlaier{
118171164Smlaier	switch (action) {
119171164Smlaier	case PF_SCRUB:
120171164Smlaier	case PF_NOSCRUB:
121171164Smlaier		return (PF_RULESET_SCRUB);
122171164Smlaier		break;
123171164Smlaier	case PF_PASS:
124171164Smlaier	case PF_DROP:
125171164Smlaier		return (PF_RULESET_FILTER);
126171164Smlaier		break;
127171164Smlaier	case PF_NAT:
128171164Smlaier	case PF_NONAT:
129171164Smlaier		return (PF_RULESET_NAT);
130171164Smlaier		break;
131171164Smlaier	case PF_BINAT:
132171164Smlaier	case PF_NOBINAT:
133171164Smlaier		return (PF_RULESET_BINAT);
134171164Smlaier		break;
135171164Smlaier	case PF_RDR:
136171164Smlaier	case PF_NORDR:
137171164Smlaier		return (PF_RULESET_RDR);
138171164Smlaier		break;
139171164Smlaier	default:
140171164Smlaier		return (PF_RULESET_MAX);
141171164Smlaier		break;
142171164Smlaier	}
143171164Smlaier}
144171164Smlaier
145171164Smlaiervoid
146171164Smlaierpf_init_ruleset(struct pf_ruleset *ruleset)
147171164Smlaier{
148171164Smlaier	int	i;
149171164Smlaier
150171164Smlaier	memset(ruleset, 0, sizeof(struct pf_ruleset));
151171164Smlaier	for (i = 0; i < PF_RULESET_MAX; i++) {
152171164Smlaier		TAILQ_INIT(&ruleset->rules[i].queues[0]);
153171164Smlaier		TAILQ_INIT(&ruleset->rules[i].queues[1]);
154171164Smlaier		ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
155171164Smlaier		ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
156171164Smlaier	}
157171164Smlaier}
158171164Smlaier
159240233Sglebiusstatic struct pf_anchor *
160171164Smlaierpf_find_anchor(const char *path)
161171164Smlaier{
162171164Smlaier	struct pf_anchor	*key, *found;
163171164Smlaier
164171164Smlaier	key = (struct pf_anchor *)rs_malloc(sizeof(*key));
165223637Sbz	if (key == NULL)
166223637Sbz		return (NULL);
167171164Smlaier	strlcpy(key->path, path, sizeof(key->path));
168223637Sbz	found = RB_FIND(pf_anchor_global, &V_pf_anchors, key);
169171164Smlaier	rs_free(key);
170171164Smlaier	return (found);
171171164Smlaier}
172171164Smlaier
173171164Smlaierstruct pf_ruleset *
174171164Smlaierpf_find_ruleset(const char *path)
175171164Smlaier{
176171164Smlaier	struct pf_anchor	*anchor;
177171164Smlaier
178171164Smlaier	while (*path == '/')
179171164Smlaier		path++;
180171164Smlaier	if (!*path)
181171164Smlaier		return (&pf_main_ruleset);
182171164Smlaier	anchor = pf_find_anchor(path);
183171164Smlaier	if (anchor == NULL)
184171164Smlaier		return (NULL);
185171164Smlaier	else
186171164Smlaier		return (&anchor->ruleset);
187171164Smlaier}
188171164Smlaier
189171164Smlaierstruct pf_ruleset *
190171164Smlaierpf_find_or_create_ruleset(const char *path)
191171164Smlaier{
192171164Smlaier	char			*p, *q, *r;
193171164Smlaier	struct pf_ruleset	*ruleset;
194171168Smlaier	struct pf_anchor	*anchor = NULL, *dup, *parent = NULL;
195171164Smlaier
196171164Smlaier	if (path[0] == 0)
197171164Smlaier		return (&pf_main_ruleset);
198171164Smlaier	while (*path == '/')
199171164Smlaier		path++;
200171164Smlaier	ruleset = pf_find_ruleset(path);
201171164Smlaier	if (ruleset != NULL)
202171164Smlaier		return (ruleset);
203171164Smlaier	p = (char *)rs_malloc(MAXPATHLEN);
204223637Sbz	if (p == NULL)
205223637Sbz		return (NULL);
206171164Smlaier	strlcpy(p, path, MAXPATHLEN);
207171164Smlaier	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
208171164Smlaier		*q = 0;
209171164Smlaier		if ((ruleset = pf_find_ruleset(p)) != NULL) {
210171164Smlaier			parent = ruleset->anchor;
211171164Smlaier			break;
212171164Smlaier		}
213171164Smlaier	}
214171164Smlaier	if (q == NULL)
215171164Smlaier		q = p;
216171164Smlaier	else
217171164Smlaier		q++;
218171164Smlaier	strlcpy(p, path, MAXPATHLEN);
219171164Smlaier	if (!*q) {
220171164Smlaier		rs_free(p);
221171164Smlaier		return (NULL);
222171164Smlaier	}
223171164Smlaier	while ((r = strchr(q, '/')) != NULL || *q) {
224171164Smlaier		if (r != NULL)
225171164Smlaier			*r = 0;
226171164Smlaier		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
227171164Smlaier		    (parent != NULL && strlen(parent->path) >=
228171164Smlaier		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
229171164Smlaier			rs_free(p);
230171164Smlaier			return (NULL);
231171164Smlaier		}
232171164Smlaier		anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor));
233171164Smlaier		if (anchor == NULL) {
234171164Smlaier			rs_free(p);
235171164Smlaier			return (NULL);
236171164Smlaier		}
237171164Smlaier		RB_INIT(&anchor->children);
238171164Smlaier		strlcpy(anchor->name, q, sizeof(anchor->name));
239171164Smlaier		if (parent != NULL) {
240171164Smlaier			strlcpy(anchor->path, parent->path,
241171164Smlaier			    sizeof(anchor->path));
242171164Smlaier			strlcat(anchor->path, "/", sizeof(anchor->path));
243171164Smlaier		}
244171164Smlaier		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
245223637Sbz		if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) !=
246171164Smlaier		    NULL) {
247171164Smlaier			printf("pf_find_or_create_ruleset: RB_INSERT1 "
248171164Smlaier			    "'%s' '%s' collides with '%s' '%s'\n",
249171164Smlaier			    anchor->path, anchor->name, dup->path, dup->name);
250171164Smlaier			rs_free(anchor);
251171164Smlaier			rs_free(p);
252171164Smlaier			return (NULL);
253171164Smlaier		}
254171164Smlaier		if (parent != NULL) {
255171164Smlaier			anchor->parent = parent;
256171164Smlaier			if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
257171164Smlaier			    anchor)) != NULL) {
258171164Smlaier				printf("pf_find_or_create_ruleset: "
259171164Smlaier				    "RB_INSERT2 '%s' '%s' collides with "
260171164Smlaier				    "'%s' '%s'\n", anchor->path, anchor->name,
261171164Smlaier				    dup->path, dup->name);
262223637Sbz				RB_REMOVE(pf_anchor_global, &V_pf_anchors,
263171164Smlaier				    anchor);
264171164Smlaier				rs_free(anchor);
265171164Smlaier				rs_free(p);
266171164Smlaier				return (NULL);
267171164Smlaier			}
268171164Smlaier		}
269171164Smlaier		pf_init_ruleset(&anchor->ruleset);
270171164Smlaier		anchor->ruleset.anchor = anchor;
271171164Smlaier		parent = anchor;
272171164Smlaier		if (r != NULL)
273171164Smlaier			q = r + 1;
274171164Smlaier		else
275171164Smlaier			*q = 0;
276171164Smlaier	}
277171164Smlaier	rs_free(p);
278171164Smlaier	return (&anchor->ruleset);
279171164Smlaier}
280171164Smlaier
281171164Smlaiervoid
282171164Smlaierpf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
283171164Smlaier{
284171164Smlaier	struct pf_anchor	*parent;
285171164Smlaier	int			 i;
286171164Smlaier
287171164Smlaier	while (ruleset != NULL) {
288171164Smlaier		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
289171164Smlaier		    !RB_EMPTY(&ruleset->anchor->children) ||
290171164Smlaier		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
291171164Smlaier		    ruleset->topen)
292171164Smlaier			return;
293171164Smlaier		for (i = 0; i < PF_RULESET_MAX; ++i)
294171164Smlaier			if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
295171164Smlaier			    !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
296171164Smlaier			    ruleset->rules[i].inactive.open)
297171164Smlaier				return;
298223637Sbz		RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor);
299171164Smlaier		if ((parent = ruleset->anchor->parent) != NULL)
300171164Smlaier			RB_REMOVE(pf_anchor_node, &parent->children,
301171164Smlaier			    ruleset->anchor);
302171164Smlaier		rs_free(ruleset->anchor);
303171164Smlaier		if (parent == NULL)
304171164Smlaier			return;
305171164Smlaier		ruleset = &parent->ruleset;
306171164Smlaier	}
307171164Smlaier}
308171164Smlaier
309171164Smlaierint
310171164Smlaierpf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
311171164Smlaier    const char *name)
312171164Smlaier{
313171164Smlaier	char			*p, *path;
314171164Smlaier	struct pf_ruleset	*ruleset;
315171164Smlaier
316171164Smlaier	r->anchor = NULL;
317171164Smlaier	r->anchor_relative = 0;
318171164Smlaier	r->anchor_wildcard = 0;
319171164Smlaier	if (!name[0])
320171164Smlaier		return (0);
321171164Smlaier	path = (char *)rs_malloc(MAXPATHLEN);
322223637Sbz	if (path == NULL)
323223637Sbz		return (1);
324171164Smlaier	if (name[0] == '/')
325171164Smlaier		strlcpy(path, name + 1, MAXPATHLEN);
326171164Smlaier	else {
327171164Smlaier		/* relative path */
328171164Smlaier		r->anchor_relative = 1;
329171164Smlaier		if (s->anchor == NULL || !s->anchor->path[0])
330171164Smlaier			path[0] = 0;
331171164Smlaier		else
332171164Smlaier			strlcpy(path, s->anchor->path, MAXPATHLEN);
333171164Smlaier		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
334171164Smlaier			if (!path[0]) {
335171164Smlaier				printf("pf_anchor_setup: .. beyond root\n");
336171164Smlaier				rs_free(path);
337171164Smlaier				return (1);
338171164Smlaier			}
339171164Smlaier			if ((p = strrchr(path, '/')) != NULL)
340171164Smlaier				*p = 0;
341171164Smlaier			else
342171164Smlaier				path[0] = 0;
343171164Smlaier			r->anchor_relative++;
344171164Smlaier			name += 3;
345171164Smlaier		}
346171164Smlaier		if (path[0])
347171164Smlaier			strlcat(path, "/", MAXPATHLEN);
348171164Smlaier		strlcat(path, name, MAXPATHLEN);
349171164Smlaier	}
350171164Smlaier	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
351171164Smlaier		r->anchor_wildcard = 1;
352171164Smlaier		*p = 0;
353171164Smlaier	}
354171164Smlaier	ruleset = pf_find_or_create_ruleset(path);
355171164Smlaier	rs_free(path);
356171164Smlaier	if (ruleset == NULL || ruleset->anchor == NULL) {
357171164Smlaier		printf("pf_anchor_setup: ruleset\n");
358171164Smlaier		return (1);
359171164Smlaier	}
360171164Smlaier	r->anchor = ruleset->anchor;
361171164Smlaier	r->anchor->refcnt++;
362171164Smlaier	return (0);
363171164Smlaier}
364171164Smlaier
365171164Smlaierint
366171164Smlaierpf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
367171164Smlaier    struct pfioc_rule *pr)
368171164Smlaier{
369171164Smlaier	pr->anchor_call[0] = 0;
370171164Smlaier	if (r->anchor == NULL)
371171164Smlaier		return (0);
372171164Smlaier	if (!r->anchor_relative) {
373171164Smlaier		strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
374171164Smlaier		strlcat(pr->anchor_call, r->anchor->path,
375171164Smlaier		    sizeof(pr->anchor_call));
376171164Smlaier	} else {
377171164Smlaier		char	*a, *p;
378171164Smlaier		int	 i;
379171164Smlaier
380171164Smlaier		a = (char *)rs_malloc(MAXPATHLEN);
381223637Sbz		if (a == NULL)
382223637Sbz			return (1);
383171164Smlaier		if (rs->anchor == NULL)
384171164Smlaier			a[0] = 0;
385171164Smlaier		else
386171164Smlaier			strlcpy(a, rs->anchor->path, MAXPATHLEN);
387171164Smlaier		for (i = 1; i < r->anchor_relative; ++i) {
388171164Smlaier			if ((p = strrchr(a, '/')) == NULL)
389171164Smlaier				p = a;
390171164Smlaier			*p = 0;
391171164Smlaier			strlcat(pr->anchor_call, "../",
392171164Smlaier			    sizeof(pr->anchor_call));
393171164Smlaier		}
394171164Smlaier		if (strncmp(a, r->anchor->path, strlen(a))) {
395171164Smlaier			printf("pf_anchor_copyout: '%s' '%s'\n", a,
396171164Smlaier			    r->anchor->path);
397171164Smlaier			rs_free(a);
398171164Smlaier			return (1);
399171164Smlaier		}
400171164Smlaier		if (strlen(r->anchor->path) > strlen(a))
401171164Smlaier			strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
402171164Smlaier			    strlen(a) + 1 : 0), sizeof(pr->anchor_call));
403171164Smlaier		rs_free(a);
404171164Smlaier	}
405171164Smlaier	if (r->anchor_wildcard)
406171164Smlaier		strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
407171164Smlaier		    sizeof(pr->anchor_call));
408171164Smlaier	return (0);
409171164Smlaier}
410171164Smlaier
411171164Smlaiervoid
412171164Smlaierpf_anchor_remove(struct pf_rule *r)
413171164Smlaier{
414171164Smlaier	if (r->anchor == NULL)
415171164Smlaier		return;
416171164Smlaier	if (r->anchor->refcnt <= 0) {
417171164Smlaier		printf("pf_anchor_remove: broken refcount\n");
418171164Smlaier		r->anchor = NULL;
419171164Smlaier		return;
420171164Smlaier	}
421171164Smlaier	if (!--r->anchor->refcnt)
422171164Smlaier		pf_remove_if_empty_ruleset(&r->anchor->ruleset);
423171164Smlaier	r->anchor = NULL;
424171164Smlaier}
425