The diff inlined in this mail fixes a regression introduced by a commit
that removed the VLAN tagging from vlan_start.

Since ether_addheader is only called once now (by the first and the inner
most VLAN) we need to add tags there. Doing all the tagging in
ether_addheader avoids all the ether_header popping and pushing that we
used to do in vlan_start so we also gain a small performance improvement.

I've added a new function called vlan_addheader that will do all VLAN
tagging plus it will prepend the ethernet header: it will look all parent
interfaces pointed by the VLAN softc (ifv_p) to decide how many VLANs will
be prepended, then it will alocate the necessary memory and do all the
VLAN tagging. (if there is too many VLANs in the stack, M_PREPEND will return
NULL and ether_addheader will fail)

To decide whether the parent interface is a VLAN or not I've used the if_output
function to determine the interface type.

Index: net/if_ethersubr.c
===================================================================
RCS file: /cvs/src/sys/net/if_ethersubr.c,v
retrieving revision 1.177
diff -u -p -r1.177 if_ethersubr.c
--- net/if_ethersubr.c  6 Nov 2014 14:28:47 -0000       1.177
+++ net/if_ethersubr.c  18 Nov 2014 13:11:30 -0000
@@ -206,18 +206,10 @@ ether_addheader(struct mbuf **m, struct 
                            ((*m)->m_pkthdr.pf.prio << EVL_PRIO_BITS);
                        /* don't return, need to add regular ethernet header */
                } else {
-                       struct ether_vlan_header        *evh;
-
-                       M_PREPEND(*m, sizeof(*evh), M_DONTWAIT);
-                       if (*m == NULL)
+                       *m = vlan_addheader(*m, ifp, etype, esrc, edst);
+                       if (m == NULL)
                                return (-1);
-                       evh = mtod(*m, struct ether_vlan_header *);
-                       memcpy(evh->evl_dhost, edst, sizeof(evh->evl_dhost));
-                       memcpy(evh->evl_shost, esrc, sizeof(evh->evl_shost));
-                       evh->evl_proto = etype;
-                       evh->evl_encap_proto = htons(ifv->ifv_type);
-                       evh->evl_tag = htons(ifv->ifv_tag +
-                           ((*m)->m_pkthdr.pf.prio << EVL_PRIO_BITS));
+
                        (*m)->m_flags &= ~M_VLANTAG;
                        return (0);
                }
Index: net/if_vlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vlan.c,v
retrieving revision 1.109
diff -u -p -r1.109 if_vlan.c
--- net/if_vlan.c       7 Oct 2014 11:16:23 -0000       1.109
+++ net/if_vlan.c       18 Nov 2014 13:11:30 -0000
@@ -788,3 +788,69 @@ vlan_ether_resetmulti(struct ifvlan *ifv
                (void)(*p->if_ioctl)(p, SIOCADDMULTI, (caddr_t)ifr);
        }
 }
+
+/*
+ * This procedures alocates the space required for ethernet header +
+ * all VLANs, it also fill the information of all inner VLANs and the
+ * packet ethernet header.
+ *
+ * When successful this function returns the new mbuf else NULL.
+ */
+struct mbuf *
+vlan_addheader(struct mbuf *m, const struct ifnet *ifp0, uint16_t etype,
+    u_char *esrc, u_char *edst)
+{
+       struct  ifvlan *ifv = ifp0->if_softc;
+       struct  ether_vlan_header *evh;
+       struct  ifnet *ifp;
+       int     nvlan = 0, off;
+       struct vlan_shim {
+               uint16_t        vs_proto;
+               uint16_t        vs_tag;
+       } vs;
+
+#define ISVLAN(ifp)                            \
+       ((ifp)->if_output == vlan_output)
+
+       /* Find out how many VLANs we are going to prepend */
+       while ((ifp = ifv->ifv_p) != NULL && ISVLAN(ifp)) {
+               ifv = ifp->if_softc;
+               nvlan++;
+       }
+#undef ISVLAN
+
+       /* Prepend VLANs + ethernet */
+       M_PREPEND(m, (nvlan * sizeof(vs)) + sizeof(*evh), M_DONTWAIT);
+       if (m == NULL)
+               return (NULL);
+
+       /*
+        * Calculate the offset of packet ethertype and copy:
+        * First VLAN + (VLAN header size * VLAN count) - ethertype size
+        */
+       off = sizeof(*evh) + (nvlan * sizeof(vs)) - sizeof(etype);
+       m_copyback(m, off, sizeof(etype), &etype, M_NOWAIT);
+
+       /* Fill inner VLAN information if any */
+       ifv = ifp0->if_softc;
+       while (nvlan-- > 0) {
+               vs.vs_tag = htons((ifv->ifv_prio << EVL_PRIO_BITS) +
+                   ifv->ifv_tag);
+               vs.vs_proto = htons(ifv->ifv_type);
+
+               off -= sizeof(vs);
+               m_copyback(m, off, sizeof(vs), &vs, M_NOWAIT);
+
+               ifp = ifv->ifv_p;
+               ifv = ifp->if_softc;
+       }
+
+       evh = mtod(m, struct ether_vlan_header *);
+       memcpy(evh->evl_dhost, edst, sizeof(evh->evl_dhost));
+       memcpy(evh->evl_shost, esrc, sizeof(evh->evl_shost));
+       evh->evl_encap_proto = htons(ifv->ifv_type);
+       evh->evl_tag = htons(ifv->ifv_tag +
+           (m->m_pkthdr.pf.prio << EVL_PRIO_BITS));
+
+       return (m);
+}
Index: net/if_vlan_var.h
===================================================================
RCS file: /cvs/src/sys/net/if_vlan_var.h,v
retrieving revision 1.24
diff -u -p -r1.24 if_vlan_var.h
--- net/if_vlan_var.h   24 Oct 2013 11:14:33 -0000      1.24
+++ net/if_vlan_var.h   18 Nov 2014 13:11:30 -0000
@@ -96,6 +96,8 @@ struct        ifvlan {
 #define        IFVF_PROMISC    0x01
 
 extern int vlan_input(struct ether_header *eh, struct mbuf *m);
+struct mbuf *vlan_addheader(struct mbuf *, const struct ifnet *, uint16_t,
+    u_char *, u_char *);
 #endif /* _KERNEL */
 
 #endif /* _NET_IF_VLAN_VAR_H_ */

Reply via email to