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

Cache object: 3dcb22c74f396ca76a1909b68d510453


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