1/*-
2 * Copyright (c) 2001 Daniel Hartmeier
3 * Copyright (c) 2002,2003 Henning Brauer
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 *    - Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    - Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials provided
15 *      with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Effort sponsored in part by the Defense Advanced Research Projects
31 * Agency (DARPA) and Air Force Research Laboratory, Air Force
32 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
33 *
34 *	$OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include <sys/param.h>
41#include <sys/socket.h>
42#ifdef _KERNEL
43# include <sys/systm.h>
44# include <sys/refcount.h>
45#endif /* _KERNEL */
46#include <sys/mbuf.h>
47
48#include <netinet/in.h>
49#include <netinet/in_systm.h>
50#include <netinet/ip.h>
51#include <netinet/tcp.h>
52
53#include <net/if.h>
54#include <net/pfvar.h>
55
56#ifdef INET6
57#include <netinet/ip6.h>
58#endif /* INET6 */
59
60
61#ifdef _KERNEL
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
68#else
69/* Userland equivalents so we can lend code to pfctl et al. */
70
71#include <arpa/inet.h>
72#include <errno.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#define rs_malloc(x)		 calloc(1, x)
77#define rs_free(x)		 free(x)
78
79#ifdef PFDEBUG
80#include <sys/stdarg.h>
81#define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
82#else
83#define DPFPRINTF(format, x...)	((void)0)
84#endif /* PFDEBUG */
85#endif /* _KERNEL */
86
87#ifdef _KERNEL
88VNET_DEFINE(struct pf_anchor_global,	pf_anchors);
89VNET_DEFINE(struct pf_anchor,		pf_main_anchor);
90#else /* ! _KERNEL */
91struct pf_anchor_global	 pf_anchors;
92struct pf_anchor	 pf_main_anchor;
93#undef V_pf_anchors
94#define V_pf_anchors		 pf_anchors
95#undef pf_main_ruleset
96#define pf_main_ruleset		 pf_main_anchor.ruleset
97#endif /* _KERNEL */
98
99static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
100
101static struct pf_anchor		*pf_find_anchor(const char *);
102
103RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
104RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
105
106static __inline int
107pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
108{
109	int c = strcmp(a->path, b->path);
110
111	return (c ? (c < 0 ? -1 : 1) : 0);
112}
113
114int
115pf_get_ruleset_number(u_int8_t action)
116{
117	switch (action) {
118	case PF_SCRUB:
119	case PF_NOSCRUB:
120		return (PF_RULESET_SCRUB);
121		break;
122	case PF_PASS:
123	case PF_DROP:
124		return (PF_RULESET_FILTER);
125		break;
126	case PF_NAT:
127	case PF_NONAT:
128		return (PF_RULESET_NAT);
129		break;
130	case PF_BINAT:
131	case PF_NOBINAT:
132		return (PF_RULESET_BINAT);
133		break;
134	case PF_RDR:
135	case PF_NORDR:
136		return (PF_RULESET_RDR);
137		break;
138	default:
139		return (PF_RULESET_MAX);
140		break;
141	}
142}
143
144void
145pf_init_ruleset(struct pf_ruleset *ruleset)
146{
147	int	i;
148
149	memset(ruleset, 0, sizeof(struct pf_ruleset));
150	for (i = 0; i < PF_RULESET_MAX; i++) {
151		TAILQ_INIT(&ruleset->rules[i].queues[0]);
152		TAILQ_INIT(&ruleset->rules[i].queues[1]);
153		ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
154		ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
155	}
156}
157
158static struct pf_anchor *
159pf_find_anchor(const char *path)
160{
161	struct pf_anchor	*key, *found;
162
163	key = (struct pf_anchor *)rs_malloc(sizeof(*key));
164	if (key == NULL)
165		return (NULL);
166	strlcpy(key->path, path, sizeof(key->path));
167	found = RB_FIND(pf_anchor_global, &V_pf_anchors, key);
168	rs_free(key);
169	return (found);
170}
171
172struct pf_ruleset *
173pf_find_ruleset(const char *path)
174{
175	struct pf_anchor	*anchor;
176
177	while (*path == '/')
178		path++;
179	if (!*path)
180		return (&pf_main_ruleset);
181	anchor = pf_find_anchor(path);
182	if (anchor == NULL)
183		return (NULL);
184	else
185		return (&anchor->ruleset);
186}
187
188struct pf_ruleset *
189pf_find_or_create_ruleset(const char *path)
190{
191	char			*p, *q, *r;
192	struct pf_ruleset	*ruleset;
193	struct pf_anchor	*anchor = NULL, *dup, *parent = NULL;
194
195	if (path[0] == 0)
196		return (&pf_main_ruleset);
197	while (*path == '/')
198		path++;
199	ruleset = pf_find_ruleset(path);
200	if (ruleset != NULL)
201		return (ruleset);
202	p = (char *)rs_malloc(MAXPATHLEN);
203	if (p == NULL)
204		return (NULL);
205	strlcpy(p, path, MAXPATHLEN);
206	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
207		*q = 0;
208		if ((ruleset = pf_find_ruleset(p)) != NULL) {
209			parent = ruleset->anchor;
210			break;
211		}
212	}
213	if (q == NULL)
214		q = p;
215	else
216		q++;
217	strlcpy(p, path, MAXPATHLEN);
218	if (!*q) {
219		rs_free(p);
220		return (NULL);
221	}
222	while ((r = strchr(q, '/')) != NULL || *q) {
223		if (r != NULL)
224			*r = 0;
225		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
226		    (parent != NULL && strlen(parent->path) >=
227		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
228			rs_free(p);
229			return (NULL);
230		}
231		anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor));
232		if (anchor == NULL) {
233			rs_free(p);
234			return (NULL);
235		}
236		RB_INIT(&anchor->children);
237		strlcpy(anchor->name, q, sizeof(anchor->name));
238		if (parent != NULL) {
239			strlcpy(anchor->path, parent->path,
240			    sizeof(anchor->path));
241			strlcat(anchor->path, "/", sizeof(anchor->path));
242		}
243		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
244		if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) !=
245		    NULL) {
246			printf("pf_find_or_create_ruleset: RB_INSERT1 "
247			    "'%s' '%s' collides with '%s' '%s'\n",
248			    anchor->path, anchor->name, dup->path, dup->name);
249			rs_free(anchor);
250			rs_free(p);
251			return (NULL);
252		}
253		if (parent != NULL) {
254			anchor->parent = parent;
255			if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
256			    anchor)) != NULL) {
257				printf("pf_find_or_create_ruleset: "
258				    "RB_INSERT2 '%s' '%s' collides with "
259				    "'%s' '%s'\n", anchor->path, anchor->name,
260				    dup->path, dup->name);
261				RB_REMOVE(pf_anchor_global, &V_pf_anchors,
262				    anchor);
263				rs_free(anchor);
264				rs_free(p);
265				return (NULL);
266			}
267		}
268		pf_init_ruleset(&anchor->ruleset);
269		anchor->ruleset.anchor = anchor;
270		parent = anchor;
271		if (r != NULL)
272			q = r + 1;
273		else
274			*q = 0;
275	}
276	rs_free(p);
277	return (&anchor->ruleset);
278}
279
280void
281pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
282{
283	struct pf_anchor	*parent;
284	int			 i;
285
286	while (ruleset != NULL) {
287		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
288		    !RB_EMPTY(&ruleset->anchor->children) ||
289		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
290		    ruleset->topen)
291			return;
292		for (i = 0; i < PF_RULESET_MAX; ++i)
293			if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
294			    !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
295			    ruleset->rules[i].inactive.open)
296				return;
297		RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor);
298		if ((parent = ruleset->anchor->parent) != NULL)
299			RB_REMOVE(pf_anchor_node, &parent->children,
300			    ruleset->anchor);
301		rs_free(ruleset->anchor);
302		if (parent == NULL)
303			return;
304		ruleset = &parent->ruleset;
305	}
306}
307
308int
309pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
310    const char *name)
311{
312	char			*p, *path;
313	struct pf_ruleset	*ruleset;
314
315	r->anchor = NULL;
316	r->anchor_relative = 0;
317	r->anchor_wildcard = 0;
318	if (!name[0])
319		return (0);
320	path = (char *)rs_malloc(MAXPATHLEN);
321	if (path == NULL)
322		return (1);
323	if (name[0] == '/')
324		strlcpy(path, name + 1, MAXPATHLEN);
325	else {
326		/* relative path */
327		r->anchor_relative = 1;
328		if (s->anchor == NULL || !s->anchor->path[0])
329			path[0] = 0;
330		else
331			strlcpy(path, s->anchor->path, MAXPATHLEN);
332		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
333			if (!path[0]) {
334				printf("pf_anchor_setup: .. beyond root\n");
335				rs_free(path);
336				return (1);
337			}
338			if ((p = strrchr(path, '/')) != NULL)
339				*p = 0;
340			else
341				path[0] = 0;
342			r->anchor_relative++;
343			name += 3;
344		}
345		if (path[0])
346			strlcat(path, "/", MAXPATHLEN);
347		strlcat(path, name, MAXPATHLEN);
348	}
349	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
350		r->anchor_wildcard = 1;
351		*p = 0;
352	}
353	ruleset = pf_find_or_create_ruleset(path);
354	rs_free(path);
355	if (ruleset == NULL || ruleset->anchor == NULL) {
356		printf("pf_anchor_setup: ruleset\n");
357		return (1);
358	}
359	r->anchor = ruleset->anchor;
360	r->anchor->refcnt++;
361	return (0);
362}
363
364int
365pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
366    struct pfioc_rule *pr)
367{
368	pr->anchor_call[0] = 0;
369	if (r->anchor == NULL)
370		return (0);
371	if (!r->anchor_relative) {
372		strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
373		strlcat(pr->anchor_call, r->anchor->path,
374		    sizeof(pr->anchor_call));
375	} else {
376		char	*a, *p;
377		int	 i;
378
379		a = (char *)rs_malloc(MAXPATHLEN);
380		if (a == NULL)
381			return (1);
382		if (rs->anchor == NULL)
383			a[0] = 0;
384		else
385			strlcpy(a, rs->anchor->path, MAXPATHLEN);
386		for (i = 1; i < r->anchor_relative; ++i) {
387			if ((p = strrchr(a, '/')) == NULL)
388				p = a;
389			*p = 0;
390			strlcat(pr->anchor_call, "../",
391			    sizeof(pr->anchor_call));
392		}
393		if (strncmp(a, r->anchor->path, strlen(a))) {
394			printf("pf_anchor_copyout: '%s' '%s'\n", a,
395			    r->anchor->path);
396			rs_free(a);
397			return (1);
398		}
399		if (strlen(r->anchor->path) > strlen(a))
400			strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
401			    strlen(a) + 1 : 0), sizeof(pr->anchor_call));
402		rs_free(a);
403	}
404	if (r->anchor_wildcard)
405		strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
406		    sizeof(pr->anchor_call));
407	return (0);
408}
409
410void
411pf_anchor_remove(struct pf_rule *r)
412{
413	if (r->anchor == NULL)
414		return;
415	if (r->anchor->refcnt <= 0) {
416		printf("pf_anchor_remove: broken refcount\n");
417		r->anchor = NULL;
418		return;
419	}
420	if (!--r->anchor->refcnt)
421		pf_remove_if_empty_ruleset(&r->anchor->ruleset);
422	r->anchor = NULL;
423}
424