diff options
author | Christian Weisgerber <naddy@cvs.openbsd.org> | 2010-06-03 16:15:01 +0000 |
---|---|---|
committer | Christian Weisgerber <naddy@cvs.openbsd.org> | 2010-06-03 16:15:01 +0000 |
commit | c79d3ee48afe01a1fdde546d24383316d3fc2a2b (patch) | |
tree | c9ca3b140cffaa97d9874bb337a635d674e4191d /sys/net | |
parent | a190b1bfa728e74bdb3b9f6990136aa5bae67a22 (diff) |
Add support for 802.1ad-style QinQ nested VLANs.
QinQ-compliant svlan (service VLAN) interfaces are implemented as
a new cloner type, use Ethernet type 0x88a8, and have a dedicated
VLAN tag hash to avoid ID conflicts. vlan(4) interfaces can be
stacked on top of svlan(4).
Originally from reyk@, input from claudio@ and deraadt@
ok claudio@
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/if_bridge.c | 5 | ||||
-rw-r--r-- | sys/net/if_ethersubr.c | 9 | ||||
-rw-r--r-- | sys/net/if_vlan.c | 75 | ||||
-rw-r--r-- | sys/net/if_vlan_var.h | 4 |
4 files changed, 64 insertions, 29 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index ae4e4b5721a..51348864aae 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.177 2010/01/13 05:25:06 claudio Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.178 2010/06/03 16:15:00 naddy Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -2704,7 +2704,8 @@ bridge_fragment(struct bridge_softc *sc, struct ifnet *ifp, #else etype = ntohs(eh->ether_type); #if NVLAN > 0 - if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN) { + if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN || + etype == ETHERTYPE_QINQ) { int len = m->m_pkthdr.len; if (m->m_flags & M_VLANTAG) diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 0c40eccc614..a82b1c0a9c3 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ethersubr.c,v 1.143 2010/05/28 12:09:09 claudio Exp $ */ +/* $OpenBSD: if_ethersubr.c,v 1.144 2010/06/03 16:15:00 naddy Exp $ */ /* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */ /* @@ -613,8 +613,8 @@ ether_input(ifp0, eh, m) } #if NVLAN > 0 - if (((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN) - && (vlan_input(eh, m) == 0)) + if (((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN || + etype == ETHERTYPE_QINQ) && (vlan_input(eh, m) == 0)) return; #endif @@ -639,7 +639,8 @@ ether_input(ifp0, eh, m) #endif #if NVLAN > 0 - if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN) { + if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN || + etype == ETHERTYPE_QINQ) { /* The bridge did not want the vlan frame either, drop it. */ ifp->if_noproto++; m_freem(m); diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index 8fb09cba298..ab678a65d76 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_vlan.c,v 1.83 2010/04/20 22:05:43 tedu Exp $ */ +/* $OpenBSD: if_vlan.c,v 1.84 2010/06/03 16:15:00 naddy Exp $ */ /* * Copyright 1998 Massachusetts Institute of Technology @@ -77,19 +77,19 @@ #include <net/if_vlan_var.h> extern struct ifaddr **ifnet_addrs; -u_long vlan_tagmask; +u_long vlan_tagmask, svlan_tagmask; -#define TAG_HASH_SIZE 32 -#define TAG_HASH(tag) (tag & vlan_tagmask) -LIST_HEAD(, ifvlan) *vlan_tagh; +#define TAG_HASH_SIZE 32 +#define TAG_HASH(tag) (tag & vlan_tagmask) +LIST_HEAD(vlan_taghash, ifvlan) *vlan_tagh, *svlan_tagh; -void vlan_start (struct ifnet *ifp); -int vlan_ioctl (struct ifnet *ifp, u_long cmd, caddr_t addr); -int vlan_unconfig (struct ifnet *ifp); -int vlan_config (struct ifvlan *, struct ifnet *, u_int16_t); +void vlan_start(struct ifnet *ifp); +int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); +int vlan_unconfig(struct ifnet *ifp); +int vlan_config(struct ifvlan *, struct ifnet *, u_int16_t); void vlan_vlandev_state(void *); -void vlanattach (int count); -int vlan_set_promisc (struct ifnet *ifp); +void vlanattach(int count); +int vlan_set_promisc(struct ifnet *ifp); int vlan_ether_addmulti(struct ifvlan *, struct ifreq *); int vlan_ether_delmulti(struct ifvlan *, struct ifreq *); void vlan_ether_purgemulti(struct ifvlan *); @@ -99,16 +99,26 @@ void vlan_ifdetach(void *); struct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy); +struct if_clone svlan_cloner = + IF_CLONE_INITIALIZER("svlan", vlan_clone_create, vlan_clone_destroy); /* ARGSUSED */ void vlanattach(int count) { - vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, &vlan_tagmask); + /* Normal VLAN */ + vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, + &vlan_tagmask); if (vlan_tagh == NULL) panic("vlanattach: hashinit"); - if_clone_attach(&vlan_cloner); + + /* Service-VLAN for QinQ/802.1ad provider bridges */ + svlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, + &svlan_tagmask); + if (svlan_tagh == NULL) + panic("vlanattach: hashinit"); + if_clone_attach(&svlan_cloner); } int @@ -129,6 +139,12 @@ vlan_clone_create(struct if_clone *ifc, int unit) /* NB: flags are not set here */ /* NB: mtu is not set here */ + /* Special handling for the IEEE 802.1ad QinQ variant */ + if (strcmp("svlan", ifc->ifc_name) == 0) + ifv->ifv_type = ETHERTYPE_QINQ; + else + ifv->ifv_type = ETHERTYPE_VLAN; + ifp->if_start = vlan_start; ifp->if_ioctl = vlan_ioctl; IFQ_SET_MAXLEN(&ifp->if_snd, 1); @@ -206,7 +222,8 @@ vlan_start(struct ifnet *ifp) * to create a special header for it. In this case, we just pass * the packet along. */ - if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) { + if ((p->if_capabilities & IFCAP_VLAN_HWTAGGING) && + (ifv->ifv_type == ETHERTYPE_VLAN)) { m->m_pkthdr.ether_vtag = ifv->ifv_tag + (ifv->ifv_prio << EVL_PRIO_BITS); m->m_flags |= M_VLANTAG; @@ -215,7 +232,7 @@ vlan_start(struct ifnet *ifp) m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh); evh.evl_proto = evh.evl_encap_proto; - evh.evl_encap_proto = htons(ETHERTYPE_VLAN); + evh.evl_encap_proto = htons(ifv->ifv_type); evh.evl_tag = htons(ifv->ifv_tag + (ifv->ifv_prio << EVL_PRIO_BITS)); @@ -255,15 +272,17 @@ vlan_start(struct ifnet *ifp) * vlan_input() returns 0 if it has consumed the packet, 1 otherwise. */ int -vlan_input(eh, m) - struct ether_header *eh; - struct mbuf *m; +vlan_input(struct ether_header *eh, struct mbuf *m) { struct ifvlan *ifv; - u_int tag; struct ifnet *ifp = m->m_pkthdr.rcvif; + struct vlan_taghash *tagh; + u_int tag; + u_int16_t etype; if (m->m_flags & M_VLANTAG) { + etype = ETHERTYPE_VLAN; + tagh = vlan_tagh; tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); } else { if (m->m_len < EVL_ENCAPLEN && @@ -272,11 +291,14 @@ vlan_input(eh, m) return (0); } + etype = ntohs(eh->ether_type); + tagh = etype == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh; tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))); } - LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) { - if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag) + LIST_FOREACH(ifv, &tagh[TAG_HASH(tag)], ifv_list) { + if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag && + etype == ifv->ifv_type) break; } if (ifv == NULL) @@ -336,6 +358,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag) { struct ifaddr *ifa1, *ifa2; struct sockaddr_dl *sdl1, *sdl2; + struct vlan_taghash *tagh; int s; if (p->if_type != IFT_ETHER) @@ -395,6 +418,13 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag) /* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */ /* + * Hardware VLAN tagging only works with the default VLAN + * ethernet type (0x8100). + */ + if (ifv->ifv_type != ETHERTYPE_VLAN) + ifv->ifv_if.if_capabilities &= ~IFCAP_VLAN_HWTAGGING; + + /* * Set up our ``Ethernet address'' to reflect the underlying * physical interface's. */ @@ -409,7 +439,8 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag) ifv->ifv_tag = tag; s = splnet(); - LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list); + tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh; + LIST_INSERT_HEAD(&tagh[TAG_HASH(tag)], ifv, ifv_list); /* Register callback for physical link state changes */ ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1, diff --git a/sys/net/if_vlan_var.h b/sys/net/if_vlan_var.h index e46439ec00a..a1d2679671b 100644 --- a/sys/net/if_vlan_var.h +++ b/sys/net/if_vlan_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_vlan_var.h,v 1.19 2008/11/07 00:52:40 brad Exp $ */ +/* $OpenBSD: if_vlan_var.h,v 1.20 2010/06/03 16:15:00 naddy Exp $ */ /* * Copyright 1998 Massachusetts Institute of Technology @@ -53,6 +53,7 @@ struct ifvlan { u_int16_t ifvm_proto; /* encapsulation ethertype */ u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ u_int16_t ifvm_prio; /* prio to apply on packet leaving if */ + u_int16_t ifvm_type; /* non-standard ethertype or 0x8100 */ } ifv_mib; LIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; LIST_ENTRY(ifvlan) ifv_list; @@ -64,6 +65,7 @@ struct ifvlan { #define ifv_if ifv_ac.ac_if #define ifv_tag ifv_mib.ifvm_tag #define ifv_prio ifv_mib.ifvm_prio +#define ifv_type ifv_mib.ifvm_type #define IFVF_PROMISC 0x01 #endif /* _KERNEL */ |