Take vlan(4) out of ether_input().
authormpi <mpi@openbsd.org>
Tue, 19 May 2015 11:09:24 +0000 (11:09 +0000)
committermpi <mpi@openbsd.org>
Tue, 19 May 2015 11:09:24 +0000 (11:09 +0000)
To keep the list of input handlers short, multiple vlans share the
same ifih.

if_input_process() now looks if the interface of a mbuf changed to
make sure the corresponding handlers are executed.  This is a hack
and will be improved later.

ok dlg@

sys/net/if.c
sys/net/if_ethersubr.c
sys/net/if_var.h
sys/net/if_vlan.c
sys/net/if_vlan_var.h

index 6fca910..81461ef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if.c,v 1.331 2015/05/15 10:15:13 mpi Exp $    */
+/*     $OpenBSD: if.c,v 1.332 2015/05/19 11:09:24 mpi Exp $    */
 /*     $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $  */
 
 /*
@@ -530,10 +530,19 @@ if_input_process(void *xmq)
                if ((++mit & 0x1f) == 0)
                        yield();
 
+again:
+               /*
+                * Pass this mbuf to all input handlers of its
+                * interface until it is consumed.
+                */
                ifp = m->m_pkthdr.rcvif;
                SLIST_FOREACH(ifih, &ifp->if_inputs, ifih_next) {
                        if ((*ifih->ifih_input)(m, NULL))
                                break;
+
+                       /* Pseudo-drivers might be stacked. */
+                       if (ifp != m->m_pkthdr.rcvif)
+                               goto again;
                }
        }
        splx(s);
index 1cf0d1c..1d02309 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_ethersubr.c,v 1.198 2015/05/15 10:15:13 mpi Exp $  */
+/*     $OpenBSD: if_ethersubr.c,v 1.199 2015/05/19 11:09:24 mpi Exp $  */
 /*     $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $        */
 
 /*
@@ -426,7 +426,7 @@ bad:
 int
 ether_input(struct mbuf *m, void *hdr)
 {
-       struct ifnet *ifp0, *ifp;
+       struct ifnet *ifp;
        struct ether_header *eh = hdr;
        struct niqueue *inq;
        u_int16_t etype;
@@ -439,7 +439,7 @@ ether_input(struct mbuf *m, void *hdr)
 
 
        /* mark incoming routing table */
-       ifp = ifp0 = m->m_pkthdr.rcvif;
+       ifp = m->m_pkthdr.rcvif;
        m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
 
        if (eh == NULL) {
@@ -481,12 +481,6 @@ ether_input(struct mbuf *m, void *hdr)
                atomic_setbits_int(&netisr, (1 << NETISR_RND_DONE));
        }
 
