1244769Sglebius/*- 2171164Smlaier * Copyright (c) 2001 Daniel Hartmeier 3171164Smlaier * Copyright (c) 2002,2003 Henning Brauer 4171164Smlaier * All rights reserved. 5171164Smlaier * 6171164Smlaier * Redistribution and use in source and binary forms, with or without 7171164Smlaier * modification, are permitted provided that the following conditions 8171164Smlaier * are met: 9171164Smlaier * 10171164Smlaier * - Redistributions of source code must retain the above copyright 11171164Smlaier * notice, this list of conditions and the following disclaimer. 12171164Smlaier * - Redistributions in binary form must reproduce the above 13171164Smlaier * copyright notice, this list of conditions and the following 14171164Smlaier * disclaimer in the documentation and/or other materials provided 15171164Smlaier * with the distribution. 16171164Smlaier * 17171164Smlaier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18171164Smlaier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19171164Smlaier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20171164Smlaier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21171164Smlaier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22171164Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23171164Smlaier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24171164Smlaier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25171164Smlaier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26171164Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27171164Smlaier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28171164Smlaier * POSSIBILITY OF SUCH DAMAGE. 29171164Smlaier * 30171164Smlaier * Effort sponsored in part by the Defense Advanced Research Projects 31171164Smlaier * Agency (DARPA) and Air Force Research Laboratory, Air Force 32171164Smlaier * Materiel Command, USAF, under agreement number F30602-01-2-0537. 33171164Smlaier * 34244769Sglebius * $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ 35171164Smlaier */ 36171164Smlaier 37171168Smlaier#include <sys/cdefs.h> 38171168Smlaier__FBSDID("$FreeBSD: releng/11.0/sys/netpfil/pf/pf_ruleset.c 257179 2013-10-26 18:18:50Z glebius $"); 39171168Smlaier 40171164Smlaier#include <sys/param.h> 41171164Smlaier#include <sys/socket.h> 42171164Smlaier#ifdef _KERNEL 43171164Smlaier# include <sys/systm.h> 44240233Sglebius# include <sys/refcount.h> 45171164Smlaier#endif /* _KERNEL */ 46171164Smlaier#include <sys/mbuf.h> 47171164Smlaier 48171164Smlaier#include <netinet/in.h> 49171164Smlaier#include <netinet/in_systm.h> 50171164Smlaier#include <netinet/ip.h> 51171164Smlaier#include <netinet/tcp.h> 52171164Smlaier 53171164Smlaier#include <net/if.h> 54257179Sglebius#include <net/vnet.h> 55171164Smlaier#include <net/pfvar.h> 56171164Smlaier 57171164Smlaier#ifdef INET6 58171164Smlaier#include <netinet/ip6.h> 59171164Smlaier#endif /* INET6 */ 60171164Smlaier 61171164Smlaier 62171164Smlaier#ifdef _KERNEL 63223637Sbz#define DPFPRINTF(format, x...) \ 64223637Sbz if (V_pf_status.debug >= PF_DEBUG_NOISY) \ 65171164Smlaier printf(format , ##x) 66223637Sbz#define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO) 67171164Smlaier#define rs_free(x) free(x, M_TEMP) 68171164Smlaier 69171164Smlaier#else 70171164Smlaier/* Userland equivalents so we can lend code to pfctl et al. */ 71171164Smlaier 72223637Sbz#include <arpa/inet.h> 73223637Sbz#include <errno.h> 74223637Sbz#include <stdio.h> 75223637Sbz#include <stdlib.h> 76223637Sbz#include <string.h> 77223637Sbz#define rs_malloc(x) calloc(1, x) 78223637Sbz#define rs_free(x) free(x) 79171164Smlaier 80223637Sbz#ifdef PFDEBUG 81223637Sbz#include <sys/stdarg.h> 82223637Sbz#define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) 83223637Sbz#else 84223637Sbz#define DPFPRINTF(format, x...) ((void)0) 85223637Sbz#endif /* PFDEBUG */ 86171164Smlaier#endif /* _KERNEL */ 87171164Smlaier 88240233Sglebius#ifdef _KERNEL 89240233SglebiusVNET_DEFINE(struct pf_anchor_global, pf_anchors); 90240233SglebiusVNET_DEFINE(struct pf_anchor, pf_main_anchor); 91240233Sglebius#else /* ! _KERNEL */ 92240233Sglebiusstruct pf_anchor_global pf_anchors; 93240233Sglebiusstruct pf_anchor pf_main_anchor; 94223637Sbz#undef V_pf_anchors 95223637Sbz#define V_pf_anchors pf_anchors 96223637Sbz#undef pf_main_ruleset 97223637Sbz#define pf_main_ruleset pf_main_anchor.ruleset 98240233Sglebius#endif /* _KERNEL */ 99223637Sbz 100171164Smlaierstatic __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 101171164Smlaier 102240233Sglebiusstatic struct pf_anchor *pf_find_anchor(const char *); 103240233Sglebius 104171164SmlaierRB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 105171164SmlaierRB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 106171164Smlaier 107171164Smlaierstatic __inline int 108171164Smlaierpf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) 109171164Smlaier{ 110171164Smlaier int c = strcmp(a->path, b->path); 111171164Smlaier 112171164Smlaier return (c ? (c < 0 ? -1 : 1) : 0); 113171164Smlaier} 114171164Smlaier 115171164Smlaierint 116171164Smlaierpf_get_ruleset_number(u_int8_t action) 117171164Smlaier{ 118171164Smlaier switch (action) { 119171164Smlaier case PF_SCRUB: 120171164Smlaier case PF_NOSCRUB: 121171164Smlaier return (PF_RULESET_SCRUB); 122171164Smlaier break; 123171164Smlaier case PF_PASS: 124171164Smlaier case PF_DROP: 125171164Smlaier return (PF_RULESET_FILTER); 126171164Smlaier break; 127171164Smlaier case PF_NAT: 128171164Smlaier case PF_NONAT: 129171164Smlaier return (PF_RULESET_NAT); 130171164Smlaier break; 131171164Smlaier case PF_BINAT: 132171164Smlaier case PF_NOBINAT: 133171164Smlaier return (PF_RULESET_BINAT); 134171164Smlaier break; 135171164Smlaier case PF_RDR: 136171164Smlaier case PF_NORDR: 137171164Smlaier return (PF_RULESET_RDR); 138171164Smlaier break; 139171164Smlaier default: 140171164Smlaier return (PF_RULESET_MAX); 141171164Smlaier break; 142171164Smlaier } 143171164Smlaier} 144171164Smlaier 145171164Smlaiervoid 146171164Smlaierpf_init_ruleset(struct pf_ruleset *ruleset) 147171164Smlaier{ 148171164Smlaier int i; 149171164Smlaier 150171164Smlaier memset(ruleset, 0, sizeof(struct pf_ruleset)); 151171164Smlaier for (i = 0; i < PF_RULESET_MAX; i++) { 152171164Smlaier TAILQ_INIT(&ruleset->rules[i].queues[0]); 153171164Smlaier TAILQ_INIT(&ruleset->rules[i].queues[1]); 154171164Smlaier ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 155171164Smlaier ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 156171164Smlaier } 157171164Smlaier} 158171164Smlaier 159240233Sglebiusstatic struct pf_anchor * 160171164Smlaierpf_find_anchor(const char *path) 161171164Smlaier{ 162171164Smlaier struct pf_anchor *key, *found; 163171164Smlaier 164171164Smlaier key = (struct pf_anchor *)rs_malloc(sizeof(*key)); 165223637Sbz if (key == NULL) 166223637Sbz return (NULL); 167171164Smlaier strlcpy(key->path, path, sizeof(key->path)); 168223637Sbz found = RB_FIND(pf_anchor_global, &V_pf_anchors, key); 169171164Smlaier rs_free(key); 170171164Smlaier return (found); 171171164Smlaier} 172171164Smlaier 173171164Smlaierstruct pf_ruleset * 174171164Smlaierpf_find_ruleset(const char *path) 175171164Smlaier{ 176171164Smlaier struct pf_anchor *anchor; 177171164Smlaier 178171164Smlaier while (*path == '/') 179171164Smlaier path++; 180171164Smlaier if (!*path) 181171164Smlaier return (&pf_main_ruleset); 182171164Smlaier anchor = pf_find_anchor(path); 183171164Smlaier if (anchor == NULL) 184171164Smlaier return (NULL); 185171164Smlaier else 186171164Smlaier return (&anchor->ruleset); 187171164Smlaier} 188171164Smlaier 189171164Smlaierstruct pf_ruleset * 190171164Smlaierpf_find_or_create_ruleset(const char *path) 191171164Smlaier{ 192171164Smlaier char *p, *q, *r; 193171164Smlaier struct pf_ruleset *ruleset; 194171168Smlaier struct pf_anchor *anchor = NULL, *dup, *parent = NULL; 195171164Smlaier 196171164Smlaier if (path[0] == 0) 197171164Smlaier return (&pf_main_ruleset); 198171164Smlaier while (*path == '/') 199171164Smlaier path++; 200171164Smlaier ruleset = pf_find_ruleset(path); 201171164Smlaier if (ruleset != NULL) 202171164Smlaier return (ruleset); 203171164Smlaier p = (char *)rs_malloc(MAXPATHLEN); 204223637Sbz if (p == NULL) 205223637Sbz return (NULL); 206171164Smlaier strlcpy(p, path, MAXPATHLEN); 207171164Smlaier while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 208171164Smlaier *q = 0; 209171164Smlaier if ((ruleset = pf_find_ruleset(p)) != NULL) { 210171164Smlaier parent = ruleset->anchor; 211171164Smlaier break; 212171164Smlaier } 213171164Smlaier } 214171164Smlaier if (q == NULL) 215171164Smlaier q = p; 216171164Smlaier else 217171164Smlaier q++; 218171164Smlaier strlcpy(p, path, MAXPATHLEN); 219171164Smlaier if (!*q) { 220171164Smlaier rs_free(p); 221171164Smlaier return (NULL); 222171164Smlaier } 223171164Smlaier while ((r = strchr(q, '/')) != NULL || *q) { 224171164Smlaier if (r != NULL) 225171164Smlaier *r = 0; 226171164Smlaier if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 227171164Smlaier (parent != NULL && strlen(parent->path) >= 228171164Smlaier MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 229171164Smlaier rs_free(p); 230171164Smlaier return (NULL); 231171164Smlaier } 232171164Smlaier anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); 233171164Smlaier if (anchor == NULL) { 234171164Smlaier rs_free(p); 235171164Smlaier return (NULL); 236171164Smlaier } 237171164Smlaier RB_INIT(&anchor->children); 238171164Smlaier strlcpy(anchor->name, q, sizeof(anchor->name)); 239171164Smlaier if (parent != NULL) { 240171164Smlaier strlcpy(anchor->path, parent->path, 241171164Smlaier sizeof(anchor->path)); 242171164Smlaier strlcat(anchor->path, "/", sizeof(anchor->path)); 243171164Smlaier } 244171164Smlaier strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 245223637Sbz if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) != 246171164Smlaier NULL) { 247171164Smlaier printf("pf_find_or_create_ruleset: RB_INSERT1 " 248171164Smlaier "'%s' '%s' collides with '%s' '%s'\n", 249171164Smlaier anchor->path, anchor->name, dup->path, dup->name); 250171164Smlaier rs_free(anchor); 251171164Smlaier rs_free(p); 252171164Smlaier return (NULL); 253171164Smlaier } 254171164Smlaier if (parent != NULL) { 255171164Smlaier anchor->parent = parent; 256171164Smlaier if ((dup = RB_INSERT(pf_anchor_node, &parent->children, 257171164Smlaier anchor)) != NULL) { 258171164Smlaier printf("pf_find_or_create_ruleset: " 259171164Smlaier "RB_INSERT2 '%s' '%s' collides with " 260171164Smlaier "'%s' '%s'\n", anchor->path, anchor->name, 261171164Smlaier dup->path, dup->name); 262223637Sbz RB_REMOVE(pf_anchor_global, &V_pf_anchors, 263171164Smlaier anchor); 264171164Smlaier rs_free(anchor); 265171164Smlaier rs_free(p); 266171164Smlaier return (NULL); 267171164Smlaier } 268171164Smlaier } 269171164Smlaier pf_init_ruleset(&anchor->ruleset); 270171164Smlaier anchor->ruleset.anchor = anchor; 271171164Smlaier parent = anchor; 272171164Smlaier if (r != NULL) 273171164Smlaier q = r + 1; 274171164Smlaier else 275171164Smlaier *q = 0; 276171164Smlaier } 277171164Smlaier rs_free(p); 278171164Smlaier return (&anchor->ruleset); 279171164Smlaier} 280171164Smlaier 281171164Smlaiervoid 282171164Smlaierpf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 283171164Smlaier{ 284171164Smlaier struct pf_anchor *parent; 285171164Smlaier int i; 286171164Smlaier 287171164Smlaier while (ruleset != NULL) { 288171164Smlaier if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || 289171164Smlaier !RB_EMPTY(&ruleset->anchor->children) || 290171164Smlaier ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 291171164Smlaier ruleset->topen) 292171164Smlaier return; 293171164Smlaier for (i = 0; i < PF_RULESET_MAX; ++i) 294171164Smlaier if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 295171164Smlaier !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || 296171164Smlaier ruleset->rules[i].inactive.open) 297171164Smlaier return; 298223637Sbz RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor); 299171164Smlaier if ((parent = ruleset->anchor->parent) != NULL) 300171164Smlaier RB_REMOVE(pf_anchor_node, &parent->children, 301171164Smlaier ruleset->anchor); 302171164Smlaier rs_free(ruleset->anchor); 303171164Smlaier if (parent == NULL) 304171164Smlaier return; 305171164Smlaier ruleset = &parent->ruleset; 306171164Smlaier } 307171164Smlaier} 308171164Smlaier 309171164Smlaierint 310171164Smlaierpf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 311171164Smlaier const char *name) 312171164Smlaier{ 313171164Smlaier char *p, *path; 314171164Smlaier struct pf_ruleset *ruleset; 315171164Smlaier 316171164Smlaier r->anchor = NULL; 317171164Smlaier r->anchor_relative = 0; 318171164Smlaier r->anchor_wildcard = 0; 319171164Smlaier if (!name[0]) 320171164Smlaier return (0); 321171164Smlaier path = (char *)rs_malloc(MAXPATHLEN); 322223637Sbz if (path == NULL) 323223637Sbz return (1); 324171164Smlaier if (name[0] == '/') 325171164Smlaier strlcpy(path, name + 1, MAXPATHLEN); 326171164Smlaier else { 327171164Smlaier /* relative path */ 328171164Smlaier r->anchor_relative = 1; 329171164Smlaier if (s->anchor == NULL || !s->anchor->path[0]) 330171164Smlaier path[0] = 0; 331171164Smlaier else 332171164Smlaier strlcpy(path, s->anchor->path, MAXPATHLEN); 333171164Smlaier while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 334171164Smlaier if (!path[0]) { 335171164Smlaier printf("pf_anchor_setup: .. beyond root\n"); 336171164Smlaier rs_free(path); 337171164Smlaier return (1); 338171164Smlaier } 339171164Smlaier if ((p = strrchr(path, '/')) != NULL) 340171164Smlaier *p = 0; 341171164Smlaier else 342171164Smlaier path[0] = 0; 343171164Smlaier r->anchor_relative++; 344171164Smlaier name += 3; 345171164Smlaier } 346171164Smlaier if (path[0]) 347171164Smlaier strlcat(path, "/", MAXPATHLEN); 348171164Smlaier strlcat(path, name, MAXPATHLEN); 349171164Smlaier } 350171164Smlaier if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 351171164Smlaier r->anchor_wildcard = 1; 352171164Smlaier *p = 0; 353171164Smlaier } 354171164Smlaier ruleset = pf_find_or_create_ruleset(path); 355171164Smlaier rs_free(path); 356171164Smlaier if (ruleset == NULL || ruleset->anchor == NULL) { 357171164Smlaier printf("pf_anchor_setup: ruleset\n"); 358171164Smlaier return (1); 359171164Smlaier } 360171164Smlaier r->anchor = ruleset->anchor; 361171164Smlaier r->anchor->refcnt++; 362171164Smlaier return (0); 363171164Smlaier} 364171164Smlaier 365171164Smlaierint 366171164Smlaierpf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 367171164Smlaier struct pfioc_rule *pr) 368171164Smlaier{ 369171164Smlaier pr->anchor_call[0] = 0; 370171164Smlaier if (r->anchor == NULL) 371171164Smlaier return (0); 372171164Smlaier if (!r->anchor_relative) { 373171164Smlaier strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 374171164Smlaier strlcat(pr->anchor_call, r->anchor->path, 375171164Smlaier sizeof(pr->anchor_call)); 376171164Smlaier } else { 377171164Smlaier char *a, *p; 378171164Smlaier int i; 379171164Smlaier 380171164Smlaier a = (char *)rs_malloc(MAXPATHLEN); 381223637Sbz if (a == NULL) 382223637Sbz return (1); 383171164Smlaier if (rs->anchor == NULL) 384171164Smlaier a[0] = 0; 385171164Smlaier else 386171164Smlaier strlcpy(a, rs->anchor->path, MAXPATHLEN); 387171164Smlaier for (i = 1; i < r->anchor_relative; ++i) { 388171164Smlaier if ((p = strrchr(a, '/')) == NULL) 389171164Smlaier p = a; 390171164Smlaier *p = 0; 391171164Smlaier strlcat(pr->anchor_call, "../", 392171164Smlaier sizeof(pr->anchor_call)); 393171164Smlaier } 394171164Smlaier if (strncmp(a, r->anchor->path, strlen(a))) { 395171164Smlaier printf("pf_anchor_copyout: '%s' '%s'\n", a, 396171164Smlaier r->anchor->path); 397171164Smlaier rs_free(a); 398171164Smlaier return (1); 399171164Smlaier } 400171164Smlaier if (strlen(r->anchor->path) > strlen(a)) 401171164Smlaier strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 402171164Smlaier strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 403171164Smlaier rs_free(a); 404171164Smlaier } 405171164Smlaier if (r->anchor_wildcard) 406171164Smlaier strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 407171164Smlaier sizeof(pr->anchor_call)); 408171164Smlaier return (0); 409171164Smlaier} 410171164Smlaier 411171164Smlaiervoid 412171164Smlaierpf_anchor_remove(struct pf_rule *r) 413171164Smlaier{ 414171164Smlaier if (r->anchor == NULL) 415171164Smlaier return; 416171164Smlaier if (r->anchor->refcnt <= 0) { 417171164Smlaier printf("pf_anchor_remove: broken refcount\n"); 418171164Smlaier r->anchor = NULL; 419171164Smlaier return; 420171164Smlaier } 421171164Smlaier if (!--r->anchor->refcnt) 422171164Smlaier pf_remove_if_empty_ruleset(&r->anchor->ruleset); 423171164Smlaier r->anchor = NULL; 424171164Smlaier} 425