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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2003 IPNET Internet Communication Company
    3  * Copyright (c) 2011 - 2012 Rozhuk Ivan <rozhuk.im@gmail.com>
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * Author: Ruslan Ermilov <ru@FreeBSD.org>
   28  *
   29  * $FreeBSD: releng/10.2/sys/netgraph/ng_vlan.c 243882 2012-12-05 08:04:20Z glebius $
   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 <netgraph/ng_message.h>
   46 #include <netgraph/ng_parse.h>
   47 #include <netgraph/ng_vlan.h>
   48 #include <netgraph/netgraph.h>
   49 
   50 struct ng_vlan_private {
   51         hook_p          downstream_hook;
   52         hook_p          nomatch_hook;
   53         uint32_t        decap_enable;
   54         uint32_t        encap_enable;
   55         uint16_t        encap_proto;
   56         hook_p          vlan_hook[(EVL_VLID_MASK + 1)];
   57 };
   58 typedef struct ng_vlan_private *priv_p;
   59 
   60 #define ETHER_VLAN_HDR_LEN (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN)
   61 #define VLAN_TAG_MASK   0xFFFF
   62 #define HOOK_VLAN_TAG_SET_MASK ((uintptr_t)((~0) & ~(VLAN_TAG_MASK)))
   63 #define IS_HOOK_VLAN_SET(hdata) \
   64             ((((uintptr_t)hdata) & HOOK_VLAN_TAG_SET_MASK) == HOOK_VLAN_TAG_SET_MASK)
   65 
   66 static ng_constructor_t ng_vlan_constructor;
   67 static ng_rcvmsg_t      ng_vlan_rcvmsg;
   68 static ng_shutdown_t    ng_vlan_shutdown;
   69 static ng_newhook_t     ng_vlan_newhook;
   70 static ng_rcvdata_t     ng_vlan_rcvdata;
   71 static ng_disconnect_t  ng_vlan_disconnect;
   72 
   73 /* Parse type for struct ng_vlan_filter. */
   74 static const struct ng_parse_struct_field ng_vlan_filter_fields[] =
   75         NG_VLAN_FILTER_FIELDS;
   76 static const struct ng_parse_type ng_vlan_filter_type = {
   77         &ng_parse_struct_type,
   78         &ng_vlan_filter_fields
   79 };
   80 
   81 static int
   82 ng_vlan_getTableLength(const struct ng_parse_type *type,
   83     const u_char *start, const u_char *buf)
   84 {
   85         const struct ng_vlan_table *const table =
   86             (const struct ng_vlan_table *)(buf - sizeof(u_int32_t));
   87 
   88         return table->n;
   89 }
   90 
   91 /* Parse type for struct ng_vlan_table. */
   92 static const struct ng_parse_array_info ng_vlan_table_array_info = {
   93         &ng_vlan_filter_type,
   94         ng_vlan_getTableLength
   95 };
   96 static const struct ng_parse_type ng_vlan_table_array_type = {
   97         &ng_parse_array_type,
   98         &ng_vlan_table_array_info
   99 };
  100 static const struct ng_parse_struct_field ng_vlan_table_fields[] =
  101         NG_VLAN_TABLE_FIELDS;
  102 static const struct ng_parse_type ng_vlan_table_type = {
  103         &ng_parse_struct_type,
  104         &ng_vlan_table_fields
  105 };
  106 
  107 /* List of commands and how to convert arguments to/from ASCII. */
  108 static const struct ng_cmdlist ng_vlan_cmdlist[] = {
  109         {
  110           NGM_VLAN_COOKIE,
  111           NGM_VLAN_ADD_FILTER,
  112           "addfilter",
  113           &ng_vlan_filter_type,
  114           NULL
  115         },
  116         {
  117           NGM_VLAN_COOKIE,
  118           NGM_VLAN_DEL_FILTER,
  119           "delfilter",
  120           &ng_parse_hookbuf_type,
  121           NULL
  122         },
  123         {
  124           NGM_VLAN_COOKIE,
  125           NGM_VLAN_GET_TABLE,
  126           "gettable",
  127           NULL,
  128           &ng_vlan_table_type
  129         },
  130         {
  131           NGM_VLAN_COOKIE,
  132           NGM_VLAN_DEL_VID_FLT,
  133           "delvidflt",
  134           &ng_parse_uint16_type,
  135           NULL
  136         },
  137         {
  138           NGM_VLAN_COOKIE,
  139           NGM_VLAN_GET_DECAP,
  140           "getdecap",
  141           NULL,
  142           &ng_parse_hint32_type
  143         },
  144         {
  145           NGM_VLAN_COOKIE,
  146           NGM_VLAN_SET_DECAP,
  147           "setdecap",
  148           &ng_parse_hint32_type,
  149           NULL
  150         },
  151         {
  152           NGM_VLAN_COOKIE,
  153           NGM_VLAN_GET_ENCAP,
  154           "getencap",
  155           NULL,
  156           &ng_parse_hint32_type
  157         },
  158         {
  159           NGM_VLAN_COOKIE,
  160           NGM_VLAN_SET_ENCAP,
  161           "setencap",
  162           &ng_parse_hint32_type,
  163           NULL
  164         },
  165         {
  166           NGM_VLAN_COOKIE,
  167           NGM_VLAN_GET_ENCAP_PROTO,
  168           "getencapproto",
  169           NULL,
  170           &ng_parse_hint16_type
  171         },
  172         {
  173           NGM_VLAN_COOKIE,
  174           NGM_VLAN_SET_ENCAP_PROTO,
  175           "setencapproto",
  176           &ng_parse_hint16_type,
  177           NULL
  178         },
  179         { 0 }
  180 };
  181 
  182 static struct ng_type ng_vlan_typestruct = {
  183         .version =      NG_ABI_VERSION,
  184         .name =         NG_VLAN_NODE_TYPE,
  185         .constructor =  ng_vlan_constructor,
  186         .rcvmsg =       ng_vlan_rcvmsg,
  187         .shutdown =     ng_vlan_shutdown,
  188         .newhook =      ng_vlan_newhook,
  189         .rcvdata =      ng_vlan_rcvdata,
  190         .disconnect =   ng_vlan_disconnect,
  191         .cmdlist =      ng_vlan_cmdlist,
  192 };
  193 NETGRAPH_INIT(vlan, &ng_vlan_typestruct);
  194 
  195 
  196 /*
  197  * Helper functions.
  198  */
  199 
  200 static __inline int
  201 m_chk(struct mbuf **mp, int len)
  202 {
  203 
  204         if ((*mp)->m_pkthdr.len < len) {
  205                 m_freem((*mp));
  206                 (*mp) = NULL;
  207                 return (EINVAL);
  208         }
  209         if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL)
  210                 return (ENOBUFS);
  211 
  212         return (0);
  213 }
  214 
  215 
  216 /*
  217  * Netgraph node functions.
  218  */
  219 
  220 static int
  221 ng_vlan_constructor(node_p node)
  222 {
  223         priv_p priv;
  224 
  225         priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
  226         priv->decap_enable = 0;
  227         priv->encap_enable = VLAN_ENCAP_FROM_FILTER;
  228         priv->encap_proto = htons(ETHERTYPE_VLAN);
  229         NG_NODE_SET_PRIVATE(node, priv);
  230         return (0);
  231 }
  232 
  233 static int
  234 ng_vlan_newhook(node_p node, hook_p hook, const char *name)
  235 {
  236         const priv_p priv = NG_NODE_PRIVATE(node);
  237 
  238         if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0)
  239                 priv->downstream_hook = hook;
  240         else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0)
  241                 priv->nomatch_hook = hook;
  242         else {
  243                 /*
  244                  * Any other hook name is valid and can
  245                  * later be associated with a filter rule.
  246                  */
  247         }
  248         NG_HOOK_SET_PRIVATE(hook, NULL);
  249         return (0);
  250 }
  251 
  252 static int
  253 ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
  254 {
  255         const priv_p priv = NG_NODE_PRIVATE(node);
  256         struct ng_mesg *msg, *resp = NULL;
  257         struct ng_vlan_filter *vf;
  258         hook_p hook;
  259         struct ng_vlan_table *t;
  260         uintptr_t hook_data;
  261         int i, vlan_count;
  262         uint16_t vid;
  263         int error = 0;
  264 
  265         NGI_GET_MSG(item, msg);
  266         /* Deal with message according to cookie and command. */
  267         switch (msg->header.typecookie) {
  268         case NGM_VLAN_COOKIE:
  269                 switch (msg->header.cmd) {
  270                 case NGM_VLAN_ADD_FILTER:
  271                         /* Check that message is long enough. */
  272                         if (msg->header.arglen != sizeof(*vf)) {
  273                                 error = EINVAL;
  274                                 break;
  275                         }
  276                         vf = (struct ng_vlan_filter *)msg->data;
  277                         /* Sanity check the VLAN ID value. */
  278 #ifdef  NG_VLAN_USE_OLD_VLAN_NAME
  279                         if (vf->vid == 0 && vf->vid != vf->vlan) {
  280                                 vf->vid = vf->vlan;
  281                         } else if (vf->vid != 0 && vf->vlan != 0 &&
  282                             vf->vid != vf->vlan) {
  283                                 error = EINVAL;
  284                                 break;
  285                         }
  286 #endif
  287                         if (vf->vid & ~EVL_VLID_MASK ||
  288                             vf->pcp & ~7 ||
  289                             vf->cfi & ~1) {
  290                                 error = EINVAL;
  291                                 break;
  292                         }
  293                         /* Check that a referenced hook exists. */
  294                         hook = ng_findhook(node, vf->hook_name);
  295                         if (hook == NULL) {
  296                                 error = ENOENT;
  297                                 break;
  298                         }
  299                         /* And is not one of the special hooks. */
  300                         if (hook == priv->downstream_hook ||
  301                             hook == priv->nomatch_hook) {
  302                                 error = EINVAL;
  303                                 break;
  304                         }
  305                         /* And is not already in service. */
  306                         if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) {
  307                                 error = EEXIST;
  308                                 break;
  309                         }
  310                         /* Check we don't already trap this VLAN. */
  311                         if (priv->vlan_hook[vf->vid] != NULL) {
  312                                 error = EEXIST;
  313                                 break;
  314                         }
  315                         /* Link vlan and hook together. */
  316                         NG_HOOK_SET_PRIVATE(hook,
  317                             (void *)(HOOK_VLAN_TAG_SET_MASK |
  318                             EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi)));
  319                         priv->vlan_hook[vf->vid] = hook;
  320                         break;
  321                 case NGM_VLAN_DEL_FILTER:
  322                         /* Check that message is long enough. */
  323                         if (msg->header.arglen != NG_HOOKSIZ) {
  324                                 error = EINVAL;
  325                                 break;
  326                         }
  327                         /* Check that hook exists and is active. */
  328                         hook = ng_findhook(node, (char *)msg->data);
  329                         if (hook == NULL) {
  330                                 error = ENOENT;
  331                                 break;
  332                         }
  333                         hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
  334                         if (IS_HOOK_VLAN_SET(hook_data) == 0) {
  335                                 error = ENOENT;
  336                                 break;
  337                         }
  338 
  339                         KASSERT(priv->vlan_hook[EVL_VLANOFTAG(hook_data)] == hook,
  340                             ("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n",
  341                             __func__, (char *)msg->data));
  342 
  343                         /* Purge a rule that refers to this hook. */
  344                         priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
  345                         NG_HOOK_SET_PRIVATE(hook, NULL);
  346                         break;
  347                 case NGM_VLAN_DEL_VID_FLT:
  348                         /* Check that message is long enough. */
  349                         if (msg->header.arglen != sizeof(uint16_t)) {
  350                                 error = EINVAL;
  351                                 break;
  352                         }
  353                         vid = (*((uint16_t *)msg->data));
  354                         /* Sanity check the VLAN ID value. */
  355                         if (vid & ~EVL_VLID_MASK) {
  356                                 error = EINVAL;
  357                                 break;
  358                         }
  359                         /* Check that hook exists and is active. */
  360                         hook = priv->vlan_hook[vid];
  361                         if (hook == NULL) {
  362                                 error = ENOENT;
  363                                 break;
  364                         }
  365                         hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
  366                         if (IS_HOOK_VLAN_SET(hook_data) == 0) {
  367                                 error = ENOENT;
  368                                 break;
  369                         }
  370 
  371                         KASSERT(EVL_VLANOFTAG(hook_data) == vid,
  372                             ("%s: NGM_VLAN_DEL_VID_FLT:"
  373                             " Invalid VID Hook = %us, must be: %us\n",
  374                             __func__, (uint16_t )EVL_VLANOFTAG(hook_data),
  375                             vid));
  376 
  377                         /* Purge a rule that refers to this hook. */
  378                         priv->vlan_hook[vid] = NULL;
  379                         NG_HOOK_SET_PRIVATE(hook, NULL);
  380                         break;
  381                 case NGM_VLAN_GET_TABLE:
  382                         /* Calculate vlans. */
  383                         vlan_count = 0;
  384                         for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
  385                                 if (priv->vlan_hook[i] != NULL &&
  386                                     NG_HOOK_IS_VALID(priv->vlan_hook[i]))
  387                                         vlan_count ++;
  388                         }
  389 
  390                         /* Allocate memory for responce. */
  391                         NG_MKRESPONSE(resp, msg, sizeof(*t) +
  392                             vlan_count * sizeof(*t->filter), M_NOWAIT);
  393                         if (resp == NULL) {
  394                                 error = ENOMEM;
  395                                 break;
  396                         }
  397 
  398                         /* Pack data to responce. */
  399                         t = (struct ng_vlan_table *)resp->data;
  400                         t->n = 0;
  401                         vf = &t->filter[0];
  402                         for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
  403                                 hook = priv->vlan_hook[i];
  404                                 if (hook == NULL || NG_HOOK_NOT_VALID(hook))
  405                                         continue;
  406                                 hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
  407                                 if (IS_HOOK_VLAN_SET(hook_data) == 0)
  408                                         continue;
  409 
  410                                 KASSERT(EVL_VLANOFTAG(hook_data) == i,
  411                                     ("%s: NGM_VLAN_GET_TABLE:"
  412                                     " hook %s VID = %us, must be: %i\n",
  413                                     __func__, NG_HOOK_NAME(hook),
  414                                     (uint16_t)EVL_VLANOFTAG(hook_data), i));
  415 
  416 #ifdef  NG_VLAN_USE_OLD_VLAN_NAME
  417                                 vf->vlan = i;
  418 #endif
  419                                 vf->vid = i;
  420                                 vf->pcp = EVL_PRIOFTAG(hook_data);
  421                                 vf->cfi = EVL_CFIOFTAG(hook_data);
  422                                 strncpy(vf->hook_name,
  423                                     NG_HOOK_NAME(hook), NG_HOOKSIZ);
  424                                 vf ++;
  425                                 t->n ++;
  426                         }
  427                         break;
  428                 case NGM_VLAN_GET_DECAP:
  429                         NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
  430                         if (resp == NULL) {
  431                                 error = ENOMEM;
  432                                 break;
  433                         }
  434                         (*((uint32_t *)resp->data)) = priv->decap_enable;
  435                         break;
  436                 case NGM_VLAN_SET_DECAP:
  437                         if (msg->header.arglen != sizeof(uint32_t)) {
  438                                 error = EINVAL;
  439                                 break;
  440                         }
  441                         priv->decap_enable = (*((uint32_t *)msg->data));
  442                         break;
  443                 case NGM_VLAN_GET_ENCAP:
  444                         NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
  445                         if (resp == NULL) {
  446                                 error = ENOMEM;
  447                                 break;
  448                         }
  449                         (*((uint32_t *)resp->data)) = priv->encap_enable;
  450                         break;
  451                 case NGM_VLAN_SET_ENCAP:
  452                         if (msg->header.arglen != sizeof(uint32_t)) {
  453                                 error = EINVAL;
  454                                 break;
  455                         }
  456                         priv->encap_enable = (*((uint32_t *)msg->data));
  457                         break;
  458                 case NGM_VLAN_GET_ENCAP_PROTO:
  459                         NG_MKRESPONSE(resp, msg, sizeof(uint16_t), M_NOWAIT);
  460                         if (resp == NULL) {
  461                                 error = ENOMEM;
  462                                 break;
  463                         }
  464                         (*((uint16_t *)resp->data)) = ntohs(priv->encap_proto);
  465                         break;
  466                 case NGM_VLAN_SET_ENCAP_PROTO:
  467                         if (msg->header.arglen != sizeof(uint16_t)) {
  468                                 error = EINVAL;
  469                                 break;
  470                         }
  471                         priv->encap_proto = htons((*((uint16_t *)msg->data)));
  472                         break;
  473                 default: /* Unknown command. */
  474                         error = EINVAL;
  475                         break;
  476                 }
  477                 break;
  478         case NGM_FLOW_COOKIE:
  479             {
  480                 struct ng_mesg *copy;
  481 
  482                 /*
  483                  * Flow control messages should come only
  484                  * from downstream.
  485                  */
  486 
  487                 if (lasthook == NULL)
  488                         break;
  489                 if (lasthook != priv->downstream_hook)
  490                         break;
  491                 /* Broadcast the event to all uplinks. */
  492                 for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
  493                         if (priv->vlan_hook[i] == NULL)
  494                                 continue;
  495 
  496                         NG_COPYMESSAGE(copy, msg, M_NOWAIT);
  497                         if (copy == NULL)
  498                                 continue;
  499                         NG_SEND_MSG_HOOK(error, node, copy,
  500                             priv->vlan_hook[i], 0);
  501                 }
  502                 break;
  503             }
  504         default: /* Unknown type cookie. */
  505                 error = EINVAL;
  506                 break;
  507         }
  508         NG_RESPOND_MSG(error, node, item, resp);
  509         NG_FREE_MSG(msg);
  510         return (error);
  511 }
  512 
  513 static int
  514 ng_vlan_rcvdata(hook_p hook, item_p item)
  515 {
  516         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  517         struct ether_header *eh;
  518         struct ether_vlan_header *evl;
  519         int error;
  520         uintptr_t hook_data;
  521         uint16_t vid, eth_vtag;
  522         struct mbuf *m;
  523         hook_p dst_hook;
  524 
  525 
  526         NGI_GET_M(item, m);
  527 
  528         /* Make sure we have an entire header. */
  529         error = m_chk(&m, ETHER_HDR_LEN);
  530         if (error != 0)
  531                 goto mchk_err;
  532 
  533         eh = mtod(m, struct ether_header *);
  534         if (hook == priv->downstream_hook) {
  535                 /*
  536                  * If from downstream, select between a match hook
  537                  * or the nomatch hook.
  538                  */
  539 
  540                 dst_hook = priv->nomatch_hook;
  541 
  542                 /* Skip packets without tag. */
  543                 if ((m->m_flags & M_VLANTAG) == 0 &&
  544                     eh->ether_type != priv->encap_proto) {
  545                         if (dst_hook == NULL)
  546                                 goto net_down;
  547                         goto send_packet;
  548                 }
  549 
  550                 /* Process packets with tag. */
  551                 if (m->m_flags & M_VLANTAG) {
  552                         /*
  553                          * Packet is tagged, m contains a normal
  554                          * Ethernet frame; tag is stored out-of-band.
  555                          */
  556                         evl = NULL;
  557                         vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
  558                 } else { /* eh->ether_type == priv->encap_proto */
  559                         error = m_chk(&m, ETHER_VLAN_HDR_LEN);
  560                         if (error != 0)
  561                                 goto mchk_err;
  562                         evl = mtod(m, struct ether_vlan_header *);
  563                         vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
  564                 }
  565 
  566                 if (priv->vlan_hook[vid] != NULL) {
  567                         /*
  568                          * VLAN filter: allways remove vlan tags and
  569                          * decapsulate packet.
  570                          */
  571                         dst_hook = priv->vlan_hook[vid];
  572                         if (evl == NULL) { /* m->m_flags & M_VLANTAG */
  573                                 m->m_pkthdr.ether_vtag = 0;
  574                                 m->m_flags &= ~M_VLANTAG;
  575                                 goto send_packet;
  576                         }
  577                 } else { /* nomatch_hook */
  578                         if (dst_hook == NULL)
  579                                 goto net_down;
  580                         if (evl == NULL || priv->decap_enable == 0)
  581                                 goto send_packet;
  582                         /* Save tag out-of-band. */
  583                         m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
  584                         m->m_flags |= M_VLANTAG;
  585                 }
  586 
  587                 /*
  588                  * Decapsulate:
  589                  * TPID = ether type encap
  590                  * Move DstMAC and SrcMAC to ETHER_TYPE.
  591                  * Before:
  592                  *  [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload]
  593                  *  |-----------| >>>>>>>>>>>>>>>>>>>> |--------------------|
  594                  * After:
  595                  *  [free space ] [dmac] [smac] [ether_type] [payload]
  596                  *                |-----------| |--------------------|
  597                  */
  598                 bcopy((char *)evl, ((char *)evl + ETHER_VLAN_ENCAP_LEN),
  599                     (ETHER_ADDR_LEN * 2));
  600                 m_adj(m, ETHER_VLAN_ENCAP_LEN);
  601         } else {
  602                 /*
  603                  * It is heading towards the downstream.
  604                  * If from nomatch, pass it unmodified.
  605                  * Otherwise, do the VLAN encapsulation.
  606                  */
  607                 dst_hook = priv->downstream_hook;
  608                 if (dst_hook == NULL)
  609                         goto net_down;
  610                 if (hook != priv->nomatch_hook) {/* Filter hook. */
  611                         hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
  612                         if (IS_HOOK_VLAN_SET(hook_data) == 0) {
  613                                 /*
  614                                  * Packet from hook not in filter
  615                                  * call addfilter for this hook to fix.
  616                                  */
  617                                 error = EOPNOTSUPP;
  618                                 goto drop;
  619                         }
  620                         eth_vtag = (hook_data & VLAN_TAG_MASK);
  621                         if ((priv->encap_enable & VLAN_ENCAP_FROM_FILTER) == 0) {
  622                                 /* Just set packet header tag and send. */
  623                                 m->m_flags |= M_VLANTAG;
  624                                 m->m_pkthdr.ether_vtag = eth_vtag;
  625                                 goto send_packet;
  626                         }
  627                 } else { /* nomatch_hook */
  628                         if ((priv->encap_enable & VLAN_ENCAP_FROM_NOMATCH) == 0 ||
  629                             (m->m_flags & M_VLANTAG) == 0)
  630                                 goto send_packet;
  631                         /* Encapsulate tagged packet. */
  632                         eth_vtag = m->m_pkthdr.ether_vtag;
  633                         m->m_pkthdr.ether_vtag = 0;
  634                         m->m_flags &= ~M_VLANTAG;
  635                 }
  636 
  637                 /*
  638                  * Transform the Ethernet header into an Ethernet header
  639                  * with 802.1Q encapsulation.
  640                  * Mod of: ether_vlanencap.
  641                  *
  642                  * TPID = ether type encap
  643                  * Move DstMAC and SrcMAC from ETHER_TYPE.
  644                  * Before:
  645                  *  [free space ] [dmac] [smac] [ether_type] [payload]
  646                  *  <<<<<<<<<<<<< |-----------| |--------------------|
  647                  * After:
  648                  *  [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload]
  649                  *  |-----------| |-- inserted tag --| |--------------------|
  650                  */
  651                 M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
  652                 if (m == NULL)
  653                         error = ENOMEM;
  654                 else
  655                         error = m_chk(&m, ETHER_VLAN_HDR_LEN);
  656                 if (error != 0)
  657                         goto mchk_err;
  658 
  659                 evl = mtod(m, struct ether_vlan_header *);
  660                 bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN),
  661                     (char *)evl, (ETHER_ADDR_LEN * 2));
  662                 evl->evl_encap_proto = priv->encap_proto;
  663                 evl->evl_tag = htons(eth_vtag);
  664         }
  665 
  666 send_packet:
  667         NG_FWD_NEW_DATA(error, item, dst_hook, m);
  668         return (error);
  669 net_down:
  670         error = ENETDOWN;
  671 drop:
  672         m_freem(m);
  673 mchk_err:
  674         NG_FREE_ITEM(item);
  675         return (error);
  676 }
  677 
  678 static int
  679 ng_vlan_shutdown(node_p node)
  680 {
  681         const priv_p priv = NG_NODE_PRIVATE(node);
  682 
  683         NG_NODE_SET_PRIVATE(node, NULL);
  684         NG_NODE_UNREF(node);
  685         free(priv, M_NETGRAPH);
  686         return (0);
  687 }
  688 
  689 static int
  690 ng_vlan_disconnect(hook_p hook)
  691 {
  692         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  693         uintptr_t hook_data;
  694 
  695         if (hook == priv->downstream_hook)
  696                 priv->downstream_hook = NULL;
  697         else if (hook == priv->nomatch_hook)
  698                 priv->nomatch_hook = NULL;
  699         else {
  700                 /* Purge a rule that refers to this hook. */
  701                 hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
  702                 if (IS_HOOK_VLAN_SET(hook_data))
  703                         priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
  704         }
  705         NG_HOOK_SET_PRIVATE(hook, NULL);
  706         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
  707             (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
  708                 ng_rmnode_self(NG_HOOK_NODE(hook));
  709         return (0);
  710 }

Cache object: cd4995513ac5782ad24f6d5869e4539a


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