-#if NVLAN > 0
-       if (((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN ||
-           etype == ETHERTYPE_QINQ) && (vlan_input(eh, m) == 0))
-               return (1);
-#endif
-
 #if NBRIDGE > 0
        /*
         * Tap the packet off here for a bridge, if configured and
@@ -535,7 +529,7 @@ ether_input(struct mbuf *m, void *hdr)
         * is for us.  Drop otherwise.
         */
        if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 &&
-           ((ifp->if_flags & IFF_PROMISC) || (ifp0->if_flags & IFF_PROMISC))) {
+           (ifp->if_flags & IFF_PROMISC)) {
                if (memcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN)) {
                        m_freem(m);
                        return (1);
index 8529152..a775c65 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_var.h,v 1.28 2015/05/18 13:32:28 reyk Exp $        */
+/*     $OpenBSD: if_var.h,v 1.29 2015/05/19 11:09:24 mpi Exp $ */
 /*     $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $  */
 
 /*
@@ -115,6 +115,7 @@ struct      ifqueue {
 struct ifih {
        SLIST_ENTRY(ifih) ifih_next;
        int             (*ifih_input)(struct mbuf *, void *);
+       int               ifih_refcnt;
 };
 
 /*
index 825c36d..5554484 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_vlan.c,v 1.119 2015/05/15 10:15:13 mpi Exp $       */
+/*     $OpenBSD: if_vlan.c,v 1.120 2015/05/19 11:09:24 mpi Exp $       */
 
 /*
  * Copyright 1998 Massachusetts Institute of Technology
@@ -79,6 +79,8 @@ u_long vlan_tagmask, svlan_tagmask;
 #define TAG_HASH(tag)          (tag & vlan_tagmask)
 LIST_HEAD(vlan_taghash, ifvlan)        *vlan_tagh, *svlan_tagh;
 
+
+int    vlan_input(struct mbuf *, void *);
 int    vlan_output(struct ifnet *, struct mbuf *, struct sockaddr *,
            struct rtentry *);
 void   vlan_start(struct ifnet *ifp);
@@ -255,32 +257,44 @@ vlan_start(struct ifnet *ifp)
 }
 
 /*
- * vlan_input() returns 0 if it has consumed the packet, 1 otherwise.
+ * vlan_input() returns 1 if it has consumed the packet, 0 otherwise.
  */
 int
-vlan_input(struct ether_header *eh, struct mbuf *m)
+vlan_input(struct mbuf *m, void *hdr)
 {
-       struct ifvlan           *ifv;
-       struct ifnet            *ifp = m->m_pkthdr.rcvif;
-       struct vlan_taghash     *tagh;
-       u_int                    tag;
-       u_int16_t                etype;
-       struct ether_header     *eh1;
+       struct ifvlan                   *ifv;
+       struct ifnet                    *ifp;
+       struct ether_vlan_header        *evl;
+       struct ether_header             *eh;
+       struct vlan_taghash             *tagh;
+       u_int                            tag;
+       u_int16_t                        etype;
+
+       ifp = m->m_pkthdr.rcvif;
+       eh = mtod(m, struct ether_header *);
+
+       etype = ntohs(eh->ether_type);
 
        if (m->m_flags & M_VLANTAG) {
                etype = ETHERTYPE_VLAN;
                tagh = vlan_tagh;
-       } else {
+       } else if ((etype == ETHERTYPE_VLAN) || (etype == ETHERTYPE_QINQ)) {
                if (m->m_len < EVL_ENCAPLEN &&
                    (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
                        ifp->if_ierrors++;
-                       return (0);
+                       return (1);
                }
 
-               etype = ntohs(eh->ether_type);
+               evl = mtod(m, struct ether_vlan_header *);
+               m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
                tagh = etype == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
-               m->m_pkthdr.ether_vtag = ntohs(*mtod(m, u_int16_t *));
+       } else {
+               /* Skip non-VLAN packets. */
+               return (0);
        }
+
+       ifp->if_ibytes += m->m_pkthdr.len;
+
        /* From now on ether_vtag is fine */
        tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
        m->m_pkthdr.pf.prio = EVL_PRIOFTAG(m->m_pkthdr.ether_vtag);
@@ -290,7 +304,7 @@ vlan_input(struct ether_header *eh, struct mbuf *m)
                m->m_pkthdr.pf.prio = !m->m_pkthdr.pf.prio;
 
        LIST_FOREACH(ifv, &tagh[TAG_HASH(tag)], ifv_list) {
-               if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag &&
+               if (ifp == ifv->ifv_p && tag == ifv->ifv_tag &&
                    etype == ifv->ifv_type)
                        break;
        }
@@ -302,40 +316,35 @@ vlan_input(struct ether_header *eh, struct mbuf *m)
                 * it a chance.
                 */
                if (ifp->if_bridgeport && (m->m_flags & M_PROTO1) == 0)
-                       return (1);
+                       return (0);
 #endif
                ifp->if_noproto++;
                m_freem(m);
-               return (0);
+               return (1);
        }
 
        if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
            (IFF_UP|IFF_RUNNING)) {
                m_freem(m);
-               return (0);
+               return (1);
        }
 
        /*
         * Having found a valid vlan interface corresponding to
         * the given source interface and vlan tag, remove the
-        * encapsulation, and run the real packet through
-        * ether_input() a second time (it had better be
-        * reentrant!).
+        * encapsulation.
         */
-       m->m_pkthdr.rcvif = &ifv->ifv_if;
        if (m->m_flags & M_VLANTAG) {
                m->m_flags &= ~M_VLANTAG;
        } else {
-               eh->ether_type = mtod(m, u_int16_t *)[1];
-               m->m_len -= EVL_ENCAPLEN;
-               m->m_data += EVL_ENCAPLEN;
-               m->m_pkthdr.len -= EVL_ENCAPLEN;
+               eh->ether_type = evl->evl_proto;
+               memmove((char *)eh + EVL_ENCAPLEN, eh, sizeof(*eh));
+               m_adj(m, EVL_ENCAPLEN);
        }
 
 #if NBPFILTER > 0
        if (ifv->ifv_if.if_bpf)
-               bpf_mtap_hdr(ifv->ifv_if.if_bpf, (char *)eh, ETHER_HDR_LEN, m,
-                   BPF_DIRECTION_IN, NULL);
+               bpf_mtap_ether(ifv->ifv_if.if_bpf, m, BPF_DIRECTION_IN);
 #endif
 
        /*
@@ -348,25 +357,19 @@ vlan_input(struct ether_header *eh, struct mbuf *m)
                if (bcmp(&ifv->ifv_ac.ac_enaddr, eh->ether_dhost,
                    ETHER_ADDR_LEN)) {
                        m_freem(m);
-                       return (0);
+                       return (1);
                }
        }
 
-       M_PREPEND(m, sizeof(*eh1), M_DONTWAIT);
-       if (m == NULL)
-               return (-1);
-       eh1 = mtod(m, struct ether_header *);
-       memmove(eh1, eh, sizeof(*eh1));
-
        ifv->ifv_if.if_ipackets++;
-       ether_input_mbuf(&ifv->ifv_if, m);
-
+       m->m_pkthdr.rcvif = &ifv->ifv_if;
        return (0);
 }
 
 int
 vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
 {
+       struct ifih             *vlan_ifih;
        struct sockaddr_dl      *sdl1, *sdl2;
        struct vlan_taghash     *tagh;
        u_int                    flags;
@@ -377,6 +380,16 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
        if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */
                return (0);
 
+       /* Share an ifih between multiple vlan(4) instances. */
+       vlan_ifih = SLIST_FIRST(&p->if_inputs);
+       if (vlan_ifih->ifih_input != vlan_input) {
+               vlan_ifih = malloc(sizeof(*vlan_ifih), M_DEVBUF, M_NOWAIT);
+               if (vlan_ifih == NULL)
+                       return (ENOMEM);
+               vlan_ifih->ifih_input = vlan_input;
+               vlan_ifih->ifih_refcnt = 0;
+       }
+
        /* Remember existing interface flags and reset the interface */
        flags = ifv->ifv_flags;
        vlan_unconfig(&ifv->ifv_if, p);
@@ -428,9 +441,10 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
        bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
 
        ifv->ifv_tag = tag;
-       s = splnet();
-       tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
-       LIST_INSERT_HEAD(&tagh[TAG_HASH(tag)], ifv, ifv_list);
+
+       /* Change input handler of the physical interface. */
+       if (++vlan_ifih->ifih_refcnt == 1)
+               SLIST_INSERT_HEAD(&p->if_inputs, vlan_ifih, ifih_next);
 
        /* Register callback for physical link state changes */
        ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
@@ -441,6 +455,10 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
            vlan_ifdetach, ifv);
 
        vlan_vlandev_state(ifv);
