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/netgraph/ng_bpf.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 /*
    2  * ng_bpf.c
    3  */
    4 
    5 /*-
    6  * Copyright (c) 1999 Whistle Communications, Inc.
    7  * All rights reserved.
    8  * 
    9  * Subject to the following obligations and disclaimer of warranty, use and
   10  * redistribution of this software, in source or object code forms, with or
   11  * without modifications are expressly permitted by Whistle Communications;
   12  * provided, however, that:
   13  * 1. Any and all reproductions of the source or object code must include the
   14  *    copyright notice above and the following disclaimer of warranties; and
   15  * 2. No rights are granted, in any manner or form, to use Whistle
   16  *    Communications, Inc. trademarks, including the mark "WHISTLE
   17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
   18  *    such appears in the above copyright notice or in the software.
   19  * 
   20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
   21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
   22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
   23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
   24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
   25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
   26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
   27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
   28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
   29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
   30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
   32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
   33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
   36  * OF SUCH DAMAGE.
   37  *
   38  * Author: Archie Cobbs <archie@freebsd.org>
   39  *
   40  * $FreeBSD: src/sys/netgraph/ng_bpf.c,v 1.18.2.1 2005/01/31 23:26:28 imp Exp $
   41  * $Whistle: ng_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $
   42  */
   43 
   44 /*
   45  * BPF NETGRAPH NODE TYPE
   46  *
   47  * This node type accepts any number of hook connections.  With each hook
   48  * is associated a bpf(4) filter program, and two hook names (each possibly
   49  * the empty string).  Incoming packets are compared against the filter;
   50  * matching packets are delivered out the first named hook (or dropped if
   51  * the empty string), and non-matching packets are delivered out the second
   52  * named hook (or dropped if the empty string).
   53  *
   54  * Each hook also keeps statistics about how many packets have matched, etc.
   55  */
   56 
   57 #include <sys/param.h>
   58 #include <sys/systm.h>
   59 #include <sys/errno.h>
   60 #include <sys/kernel.h>
   61 #include <sys/malloc.h>
   62 #include <sys/mbuf.h>
   63 
   64 #include <net/bpf.h>
   65 
   66 #include <netgraph/ng_message.h>
   67 #include <netgraph/netgraph.h>
   68 #include <netgraph/ng_parse.h>
   69 #include <netgraph/ng_bpf.h>
   70 
   71 #ifdef NG_SEPARATE_MALLOC
   72 MALLOC_DEFINE(M_NETGRAPH_BPF, "netgraph_bpf", "netgraph bpf node ");
   73 #else
   74 #define M_NETGRAPH_BPF M_NETGRAPH
   75 #endif
   76 
   77 #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0))
   78 
   79 #define ERROUT(x)       do { error = (x); goto done; } while (0)
   80 
   81 /* Per hook private info */
   82 struct ng_bpf_hookinfo {
   83         node_p                  node;
   84         hook_p                  hook;
   85         struct ng_bpf_hookprog  *prog;
   86         struct ng_bpf_hookstat  stats;
   87 };
   88 typedef struct ng_bpf_hookinfo *hinfo_p;
   89 
   90 /* Netgraph methods */
   91 static ng_constructor_t ng_bpf_constructor;
   92 static ng_rcvmsg_t      ng_bpf_rcvmsg;
   93 static ng_shutdown_t    ng_bpf_shutdown;
   94 static ng_newhook_t     ng_bpf_newhook;
   95 static ng_rcvdata_t     ng_bpf_rcvdata;
   96 static ng_disconnect_t  ng_bpf_disconnect;
   97 
   98 /* Internal helper functions */
   99 static int      ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp);
  100 
  101 /* Parse type for one struct bfp_insn */
  102 static const struct ng_parse_struct_field ng_bpf_insn_type_fields[] = {
  103         { "code",       &ng_parse_hint16_type   },
  104         { "jt",         &ng_parse_uint8_type    },
  105         { "jf",         &ng_parse_uint8_type    },
  106         { "k",          &ng_parse_uint32_type   },
  107         { NULL }
  108 };
  109 static const struct ng_parse_type ng_bpf_insn_type = {
  110         &ng_parse_struct_type,
  111         &ng_bpf_insn_type_fields
  112 };
  113 
  114 /* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */
  115 static int
  116 ng_bpf_hookprogary_getLength(const struct ng_parse_type *type,
  117         const u_char *start, const u_char *buf)
  118 {
  119         const struct ng_bpf_hookprog *hp;
  120 
  121         hp = (const struct ng_bpf_hookprog *)
  122             (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog));
  123         return hp->bpf_prog_len;
  124 }
  125 
  126 static const struct ng_parse_array_info ng_bpf_hookprogary_info = {
  127         &ng_bpf_insn_type,
  128         &ng_bpf_hookprogary_getLength,
  129         NULL
  130 };
  131 static const struct ng_parse_type ng_bpf_hookprogary_type = {
  132         &ng_parse_array_type,
  133         &ng_bpf_hookprogary_info
  134 };
  135 
  136 /* Parse type for struct ng_bpf_hookprog */
  137 static const struct ng_parse_struct_field ng_bpf_hookprog_type_fields[]
  138         = NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type);
  139 static const struct ng_parse_type ng_bpf_hookprog_type = {
  140         &ng_parse_struct_type,
  141         &ng_bpf_hookprog_type_fields
  142 };
  143 
  144 /* Parse type for struct ng_bpf_hookstat */
  145 static const struct ng_parse_struct_field ng_bpf_hookstat_type_fields[]
  146         = NG_BPF_HOOKSTAT_TYPE_INFO;
  147 static const struct ng_parse_type ng_bpf_hookstat_type = {
  148         &ng_parse_struct_type,
  149         &ng_bpf_hookstat_type_fields
  150 };
  151 
  152 /* List of commands and how to convert arguments to/from ASCII */
  153 static const struct ng_cmdlist ng_bpf_cmdlist[] = {
  154         {
  155           NGM_BPF_COOKIE,
  156           NGM_BPF_SET_PROGRAM,
  157           "setprogram",
  158           &ng_bpf_hookprog_type,
  159           NULL
  160         },
  161         {
  162           NGM_BPF_COOKIE,
  163           NGM_BPF_GET_PROGRAM,
  164           "getprogram",
  165           &ng_parse_hookbuf_type,
  166           &ng_bpf_hookprog_type
  167         },
  168         {
  169           NGM_BPF_COOKIE,
  170           NGM_BPF_GET_STATS,
  171           "getstats",
  172           &ng_parse_hookbuf_type,
  173           &ng_bpf_hookstat_type
  174         },
  175         {
  176           NGM_BPF_COOKIE,
  177           NGM_BPF_CLR_STATS,
  178           "clrstats",
  179           &ng_parse_hookbuf_type,
  180           NULL
  181         },
  182         {
  183           NGM_BPF_COOKIE,
  184           NGM_BPF_GETCLR_STATS,
  185           "getclrstats",
  186           &ng_parse_hookbuf_type,
  187           &ng_bpf_hookstat_type
  188         },
  189         { 0 }
  190 };
  191 
  192 /* Netgraph type descriptor */
  193 static struct ng_type typestruct = {
  194         .version =      NG_ABI_VERSION,
  195         .name =         NG_BPF_NODE_TYPE,
  196         .constructor =  ng_bpf_constructor,
  197         .rcvmsg =       ng_bpf_rcvmsg,
  198         .shutdown =     ng_bpf_shutdown,
  199         .newhook =      ng_bpf_newhook,
  200         .rcvdata =      ng_bpf_rcvdata,
  201         .disconnect =   ng_bpf_disconnect,
  202         .cmdlist =      ng_bpf_cmdlist,
  203 };
  204 NETGRAPH_INIT(bpf, &typestruct);
  205 
  206 /* Default BPF program for a hook that matches nothing */
  207 static const struct ng_bpf_hookprog ng_bpf_default_prog = {
  208         { '\0' },               /* to be filled in at hook creation time */
  209         { '\0' },
  210         { '\0' },
  211         1,
  212         { BPF_STMT(BPF_RET+BPF_K, 0) }
  213 };
  214 
  215 /*
  216  * Node constructor
  217  *
  218  * We don't keep any per-node private data
  219  * We go via the hooks.
  220  */
  221 static int
  222 ng_bpf_constructor(node_p node)
  223 {
  224         NG_NODE_SET_PRIVATE(node, NULL);
  225         return (0);
  226 }
  227 
  228 /*
  229  * Add a hook
  230  */
  231 static int
  232 ng_bpf_newhook(node_p node, hook_p hook, const char *name)
  233 {
  234         hinfo_p hip;
  235         int error;
  236 
  237         /* Create hook private structure */
  238         MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH_BPF, M_NOWAIT | M_ZERO);
  239         if (hip == NULL)
  240                 return (ENOMEM);
  241         hip->hook = hook;
  242         NG_HOOK_SET_PRIVATE(hook, hip);
  243         hip->node = node;
  244 
  245         /* Attach the default BPF program */
  246         if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) {
  247                 FREE(hip, M_NETGRAPH_BPF);
  248                 NG_HOOK_SET_PRIVATE(hook, NULL);
  249                 return (error);
  250         }
  251 
  252         /* Set hook name */
  253         strncpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook) - 1);
  254         hip->prog->thisHook[sizeof(hip->prog->thisHook) - 1] = '\0';
  255         return (0);
  256 }
  257 
  258 /*
  259  * Receive a control message
  260  */
  261 static int
  262 ng_bpf_rcvmsg(node_p node, item_p item, hook_p lasthook)
  263 {
  264         struct ng_mesg *msg;
  265         struct ng_mesg *resp = NULL;
  266         int error = 0;
  267 
  268         NGI_GET_MSG(item, msg);
  269         switch (msg->header.typecookie) {
  270         case NGM_BPF_COOKIE:
  271                 switch (msg->header.cmd) {
  272                 case NGM_BPF_SET_PROGRAM:
  273                     {
  274                         struct ng_bpf_hookprog *const
  275                             hp = (struct ng_bpf_hookprog *)msg->data;
  276                         hook_p hook;
  277 
  278                         /* Sanity check */
  279                         if (msg->header.arglen < sizeof(*hp)
  280                             || msg->header.arglen
  281                               != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len))
  282                                 ERROUT(EINVAL);
  283 
  284                         /* Find hook */
  285                         if ((hook = ng_findhook(node, hp->thisHook)) == NULL)
  286                                 ERROUT(ENOENT);
  287 
  288                         /* Set new program */
  289                         if ((error = ng_bpf_setprog(hook, hp)) != 0)
  290                                 ERROUT(error);
  291                         break;
  292                     }
  293 
  294                 case NGM_BPF_GET_PROGRAM:
  295                     {
  296                         struct ng_bpf_hookprog *hp;
  297                         hook_p hook;
  298 
  299                         /* Sanity check */
  300                         if (msg->header.arglen == 0)
  301                                 ERROUT(EINVAL);
  302                         msg->data[msg->header.arglen - 1] = '\0';
  303 
  304                         /* Find hook */
  305                         if ((hook = ng_findhook(node, msg->data)) == NULL)
  306                                 ERROUT(ENOENT);
  307 
  308                         /* Build response */
  309                         hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->prog;
  310                         NG_MKRESPONSE(resp, msg,
  311                             NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT);
  312                         if (resp == NULL)
  313                                 ERROUT(ENOMEM);
  314                         bcopy(hp, resp->data,
  315                            NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len));
  316                         break;
  317                     }
  318 
  319                 case NGM_BPF_GET_STATS:
  320                 case NGM_BPF_CLR_STATS:
  321                 case NGM_BPF_GETCLR_STATS:
  322                     {
  323                         struct ng_bpf_hookstat *stats;
  324                         hook_p hook;
  325 
  326                         /* Sanity check */
  327                         if (msg->header.arglen == 0)
  328                                 ERROUT(EINVAL);
  329                         msg->data[msg->header.arglen - 1] = '\0';
  330 
  331                         /* Find hook */
  332                         if ((hook = ng_findhook(node, msg->data)) == NULL)
  333                                 ERROUT(ENOENT);
  334                         stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats;
  335 
  336                         /* Build response (if desired) */
  337                         if (msg->header.cmd != NGM_BPF_CLR_STATS) {
  338                                 NG_MKRESPONSE(resp,
  339                                     msg, sizeof(*stats), M_NOWAIT);
  340                                 if (resp == NULL)
  341                                         ERROUT(ENOMEM);
  342                                 bcopy(stats, resp->data, sizeof(*stats));
  343                         }
  344 
  345                         /* Clear stats (if desired) */
  346                         if (msg->header.cmd != NGM_BPF_GET_STATS)
  347                                 bzero(stats, sizeof(*stats));
  348                         break;
  349                     }
  350 
  351                 default:
  352                         error = EINVAL;
  353                         break;
  354                 }
  355                 break;
  356         default:
  357                 error = EINVAL;
  358                 break;
  359         }
  360 done:
  361         NG_RESPOND_MSG(error, node, item, resp);
  362         NG_FREE_MSG(msg);
  363         return (error);
  364 }
  365 
  366 /*
  367  * Receive data on a hook
  368  *
  369  * Apply the filter, and then drop or forward packet as appropriate.
  370  */
  371 static int
  372 ng_bpf_rcvdata(hook_p hook, item_p item)
  373 {
  374         const hinfo_p hip = NG_HOOK_PRIVATE(hook);
  375         int totlen;
  376         int needfree = 0, error = 0;
  377         u_char *data, buf[256];
  378         hinfo_p dhip;
  379         hook_p dest;
  380         u_int len;
  381         struct mbuf *m;
  382 
  383         m = NGI_M(item);        /* 'item' still owns it.. we are peeking */ 
  384         totlen = m->m_pkthdr.len;
  385         /* Update stats on incoming hook. XXX Can we do 64 bits atomically? */
  386         /* atomic_add_int64(&hip->stats.recvFrames, 1); */
  387         /* atomic_add_int64(&hip->stats.recvOctets, totlen); */
  388         hip->stats.recvFrames++; 
  389         hip->stats.recvOctets += totlen;
  390 
  391         /* Need to put packet in contiguous memory for bpf */
  392         if (m->m_next != NULL) {
  393                 if (totlen > sizeof(buf)) {
  394                         MALLOC(data, u_char *, totlen, M_NETGRAPH_BPF, M_NOWAIT);
  395                         if (data == NULL) {
  396                                 NG_FREE_ITEM(item);
  397                                 return (ENOMEM);
  398                         }
  399                         needfree = 1;
  400                 } else
  401                         data = buf;
  402                 m_copydata(m, 0, totlen, (caddr_t)data);
  403         } else
  404                 data = mtod(m, u_char *);
  405 
  406         /* Run packet through filter */
  407         if (totlen == 0)
  408                 len = 0;        /* don't call bpf_filter() with totlen == 0! */
  409         else
  410                 len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen);
  411         if (needfree)
  412                 FREE(data, M_NETGRAPH_BPF);
  413 
  414         /* See if we got a match and find destination hook */
  415         if (len > 0) {
  416 
  417                 /* Update stats */
  418                 /* XXX atomically? */
  419                 hip->stats.recvMatchFrames++;
  420                 hip->stats.recvMatchOctets += totlen;
  421 
  422                 /* Truncate packet length if required by the filter */
  423                 /* Assume this never changes m */
  424                 if (len < totlen) {
  425                         m_adj(m, -(totlen - len));
  426                         totlen -= len;
  427                 }
  428                 dest = ng_findhook(hip->node, hip->prog->ifMatch);
  429         } else
  430                 dest = ng_findhook(hip->node, hip->prog->ifNotMatch);
  431         if (dest == NULL) {
  432                 NG_FREE_ITEM(item);
  433                 return (0);
  434         }
  435 
  436         /* Deliver frame out destination hook */
  437         dhip = NG_HOOK_PRIVATE(dest);
  438         dhip->stats.xmitOctets += totlen;
  439         dhip->stats.xmitFrames++;
  440         NG_FWD_ITEM_HOOK(error, item, dest);
  441         return (error);
  442 }
  443 
  444 /*
  445  * Shutdown processing
  446  */
  447 static int
  448 ng_bpf_shutdown(node_p node)
  449 {
  450         NG_NODE_UNREF(node);
  451         return (0);
  452 }
  453 
  454 /*
  455  * Hook disconnection
  456  */
  457 static int
  458 ng_bpf_disconnect(hook_p hook)
  459 {
  460         const hinfo_p hip = NG_HOOK_PRIVATE(hook);
  461 
  462         KASSERT(hip != NULL, ("%s: null info", __func__));
  463         FREE(hip->prog, M_NETGRAPH_BPF);
  464         bzero(hip, sizeof(*hip));
  465         FREE(hip, M_NETGRAPH_BPF);
  466         NG_HOOK_SET_PRIVATE(hook, NULL);                        /* for good measure */
  467         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
  468         && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
  469                 ng_rmnode_self(NG_HOOK_NODE(hook));
  470         }
  471         return (0);
  472 }
  473 
  474 /************************************************************************
  475                         HELPER STUFF
  476  ************************************************************************/
  477 
  478 /*
  479  * Set the BPF program associated with a hook
  480  */
  481 static int
  482 ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0)
  483 {
  484         const hinfo_p hip = NG_HOOK_PRIVATE(hook);
  485         struct ng_bpf_hookprog *hp;
  486         int size;
  487 
  488         /* Check program for validity */
  489         if (!bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len))
  490                 return (EINVAL);
  491 
  492         /* Make a copy of the program */
  493         size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len);
  494         MALLOC(hp, struct ng_bpf_hookprog *, size, M_NETGRAPH_BPF, M_NOWAIT);
  495         if (hp == NULL)
  496                 return (ENOMEM);
  497         bcopy(hp0, hp, size);
  498 
  499         /* Free previous program, if any, and assign new one */
  500         if (hip->prog != NULL)
  501                 FREE(hip->prog, M_NETGRAPH_BPF);
  502         hip->prog = hp;
  503         return (0);
  504 }
  505 

Cache object: cbbb16db09e5f5ab34aa933dc9325540


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