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

Cache object: 59794a1dc7008383289885a8fac9b75d


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