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_vlan.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  * Copyright (c) 2003 IPNET Internet Communication Company
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * Author: Ruslan Ermilov <ru@FreeBSD.org>
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 #include <sys/param.h>
   32 #include <sys/errno.h>
   33 #include <sys/kernel.h>
   34 #include <sys/malloc.h>
   35 #include <sys/mbuf.h>
   36 #include <sys/queue.h>
   37 #include <sys/socket.h>
   38 #include <sys/systm.h>
   39 
   40 #include <net/ethernet.h>
   41 #include <net/if.h>
   42 #include <net/if_vlan_var.h>
   43 
   44 #include <netgraph/ng_message.h>
   45 #include <netgraph/ng_parse.h>
   46 #include <netgraph/ng_vlan.h>
   47 #include <netgraph/netgraph.h>
   48 
   49 static ng_constructor_t ng_vlan_constructor;
   50 static ng_rcvmsg_t      ng_vlan_rcvmsg;
   51 static ng_shutdown_t    ng_vlan_shutdown;
   52 static ng_newhook_t     ng_vlan_newhook;
   53 static ng_rcvdata_t     ng_vlan_rcvdata;
   54 static ng_disconnect_t  ng_vlan_disconnect;
   55 
   56 /* Parse type for struct ng_vlan_filter. */
   57 static const struct ng_parse_struct_field ng_vlan_filter_fields[] =
   58         NG_VLAN_FILTER_FIELDS;
   59 static const struct ng_parse_type ng_vlan_filter_type = {
   60         &ng_parse_struct_type,
   61         &ng_vlan_filter_fields
   62 };
   63 
   64 static int
   65 ng_vlan_getTableLength(const struct ng_parse_type *type,
   66     const u_char *start, const u_char *buf)
   67 {
   68         const struct ng_vlan_table *const table =
   69             (const struct ng_vlan_table *)(buf - sizeof(u_int32_t));
   70 
   71         return table->n;
   72 }
   73 
   74 /* Parse type for struct ng_vlan_table. */
   75 static const struct ng_parse_array_info ng_vlan_table_array_info = {
   76         &ng_vlan_filter_type,
   77         ng_vlan_getTableLength
   78 };
   79 static const struct ng_parse_type ng_vlan_table_array_type = {
   80         &ng_parse_array_type,
   81         &ng_vlan_table_array_info
   82 };
   83 static const struct ng_parse_struct_field ng_vlan_table_fields[] =
   84         NG_VLAN_TABLE_FIELDS;
   85 static const struct ng_parse_type ng_vlan_table_type = {
   86         &ng_parse_struct_type,
   87         &ng_vlan_table_fields
   88 };
   89 
   90 /* List of commands and how to convert arguments to/from ASCII. */
   91 static const struct ng_cmdlist ng_vlan_cmdlist[] = {
   92         {
   93           NGM_VLAN_COOKIE,
   94           NGM_VLAN_ADD_FILTER,
   95           "addfilter",
   96           &ng_vlan_filter_type,
   97           NULL
   98         },
   99         {
  100           NGM_VLAN_COOKIE,
  101           NGM_VLAN_DEL_FILTER,
  102           "delfilter",
  103           &ng_parse_hookbuf_type,
  104           NULL
  105         },
  106         {
  107           NGM_VLAN_COOKIE,
  108           NGM_VLAN_GET_TABLE,
  109           "gettable",
  110           NULL,
  111           &ng_vlan_table_type
  112         },
  113         { 0 }
  114 };
  115 
  116 static struct ng_type ng_vlan_typestruct = {
  117         .version =      NG_ABI_VERSION,
  118         .name =         NG_VLAN_NODE_TYPE,
  119         .constructor =  ng_vlan_constructor,
  120         .rcvmsg =       ng_vlan_rcvmsg,
  121         .shutdown =     ng_vlan_shutdown,
  122         .newhook =      ng_vlan_newhook,
  123         .rcvdata =      ng_vlan_rcvdata,
  124         .disconnect =   ng_vlan_disconnect,
  125         .cmdlist =      ng_vlan_cmdlist,
  126 };
  127 NETGRAPH_INIT(vlan, &ng_vlan_typestruct);
  128 
  129 struct filter {
  130         LIST_ENTRY(filter) next;
  131         u_int16_t       vlan;
  132         hook_p          hook;
  133 };
  134 
  135 #define HASHSIZE        16
  136 #define HASH(id)        ((((id) >> 8) ^ ((id) >> 4) ^ (id)) & 0x0f)
  137 LIST_HEAD(filterhead, filter);
  138 
  139 typedef struct {
  140         hook_p          downstream_hook;
  141         hook_p          nomatch_hook;
  142         struct filterhead hashtable[HASHSIZE];
  143         u_int32_t       nent;
  144 } *priv_p;
  145 
  146 static struct filter *
  147 ng_vlan_findentry(priv_p priv, u_int16_t vlan)
  148 {
  149         struct filterhead *chain = &priv->hashtable[HASH(vlan)];
  150         struct filter *f;
  151 
  152         LIST_FOREACH(f, chain, next)
  153                 if (f->vlan == vlan)
  154                         return (f);
  155         return (NULL);
  156 }
  157 
  158 static int
  159 ng_vlan_constructor(node_p node)
  160 {
  161         priv_p priv;
  162         int i;
  163 
  164         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
  165         if (priv == NULL)
  166                 return (ENOMEM);
  167         for (i = 0; i < HASHSIZE; i++)
  168                 LIST_INIT(&priv->hashtable[i]);
  169         NG_NODE_SET_PRIVATE(node, priv);
  170         return (0);
  171 }
  172 
  173 static int
  174 ng_vlan_newhook(node_p node, hook_p hook, const char *name)
  175 {
  176         const priv_p priv = NG_NODE_PRIVATE(node);
  177 
  178         if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0)
  179                 priv->downstream_hook = hook;
  180         else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0)
  181                 priv->nomatch_hook = hook;
  182         else {
  183                 /*
  184                  * Any other hook name is valid and can
  185                  * later be associated with a filter rule.
  186                  */
  187         }
  188         NG_HOOK_SET_PRIVATE(hook, NULL);
  189         return (0);
  190 }
  191 
  192 static int
  193 ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
  194 {
  195         const priv_p priv = NG_NODE_PRIVATE(node);
  196         int error = 0;
  197         struct ng_mesg *msg, *resp = NULL;
  198         struct ng_vlan_filter *vf;
  199         struct filter *f;
  200         hook_p hook;
  201         struct ng_vlan_table *t;
  202         int i;
  203 
  204         NGI_GET_MSG(item, msg);
  205         /* Deal with message according to cookie and command. */
  206         switch (msg->header.typecookie) {
  207         case NGM_VLAN_COOKIE:
  208                 switch (msg->header.cmd) {
  209                 case NGM_VLAN_ADD_FILTER:
  210                         /* Check that message is long enough. */
  211                         if (msg->header.arglen != sizeof(*vf)) {
  212                                 error = EINVAL;
  213                                 break;
  214                         }
  215                         vf = (struct ng_vlan_filter *)msg->data;
  216                         /* Sanity check the VLAN ID value. */
  217                         if (vf->vlan & ~EVL_VLID_MASK) {
  218                                 error = EINVAL;
  219                                 break;
  220                         }
  221                         /* Check that a referenced hook exists. */
  222                         hook = ng_findhook(node, vf->hook);
  223                         if (hook == NULL) {
  224                                 error = ENOENT;
  225                                 break;
  226                         }
  227                         /* And is not one of the special hooks. */
  228                         if (hook == priv->downstream_hook ||
  229                             hook == priv->nomatch_hook) {
  230                                 error = EINVAL;
  231                                 break;
  232                         }
  233                         /* And is not already in service. */
  234                         if (NG_HOOK_PRIVATE(hook) != NULL) {
  235                                 error = EEXIST;
  236                                 break;
  237                         }
  238                         /* Check we don't already trap this VLAN. */
  239                         if (ng_vlan_findentry(priv, vf->vlan)) {
  240                                 error = EEXIST;
  241                                 break;
  242                         }
  243                         /* Create filter. */
  244                         MALLOC(f, struct filter *, sizeof(*f),
  245                             M_NETGRAPH, M_NOWAIT | M_ZERO);
  246                         if (f == NULL) {
  247                                 error = ENOMEM;
  248                                 break;
  249                         }
  250                         /* Link filter and hook together. */
  251                         f->hook = hook;
  252                         f->vlan = vf->vlan;
  253                         NG_HOOK_SET_PRIVATE(hook, f);
  254                         /* Register filter in a hash table. */
  255                         LIST_INSERT_HEAD(
  256                             &priv->hashtable[HASH(f->vlan)], f, next);
  257                         priv->nent++;
  258                         break;
  259                 case NGM_VLAN_DEL_FILTER:
  260                         /* Check that message is long enough. */
  261                         if (msg->header.arglen != NG_HOOKSIZ) {
  262                                 error = EINVAL;
  263                                 break;
  264                         }
  265                         /* Check that hook exists and is active. */
  266                         hook = ng_findhook(node, (char *)msg->data);
  267                         if (hook == NULL ||
  268                             (f = NG_HOOK_PRIVATE(hook)) == NULL) {
  269                                 error = ENOENT;
  270                                 break;
  271                         }
  272                         /* Purge a rule that refers to this hook. */
  273                         NG_HOOK_SET_PRIVATE(hook, NULL);
  274                         LIST_REMOVE(f, next);
  275                         priv->nent--;
  276                         FREE(f, M_NETGRAPH);
  277                         break;
  278                 case NGM_VLAN_GET_TABLE:
  279                         NG_MKRESPONSE(resp, msg, sizeof(*t) +
  280                             priv->nent * sizeof(*t->filter), M_NOWAIT);
  281                         if (resp == NULL) {
  282                                 error = ENOMEM;
  283                                 break;
  284                         }
  285                         t = (struct ng_vlan_table *)resp->data;
  286                         t->n = priv->nent;
  287                         vf = &t->filter[0];
  288                         for (i = 0; i < HASHSIZE; i++) {
  289                                 LIST_FOREACH(f, &priv->hashtable[i], next) {
  290                                         vf->vlan = f->vlan;
  291                                         strncpy(vf->hook, NG_HOOK_NAME(f->hook),
  292                                             NG_HOOKSIZ);
  293                                         vf++;
  294                                 }
  295                         }
  296                         break;
  297                 default:                /* Unknown command. */
  298                         error = EINVAL;
  299                         break;
  300                 }
  301                 break;
  302         case NGM_FLOW_COOKIE:
  303             {
  304                 struct ng_mesg *copy;
  305                 struct filterhead *chain;
  306                 struct filter *f;
  307 
  308                 /*
  309                  * Flow control messages should come only
  310                  * from downstream.
  311                  */
  312 
  313                 if (lasthook == NULL)
  314                         break;
  315                 if (lasthook != priv->downstream_hook)
  316                         break;
  317 
  318                 /* Broadcast the event to all uplinks. */
  319                 for (i = 0, chain = priv->hashtable; i < HASHSIZE;
  320                     i++, chain++)
  321                 LIST_FOREACH(f, chain, next) {
  322                         NG_COPYMESSAGE(copy, msg, M_NOWAIT);
  323                         if (copy == NULL)
  324                                 continue;
  325                         NG_SEND_MSG_HOOK(error, node, copy, f->hook, 0);
  326                 }
  327 
  328                 break;
  329             }
  330         default:                        /* Unknown type cookie. */
  331                 error = EINVAL;
  332                 break;
  333         }
  334         NG_RESPOND_MSG(error, node, item, resp);
  335         NG_FREE_MSG(msg);
  336         return (error);
  337 }
  338 
  339 static int
  340 ng_vlan_rcvdata(hook_p hook, item_p item)
  341 {
  342         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  343         struct ether_header *eh;
  344         struct ether_vlan_header *evl = NULL;
  345         int error;
  346         u_int16_t vlan;
  347         struct mbuf *m;
  348         struct filter *f;
  349 
  350         /* Make sure we have an entire header. */
  351         NGI_GET_M(item, m);
  352         if (m->m_len < sizeof(*eh) &&
  353             (m = m_pullup(m, sizeof(*eh))) == NULL) {
  354                 NG_FREE_ITEM(item);
  355                 return (EINVAL);
  356         }
  357         eh = mtod(m, struct ether_header *);
  358         if (hook == priv->downstream_hook) {
  359                 /*
  360                  * If from downstream, select between a match hook
  361                  * or the nomatch hook.
  362                  */
  363                 if (m->m_flags & M_VLANTAG ||
  364                     eh->ether_type == htons(ETHERTYPE_VLAN)) {
  365                         if (m->m_flags & M_VLANTAG) {
  366                                 /*
  367                                  * Packet is tagged, m contains a normal
  368                                  * Ethernet frame; tag is stored out-of-band.
  369                                  */
  370                                 vlan = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
  371                         } else {
  372                                 if (m->m_len < sizeof(*evl) &&
  373                                     (m = m_pullup(m, sizeof(*evl))) == NULL) {
  374                                         NG_FREE_ITEM(item);
  375                                         return (EINVAL);
  376                                 }
  377                                 evl = mtod(m, struct ether_vlan_header *);
  378                                 vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag));
  379                         }
  380                         if ((f = ng_vlan_findentry(priv, vlan)) != NULL) {
  381                                 if (m->m_flags & M_VLANTAG) {
  382                                         m->m_pkthdr.ether_vtag = 0;
  383                                         m->m_flags &= ~M_VLANTAG;
  384                                 } else {
  385                                         evl->evl_encap_proto = evl->evl_proto;
  386                                         bcopy(mtod(m, caddr_t),
  387                                             mtod(m, caddr_t) +
  388                                             ETHER_VLAN_ENCAP_LEN,
  389                                             ETHER_HDR_LEN);
  390                                         m_adj(m, ETHER_VLAN_ENCAP_LEN);
  391                                 }
  392                         }
  393                 } else
  394                         f = NULL;
  395                 if (f != NULL)
  396                         NG_FWD_NEW_DATA(error, item, f->hook, m);
  397                 else
  398                         NG_FWD_NEW_DATA(error, item, priv->nomatch_hook, m);
  399         } else {
  400                 /*
  401                  * It is heading towards the downstream.
  402                  * If from nomatch, pass it unmodified.
  403                  * Otherwise, do the VLAN encapsulation.
  404                  */
  405                 if (hook != priv->nomatch_hook) {
  406                         if ((f = NG_HOOK_PRIVATE(hook)) == NULL) {
  407                                 NG_FREE_ITEM(item);
  408                                 NG_FREE_M(m);
  409                                 return (EOPNOTSUPP);
  410                         }
  411                         M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
  412                         /* M_PREPEND takes care of m_len and m_pkthdr.len. */
  413                         if (m == NULL || (m->m_len < sizeof(*evl) &&
  414                             (m = m_pullup(m, sizeof(*evl))) == NULL)) {
  415                                 NG_FREE_ITEM(item);
  416                                 return (ENOMEM);
  417                         }
  418                         /*
  419                          * Transform the Ethernet header into an Ethernet header
  420                          * with 802.1Q encapsulation.
  421                          */
  422                         bcopy(mtod(m, char *) + ETHER_VLAN_ENCAP_LEN,
  423                             mtod(m, char *), ETHER_HDR_LEN);
  424                         evl = mtod(m, struct ether_vlan_header *);
  425                         evl->evl_proto = evl->evl_encap_proto;
  426                         evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
  427                         evl->evl_tag = htons(f->vlan);
  428                 }
  429                 NG_FWD_NEW_DATA(error, item, priv->downstream_hook, m);
  430         }
  431         return (error);
  432 }
  433 
  434 static int
  435 ng_vlan_shutdown(node_p node)
  436 {
  437         const priv_p priv = NG_NODE_PRIVATE(node);
  438 
  439         NG_NODE_SET_PRIVATE(node, NULL);
  440         NG_NODE_UNREF(node);
  441         FREE(priv, M_NETGRAPH);
  442         return (0);
  443 }
  444 
  445 static int
  446 ng_vlan_disconnect(hook_p hook)
  447 {
  448         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  449         struct filter *f;
  450 
  451         if (hook == priv->downstream_hook)
  452                 priv->downstream_hook = NULL;
  453         else if (hook == priv->nomatch_hook)
  454                 priv->nomatch_hook = NULL;
  455         else {
  456                 /* Purge a rule that refers to this hook. */
  457                 if ((f = NG_HOOK_PRIVATE(hook)) != NULL) {
  458                         LIST_REMOVE(f, next);
  459                         priv->nent--;
  460                         FREE(f, M_NETGRAPH);
  461                 }
  462         }
  463         NG_HOOK_SET_PRIVATE(hook, NULL);
  464         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
  465             (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
  466                 ng_rmnode_self(NG_HOOK_NODE(hook));
  467         return (0);
  468 }

Cache object: f4d04129b2c27539b00b3aa44f52663e


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