+
+       tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
+       s = splnet();
+       LIST_INSERT_HEAD(&tagh[TAG_HASH(tag)], ifv, ifv_list);
        splx(s);
 
        return (0);
@@ -449,6 +467,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
 int
 vlan_unconfig(struct ifnet *ifp, struct ifnet *newp)
 {
+       struct ifih             *vlan_ifih;
        struct sockaddr_dl      *sdl;
        struct ifvlan           *ifv;
        struct ifnet            *p;
@@ -476,6 +495,14 @@ vlan_unconfig(struct ifnet *ifp, struct ifnet *newp)
                if_link_state_change(ifp);
        }
 
+       /* Restore previous input handler. */
+       vlan_ifih = SLIST_FIRST(&p->if_inputs);
+       KASSERT(vlan_ifih->ifih_input == vlan_input);
+       if (--vlan_ifih->ifih_refcnt == 0) {
+               SLIST_REMOVE_HEAD(&p->if_inputs, ifih_next);
+               free(vlan_ifih, M_DEVBUF, sizeof(*vlan_ifih));
+       }
+
        /*
         * Since the interface is being unconfigured, we need to
         * empty the list of multicast groups that we may have joined
index 65a079d..f02540d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_vlan_var.h,v 1.24 2013/10/24 11:14:33 deraadt Exp $        */
+/*     $OpenBSD: if_vlan_var.h,v 1.25 2015/05/19 11:09:24 mpi Exp $    */
 
 /*
  * Copyright 1998 Massachusetts Institute of Technology
@@ -94,8 +94,6 @@ struct        ifvlan {
 #define        ifv_prio        ifv_mib.ifvm_prio
 #define        ifv_type        ifv_mib.ifvm_type
 #define        IFVF_PROMISC    0x01
-
-extern int vlan_input(struct ether_header *eh, struct mbuf *m);
 #endif /* _KERNEL */
 
 #endif /* _NET_IF_VLAN_VAR_H_ */