>Number:         161908
>Category:       kern
>Synopsis:       ng_vlan update for QinQ support
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Sat Oct 22 20:00:28 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     Ivan
>Release:        FreeBSD 8.2-STABLE
>Organization:
>Environment:
FreeBSD firewall 8.2-STABLE FreeBSD 8.2-STABLE #16: Sat Oct 22 07:46:34 IRKST 
2011     root@firewall:/usr/obj/usr/src/sys/RIMx64  amd64
>Description:
+ ethernet_type for VLAN encapsulation is tunable, default is: 0x8100 (33024)
+ PCP (Priority Code Point) and CFI (Canonical Format Indicator) for VLAN 
encapsulation is tunable per VID
+ VLAN filter can be deleted by VID
+ tunable encapsulation: on - do 802.1Q encapsulation, off - set M_VLANTAG and 
ether_vtag
* improved encapsulation/decapsulation code
* "vlan" changed to "vid" in "addfilter" and "gettable" messages
* many other canges


kldunload ng_vlan
kldload ng_ether

ngctl msg re0: setpromisc   1
ngctl msg re0: setautosrc   0

ngctl mkpeer re0: vlan lower downstream
ngctl connect re0: re0:lower upper nomatch

ngctl mkpeer re0:lower eiface vlan1001 ether
ngctl mkpeer re0:lower eiface vlan1002 ether

ngctl name re0:lower:vlan1001 ngeth0
ifconfig ngeth0 link 00:1a:4d:55:9a:43
ifconfig ngeth0 inet 192.168.0.36 netmask 255.255.255.0

ngctl name re0:lower:vlan1002 ngeth1
ifconfig ngeth1 link 00:1a:4d:55:9a:44
ifconfig ngeth1 inet 192.168.254.36 netmask 255.255.255.0

ngctl msg re0:lower addfilter '{ vid=1001 hook="vlan1001" }'
ngctl msg re0:lower addfilter '{ vid=1002 pcp=6 cfi=1 hook="vlan1002" }'



# ngctl msg re0:lower gettable
Rec'd response "gettable" (4) from "[156]:":
Args:   { n=2 filter=[ { hook="vlan1001" vid=1001 } { hook="vlan1002" vid=1002 
pcp=6 cfi=1 } ] }

# ngctl msg re0:lower getencap
Rec'd response "getencap" (5) from "[156]:":
Args:   1

# ngctl msg re0:lower getencapproto
Rec'd response "getencapproto" (7) from "[156]:":
Args:   33024

# ngctl msg re0:lower delvidflt 1001

# ngctl msg re0:lower gettable
Rec'd response "gettable" (4) from "[156]:":
Args:   { n=1 filter=[ { hook="vlan1002" vid=1002 pcp=6 cfi=1 } ] }

# ngctl msg re0:lower delfilter '"vlan1002"'

# ngctl msg re0:lower gettable
Rec'd response "gettable" (4) from "[156]:":
Args:   {}



NOTE:
1.1 Use "setencap" = 0 with care: node connected to "downstream" must handle 
M_VLANTAG + ether_vtag. If it ng_ether, then IFCAP_VLAN_HWTAGGING must be 
enabled on attached network adapter.

1.2 Then "setencap" = 0, "setencapproto" value is ignored and assumed that 
0x8100 (M_VLANTAG + ether_vtag - allways encapsulated with tag 0x8100)


2. "addfilter" syntax changed!!!
was:
ngctl msg re0:lower addfilter '{ vlan=1001 hook="vlan1001" }'

now:
ngctl msg re0:lower addfilter '{ vid=1001 hook="vlan1001" }'
ngctl msg re0:lower addfilter '{ vid=1001 hook="vlan1001" pcp=0 cfi=0 }'
(equivalent)


3. Trick:
kern.ipc.max_linkhdr should be increased via sysctl for best perfomance:
20 - 1 VLAN tag (.Q)
24 - 2 VLAN tags (QinQ)
28 - 3 VLAN tags (QinQinQ)
32 - 4 VLAN tags (...)

