pf_ruleset.c revision 240494
1/*	$OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ */
2
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 */
37
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/sys/netpfil/pf/pf_ruleset.c 240494 2012-09-14 11:51:49Z glebius $");
40
41#include <sys/param.h>
42#include <sys/socket.h>
43#ifdef _KERNEL
44# include <sys/systm.h>
45# include <sys/refcount.h>
46#endif /* _KERNEL */
47#include <sys/mbuf.h>
48
49#include <netinet/in.h>
50#include <netinet/in_systm.h>
51#include <netinet/ip.h>
52#include <netinet/tcp.h>
53
54#include <net/if.h>
55#include <net/pfvar.h>
56
57#ifdef INET6
58#include <netinet/ip6.h>
59#endif /* INET6 */
60
61
62#ifdef _KERNEL
63#define DPFPRINTF(format, x...)				\
64	if (V_pf_status.debug >= PF_DEBUG_NOISY)	\
65		printf(format , ##x)
66#define rs_malloc(x)		malloc(x, M_TEMP, M_NOWAIT|M_ZERO)
67#define rs_free(x)		free(x, M_TEMP)
68
69#else
70/* Userland equivalents so we can lend code to pfctl et al. */
71
72#include <arpa/inet.h>
73#include <errno.h>
74#include <stdio.h>
75#include <stdlib.h>
76#include <string.h>
77#define rs_malloc(x)		 calloc(1, x)
78#define rs_free(x)		 free(x)
79
80#ifdef PFDEBUG
81#include <sys/stdarg.h>
82#define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
83#else
84#define DPFPRINTF(format, x...)	((void)0)
85#endif /* PFDEBUG */
86#endif /* _KERNEL */
87
88#ifdef _KERNEL
89VNET_DEFINE(struct pf_anchor_global,	pf_anchors);
90VNET_DEFINE(struct pf_anchor,		pf_main_anchor);
91#else /* ! _KERNEL */
92struct pf_anchor_global	 pf_anchors;
93struct pf_anchor	 pf_main_anchor;
94#undef V_pf_anchors
95#define V_pf_anchors		 pf_anchors
96#undef pf_main_ruleset
97#define pf_main_ruleset		 pf_main_anchor.ruleset
98#endif /* _KERNEL */
99
100static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
101
102static struct pf_anchor		*pf_find_anchor(const char *);
103
104RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
105RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
106
107static __inline int
108pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
109{
110	int c = strcmp(a->path, b->path);
111
112	return (c ? (c < 0 ? -1 : 1) : 0);
113}
114
115int
116pf_get_ruleset_number(u_int8_t action)
117{
118	switch (action) {
119	case PF_SCRUB:
120	case PF_NOSCRUB:
121		return (PF_RULESET_SCRUB);
122		break;
123	case PF_PASS:
124	case PF_DROP:
125		return (PF_RULESET_FILTER);
126		break;
127	case PF_NAT:
128	case PF_NONAT:
129		return (PF_RULESET_NAT);
130		break;
131	case PF_BINAT:
132	case PF_NOBINAT:
133		return (PF_RULESET_BINAT);
134		break;
135	case PF_RDR:
136	case PF_NORDR:
137		return (PF_RULESET_RDR);
138		break;
139	default:
140		return (PF_RULESET_MAX);
141		break;
142	}
143}
144
145void
146pf_init_ruleset(struct pf_ruleset *ruleset)
147{
148	int	i;
149
150	memset(ruleset, 0, sizeof(struct pf_ruleset));
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
159static struct pf_anchor *
160pf_find_anchor(const char *path)
161{
162	struct pf_anchor	*key, *found;
163
164	key = (struct pf_anchor *)rs_malloc(sizeof(*key));
165	if (key == NULL)
166		return (NULL);
167	strlcpy(key->path, path, sizeof(key->path));
168	found = RB_FIND(pf_anchor_global, &V_pf_anchors, key);
169	rs_free(key);
170	return (found);
171}
172
173struct pf_ruleset *
174pf_find_ruleset(const char *path)
175{
176	struct pf_anchor	*anchor;
177
178	while (*path == '/')
179		path++;
180	if (!*path)
181		return (&pf_main_ruleset);
182	anchor = pf_find_anchor(path);
183	if (anchor == NULL)
184		return (NULL);
185	else
186		return (&anchor->ruleset);
187}
188
189struct pf_ruleset *
190pf_find_or_create_ruleset(const char *path)
191{
192	char			*p, *q, *r;
193	struct pf_ruleset	*ruleset;
194	struct pf_anchor	*anchor = NULL, *dup, *parent = NULL;
195
196	if (path[0] == 0)
197		return (&pf_main_ruleset);
198	while (*path == '/')
199		path++;
200	ruleset = pf_find_ruleset(path);
201	if (ruleset != NULL)
202		return (ruleset);
203	p = (char *)rs_malloc(MAXPATHLEN);
204	if (p == NULL)
205		return (NULL);
206	strlcpy(p, path, MAXPATHLEN);
207	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
208		*q = 0;
209		if ((ruleset = pf_find_ruleset(p)) != NULL) {
210			parent = ruleset->anchor;
211			break;
212		}
213	}
214	if (q == NULL)
215		q = p;
216	else
217		q++;
218	strlcpy(p, path, MAXPATHLEN);
219	if (!*q) {
220		rs_free(p);
221		return (NULL);
222	}
223	while ((r = strchr(q, '/')) != NULL || *q) {
224		if (r != NULL)
225			*r = 0;
226		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
227		    (parent != NULL && strlen(parent->path) >=
228		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
229			rs_free(p);
230			return (NULL);
231		}
232		anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor));
233		if (anchor == NULL) {
234			rs_free(p);
235			return (NULL);
236		}
237		RB_INIT(&anchor->children);
238		strlcpy(anchor->name, q, sizeof(anchor->name));
239		if (parent != NULL) {
240			strlcpy(anchor->path, parent->path,
241			    sizeof(anchor->path));
242			strlcat(anchor->path, "/", sizeof(anchor->path));
243		}
244		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
245		if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) !=
246		    NULL) {
247			printf("pf_find_or_create_ruleset: RB_INSERT1 "
248			    "'%s' '%s' collides with '%s' '%s'\n",
249			    anchor->path, anchor->name, dup->path, dup->name);
250			rs_free(anchor);
251			rs_free(p);
252			return (NULL);
253		}
254		if (parent != NULL) {
255			anchor->parent = parent;
256			if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
257			    anchor)) != NULL) {
258				printf("pf_find_or_create_ruleset: "
259				    "RB_INSERT2 '%s' '%s' collides with "
260				    "'%s' '%s'\n", anchor->path, anchor->name,
261				    dup->path, dup->name);
262				RB_REMOVE(pf_anchor_global, &V_pf_anchors,
263				    anchor);
264				rs_free(anchor);
265				rs_free(p);
266				return (NULL);
267			}
268		}
269		pf_init_ruleset(&anchor->ruleset);
270		anchor->ruleset.anchor = anchor;
271		parent = anchor;
272		if (r != NULL)
273			q = r + 1;
274		else
275			*q = 0;
276	}
277	rs_free(p);
278	return (&anchor->ruleset);
279}
280
281void
282pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
283{
284	struct pf_anchor	*parent;
285	int			 i;
286
287	while (ruleset != NULL) {
288		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
289		    !RB_EMPTY(&ruleset->anchor->children) ||
290		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
291		    ruleset->topen)
292			return;
293		for (i = 0; i < PF_RULESET_MAX; ++i)
294			if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
295			    !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
296			    ruleset->rules[i].inactive.open)
297				return;
298		RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor);
299		if ((parent = ruleset->anchor->parent) != NULL)
300			RB_REMOVE(pf_anchor_node, &parent->children,
301			    ruleset->anchor);
302		rs_free(ruleset->anchor);
303		if (parent == NULL)
304			return;
305		ruleset = &parent->ruleset;
306	}
307}
308
309int
310pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
311    const char *name)
312{
313	char			*p, *path;
314	struct pf_ruleset	*ruleset;
315
316	r->anchor = NULL;
317	r->anchor_relative = 0;
318	r->anchor_wildcard = 0;
319	if (!name[0])
320		return (0);
321	path = (char *)rs_malloc(MAXPATHLEN);
322	if (path == NULL)
323		return (1);
324	if (name[0] == '/')
325		strlcpy(path, name + 1, MAXPATHLEN);
326	else {
327		/* relative path */
328		r->anchor_relative = 1;
329		if (s->anchor == NULL || !s->anchor->path[0])
330			path[0] = 0;
331		else
332			strlcpy(path, s->anchor->path, MAXPATHLEN);
333		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
334			if (!path[0]) {
335				printf("pf_anchor_setup: .. beyond root\n");
336				rs_free(path);
337				return (1);
338			}
339			if ((p = strrchr(path, '/')) != NULL)
340				*p = 0;
341			else
342				path[0] = 0;
343			r->anchor_relative++;
344			name += 3;
345		}
346		if (path[0])
347			strlcat(path, "/", MAXPATHLEN);
348		strlcat(path, name, MAXPATHLEN);
349	}
350	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
351		r->anchor_wildcard = 1;
352		*p = 0;
353	}
354	ruleset = pf_find_or_create_ruleset(path);
355	rs_free(path);
356	if (ruleset == NULL || ruleset->anchor == NULL) {
357		printf("pf_anchor_setup: ruleset\n");
358		return (1);
359	}
360	r->anchor = ruleset->anchor;
361	r->anchor->refcnt++;
362	return (0);
363}
364
365int
366pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
367    struct pfioc_rule *pr)
368{
369	pr->anchor_call[0] = 0;
370	if (r->anchor == NULL)
371		return (0);
372	if (!r->anchor_relative) {
373		strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
374		strlcat(pr->anchor_call, r->anchor->path,
375		    sizeof(pr->anchor_call));
376	} else {
377		char	*a, *p;
378		int	 i;
379
380		a = (char *)rs_malloc(MAXPATHLEN);
381		if (a == NULL)
382			return (1);
383		if (rs->anchor == NULL)
384			a[0] = 0;
385		else
386			strlcpy(a, rs->anchor->path, MAXPATHLEN);
387		for (i = 1; i < r->anchor_relative; ++i) {
388			if ((p = strrchr(a, '/')) == NULL)
389				p = a;
390			*p = 0;
391			strlcat(pr->anchor_call, "../",
392			    sizeof(pr->anchor_call));
393		}
394		if (strncmp(a, r->anchor->path, strlen(a))) {
395			printf("pf_anchor_copyout: '%s' '%s'\n", a,
396			    r->anchor->path);
397			rs_free(a);
398			return (1);
399		}
400		if (strlen(r->anchor->path) > strlen(a))
401			strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
402			    strlen(a) + 1 : 0), sizeof(pr->anchor_call));
403		rs_free(a);
404	}
405	if (r->anchor_wildcard)
406		strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
407		    sizeof(pr->anchor_call));
408	return (0);
409}
410
411void
412pf_anchor_remove(struct pf_rule *r)
413{
414	if (r->anchor == NULL)
415		return;
416	if (r->anchor->refcnt <= 0) {
417		printf("pf_anchor_remove: broken refcount\n");
418		r->anchor = NULL;
419		return;
420	}
421	if (!--r->anchor->refcnt)
422		pf_remove_if_empty_ruleset(&r->anchor->ruleset);
423	r->anchor = NULL;
424}
425