pf_ruleset.c revision 171164
132145Spst/* $OpenBSD: pf_ruleset.c,v 1.1 2006/10/27 13:56:51 mcbride Exp $ */ 232145Spst 332145Spst/* 432145Spst * Copyright (c) 2001 Daniel Hartmeier 532145Spst * Copyright (c) 2002,2003 Henning Brauer 632145Spst * All rights reserved. 732145Spst * 832145Spst * Redistribution and use in source and binary forms, with or without 932145Spst * modification, are permitted provided that the following conditions 1032145Spst * are met: 1132145Spst * 1232145Spst * - Redistributions of source code must retain the above copyright 1332145Spst * notice, this list of conditions and the following disclaimer. 1432145Spst * - Redistributions in binary form must reproduce the above 1532145Spst * copyright notice, this list of conditions and the following 1632145Spst * disclaimer in the documentation and/or other materials provided 1732145Spst * with the distribution. 1832145Spst * 1932145Spst * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2032145Spst * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2132145Spst * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2232145Spst * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2332145Spst * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2432145Spst * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2532145Spst * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2632145Spst * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 2732145Spst * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2832145Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 2932145Spst * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3032145Spst * POSSIBILITY OF SUCH DAMAGE. 3132145Spst * 3232145Spst * Effort sponsored in part by the Defense Advanced Research Projects 3332145Spst * Agency (DARPA) and Air Force Research Laboratory, Air Force 3432145Spst * Materiel Command, USAF, under agreement number F30602-01-2-0537. 3532145Spst * 3632145Spst */ 3732145Spst 3832145Spst#include <sys/param.h> 3932145Spst#include <sys/socket.h> 4032145Spst#ifdef _KERNEL 4132145Spst# include <sys/systm.h> 4232145Spst#endif /* _KERNEL */ 4332145Spst#include <sys/mbuf.h> 4432145Spst 4532145Spst#include <netinet/in.h> 4632145Spst#include <netinet/in_systm.h> 4732145Spst#include <netinet/ip.h> 4832145Spst#include <netinet/tcp.h> 4932145Spst 5032145Spst#include <net/if.h> 5132145Spst#include <net/pfvar.h> 5232145Spst 5332145Spst#ifdef INET6 5432145Spst#include <netinet/ip6.h> 5532145Spst#endif /* INET6 */ 5632145Spst 5732145Spst 5832145Spst#ifdef _KERNEL 5932145Spst# define DPFPRINTF(format, x...) \ 6032145Spst if (pf_status.debug >= PF_DEBUG_NOISY) \ 6132145Spst printf(format , ##x) 6232145Spst#define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK) 6332145Spst#define rs_free(x) free(x, M_TEMP) 6432145Spst 6532145Spst#else 6632145Spst/* Userland equivalents so we can lend code to pfctl et al. */ 6732145Spst 6832145Spst# include <arpa/inet.h> 6932145Spst# include <errno.h> 7032145Spst# include <stdio.h> 7132145Spst# include <stdlib.h> 7232145Spst# include <string.h> 7332145Spst# define rs_malloc(x) malloc(x) 7432145Spst# define rs_free(x) free(x) 7532145Spst 7632145Spst# ifdef PFDEBUG 7732145Spst# include <sys/stdarg.h> 7832145Spst# define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) 7932145Spst# else 8032145Spst# define DPFPRINTF(format, x...) ((void)0) 8132145Spst# endif /* PFDEBUG */ 8232145Spst#endif /* _KERNEL */ 8332145Spst 8432145Spst 8532145Spststruct pf_anchor_global pf_anchors; 8632145Spststruct pf_anchor pf_main_anchor; 8732145Spst 8832145Spstint pf_get_ruleset_number(u_int8_t); 8932145Spstvoid pf_init_ruleset(struct pf_ruleset *); 9032145Spstint pf_anchor_setup(struct pf_rule *, 9132145Spst const struct pf_ruleset *, const char *); 9232145Spstint pf_anchor_copyout(const struct pf_ruleset *, 9332145Spst const struct pf_rule *, struct pfioc_rule *); 9432145Spstvoid pf_anchor_remove(struct pf_rule *); 9532145Spst 9632145Spststatic __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 9732145Spst 9832145SpstRB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 9932145SpstRB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 10032145Spst 10132145Spststatic __inline int 10232145Spstpf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) 10332145Spst{ 10432145Spst int c = strcmp(a->path, b->path); 10532145Spst 10632145Spst return (c ? (c < 0 ? -1 : 1) : 0); 10732145Spst} 10832145Spst 10932145Spstint 11032145Spstpf_get_ruleset_number(u_int8_t action) 11132145Spst{ 11232145Spst switch (action) { 11332145Spst case PF_SCRUB: 11432145Spst case PF_NOSCRUB: 11532145Spst return (PF_RULESET_SCRUB); 11632145Spst break; 11732145Spst case PF_PASS: 11832145Spst case PF_DROP: 11932145Spst return (PF_RULESET_FILTER); 12032145Spst break; 12132145Spst case PF_NAT: 12232145Spst case PF_NONAT: 12332145Spst return (PF_RULESET_NAT); 12432145Spst break; 12532145Spst case PF_BINAT: 12632145Spst case PF_NOBINAT: 12732145Spst return (PF_RULESET_BINAT); 12832145Spst break; 12932145Spst case PF_RDR: 13032145Spst case PF_NORDR: 13132145Spst return (PF_RULESET_RDR); 13232145Spst break; 13332145Spst default: 13432145Spst return (PF_RULESET_MAX); 13532145Spst break; 13632145Spst } 13732145Spst} 13832145Spst 13932145Spstvoid 14032145Spstpf_init_ruleset(struct pf_ruleset *ruleset) 14132145Spst{ 14232145Spst int i; 14332145Spst 14432145Spst memset(ruleset, 0, sizeof(struct pf_ruleset)); 14532145Spst for (i = 0; i < PF_RULESET_MAX; i++) { 14632145Spst TAILQ_INIT(&ruleset->rules[i].queues[0]); 14732145Spst TAILQ_INIT(&ruleset->rules[i].queues[1]); 14832145Spst ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 14932145Spst ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 15032145Spst } 15132145Spst} 15232145Spst 15332145Spststruct pf_anchor * 15432145Spstpf_find_anchor(const char *path) 15532145Spst{ 15632145Spst struct pf_anchor *key, *found; 15732145Spst 15832145Spst key = (struct pf_anchor *)rs_malloc(sizeof(*key)); 15932145Spst memset(key, 0, sizeof(*key)); 16032145Spst strlcpy(key->path, path, sizeof(key->path)); 16132145Spst found = RB_FIND(pf_anchor_global, &pf_anchors, key); 16232145Spst rs_free(key); 16332145Spst return (found); 16432145Spst} 16532145Spst 16632145Spststruct pf_ruleset * 16732145Spstpf_find_ruleset(const char *path) 16832145Spst{ 16932145Spst struct pf_anchor *anchor; 17032145Spst 17132145Spst while (*path == '/') 17232145Spst path++; 17332145Spst if (!*path) 17432145Spst return (&pf_main_ruleset); 17532145Spst anchor = pf_find_anchor(path); 17632145Spst if (anchor == NULL) 17732145Spst return (NULL); 17832145Spst else 17932145Spst return (&anchor->ruleset); 18032145Spst} 18132145Spst 18232145Spststruct pf_ruleset * 18332145Spstpf_find_or_create_ruleset(const char *path) 18432145Spst{ 18532145Spst char *p, *q, *r; 18632145Spst struct pf_ruleset *ruleset; 18732145Spst struct pf_anchor *anchor, *dup, *parent = NULL; 18832145Spst 18932145Spst if (path[0] == 0) 19032145Spst return (&pf_main_ruleset); 19132145Spst while (*path == '/') 19232145Spst path++; 19332145Spst ruleset = pf_find_ruleset(path); 19432145Spst if (ruleset != NULL) 19532145Spst return (ruleset); 19632145Spst p = (char *)rs_malloc(MAXPATHLEN); 19732145Spst bzero(p, MAXPATHLEN); 19832145Spst strlcpy(p, path, MAXPATHLEN); 19932145Spst while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 20032145Spst *q = 0; 20132145Spst if ((ruleset = pf_find_ruleset(p)) != NULL) { 20232145Spst parent = ruleset->anchor; 20332145Spst break; 20432145Spst } 20532145Spst } 20632145Spst if (q == NULL) 20732145Spst q = p; 20832145Spst else 20932145Spst q++; 21032145Spst strlcpy(p, path, MAXPATHLEN); 21132145Spst if (!*q) { 21232145Spst rs_free(p); 21332145Spst return (NULL); 21432145Spst } 21532145Spst while ((r = strchr(q, '/')) != NULL || *q) { 21632145Spst if (r != NULL) 21732145Spst *r = 0; 21832145Spst if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 21932145Spst (parent != NULL && strlen(parent->path) >= 22032145Spst MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 22132145Spst rs_free(p); 22232145Spst return (NULL); 22332145Spst } 22432145Spst anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor)); 22532145Spst if (anchor == NULL) { 22632145Spst rs_free(p); 22732145Spst return (NULL); 22832145Spst } 22932145Spst memset(anchor, 0, sizeof(*anchor)); 23032145Spst RB_INIT(&anchor->children); 23132145Spst strlcpy(anchor->name, q, sizeof(anchor->name)); 23232145Spst if (parent != NULL) { 23332145Spst strlcpy(anchor->path, parent->path, 23432145Spst sizeof(anchor->path)); 23532145Spst strlcat(anchor->path, "/", sizeof(anchor->path)); 23632145Spst } 23732145Spst strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 23832145Spst if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != 23932145Spst NULL) { 24032145Spst printf("pf_find_or_create_ruleset: RB_INSERT1 " 24132145Spst "'%s' '%s' collides with '%s' '%s'\n", 24232145Spst anchor->path, anchor->name, dup->path, dup->name); 24332145Spst rs_free(anchor); 24432145Spst rs_free(p); 24532145Spst return (NULL); 24632145Spst } 24732145Spst if (parent != NULL) { 24832145Spst anchor->parent = parent; 24932145Spst if ((dup = RB_INSERT(pf_anchor_node, &parent->children, 25032145Spst anchor)) != NULL) { 25132145Spst printf("pf_find_or_create_ruleset: " 25232145Spst "RB_INSERT2 '%s' '%s' collides with " 25332145Spst "'%s' '%s'\n", anchor->path, anchor->name, 25432145Spst dup->path, dup->name); 25532145Spst RB_REMOVE(pf_anchor_global, &pf_anchors, 25632145Spst anchor); 25732145Spst rs_free(anchor); 25832145Spst rs_free(p); 25932145Spst return (NULL); 26032145Spst } 26132145Spst } 26232145Spst pf_init_ruleset(&anchor->ruleset); 26332145Spst anchor->ruleset.anchor = anchor; 26432145Spst parent = anchor; 26532145Spst if (r != NULL) 26632145Spst q = r + 1; 26732145Spst else 26832145Spst *q = 0; 26932145Spst } 27032145Spst rs_free(p); 27132145Spst return (&anchor->ruleset); 27232145Spst} 27332145Spst 27432145Spstvoid 27532145Spstpf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 27632145Spst{ 27732145Spst struct pf_anchor *parent; 27832145Spst int i; 27932145Spst 28032145Spst while (ruleset != NULL) { 28132145Spst if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || 28232145Spst !RB_EMPTY(&ruleset->anchor->children) || 28332145Spst ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 28432145Spst ruleset->topen) 28532145Spst return; 28632145Spst for (i = 0; i < PF_RULESET_MAX; ++i) 28732145Spst if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 28832145Spst !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || 28932145Spst ruleset->rules[i].inactive.open) 29032145Spst return; 29132145Spst RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); 29232145Spst if ((parent = ruleset->anchor->parent) != NULL) 29332145Spst RB_REMOVE(pf_anchor_node, &parent->children, 29432145Spst ruleset->anchor); 29532145Spst rs_free(ruleset->anchor); 29632145Spst if (parent == NULL) 29732145Spst return; 29832145Spst ruleset = &parent->ruleset; 29932145Spst } 30032145Spst} 30132145Spst 30232145Spstint 30332145Spstpf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 30432145Spst const char *name) 30532145Spst{ 30632145Spst char *p, *path; 30732145Spst struct pf_ruleset *ruleset; 30832145Spst 30932145Spst r->anchor = NULL; 31032145Spst r->anchor_relative = 0; 31132145Spst r->anchor_wildcard = 0; 31232145Spst if (!name[0]) 31332145Spst return (0); 31432145Spst path = (char *)rs_malloc(MAXPATHLEN); 31532145Spst bzero(path, MAXPATHLEN); 31632145Spst if (name[0] == '/') 31732145Spst strlcpy(path, name + 1, MAXPATHLEN); 31832145Spst else { 31932145Spst /* relative path */ 32032145Spst r->anchor_relative = 1; 32132145Spst if (s->anchor == NULL || !s->anchor->path[0]) 32232145Spst path[0] = 0; 32332145Spst else 32432145Spst strlcpy(path, s->anchor->path, MAXPATHLEN); 32532145Spst while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 32632145Spst if (!path[0]) { 32732145Spst printf("pf_anchor_setup: .. beyond root\n"); 32832145Spst rs_free(path); 32932145Spst return (1); 33032145Spst } 33132145Spst if ((p = strrchr(path, '/')) != NULL) 33232145Spst *p = 0; 33332145Spst else 33432145Spst path[0] = 0; 33532145Spst r->anchor_relative++; 33632145Spst name += 3; 33732145Spst } 33832145Spst if (path[0]) 33932145Spst strlcat(path, "/", MAXPATHLEN); 34032145Spst strlcat(path, name, MAXPATHLEN); 34132145Spst } 34232145Spst if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 34332145Spst r->anchor_wildcard = 1; 34432145Spst *p = 0; 34532145Spst } 34632145Spst ruleset = pf_find_or_create_ruleset(path); 34732145Spst rs_free(path); 34832145Spst if (ruleset == NULL || ruleset->anchor == NULL) { 34932145Spst printf("pf_anchor_setup: ruleset\n"); 35032145Spst return (1); 35132145Spst } 35232145Spst r->anchor = ruleset->anchor; 35332145Spst r->anchor->refcnt++; 35432145Spst return (0); 35532145Spst} 35632145Spst 35732145Spstint 35832145Spstpf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 35932145Spst struct pfioc_rule *pr) 36032145Spst{ 36132145Spst pr->anchor_call[0] = 0; 36232145Spst if (r->anchor == NULL) 36332145Spst return (0); 36432145Spst if (!r->anchor_relative) { 36532145Spst strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 36632145Spst strlcat(pr->anchor_call, r->anchor->path, 36732145Spst sizeof(pr->anchor_call)); 36832145Spst } else { 36932145Spst char *a, *p; 37032145Spst int i; 37132145Spst 37232145Spst a = (char *)rs_malloc(MAXPATHLEN); 37332145Spst bzero(a, MAXPATHLEN); 37432145Spst if (rs->anchor == NULL) 37532145Spst a[0] = 0; 37632145Spst else 37732145Spst strlcpy(a, rs->anchor->path, MAXPATHLEN); 37832145Spst for (i = 1; i < r->anchor_relative; ++i) { 37932145Spst if ((p = strrchr(a, '/')) == NULL) 38032145Spst p = a; 38132145Spst *p = 0; 38232145Spst strlcat(pr->anchor_call, "../", 38332145Spst sizeof(pr->anchor_call)); 38432145Spst } 38532145Spst if (strncmp(a, r->anchor->path, strlen(a))) { 38632145Spst printf("pf_anchor_copyout: '%s' '%s'\n", a, 38732145Spst r->anchor->path); 38832145Spst rs_free(a); 38932145Spst return (1); 39032145Spst } 39132145Spst if (strlen(r->anchor->path) > strlen(a)) 39232145Spst strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 39332145Spst strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 39432145Spst rs_free(a); 39532145Spst } 39632145Spst if (r->anchor_wildcard) 39732145Spst strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 39832145Spst sizeof(pr->anchor_call)); 39932145Spst return (0); 40032145Spst} 40132145Spst 40232145Spstvoid 40332145Spstpf_anchor_remove(struct pf_rule *r) 40432145Spst{ 40532145Spst if (r->anchor == NULL) 40632145Spst return; 40732145Spst if (r->anchor->refcnt <= 0) { 40832145Spst printf("pf_anchor_remove: broken refcount\n"); 40932145Spst r->anchor = NULL; 41032145Spst return; 41132145Spst } 41232145Spst if (!--r->anchor->refcnt) 41332145Spst pf_remove_if_empty_ruleset(&r->anchor->ruleset); 41432145Spst r->anchor = NULL; 41532145Spst} 41632145Spst