1223637Sbz/* $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ */ 2171164Smlaier 3171164Smlaier/* 4171164Smlaier * Copyright (c) 2001 Daniel Hartmeier 5171164Smlaier * Copyright (c) 2002,2003 Henning Brauer 6171164Smlaier * All rights reserved. 7171164Smlaier * 8171164Smlaier * Redistribution and use in source and binary forms, with or without 9171164Smlaier * modification, are permitted provided that the following conditions 10171164Smlaier * are met: 11171164Smlaier * 12171164Smlaier * - Redistributions of source code must retain the above copyright 13171164Smlaier * notice, this list of conditions and the following disclaimer. 14171164Smlaier * - Redistributions in binary form must reproduce the above 15171164Smlaier * copyright notice, this list of conditions and the following 16171164Smlaier * disclaimer in the documentation and/or other materials provided 17171164Smlaier * with the distribution. 18171164Smlaier * 19171164Smlaier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20171164Smlaier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21171164Smlaier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22171164Smlaier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23171164Smlaier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24171164Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25171164Smlaier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26171164Smlaier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27171164Smlaier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28171164Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29171164Smlaier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30171164Smlaier * POSSIBILITY OF SUCH DAMAGE. 31171164Smlaier * 32171164Smlaier * Effort sponsored in part by the Defense Advanced Research Projects 33171164Smlaier * Agency (DARPA) and Air Force Research Laboratory, Air Force 34171164Smlaier * Materiel Command, USAF, under agreement number F30602-01-2-0537. 35171164Smlaier * 36171164Smlaier */ 37171164Smlaier 38171168Smlaier#ifdef __FreeBSD__ 39171168Smlaier#include <sys/cdefs.h> 40171168Smlaier__FBSDID("$FreeBSD$"); 41171168Smlaier#endif 42171168Smlaier 43171164Smlaier#include <sys/param.h> 44171164Smlaier#include <sys/socket.h> 45171164Smlaier#ifdef _KERNEL 46171164Smlaier# include <sys/systm.h> 47171164Smlaier#endif /* _KERNEL */ 48171164Smlaier#include <sys/mbuf.h> 49171164Smlaier 50171164Smlaier#include <netinet/in.h> 51171164Smlaier#include <netinet/in_systm.h> 52171164Smlaier#include <netinet/ip.h> 53171164Smlaier#include <netinet/tcp.h> 54171164Smlaier 55171164Smlaier#include <net/if.h> 56171164Smlaier#include <net/pfvar.h> 57171164Smlaier 58171164Smlaier#ifdef INET6 59171164Smlaier#include <netinet/ip6.h> 60171164Smlaier#endif /* INET6 */ 61171164Smlaier 62171164Smlaier 63171164Smlaier#ifdef _KERNEL 64223637Sbz#ifdef __FreeBSD__ 65223637Sbz#define DPFPRINTF(format, x...) \ 66223637Sbz if (V_pf_status.debug >= PF_DEBUG_NOISY) \ 67171164Smlaier printf(format , ##x) 68223637Sbz#else 69223637Sbz#define DPFPRINTF(format, x...) \ 70223637Sbz if (pf_status.debug >= PF_DEBUG_NOISY) \ 71223637Sbz printf(format , ##x) 72223637Sbz#endif 73171168Smlaier#ifdef __FreeBSD__ 74223637Sbz#define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO) 75171168Smlaier#else 76223637Sbz#define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO) 77171168Smlaier#endif 78171164Smlaier#define rs_free(x) free(x, M_TEMP) 79171164Smlaier 80171164Smlaier#else 81171164Smlaier/* Userland equivalents so we can lend code to pfctl et al. */ 82171164Smlaier 83223637Sbz#include <arpa/inet.h> 84223637Sbz#include <errno.h> 85223637Sbz#include <stdio.h> 86223637Sbz#include <stdlib.h> 87223637Sbz#include <string.h> 88223637Sbz#define rs_malloc(x) calloc(1, x) 89223637Sbz#define rs_free(x) free(x) 90171164Smlaier 91223637Sbz#ifdef PFDEBUG 92223637Sbz#include <sys/stdarg.h> 93223637Sbz#define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) 94223637Sbz#else 95223637Sbz#define DPFPRINTF(format, x...) ((void)0) 96223637Sbz#endif /* PFDEBUG */ 97171164Smlaier#endif /* _KERNEL */ 98171164Smlaier 99223637Sbz#if defined(__FreeBSD__) && !defined(_KERNEL) 100223637Sbz#undef V_pf_anchors 101223637Sbz#define V_pf_anchors pf_anchors 102171164Smlaier 103223637Sbz#undef pf_main_ruleset 104223637Sbz#define pf_main_ruleset pf_main_anchor.ruleset 105223637Sbz#endif 106223637Sbz 107223637Sbz#if defined(__FreeBSD__) && defined(_KERNEL) 108223637SbzVNET_DEFINE(struct pf_anchor_global, pf_anchors); 109223637SbzVNET_DEFINE(struct pf_anchor, pf_main_anchor); 110223637Sbz#else 111171164Smlaierstruct pf_anchor_global pf_anchors; 112171164Smlaierstruct pf_anchor pf_main_anchor; 113171168Smlaier#endif 114171164Smlaier 115171164Smlaierstatic __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 116171164Smlaier 117171164SmlaierRB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 118171164SmlaierRB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 119171164Smlaier 120171164Smlaierstatic __inline int 121171164Smlaierpf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) 122171164Smlaier{ 123171164Smlaier int c = strcmp(a->path, b->path); 124171164Smlaier 125171164Smlaier return (c ? (c < 0 ? -1 : 1) : 0); 126171164Smlaier} 127171164Smlaier 128171164Smlaierint 129171164Smlaierpf_get_ruleset_number(u_int8_t action) 130171164Smlaier{ 131171164Smlaier switch (action) { 132171164Smlaier case PF_SCRUB: 133171164Smlaier case PF_NOSCRUB: 134171164Smlaier return (PF_RULESET_SCRUB); 135171164Smlaier break; 136171164Smlaier case PF_PASS: 137171164Smlaier case PF_DROP: 138171164Smlaier return (PF_RULESET_FILTER); 139171164Smlaier break; 140171164Smlaier case PF_NAT: 141171164Smlaier case PF_NONAT: 142171164Smlaier return (PF_RULESET_NAT); 143171164Smlaier break; 144171164Smlaier case PF_BINAT: 145171164Smlaier case PF_NOBINAT: 146171164Smlaier return (PF_RULESET_BINAT); 147171164Smlaier break; 148171164Smlaier case PF_RDR: 149171164Smlaier case PF_NORDR: 150171164Smlaier return (PF_RULESET_RDR); 151171164Smlaier break; 152171164Smlaier default: 153171164Smlaier return (PF_RULESET_MAX); 154171164Smlaier break; 155171164Smlaier } 156171164Smlaier} 157171164Smlaier 158171164Smlaiervoid 159171164Smlaierpf_init_ruleset(struct pf_ruleset *ruleset) 160171164Smlaier{ 161171164Smlaier int i; 162171164Smlaier 163171164Smlaier memset(ruleset, 0, sizeof(struct pf_ruleset)); 164171164Smlaier for (i = 0; i < PF_RULESET_MAX; i++) { 165171164Smlaier TAILQ_INIT(&ruleset->rules[i].queues[0]); 166171164Smlaier TAILQ_INIT(&ruleset->rules[i].queues[1]); 167171164Smlaier ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 168171164Smlaier ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 169171164Smlaier } 170171164Smlaier} 171171164Smlaier 172171164Smlaierstruct pf_anchor * 173171164Smlaierpf_find_anchor(const char *path) 174171164Smlaier{ 175171164Smlaier struct pf_anchor *key, *found; 176171164Smlaier 177171164Smlaier key = (struct pf_anchor *)rs_malloc(sizeof(*key)); 178223637Sbz if (key == NULL) 179223637Sbz return (NULL); 180171164Smlaier strlcpy(key->path, path, sizeof(key->path)); 181223637Sbz#ifdef __FreeBSD__ 182223637Sbz found = RB_FIND(pf_anchor_global, &V_pf_anchors, key); 183223637Sbz#else 184171164Smlaier found = RB_FIND(pf_anchor_global, &pf_anchors, key); 185223637Sbz#endif 186171164Smlaier rs_free(key); 187171164Smlaier return (found); 188171164Smlaier} 189171164Smlaier 190171164Smlaierstruct pf_ruleset * 191171164Smlaierpf_find_ruleset(const char *path) 192171164Smlaier{ 193171164Smlaier struct pf_anchor *anchor; 194171164Smlaier 195171164Smlaier while (*path == '/') 196171164Smlaier path++; 197171164Smlaier if (!*path) 198171164Smlaier return (&pf_main_ruleset); 199171164Smlaier anchor = pf_find_anchor(path); 200171164Smlaier if (anchor == NULL) 201171164Smlaier return (NULL); 202171164Smlaier else 203171164Smlaier return (&anchor->ruleset); 204171164Smlaier} 205171164Smlaier 206171164Smlaierstruct pf_ruleset * 207171164Smlaierpf_find_or_create_ruleset(const char *path) 208171164Smlaier{ 209171164Smlaier char *p, *q, *r; 210171164Smlaier struct pf_ruleset *ruleset; 211171168Smlaier#ifdef __FreeBSD__ 212171168Smlaier struct pf_anchor *anchor = NULL, *dup, *parent = NULL; 213171168Smlaier#else 214171164Smlaier struct pf_anchor *anchor, *dup, *parent = NULL; 215171168Smlaier#endif 216171164Smlaier 217171164Smlaier if (path[0] == 0) 218171164Smlaier return (&pf_main_ruleset); 219171164Smlaier while (*path == '/') 220171164Smlaier path++; 221171164Smlaier ruleset = pf_find_ruleset(path); 222171164Smlaier if (ruleset != NULL) 223171164Smlaier return (ruleset); 224171164Smlaier p = (char *)rs_malloc(MAXPATHLEN); 225223637Sbz if (p == NULL) 226223637Sbz return (NULL); 227171164Smlaier strlcpy(p, path, MAXPATHLEN); 228171164Smlaier while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 229171164Smlaier *q = 0; 230171164Smlaier if ((ruleset = pf_find_ruleset(p)) != NULL) { 231171164Smlaier parent = ruleset->anchor; 232171164Smlaier break; 233171164Smlaier } 234171164Smlaier } 235171164Smlaier if (q == NULL) 236171164Smlaier q = p; 237171164Smlaier else 238171164Smlaier q++; 239171164Smlaier strlcpy(p, path, MAXPATHLEN); 240171164Smlaier if (!*q) { 241171164Smlaier rs_free(p); 242171164Smlaier return (NULL); 243171164Smlaier } 244171164Smlaier while ((r = strchr(q, '/')) != NULL || *q) { 245171164Smlaier if (r != NULL) 246171164Smlaier *r = 0; 247171164Smlaier if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 248171164Smlaier (parent != NULL && strlen(parent->path) >= 249171164Smlaier MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 250171164Smlaier rs_free(p); 251171164Smlaier return (NULL); 252171164Smlaier } 253171164Smlaier anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); 254171164Smlaier if (anchor == NULL) { 255171164Smlaier rs_free(p); 256171164Smlaier return (NULL); 257171164Smlaier } 258171164Smlaier RB_INIT(&anchor->children); 259171164Smlaier strlcpy(anchor->name, q, sizeof(anchor->name)); 260171164Smlaier if (parent != NULL) { 261171164Smlaier strlcpy(anchor->path, parent->path, 262171164Smlaier sizeof(anchor->path)); 263171164Smlaier strlcat(anchor->path, "/", sizeof(anchor->path)); 264171164Smlaier } 265171164Smlaier strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 266223637Sbz#ifdef __FreeBSD__ 267223637Sbz if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) != 268223637Sbz#else 269171164Smlaier if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != 270223637Sbz#endif 271171164Smlaier NULL) { 272171164Smlaier printf("pf_find_or_create_ruleset: RB_INSERT1 " 273171164Smlaier "'%s' '%s' collides with '%s' '%s'\n", 274171164Smlaier anchor->path, anchor->name, dup->path, dup->name); 275171164Smlaier rs_free(anchor); 276171164Smlaier rs_free(p); 277171164Smlaier return (NULL); 278171164Smlaier } 279171164Smlaier if (parent != NULL) { 280171164Smlaier anchor->parent = parent; 281171164Smlaier if ((dup = RB_INSERT(pf_anchor_node, &parent->children, 282171164Smlaier anchor)) != NULL) { 283171164Smlaier printf("pf_find_or_create_ruleset: " 284171164Smlaier "RB_INSERT2 '%s' '%s' collides with " 285171164Smlaier "'%s' '%s'\n", anchor->path, anchor->name, 286171164Smlaier dup->path, dup->name); 287223637Sbz#ifdef __FreeBSD__ 288223637Sbz RB_REMOVE(pf_anchor_global, &V_pf_anchors, 289223637Sbz#else 290171164Smlaier RB_REMOVE(pf_anchor_global, &pf_anchors, 291223637Sbz#endif 292171164Smlaier anchor); 293171164Smlaier rs_free(anchor); 294171164Smlaier rs_free(p); 295171164Smlaier return (NULL); 296171164Smlaier } 297171164Smlaier } 298171164Smlaier pf_init_ruleset(&anchor->ruleset); 299171164Smlaier anchor->ruleset.anchor = anchor; 300171164Smlaier parent = anchor; 301171164Smlaier if (r != NULL) 302171164Smlaier q = r + 1; 303171164Smlaier else 304171164Smlaier *q = 0; 305171164Smlaier } 306171164Smlaier rs_free(p); 307171164Smlaier return (&anchor->ruleset); 308171164Smlaier} 309171164Smlaier 310171164Smlaiervoid 311171164Smlaierpf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 312171164Smlaier{ 313171164Smlaier struct pf_anchor *parent; 314171164Smlaier int i; 315171164Smlaier 316171164Smlaier while (ruleset != NULL) { 317171164Smlaier if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || 318171164Smlaier !RB_EMPTY(&ruleset->anchor->children) || 319171164Smlaier ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 320171164Smlaier ruleset->topen) 321171164Smlaier return; 322171164Smlaier for (i = 0; i < PF_RULESET_MAX; ++i) 323171164Smlaier if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 324171164Smlaier !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || 325171164Smlaier ruleset->rules[i].inactive.open) 326171164Smlaier return; 327223637Sbz#ifdef __FreeBSD__ 328223637Sbz RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor); 329223637Sbz#else 330171164Smlaier RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); 331223637Sbz#endif 332171164Smlaier if ((parent = ruleset->anchor->parent) != NULL) 333171164Smlaier RB_REMOVE(pf_anchor_node, &parent->children, 334171164Smlaier ruleset->anchor); 335171164Smlaier rs_free(ruleset->anchor); 336171164Smlaier if (parent == NULL) 337171164Smlaier return; 338171164Smlaier ruleset = &parent->ruleset; 339171164Smlaier } 340171164Smlaier} 341171164Smlaier 342171164Smlaierint 343171164Smlaierpf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 344171164Smlaier const char *name) 345171164Smlaier{ 346171164Smlaier char *p, *path; 347171164Smlaier struct pf_ruleset *ruleset; 348171164Smlaier 349171164Smlaier r->anchor = NULL; 350171164Smlaier r->anchor_relative = 0; 351171164Smlaier r->anchor_wildcard = 0; 352171164Smlaier if (!name[0]) 353171164Smlaier return (0); 354171164Smlaier path = (char *)rs_malloc(MAXPATHLEN); 355223637Sbz if (path == NULL) 356223637Sbz return (1); 357171164Smlaier if (name[0] == '/') 358171164Smlaier strlcpy(path, name + 1, MAXPATHLEN); 359171164Smlaier else { 360171164Smlaier /* relative path */ 361171164Smlaier r->anchor_relative = 1; 362171164Smlaier if (s->anchor == NULL || !s->anchor->path[0]) 363171164Smlaier path[0] = 0; 364171164Smlaier else 365171164Smlaier strlcpy(path, s->anchor->path, MAXPATHLEN); 366171164Smlaier while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 367171164Smlaier if (!path[0]) { 368171164Smlaier printf("pf_anchor_setup: .. beyond root\n"); 369171164Smlaier rs_free(path); 370171164Smlaier return (1); 371171164Smlaier } 372171164Smlaier if ((p = strrchr(path, '/')) != NULL) 373171164Smlaier *p = 0; 374171164Smlaier else 375171164Smlaier path[0] = 0; 376171164Smlaier r->anchor_relative++; 377171164Smlaier name += 3; 378171164Smlaier } 379171164Smlaier if (path[0]) 380171164Smlaier strlcat(path, "/", MAXPATHLEN); 381171164Smlaier strlcat(path, name, MAXPATHLEN); 382171164Smlaier } 383171164Smlaier if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 384171164Smlaier r->anchor_wildcard = 1; 385171164Smlaier *p = 0; 386171164Smlaier } 387171164Smlaier ruleset = pf_find_or_create_ruleset(path); 388171164Smlaier rs_free(path); 389171164Smlaier if (ruleset == NULL || ruleset->anchor == NULL) { 390171164Smlaier printf("pf_anchor_setup: ruleset\n"); 391171164Smlaier return (1); 392171164Smlaier } 393171164Smlaier r->anchor = ruleset->anchor; 394171164Smlaier r->anchor->refcnt++; 395171164Smlaier return (0); 396171164Smlaier} 397171164Smlaier 398171164Smlaierint 399171164Smlaierpf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 400171164Smlaier struct pfioc_rule *pr) 401171164Smlaier{ 402171164Smlaier pr->anchor_call[0] = 0; 403171164Smlaier if (r->anchor == NULL) 404171164Smlaier return (0); 405171164Smlaier if (!r->anchor_relative) { 406171164Smlaier strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 407171164Smlaier strlcat(pr->anchor_call, r->anchor->path, 408171164Smlaier sizeof(pr->anchor_call)); 409171164Smlaier } else { 410171164Smlaier char *a, *p; 411171164Smlaier int i; 412171164Smlaier 413171164Smlaier a = (char *)rs_malloc(MAXPATHLEN); 414223637Sbz if (a == NULL) 415223637Sbz return (1); 416171164Smlaier if (rs->anchor == NULL) 417171164Smlaier a[0] = 0; 418171164Smlaier else 419171164Smlaier strlcpy(a, rs->anchor->path, MAXPATHLEN); 420171164Smlaier for (i = 1; i < r->anchor_relative; ++i) { 421171164Smlaier if ((p = strrchr(a, '/')) == NULL) 422171164Smlaier p = a; 423171164Smlaier *p = 0; 424171164Smlaier strlcat(pr->anchor_call, "../", 425171164Smlaier sizeof(pr->anchor_call)); 426171164Smlaier } 427171164Smlaier if (strncmp(a, r->anchor->path, strlen(a))) { 428171164Smlaier printf("pf_anchor_copyout: '%s' '%s'\n", a, 429171164Smlaier r->anchor->path); 430171164Smlaier rs_free(a); 431171164Smlaier return (1); 432171164Smlaier } 433171164Smlaier if (strlen(r->anchor->path) > strlen(a)) 434171164Smlaier strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 435171164Smlaier strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 436171164Smlaier rs_free(a); 437171164Smlaier } 438171164Smlaier if (r->anchor_wildcard) 439171164Smlaier strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 440171164Smlaier sizeof(pr->anchor_call)); 441171164Smlaier return (0); 442171164Smlaier} 443171164Smlaier 444171164Smlaiervoid 445171164Smlaierpf_anchor_remove(struct pf_rule *r) 446171164Smlaier{ 447171164Smlaier if (r->anchor == NULL) 448171164Smlaier return; 449171164Smlaier if (r->anchor->refcnt <= 0) { 450171164Smlaier printf("pf_anchor_remove: broken refcount\n"); 451171164Smlaier r->anchor = NULL; 452171164Smlaier return; 453171164Smlaier } 454171164Smlaier if (!--r->anchor->refcnt) 455171164Smlaier pf_remove_if_empty_ruleset(&r->anchor->ruleset); 456171164Smlaier r->anchor = NULL; 457171164Smlaier} 458