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