1223637Sbz/*	$OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ */
2171164Smlaier
3171164Smlaier/*
4171164Smlaier * Copyright (c) 2001 Daniel Hartmeier
5171164Smlaier * Copyright (c) 2002,2003 Henning Brauer
6171164Smlaier * All rights reserved.
7171164Smlaier *
8171164Smlaier * Redistribution and use in source and binary forms, with or without
9171164Smlaier * modification, are permitted provided that the following conditions
10171164Smlaier * are met:
11171164Smlaier *
12171164Smlaier *    - Redistributions of source code must retain the above copyright
13171164Smlaier *      notice, this list of conditions and the following disclaimer.
14171164Smlaier *    - Redistributions in binary form must reproduce the above
15171164Smlaier *      copyright notice, this list of conditions and the following
16171164Smlaier *      disclaimer in the documentation and/or other materials provided
17171164Smlaier *      with the distribution.
18171164Smlaier *
19171164Smlaier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20171164Smlaier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21171164Smlaier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22171164Smlaier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23171164Smlaier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24171164Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25171164Smlaier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26171164Smlaier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27171164Smlaier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28171164Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29171164Smlaier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30171164Smlaier * POSSIBILITY OF SUCH DAMAGE.
31171164Smlaier *
32171164Smlaier * Effort sponsored in part by the Defense Advanced Research Projects
33171164Smlaier * Agency (DARPA) and Air Force Research Laboratory, Air Force
34171164Smlaier * Materiel Command, USAF, under agreement number F30602-01-2-0537.
35171164Smlaier *
36171164Smlaier */
37171164Smlaier
38171168Smlaier#ifdef __FreeBSD__
39171168Smlaier#include <sys/cdefs.h>
40171168Smlaier__FBSDID("$FreeBSD$");
41171168Smlaier#endif
42171168Smlaier
43171164Smlaier#include <sys/param.h>
44171164Smlaier#include <sys/socket.h>
45171164Smlaier#ifdef _KERNEL
46171164Smlaier# include <sys/systm.h>
47171164Smlaier#endif /* _KERNEL */
48171164Smlaier#include <sys/mbuf.h>
49171164Smlaier
50171164Smlaier#include <netinet/in.h>
51171164Smlaier#include <netinet/in_systm.h>
52171164Smlaier#include <netinet/ip.h>
53171164Smlaier#include <netinet/tcp.h>
54171164Smlaier
55171164Smlaier#include <net/if.h>
56171164Smlaier#include <net/pfvar.h>
57171164Smlaier
58171164Smlaier#ifdef INET6
59171164Smlaier#include <netinet/ip6.h>
60171164Smlaier#endif /* INET6 */
61171164Smlaier
62171164Smlaier
63171164Smlaier#ifdef _KERNEL
64223637Sbz#ifdef __FreeBSD__
65223637Sbz#define DPFPRINTF(format, x...)				\
66223637Sbz	if (V_pf_status.debug >= PF_DEBUG_NOISY)	\
67171164Smlaier		printf(format , ##x)
68223637Sbz#else
69223637Sbz#define DPFPRINTF(format, x...)				\
70223637Sbz	if (pf_status.debug >= PF_DEBUG_NOISY)		\
71223637Sbz		printf(format , ##x)
72223637Sbz#endif
73171168Smlaier#ifdef __FreeBSD__
74223637Sbz#define rs_malloc(x)		malloc(x, M_TEMP, M_NOWAIT|M_ZERO)
75171168Smlaier#else
76223637Sbz#define rs_malloc(x)		malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO)
77171168Smlaier#endif
78171164Smlaier#define rs_free(x)		free(x, M_TEMP)
79171164Smlaier
80171164Smlaier#else
81171164Smlaier/* Userland equivalents so we can lend code to pfctl et al. */
82171164Smlaier
83223637Sbz#include <arpa/inet.h>
84223637Sbz#include <errno.h>
85223637Sbz#include <stdio.h>
86223637Sbz#include <stdlib.h>
87223637Sbz#include <string.h>
88223637Sbz#define rs_malloc(x)		 calloc(1, x)
89223637Sbz#define rs_free(x)		 free(x)
90171164Smlaier
91223637Sbz#ifdef PFDEBUG
92223637Sbz#include <sys/stdarg.h>
93223637Sbz#define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
94223637Sbz#else
95223637Sbz#define DPFPRINTF(format, x...)	((void)0)
96223637Sbz#endif /* PFDEBUG */
97171164Smlaier#endif /* _KERNEL */
98171164Smlaier
99223637Sbz#if defined(__FreeBSD__) && !defined(_KERNEL)
100223637Sbz#undef V_pf_anchors
101223637Sbz#define V_pf_anchors		 pf_anchors
102171164Smlaier
103223637Sbz#undef pf_main_ruleset
104223637Sbz#define pf_main_ruleset		 pf_main_anchor.ruleset
105223637Sbz#endif
106223637Sbz
107223637Sbz#if defined(__FreeBSD__) && defined(_KERNEL)
108223637SbzVNET_DEFINE(struct pf_anchor_global,	pf_anchors);
109223637SbzVNET_DEFINE(struct pf_anchor,		pf_main_anchor);
110223637Sbz#else
111171164Smlaierstruct pf_anchor_global	 pf_anchors;
112171164Smlaierstruct pf_anchor	 pf_main_anchor;
113171168Smlaier#endif
114171164Smlaier
115171164Smlaierstatic __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
116171164Smlaier
117171164SmlaierRB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
118171164SmlaierRB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
119171164Smlaier
120171164Smlaierstatic __inline int
121171164Smlaierpf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
122171164Smlaier{
123171164Smlaier	int c = strcmp(a->path, b->path);
124171164Smlaier
125171164Smlaier	return (c ? (c < 0 ? -1 : 1) : 0);
126171164Smlaier}
127171164Smlaier
128171164Smlaierint
129171164Smlaierpf_get_ruleset_number(u_int8_t action)
130171164Smlaier{
131171164Smlaier	switch (action) {
132171164Smlaier	case PF_SCRUB:
133171164Smlaier	case PF_NOSCRUB:
134171164Smlaier		return (PF_RULESET_SCRUB);
135171164Smlaier		break;
136171164Smlaier	case PF_PASS:
137171164Smlaier	case PF_DROP:
138171164Smlaier		return (PF_RULESET_FILTER);
139171164Smlaier		break;
140171164Smlaier	case PF_NAT:
141171164Smlaier	case PF_NONAT:
142171164Smlaier		return (PF_RULESET_NAT);
143171164Smlaier		break;
144171164Smlaier	case PF_BINAT:
145171164Smlaier	case PF_NOBINAT:
146171164Smlaier		return (PF_RULESET_BINAT);
147171164Smlaier		break;
148171164Smlaier	case PF_RDR:
149171164Smlaier	case PF_NORDR:
150171164Smlaier		return (PF_RULESET_RDR);
151171164Smlaier		break;
152171164Smlaier	default:
153171164Smlaier		return (PF_RULESET_MAX);
154171164Smlaier		break;
155171164Smlaier	}
156171164Smlaier}
157171164Smlaier
158171164Smlaiervoid
159171164Smlaierpf_init_ruleset(struct pf_ruleset *ruleset)
160171164Smlaier{
161171164Smlaier	int	i;
162171164Smlaier
163171164Smlaier	memset(ruleset, 0, sizeof(struct pf_ruleset));
164171164Smlaier	for (i = 0; i < PF_RULESET_MAX; i++) {
165171164Smlaier		TAILQ_INIT(&ruleset->rules[i].queues[0]);
166171164Smlaier		TAILQ_INIT(&ruleset->rules[i].queues[1]);
167171164Smlaier		ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
168171164Smlaier		ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
169171164Smlaier	}
170171164Smlaier}
171171164Smlaier
172171164Smlaierstruct pf_anchor *
173171164Smlaierpf_find_anchor(const char *path)
174171164Smlaier{
175171164Smlaier	struct pf_anchor	*key, *found;
176171164Smlaier
177171164Smlaier	key = (struct pf_anchor *)rs_malloc(sizeof(*key));
178223637Sbz	if (key == NULL)
179223637Sbz		return (NULL);
180171164Smlaier	strlcpy(key->path, path, sizeof(key->path));
181223637Sbz#ifdef __FreeBSD__
182223637Sbz	found = RB_FIND(pf_anchor_global, &V_pf_anchors, key);
183223637Sbz#else
184171164Smlaier	found = RB_FIND(pf_anchor_global, &pf_anchors, key);
185223637Sbz#endif
186171164Smlaier	rs_free(key);
187171164Smlaier	return (found);
188171164Smlaier}
189171164Smlaier
190171164Smlaierstruct pf_ruleset *
191171164Smlaierpf_find_ruleset(const char *path)
192171164Smlaier{
193171164Smlaier	struct pf_anchor	*anchor;
194171164Smlaier
195171164Smlaier	while (*path == '/')
196171164Smlaier		path++;
197171164Smlaier	if (!*path)
198171164Smlaier		return (&pf_main_ruleset);
199171164Smlaier	anchor = pf_find_anchor(path);
200171164Smlaier	if (anchor == NULL)
201171164Smlaier		return (NULL);
202171164Smlaier	else
203171164Smlaier		return (&anchor->ruleset);
204171164Smlaier}
205171164Smlaier
206171164Smlaierstruct pf_ruleset *
207171164Smlaierpf_find_or_create_ruleset(const char *path)
208171164Smlaier{
209171164Smlaier	char			*p, *q, *r;
210171164Smlaier	struct pf_ruleset	*ruleset;
211171168Smlaier#ifdef __FreeBSD__
212171168Smlaier	struct pf_anchor	*anchor = NULL, *dup, *parent = NULL;
213171168Smlaier#else
214171164Smlaier	struct pf_anchor	*anchor, *dup, *parent = NULL;
215171168Smlaier#endif
216171164Smlaier
217171164Smlaier	if (path[0] == 0)
218171164Smlaier		return (&pf_main_ruleset);
219171164Smlaier	while (*path == '/')
220171164Smlaier		path++;
221171164Smlaier	ruleset = pf_find_ruleset(path);
222171164Smlaier	if (ruleset != NULL)
223171164Smlaier		return (ruleset);
224171164Smlaier	p = (char *)rs_malloc(MAXPATHLEN);
225223637Sbz	if (p == NULL)
226223637Sbz		return (NULL);
227171164Smlaier	strlcpy(p, path, MAXPATHLEN);
228171164Smlaier	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
229171164Smlaier		*q = 0;
230171164Smlaier		if ((ruleset = pf_find_ruleset(p)) != NULL) {
231171164Smlaier			parent = ruleset->anchor;
232171164Smlaier			break;
233171164Smlaier		}
234171164Smlaier	}
235171164Smlaier	if (q == NULL)
236171164Smlaier		q = p;
237171164Smlaier	else
238171164Smlaier		q++;
239171164Smlaier	strlcpy(p, path, MAXPATHLEN);
240171164Smlaier	if (!*q) {
241171164Smlaier		rs_free(p);
242171164Smlaier		return (NULL);
243171164Smlaier	}
244171164Smlaier	while ((r = strchr(q, '/')) != NULL || *q) {
245171164Smlaier		if (r != NULL)
246171164Smlaier			*r = 0;
247171164Smlaier		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
248171164Smlaier		    (parent != NULL && strlen(parent->path) >=
249171164Smlaier		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
250171164Smlaier			rs_free(p);
251171164Smlaier			return (NULL);
252171164Smlaier		}
253171164Smlaier		anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor));
254171164Smlaier		if (anchor == NULL) {
255171164Smlaier			rs_free(p);
256171164Smlaier			return (NULL);
257171164Smlaier		}
258171164Smlaier		RB_INIT(&anchor->children);
259171164Smlaier		strlcpy(anchor->name, q, sizeof(anchor->name));
260171164Smlaier		if (parent != NULL) {
261171164Smlaier			strlcpy(anchor->path, parent->path,
262171164Smlaier			    sizeof(anchor->path));
263171164Smlaier			strlcat(anchor->path, "/", sizeof(anchor->path));
264171164Smlaier		}
265171164Smlaier		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
266223637Sbz#ifdef __FreeBSD__
267223637Sbz		if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) !=
268223637Sbz#else
269171164Smlaier		if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) !=
270223637Sbz#endif
271171164Smlaier		    NULL) {
272171164Smlaier			printf("pf_find_or_create_ruleset: RB_INSERT1 "
273171164Smlaier			    "'%s' '%s' collides with '%s' '%s'\n",
274171164Smlaier			    anchor->path, anchor->name, dup->path, dup->name);
275171164Smlaier			rs_free(anchor);
276171164Smlaier			rs_free(p);
277171164Smlaier			return (NULL);
278171164Smlaier		}
279171164Smlaier		if (parent != NULL) {
280171164Smlaier			anchor->parent = parent;
281171164Smlaier			if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
282171164Smlaier			    anchor)) != NULL) {
283171164Smlaier				printf("pf_find_or_create_ruleset: "
284171164Smlaier				    "RB_INSERT2 '%s' '%s' collides with "
285171164Smlaier				    "'%s' '%s'\n", anchor->path, anchor->name,
286171164Smlaier				    dup->path, dup->name);
287223637Sbz#ifdef __FreeBSD__
288223637Sbz				RB_REMOVE(pf_anchor_global, &V_pf_anchors,
289223637Sbz#else
290171164Smlaier				RB_REMOVE(pf_anchor_global, &pf_anchors,
291223637Sbz#endif
292171164Smlaier				    anchor);
293171164Smlaier				rs_free(anchor);
294171164Smlaier				rs_free(p);
295171164Smlaier				return (NULL);
296171164Smlaier			}
297171164Smlaier		}
298171164Smlaier		pf_init_ruleset(&anchor->ruleset);
299171164Smlaier		anchor->ruleset.anchor = anchor;
300171164Smlaier		parent = anchor;
301171164Smlaier		if (r != NULL)
302171164Smlaier			q = r + 1;
303171164Smlaier		else
304171164Smlaier			*q = 0;
305171164Smlaier	}
306171164Smlaier	rs_free(p);
307171164Smlaier	return (&anchor->ruleset);
308171164Smlaier}
309171164Smlaier
310171164Smlaiervoid
311171164Smlaierpf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
312171164Smlaier{
313171164Smlaier	struct pf_anchor	*parent;
314171164Smlaier	int			 i;
315171164Smlaier
316171164Smlaier	while (ruleset != NULL) {
317171164Smlaier		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
318171164Smlaier		    !RB_EMPTY(&ruleset->anchor->children) ||
319171164Smlaier		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
320171164Smlaier		    ruleset->topen)
321171164Smlaier			return;
322171164Smlaier		for (i = 0; i < PF_RULESET_MAX; ++i)
323171164Smlaier			if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
324171164Smlaier			    !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
325171164Smlaier			    ruleset->rules[i].inactive.open)
326171164Smlaier				return;
327223637Sbz#ifdef __FreeBSD__
328223637Sbz		RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor);
329223637Sbz#else
330171164Smlaier		RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
331223637Sbz#endif
332171164Smlaier		if ((parent = ruleset->anchor->parent) != NULL)
333171164Smlaier			RB_REMOVE(pf_anchor_node, &parent->children,
334171164Smlaier			    ruleset->anchor);
335171164Smlaier		rs_free(ruleset->anchor);
336171164Smlaier		if (parent == NULL)
337171164Smlaier			return;
338171164Smlaier		ruleset = &parent->ruleset;
339171164Smlaier	}
340171164Smlaier}
341171164Smlaier
342171164Smlaierint
343171164Smlaierpf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
344171164Smlaier    const char *name)
345171164Smlaier{
346171164Smlaier	char			*p, *path;
347171164Smlaier	struct pf_ruleset	*ruleset;
348171164Smlaier
349171164Smlaier	r->anchor = NULL;
350171164Smlaier	r->anchor_relative = 0;
351171164Smlaier	r->anchor_wildcard = 0;
352171164Smlaier	if (!name[0])
353171164Smlaier		return (0);
354171164Smlaier	path = (char *)rs_malloc(MAXPATHLEN);
355223637Sbz	if (path == NULL)
356223637Sbz		return (1);
357171164Smlaier	if (name[0] == '/')
358171164Smlaier		strlcpy(path, name + 1, MAXPATHLEN);
359171164Smlaier	else {
360171164Smlaier		/* relative path */
361171164Smlaier		r->anchor_relative = 1;
362171164Smlaier		if (s->anchor == NULL || !s->anchor->path[0])
363171164Smlaier			path[0] = 0;
364171164Smlaier		else
365171164Smlaier			strlcpy(path, s->anchor->path, MAXPATHLEN);
366171164Smlaier		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
367171164Smlaier			if (!path[0]) {
368171164Smlaier				printf("pf_anchor_setup: .. beyond root\n");
369171164Smlaier				rs_free(path);
370171164Smlaier				return (1);
371171164Smlaier			}
372171164Smlaier			if ((p = strrchr(path, '/')) != NULL)
373171164Smlaier				*p = 0;
374171164Smlaier			else
375171164Smlaier				path[0] = 0;
376171164Smlaier			r->anchor_relative++;
377171164Smlaier			name += 3;
378171164Smlaier		}
379171164Smlaier		if (path[0])
380171164Smlaier			strlcat(path, "/", MAXPATHLEN);
381171164Smlaier		strlcat(path, name, MAXPATHLEN);
382171164Smlaier	}
383171164Smlaier	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
384171164Smlaier		r->anchor_wildcard = 1;
385171164Smlaier		*p = 0;
386171164Smlaier	}
387171164Smlaier	ruleset = pf_find_or_create_ruleset(path);
388171164Smlaier	rs_free(path);
389171164Smlaier	if (ruleset == NULL || ruleset->anchor == NULL) {
390171164Smlaier		printf("pf_anchor_setup: ruleset\n");
391171164Smlaier		return (1);
392171164Smlaier	}
393171164Smlaier	r->anchor = ruleset->anchor;
394171164Smlaier	r->anchor->refcnt++;
395171164Smlaier	return (0);
396171164Smlaier}
397171164Smlaier
398171164Smlaierint
399171164Smlaierpf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
400171164Smlaier    struct pfioc_rule *pr)
401171164Smlaier{
402171164Smlaier	pr->anchor_call[0] = 0;
403171164Smlaier	if (r->anchor == NULL)
404171164Smlaier		return (0);
405171164Smlaier	if (!r->anchor_relative) {
406171164Smlaier		strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
407171164Smlaier		strlcat(pr->anchor_call, r->anchor->path,
408171164Smlaier		    sizeof(pr->anchor_call));
409171164Smlaier	} else {
410171164Smlaier		char	*a, *p;
411171164Smlaier		int	 i;
412171164Smlaier
413171164Smlaier		a = (char *)rs_malloc(MAXPATHLEN);
414223637Sbz		if (a == NULL)
415223637Sbz			return (1);
416171164Smlaier		if (rs->anchor == NULL)
417171164Smlaier			a[0] = 0;
418171164Smlaier		else
419171164Smlaier			strlcpy(a, rs->anchor->path, MAXPATHLEN);
420171164Smlaier		for (i = 1; i < r->anchor_relative; ++i) {
421171164Smlaier			if ((p = strrchr(a, '/')) == NULL)
422171164Smlaier				p = a;
423171164Smlaier			*p = 0;
424171164Smlaier			strlcat(pr->anchor_call, "../",
425171164Smlaier			    sizeof(pr->anchor_call));
426171164Smlaier		}
427171164Smlaier		if (strncmp(a, r->anchor->path, strlen(a))) {
428171164Smlaier			printf("pf_anchor_copyout: '%s' '%s'\n", a,
429171164Smlaier			    r->anchor->path);
430171164Smlaier			rs_free(a);
431171164Smlaier			return (1);
432171164Smlaier		}
433171164Smlaier		if (strlen(r->anchor->path) > strlen(a))
434171164Smlaier			strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
435171164Smlaier			    strlen(a) + 1 : 0), sizeof(pr->anchor_call));
436171164Smlaier		rs_free(a);
437171164Smlaier	}
438171164Smlaier	if (r->anchor_wildcard)
439171164Smlaier		strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
440171164Smlaier		    sizeof(pr->anchor_call));
441171164Smlaier	return (0);
442171164Smlaier}
443171164Smlaier
444171164Smlaiervoid
445171164Smlaierpf_anchor_remove(struct pf_rule *r)
446171164Smlaier{
447171164Smlaier	if (r->anchor == NULL)
448171164Smlaier		return;
449171164Smlaier	if (r->anchor->refcnt <= 0) {
450171164Smlaier		printf("pf_anchor_remove: broken refcount\n");
451171164Smlaier		r->anchor = NULL;
452171164Smlaier		return;
453171164Smlaier	}
454171164Smlaier	if (!--r->anchor->refcnt)
455171164Smlaier		pf_remove_if_empty_ruleset(&r->anchor->ruleset);
456171164Smlaier	r->anchor = NULL;
457171164Smlaier}
458