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$"); 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 89 VNET_DEFINE(struct pf_anchor_global, pf_anchors); 90 VNET_DEFINE(struct pf_anchor, pf_main_anchor); 91 #else /* ! _KERNEL */ 92 struct pf_anchor_global pf_anchors; 93 struct 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 100 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 101 102 static struct pf_anchor *pf_find_anchor(const char *); 103 104 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 105 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 106 107 static __inline int 108 pf_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 115 int 116 pf_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 145 void 146 pf_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 159 static struct pf_anchor * 160 pf_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 173 struct pf_ruleset * 174 pf_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 189 struct pf_ruleset * 190 pf_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 281 void 282 pf_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 309 int 310 pf_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 365 int 366 pf_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 411 void 412 pf_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