ive got a large reworking of vlan(4) to make vlan tx mpsafe, which
affected mpw(4).

the biggest effect was that it was no longer safe to traverse the
interface parent hierarchy looking for vlan interfaces. however,
from what i could tell from the rfc, such traversal is unecessary.

this refactors the code so it only reinject vlan headers in tagged
mode for vlan interfaces in the same bridge as it. in raw mode it
just sends the packet as is. this is based on my interpretation of
4.4.1 in rfc4448.

this also has the benefit that you can assign local addresses on
mpw interfaces in raw mode and talk over them without making the
machine panic. it should also be possible to assign addresses on
tagged interfaces too.

to be clear, not traversing the vlan interface parents is necessary
for making vlan itself mpsafe. i am confident we're still within
spec without that functionality, and actually get simplifications
and semantic improvements out of it this way.

this diff relies on the vlan_input() code i sent to tech@

ok?

Index: if_mpw.c
===================================================================
RCS file: /cvs/src/sys/net/if_mpw.c,v
retrieving revision 1.12
diff -u -p -r1.12 if_mpw.c
--- if_mpw.c    5 Dec 2015 10:07:55 -0000       1.12
+++ if_mpw.c    2 Mar 2016 01:36:49 -0000
@@ -351,164 +351,51 @@ extern void vlan_start(struct ifnet *ifp
 struct mbuf *
 mpw_vlan_handle(struct mbuf *m, struct mpw_softc *sc)
 {
-       int needsdummy = 0;
-       int fakeifv = 0;
-       struct ifvlan *ifv = NULL;
-       struct ether_vlan_header *evh;
-       struct ifnet *ifp, *ifp0;
-       int nvlan, moff;
-       struct ether_header eh;
-       struct ifvlan fifv;
-       struct vlan_shim {
-               uint16_t        vs_tpid;
-               uint16_t        vs_tci;
-       } vs;
-
-       ifp = ifp0 = if_get(m->m_pkthdr.ph_ifidx);
-       KASSERT(ifp != NULL);
-       if (ifp->if_start == vlan_start)
-               ifv = ifp->if_softc;
-
-       /* If we were relying on VLAN HW support, fake an ifv */
-       if (ifv == NULL && (m->m_flags & M_VLANTAG) == M_VLANTAG) {
-               memset(&fifv, 0, sizeof(fifv));
-               fifv.ifv_tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
-               fifv.ifv_prio = EVL_PRIOFTAG(m->m_pkthdr.ether_vtag);
-               ifv = &fifv;
-               fakeifv = 1;
-       }
-
-       /*
-        * Always remove VLAN flag as we are inserting them here. Also we
-        * might get a tagged packet with no VLAN interface, in this case
-        * we can't do anything.
-        */
-       m->m_flags &= ~M_VLANTAG;
-
-       /*
-        * Do VLAN managing.
-        *
-        * Case ethernet (raw):
-        *  No VLAN: just pass it.
-        *  One or more VLANs: insert VLAN tag back.
-        *
-        * NOTE: In case of raw access mode, the if_vlan will do the job
-        * of dropping non tagged packets for us.
-        */
-       if (sc->sc_type == IMR_TYPE_ETHERNET && ifv == NULL) {
-               if_put(ifp0);
-               return (m);
-       }
-
-       /*
-        * Case ethernet-tagged:
-        *  0 VLAN: Drop packet
-        *  1 VLAN: Tag packet with dummy VLAN
-        *  >1 VLAN: Nothing
-        */
-       if (sc->sc_type == IMR_TYPE_ETHERNET_TAGGED && ifv == NULL) {
-               m_freem(m);
-               if_put(ifp0);
-               return (NULL);
-       }
-
-       /* Copy and remove ethernet header */
-       m_copydata(m, 0, sizeof(eh), (caddr_t) &eh);
-       if (ntohs(eh.ether_type) == ETHERTYPE_VLAN ||
-           ntohs(eh.ether_type) == ETHERTYPE_QINQ)
-               m_adj(m, sizeof(*evh));
-       else
-               m_adj(m, sizeof(eh));
-
-       /* Count VLAN stack size */
-       nvlan = 0;
-       while ((ifp = ifv->ifv_p) != NULL && ifp->if_start == vlan_start) {
-               ifv = ifp->if_softc;
-               nvlan++;
-       }
-       moff = sizeof(*evh) + (nvlan * EVL_ENCAPLEN);
-
-       /* The mode ethernet tagged always need at least 2 VLANs */
-       if (sc->sc_type == IMR_TYPE_ETHERNET_TAGGED && nvlan == 0) {
-               needsdummy = 1;
-               moff += EVL_ENCAPLEN;
-       }
-
-       /* Add VLAN to the beginning of the packet */
-       M_PREPEND(m, moff, M_NOWAIT);
-       if (m == NULL) {
-               if_put(ifp0);
-               return (NULL);
-       }
-
-       /* Copy original ethernet type */
-       moff -= sizeof(eh.ether_type);
-       m_copyback(m, moff, sizeof(eh.ether_type), &eh.ether_type, M_NOWAIT);
-
-       /* Fill inner VLAN values */
-       ifv = ifp0->if_softc;
-       while (nvlan-- > 0) {
-               vs.vs_tci = htons((ifv->ifv_prio << EVL_PRIO_BITS) +
-                   ifv->ifv_tag);
-               vs.vs_tpid = htons(ifv->ifv_type);
-
-               moff -= sizeof(vs);
-               m_copyback(m, moff, sizeof(vs), &vs, M_NOWAIT);
-
-               ifp = ifv->ifv_p;
-               ifv = ifp->if_softc;
-       }
-
-       /* Copy ethernet header back */
-       evh = mtod(m, struct ether_vlan_header *);
-       memcpy(evh->evl_dhost, eh.ether_dhost, sizeof(evh->evl_dhost));
-       memcpy(evh->evl_shost, eh.ether_shost, sizeof(evh->evl_shost));
-
-       if (fakeifv)
-               ifv = &fifv;
-
-       /* Insert the last VLAN and optionally a dummy VLAN */
-       if (needsdummy) {
-               evh->evl_encap_proto = ntohs(ETHERTYPE_QINQ);
-               evh->evl_tag = 0;
-
-               vs.vs_tci = ntohs((m->m_pkthdr.pf.prio << EVL_PRIO_BITS) +
-                   ifv->ifv_tag);
-               vs.vs_tpid = ntohs(ETHERTYPE_VLAN);
-               m_copyback(m, moff, sizeof(vs), &vs, M_NOWAIT);
-       } else {
-               evh->evl_encap_proto = (nvlan > 0) ?
-                   ntohs(ETHERTYPE_QINQ) : ntohs(ETHERTYPE_VLAN);
-               evh->evl_tag = ntohs((m->m_pkthdr.pf.prio << EVL_PRIO_BITS) +
-                   ifv->ifv_tag);
-       }
-
-       if_put(ifp0);
+       struct ifnet *ifp;
+       struct ifvlan *ifv;
+ 
+       uint16_t type = ETHERTYPE_QINQ;
+       uint16_t tag = 0;
+ 
+       ifp = if_get(m->m_pkthdr.ph_ifidx);
+       if (ifp != NULL && ifp->if_start == vlan_start) {
+               ifv = ifp->if_softc;
+               type = ifv->ifv_type;
+               tag = ifv->ifv_tag;
+       }
+       if_put(ifp);
 
-       return (m);
+       return (vlan_inject(m, type, tag));
 }
 #endif /* NVLAN */
 
 void
-mpw_start(struct ifnet *ifp0)
+mpw_start(struct ifnet *ifp)
 {
-       struct mpw_softc *sc = ifp0->if_softc;
-       struct mbuf *m;
+       struct mpw_softc *sc = ifp->if_softc;
        struct rtentry *rt;
-       struct ifnet *ifp;
+       struct ifnet *p;
+       struct mbuf *m;
        struct shim_hdr *shim;
        struct sockaddr_storage ss;
 
-       rt = rtalloc((struct sockaddr *) &sc->sc_nexthop, RT_RESOLVE, 0);
-       if (!rtisvalid(rt)) {
-               rtfree(rt);
+       if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
+           sc->sc_rshim.shim_label == 0 ||
+           sc->sc_type == IMR_TYPE_NONE) {
+               IFQ_PURGE(&ifp->if_snd);
                return;
        }
 
-       ifp = if_get(rt->rt_ifidx);
-       if (ifp == NULL) {
-               rtfree(rt);
-               return;
+       rt = rtalloc((struct sockaddr *)&sc->sc_nexthop, RT_RESOLVE, 0);
+       if (!rtisvalid(rt)) {
+               IFQ_PURGE(&ifp->if_snd);
+               goto rtfree;
+       }
+
+       p = if_get(rt->rt_ifidx);
+       if (p == NULL) {
+               IFQ_PURGE(&ifp->if_snd);
+               goto rtfree;
        }
 
        /*
@@ -516,38 +403,29 @@ mpw_start(struct ifnet *ifp0)
         * the right place.
         */
        memcpy(&ss, &sc->sc_nexthop, sizeof(sc->sc_nexthop));
-       ((struct sockaddr *) &ss)->sa_family = AF_MPLS;
-
-       for (;;) {
-               IFQ_DEQUEUE(&ifp0->if_snd, m);
-               if (m == NULL)
-                       break;
-
-               if ((ifp0->if_flags & IFF_RUNNING) == 0 ||
-                   sc->sc_rshim.shim_label == 0 ||
-                   sc->sc_type == IMR_TYPE_NONE) {
-                       m_freem(m);
-                       continue;
-               }
-
-#if NVLAN > 0
-               m = mpw_vlan_handle(m, sc);
-               if (m == NULL)
-                       continue;
-#else
-               /* Ethernet tagged doesn't work without VLANs'*/
-               if (sc->sc_type == IMR_TYPE_ETHERNET_TAGGED) {
-                       m_freem(m);
-                       continue;
-               }
-#endif /* NVLAN */
+       ((struct sockaddr *)&ss)->sa_family = AF_MPLS;
 
+       while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
 #if NBPFILTER > 0
                if (sc->sc_if.if_bpf)
                        bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_OUT);
 #endif /* NBPFILTER */
 
-               if (sc->sc_flags & IMR_FLAG_CONTROLWORD) {
+               if (sc->sc_type == IMR_TYPE_ETHERNET_TAGGED) {
+ #if NVLAN > 0
+                       m = mpw_vlan_handle(m, sc);
+                       if (m == NULL) {
+                               ifp->if_oerrors++;
+                               continue;
+                       }
+ #else
+                       /* Ethernet tagged doesn't work without VLANs'*/
+                       m_freem(m);
+                       continue;
+ #endif /* NVLAN */
+               }
+ 
+               if (sc->sc_flags & IMR_FLAG_CONTROLWORD) {
                        M_PREPEND(m, sizeof(*shim), M_NOWAIT);
                        if (m == NULL)
                                continue;
@@ -567,9 +445,10 @@ mpw_start(struct ifnet *ifp0)
                /* XXX: MPLS only uses domain 0 */
                m->m_pkthdr.ph_rtableid = 0;
 
-               mpls_output(ifp, m, (struct sockaddr *) &ss, rt);
+               mpls_output(p, m, (struct sockaddr *)&ss, rt);
        }
 
-       if_put(ifp);
+       if_put(p);
+rtfree:
        rtfree(rt);
 }

Reply via email to