summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2016-03-09 04:19:44 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2016-03-09 04:19:44 +0000
commitcb3c5b79be491c15ac84f236cfb0ce17de21f912 (patch)
tree42633f5ff480a6ac3c47bbea77c0c8009de0a75a
parent9c79e8a9809abcbf84c518a776d6e6ea90ed39be (diff)
rework how mpw(4) interacts with vlan(4)
previously mpw would walk around our interface hierarchy so it could reinject vlan tags for packets that were received on a stack of vlan interfaces. this got in the way of making vlan mpsafe, but is also unecessary according to how i read RFC 4448 which describes how mpls pseudowires should function. the behaviour is now: - in raw mode mpw(4) acts like a normal ethernet interface it no longer injects a tag from a vlan interface that may be on the same bridge as it, and it allows you to configure ip addresses on the local interface and use them without panicking the kernel. - in tagged mode it will only add tags from vlan interfaces on the same bridge as it, but not any parent/child tags from interfaces on top of that vlan. if the packet did not come from a vlan interface on a bridge it will inject a tag for vlan 0. this will also allow vlan to move forward. ok mpi@ rzalamena@ claudio@
-rw-r--r--sys/net/if_mpw.c228
1 files changed, 54 insertions, 174 deletions
diff --git a/sys/net/if_mpw.c b/sys/net/if_mpw.c
index 407b545ff92..8e8159f09e0 100644
--- a/sys/net/if_mpw.c
+++ b/sys/net/if_mpw.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_mpw.c,v 1.12 2015/12/05 10:07:55 tedu Exp $ */
+/* $OpenBSD: if_mpw.c,v 1.13 2016/03/09 04:19:43 dlg Exp $ */
/*
* Copyright (c) 2015 Rafael Zalamena <rzalamena@openbsd.org>
@@ -351,164 +351,52 @@ 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 &&
+ ISSET(ifp->if_flags, IFF_RUNNING)) {
+ 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 +404,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 +446,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);
}