summaryrefslogtreecommitdiff
path: root/sys/net
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 /sys/net
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@
Diffstat (limited to 'sys/net')
-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);
}