>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- /usr/src/sys/netgraph/ng_vlan.c.orig        2009-08-03 17:13:06.000000000 
+0900
+++ /usr/src/sys/netgraph/ng_vlan.c     2011-10-23 03:41:00.000000000 +0900
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003 IPNET Internet Communication Company
+ * Copyright (c) 2011 Rozhuk Ivan <[email protected]>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -46,6 +47,22 @@
 #include <netgraph/ng_vlan.h>
 #include <netgraph/netgraph.h>
 
+
+
+struct ng_vlan_private {
+       hook_p          downstream_hook;
+       hook_p          nomatch_hook;
+       int             encap_enable;
+       u_int16_t       encap_proto;
+       hook_p          vlan_hook[(EVL_VLID_MASK + 1)];
+};
+typedef struct ng_vlan_private *priv_p;
+
+#define VLAN_TAG_MASK  0xFFFF
+#define HOOK_VLAN_TAG_SET_MASK ((uintptr_t)((~0) & ~(VLAN_TAG_MASK)))
+#define IS_HOOK_VLAN_SET(hook_data) ((((uintptr_t)hook_data) & 
HOOK_VLAN_TAG_SET_MASK) == HOOK_VLAN_TAG_SET_MASK)
+
+
 static ng_constructor_t        ng_vlan_constructor;
 static ng_rcvmsg_t     ng_vlan_rcvmsg;
 static ng_shutdown_t   ng_vlan_shutdown;
@@ -105,11 +122,46 @@
        },
        {
          NGM_VLAN_COOKIE,
+         NGM_VLAN_DEL_VID_FLT,
+         "delvidflt",
+         &ng_parse_int16_type,
+         NULL
+       },
+       {
+         NGM_VLAN_COOKIE,
          NGM_VLAN_GET_TABLE,
          "gettable",
          NULL,
          &ng_vlan_table_type
        },
+       {
+         NGM_VLAN_COOKIE,
+         NGM_VLAN_GET_ENCAP,
+         "getencap",
+         NULL,
+         &ng_parse_int32_type
+       },
+       {
+         NGM_VLAN_COOKIE,
+         NGM_VLAN_SET_ENCAP,
+         "setencap",
+         &ng_parse_int32_type,
+         NULL
+       },
+       {
+         NGM_VLAN_COOKIE,
+         NGM_VLAN_GET_ENCAP_PROTO,
+         "getencapproto",
+         NULL,
+         &ng_parse_int32_type
+       },
+       {
+         NGM_VLAN_COOKIE,
+         NGM_VLAN_SET_ENCAP_PROTO,
+         "setencapproto",
+         &ng_parse_int32_type,
+         NULL
+       },
        { 0 }
 };
 
@@ -126,47 +178,43 @@
 };
 NETGRAPH_INIT(vlan, &ng_vlan_typestruct);
 
-struct filter {
-       LIST_ENTRY(filter) next;
-       u_int16_t       vlan;
-       hook_p          hook;
-};
 
-#define        HASHSIZE        16
-#define        HASH(id)        ((((id) >> 8) ^ ((id) >> 4) ^ (id)) & 0x0f)
-LIST_HEAD(filterhead, filter);
 
-typedef struct {
-       hook_p          downstream_hook;
-       hook_p          nomatch_hook;
-       struct filterhead hashtable[HASHSIZE];
-       u_int32_t       nent;
-} *priv_p;
+//************************************************************************
+//                     HELPER STUFF
+//************************************************************************
 
-static struct filter *
-ng_vlan_findentry(priv_p priv, u_int16_t vlan)
+static __inline int
+m_chk(struct mbuf **mp, int len)
 {
-       struct filterhead *chain = &priv->hashtable[HASH(vlan)];
-       struct filter *f;
+       if ((*mp)->m_pkthdr.len < len) {
+               m_freem((*mp));
+               (*mp) = NULL;
+               return (EINVAL);
+       }
+       if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL)
+               return (ENOBUFS);
 
-       LIST_FOREACH(f, chain, next)
-               if (f->vlan == vlan)
-                       return (f);
-       return (NULL);
+return (0);
 }
 
