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_arp.h>
   43 #include <net/if_vlan_var.h>
   44 
   45 #include <netgraph/ng_message.h>
   46 #include <netgraph/ng_parse.h>
   47 #include <netgraph/ng_vlan.h>
   48 #include <netgraph/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         NG_ABI_VERSION,
  119         NG_VLAN_NODE_TYPE,
  120         NULL,
  121         ng_vlan_constructor,
  122         ng_vlan_rcvmsg,
  123         ng_vlan_shutdown,
  124         ng_vlan_newhook,
  125         NULL,
  126         NULL,
  127         ng_vlan_rcvdata,
  128         ng_vlan_rcvdata,
  129         ng_vlan_disconnect,
  130         ng_vlan_cmdlist
  131 };
  132 NETGRAPH_INIT(vlan, &ng_vlan_typestruct);
  133 
  134 struct filter {
  135         LIST_ENTRY(filter) next;
  136         u_int16_t       vlan;
  137         hook_p          hook;
  138 };
  139 
  140 #define HASHSIZE        16
  141 #define HASH(id)        ((((id) >> 8) ^ ((id) >> 4) ^ (id)) & 0x0f)
  142 LIST_HEAD(filterhead, filter);
  143 
  144 typedef struct {
  145         hook_p          downstream_hook;
  146         hook_p          nomatch_hook;
  147         struct filterhead hashtable[HASHSIZE];
  148         u_int32_t       nent;
  149 } *priv_p;
  150 
  151 static struct filter *
  152 ng_vlan_findentry(priv_p priv, u_int16_t vlan)
  153 {
  154         struct filterhead *chain = &priv->hashtable[HASH(vlan)];
  155         struct filter *f;
  156 
  157         LIST_FOREACH(f, chain, next)
  158                 if (f->vlan == vlan)
  159                         return (f);
  160         return (NULL);
  161 }
  162 
  163 static int
  164 ng_vlan_constructor(node_p *nodep)
  165 {
  166         priv_p priv;
  167         int error, i;
  168 
  169         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
  170         if (priv == NULL)
  171                 return (ENOMEM);
  172         for (i = 0; i < HASHSIZE; i++)
  173                 LIST_INIT(&priv->hashtable[i]);
  174         /* Call the generic node constructor. */
  175         if ((error = ng_make_node_common(&ng_vlan_typestruct, nodep)) != 0) {
  176                 FREE(priv, M_NETGRAPH);
  177                 return (error);
  178         }
  179         NG_NODE_SET_PRIVATE(*nodep, priv);
  180         return (0);
  181 }
  182 
  183 static int
  184 ng_vlan_newhook(node_p node, hook_p hook, const char *name)
  185 {
  186         const priv_p priv = NG_NODE_PRIVATE(node);
  187 
  188         if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0)
  189                 priv->downstream_hook = hook;
  190         else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0)
  191                 priv->nomatch_hook = hook;
  192         else {
  193                 /*
  194                  * Any other hook name is valid and can
  195                  * later be associated with a filter rule.
  196                  */
  197         }
  198         NG_HOOK_SET_PRIVATE(hook, NULL);
  199         return (0);
  200 }
  201 
  202 static int
  203 ng_vlan_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
  204     struct ng_mesg **rptr)
  205 {
  206         const priv_p priv = NG_NODE_PRIVATE(node);
  207         int error = 0;
  208         struct ng_mesg *resp = NULL;
  209         struct ng_vlan_filter *vf;
  210         struct filter *f;
  211         hook_p hook;
  212         struct ng_vlan_table *t;
  213         int i;
  214 
  215         /* Deal with message according to cookie and command. */
  216         switch (msg->header.typecookie) {
  217         case NGM_VLAN_COOKIE:
  218                 switch (msg->header.cmd) {
  219                 case NGM_VLAN_ADD_FILTER:
  220                         /* Check that message is long enough. */
  221                         if (msg->header.arglen != sizeof(*vf)) {
  222                                 error = EINVAL;
  223                                 break;
  224                         }
  225                         vf = (struct ng_vlan_filter *)msg->data;
  226                         /* Sanity check the VLAN ID value. */
  227 #ifndef EVL_VLID_MASK
  228 #define EVL_VLID_MASK   0x0FFF
  229 #endif
  230                         if (vf->vlan & ~EVL_VLID_MASK) {
  231                                 error = EINVAL;
  232                                 break;
  233                         }
  234                         /* Check that a referenced hook exists. */
  235                         hook = ng_findhook(node, vf->hook);
  236                         if (hook == NULL) {
  237                                 error = ENOENT;
  238                                 break;
  239                         }
  240                         /* And is not one of the special hooks. */
  241                         if (hook == priv->downstream_hook ||
  242                             hook == priv->nomatch_hook) {
  243                                 error = EINVAL;
  244                                 break;
  245                         }
  246                         /* And is not already in service. */
  247                         if (NG_HOOK_PRIVATE(hook) != NULL) {
  248                                 error = EEXIST;
  249                                 break;
  250                         }
  251                         /* Check we don't already trap this VLAN. */
  252                         if (ng_vlan_findentry(priv, vf->vlan)) {
  253                                 error = EEXIST;
  254                                 break;
  255                         }
  256                         /* Create filter. */
  257                         MALLOC(f, struct filter *, sizeof(*f),
  258                             M_NETGRAPH, M_NOWAIT | M_ZERO);
  259                         if (f == NULL) {
  260                                 error = ENOMEM;
  261                                 break;
  262                         }
  263                         /* Link filter and hook together. */
  264                         f->hook = hook;
  265                         f->vlan = vf->vlan;
  266                         NG_HOOK_SET_PRIVATE(hook, f);
  267                         /* Register filter in a hash table. */
  268                         LIST_INSERT_HEAD(
  269                             &priv->hashtable[HASH(f->vlan)], f, next);
  270                         priv->nent++;
  271                         break;
  272                 case NGM_VLAN_DEL_FILTER:
  273                         /* Check that message is long enough. */
  274                         if (msg->header.arglen != NG_HOOKLEN + 1) {
  275                                 error = EINVAL;
  276                                 break;
  277                         }
  278                         /* Check that hook exists and is active. */
  279                         hook = ng_findhook(node, (char *)msg->data);
  280                         if (hook == NULL ||
  281                             (f = NG_HOOK_PRIVATE(hook)) == NULL) {
  282                                 error = ENOENT;
  283                                 break;
  284                         }
  285                         /* Purge a rule that refers to this hook. */
  286                         NG_HOOK_SET_PRIVATE(hook, NULL);
  287                         LIST_REMOVE(f, next);
  288                         priv->nent--;
  289                         FREE(f, M_NETGRAPH);
  290                         break;
  291                 case NGM_VLAN_GET_TABLE:
  292                         NG_MKRESPONSE(resp, msg, sizeof(*t) +
  293                             priv->nent * sizeof(*t->filter), M_NOWAIT);
  294                         if (resp == NULL) {
  295                                 error = ENOMEM;
  296                                 break;
  297                         }
  298                         t = (struct ng_vlan_table *)resp->data;
  299                         t->n = priv->nent;
  300                         vf = &t->filter[0];
  301                         for (i = 0; i < HASHSIZE; i++) {
  302                                 LIST_FOREACH(f, &priv->hashtable[i], next) {
  303                                         vf->vlan = f->vlan;
  304                                         strncpy(vf->hook, NG_HOOK_NAME(f->hook),
  305                                             NG_HOOKLEN + 1);
  306                                         vf++;
  307                                 }
  308                         }
  309                         break;
  310                 default:                /* Unknown command. */
  311                         error = EINVAL;
  312                         break;
  313                 }
  314                 break;
  315         default:                        /* Unknown type cookie. */
  316                 error = EINVAL;
  317                 break;
  318         }
  319         NG_RESPOND_MSG(error, node, retaddr, resp, rptr);
  320         NG_FREE_MSG(msg);
  321         return (error);
  322 }
  323 
  324 static int
  325 ng_vlan_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
  326 {
  327         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  328         struct ether_header *eh;
  329         struct ether_vlan_header *evl;
  330         int error;
  331         u_int16_t vlan;
  332         struct filter *f;
  333 
  334         /* Make sure we have an entire header. */
  335         if (m->m_len < sizeof(*eh) &&
  336             (m = m_pullup(m, sizeof(*eh))) == NULL) {
  337                 NG_FREE_META(meta);
  338                 return (EINVAL);
  339         }
  340         eh = mtod(m, struct ether_header *);
  341         if (hook == priv->downstream_hook) {
  342                 /*
  343                  * If from downstream, select between a match hook
  344                  * or the nomatch hook.
  345                  */
  346                 if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
  347                         {
  348                                 if (m->m_len < sizeof(*evl) &&
  349                                     (m = m_pullup(m, sizeof(*evl))) == NULL) {
  350                                         NG_FREE_META(meta);
  351                                         return (EINVAL);
  352                                 }
  353                                 evl = mtod(m, struct ether_vlan_header *);
  354                                 vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag));
  355                         }
  356                         if ((f = ng_vlan_findentry(priv, vlan)) != NULL) {
  357                                 {
  358                                         evl->evl_encap_proto = evl->evl_proto;
  359                                         bcopy(mtod(m, caddr_t),
  360                                             mtod(m, caddr_t) +
  361                                             EVL_ENCAPLEN,
  362                                             ETHER_HDR_LEN);
  363                                         m_adj(m, EVL_ENCAPLEN);
  364                                 }
  365                         }
  366                 } else
  367                         f = NULL;
  368                 if (f != NULL)
  369                         NG_SEND_DATA(error, f->hook, m, meta);
  370                 else
  371                         NG_SEND_DATA(error, priv->nomatch_hook, m, meta);
  372         } else {
  373                 /*
  374                  * It is heading towards the downstream.
  375                  * If from nomatch, pass it unmodified.
  376                  * Otherwise, do the VLAN encapsulation.
  377                  */
  378                 if (hook != priv->nomatch_hook) {
  379                         if ((f = NG_HOOK_PRIVATE(hook)) == NULL) {
  380                                 NG_FREE_DATA(m, meta);
  381                                 return (EOPNOTSUPP);
  382                         }
  383                         M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
  384                         /* M_PREPEND takes care of m_len and m_pkthdr.len. */
  385                         if (m == NULL || (m->m_len < sizeof(*evl) &&
  386                             (m = m_pullup(m, sizeof(*evl))) == NULL)) {
  387                                 NG_FREE_META(meta);
  388                                 return (ENOMEM);
  389                         }
  390                         /*
  391                          * Transform the Ethernet header into an Ethernet header
  392                          * with 802.1Q encapsulation.
  393                          */
  394                         bcopy(mtod(m, char *) + EVL_ENCAPLEN,
  395                             mtod(m, char *), ETHER_HDR_LEN);
  396                         evl = mtod(m, struct ether_vlan_header *);
  397                         evl->evl_proto = evl->evl_encap_proto;
  398                         evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
  399                         evl->evl_tag = htons(f->vlan);
  400                 }
  401                 NG_SEND_DATA(error, priv->downstream_hook, m, meta);
  402         }
  403         return (error);
  404 }
  405 
  406 static int
  407 ng_vlan_shutdown(node_p node)
  408 {
  409         const priv_p priv = NG_NODE_PRIVATE(node);
  410 
  411         node->flags |= NG_INVALID;
  412         ng_cutlinks(node);
  413         ng_unname(node);
  414         NG_NODE_SET_PRIVATE(node, NULL);
  415         NG_NODE_UNREF(node);
  416         FREE(priv, M_NETGRAPH);
  417         return (0);
  418 }
  419 
  420 static int
  421 ng_vlan_disconnect(hook_p hook)
  422 {
  423         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  424         struct filter *f;
  425 
  426         if (hook == priv->downstream_hook)
  427                 priv->downstream_hook = NULL;
  428         else if (hook == priv->nomatch_hook)
  429                 priv->nomatch_hook = NULL;
  430         else {
  431                 /* Purge a rule that refers to this hook. */
  432                 if ((f = NG_HOOK_PRIVATE(hook)) != NULL) {
  433                         LIST_REMOVE(f, next);
  434                         priv->nent--;
  435                         FREE(f, M_NETGRAPH);
  436                 }
  437         }
  438         NG_HOOK_SET_PRIVATE(hook, NULL);
  439         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
  440             (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
  441                 ng_rmnode(NG_HOOK_NODE(hook));
  442         return (0);
  443 }

Cache object: db6ccef2062c4966484cc2071e6cf8e3


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