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 55 #ifdef _KERNEL 56 # define DPFPRINTF(format, x...) \ 57 if (pf_status.debug >= PF_DEBUG_NOISY) \ 58 kprintf(format , ##x) 59 #define rs_malloc(x) kmalloc(x, M_PFRS, M_WAITOK) 60 #define rs_free(x) kfree(x, M_PFRS) 61 #define printf kprintf 62 63 static MALLOC_DEFINE(M_PFRS, "pfrulesetpl", "pf ruleset pool list"); 64 65 #else 66 /* Userland equivalents so we can lend code to pfctl et al. */ 67 68 # include <arpa/inet.h> 69 # include <errno.h> 70 # include <stdio.h> 71 # include <stdlib.h> 72 # include <string.h> 73 # define rs_malloc(x) malloc(x) 74 # define rs_free(x) free(x) 75 76 # ifdef PFDEBUG 77 # include <sys/stdarg.h> 78 # define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) 79 # else 80 # define DPFPRINTF(format, x...) ((void)0) 81 # endif /* PFDEBUG */ 82 #endif /* _KERNEL */ 83 84 struct pf_anchor_global pf_anchors; 85 struct pf_anchor pf_main_anchor; 86 87 /* 88 * XXX: hum? 89 int pf_get_ruleset_number(u_int8_t); 90 void pf_init_ruleset(struct pf_ruleset *); 91 int pf_anchor_setup(struct pf_rule *, 92 const struct pf_ruleset *, const char *); 93 int pf_anchor_copyout(const struct pf_ruleset *, 94 const struct pf_rule *, struct pfioc_rule *); 95 void pf_anchor_remove(struct pf_rule *); 96 */ 97 98 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 99 100 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 101 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 102 103 static __inline int 104 pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) 105 { 106 int c = strcmp(a->path, b->path); 107 108 return (c ? (c < 0 ? -1 : 1) : 0); 109 } 110 111 int 112 pf_get_ruleset_number(u_int8_t action) 113 { 114 switch (action) { 115 case PF_SCRUB: 116 case PF_NOSCRUB: 117 return (PF_RULESET_SCRUB); 118 break; 119 case PF_PASS: 120 case PF_DROP: 121 return (PF_RULESET_FILTER); 122 break; 123 case PF_NAT: 124 case PF_NONAT: 125 return (PF_RULESET_NAT); 126 break; 127 case PF_BINAT: 128 case PF_NOBINAT: 129 return (PF_RULESET_BINAT); 130 break; 131 case PF_RDR: 132 case PF_NORDR: 133 return (PF_RULESET_RDR); 134 break; 135 default: 136 return (PF_RULESET_MAX); 137 break; 138 } 139 } 140 141 void 142 pf_init_ruleset(struct pf_ruleset *ruleset) 143 { 144 int i; 145 146 memset(ruleset, 0, sizeof(struct pf_ruleset)); 147 for (i = 0; i < PF_RULESET_MAX; i++) { 148 TAILQ_INIT(&ruleset->rules[i].queues[0]); 149 TAILQ_INIT(&ruleset->rules[i].queues[1]); 150 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 151 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 152 } 153 } 154 155 struct pf_anchor * 156 pf_find_anchor(const char *path) 157 { 158 struct pf_anchor *key, *found; 159 160 key = (struct pf_anchor *)rs_malloc(sizeof(*key)); 161 memset(key, 0, sizeof(*key)); 162 strlcpy(key->path, path, sizeof(key->path)); 163 found = RB_FIND(pf_anchor_global, &pf_anchors, key); 164 rs_free(key); 165 return (found); 166 } 167 168 struct pf_ruleset * 169 pf_find_ruleset(const char *path) 170 { 171 struct pf_anchor *anchor; 172 173 while (*path == '/') 174 path++; 175 if (!*path) 176 return (&pf_main_ruleset); 177 anchor = pf_find_anchor(path); 178 if (anchor == NULL) 179 return (NULL); 180 else 181 return (&anchor->ruleset); 182 } 183 184 struct pf_ruleset * 185 pf_find_or_create_ruleset(const char *path) 186 { 187 char *p, *q, *r; 188 struct pf_ruleset *ruleset; 189 struct pf_anchor *anchor = NULL, *dup, *parent = NULL; 190 191 if (path[0] == 0) 192 return (&pf_main_ruleset); 193 while (*path == '/') 194 path++; 195 ruleset = pf_find_ruleset(path); 196 if (ruleset != NULL) 197 return (ruleset); 198 p = (char *)rs_malloc(MAXPATHLEN); 199 bzero(p, MAXPATHLEN); 200 strlcpy(p, path, MAXPATHLEN); 201 while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 202 *q = 0; 203 if ((ruleset = pf_find_ruleset(p)) != NULL) { 204 parent = ruleset->anchor; 205 break; 206 } 207 } 208 if (q == NULL) 209 q = p; 210 else 211 q++; 212 strlcpy(p, path, MAXPATHLEN); 213 if (!*q) { 214 rs_free(p); 215 return (NULL); 216 } 217 while ((r = strchr(q, '/')) != NULL || *q) { 218 if (r != NULL) 219 *r = 0; 220 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 221 (parent != NULL && strlen(parent->path) >= 222 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 223 rs_free(p); 224 return (NULL); 225 } 226 anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); 227 if (anchor == NULL) { 228 rs_free(p); 229 return (NULL); 230 } 231 memset(anchor, 0, sizeof(*anchor)); 232 RB_INIT(&anchor->children); 233 strlcpy(anchor->name, q, sizeof(anchor->name)); 234 if (parent != NULL) { 235 strlcpy(anchor->path, parent->path, 236 sizeof(anchor->path)); 237 strlcat(anchor->path, "/", sizeof(anchor->path)); 238 } 239 strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 240 if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != 241 NULL) { 242 printf("pf_find_or_create_ruleset: RB_INSERT1 " 243 "'%s' '%s' collides with '%s' '%s'\n", 244 anchor->path, anchor->name, dup->path, dup->name); 245 rs_free(anchor); 246 rs_free(p); 247 return (NULL); 248 } 249 if (parent != NULL) { 250 anchor->parent = parent; 251 if ((dup = RB_INSERT(pf_anchor_node, &parent->children, 252 anchor)) != NULL) { 253 printf("pf_find_or_create_ruleset: " 254 "RB_INSERT2 '%s' '%s' collides with " 255 "'%s' '%s'\n", anchor->path, anchor->name, 256 dup->path, dup->name); 257 RB_REMOVE(pf_anchor_global, &pf_anchors, 258 anchor); 259 rs_free(anchor); 260 rs_free(p); 261 return (NULL); 262 } 263 } 264 pf_init_ruleset(&anchor->ruleset); 265 anchor->ruleset.anchor = anchor; 266 parent = anchor; 267 if (r != NULL) 268 q = r + 1; 269 else 270 *q = 0; 271 } 272 rs_free(p); 273 return (&anchor->ruleset); 274 } 275 276 void 277 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 278 { 279 struct pf_anchor *parent; 280 int i; 281 282 while (ruleset != NULL) { 283 if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || 284 !RB_EMPTY(&ruleset->anchor->children) || 285 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 286 ruleset->topen) 287 return; 288 for (i = 0; i < PF_RULESET_MAX; ++i) 289 if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 290 !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || 291 ruleset->rules[i].inactive.open) 292 return; 293 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); 294 if ((parent = ruleset->anchor->parent) != NULL) 295 RB_REMOVE(pf_anchor_node, &parent->children, 296 ruleset->anchor); 297 rs_free(ruleset->anchor); 298 if (parent == NULL) 299 return; 300 ruleset = &parent->ruleset; 301 } 302 } 303 304 int 305 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 306 const char *name) 307 { 308 char *p, *path; 309 struct pf_ruleset *ruleset; 310 311 r->anchor = NULL; 312 r->anchor_relative = 0; 313 r->anchor_wildcard = 0; 314 if (!name[0]) 315 return (0); 316 path = (char *)rs_malloc(MAXPATHLEN); 317 bzero(path, MAXPATHLEN); 318 if (name[0] == '/') 319 strlcpy(path, name + 1, MAXPATHLEN); 320 else { 321 /* relative path */ 322 r->anchor_relative = 1; 323 if (s->anchor == NULL || !s->anchor->path[0]) 324 path[0] = 0; 325 else 326 strlcpy(path, s->anchor->path, MAXPATHLEN); 327 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 328 if (!path[0]) { 329 printf("pf_anchor_setup: .. beyond root\n"); 330 rs_free(path); 331 return (1); 332 } 333 if ((p = strrchr(path, '/')) != NULL) 334 *p = 0; 335 else 336 path[0] = 0; 337 r->anchor_relative++; 338 name += 3; 339 } 340 if (path[0]) 341 strlcat(path, "/", MAXPATHLEN); 342 strlcat(path, name, MAXPATHLEN); 343 } 344 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 345 r->anchor_wildcard = 1; 346 *p = 0; 347 } 348 ruleset = pf_find_or_create_ruleset(path); 349 rs_free(path); 350 if (ruleset == NULL || ruleset->anchor == NULL) { 351 printf("pf_anchor_setup: ruleset\n"); 352 return (1); 353 } 354 r->anchor = ruleset->anchor; 355 r->anchor->refcnt++; 356 return (0); 357 } 358 359 int 360 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 361 struct pfioc_rule *pr) 362 { 363 pr->anchor_call[0] = 0; 364 if (r->anchor == NULL) 365 return (0); 366 if (!r->anchor_relative) { 367 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 368 strlcat(pr->anchor_call, r->anchor->path, 369 sizeof(pr->anchor_call)); 370 } else { 371 char *a, *p; 372 int i; 373 374 a = (char *)rs_malloc(MAXPATHLEN); 375 bzero(a, MAXPATHLEN); 376 if (rs->anchor == NULL) 377 a[0] = 0; 378 else 379 strlcpy(a, rs->anchor->path, MAXPATHLEN); 380 for (i = 1; i < r->anchor_relative; ++i) { 381 if ((p = strrchr(a, '/')) == NULL) 382 p = a; 383 *p = 0; 384 strlcat(pr->anchor_call, "../", 385 sizeof(pr->anchor_call)); 386 } 387 if (strncmp(a, r->anchor->path, strlen(a))) { 388 printf("pf_anchor_copyout: '%s' '%s'\n", a, 389 r->anchor->path); 390 rs_free(a); 391 return (1); 392 } 393 if (strlen(r->anchor->path) > strlen(a)) 394 strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 395 strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 396 rs_free(a); 397 } 398 if (r->anchor_wildcard) 399 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 400 sizeof(pr->anchor_call)); 401 return (0); 402 } 403 404 void 405 pf_anchor_remove(struct pf_rule *r) 406 { 407 if (r->anchor == NULL) 408 return; 409 if (r->anchor->refcnt <= 0) { 410 printf("pf_anchor_remove: broken refcount\n"); 411 r->anchor = NULL; 412 return; 413 } 414 if (!--r->anchor->refcnt) 415 pf_remove_if_empty_ruleset(&r->anchor->ruleset); 416 r->anchor = NULL; 417 } 418