From ec566039aba5786e01795e673cf02a62dfe37bc4 Mon Sep 17 00:00:00 2001 From: wollman Date: Wed, 18 Mar 1998 01:40:12 +0000 Subject: [PATCH] Add preliminary support for IEEE 802.1Q VLAN tagging. It doesn't actually work reliably yet (I've had panics), but it does seem to occasionally be able to transmit and receive syntactically-correct packets. Also fixes one of if_ethersubr.c's legion style bugs, and removes the hostcache code from standard kernels---the code that depends on it is not going to happen any time soon, I'm afraid. --- sys/conf/files | 5 +- sys/net/ethernet.h | 3 +- sys/net/if_ethersubr.c | 33 +++- sys/net/if_vlan.c | 363 +++++++++++++++++++++++++++++++++++++++++ sys/net/if_vlan_var.h | 84 ++++++++++ 5 files changed, 476 insertions(+), 12 deletions(-) create mode 100644 sys/net/if_vlan.c create mode 100644 sys/net/if_vlan_var.h diff --git a/sys/conf/files b/sys/conf/files index 2e906cc566f..b4f059b5260 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -197,7 +197,7 @@ msdosfs/msdosfs_vnops.c optional msdosfs net/bpf.c optional bpfilter net/bpf_filter.c optional bpfilter net/bsd_comp.c optional ppp_bsdcomp -net/hostcache.c standard +#net/hostcache.c standard net/if.c standard net/if_atmsubr.c optional atm net/if_disc.c optional disc @@ -210,6 +210,7 @@ net/if_ppp.c optional ppp net/if_sl.c optional sl net/if_spppsubr.c optional sppp net/if_tun.c optional tun +net/if_vlan.c optional vlan net/ppp_deflate.c optional ppp_deflate net/ppp_tty.c optional ppp net/radix.c standard @@ -254,7 +255,7 @@ netinet/if_atm.c optional atm netinet/if_ether.c optional ether netinet/igmp.c optional inet netinet/in.c optional inet -netinet/in_hostcache.c optional inet +#netinet/in_hostcache.c optional inet netinet/in_pcb.c optional inet netinet/in_proto.c optional inet netinet/in_rmx.c optional inet diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h index 36d9a8c218c..c9c03a7c924 100644 --- a/sys/net/ethernet.h +++ b/sys/net/ethernet.h @@ -1,7 +1,7 @@ /* * Fundamental constants relating to ethernet. * - * $Id$ + * $Id: ethernet.h,v 1.8 1997/02/22 09:40:58 peter Exp $ * */ @@ -64,6 +64,7 @@ struct ether_addr { #define ETHERTYPE_IP 0x0800 /* IP protocol */ #define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */ #define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */ +#define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging */ #define ETHERTYPE_LOOPBACK 0x9000 /* used to test interfaces */ /* XXX - add more useful types here */ diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 73c7cc27f79..9b64a590016 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 - * $Id: if_ethersubr.c,v 1.44 1998/01/31 07:23:14 eivind Exp $ + * $Id: if_ethersubr.c,v 1.45 1998/02/20 13:11:49 bde Exp $ */ #include "opt_atalk.h" @@ -97,9 +97,14 @@ extern struct ifqueue pkintrq; #define llc_snap_org_code llc_un.type_snap.org_code #define llc_snap_ether_type llc_un.type_snap.ether_type -extern u_char at_org_code[ 3 ]; -extern u_char aarp_org_code[ 3 ]; -#endif NETATALK +extern u_char at_org_code[3]; +extern u_char aarp_org_code[3]; +#endif /* NETATALK */ + +#include "vlan.h" +#if NVLAN > 0 +#include +#endif /* NVLAN > 0 */ static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); @@ -473,16 +478,26 @@ ether_input(ifp, eh, m) return; } ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh); - if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, - sizeof(etherbroadcastaddr)) == 0) - m->m_flags |= M_BCAST; - else if (eh->ether_dhost[0] & 1) - m->m_flags |= M_MCAST; + if (eh->ether_dhost[0] & 1) { + if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, + sizeof(etherbroadcastaddr)) == 0) + m->m_flags |= M_BCAST; + else + m->m_flags |= M_MCAST; + } if (m->m_flags & (M_BCAST|M_MCAST)) ifp->if_imcasts++; ether_type = ntohs(eh->ether_type); +#if NVLAN > 0 + if (ether_type == vlan_proto) { + if (vlan_input(eh, m) < 0) + ifp->if_data.ifi_noproto++; + return; + } +#endif /* NVLAN > 0 */ + switch (ether_type) { #ifdef INET case ETHERTYPE_IP: diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c new file mode 100644 index 00000000000..eb28226cd24 --- /dev/null +++ b/sys/net/if_vlan.c @@ -0,0 +1,363 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* + * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. + * Might be extended some day to also handle IEEE 802.1p priority + * tagging. This is sort of sneaky in the implementation, since + * we need to pretend to be enough of an Ethernet implementation + * to make arp work. The way we do this is by telling everyone + * that we are an Ethernet, and then catch the packets that + * ether_output() left on our output queue queue when it calls + * if_start(), rewrite them for use by the real outgoing interface, + * and ask it to send them. + */ + +#include "vlan.h" +#if NVLAN > 0 +#include "opt_inet.h" +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include + +#if NBPFILTER > 0 +#include +#endif +#include +#include +#include +#include +#include +#include + +#ifdef INET +#include +#include +#endif + +SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); +SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); + +u_int vlan_proto = ETHERTYPE_VLAN; +SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto, + 0, "Ethernet protocol used for VLAN encapsulation"); + +static struct ifvlan ifv_softc[NVLAN]; + +static void vlan_start(struct ifnet *ifp); +static void vlan_ifinit(void *foo); +static int vlan_ioctl(struct ifnet *ifp, int cmd, caddr_t addr); + +static void +vlaninit(void *dummy) +{ + int i; + + for (i = 0; i < NVLAN; i++) { + struct ifnet *ifp = &ifv_softc[i].ifv_if; + + ifp->if_softc = &ifv_softc[i]; + ifp->if_name = "vlan"; + ifp->if_unit = i; + /* NB: flags are not set here */ + ifp->if_linkmib = &ifv_softc[i].ifv_mib; + ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; + /* NB: mtu is not set here */ + + ifp->if_init = vlan_ifinit; + ifp->if_start = vlan_start; + ifp->if_ioctl = vlan_ioctl; + ifp->if_output = ether_output; + if_attach(ifp); + ether_ifattach(ifp); +#if NBPFILTER > 0 + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + /* Now undo some of the damage... */ + ifp->if_data.ifi_type = IFT_8021_VLAN; + ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; + ifp->if_resolvemulti = 0; + } +} +PSEUDO_SET(vlaninit, if_vlan); + +static void +vlan_ifinit(void *foo) +{ + ; +} + +static void +vlan_start(struct ifnet *ifp) +{ + struct ifvlan *ifv; + struct ifnet *p; + struct ether_vlan_header *evl; + struct mbuf *m; + + ifv = ifp->if_softc; + p = ifv->ifv_p; + + ifp->if_flags |= IFF_OACTIVE; + for (;;) { + IF_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(m, ifp); +#endif /* NBPFILTER > 0 */ + + M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); + if (m == 0) + continue; + /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ + + /* + * Transform the Ethernet header into an Ethernet header + * with 802.1Q encapsulation. + */ + bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), + sizeof(struct ether_header)); + evl = mtod(m, struct ether_vlan_header *); + evl->evl_proto = evl->evl_encap_proto; + evl->evl_encap_proto = htons(vlan_proto); + evl->evl_tag = htons(ifv->ifv_tag); + printf("vlan_start: %*D\n", sizeof *evl, (char *)evl, ":"); + + /* + * Send it, precisely as ether_output() would have. + * We are already running at splimp. + */ + if (IF_QFULL(&p->if_snd)) { + IF_DROP(&p->if_snd); + /* XXX stats */ + } + IF_ENQUEUE(&p->if_snd, m); + if ((p->if_flags & IFF_OACTIVE) == 0) + p->if_start(p); + } + ifp->if_flags &= ~IFF_OACTIVE; +} + +int +vlan_input(struct ether_header *eh, struct mbuf *m) +{ + int i; + struct ifvlan *ifv; + + for (i = 0; i < NVLAN; i++) { + ifv = &ifv_softc[i]; + if (m->m_pkthdr.rcvif == ifv->ifv_p + && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) + == ifv->ifv_tag)) + break; + } + + if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { + m_freem(m); + return -1; /* so ether_input can take note */ + } + + /* + * 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!). + */ + m->m_pkthdr.rcvif = &ifv->ifv_if; + eh->ether_type = mtod(m, u_int16_t *)[1]; + m->m_data += EVL_ENCAPLEN; + m->m_len -= EVL_ENCAPLEN; + m->m_pkthdr.len -= EVL_ENCAPLEN; + +#if NBPFILTER > 0 + if (ifv->ifv_if.if_bpf) { + /* + * Do the usual BPF fakery. Note that we don't support + * promiscuous mode here, since it would require the + * drivers to know about VLANs and we're not ready for + * that yet. + */ + struct mbuf m0; + m0.m_next = m; + m0.m_len = sizeof(struct ether_header); + m0.m_data = (char *)eh; + bpf_mtap(&ifv->ifv_if, &m0); + } +#endif + ether_input(&ifv->ifv_if, eh, m); + return 0; +} + +static int +vlan_config(struct ifvlan *ifv, struct ifnet *p) +{ + struct ifaddr *ifa1, *ifa2; + struct sockaddr_dl *sdl1, *sdl2; + + if (p->if_data.ifi_type != IFT_ETHER) + return EPROTONOSUPPORT; + if (ifv->ifv_p) + return EBUSY; + ifv->ifv_p = p; + if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) + ifv->ifv_if.if_mtu = p->if_mtu; + else + ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; + + /* + * NB: we don't support multicast at this point. + */ + ifv->ifv_if.if_flags = (p->if_flags & ~IFF_MULTICAST); /* XXX */ + + /* + * Set up our ``Ethernet address'' to reflect the underlying + * physical interface's. + */ + ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1]; + ifa2 = ifnet_addrs[p->if_index - 1]; + sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; + sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; + sdl1->sdl_type = IFT_ETHER; + sdl1->sdl_alen = ETHER_ADDR_LEN; + bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); + bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); + return 0; +} + +static int +vlan_ioctl(struct ifnet *ifp, int cmd, caddr_t data) +{ + struct ifaddr *ifa; + struct ifnet *p; + struct ifreq *ifr; + struct ifvlan *ifv; + struct vlanreq vlr; + int error = 0; + + ifr = (struct ifreq *)data; + ifa = (struct ifaddr *)data; + ifv = ifp->if_softc; + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&ifv->ifv_ac, ifa); + break; +#endif + default: + break; + } + break; + + case SIOCGIFADDR: + { + struct sockaddr *sa; + + sa = (struct sockaddr *) &ifr->ifr_data; + bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, + (caddr_t) sa->sa_data, ETHER_ADDR_LEN); + } + break; + + case SIOCSIFMTU: + /* + * Set the interface MTU. + */ + if (ifr->ifr_mtu > ETHERMTU) { + error = EINVAL; + } else { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + + case SIOCSETVLAN: + error = copyin(ifr->ifr_data, &vlr, sizeof vlr); + if (error) + break; + if (vlr.vlr_parent[0] == '\0') { + ifv->ifv_p = 0; + if_down(ifp); + break; + } + p = ifunit(vlr.vlr_parent); + if (p == 0) { + error = ENOENT; + break; + } + error = vlan_config(ifv, p); + if (error) + break; + ifv->ifv_tag = vlr.vlr_tag; + break; + + case SIOCGETVLAN: + bzero(&vlr, sizeof vlr); + if (ifv->ifv_p) { + sprintf(vlr.vlr_parent, "%s%d", ifv->ifv_p->if_name, + ifv->ifv_p->if_unit); + vlr.vlr_tag = ifv->ifv_tag; + } + error = copyout(&vlr, ifr->ifr_data, sizeof vlr); + break; + + case SIOCSIFFLAGS: + /* + * We don't support all-multicast or promiscuous modes + * right now because it would require help from the + * underlying drivers, which hasn't been implemented. + */ + if (ifr->ifr_flags & (IFF_PROMISC|IFF_ALLMULTI)) { + ifp->if_flags &= ~(IFF_PROMISC|IFF_ALLMULTI); + error = EINVAL; + } + break; + + /* NB: this will reject multicast state changes */ + default: + error = EINVAL; + } + return error; +} + +#endif /* NVLAN > 0 */ diff --git a/sys/net/if_vlan_var.h b/sys/net/if_vlan_var.h new file mode 100644 index 00000000000..83bb41b7e20 --- /dev/null +++ b/sys/net/if_vlan_var.h @@ -0,0 +1,84 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _NET_IF_VLAN_VAR_H_ +#define _NET_IF_VLAN_VAR_H_ 1 + +#ifdef KERNEL +struct ifvlan { + struct arpcom ifv_ac; /* make this an interface */ + struct ifnet *ifv_p; /* parent inteface of this vlan */ + struct ifv_linkmib { + int ifvm_parent; + u_int16_t ifvm_proto; /* encapsulation ethertype */ + u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ + } ifv_mib; +}; +#define ifv_if ifv_ac.ac_if +#define ifv_tag ifv_mib.ifvm_tag +#endif /* KERNEL */ + +struct ether_vlan_header { + u_char evl_dhost[ETHER_ADDR_LEN]; + u_char evl_shost[ETHER_ADDR_LEN]; + u_int16_t evl_encap_proto; + u_int16_t evl_tag; + u_int16_t evl_proto; +}; + +#define EVL_VLANOFTAG(tag) ((tag) & 4095) +#define EVL_PRIOFTAG(tag) (((tag) >> 13) & 7) +#define EVL_ENCAPLEN 4 /* length in octets of encapsulation */ + +/* When these sorts of interfaces get their own identifier... */ +#define IFT_8021_VLAN IFT_PROPVIRTUAL + +/* sysctl(3) tags, for compatibility purposes */ +#define VLANCTL_PROTO 1 +#define VLANCTL_MAX 2 + +/* + * Configuration structure for SIOCSETVLAN and SIOCGETVLAN ioctls. + */ +struct vlanreq { + char vlr_parent[IFNAMSIZ]; + u_short vlr_tag; +}; +#define SIOCSETVLAN SIOCSIFGENERIC +#define SIOCGETVLAN SIOCGIFGENERIC + +#ifdef KERNEL +/* shared with if_ethersubr.c: */ +extern u_int vlan_proto; +extern int vlan_input(struct ether_header *eh, struct mbuf *m); +#endif + +#endif /* _NET_IF_VLAN_VAR_H_ */ -- 2.45.2