+//************************************************************************
+//                     NETGRAPH NODE STUFF
+//************************************************************************
+
 static int
 ng_vlan_constructor(node_p node)
 {
        priv_p priv;
-       int i;
 
        priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
        if (priv == NULL)
                return (ENOMEM);
-       for (i = 0; i < HASHSIZE; i++)
-               LIST_INIT(&priv->hashtable[i]);
+       priv->encap_enable = 1;
+       priv->encap_proto = htons(ETHERTYPE_VLAN);
+       
        NG_NODE_SET_PRIVATE(node, priv);
+
        return (0);
 }
 
@@ -193,13 +241,14 @@
 ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
 {
        const priv_p priv = NG_NODE_PRIVATE(node);
-       int error = 0;
        struct ng_mesg *msg, *resp = NULL;
        struct ng_vlan_filter *vf;
-       struct filter *f;
        hook_p hook;
        struct ng_vlan_table *t;
-       int i;
+       uintptr_t hook_data;
+       int i, vlan_count;
+       u_int16_t vid;
+       int error = 0;
 
        NGI_GET_MSG(item, msg);
        /* Deal with message according to cookie and command. */
@@ -214,12 +263,12 @@
                        }
                        vf = (struct ng_vlan_filter *)msg->data;
                        /* Sanity check the VLAN ID value. */
-                       if (vf->vlan & ~EVL_VLID_MASK) {
+                       if (vf->vid & ~EVL_VLID_MASK || vf->pcp & ~7 || vf->cfi 
& ~1) {
                                error = EINVAL;
                                break;
                        }
                        /* Check that a referenced hook exists. */
-                       hook = ng_findhook(node, vf->hook);
+                       hook = ng_findhook(node, vf->hook_name);
                        if (hook == NULL) {
                                error = ENOENT;
                                break;
@@ -231,30 +280,18 @@
                                break;
                        }
                        /* And is not already in service. */
-                       if (NG_HOOK_PRIVATE(hook) != NULL) {
+                       if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) {
                                error = EEXIST;
                                break;
                        }
                        /* Check we don't already trap this VLAN. */
-                       if (ng_vlan_findentry(priv, vf->vlan)) {
+                       if (priv->vlan_hook[vf->vid] != NULL) {
                                error = EEXIST;
                                break;
                        }
-                       /* Create filter. */
-                       f = malloc(sizeof(*f),
-                           M_NETGRAPH, M_NOWAIT | M_ZERO);
-                       if (f == NULL) {
-                               error = ENOMEM;
-                               break;
-                       }
-                       /* Link filter and hook together. */
-                       f->hook = hook;
-                       f->vlan = vf->vlan;
-                       NG_HOOK_SET_PRIVATE(hook, f);
-                       /* Register filter in a hash table. */
-                       LIST_INSERT_HEAD(
-                           &priv->hashtable[HASH(f->vlan)], f, next);
-                       priv->nent++;
+                       /* Link vlan and hook together. */
+                       NG_HOOK_SET_PRIVATE(hook, (void 
*)(HOOK_VLAN_TAG_SET_MASK | EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi)));
+                       priv->vlan_hook[vf->vid] = hook;
                        break;
                case NGM_VLAN_DEL_FILTER:
                        /* Check that message is long enough. */
@@ -264,35 +301,125 @@
                        }
                        /* Check that hook exists and is active. */
                        hook = ng_findhook(node, (char *)msg->data);
