xref: /netbsd/sys/net/if_vlan.c (revision a5028df3)
1*a5028df3Senami /*	$NetBSD: if_vlan.c,v 1.7 2000/09/28 08:28:56 enami Exp $	*/
200746cb0Sthorpej 
300746cb0Sthorpej /*-
400746cb0Sthorpej  * Copyright (c) 2000 The NetBSD Foundation, Inc.
500746cb0Sthorpej  * All rights reserved.
600746cb0Sthorpej  *
700746cb0Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
800746cb0Sthorpej  * by Andrew Doran and Jason R. Thorpe.
900746cb0Sthorpej  *
1000746cb0Sthorpej  * Redistribution and use in source and binary forms, with or without
1100746cb0Sthorpej  * modification, are permitted provided that the following conditions
1200746cb0Sthorpej  * are met:
1300746cb0Sthorpej  * 1. Redistributions of source code must retain the above copyright
1400746cb0Sthorpej  *    notice, this list of conditions and the following disclaimer.
1500746cb0Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
1600746cb0Sthorpej  *    notice, this list of conditions and the following disclaimer in the
1700746cb0Sthorpej  *    documentation and/or other materials provided with the distribution.
1800746cb0Sthorpej  * 3. All advertising materials mentioning features or use of this software
1900746cb0Sthorpej  *    must display the following acknowledgement:
2000746cb0Sthorpej  *	This product includes software developed by the NetBSD
2100746cb0Sthorpej  *	Foundation, Inc. and its contributors.
2200746cb0Sthorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
2300746cb0Sthorpej  *    contributors may be used to endorse or promote products derived
2400746cb0Sthorpej  *    from this software without specific prior written permission.
2500746cb0Sthorpej  *
2600746cb0Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2700746cb0Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2800746cb0Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2900746cb0Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3000746cb0Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3100746cb0Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3200746cb0Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3300746cb0Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3400746cb0Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3500746cb0Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3600746cb0Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
3700746cb0Sthorpej  */
3800746cb0Sthorpej 
3900746cb0Sthorpej /*
4000746cb0Sthorpej  * Copyright 1998 Massachusetts Institute of Technology
4100746cb0Sthorpej  *
4200746cb0Sthorpej  * Permission to use, copy, modify, and distribute this software and
4300746cb0Sthorpej  * its documentation for any purpose and without fee is hereby
4400746cb0Sthorpej  * granted, provided that both the above copyright notice and this
4500746cb0Sthorpej  * permission notice appear in all copies, that both the above
4600746cb0Sthorpej  * copyright notice and this permission notice appear in all
4700746cb0Sthorpej  * supporting documentation, and that the name of M.I.T. not be used
4800746cb0Sthorpej  * in advertising or publicity pertaining to distribution of the
4900746cb0Sthorpej  * software without specific, written prior permission.  M.I.T. makes
5000746cb0Sthorpej  * no representations about the suitability of this software for any
5100746cb0Sthorpej  * purpose.  It is provided "as is" without express or implied
5200746cb0Sthorpej  * warranty.
5300746cb0Sthorpej  *
5400746cb0Sthorpej  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
5500746cb0Sthorpej  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
5600746cb0Sthorpej  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
5700746cb0Sthorpej  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
5800746cb0Sthorpej  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
5900746cb0Sthorpej  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
6000746cb0Sthorpej  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
6100746cb0Sthorpej  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
6200746cb0Sthorpej  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
6300746cb0Sthorpej  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
6400746cb0Sthorpej  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6500746cb0Sthorpej  * SUCH DAMAGE.
6600746cb0Sthorpej  *
6700746cb0Sthorpej  * from FreeBSD: if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp
6800746cb0Sthorpej  * via OpenBSD: if_vlan.c,v 1.4 2000/05/15 19:15:00 chris Exp
6900746cb0Sthorpej  */
7000746cb0Sthorpej 
7100746cb0Sthorpej /*
7200746cb0Sthorpej  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.  Might be
7300746cb0Sthorpej  * extended some day to also handle IEEE 802.1P priority tagging.  This is
7400746cb0Sthorpej  * sort of sneaky in the implementation, since we need to pretend to be
7500746cb0Sthorpej  * enough of an Ethernet implementation to make ARP work.  The way we do
7600746cb0Sthorpej  * this is by telling everyone that we are an Ethernet interface, and then
7700746cb0Sthorpej  * catch the packets that ether_output() left on our output queue when it
7800746cb0Sthorpej  * calls if_start(), rewrite them for use by the real outgoing interface,
7900746cb0Sthorpej  * and ask it to send them.
8000746cb0Sthorpej  *
8100746cb0Sthorpej  * TODO:
8200746cb0Sthorpej  *
8300746cb0Sthorpej  *	- Need some way to notify vlan interfaces when the parent
8400746cb0Sthorpej  *	  interface changes MTU.
8500746cb0Sthorpej  *
8600746cb0Sthorpej  *	- Need to make promiscuous mode work.
8700746cb0Sthorpej  */
8800746cb0Sthorpej 
8900746cb0Sthorpej #include "opt_inet.h"
9000746cb0Sthorpej #include "bpfilter.h"
9100746cb0Sthorpej 
9200746cb0Sthorpej #include <sys/param.h>
9300746cb0Sthorpej #include <sys/kernel.h>
9400746cb0Sthorpej #include <sys/mbuf.h>
9500746cb0Sthorpej #include <sys/queue.h>
9600746cb0Sthorpej #include <sys/socket.h>
9700746cb0Sthorpej #include <sys/sockio.h>
9800746cb0Sthorpej #include <sys/systm.h>
9900746cb0Sthorpej #include <sys/proc.h>
10000746cb0Sthorpej 
10100746cb0Sthorpej #if NBPFILTER > 0
10200746cb0Sthorpej #include <net/bpf.h>
10300746cb0Sthorpej #endif
10400746cb0Sthorpej #include <net/if.h>
10500746cb0Sthorpej #include <net/if_dl.h>
10600746cb0Sthorpej #include <net/if_types.h>
10700746cb0Sthorpej #include <net/if_ether.h>
10800746cb0Sthorpej #include <net/if_vlanvar.h>
10900746cb0Sthorpej 
11000746cb0Sthorpej #ifdef INET
11100746cb0Sthorpej #include <netinet/in.h>
11200746cb0Sthorpej #include <netinet/if_inarp.h>
11300746cb0Sthorpej #endif
11400746cb0Sthorpej 
11500746cb0Sthorpej extern struct	ifaddr **ifnet_addrs;	/* XXX if.c */
11600746cb0Sthorpej 
11700746cb0Sthorpej static int	vlan_clone_create(struct if_clone *, int);
11800746cb0Sthorpej static void	vlan_clone_destroy(struct ifnet *);
11900746cb0Sthorpej static int	vlan_config(struct ifvlan *, struct ifnet *);
12000746cb0Sthorpej static int	vlan_ioctl(struct ifnet *, u_long, caddr_t);
1218e1cd4f3Senami static int	vlan_addmulti(struct ifvlan *, struct ifreq *);
1228e1cd4f3Senami static int	vlan_delmulti(struct ifvlan *, struct ifreq *);
1238e1cd4f3Senami static void	vlan_purgemulti(struct ifvlan *);
12400746cb0Sthorpej static void	vlan_start(struct ifnet *);
12500746cb0Sthorpej static int	vlan_unconfig(struct ifnet *);
12600746cb0Sthorpej void	vlanattach(int);
12700746cb0Sthorpej 
12800746cb0Sthorpej /* XXX This should be a hash table with the tag as the basis of the key. */
12900746cb0Sthorpej static LIST_HEAD(, ifvlan) ifv_list;
13000746cb0Sthorpej 
13100746cb0Sthorpej struct if_clone vlan_cloner =
13200746cb0Sthorpej     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
13300746cb0Sthorpej 
13400746cb0Sthorpej void
13500746cb0Sthorpej vlanattach(int n)
13600746cb0Sthorpej {
13700746cb0Sthorpej 
13800746cb0Sthorpej 	LIST_INIT(&ifv_list);
13900746cb0Sthorpej 	if_clone_attach(&vlan_cloner);
14000746cb0Sthorpej }
14100746cb0Sthorpej 
14200746cb0Sthorpej static int
14300746cb0Sthorpej vlan_clone_create(struct if_clone *ifc, int unit)
14400746cb0Sthorpej {
14500746cb0Sthorpej 	struct ifvlan *ifv;
14600746cb0Sthorpej 	struct ifnet *ifp;
147dcf72a77Senami 	u_int8_t eaddr[ETHER_ADDR_LEN];
14800746cb0Sthorpej 
14900746cb0Sthorpej 	ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAIT);
15000746cb0Sthorpej 	memset(ifv, 0, sizeof(struct ifvlan));
15100746cb0Sthorpej 	ifp = &ifv->ifv_ec.ec_if;
1528e1cd4f3Senami 	LIST_INIT(&ifv->ifv_mc_listhead);
15300746cb0Sthorpej 	LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
15400746cb0Sthorpej 
15500746cb0Sthorpej 	sprintf(ifp->if_xname, "%s%d", ifc->ifc_name, unit);
15600746cb0Sthorpej 	ifp->if_softc = ifv;
15700746cb0Sthorpej 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
15800746cb0Sthorpej 	ifp->if_start = vlan_start;
15900746cb0Sthorpej 	ifp->if_ioctl = vlan_ioctl;
16000746cb0Sthorpej 
16100746cb0Sthorpej 	if_attach(ifp);
16200746cb0Sthorpej 	memset(eaddr, 0, sizeof(eaddr));
16300746cb0Sthorpej 	ether_ifattach(ifp, eaddr);
16400746cb0Sthorpej 
16500746cb0Sthorpej 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
16600746cb0Sthorpej 	ifp->if_mtu = ETHERMTU - EVL_ENCAPLEN;
16700746cb0Sthorpej 
16800746cb0Sthorpej #if NBPFILTER > 0
16900746cb0Sthorpej 	bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB,
17000746cb0Sthorpej 	    sizeof(struct ether_header));
17100746cb0Sthorpej #endif
17200746cb0Sthorpej 
17300746cb0Sthorpej 	return (0);
17400746cb0Sthorpej }
17500746cb0Sthorpej 
17600746cb0Sthorpej static void
17700746cb0Sthorpej vlan_clone_destroy(struct ifnet *ifp)
17800746cb0Sthorpej {
17900746cb0Sthorpej 	struct ifvlan *ifv;
18000746cb0Sthorpej 	int s;
18100746cb0Sthorpej 
18200746cb0Sthorpej 	ifv = (struct ifvlan *)ifp->if_softc;
18300746cb0Sthorpej 	s = splsoftnet();
18400746cb0Sthorpej 
18500746cb0Sthorpej 	LIST_REMOVE(ifv, ifv_list);
18600746cb0Sthorpej 	vlan_unconfig(ifp);
18700746cb0Sthorpej 
18800746cb0Sthorpej #if NBPFILTER > 0
18900746cb0Sthorpej 	bpfdetach(ifp);
19000746cb0Sthorpej #endif
19100746cb0Sthorpej 	ether_ifdetach(ifp);
19200746cb0Sthorpej 	if_detach(ifp);
19300746cb0Sthorpej 	free(ifv, M_DEVBUF);
19400746cb0Sthorpej 
19500746cb0Sthorpej 	splx(s);
19600746cb0Sthorpej }
19700746cb0Sthorpej 
19800746cb0Sthorpej static int
19900746cb0Sthorpej vlan_config(struct ifvlan *ifv, struct ifnet *p)
20000746cb0Sthorpej {
20100746cb0Sthorpej 	struct ifaddr *ifa1, *ifa2;
20200746cb0Sthorpej 	struct sockaddr_dl *sdl1, *sdl2;
20300746cb0Sthorpej 
20400746cb0Sthorpej 	if (p->if_data.ifi_type != IFT_ETHER)
20500746cb0Sthorpej 		return (EPROTONOSUPPORT);
20600746cb0Sthorpej 	if (ifv->ifv_p != NULL)
20700746cb0Sthorpej 		return (EBUSY);
20800746cb0Sthorpej 	ifv->ifv_p = p;
20900746cb0Sthorpej 	ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
21000746cb0Sthorpej 	ifv->ifv_if.if_flags = p->if_flags;
21100746cb0Sthorpej 
21200746cb0Sthorpej 	/*
21300746cb0Sthorpej 	 * Set up our ``Ethernet address'' to match the underlying
21400746cb0Sthorpej 	 * physical interface's.
21500746cb0Sthorpej 	 */
21600746cb0Sthorpej 	ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
21700746cb0Sthorpej 	ifa2 = ifnet_addrs[p->if_index];
21800746cb0Sthorpej 	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
21900746cb0Sthorpej 	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
22000746cb0Sthorpej 	sdl1->sdl_type = IFT_ETHER;
22100746cb0Sthorpej 	sdl1->sdl_alen = ETHER_ADDR_LEN;
22200746cb0Sthorpej 	memcpy(LLADDR(sdl1), LLADDR(sdl2), ETHER_ADDR_LEN);
22300746cb0Sthorpej 	memcpy(LLADDR(ifv->ifv_ec.ec_if.if_sadl), LLADDR(sdl2), ETHER_ADDR_LEN);
22400746cb0Sthorpej 	return (0);
22500746cb0Sthorpej }
22600746cb0Sthorpej 
22700746cb0Sthorpej static int
22800746cb0Sthorpej vlan_unconfig(struct ifnet *ifp)
22900746cb0Sthorpej {
23000746cb0Sthorpej 	struct ifaddr *ifa;
23100746cb0Sthorpej 	struct sockaddr_dl *sdl;
23200746cb0Sthorpej 	struct ifvlan *ifv;
2338e1cd4f3Senami 	int s;
23400746cb0Sthorpej 
23500746cb0Sthorpej 	ifv = ifp->if_softc;
236*a5028df3Senami 	if (ifv->ifv_p == NULL)
237*a5028df3Senami 		return (0);
238*a5028df3Senami 
239*a5028df3Senami 	s = splsoftnet();
24000746cb0Sthorpej 
24100746cb0Sthorpej 	/*
24200746cb0Sthorpej  	 * Since the interface is being unconfigured, we need to empty the
24300746cb0Sthorpej 	 * list of multicast groups that we may have joined while we were
24400746cb0Sthorpej 	 * alive and remove them from the parent's list also.
24500746cb0Sthorpej 	 */
2468e1cd4f3Senami 	vlan_purgemulti(ifv);
24700746cb0Sthorpej 
24800746cb0Sthorpej 	/* Disconnect from parent. */
24900746cb0Sthorpej 	ifv->ifv_p = NULL;
25000746cb0Sthorpej 	ifv->ifv_if.if_mtu = ETHERMTU - EVL_ENCAPLEN;
25100746cb0Sthorpej 
25200746cb0Sthorpej 	/* Clear our MAC address. */
25300746cb0Sthorpej 	ifa = ifnet_addrs[ifv->ifv_if.if_index];
25400746cb0Sthorpej 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
25500746cb0Sthorpej 	sdl->sdl_type = IFT_ETHER;
25600746cb0Sthorpej 	sdl->sdl_alen = ETHER_ADDR_LEN;
25700746cb0Sthorpej 	memset(LLADDR(sdl), 0, ETHER_ADDR_LEN);
25800746cb0Sthorpej 	memset(LLADDR(ifv->ifv_ec.ec_if.if_sadl), 0, ETHER_ADDR_LEN);
25900746cb0Sthorpej 
26000746cb0Sthorpej 	splx(s);
26100746cb0Sthorpej 	return (0);
26200746cb0Sthorpej }
26300746cb0Sthorpej 
26400746cb0Sthorpej static int
26500746cb0Sthorpej vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
26600746cb0Sthorpej {
26700746cb0Sthorpej 	struct proc *p = curproc;	/* XXX */
26800746cb0Sthorpej 	struct ifaddr *ifa;
26900746cb0Sthorpej 	struct ifnet *pr;
27000746cb0Sthorpej 	struct ifreq *ifr;
27100746cb0Sthorpej 	struct ifvlan *ifv;
27200746cb0Sthorpej 	struct vlanreq vlr;
27300746cb0Sthorpej 	struct sockaddr *sa;
27400746cb0Sthorpej 	int error;
27500746cb0Sthorpej 
27600746cb0Sthorpej 	error = 0;
27700746cb0Sthorpej 	ifr = (struct ifreq *)data;
27800746cb0Sthorpej 	ifa = (struct ifaddr *)data;
27900746cb0Sthorpej 	ifv = ifp->if_softc;
28000746cb0Sthorpej 
28100746cb0Sthorpej 	switch (cmd) {
28200746cb0Sthorpej 	case SIOCSIFADDR:
28300746cb0Sthorpej 		ifp->if_flags |= IFF_UP;
28400746cb0Sthorpej 
28500746cb0Sthorpej 		switch (ifa->ifa_addr->sa_family) {
28600746cb0Sthorpej #ifdef INET
28700746cb0Sthorpej 		case AF_INET:
28800746cb0Sthorpej 			arp_ifinit(ifp, ifa);
28900746cb0Sthorpej 			break;
29000746cb0Sthorpej #endif
29100746cb0Sthorpej 		default:
29200746cb0Sthorpej 			break;
29300746cb0Sthorpej 		}
29400746cb0Sthorpej 		break;
29500746cb0Sthorpej 
29600746cb0Sthorpej 	case SIOCGIFADDR:
29700746cb0Sthorpej 		sa = (struct sockaddr *)&ifr->ifr_data;
29800746cb0Sthorpej 		memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
29900746cb0Sthorpej 		break;
30000746cb0Sthorpej 
30100746cb0Sthorpej 	case SIOCSIFMTU:
30200746cb0Sthorpej 		if (ifv->ifv_p != NULL) {
30300746cb0Sthorpej 			if (ifr->ifr_mtu > ifv->ifv_p->if_mtu - EVL_ENCAPLEN ||
30400746cb0Sthorpej 			    ifr->ifr_mtu < ETHERMIN + EVL_ENCAPLEN)
30500746cb0Sthorpej 				error = EINVAL;
30600746cb0Sthorpej 			else
30700746cb0Sthorpej 				ifp->if_mtu = ifr->ifr_mtu;
30800746cb0Sthorpej 		} else
30900746cb0Sthorpej 			error = EINVAL;
31000746cb0Sthorpej 		break;
31100746cb0Sthorpej 
31200746cb0Sthorpej 	case SIOCSETVLAN:
31300746cb0Sthorpej 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
31400746cb0Sthorpej 			break;
31500746cb0Sthorpej 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
31600746cb0Sthorpej 			break;
31700746cb0Sthorpej 		if (vlr.vlr_parent[0] == '\0') {
31800746cb0Sthorpej 			vlan_unconfig(ifp);
31900746cb0Sthorpej 			if_down(ifp);
32000746cb0Sthorpej 			ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
32100746cb0Sthorpej 			break;
32200746cb0Sthorpej 		}
32300746cb0Sthorpej 		if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
32400746cb0Sthorpej 			error = EINVAL;		 /* check for valid tag */
32500746cb0Sthorpej 			break;
32600746cb0Sthorpej 		}
32700746cb0Sthorpej 		if ((pr = ifunit(vlr.vlr_parent)) == 0) {
32800746cb0Sthorpej 			error = ENOENT;
32900746cb0Sthorpej 			break;
33000746cb0Sthorpej 		}
33100746cb0Sthorpej 		if ((error = vlan_config(ifv, pr)) != 0)
33200746cb0Sthorpej 			break;
33300746cb0Sthorpej 		ifv->ifv_tag = vlr.vlr_tag;
33400746cb0Sthorpej 		ifp->if_flags |= IFF_RUNNING;
33500746cb0Sthorpej 		break;
33600746cb0Sthorpej 
33700746cb0Sthorpej 	case SIOCGETVLAN:
33800746cb0Sthorpej 		memset(&vlr, 0, sizeof(vlr));
33900746cb0Sthorpej 		if (ifv->ifv_p != NULL) {
34000746cb0Sthorpej 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
34100746cb0Sthorpej 			    ifv->ifv_p->if_xname);
34200746cb0Sthorpej 			vlr.vlr_tag = ifv->ifv_tag;
34300746cb0Sthorpej 		}
34400746cb0Sthorpej 		error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
34500746cb0Sthorpej 		break;
34600746cb0Sthorpej 
34700746cb0Sthorpej 	case SIOCSIFFLAGS:
34800746cb0Sthorpej 		/*
34900746cb0Sthorpej 		 * XXX We don't support promiscuous mode right now because
35000746cb0Sthorpej 		 * it would require help from the underlying drivers, which
35100746cb0Sthorpej 		 * hasn't been implemented.
35200746cb0Sthorpej 		 */
35300746cb0Sthorpej 		if ((ifr->ifr_flags & IFF_PROMISC) != 0) {
35400746cb0Sthorpej 			ifp->if_flags &= ~(IFF_PROMISC);
35500746cb0Sthorpej 			error = EINVAL;
35600746cb0Sthorpej 		}
35700746cb0Sthorpej 		break;
35800746cb0Sthorpej 
35900746cb0Sthorpej 	case SIOCADDMULTI:
3608e1cd4f3Senami 		error = vlan_addmulti(ifv, ifr);
361177a2587Senami 		break;
362177a2587Senami 
363177a2587Senami 	case SIOCDELMULTI:
3648e1cd4f3Senami 		error = vlan_delmulti(ifv, ifr);
36500746cb0Sthorpej 		break;
36600746cb0Sthorpej 
36700746cb0Sthorpej 	default:
36800746cb0Sthorpej 		error = EINVAL;
36900746cb0Sthorpej 	}
37000746cb0Sthorpej 
37100746cb0Sthorpej 	return (error);
37200746cb0Sthorpej }
37300746cb0Sthorpej 
37400746cb0Sthorpej static int
3758e1cd4f3Senami vlan_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
37600746cb0Sthorpej {
37700746cb0Sthorpej 	struct vlan_mc_entry *mc;
3788e1cd4f3Senami 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
37900746cb0Sthorpej 	int error;
38000746cb0Sthorpej 
3818e1cd4f3Senami 	if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
3828e1cd4f3Senami 		return (EINVAL);
38300746cb0Sthorpej 
3848e1cd4f3Senami 	error = ether_addmulti(ifr, &ifv->ifv_ec);
3858e1cd4f3Senami 	if (error != ENETRESET)
38600746cb0Sthorpej 		return (error);
3878e1cd4f3Senami 
3888e1cd4f3Senami 	/*
3898e1cd4f3Senami 	 * This is new multicast address.  We have to tell parent
3908e1cd4f3Senami 	 * about it.  Also, remember this multicast address so that
3918e1cd4f3Senami 	 * we can delete them on unconfigure.
3928e1cd4f3Senami 	 */
3938e1cd4f3Senami 	MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
3948e1cd4f3Senami 	    M_DEVBUF, M_NOWAIT);
3958e1cd4f3Senami 	if (mc == NULL) {
3968e1cd4f3Senami 		error = ENOMEM;
3978e1cd4f3Senami 		goto alloc_failed;
39800746cb0Sthorpej 	}
39900746cb0Sthorpej 
4008e1cd4f3Senami 	/*
4018e1cd4f3Senami 	 * As ether_addmulti() returns ENETRESET, following two
4028e1cd4f3Senami 	 * statement shouldn't fail.
4038e1cd4f3Senami 	 */
4048e1cd4f3Senami 	(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
4058e1cd4f3Senami 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm);
4068e1cd4f3Senami 	memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
4078e1cd4f3Senami 	LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
4088e1cd4f3Senami 
4098e1cd4f3Senami 	error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI,
4108e1cd4f3Senami 	    (caddr_t)ifr);
4118e1cd4f3Senami 	if (error != 0)
4128e1cd4f3Senami 		goto ioctl_failed;
41300746cb0Sthorpej 	return (error);
4148e1cd4f3Senami 
4158e1cd4f3Senami  ioctl_failed:
4168e1cd4f3Senami 	LIST_REMOVE(mc, mc_entries);
4178e1cd4f3Senami 	FREE(mc, M_DEVBUF);
4188e1cd4f3Senami  alloc_failed:
4198e1cd4f3Senami 	(void)ether_delmulti(ifr, &ifv->ifv_ec);
4208e1cd4f3Senami 	return (error);
42100746cb0Sthorpej }
42200746cb0Sthorpej 
4238e1cd4f3Senami static int
4248e1cd4f3Senami vlan_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
4258e1cd4f3Senami {
4268e1cd4f3Senami 	struct ether_multi *enm;
4278e1cd4f3Senami 	struct vlan_mc_entry *mc;
4288e1cd4f3Senami 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
4298e1cd4f3Senami 	int error;
4308e1cd4f3Senami 
4318e1cd4f3Senami 	/*
4328e1cd4f3Senami 	 * Find a key to lookup vlan_mc_entry.  We have to do this
4338e1cd4f3Senami 	 * before calling ether_delmulti for obvious reason.
4348e1cd4f3Senami 	 */
4358e1cd4f3Senami 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
4368e1cd4f3Senami 		return (error);
4378e1cd4f3Senami 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm);
4388e1cd4f3Senami 
4398e1cd4f3Senami 	error = ether_delmulti(ifr, &ifv->ifv_ec);
4408e1cd4f3Senami 	if (error != ENETRESET)
4418e1cd4f3Senami 		return (error);
4428e1cd4f3Senami 
4438e1cd4f3Senami 	/* We no longer use this multicast address.  Tell parent so. */
4448e1cd4f3Senami 	error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI,
4458e1cd4f3Senami 	    (caddr_t)ifr);
4468e1cd4f3Senami 	if (error == 0) {
4478e1cd4f3Senami 		/* And forget about this address. */
4488e1cd4f3Senami 		for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL;
4498e1cd4f3Senami 		    mc = LIST_NEXT(mc, mc_entries)) {
4508e1cd4f3Senami 			if (mc->mc_enm == enm) {
4518e1cd4f3Senami 				LIST_REMOVE(mc, mc_entries);
4528e1cd4f3Senami 				FREE(mc, M_DEVBUF);
4538e1cd4f3Senami 				break;
4548e1cd4f3Senami 			}
4558e1cd4f3Senami 		}
4568e1cd4f3Senami 		KASSERT(mc != NULL);
4578e1cd4f3Senami 	} else
4588e1cd4f3Senami 		(void)ether_addmulti(ifr, &ifv->ifv_ec);
4598e1cd4f3Senami 	return (error);
4608e1cd4f3Senami }
4618e1cd4f3Senami 
4628e1cd4f3Senami /*
4638e1cd4f3Senami  * Delete any multicast address we have asked to add form parent
4648e1cd4f3Senami  * interface.  Called when the vlan is being unconfigured.
4658e1cd4f3Senami  */
4668e1cd4f3Senami static void
4678e1cd4f3Senami vlan_purgemulti(struct ifvlan *ifv)
4688e1cd4f3Senami {
4698e1cd4f3Senami 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
4708e1cd4f3Senami 	struct vlan_mc_entry *mc;
4718e1cd4f3Senami 	union {
4728e1cd4f3Senami 		struct ifreq ifreq;
4738e1cd4f3Senami 		struct {
4748e1cd4f3Senami 			char ifr_name[IFNAMSIZ];
4758e1cd4f3Senami 			struct sockaddr_storage;
4768e1cd4f3Senami 		} ifreq_storage;
4778e1cd4f3Senami 	} ifreq;
4788e1cd4f3Senami 	struct ifreq *ifr = &ifreq.ifreq;
4798e1cd4f3Senami 
4808e1cd4f3Senami 	memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
4818e1cd4f3Senami 	while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
4828e1cd4f3Senami 		memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
4838e1cd4f3Senami 		(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
4848e1cd4f3Senami 		LIST_REMOVE(mc->mc_enm, enm_list);
4858e1cd4f3Senami 		free(mc->mc_enm, M_IFMADDR);
4868e1cd4f3Senami 		LIST_REMOVE(mc, mc_entries);
4878e1cd4f3Senami 		FREE(mc, M_DEVBUF);
4888e1cd4f3Senami 	}
4898e1cd4f3Senami 
4908e1cd4f3Senami 	KASSERT(LIST_FIRST(&ifv->ifv_ec.ec_multiaddrs) == NULL);
49100746cb0Sthorpej }
49200746cb0Sthorpej 
49300746cb0Sthorpej static void
49400746cb0Sthorpej vlan_start(struct ifnet *ifp)
49500746cb0Sthorpej {
49600746cb0Sthorpej 	struct ifvlan *ifv;
49700746cb0Sthorpej 	struct ifnet *p;
49800746cb0Sthorpej 	struct ether_vlan_header *evl;
49900746cb0Sthorpej 	struct mbuf *m;
50000746cb0Sthorpej 
50100746cb0Sthorpej 	ifv = ifp->if_softc;
50200746cb0Sthorpej 	p = ifv->ifv_p;
50300746cb0Sthorpej 	ifp->if_flags |= IFF_OACTIVE;
50400746cb0Sthorpej 
50500746cb0Sthorpej 	for (;;) {
50600746cb0Sthorpej 		IF_DEQUEUE(&ifp->if_snd, m);
50700746cb0Sthorpej 		if (m == NULL)
50800746cb0Sthorpej 			break;
50900746cb0Sthorpej 
51000746cb0Sthorpej #if NBPFILTER > 0
51100746cb0Sthorpej 		if (ifp->if_bpf)
51200746cb0Sthorpej 			bpf_mtap(ifp->if_bpf, m);
51300746cb0Sthorpej #endif
51400746cb0Sthorpej 
51500746cb0Sthorpej 		/*
51600746cb0Sthorpej 		 * XXX Should handle the case where the underlying hardware
51700746cb0Sthorpej 		 * interface can do VLAN tag insertion itself.
51800746cb0Sthorpej 		 */
51900746cb0Sthorpej 		M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
52000746cb0Sthorpej 		if (m == NULL) {
52100746cb0Sthorpej 			printf("%s: M_PREPEND failed", ifv->ifv_p->if_xname);
52200746cb0Sthorpej 			ifp->if_ierrors++;
52300746cb0Sthorpej 			continue;
52400746cb0Sthorpej 		}
52500746cb0Sthorpej 
52600746cb0Sthorpej 		if (m->m_len < sizeof(struct ether_vlan_header) &&
52700746cb0Sthorpej 		    (m = m_pullup(m,
52800746cb0Sthorpej 		     sizeof(struct ether_vlan_header))) == NULL) {
52900746cb0Sthorpej 			printf("%s: m_pullup failed", ifv->ifv_p->if_xname);
53000746cb0Sthorpej 			ifp->if_ierrors++;
53100746cb0Sthorpej 			continue;
53200746cb0Sthorpej 		}
53300746cb0Sthorpej 
53400746cb0Sthorpej 		/*
53500746cb0Sthorpej 		 * Transform the Ethernet header into an Ethernet header
53600746cb0Sthorpej 		 * with 802.1Q encapsulation.
53700746cb0Sthorpej 		 */
53800746cb0Sthorpej 		memmove(mtod(m, caddr_t), mtod(m, caddr_t) + EVL_ENCAPLEN,
53900746cb0Sthorpej 		    sizeof(struct ether_header));
54000746cb0Sthorpej 		evl = mtod(m, struct ether_vlan_header *);
54100746cb0Sthorpej 		evl->evl_proto = evl->evl_encap_proto;
54200746cb0Sthorpej 		evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
54300746cb0Sthorpej 		evl->evl_tag = htons(ifv->ifv_tag);
54400746cb0Sthorpej 
54500746cb0Sthorpej 		/*
54600746cb0Sthorpej 		 * Send it, precisely as ether_output() would have.  We are
54700746cb0Sthorpej 		 * already running at splimp.
54800746cb0Sthorpej 		 */
54900746cb0Sthorpej 		if (IF_QFULL(&p->if_snd)) {
55000746cb0Sthorpej 			IF_DROP(&p->if_snd);
55100746cb0Sthorpej 			/* XXX stats */
55200746cb0Sthorpej 			ifp->if_oerrors++;
55300746cb0Sthorpej 			m_freem(m);
55400746cb0Sthorpej 			continue;
55500746cb0Sthorpej 		}
55600746cb0Sthorpej 
55700746cb0Sthorpej 		IF_ENQUEUE(&p->if_snd, m);
55800746cb0Sthorpej 		if ((p->if_flags & IFF_OACTIVE) == 0) {
55900746cb0Sthorpej 			p->if_start(p);
56000746cb0Sthorpej 			ifp->if_opackets++;
56100746cb0Sthorpej 		}
56200746cb0Sthorpej 	}
56300746cb0Sthorpej 
56400746cb0Sthorpej 	ifp->if_flags &= ~IFF_OACTIVE;
56500746cb0Sthorpej }
56600746cb0Sthorpej 
56700746cb0Sthorpej /*
56800746cb0Sthorpej  * Given an Ethernet frame, find a valid vlan interface corresponding to the
56900746cb0Sthorpej  * given source interface and tag, then run the the real packet through
57000746cb0Sthorpej  * the parent's input routine.
57100746cb0Sthorpej  */
57200746cb0Sthorpej void
57300746cb0Sthorpej vlan_input(struct ifnet *ifp, struct mbuf *m)
57400746cb0Sthorpej {
57500746cb0Sthorpej 	struct ether_vlan_header *evl;
57600746cb0Sthorpej 	struct ifvlan *ifv;
57700746cb0Sthorpej 	u_int tag;
57800746cb0Sthorpej 
57900746cb0Sthorpej 	if (m->m_len < sizeof(struct ether_vlan_header) &&
58000746cb0Sthorpej 	    (m = m_pullup(m, sizeof(struct ether_vlan_header))) == NULL) {
58100746cb0Sthorpej 		printf("%s: no memory for VLAN header, dropping packet.\n",
58200746cb0Sthorpej 		    ifp->if_xname);
58300746cb0Sthorpej 		return;
58400746cb0Sthorpej 	}
58500746cb0Sthorpej 	evl = mtod(m, struct ether_vlan_header *);
58600746cb0Sthorpej 	KASSERT(htons(evl->evl_encap_proto) == ETHERTYPE_VLAN);
58700746cb0Sthorpej 
58800746cb0Sthorpej 	tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
58900746cb0Sthorpej 
59000746cb0Sthorpej 	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
59100746cb0Sthorpej 	    ifv = LIST_NEXT(ifv, ifv_list))
59200746cb0Sthorpej 		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
59300746cb0Sthorpej 			break;
59400746cb0Sthorpej 
59500746cb0Sthorpej 	if (ifv == NULL || (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
59600746cb0Sthorpej 	    (IFF_UP|IFF_RUNNING)) {
59700746cb0Sthorpej 		m_free(m);
59800746cb0Sthorpej 		ifp->if_data.ifi_noproto++;
59900746cb0Sthorpej 		return;
60000746cb0Sthorpej 	}
60100746cb0Sthorpej 
60200746cb0Sthorpej 	/*
60300746cb0Sthorpej 	 * Having found a valid vlan interface corresponding to the given
60400746cb0Sthorpej 	 * source interface and vlan tag, remove the encapsulation.
60500746cb0Sthorpej 	 */
60600746cb0Sthorpej 	evl->evl_encap_proto = evl->evl_proto;
60700746cb0Sthorpej 	memmove(mtod(m, caddr_t) + EVL_ENCAPLEN, mtod(m, caddr_t),
60800746cb0Sthorpej 	    EVL_ENCAPLEN);
60900746cb0Sthorpej 	m_adj(m, EVL_ENCAPLEN);
61000746cb0Sthorpej 
61100746cb0Sthorpej 	m->m_pkthdr.rcvif = &ifv->ifv_if;
61200746cb0Sthorpej 	ifv->ifv_if.if_ipackets++;
61300746cb0Sthorpej 
61400746cb0Sthorpej #if NBPFILTER > 0
61500746cb0Sthorpej 	if (ifv->ifv_if.if_bpf)
61600746cb0Sthorpej 		bpf_mtap(ifv->ifv_if.if_bpf, m);
61700746cb0Sthorpej #endif
61800746cb0Sthorpej 
61900746cb0Sthorpej 	/* Pass it back through the parent's input routine. */
62000746cb0Sthorpej 	(*ifp->if_input)(&ifv->ifv_if, m);
62100746cb0Sthorpej }
622