1 /* $OpenBSD: pf_ruleset.c,v 1.1 2006/10/27 13:56:51 mcbride 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/param.h> 39 #include <sys/socket.h> 40 #ifdef _KERNEL 41 # include <sys/systm.h> 42 # include <sys/malloc.h> 43 #endif /* _KERNEL */ 44 #include <sys/mbuf.h> 45 46 #include <netinet/in.h> 47 #include <netinet/in_systm.h> 48 #include <netinet/ip.h> 49 #include <netinet/tcp.h> 50 51 #include <net/if.h> 52 #include <net/pf/pfvar.h> 53 54 #ifdef INET6 55 #include <netinet/ip6.h> 56 #endif /* INET6 */ 57 58 59 #ifdef _KERNEL 60 # define DPFPRINTF(format, x...) \ 61 if (pf_status.debug >= PF_DEBUG_NOISY) \ 62 kprintf(format , ##x) 63 #define rs_malloc(x) kmalloc(x, M_PFRS, M_WAITOK) 64 #define rs_free(x) kfree(x, M_PFRS) 65 #define printf kprintf 66 67 static MALLOC_DEFINE(M_PFRS, "pfrulesetpl", "pf ruleset pool list"); 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 struct pf_anchor_global pf_anchors; 89 struct pf_anchor pf_main_anchor; 90 91 /* 92 * XXX: hum? 93 int pf_get_ruleset_number(u_int8_t); 94 void pf_init_ruleset(struct pf_ruleset *); 95 int pf_anchor_setup(struct pf_rule *, 96 const struct pf_ruleset *, const char *); 97 int pf_anchor_copyout(const struct pf_ruleset *, 98 const struct pf_rule *, struct pfioc_rule *); 99 void pf_anchor_remove(struct pf_rule *); 100 */ 101 102 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 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 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 memset(key, 0, sizeof(*key)); 166 strlcpy(key->path, path, sizeof(key->path)); 167 found = RB_FIND(pf_anchor_global, &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 bzero(p, MAXPATHLEN); 204 strlcpy(p, path, MAXPATHLEN); 205 while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 206 *q = 0; 207 if ((ruleset = pf_find_ruleset(p)) != NULL) { 208 parent = ruleset->anchor; 209 break; 210 } 211 } 212 if (q == NULL) 213 q = p; 214 else 215 q++; 216 strlcpy(p, path, MAXPATHLEN); 217 if (!*q) { 218 rs_free(p); 219 return (NULL); 220 } 221 while ((r = strchr(q, '/')) != NULL || *q) { 222 if (r != NULL) 223 *r = 0; 224 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 225 (parent != NULL && strlen(parent->path) >= 226 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 227 rs_free(p); 228 return (NULL); 229 } 230 anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); 231 if (anchor == NULL) { 232 rs_free(p); 233 return (NULL); 234 } 235 memset(anchor, 0, sizeof(*anchor)); 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, &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, &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, &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 bzero(path, MAXPATHLEN); 322 if (name[0] == '/') 323 strlcpy(path, name + 1, MAXPATHLEN); 324 else { 325 /* relative path */ 326 r->anchor_relative = 1; 327 if (s->anchor == NULL || !s->anchor->path[0]) 328 path[0] = 0; 329 else 330 strlcpy(path, s->anchor->path, MAXPATHLEN); 331 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 332 if (!path[0]) { 333 printf("pf_anchor_setup: .. beyond root\n"); 334 rs_free(path); 335 return (1); 336 } 337 if ((p = strrchr(path, '/')) != NULL) 338 *p = 0; 339 else 340 path[0] = 0; 341 r->anchor_relative++; 342 name += 3; 343 } 344 if (path[0]) 345 strlcat(path, "/", MAXPATHLEN); 346 strlcat(path, name, MAXPATHLEN); 347 } 348 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 349 r->anchor_wildcard = 1; 350 *p = 0; 351 } 352 ruleset = pf_find_or_create_ruleset(path); 353 rs_free(path); 354 if (ruleset == NULL || ruleset->anchor == NULL) { 355 printf("pf_anchor_setup: ruleset\n"); 356 return (1); 357 } 358 r->anchor = ruleset->anchor; 359 r->anchor->refcnt++; 360 return (0); 361 } 362 363 int 364 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 365 struct pfioc_rule *pr) 366 { 367 pr->anchor_call[0] = 0; 368 if (r->anchor == NULL) 369 return (0); 370 if (!r->anchor_relative) { 371 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 372 strlcat(pr->anchor_call, r->anchor->path, 373 sizeof(pr->anchor_call)); 374 } else { 375 char *a, *p; 376 int i; 377 378 a = (char *)rs_malloc(MAXPATHLEN); 379 bzero(a, MAXPATHLEN); 380 if (rs->anchor == NULL) 381 a[0] = 0; 382 else 383 strlcpy(a, rs->anchor->path, MAXPATHLEN); 384 for (i = 1; i < r->anchor_relative; ++i) { 385 if ((p = strrchr(a, '/')) == NULL) 386 p = a; 387 *p = 0; 388 strlcat(pr->anchor_call, "../", 389 sizeof(pr->anchor_call)); 390 } 391 if (strncmp(a, r->anchor->path, strlen(a))) { 392 printf("pf_anchor_copyout: '%s' '%s'\n", a, 393 r->anchor->path); 394 rs_free(a); 395 return (1); 396 } 397 if (strlen(r->anchor->path) > strlen(a)) 398 strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 399 strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 400 rs_free(a); 401 } 402 if (r->anchor_wildcard) 403 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 404 sizeof(pr->anchor_call)); 405 return (0); 406 } 407 408 void 409 pf_anchor_remove(struct pf_rule *r) 410 { 411 if (r->anchor == NULL) 412 return; 413 if (r->anchor->refcnt <= 0) { 414 printf("pf_anchor_remove: broken refcount\n"); 415 r->anchor = NULL; 416 return; 417 } 418 if (!--r->anchor->refcnt) 419 pf_remove_if_empty_ruleset(&r->anchor->ruleset); 420 r->anchor = NULL; 421 } 422