-                       if (hook == NULL ||
-                           (f = NG_HOOK_PRIVATE(hook)) == NULL) {
+                       if (hook == NULL) {
+                               error = ENOENT;
+                               break;
+                       }
+                       hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+                       if (IS_HOOK_VLAN_SET(hook_data) == 0) {
+                               error = ENOENT;
+                               break;
+                       }
+#ifdef NETGRAPH_DEBUG
+                       if (priv->vlan_hook[EVL_VLANOFTAG(hook_data)] != hook)
+                               printf("%s: NGM_VLAN_DEL_FILTER: Invalid VID 
for Hook = %s\n", __func__, (char *)msg->data);
+#endif
+                       /* Purge a rule that refers to this hook. */
+                       priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
+                       NG_HOOK_SET_PRIVATE(hook, NULL);
+                       break;
+               case NGM_VLAN_DEL_VID_FLT:
+                       /* Check that message is long enough. */
+                       if (msg->header.arglen != sizeof(u_int16_t)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       vid = (*((u_int16_t *)msg->data));
+                       /* Sanity check the VLAN ID value. */
+                       if (vid & ~EVL_VLID_MASK) {
+                               error = EINVAL;
+                               break;
+                       }
+                       /* Check that hook exists and is active. */
+                       hook = priv->vlan_hook[vid];
+                       if (hook == NULL) {
+                               error = ENOENT;
+                               break;
+                       }
+                       hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+                       if (IS_HOOK_VLAN_SET(hook_data) == 0) {
                                error = ENOENT;
                                break;
                        }
+#ifdef NETGRAPH_DEBUG
+                       if (EVL_VLANOFTAG(hook_data) != vid)
+                               printf("%s: NGM_VLAN_DEL_VID_FLT: Invalid VID 
Hook = %us, must be: %us\n", __func__, (u_int16_t)EVL_VLANOFTAG(hook_data), 
vid);
+#endif
                        /* Purge a rule that refers to this hook. */
+                       priv->vlan_hook[vid] = NULL;
                        NG_HOOK_SET_PRIVATE(hook, NULL);
-                       LIST_REMOVE(f, next);
-                       priv->nent--;
-                       free(f, M_NETGRAPH);
                        break;
                case NGM_VLAN_GET_TABLE:
+                       /* calculate vlans */
+                       vlan_count = 0;
+                       for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
+                               if (priv->vlan_hook[i] != NULL
+                                  && NG_HOOK_IS_VALID(priv->vlan_hook[i]))
+                                       vlan_count ++;
+                       }
+
+                       /* allocate memory for responce */
                        NG_MKRESPONSE(resp, msg, sizeof(*t) +
-                           priv->nent * sizeof(*t->filter), M_NOWAIT);
+                           vlan_count * sizeof(*t->filter), M_NOWAIT);
                        if (resp == NULL) {
                                error = ENOMEM;
                                break;
                        }
+
+                       /* pack data to responce */
                        t = (struct ng_vlan_table *)resp->data;
-                       t->n = priv->nent;
+                       t->n = 0;
                        vf = &t->filter[0];
-                       for (i = 0; i < HASHSIZE; i++) {
-                               LIST_FOREACH(f, &priv->hashtable[i], next) {
-                                       vf->vlan = f->vlan;
-                                       strncpy(vf->hook, NG_HOOK_NAME(f->hook),
+                       for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
+                               hook = priv->vlan_hook[i];
+                               if (hook == NULL
+                                  || NG_HOOK_NOT_VALID(hook))
+                                       continue;
+                               hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+                               if (IS_HOOK_VLAN_SET(hook_data) == 0)
+                                       continue;
+#ifdef NETGRAPH_DEBUG
+                               if (EVL_VLANOFTAG(hook_data) != i)
+                                       printf("%s: NGM_VLAN_GET_TABLE: hook %s 
VID = %us, must be: %i\n", __func__, NG_HOOK_NAME(hook), 
(u_int16_t)EVL_VLANOFTAG(hook_data), i);
+#endif
+                               vf->vid = i;
+                               vf->pcp = EVL_PRIOFTAG(hook_data);
+                               vf->cfi = EVL_CFIOFTAG(hook_data);
+                               strncpy(vf->hook_name, NG_HOOK_NAME(hook),
                                            NG_HOOKSIZ);
-                                       vf++;
-                               }
+                               vf ++;
+                               t->n ++;
+                       }
+                       break;
+               case NGM_VLAN_GET_ENCAP:
+                       NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       (*((u_int32_t *)resp->data)) = priv->encap_enable;
+                       break;
+               case NGM_VLAN_SET_ENCAP:
+                       if (msg->header.arglen != sizeof(u_int32_t)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       priv->encap_enable = ((*((u_int32_t *)msg->data)) != 0);
+                       break;
+               case NGM_VLAN_GET_ENCAP_PROTO:
+                       NG_MKRESPONSE(resp, msg, sizeof(u_int16_t), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       (*((u_int16_t *)resp->data)) = ntohs(priv->encap_proto);
+                       break;
+               case NGM_VLAN_SET_ENCAP_PROTO:
+                       if (msg->header.arglen != sizeof(u_int16_t)) {
+                               error = EINVAL;
+                               break;
                        }
+                       priv->encap_proto = htons((*((u_int16_t *)msg->data)));
                        break;
                default:                /* Unknown command. */
                        error = EINVAL;
@@ -302,8 +429,6 @@
        case NGM_FLOW_COOKIE:
            {
                struct ng_mesg *copy;
-               struct filterhead *chain;
-               struct filter *f;
 
                /*
                 * Flow control messages should come only
@@ -314,17 +439,16 @@
                        break;
                if (lasthook != priv->downstream_hook)
                        break;
-
                /* Broadcast the event to all uplinks. */
-               for (i = 0, chain = priv->hashtable; i < HASHSIZE;
-                   i++, chain++)
-               LIST_FOREACH(f, chain, next) {
+               for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
+                       if (priv->vlan_hook[i] == NULL)
+                               continue;
+
                        NG_COPYMESSAGE(copy, msg, M_NOWAIT);
                        if (copy == NULL)
-                               continue;
-                       NG_SEND_MSG_HOOK(error, node, copy, f->hook, 0);
+                                       continue;
+                       NG_SEND_MSG_HOOK(error, node, copy, priv->vlan_hook[i], 
0);
                }
-
                break;
            }
        default:                        /* Unknown type cookie. */
@@ -343,16 +467,19 @@
        struct ether_header *eh;
        struct ether_vlan_header *evl = NULL;
        int error;
-       u_int16_t vlan;
+       uintptr_t hook_data;
+       u_int16_t vid;
        struct mbuf *m;
-       struct filter *f;
+       hook_p dst_hook;
+
 
-       /* Make sure we have an entire header. */
        NGI_GET_M(item, m);
-       if (m->m_len < sizeof(*eh) &&
-           (m = m_pullup(m, sizeof(*eh))) == NULL) {
+
+       /* Make sure we have an entire header. */
+       error = m_chk(&m, ETHER_HDR_LEN);
+       if (error != 0) {
                NG_FREE_ITEM(item);
-               return (EINVAL);
+               return (error);
        }
        eh = mtod(m, struct ether_header *);
        if (hook == priv->downstream_hook) {
@@ -360,75 +487,104 @@
                 * If from downstream, select between a match hook
                 * or the nomatch hook.
                 */
+               dst_hook = priv->nomatch_hook;
                if (m->m_flags & M_VLANTAG ||
-                   eh->ether_type == htons(ETHERTYPE_VLAN)) {
+                   eh->ether_type == priv->encap_proto) {
                        if (m->m_flags & M_VLANTAG) {
                                /*
                                 * Packet is tagged, m contains a normal
                                 * Ethernet frame; tag is stored out-of-band.
                                 */
-                               vlan = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
-                       } else {
-                               if (m->m_len < sizeof(*evl) &&
-                                   (m = m_pullup(m, sizeof(*evl))) == NULL) {
+                               vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
+                       } else { /* eh->ether_type == priv->encap_proto */
+                               error = m_chk(&m, (ETHER_HDR_LEN + 
ETHER_VLAN_ENCAP_LEN));
+                               if (error != 0) {
                                        NG_FREE_ITEM(item);
-                                       return (EINVAL);
+                                       return (error);
                                }
                                evl = mtod(m, struct ether_vlan_header *);
-                               vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+                               vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
                        }
-                       if ((f = ng_vlan_findentry(priv, vlan)) != NULL) {
+
+                       if (priv->vlan_hook[vid] != NULL) {
+                               dst_hook = priv->vlan_hook[vid];
                                if (m->m_flags & M_VLANTAG) {
                                        m->m_pkthdr.ether_vtag = 0;
                                        m->m_flags &= ~M_VLANTAG;
                                } else {
-                                       evl->evl_encap_proto = evl->evl_proto;
-                                       bcopy(mtod(m, caddr_t),
-                                           mtod(m, caddr_t) +
-                                           ETHER_VLAN_ENCAP_LEN,
-                                           ETHER_HDR_LEN);
+                                       /* 
+                                        * move DstMAC and SrcMAC to ETHER_TYPE
+                                        * before: [dst_mac] [src_mac] 
[ether_type_encap(TPID)] [PCP/CFI/VID] [ether_type] [payload]
+                                        *         |-----------------| 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |---------------------|
+                                        * after: [free space] [dst_mac] 
[src_mac] [ether_type] [payload]
+                                        *                     
|-----------------| |---------------------|
+                                        */
+                                       bcopy((char *)evl, ((char *)evl + 
ETHER_VLAN_ENCAP_LEN),
+                                               (ETHER_ADDR_LEN * 2));
                                        m_adj(m, ETHER_VLAN_ENCAP_LEN);
                                }
                        }
-               } else
-                       f = NULL;
-               if (f != NULL)
-                       NG_FWD_NEW_DATA(error, item, f->hook, m);
-               else
-                       NG_FWD_NEW_DATA(error, item, priv->nomatch_hook, m);
+               }
        } else {
                /*
                 * It is heading towards the downstream.
                 * If from nomatch, pass it unmodified.
                 * Otherwise, do the VLAN encapsulation.
                 */
-               if (hook != priv->nomatch_hook) {
-                       if ((f = NG_HOOK_PRIVATE(hook)) == NULL) {
+               dst_hook = priv->downstream_hook;
+               if (dst_hook != NULL && hook != priv->nomatch_hook) {
+                       hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+                       if (IS_HOOK_VLAN_SET(hook_data) == 0) {
+                               m_freem(m);
                                NG_FREE_ITEM(item);
-                               NG_FREE_M(m);
                                return (EOPNOTSUPP);
                        }
-                       M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
-                       /* M_PREPEND takes care of m_len and m_pkthdr.len. */
-                       if (m == NULL || (m->m_len < sizeof(*evl) &&
-                           (m = m_pullup(m, sizeof(*evl))) == NULL)) {
-                               NG_FREE_ITEM(item);
-                               return (ENOMEM);
+                       if (priv->encap_enable == 0) {
+                               /* just set packet header tag */
+                               m->m_flags |= M_VLANTAG;
+                               m->m_pkthdr.ether_vtag = (hook_data & 
VLAN_TAG_MASK);
+                       } else {
+                               /*
+                                * Transform the Ethernet header into an 
Ethernet header
+                                * with 802.1Q encapsulation.
+                                * mod of: ether_vlanencap 
+                                */
+                               M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
+                               /* M_PREPEND takes care of m_len and 
m_pkthdr.len. */
+                               if (m == NULL)
+                                       error = ENOMEM;
+                               else
+                                       error = m_chk(&m, (ETHER_HDR_LEN + 
ETHER_VLAN_ENCAP_LEN));
+                               if (error != 0) {
+                                       NG_FREE_ITEM(item);
+                                       return (error);
+                               }
+                               /* move DstMAC and SrcMAC from ETHER_TYPE
+                                * before: [free - prepended space] [dst_mac] 
[src_mac] [ether_type] [payload]
+                                *          <<<<<<<<<<<<<<<<<<<<<<  
|-----------------| |--------------------|
+                                * after: [dst_mac] [src_mac] 
[ether_type_encap(TPID)] [PCP/CFI/VID] [ether_type] [payload]
+                                *        |-----------------| |----------- 
inserted tag -----------| |--------------------| 
+                                */
+                               evl = mtod(m, struct ether_vlan_header *);
+                               bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN),
+                                       (char *)evl, (ETHER_ADDR_LEN * 2));
+                               evl->evl_encap_proto = priv->encap_proto;
+                               evl->evl_tag = htons((hook_data & 
VLAN_TAG_MASK));
                        }
-                       /*
-                        * Transform the Ethernet header into an Ethernet header
-                        * with 802.1Q encapsulation.
-                        */
-                       bcopy(mtod(m, char *) + ETHER_VLAN_ENCAP_LEN,
-                           mtod(m, char *), ETHER_HDR_LEN);
-                       evl = mtod(m, struct ether_vlan_header *);
-                       evl->evl_proto = evl->evl_encap_proto;
-                       evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-                       evl->evl_tag = htons(f->vlan);
                }
-               NG_FWD_NEW_DATA(error, item, priv->downstream_hook, m);
        }
-       return (error);
+
+       /* send packet */
+       if (dst_hook != NULL) {
+               NG_FWD_NEW_DATA(error, item, dst_hook, m);
+               return (error);
+       }
+
+       /* no hook to send */
+       m_freem(m);
+       NG_FREE_ITEM(item);
+
+       return (ENETDOWN);
 }
 
 static int
@@ -446,7 +602,7 @@
 ng_vlan_disconnect(hook_p hook)
 {
        const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
-       struct filter *f;
+       uintptr_t hook_data;
 
        if (hook == priv->downstream_hook)
                priv->downstream_hook = NULL;
@@ -454,11 +610,9 @@
                priv->nomatch_hook = NULL;
        else {
                /* Purge a rule that refers to this hook. */
-               if ((f = NG_HOOK_PRIVATE(hook)) != NULL) {
-                       LIST_REMOVE(f, next);
-                       priv->nent--;
-                       free(f, M_NETGRAPH);
-               }
+               hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+               if (IS_HOOK_VLAN_SET(hook_data) == 0)
+                       priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
        }
        NG_HOOK_SET_PRIVATE(hook, NULL);
        if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&


--- /usr/src/sys/netgraph/ng_vlan.h.orig        2009-08-03 17:13:06.000000000 
+0900
+++ /usr/src/sys/netgraph/ng_vlan.h     2011-10-22 19:11:01.000000000 +0900
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003 IPNET Internet Communication Company
+ * Copyright (c) 2011 Rozhuk Ivan <[email protected]>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -43,19 +44,28 @@
 enum {
        NGM_VLAN_ADD_FILTER = 1,
        NGM_VLAN_DEL_FILTER,
-       NGM_VLAN_GET_TABLE
+       NGM_VLAN_DEL_VID_FLT,
+       NGM_VLAN_GET_TABLE,
+       NGM_VLAN_GET_ENCAP,
+       NGM_VLAN_SET_ENCAP,
+       NGM_VLAN_GET_ENCAP_PROTO,
+       NGM_VLAN_SET_ENCAP_PROTO,
 };
 
 /* For NGM_VLAN_ADD_FILTER control message. */
 struct ng_vlan_filter {
-       char            hook[NG_HOOKSIZ];
-       u_int16_t       vlan;
-};     
+       char            hook_name[NG_HOOKSIZ];
+       u_int16_t       vid;    /* VID - VLAN Identifier */
+       u_int8_t        pcp;    /* PCP - Priority Code Point */
+       u_int8_t        cfi;    /* CFI - Canonical Format Indicator */
+};
 
 /* Keep this in sync with the above structure definition.  */
 #define        NG_VLAN_FILTER_FIELDS   {                               \
-       { "hook",       &ng_parse_hookbuf_type  },              \
-       { "vlan",       &ng_parse_uint16_type   },              \
+       { "hook",       &ng_parse_hookbuf_type  },              \
+       { "vid",        &ng_parse_uint16_type   },              \
+       { "pcp",        &ng_parse_uint8_type    },              \
+       { "cfi",        &ng_parse_uint8_type    },              \
        { NULL }                                                \
 }
 


>Release-Note:
>Audit-Trail:
>Unformatted:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"

Reply via email to