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_etf.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_etf.c  Ethertype filter
    3  *
    4  * Copyright (c) 2001, FreeBSD Incorporated 
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice unmodified, this list of conditions, and the following
   12  *    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  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  * Author: Julian Elischer <julian@freebsd.org>
   30  *
   31  * $FreeBSD$
   32  */
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/kernel.h>
   37 #include <sys/mbuf.h>
   38 #include <sys/malloc.h>
   39 #include <sys/ctype.h>
   40 #include <sys/errno.h>
   41 #include <sys/queue.h>
   42 #include <sys/syslog.h>
   43 
   44 #include <net/ethernet.h>
   45 
   46 #include <netgraph/ng_message.h>
   47 #include <netgraph/ng_parse.h>
   48 #include <netgraph/ng_etf.h>
   49 #include <netgraph/netgraph.h>
   50 
   51 /* If you do complicated mallocs you may want to do this */
   52 /* and use it for your mallocs */
   53 #ifdef NG_SEPARATE_MALLOC
   54 MALLOC_DEFINE(M_NETGRAPH_ETF, "netgraph_etf", "netgraph etf node ");
   55 #else
   56 #define M_NETGRAPH_ETF M_NETGRAPH
   57 #endif
   58 
   59 /*
   60  * This section contains the netgraph method declarations for the
   61  * etf node. These methods define the netgraph 'type'.
   62  */
   63 
   64 static ng_constructor_t ng_etf_constructor;
   65 static ng_rcvmsg_t      ng_etf_rcvmsg;
   66 static ng_shutdown_t    ng_etf_shutdown;
   67 static ng_newhook_t     ng_etf_newhook;
   68 static ng_connect_t     ng_etf_connect;
   69 static ng_rcvdata_t     ng_etf_rcvdata;  /* note these are both ng_rcvdata_t */
   70 static ng_disconnect_t  ng_etf_disconnect;
   71 
   72 /* Parse type for struct ng_etfstat */
   73 static const struct ng_parse_struct_field ng_etf_stat_type_fields[]
   74         = NG_ETF_STATS_TYPE_INFO;
   75 static const struct ng_parse_type ng_etf_stat_type = {
   76         &ng_parse_struct_type,
   77         &ng_etf_stat_type_fields
   78 };
   79 /* Parse type for struct ng_setfilter */
   80 static const struct ng_parse_struct_field ng_etf_filter_type_fields[]
   81         = NG_ETF_FILTER_TYPE_INFO;
   82 static const struct ng_parse_type ng_etf_filter_type = {
   83         &ng_parse_struct_type,
   84         &ng_etf_filter_type_fields
   85 };
   86 
   87 /* List of commands and how to convert arguments to/from ASCII */
   88 static const struct ng_cmdlist ng_etf_cmdlist[] = {
   89         {
   90           NGM_ETF_COOKIE,
   91           NGM_ETF_GET_STATUS,
   92           "getstatus",
   93           NULL,
   94           &ng_etf_stat_type,
   95         },
   96         {
   97           NGM_ETF_COOKIE,
   98           NGM_ETF_SET_FLAG,
   99           "setflag",
  100           &ng_parse_int32_type,
  101           NULL
  102         },
  103         {
  104           NGM_ETF_COOKIE,
  105           NGM_ETF_SET_FILTER,
  106           "setfilter",
  107           &ng_etf_filter_type,
  108           NULL
  109         },
  110         { 0 }
  111 };
  112 
  113 /* Netgraph node type descriptor */
  114 static struct ng_type typestruct = {
  115         NG_ABI_VERSION,
  116         NG_ETF_NODE_TYPE,
  117         NULL,
  118         ng_etf_constructor,
  119         ng_etf_rcvmsg,
  120         ng_etf_shutdown,
  121         ng_etf_newhook,
  122         NULL,
  123         ng_etf_connect,
  124         ng_etf_rcvdata,
  125         ng_etf_rcvdata,
  126         ng_etf_disconnect,
  127         ng_etf_cmdlist
  128 };
  129 NETGRAPH_INIT(etf, &typestruct);
  130 
  131 /* Information we store for each hook on each node */
  132 struct ETF_hookinfo {
  133         hook_p  hook;
  134 };
  135 
  136 struct filter {
  137         LIST_ENTRY(filter) next;
  138         u_int16_t       ethertype;      /* network order ethertype */
  139         hook_p          match_hook;     /* Hook to use on a match */
  140 };
  141 
  142 #define HASHSIZE 16 /* Dont change this without changing HASH() */
  143 #define HASH(et) ((((et)>>12)+((et)>>8)+((et)>>4)+(et)) & 0x0f)
  144 LIST_HEAD(filterhead, filter);
  145 
  146 /* Information we store for each node */
  147 struct ETF {
  148         struct ETF_hookinfo downstream_hook;
  149         struct ETF_hookinfo nomatch_hook;
  150         node_p          node;           /* back pointer to node */
  151         u_int           packets_in;     /* packets in from downstream */
  152         u_int           packets_out;    /* packets out towards downstream */
  153         u_int32_t       flags;
  154         struct filterhead hashtable[HASHSIZE];
  155 };
  156 typedef struct ETF *etf_p;
  157 
  158 static struct filter *
  159 ng_etf_findentry(etf_p etfp, u_int16_t ethertype)
  160 {
  161         struct filterhead *chain = etfp->hashtable + HASH(ethertype);
  162         struct filter *fil;
  163         
  164 
  165         LIST_FOREACH(fil, chain, next) {
  166                 if (fil->ethertype == ethertype) {
  167                         return (fil);
  168                 }
  169         }
  170         return (NULL);
  171 }
  172 
  173 
  174 /*
  175  * Allocate the private data structure. The generic node has already
  176  * been created. Link them together. We arrive with a reference to the node
  177  * i.e. the reference count is incremented for us already.
  178  */
  179 static int
  180 ng_etf_constructor(node_p *nodep)
  181 {
  182         etf_p privdata;
  183         int error, i;
  184 
  185         /* Initialize private descriptor */
  186         MALLOC(privdata, etf_p, sizeof(*privdata), M_NETGRAPH_ETF,
  187                 M_NOWAIT | M_ZERO);
  188         if (privdata == NULL)
  189                 return (ENOMEM);
  190         for (i = 0; i < HASHSIZE; i++) {
  191                 LIST_INIT((privdata->hashtable + i));
  192         }
  193 
  194         /* Call the 'generic' (ie, superclass) node constructor */
  195         if ((error = ng_make_node_common(&typestruct, nodep))) {
  196                 FREE(privdata, M_NETGRAPH);
  197                 return (error);
  198         }
  199         /* Link structs together; this counts as our one reference to node */
  200         NG_NODE_SET_PRIVATE((*nodep), privdata);
  201         privdata->node = *nodep;
  202         return (0);
  203 }
  204 
  205 /*
  206  * Give our ok for a hook to be added...
  207  * All names are ok. Two names are special.
  208  */
  209 static int
  210 ng_etf_newhook(node_p node, hook_p hook, const char *name)
  211 {
  212         const etf_p etfp = NG_NODE_PRIVATE(node);
  213         struct ETF_hookinfo *hpriv;
  214 
  215         if (strcmp(name, NG_ETF_HOOK_DOWNSTREAM) == 0) {
  216                 etfp->downstream_hook.hook = hook;
  217                 NG_HOOK_SET_PRIVATE(hook, &etfp->downstream_hook);
  218                 etfp->packets_in = 0;
  219                 etfp->packets_out = 0;
  220         } else if (strcmp(name, NG_ETF_HOOK_NOMATCH) == 0) {
  221                 etfp->nomatch_hook.hook = hook;
  222                 NG_HOOK_SET_PRIVATE(hook, &etfp->nomatch_hook);
  223         } else {
  224                 /*
  225                  * Any other hook name is valid and can
  226                  * later be associated with a filter rule.
  227                  */
  228                 MALLOC(hpriv, struct ETF_hookinfo *, sizeof(*hpriv),
  229                         M_NETGRAPH_ETF, M_NOWAIT | M_ZERO);
  230                 if (hpriv == NULL) {
  231                         return (ENOMEM);
  232                 }
  233 
  234                 NG_HOOK_SET_PRIVATE(hook, hpriv);
  235                 hpriv->hook = hook;
  236         }
  237         return(0);
  238 }
  239 
  240 /*
  241  * Get a netgraph control message.
  242  * We actually recieve a queue item that has a pointer to the message.
  243  * If we free the item, the message will be freed too, unless we remove
  244  * it from the item using NGI_GET_MSG();
  245  * The return address is also stored in the item, as an ng_ID_t,
  246  * accessible as NGI_RETADDR(item);
  247  * Check it is one we understand. If needed, send a response.
  248  * We could save the address for an async action later, but don't here.
  249  * Always free the message.
  250  * The response should be in a malloc'd region that the caller can 'free'.
  251  * The NG_MKRESPONSE macro does all this for us.
  252  * A response is not required.
  253  * Theoretically you could respond defferently to old message types if
  254  * the cookie in the header didn't match what we consider to be current
  255  * (so that old userland programs could continue to work).
  256  */
  257 static int
  258 ng_etf_rcvmsg(node_p node, struct ng_mesg *msg,
  259     const char *retaddr, struct ng_mesg **rptr)
  260 {
  261         const etf_p etfp = NG_NODE_PRIVATE(node);
  262         struct ng_mesg *resp = NULL;
  263         int error = 0;
  264 
  265         /* Deal with message according to cookie and command */
  266         switch (msg->header.typecookie) {
  267         case NGM_ETF_COOKIE: 
  268                 switch (msg->header.cmd) {
  269                 case NGM_ETF_GET_STATUS:
  270                     {
  271                         struct ng_etfstat *stats;
  272 
  273                         NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
  274                         if (!resp) {
  275                                 error = ENOMEM;
  276                                 break;
  277                         }
  278                         stats = (struct ng_etfstat *) resp->data;
  279                         stats->packets_in = etfp->packets_in;
  280                         stats->packets_out = etfp->packets_out;
  281                         break;
  282                     }
  283                 case NGM_ETF_SET_FLAG:
  284                         if (msg->header.arglen != sizeof(u_int32_t)) {
  285                                 error = EINVAL;
  286                                 break;
  287                         }
  288                         etfp->flags = *((u_int32_t *) msg->data);
  289                         break;
  290                 case NGM_ETF_SET_FILTER:
  291                         {
  292                                 struct ng_etffilter *f;
  293                                 struct filter *fil;
  294                                 hook_p  hook;
  295 
  296                                 /* Check message long enough for this command */
  297                                 if (msg->header.arglen != sizeof(*f)) {
  298                                         error = EINVAL;
  299                                         break;
  300                                 }
  301 
  302                                 /* Make sure hook referenced exists */
  303                                 f = (struct ng_etffilter *)msg->data;
  304                                 hook = ng_findhook(node, f->matchhook);
  305                                 if (hook == NULL) {
  306                                         error = ENOENT;
  307                                         break;
  308                                 }
  309 
  310                                 /* and is not the downstream hook */
  311                                 if (hook == etfp->downstream_hook.hook) {
  312                                         error = EINVAL;
  313                                         break;
  314                                 }
  315 
  316                                 /* Check we don't already trap this ethertype */
  317                                 if (ng_etf_findentry(etfp,
  318                                                 htons(f->ethertype))) {
  319                                         error = EEXIST;
  320                                         break;
  321                                 }
  322 
  323                                 /*
  324                                  * Ok, make the filter and put it in the 
  325                                  * hashtable ready for matching.
  326                                  */
  327                                 MALLOC(fil, struct filter *, sizeof(*fil),
  328                                         M_NETGRAPH_ETF, M_NOWAIT | M_ZERO);
  329                                 if (fil == NULL) {
  330                                         return (ENOMEM);
  331                                 }
  332 
  333                                 fil->match_hook = hook;
  334                                 fil->ethertype = htons(f->ethertype);
  335                                 LIST_INSERT_HEAD( etfp->hashtable
  336                                         + HASH(fil->ethertype),
  337                                                 fil, next);
  338                         }
  339                         break;
  340                 default:
  341                         error = EINVAL;         /* unknown command */
  342                         break;
  343                 }
  344                 break;
  345         default:
  346                 error = EINVAL;                 /* unknown cookie type */
  347                 break;
  348         }
  349 
  350         /* Take care of synchronous response, if any */
  351         NG_RESPOND_MSG(error, node, retaddr, resp, rptr);
  352         /* Free the message and return */
  353         NG_FREE_MSG(msg);
  354         return(error);
  355 }
  356 
  357 /*
  358  * Receive data, and do something with it.
  359  * Actually we receive a queue item which holds the data.
  360  * If we free the item it wil also froo the data and metadata unless
  361  * we have previously disassociated them using the NGI_GET_etf() macros.
  362  * Possibly send it out on another link after processing.
  363  * Possibly do something different if it comes from different
  364  * hooks. the caller will never free m or meta, so
  365  * if we use up this data or abort we must free BOTH of these.
  366  *
  367  * If we want, we may decide to force this data to be queued and reprocessed
  368  * at the netgraph NETISR time.
  369  * We would do that by setting the HK_QUEUE flag on our hook. We would do that
  370  * in the connect() method. 
  371  */
  372 static int
  373 ng_etf_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
  374 {
  375         const etf_p etfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  376         struct ether_header *eh;
  377         int error = 0;
  378         u_int16_t ethertype;
  379         struct filter *fil;
  380 
  381         if (NG_HOOK_PRIVATE(hook) == NULL) { /* Shouldn't happen but.. */
  382                 NG_FREE_DATA(m, meta);
  383         }
  384 
  385         /* 
  386          * Everything not from the downstream hook goes to the
  387          * downstream hook. But only if it matches the ethertype
  388          * of the source hook. Un matching must go to/from 'nomatch'.
  389          */
  390 
  391         /* Make sure we have an entire header */
  392         if (m->m_len < sizeof(*eh) ) {
  393                 m = m_pullup(m, sizeof(*eh));
  394                 if (m == NULL) {
  395                         NG_FREE_META(meta);
  396                         return(EINVAL);
  397                 }
  398         }
  399 
  400         eh = mtod(m, struct ether_header *);
  401         ethertype = eh->ether_type;
  402         fil = ng_etf_findentry(etfp, ethertype);
  403 
  404         /*
  405          * if from downstream, select between a match hook or
  406          * the nomatch hook
  407          */
  408         if (hook == etfp->downstream_hook.hook) {
  409                 etfp->packets_in++;
  410                 if (fil && fil->match_hook) {
  411                         NG_SEND_DATA(error, fil->match_hook, m, meta);
  412                 } else {
  413                         NG_SEND_DATA(error, etfp->nomatch_hook.hook, m, meta);
  414                 }
  415         } else {
  416                 /* 
  417                  * It must be heading towards the downstream.
  418                  * Check that it's ethertype matches 
  419                  * the filters for it's input hook.
  420                  * If it doesn't have one, check it's from nomatch.
  421                  */
  422                 if ((fil && (fil->match_hook != hook))
  423                 || ((fil == NULL) && (hook != etfp->nomatch_hook.hook))) {
  424                         NG_FREE_DATA(m, meta);
  425                         return (EPROTOTYPE);
  426                 }
  427                 NG_SEND_DATA( error, etfp->downstream_hook.hook, m, meta);
  428                 if (error == 0) {
  429                         etfp->packets_out++;
  430                 }
  431         }
  432         return (error);
  433 }
  434 
  435 /*
  436  * Do local shutdown processing..
  437  * All our links and the name have already been removed.
  438  */
  439 static int
  440 ng_etf_shutdown(node_p node)
  441 {
  442         const etf_p privdata = NG_NODE_PRIVATE(node);
  443 
  444         node->flags |= NG_INVALID;
  445         ng_cutlinks(node);
  446         ng_unname(node);
  447         NG_NODE_SET_PRIVATE(node, NULL);
  448         NG_NODE_UNREF(privdata->node);
  449         FREE(privdata, M_NETGRAPH_ETF);
  450         return (0);
  451 }
  452 
  453 /*
  454  * This is called once we've already connected a new hook to the other node.
  455  * It gives us a chance to balk at the last minute.
  456  */
  457 static int
  458 ng_etf_connect(hook_p hook)
  459 {
  460         return (0);
  461 }
  462 
  463 /*
  464  * Hook disconnection
  465  *
  466  * For this type, removal of the last link destroys the node
  467  */
  468 static int
  469 ng_etf_disconnect(hook_p hook)
  470 {
  471         const etf_p etfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  472         int i;
  473         struct filter *fil;
  474 
  475         /* purge any rules that refer to this filter */
  476         for (i = 0; i < HASHSIZE; i++) {
  477                 LIST_FOREACH(fil, (etfp->hashtable + i), next) {
  478                         if (fil->match_hook == hook) {
  479                                 LIST_REMOVE(fil, next);
  480                         }
  481                 }
  482         }
  483                 
  484         /* If it's not one of the special hooks, then free it */
  485         if (hook == etfp->downstream_hook.hook) {
  486                 etfp->downstream_hook.hook = NULL;
  487         } else if (hook == etfp->nomatch_hook.hook) {
  488                 etfp->nomatch_hook.hook = NULL;
  489         } else {
  490                 if (NG_HOOK_PRIVATE(hook)) /* Paranoia */
  491                         FREE(NG_HOOK_PRIVATE(hook), M_NETGRAPH_ETF);
  492         }
  493 
  494         NG_HOOK_SET_PRIVATE(hook, NULL);
  495 
  496         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
  497         && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) /* already shutting down? */
  498                 ng_rmnode(NG_HOOK_NODE(hook));
  499         return (0);
  500 }
  501 

Cache object: c983dad57f6f05be2aa0f45c24cd6aa6


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