The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net/pf_ruleset.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: pf_ruleset.c,v 1.19 2022/07/20 09:33:11 mbuhl 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/mbuf.h>
   43 #include <sys/pool.h>
   44 #endif /* _KERNEL */
   45 #include <sys/syslog.h>
   46 
   47 #include <netinet/in.h>
   48 #include <netinet/ip.h>
   49 #include <netinet/tcp.h>
   50 
   51 #include <net/if.h>
   52 #include <net/pfvar.h>
   53 
   54 #ifdef INET6
   55 #include <netinet/ip6.h>
   56 #endif /* INET6 */
   57 
   58 
   59 #ifdef _KERNEL
   60 #define rs_malloc(x)            malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO)
   61 #define rs_free(x, siz)         free(x, M_TEMP, siz)
   62 #define rs_pool_get_anchor()    pool_get(&pf_anchor_pl, \
   63                                     PR_WAITOK|PR_LIMITFAIL|PR_ZERO)
   64 #define rs_pool_put_anchor(x)   pool_put(&pf_anchor_pl, x)
   65 
   66 struct pool     pf_anchor_pl;
   67 
   68 #else   /* !_KERNEL */
   69 /* Userland equivalents so we can lend code to pfctl et al. */
   70 
   71 #include <arpa/inet.h>
   72 #include <errno.h>
   73 #include <stdio.h>
   74 #include <stdlib.h>
   75 #include <string.h>
   76 #define rs_malloc(x)            calloc(1, x)
   77 #define rs_free(x, siz)         freezero(x, siz)
   78 #define rs_pool_get_anchor()    calloc(1, sizeof(struct pf_anchor))
   79 #define rs_pool_put_anchor(x)   freezero(x, sizeof(struct pf_anchor))
   80 
   81 #ifdef PFDEBUG
   82 #include <sys/stdarg.h> /* for DPFPRINTF() */
   83 #endif  /* PFDEBUG */
   84 #endif /* _KERNEL */
   85 
   86 
   87 struct pf_anchor_global  pf_anchors;
   88 struct pf_anchor         pf_main_anchor;
   89 
   90 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
   91 
   92 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
   93 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
   94 
   95 static __inline int
   96 pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
   97 {
   98         int c = strcmp(a->path, b->path);
   99 
  100         return (c ? (c < 0 ? -1 : 1) : 0);
  101 }
  102 
  103 void
  104 pf_init_ruleset(struct pf_ruleset *ruleset)
  105 {
  106         memset(ruleset, 0, sizeof(struct pf_ruleset));
  107         TAILQ_INIT(&ruleset->rules.queues[0]);
  108         TAILQ_INIT(&ruleset->rules.queues[1]);
  109         ruleset->rules.active.ptr = &ruleset->rules.queues[0];
  110         ruleset->rules.inactive.ptr = &ruleset->rules.queues[1];
  111 }
  112 
  113 struct pf_anchor *
  114 pf_find_anchor(const char *path)
  115 {
  116         struct pf_anchor        *key, *found;
  117 
  118         key = rs_malloc(sizeof(*key));
  119         if (key == NULL)
  120                 return (NULL);
  121         strlcpy(key->path, path, sizeof(key->path));
  122         found = RB_FIND(pf_anchor_global, &pf_anchors, key);
  123         rs_free(key, sizeof(*key));
  124         return (found);
  125 }
  126 
  127 struct pf_ruleset *
  128 pf_find_ruleset(const char *path)
  129 {
  130         struct pf_anchor        *anchor;
  131 
  132         while (*path == '/')
  133                 path++;
  134         if (!*path)
  135                 return (&pf_main_ruleset);
  136         anchor = pf_find_anchor(path);
  137         if (anchor == NULL)
  138                 return (NULL);
  139         else
  140                 return (&anchor->ruleset);
  141 }
  142 
  143 struct pf_ruleset *
  144 pf_get_leaf_ruleset(char *path, char **path_remainder)
  145 {
  146         struct pf_ruleset       *ruleset;
  147         char                    *leaf, *p;
  148         int                      i = 0;
  149 
  150         p = path;
  151         while (*p == '/')
  152                 p++;
  153 
  154         ruleset = pf_find_ruleset(p);
  155         leaf = p;
  156         while (ruleset == NULL) {
  157                 leaf = strrchr(p, '/');
  158                 if (leaf != NULL) {
  159                         *leaf = '\0';
  160                         i++;
  161                         ruleset = pf_find_ruleset(p);
  162                 } else {
  163                         leaf = path;
  164                         /*
  165                          * if no path component exists, then main ruleset is
  166                          * our parent.
  167                          */
  168                         ruleset = &pf_main_ruleset;
  169                 }
  170         }
  171 
  172         if (path_remainder != NULL)
  173                 *path_remainder = leaf;
  174 
  175         /* restore slashes in path.  */
  176         while (i != 0) {
  177                 while (*leaf != '\0')
  178                         leaf++;
  179                 *leaf = '/';
  180                 i--;
  181         }
  182 
  183         return (ruleset);
  184 }
  185 
  186 struct pf_anchor *
  187 pf_create_anchor(struct pf_anchor *parent, const char *aname)
  188 {
  189         struct pf_anchor        *anchor, *dup;
  190 
  191         if (!*aname || (strlen(aname) >= PF_ANCHOR_NAME_SIZE) ||
  192             ((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH)))
  193                 return (NULL);
  194 
  195         anchor = rs_pool_get_anchor();
  196         if (anchor == NULL)
  197                 return (NULL);
  198 
  199         RB_INIT(&anchor->children);
  200         strlcpy(anchor->name, aname, sizeof(anchor->name));
  201         if (parent != NULL) {
  202                 /*
  203                  * Make sure path for levels 2, 3, ... is terminated by '/':
  204                  *      1/2/3/...
  205                  */
  206                 strlcpy(anchor->path, parent->path, sizeof(anchor->path));
  207                 strlcat(anchor->path, "/", sizeof(anchor->path));
  208         }
  209         strlcat(anchor->path, anchor->name, sizeof(anchor->path));
  210 
  211         if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != NULL) {
  212                 DPFPRINTF(LOG_NOTICE,
  213                     "%s: RB_INSERT to global '%s' '%s' collides with '%s' '%s'",
  214                     __func__, anchor->path, anchor->name, dup->path, dup->name);
  215                 rs_pool_put_anchor(anchor);
  216                 return (NULL);
  217         }
  218 
  219         if (parent != NULL) {
  220                 anchor->parent = parent;
  221                 dup = RB_INSERT(pf_anchor_node, &parent->children, anchor);
  222                 if (dup != NULL) {
  223                         DPFPRINTF(LOG_NOTICE,
  224                             "%s: RB_INSERT to parent '%s' '%s' collides with "
  225                             "'%s' '%s'", __func__, anchor->path, anchor->name,
  226                             dup->path, dup->name);
  227                         RB_REMOVE(pf_anchor_global, &pf_anchors,
  228                             anchor);
  229                         rs_pool_put_anchor(anchor);
  230                         return (NULL);
  231                 }
  232         }
  233 
  234         pf_init_ruleset(&anchor->ruleset);
  235         anchor->ruleset.anchor = anchor;
  236 
  237         return (anchor);
  238 }
  239 
  240 struct pf_ruleset *
  241 pf_find_or_create_ruleset(const char *path)
  242 {
  243         char                    *p, *aname, *r;
  244         struct pf_ruleset       *ruleset;
  245         struct pf_anchor        *anchor;
  246 
  247         if (path[0] == 0)
  248                 return (&pf_main_ruleset);
  249 
  250         while (*path == '/')
  251                 path++;
  252 
  253         ruleset = pf_find_ruleset(path);
  254         if (ruleset != NULL)
  255                 return (ruleset);
  256 
  257         p = rs_malloc(MAXPATHLEN);
  258         if (p == NULL)
  259                 return (NULL);
  260         strlcpy(p, path, MAXPATHLEN);
  261 
  262         ruleset = pf_get_leaf_ruleset(p, &aname);
  263         anchor = ruleset->anchor;
  264 
  265         while (*aname == '/')
  266                 aname++;
  267         /*
  268          * aname is a path remainder, which contains nodes we must create.  We
  269          * process the aname path from left to right, effectively descending
  270          * from parents to children.
  271          */
  272         while ((r = strchr(aname, '/')) != NULL || *aname) {
  273                 if (r != NULL)
  274                         *r = 0;
  275 
  276                 anchor = pf_create_anchor(anchor, aname);
  277                 if (anchor == NULL) {
  278                         rs_free(p, MAXPATHLEN);
  279                         return (NULL);
  280                 }
  281 
  282                 if (r == NULL)
  283                         break;
  284                 else
  285                         aname = r + 1;
  286         }
  287 
  288         rs_free(p, MAXPATHLEN);
  289         return (&anchor->ruleset);
  290 }
  291 
  292 void
  293 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
  294 {
  295         struct pf_anchor        *parent;
  296 
  297         while (ruleset != NULL) {
  298                 if (ruleset == &pf_main_ruleset ||
  299                     !RB_EMPTY(&ruleset->anchor->children) ||
  300                     ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
  301                     ruleset->topen)
  302                         return;
  303                 if (!TAILQ_EMPTY(ruleset->rules.active.ptr) ||
  304                     !TAILQ_EMPTY(ruleset->rules.inactive.ptr) ||
  305                     ruleset->rules.inactive.open)
  306                         return;
  307                 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
  308                 if ((parent = ruleset->anchor->parent) != NULL)
  309                         RB_REMOVE(pf_anchor_node, &parent->children,
  310                             ruleset->anchor);
  311                 rs_pool_put_anchor(ruleset->anchor);
  312                 if (parent == NULL)
  313                         return;
  314                 ruleset = &parent->ruleset;
  315         }
  316 }
  317 
  318 int
  319 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
  320     const char *name)
  321 {
  322         char                    *p, *path;
  323         struct pf_ruleset       *ruleset;
  324 
  325         r->anchor = NULL;
  326         r->anchor_relative = 0;
  327         r->anchor_wildcard = 0;
  328         if (!name[0])
  329                 return (0);
  330         path = rs_malloc(MAXPATHLEN);
  331         if (path == NULL)
  332                 return (1);
  333         if (name[0] == '/')
  334                 strlcpy(path, name + 1, MAXPATHLEN);
  335         else {
  336                 /* relative path */
  337                 r->anchor_relative = 1;
  338                 if (s->anchor == NULL || !s->anchor->path[0])
  339                         path[0] = 0;
  340                 else
  341                         strlcpy(path, s->anchor->path, MAXPATHLEN);
  342                 while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
  343                         if (!path[0]) {
  344                                 DPFPRINTF(LOG_NOTICE,
  345                                     "pf_anchor_setup: .. beyond root");
  346                                 rs_free(path, MAXPATHLEN);
  347                                 return (1);
  348                         }
  349                         if ((p = strrchr(path, '/')) != NULL)
  350                                 *p = 0;
  351                         else
  352                                 path[0] = 0;
  353                         r->anchor_relative++;
  354                         name += 3;
  355                 }
  356                 if (path[0])
  357                         strlcat(path, "/", MAXPATHLEN);
  358                 strlcat(path, name, MAXPATHLEN);
  359         }
  360         if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
  361                 r->anchor_wildcard = 1;
  362                 *p = 0;
  363         }
  364         ruleset = pf_find_or_create_ruleset(path);
  365         rs_free(path, MAXPATHLEN);
  366         if (ruleset == NULL || ruleset == &pf_main_ruleset) {
  367                 DPFPRINTF(LOG_NOTICE,
  368                     "pf_anchor_setup: ruleset");
  369                 return (1);
  370         }
  371         r->anchor = ruleset->anchor;
  372         r->anchor->refcnt++;
  373         return (0);
  374 }
  375 
  376 int
  377 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
  378     struct pfioc_rule *pr)
  379 {
  380         pr->anchor_call[0] = 0;
  381         if (r->anchor == NULL)
  382                 return (0);
  383         if (!r->anchor_relative) {
  384                 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
  385                 strlcat(pr->anchor_call, r->anchor->path,
  386                     sizeof(pr->anchor_call));
  387         } else {
  388                 char    *a, *p;
  389                 int      i;
  390 
  391                 a = rs_malloc(MAXPATHLEN);
  392                 if (a == NULL)
  393                         return (1);
  394                 if (rs == &pf_main_ruleset)
  395                         a[0] = 0;
  396                 else
  397                         strlcpy(a, rs->anchor->path, MAXPATHLEN);
  398                 for (i = 1; i < r->anchor_relative; ++i) {
  399                         if ((p = strrchr(a, '/')) == NULL)
  400                                 p = a;
  401                         *p = 0;
  402                         strlcat(pr->anchor_call, "../",
  403                             sizeof(pr->anchor_call));
  404                 }
  405                 if (strncmp(a, r->anchor->path, strlen(a))) {
  406                         DPFPRINTF(LOG_NOTICE,
  407                             "pf_anchor_copyout: '%s' '%s'", a,
  408                             r->anchor->path);
  409                         rs_free(a, MAXPATHLEN);
  410                         return (1);
  411                 }
  412                 if (strlen(r->anchor->path) > strlen(a))
  413                         strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
  414                             strlen(a) + 1 : 0), sizeof(pr->anchor_call));
  415                 rs_free(a, MAXPATHLEN);
  416         }
  417         if (r->anchor_wildcard)
  418                 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
  419                     sizeof(pr->anchor_call));
  420         return (0);
  421 }
  422 
  423 void
  424 pf_remove_anchor(struct pf_rule *r)
  425 {
  426         if (r->anchor == NULL)
  427                 return;
  428         if (r->anchor->refcnt <= 0)
  429                 DPFPRINTF(LOG_NOTICE, "pf_remove_anchor: broken refcount");
  430         else if (!--r->anchor->refcnt)
  431                 pf_remove_if_empty_ruleset(&r->anchor->ruleset);
  432         r->anchor = NULL;
  433 }

Cache object: f4844168d87ee5f14f66a6f34d02b176


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.