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