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 88 VNET_DEFINE(struct pf_anchor_global, pf_anchors); 89 VNET_DEFINE(struct pf_anchor, pf_main_anchor); 90 #else /* ! _KERNEL */ 91 struct pf_anchor_global pf_anchors; 92 struct 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 99 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 100 101 static struct pf_anchor *pf_find_anchor(const char *); 102 103 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 104 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 105 106 static __inline int 107 pf_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 114 int 115 pf_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 144 void 145 pf_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 158 static struct pf_anchor * 159 pf_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 172 struct pf_ruleset * 173 pf_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 188 struct pf_ruleset * 189 pf_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 280 void 281 pf_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 308 int 309 pf_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 364 int 365 pf_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 410 void 411 pf_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