xref: /freebsd/sys/net/if_vlan.c (revision cfe8b629f1f13b06fa1aec801248b7e431e48aba)
12cc2df49SGarrett Wollman /*
22cc2df49SGarrett Wollman  * Copyright 1998 Massachusetts Institute of Technology
32cc2df49SGarrett Wollman  *
42cc2df49SGarrett Wollman  * Permission to use, copy, modify, and distribute this software and
52cc2df49SGarrett Wollman  * its documentation for any purpose and without fee is hereby
62cc2df49SGarrett Wollman  * granted, provided that both the above copyright notice and this
72cc2df49SGarrett Wollman  * permission notice appear in all copies, that both the above
82cc2df49SGarrett Wollman  * copyright notice and this permission notice appear in all
92cc2df49SGarrett Wollman  * supporting documentation, and that the name of M.I.T. not be used
102cc2df49SGarrett Wollman  * in advertising or publicity pertaining to distribution of the
112cc2df49SGarrett Wollman  * software without specific, written prior permission.  M.I.T. makes
122cc2df49SGarrett Wollman  * no representations about the suitability of this software for any
132cc2df49SGarrett Wollman  * purpose.  It is provided "as is" without express or implied
142cc2df49SGarrett Wollman  * warranty.
152cc2df49SGarrett Wollman  *
162cc2df49SGarrett Wollman  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
172cc2df49SGarrett Wollman  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
182cc2df49SGarrett Wollman  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
192cc2df49SGarrett Wollman  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
202cc2df49SGarrett Wollman  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212cc2df49SGarrett Wollman  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222cc2df49SGarrett Wollman  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
232cc2df49SGarrett Wollman  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
242cc2df49SGarrett Wollman  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
252cc2df49SGarrett Wollman  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
262cc2df49SGarrett Wollman  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
272cc2df49SGarrett Wollman  * SUCH DAMAGE.
282cc2df49SGarrett Wollman  *
29cfe8b629SGarrett Wollman  *	$Id: if_vlan.c,v 1.2 1998/05/15 20:02:47 wollman Exp $
302cc2df49SGarrett Wollman  */
312cc2df49SGarrett Wollman 
322cc2df49SGarrett Wollman /*
332cc2df49SGarrett Wollman  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
342cc2df49SGarrett Wollman  * Might be extended some day to also handle IEEE 802.1p priority
352cc2df49SGarrett Wollman  * tagging.  This is sort of sneaky in the implementation, since
362cc2df49SGarrett Wollman  * we need to pretend to be enough of an Ethernet implementation
372cc2df49SGarrett Wollman  * to make arp work.  The way we do this is by telling everyone
382cc2df49SGarrett Wollman  * that we are an Ethernet, and then catch the packets that
392cc2df49SGarrett Wollman  * ether_output() left on our output queue queue when it calls
402cc2df49SGarrett Wollman  * if_start(), rewrite them for use by the real outgoing interface,
412cc2df49SGarrett Wollman  * and ask it to send them.
422cc2df49SGarrett Wollman  */
432cc2df49SGarrett Wollman 
442cc2df49SGarrett Wollman #include "vlan.h"
452cc2df49SGarrett Wollman #if NVLAN > 0
462cc2df49SGarrett Wollman #include "opt_inet.h"
472cc2df49SGarrett Wollman #include "bpfilter.h"
482cc2df49SGarrett Wollman 
492cc2df49SGarrett Wollman #include <sys/param.h>
502cc2df49SGarrett Wollman #include <sys/kernel.h>
512cc2df49SGarrett Wollman #include <sys/mbuf.h>
522cc2df49SGarrett Wollman #include <sys/socket.h>
532cc2df49SGarrett Wollman #include <sys/sockio.h>
542cc2df49SGarrett Wollman #include <sys/sysctl.h>
552cc2df49SGarrett Wollman #include <sys/systm.h>
562cc2df49SGarrett Wollman 
572cc2df49SGarrett Wollman #if NBPFILTER > 0
582cc2df49SGarrett Wollman #include <net/bpf.h>
592cc2df49SGarrett Wollman #endif
602cc2df49SGarrett Wollman #include <net/ethernet.h>
612cc2df49SGarrett Wollman #include <net/if.h>
622cc2df49SGarrett Wollman #include <net/if_arp.h>
632cc2df49SGarrett Wollman #include <net/if_dl.h>
642cc2df49SGarrett Wollman #include <net/if_types.h>
652cc2df49SGarrett Wollman #include <net/if_vlan_var.h>
662cc2df49SGarrett Wollman 
672cc2df49SGarrett Wollman #ifdef INET
682cc2df49SGarrett Wollman #include <netinet/in.h>
692cc2df49SGarrett Wollman #include <netinet/if_ether.h>
702cc2df49SGarrett Wollman #endif
712cc2df49SGarrett Wollman 
722cc2df49SGarrett Wollman SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
732cc2df49SGarrett Wollman SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
742cc2df49SGarrett Wollman 
752cc2df49SGarrett Wollman u_int	vlan_proto = ETHERTYPE_VLAN;
762cc2df49SGarrett Wollman SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto,
772cc2df49SGarrett Wollman 	   0, "Ethernet protocol used for VLAN encapsulation");
782cc2df49SGarrett Wollman 
792cc2df49SGarrett Wollman static	struct ifvlan ifv_softc[NVLAN];
802cc2df49SGarrett Wollman 
812cc2df49SGarrett Wollman static	void vlan_start(struct ifnet *ifp);
822cc2df49SGarrett Wollman static	void vlan_ifinit(void *foo);
83cfe8b629SGarrett Wollman static	int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
842cc2df49SGarrett Wollman 
852cc2df49SGarrett Wollman static void
862cc2df49SGarrett Wollman vlaninit(void *dummy)
872cc2df49SGarrett Wollman {
882cc2df49SGarrett Wollman 	int i;
892cc2df49SGarrett Wollman 
902cc2df49SGarrett Wollman 	for (i = 0; i < NVLAN; i++) {
912cc2df49SGarrett Wollman 		struct ifnet *ifp = &ifv_softc[i].ifv_if;
922cc2df49SGarrett Wollman 
932cc2df49SGarrett Wollman 		ifp->if_softc = &ifv_softc[i];
942cc2df49SGarrett Wollman 		ifp->if_name = "vlan";
952cc2df49SGarrett Wollman 		ifp->if_unit = i;
962cc2df49SGarrett Wollman 		/* NB: flags are not set here */
972cc2df49SGarrett Wollman 		ifp->if_linkmib = &ifv_softc[i].ifv_mib;
982cc2df49SGarrett Wollman 		ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib;
992cc2df49SGarrett Wollman 		/* NB: mtu is not set here */
1002cc2df49SGarrett Wollman 
1012cc2df49SGarrett Wollman 		ifp->if_init = vlan_ifinit;
1022cc2df49SGarrett Wollman 		ifp->if_start = vlan_start;
1032cc2df49SGarrett Wollman 		ifp->if_ioctl = vlan_ioctl;
1042cc2df49SGarrett Wollman 		ifp->if_output = ether_output;
1052cc2df49SGarrett Wollman 		if_attach(ifp);
1062cc2df49SGarrett Wollman 		ether_ifattach(ifp);
1072cc2df49SGarrett Wollman #if NBPFILTER > 0
1082cc2df49SGarrett Wollman 		bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
1092cc2df49SGarrett Wollman #endif
1102cc2df49SGarrett Wollman 		/* Now undo some of the damage... */
1112cc2df49SGarrett Wollman 		ifp->if_data.ifi_type = IFT_8021_VLAN;
1122cc2df49SGarrett Wollman 		ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
1132cc2df49SGarrett Wollman 		ifp->if_resolvemulti = 0;
1142cc2df49SGarrett Wollman 	}
1152cc2df49SGarrett Wollman }
1162cc2df49SGarrett Wollman PSEUDO_SET(vlaninit, if_vlan);
1172cc2df49SGarrett Wollman 
1182cc2df49SGarrett Wollman static void
1192cc2df49SGarrett Wollman vlan_ifinit(void *foo)
1202cc2df49SGarrett Wollman {
1212cc2df49SGarrett Wollman 	;
1222cc2df49SGarrett Wollman }
1232cc2df49SGarrett Wollman 
1242cc2df49SGarrett Wollman static void
1252cc2df49SGarrett Wollman vlan_start(struct ifnet *ifp)
1262cc2df49SGarrett Wollman {
1272cc2df49SGarrett Wollman 	struct ifvlan *ifv;
1282cc2df49SGarrett Wollman 	struct ifnet *p;
1292cc2df49SGarrett Wollman 	struct ether_vlan_header *evl;
1302cc2df49SGarrett Wollman 	struct mbuf *m;
1312cc2df49SGarrett Wollman 
1322cc2df49SGarrett Wollman 	ifv = ifp->if_softc;
1332cc2df49SGarrett Wollman 	p = ifv->ifv_p;
1342cc2df49SGarrett Wollman 
1352cc2df49SGarrett Wollman 	ifp->if_flags |= IFF_OACTIVE;
1362cc2df49SGarrett Wollman 	for (;;) {
1372cc2df49SGarrett Wollman 		IF_DEQUEUE(&ifp->if_snd, m);
1382cc2df49SGarrett Wollman 		if (m == 0)
1392cc2df49SGarrett Wollman 			break;
1402cc2df49SGarrett Wollman #if NBPFILTER > 0
1412cc2df49SGarrett Wollman 		if (ifp->if_bpf)
142eb92a347SGarrett Wollman 			bpf_mtap(ifp, m);
1432cc2df49SGarrett Wollman #endif /* NBPFILTER > 0 */
1442cc2df49SGarrett Wollman 
1452cc2df49SGarrett Wollman 		M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
1462cc2df49SGarrett Wollman 		if (m == 0)
1472cc2df49SGarrett Wollman 			continue;
1482cc2df49SGarrett Wollman 		/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
1492cc2df49SGarrett Wollman 
1502cc2df49SGarrett Wollman 		/*
1512cc2df49SGarrett Wollman 		 * Transform the Ethernet header into an Ethernet header
1522cc2df49SGarrett Wollman 		 * with 802.1Q encapsulation.
1532cc2df49SGarrett Wollman 		 */
1542cc2df49SGarrett Wollman 		bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
1552cc2df49SGarrett Wollman 		      sizeof(struct ether_header));
1562cc2df49SGarrett Wollman 		evl = mtod(m, struct ether_vlan_header *);
1572cc2df49SGarrett Wollman 		evl->evl_proto = evl->evl_encap_proto;
1582cc2df49SGarrett Wollman 		evl->evl_encap_proto = htons(vlan_proto);
1592cc2df49SGarrett Wollman 		evl->evl_tag = htons(ifv->ifv_tag);
1602cc2df49SGarrett Wollman 		printf("vlan_start: %*D\n", sizeof *evl, (char *)evl, ":");
1612cc2df49SGarrett Wollman 
1622cc2df49SGarrett Wollman 		/*
1632cc2df49SGarrett Wollman 		 * Send it, precisely as ether_output() would have.
1642cc2df49SGarrett Wollman 		 * We are already running at splimp.
1652cc2df49SGarrett Wollman 		 */
1662cc2df49SGarrett Wollman 		if (IF_QFULL(&p->if_snd)) {
1672cc2df49SGarrett Wollman 			IF_DROP(&p->if_snd);
1682cc2df49SGarrett Wollman 				/* XXX stats */
1692cc2df49SGarrett Wollman 		}
1702cc2df49SGarrett Wollman 		IF_ENQUEUE(&p->if_snd, m);
1712cc2df49SGarrett Wollman 		if ((p->if_flags & IFF_OACTIVE) == 0)
1722cc2df49SGarrett Wollman 			p->if_start(p);
1732cc2df49SGarrett Wollman 	}
1742cc2df49SGarrett Wollman 	ifp->if_flags &= ~IFF_OACTIVE;
1752cc2df49SGarrett Wollman }
1762cc2df49SGarrett Wollman 
1772cc2df49SGarrett Wollman int
1782cc2df49SGarrett Wollman vlan_input(struct ether_header *eh, struct mbuf *m)
1792cc2df49SGarrett Wollman {
1802cc2df49SGarrett Wollman 	int i;
1812cc2df49SGarrett Wollman 	struct ifvlan *ifv;
1822cc2df49SGarrett Wollman 
1832cc2df49SGarrett Wollman 	for (i = 0; i < NVLAN; i++) {
1842cc2df49SGarrett Wollman 		ifv = &ifv_softc[i];
1852cc2df49SGarrett Wollman 		if (m->m_pkthdr.rcvif == ifv->ifv_p
1862cc2df49SGarrett Wollman 		    && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
1872cc2df49SGarrett Wollman 			== ifv->ifv_tag))
1882cc2df49SGarrett Wollman 			break;
1892cc2df49SGarrett Wollman 	}
1902cc2df49SGarrett Wollman 
1912cc2df49SGarrett Wollman 	if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
1922cc2df49SGarrett Wollman 		m_freem(m);
1932cc2df49SGarrett Wollman 		return -1;	/* so ether_input can take note */
1942cc2df49SGarrett Wollman 	}
1952cc2df49SGarrett Wollman 
1962cc2df49SGarrett Wollman 	/*
1972cc2df49SGarrett Wollman 	 * Having found a valid vlan interface corresponding to
1982cc2df49SGarrett Wollman 	 * the given source interface and vlan tag, remove the
1992cc2df49SGarrett Wollman 	 * encapsulation, and run the real packet through
2002cc2df49SGarrett Wollman 	 * ether_input() a second time (it had better be
2012cc2df49SGarrett Wollman 	 * reentrant!).
2022cc2df49SGarrett Wollman 	 */
2032cc2df49SGarrett Wollman 	m->m_pkthdr.rcvif = &ifv->ifv_if;
2042cc2df49SGarrett Wollman 	eh->ether_type = mtod(m, u_int16_t *)[1];
2052cc2df49SGarrett Wollman 	m->m_data += EVL_ENCAPLEN;
2062cc2df49SGarrett Wollman 	m->m_len -= EVL_ENCAPLEN;
2072cc2df49SGarrett Wollman 	m->m_pkthdr.len -= EVL_ENCAPLEN;
2082cc2df49SGarrett Wollman 
2092cc2df49SGarrett Wollman #if NBPFILTER > 0
2102cc2df49SGarrett Wollman 	if (ifv->ifv_if.if_bpf) {
2112cc2df49SGarrett Wollman 		/*
2122cc2df49SGarrett Wollman 		 * Do the usual BPF fakery.  Note that we don't support
2132cc2df49SGarrett Wollman 		 * promiscuous mode here, since it would require the
2142cc2df49SGarrett Wollman 		 * drivers to know about VLANs and we're not ready for
2152cc2df49SGarrett Wollman 		 * that yet.
2162cc2df49SGarrett Wollman 		 */
2172cc2df49SGarrett Wollman 		struct mbuf m0;
2182cc2df49SGarrett Wollman 		m0.m_next = m;
2192cc2df49SGarrett Wollman 		m0.m_len = sizeof(struct ether_header);
2202cc2df49SGarrett Wollman 		m0.m_data = (char *)eh;
2212cc2df49SGarrett Wollman 		bpf_mtap(&ifv->ifv_if, &m0);
2222cc2df49SGarrett Wollman 	}
2232cc2df49SGarrett Wollman #endif
2242cc2df49SGarrett Wollman 	ether_input(&ifv->ifv_if, eh, m);
2252cc2df49SGarrett Wollman 	return 0;
2262cc2df49SGarrett Wollman }
2272cc2df49SGarrett Wollman 
2282cc2df49SGarrett Wollman static int
2292cc2df49SGarrett Wollman vlan_config(struct ifvlan *ifv, struct ifnet *p)
2302cc2df49SGarrett Wollman {
2312cc2df49SGarrett Wollman 	struct ifaddr *ifa1, *ifa2;
2322cc2df49SGarrett Wollman 	struct sockaddr_dl *sdl1, *sdl2;
2332cc2df49SGarrett Wollman 
2342cc2df49SGarrett Wollman 	if (p->if_data.ifi_type != IFT_ETHER)
2352cc2df49SGarrett Wollman 		return EPROTONOSUPPORT;
2362cc2df49SGarrett Wollman 	if (ifv->ifv_p)
2372cc2df49SGarrett Wollman 		return EBUSY;
2382cc2df49SGarrett Wollman 	ifv->ifv_p = p;
2392cc2df49SGarrett Wollman 	if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
2402cc2df49SGarrett Wollman 		ifv->ifv_if.if_mtu = p->if_mtu;
2412cc2df49SGarrett Wollman 	else
2422cc2df49SGarrett Wollman 		ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
2432cc2df49SGarrett Wollman 
2442cc2df49SGarrett Wollman 	/*
2452cc2df49SGarrett Wollman 	 * NB: we don't support multicast at this point.
2462cc2df49SGarrett Wollman 	 */
2472cc2df49SGarrett Wollman 	ifv->ifv_if.if_flags = (p->if_flags & ~IFF_MULTICAST); /* XXX */
2482cc2df49SGarrett Wollman 
2492cc2df49SGarrett Wollman 	/*
2502cc2df49SGarrett Wollman 	 * Set up our ``Ethernet address'' to reflect the underlying
2512cc2df49SGarrett Wollman 	 * physical interface's.
2522cc2df49SGarrett Wollman 	 */
2532cc2df49SGarrett Wollman 	ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
2542cc2df49SGarrett Wollman 	ifa2 = ifnet_addrs[p->if_index - 1];
2552cc2df49SGarrett Wollman 	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
2562cc2df49SGarrett Wollman 	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
2572cc2df49SGarrett Wollman 	sdl1->sdl_type = IFT_ETHER;
2582cc2df49SGarrett Wollman 	sdl1->sdl_alen = ETHER_ADDR_LEN;
2592cc2df49SGarrett Wollman 	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
2602cc2df49SGarrett Wollman 	bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
2612cc2df49SGarrett Wollman 	return 0;
2622cc2df49SGarrett Wollman }
2632cc2df49SGarrett Wollman 
2642cc2df49SGarrett Wollman static int
265cfe8b629SGarrett Wollman vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
2662cc2df49SGarrett Wollman {
2672cc2df49SGarrett Wollman 	struct ifaddr *ifa;
2682cc2df49SGarrett Wollman 	struct ifnet *p;
2692cc2df49SGarrett Wollman 	struct ifreq *ifr;
2702cc2df49SGarrett Wollman 	struct ifvlan *ifv;
2712cc2df49SGarrett Wollman 	struct vlanreq vlr;
2722cc2df49SGarrett Wollman 	int error = 0;
2732cc2df49SGarrett Wollman 
2742cc2df49SGarrett Wollman 	ifr = (struct ifreq *)data;
2752cc2df49SGarrett Wollman 	ifa = (struct ifaddr *)data;
2762cc2df49SGarrett Wollman 	ifv = ifp->if_softc;
2772cc2df49SGarrett Wollman 
2782cc2df49SGarrett Wollman 	switch (cmd) {
2792cc2df49SGarrett Wollman 	case SIOCSIFADDR:
2802cc2df49SGarrett Wollman 		ifp->if_flags |= IFF_UP;
2812cc2df49SGarrett Wollman 
2822cc2df49SGarrett Wollman 		switch (ifa->ifa_addr->sa_family) {
2832cc2df49SGarrett Wollman #ifdef INET
2842cc2df49SGarrett Wollman 		case AF_INET:
2852cc2df49SGarrett Wollman 			arp_ifinit(&ifv->ifv_ac, ifa);
2862cc2df49SGarrett Wollman 			break;
2872cc2df49SGarrett Wollman #endif
2882cc2df49SGarrett Wollman 		default:
2892cc2df49SGarrett Wollman 			break;
2902cc2df49SGarrett Wollman 		}
2912cc2df49SGarrett Wollman 		break;
2922cc2df49SGarrett Wollman 
2932cc2df49SGarrett Wollman 	case SIOCGIFADDR:
2942cc2df49SGarrett Wollman 		{
2952cc2df49SGarrett Wollman 			struct sockaddr *sa;
2962cc2df49SGarrett Wollman 
2972cc2df49SGarrett Wollman 			sa = (struct sockaddr *) &ifr->ifr_data;
2982cc2df49SGarrett Wollman 			bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
2992cc2df49SGarrett Wollman 			      (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
3002cc2df49SGarrett Wollman 		}
3012cc2df49SGarrett Wollman 		break;
3022cc2df49SGarrett Wollman 
3032cc2df49SGarrett Wollman 	case SIOCSIFMTU:
3042cc2df49SGarrett Wollman 		/*
3052cc2df49SGarrett Wollman 		 * Set the interface MTU.
3062cc2df49SGarrett Wollman 		 */
3072cc2df49SGarrett Wollman 		if (ifr->ifr_mtu > ETHERMTU) {
3082cc2df49SGarrett Wollman 			error = EINVAL;
3092cc2df49SGarrett Wollman 		} else {
3102cc2df49SGarrett Wollman 			ifp->if_mtu = ifr->ifr_mtu;
3112cc2df49SGarrett Wollman 		}
3122cc2df49SGarrett Wollman 		break;
3132cc2df49SGarrett Wollman 
3142cc2df49SGarrett Wollman 	case SIOCSETVLAN:
3152cc2df49SGarrett Wollman 		error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
3162cc2df49SGarrett Wollman 		if (error)
3172cc2df49SGarrett Wollman 			break;
3182cc2df49SGarrett Wollman 		if (vlr.vlr_parent[0] == '\0') {
3192cc2df49SGarrett Wollman 			ifv->ifv_p = 0;
3202cc2df49SGarrett Wollman 			if_down(ifp);
3212cc2df49SGarrett Wollman 			break;
3222cc2df49SGarrett Wollman 		}
3232cc2df49SGarrett Wollman 		p = ifunit(vlr.vlr_parent);
3242cc2df49SGarrett Wollman 		if (p == 0) {
3252cc2df49SGarrett Wollman 			error = ENOENT;
3262cc2df49SGarrett Wollman 			break;
3272cc2df49SGarrett Wollman 		}
3282cc2df49SGarrett Wollman 		error = vlan_config(ifv, p);
3292cc2df49SGarrett Wollman 		if (error)
3302cc2df49SGarrett Wollman 			break;
3312cc2df49SGarrett Wollman 		ifv->ifv_tag = vlr.vlr_tag;
3322cc2df49SGarrett Wollman 		break;
3332cc2df49SGarrett Wollman 
3342cc2df49SGarrett Wollman 	case SIOCGETVLAN:
3352cc2df49SGarrett Wollman 		bzero(&vlr, sizeof vlr);
3362cc2df49SGarrett Wollman 		if (ifv->ifv_p) {
3372cc2df49SGarrett Wollman 			sprintf(vlr.vlr_parent, "%s%d", ifv->ifv_p->if_name,
3382cc2df49SGarrett Wollman 				ifv->ifv_p->if_unit);
3392cc2df49SGarrett Wollman 			vlr.vlr_tag = ifv->ifv_tag;
3402cc2df49SGarrett Wollman 		}
3412cc2df49SGarrett Wollman 		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
3422cc2df49SGarrett Wollman 		break;
3432cc2df49SGarrett Wollman 
3442cc2df49SGarrett Wollman 	case SIOCSIFFLAGS:
3452cc2df49SGarrett Wollman 		/*
3462cc2df49SGarrett Wollman 		 * We don't support all-multicast or promiscuous modes
3472cc2df49SGarrett Wollman 		 * right now because it would require help from the
3482cc2df49SGarrett Wollman 		 * underlying drivers, which hasn't been implemented.
3492cc2df49SGarrett Wollman 		 */
3502cc2df49SGarrett Wollman 		if (ifr->ifr_flags & (IFF_PROMISC|IFF_ALLMULTI)) {
3512cc2df49SGarrett Wollman 			ifp->if_flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
3522cc2df49SGarrett Wollman 			error = EINVAL;
3532cc2df49SGarrett Wollman 		}
3542cc2df49SGarrett Wollman 		break;
3552cc2df49SGarrett Wollman 
3562cc2df49SGarrett Wollman 		/* NB: this will reject multicast state changes */
3572cc2df49SGarrett Wollman 	default:
3582cc2df49SGarrett Wollman 		error = EINVAL;
3592cc2df49SGarrett Wollman 	}
3602cc2df49SGarrett Wollman 	return error;
3612cc2df49SGarrett Wollman }
3622cc2df49SGarrett Wollman 
3632cc2df49SGarrett Wollman #endif /* NVLAN > 0 */
364