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/pfil.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 /*      $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $  */
    2 /* $DragonFly: src/sys/net/pfil.c,v 1.14 2008/09/20 06:08:13 sephe Exp $ */
    3 
    4 /*
    5  * Copyright (c) 1996 Matthew R. Green
    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  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. The name of the author may not be used to endorse or promote products
   17  *    derived from this software without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/param.h>
   33 #include <sys/errno.h>
   34 #include <sys/malloc.h>
   35 #include <sys/socket.h>
   36 #include <sys/socketvar.h>
   37 #include <sys/systm.h>
   38 #include <sys/proc.h>
   39 #include <sys/queue.h>
   40 
   41 #include <net/if.h>
   42 #include <net/pfil.h>
   43 #include <net/netmsg2.h>
   44 #include <net/netisr2.h>
   45 #include <sys/mplock2.h>
   46 
   47 #define PFIL_CFGPORT    netisr_cpuport(0)
   48 
   49 #define PFIL_GETMPLOCK(pfh) \
   50 do { \
   51         if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
   52                 get_mplock(); \
   53 } while (0)
   54 
   55 #define PFIL_RELMPLOCK(pfh) \
   56 do { \
   57         if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
   58                 rel_mplock(); \
   59 } while (0)
   60 
   61 /*
   62  * The packet filter hooks are designed for anything to call them to
   63  * possibly intercept the packet.
   64  */
   65 struct packet_filter_hook {
   66         TAILQ_ENTRY(packet_filter_hook) pfil_link;
   67         pfil_func_t     pfil_func;
   68         void            *pfil_arg;
   69         int             pfil_flags;
   70 };
   71 
   72 struct netmsg_pfil {
   73         struct netmsg_base      base;
   74         pfil_func_t             pfil_func;
   75         void                    *pfil_arg;
   76         int                     pfil_flags;
   77         struct pfil_head        *pfil_ph;
   78 };
   79 
   80 static LIST_HEAD(, pfil_head) pfil_head_list =
   81         LIST_HEAD_INITIALIZER(&pfil_head_list);
   82 
   83 static pfil_list_t      *pfil_list_alloc(void);
   84 static void             pfil_list_free(pfil_list_t *);
   85 static void             pfil_list_dup(const pfil_list_t *, pfil_list_t *,
   86                                       const struct packet_filter_hook *);
   87 static void             pfil_list_add(pfil_list_t *, pfil_func_t, void *, int);
   88 static struct packet_filter_hook *
   89                         pfil_list_find(const pfil_list_t *, pfil_func_t,
   90                                        const void *);
   91 
   92 static void             pfil_remove_hook_dispatch(netmsg_t);
   93 static void             pfil_add_hook_dispatch(netmsg_t);
   94 
   95 /*
   96  * pfil_run_hooks() runs the specified packet filter hooks.
   97  */
   98 int
   99 pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
  100     int dir)
  101 {
  102         struct packet_filter_hook *pfh;
  103         struct mbuf *m = *mp;
  104         pfil_list_t *list;
  105         int rv = 0;
  106 
  107         if (dir == PFIL_IN)
  108                 list = ph->ph_in;
  109         else if (dir == PFIL_OUT)
  110                 list = ph->ph_out;
  111         else
  112                 return 0; /* XXX panic? */
  113 
  114         TAILQ_FOREACH(pfh, list, pfil_link) {
  115                 if (pfh->pfil_func != NULL) {
  116                         PFIL_GETMPLOCK(pfh);
  117                         rv = pfh->pfil_func(pfh->pfil_arg, &m, ifp, dir);
  118                         PFIL_RELMPLOCK(pfh);
  119 
  120                         if (rv != 0 || m == NULL)
  121                                 break;
  122                 }
  123         }
  124 
  125         *mp = m;
  126         return (rv);
  127 }
  128 
  129 /*
  130  * pfil_head_register() registers a pfil_head with the packet filter
  131  * hook mechanism.
  132  */
  133 int
  134 pfil_head_register(struct pfil_head *ph)
  135 {
  136         struct pfil_head *lph;
  137 
  138         LIST_FOREACH(lph, &pfil_head_list, ph_list) {
  139                 if (ph->ph_type == lph->ph_type &&
  140                     ph->ph_un.phu_val == lph->ph_un.phu_val)
  141                         return EEXIST;
  142         }
  143 
  144         ph->ph_in = pfil_list_alloc();
  145         ph->ph_out = pfil_list_alloc();
  146         ph->ph_hashooks = 0;
  147 
  148         LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list);
  149 
  150         return (0);
  151 }
  152 
  153 /*
  154  * pfil_head_unregister() removes a pfil_head from the packet filter
  155  * hook mechanism.
  156  */
  157 int
  158 pfil_head_unregister(struct pfil_head *pfh)
  159 {
  160         LIST_REMOVE(pfh, ph_list);
  161         return (0);
  162 }
  163 
  164 /*
  165  * pfil_head_get() returns the pfil_head for a given key/dlt.
  166  */
  167 struct pfil_head *
  168 pfil_head_get(int type, u_long val)
  169 {
  170         struct pfil_head *ph;
  171 
  172         LIST_FOREACH(ph, &pfil_head_list, ph_list) {
  173                 if (ph->ph_type == type && ph->ph_un.phu_val == val)
  174                         break;
  175         }
  176         return (ph);
  177 }
  178 
  179 static void
  180 pfil_add_hook_dispatch(netmsg_t nmsg)
  181 {
  182         struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg;
  183         pfil_func_t func = pfilmsg->pfil_func;
  184         void *arg = pfilmsg->pfil_arg;
  185         int flags = pfilmsg->pfil_flags;
  186         struct pfil_head *ph = pfilmsg->pfil_ph;
  187         const struct packet_filter_hook *pfh;
  188         pfil_list_t *list_in = NULL, *list_out = NULL;
  189         pfil_list_t *old_list_in = NULL, *old_list_out = NULL;
  190         int err = 0;
  191 
  192         /* This probably should not happen ... */
  193         if ((flags & (PFIL_IN | PFIL_OUT)) == 0)
  194                 goto reply; /* XXX panic? */
  195 
  196         /*
  197          * If pfil hooks exist on any of the requested lists,
  198          * then bail out.
  199          */
  200         if (flags & PFIL_IN) {
  201                 pfh = pfil_list_find(ph->ph_in, func, arg);
  202                 if (pfh != NULL) {
  203                         err = EEXIST;
  204                         goto reply;
  205                 }
  206         }
  207         if (flags & PFIL_OUT) {
  208                 pfh = pfil_list_find(ph->ph_out, func, arg);
  209                 if (pfh != NULL) {
  210                         err = EEXIST;
  211                         goto reply;
  212                 }
  213         }
  214 
  215         /*
  216          * Duplicate the requested lists, install new hooks
  217          * on the duplication
  218          */
  219         if (flags & PFIL_IN) {
  220                 list_in = pfil_list_alloc();
  221                 pfil_list_dup(ph->ph_in, list_in, NULL);
  222                 pfil_list_add(list_in, func, arg, flags & ~PFIL_OUT);
  223         }
  224         if (flags & PFIL_OUT) {
  225                 list_out = pfil_list_alloc();
  226                 pfil_list_dup(ph->ph_out, list_out, NULL);
  227                 pfil_list_add(list_out, func, arg, flags & ~PFIL_IN);
  228         }
  229 
  230         /*
  231          * Switch list pointers, but keep the old ones
  232          */
  233         if (list_in != NULL) {
  234                 old_list_in = ph->ph_in;
  235                 ph->ph_in = list_in;
  236         }
  237         if (list_out != NULL) {
  238                 old_list_out = ph->ph_out;
  239                 ph->ph_out = list_out;
  240         }
  241 
  242         /*
  243          * Wait until everyone has finished the old lists iteration
  244          */
  245         netmsg_service_sync();
  246         ph->ph_hashooks = 1;
  247 
  248         /*
  249          * Now it is safe to free the old lists, since no one sees it
  250          */
  251         if (old_list_in != NULL)
  252                 pfil_list_free(old_list_in);
  253         if (old_list_out != NULL)
  254                 pfil_list_free(old_list_out);
  255 reply:
  256         lwkt_replymsg(&nmsg->base.lmsg, err);
  257 }
  258 
  259 /*
  260  * pfil_add_hook() adds a function to the packet filter hook.  the
  261  * flags are:
  262  *      PFIL_IN         call me on incoming packets
  263  *      PFIL_OUT        call me on outgoing packets
  264  *      PFIL_ALL        call me on all of the above
  265  *      PFIL_MPSAFE     call me without BGL
  266  */
  267 int
  268 pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
  269 {
  270         struct netmsg_pfil pfilmsg;
  271         netmsg_base_t nmsg;
  272         int error;
  273 
  274         nmsg = &pfilmsg.base;
  275         netmsg_init(nmsg, NULL, &curthread->td_msgport,
  276                     0, pfil_add_hook_dispatch);
  277         pfilmsg.pfil_func = func;
  278         pfilmsg.pfil_arg = arg;
  279         pfilmsg.pfil_flags = flags;
  280         pfilmsg.pfil_ph = ph;
  281 
  282         error = lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0);
  283         return error;
  284 }
  285 
  286 static void
  287 pfil_remove_hook_dispatch(netmsg_t nmsg)
  288 {
  289         struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg;
  290         pfil_func_t func = pfilmsg->pfil_func;
  291         void *arg = pfilmsg->pfil_arg;
  292         int flags = pfilmsg->pfil_flags;
  293         struct pfil_head *ph = pfilmsg->pfil_ph;
  294         struct packet_filter_hook *skip_in = NULL, *skip_out = NULL;
  295         pfil_list_t *list_in = NULL, *list_out = NULL;
  296         pfil_list_t *old_list_in = NULL, *old_list_out = NULL;
  297         int err = 0;
  298 
  299         /* This probably should not happen ... */
  300         if ((flags & (PFIL_IN | PFIL_OUT)) == 0)
  301                 goto reply; /* XXX panic? */
  302 
  303         /*
  304          * The pfil hook should exist on all requested lists,
  305          * if not just bail out
  306          */
  307         if (flags & PFIL_IN) {
  308                 skip_in = pfil_list_find(ph->ph_in, func, arg);
  309                 if (!skip_in) {
  310                         err = ENOENT;
  311                         goto reply;
  312                 }
  313         }
  314         if (flags & PFIL_OUT) {
  315                 skip_out = pfil_list_find(ph->ph_out, func, arg);
  316                 if (!skip_out) {
  317                         err = ENOENT;
  318                         goto reply;
  319                 }
  320         }
  321 
  322         /*
  323          * Duplicate the requested lists, but the pfil hook to
  324          * be deleted is not copied
  325          */
  326         if (flags & PFIL_IN) {
  327                 KKASSERT(skip_in != NULL);
  328                 list_in = pfil_list_alloc();
  329                 pfil_list_dup(ph->ph_in, list_in, skip_in);
  330         }
  331         if (flags & PFIL_OUT) {
  332                 KKASSERT(skip_out != NULL);
  333                 list_out = pfil_list_alloc();
  334                 pfil_list_dup(ph->ph_out, list_out, skip_out);
  335         }
  336 
  337         /*
  338          * Switch list pointers, but keep the old ones
  339          */
  340         if (list_in != NULL) {
  341                 old_list_in = ph->ph_in;
  342                 ph->ph_in = list_in;
  343         }
  344         if (list_out != NULL) {
  345                 old_list_out = ph->ph_out;
  346                 ph->ph_out = list_out;
  347         }
  348 
  349         /*
  350          * Wait until everyone has finished the old lists iteration
  351          */
  352         if (TAILQ_EMPTY(ph->ph_in) && TAILQ_EMPTY(ph->ph_out))
  353                 ph->ph_hashooks = 0;
  354         netmsg_service_sync();
  355 
  356         /*
  357          * Now it is safe to free the old lists, since no one sees it
  358          */
  359         if (old_list_in != NULL)
  360                 pfil_list_free(old_list_in);
  361         if (old_list_out != NULL)
  362                 pfil_list_free(old_list_out);
  363 reply:
  364         lwkt_replymsg(&nmsg->base.lmsg, err);
  365 }
  366 
  367 /*
  368  * pfil_remove_hook removes a specific function from the packet filter
  369  * hook list.
  370  */
  371 int
  372 pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
  373 {
  374         struct netmsg_pfil pfilmsg;
  375         netmsg_base_t nmsg;
  376 
  377         nmsg = &pfilmsg.base;
  378         netmsg_init(nmsg, NULL, &curthread->td_msgport,
  379                     0, pfil_remove_hook_dispatch);
  380         pfilmsg.pfil_func = func;
  381         pfilmsg.pfil_arg = arg;
  382         pfilmsg.pfil_flags = flags;
  383         pfilmsg.pfil_ph = ph;
  384 
  385         return lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0);
  386 }
  387 
  388 static void
  389 pfil_list_add(pfil_list_t *list, pfil_func_t func, void *arg, int flags)
  390 {
  391         struct packet_filter_hook *pfh;
  392 
  393         KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
  394 
  395         pfh = kmalloc(sizeof(*pfh), M_IFADDR, M_WAITOK);
  396 
  397         pfh->pfil_func = func;
  398         pfh->pfil_arg  = arg;
  399         pfh->pfil_flags = flags;
  400 
  401         /*
  402          * Insert the input list in reverse order of the output list
  403          * so that the same path is followed in or out of the kernel.
  404          */
  405         if (flags & PFIL_IN)
  406                 TAILQ_INSERT_HEAD(list, pfh, pfil_link);
  407         else
  408                 TAILQ_INSERT_TAIL(list, pfh, pfil_link);
  409 }
  410 
  411 static void
  412 pfil_list_dup(const pfil_list_t *from, pfil_list_t *to,
  413               const struct packet_filter_hook *skip)
  414 {
  415         struct packet_filter_hook *pfh_to, *pfh_from;
  416 
  417         KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
  418         KKASSERT(TAILQ_EMPTY(to));
  419 
  420         TAILQ_FOREACH(pfh_from, from, pfil_link) {
  421                 if (pfh_from == skip)
  422                         continue;
  423 
  424                 pfh_to = kmalloc(sizeof(*pfh_to), M_IFADDR, M_WAITOK);
  425                 bcopy(pfh_from, pfh_to, sizeof(*pfh_to));
  426 
  427                 TAILQ_INSERT_TAIL(to, pfh_to, pfil_link);
  428         }
  429 }
  430 
  431 static pfil_list_t *
  432 pfil_list_alloc(void)
  433 {
  434         pfil_list_t *list;
  435 
  436         list = kmalloc(sizeof(*list), M_IFADDR, M_WAITOK);
  437         TAILQ_INIT(list);
  438         return list;
  439 }
  440 
  441 static void
  442 pfil_list_free(pfil_list_t *list)
  443 {
  444         struct packet_filter_hook *pfh;
  445 
  446         while ((pfh = TAILQ_FIRST(list)) != NULL) {
  447                 TAILQ_REMOVE(list, pfh, pfil_link);
  448                 kfree(pfh, M_IFADDR);
  449         }
  450         kfree(list, M_IFADDR);
  451 }
  452 
  453 static struct packet_filter_hook *
  454 pfil_list_find(const pfil_list_t *list, pfil_func_t func, const void *arg)
  455 {
  456         struct packet_filter_hook *pfh;
  457 
  458         KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
  459 
  460         TAILQ_FOREACH(pfh, list, pfil_link) {
  461                 if (pfh->pfil_func == func && pfh->pfil_arg == arg)
  462                         return pfh;
  463         }
  464         return NULL;
  465 }

Cache object: 0a05c1aecc90567e2ccb11728b4cbc38


[ 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.