1*874fef37Selad /* $NetBSD: if_vlan.c,v 1.48 2006/05/14 21:19:33 elad Exp $ */ 200746cb0Sthorpej 300746cb0Sthorpej /*- 45cbdcc50Sthorpej * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc. 500746cb0Sthorpej * All rights reserved. 600746cb0Sthorpej * 700746cb0Sthorpej * This code is derived from software contributed to The NetBSD Foundation 8efbead50Sthorpej * by Andrew Doran, and by Jason R. Thorpe of Zembu Labs, Inc. 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 8734d65a34Slukem #include <sys/cdefs.h> 88*874fef37Selad __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.48 2006/05/14 21:19:33 elad Exp $"); 8934d65a34Slukem 9000746cb0Sthorpej #include "opt_inet.h" 9100746cb0Sthorpej #include "bpfilter.h" 9200746cb0Sthorpej 9300746cb0Sthorpej #include <sys/param.h> 9400746cb0Sthorpej #include <sys/kernel.h> 9500746cb0Sthorpej #include <sys/mbuf.h> 9600746cb0Sthorpej #include <sys/queue.h> 9700746cb0Sthorpej #include <sys/socket.h> 9800746cb0Sthorpej #include <sys/sockio.h> 9900746cb0Sthorpej #include <sys/systm.h> 10000746cb0Sthorpej #include <sys/proc.h> 101*874fef37Selad #include <sys/kauth.h> 10200746cb0Sthorpej 10300746cb0Sthorpej #if NBPFILTER > 0 10400746cb0Sthorpej #include <net/bpf.h> 10500746cb0Sthorpej #endif 10600746cb0Sthorpej #include <net/if.h> 10700746cb0Sthorpej #include <net/if_dl.h> 10800746cb0Sthorpej #include <net/if_types.h> 10900746cb0Sthorpej #include <net/if_ether.h> 11000746cb0Sthorpej #include <net/if_vlanvar.h> 11100746cb0Sthorpej 11200746cb0Sthorpej #ifdef INET 11300746cb0Sthorpej #include <netinet/in.h> 11400746cb0Sthorpej #include <netinet/if_inarp.h> 11500746cb0Sthorpej #endif 11600746cb0Sthorpej 11796375e93Sthorpej struct vlan_mc_entry { 11896375e93Sthorpej LIST_ENTRY(vlan_mc_entry) mc_entries; 11996375e93Sthorpej /* 12096375e93Sthorpej * A key to identify this entry. The mc_addr below can't be 12196375e93Sthorpej * used since multiple sockaddr may mapped into the same 12296375e93Sthorpej * ether_multi (e.g., AF_UNSPEC). 12396375e93Sthorpej */ 12496375e93Sthorpej union { 12596375e93Sthorpej struct ether_multi *mcu_enm; 12696375e93Sthorpej } mc_u; 12796375e93Sthorpej struct sockaddr_storage mc_addr; 12896375e93Sthorpej }; 12996375e93Sthorpej 13096375e93Sthorpej #define mc_enm mc_u.mcu_enm 13196375e93Sthorpej 13296375e93Sthorpej struct ifvlan { 13396375e93Sthorpej union { 13496375e93Sthorpej struct ethercom ifvu_ec; 13596375e93Sthorpej } ifv_u; 13696375e93Sthorpej struct ifnet *ifv_p; /* parent interface of this vlan */ 13796375e93Sthorpej struct ifv_linkmib { 13896375e93Sthorpej const struct vlan_multisw *ifvm_msw; 13996375e93Sthorpej int ifvm_encaplen; /* encapsulation length */ 14096375e93Sthorpej int ifvm_mtufudge; /* MTU fudged by this much */ 14196375e93Sthorpej int ifvm_mintu; /* min transmission unit */ 14296375e93Sthorpej u_int16_t ifvm_proto; /* encapsulation ethertype */ 14396375e93Sthorpej u_int16_t ifvm_tag; /* tag to apply on packets */ 14496375e93Sthorpej } ifv_mib; 14596375e93Sthorpej LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead; 14696375e93Sthorpej LIST_ENTRY(ifvlan) ifv_list; 147c1e8f104Sthorpej int ifv_flags; 14896375e93Sthorpej }; 14996375e93Sthorpej 150c1e8f104Sthorpej #define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ 151c1e8f104Sthorpej 15296375e93Sthorpej #define ifv_ec ifv_u.ifvu_ec 15396375e93Sthorpej 15496375e93Sthorpej #define ifv_if ifv_ec.ec_if 15596375e93Sthorpej 15696375e93Sthorpej #define ifv_msw ifv_mib.ifvm_msw 15796375e93Sthorpej #define ifv_encaplen ifv_mib.ifvm_encaplen 15896375e93Sthorpej #define ifv_mtufudge ifv_mib.ifvm_mtufudge 15996375e93Sthorpej #define ifv_mintu ifv_mib.ifvm_mintu 16096375e93Sthorpej #define ifv_tag ifv_mib.ifvm_tag 16196375e93Sthorpej 16296375e93Sthorpej struct vlan_multisw { 16396375e93Sthorpej int (*vmsw_addmulti)(struct ifvlan *, struct ifreq *); 16496375e93Sthorpej int (*vmsw_delmulti)(struct ifvlan *, struct ifreq *); 16596375e93Sthorpej void (*vmsw_purgemulti)(struct ifvlan *); 16696375e93Sthorpej }; 16796375e93Sthorpej 16896375e93Sthorpej static int vlan_ether_addmulti(struct ifvlan *, struct ifreq *); 16996375e93Sthorpej static int vlan_ether_delmulti(struct ifvlan *, struct ifreq *); 17096375e93Sthorpej static void vlan_ether_purgemulti(struct ifvlan *); 17196375e93Sthorpej 17296375e93Sthorpej const struct vlan_multisw vlan_ether_multisw = { 17396375e93Sthorpej vlan_ether_addmulti, 17496375e93Sthorpej vlan_ether_delmulti, 17596375e93Sthorpej vlan_ether_purgemulti, 17696375e93Sthorpej }; 17796375e93Sthorpej 17800746cb0Sthorpej static int vlan_clone_create(struct if_clone *, int); 179b9c49ebfSpeter static int vlan_clone_destroy(struct ifnet *); 18000746cb0Sthorpej static int vlan_config(struct ifvlan *, struct ifnet *); 18100746cb0Sthorpej static int vlan_ioctl(struct ifnet *, u_long, caddr_t); 18200746cb0Sthorpej static void vlan_start(struct ifnet *); 183efbead50Sthorpej static void vlan_unconfig(struct ifnet *); 18496375e93Sthorpej 18500746cb0Sthorpej void vlanattach(int); 18600746cb0Sthorpej 18700746cb0Sthorpej /* XXX This should be a hash table with the tag as the basis of the key. */ 18800746cb0Sthorpej static LIST_HEAD(, ifvlan) ifv_list; 18900746cb0Sthorpej 19000746cb0Sthorpej struct if_clone vlan_cloner = 19100746cb0Sthorpej IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy); 19200746cb0Sthorpej 193a9760a89Sscw /* Used to pad ethernet frames with < ETHER_MIN_LEN bytes */ 194a9760a89Sscw static char vlan_zero_pad_buff[ETHER_MIN_LEN]; 195a9760a89Sscw 19600746cb0Sthorpej void 19700746cb0Sthorpej vlanattach(int n) 19800746cb0Sthorpej { 19900746cb0Sthorpej 20000746cb0Sthorpej LIST_INIT(&ifv_list); 20100746cb0Sthorpej if_clone_attach(&vlan_cloner); 20200746cb0Sthorpej } 20300746cb0Sthorpej 20453c23f54Sthorpej static void 20553c23f54Sthorpej vlan_reset_linkname(struct ifnet *ifp) 20653c23f54Sthorpej { 20753c23f54Sthorpej 20853c23f54Sthorpej /* 20953c23f54Sthorpej * We start out with a "802.1Q VLAN" type and zero-length 21053c23f54Sthorpej * addresses. When we attach to a parent interface, we 21153c23f54Sthorpej * inherit its type, address length, address, and data link 21253c23f54Sthorpej * type. 21353c23f54Sthorpej */ 21453c23f54Sthorpej 21553c23f54Sthorpej ifp->if_type = IFT_L2VLAN; 21653c23f54Sthorpej ifp->if_addrlen = 0; 21753c23f54Sthorpej ifp->if_dlt = DLT_NULL; 21853c23f54Sthorpej if_alloc_sadl(ifp); 21953c23f54Sthorpej } 22053c23f54Sthorpej 22100746cb0Sthorpej static int 22200746cb0Sthorpej vlan_clone_create(struct if_clone *ifc, int unit) 22300746cb0Sthorpej { 22400746cb0Sthorpej struct ifvlan *ifv; 22500746cb0Sthorpej struct ifnet *ifp; 226efbead50Sthorpej int s; 22700746cb0Sthorpej 22896375e93Sthorpej ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK); 22900746cb0Sthorpej memset(ifv, 0, sizeof(struct ifvlan)); 230b817ce40Senami ifp = &ifv->ifv_if; 2318e1cd4f3Senami LIST_INIT(&ifv->ifv_mc_listhead); 232efbead50Sthorpej 233efbead50Sthorpej s = splnet(); 23400746cb0Sthorpej LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 235efbead50Sthorpej splx(s); 23600746cb0Sthorpej 237d2f1c029Sitojun snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", ifc->ifc_name, 238d2f1c029Sitojun unit); 23900746cb0Sthorpej ifp->if_softc = ifv; 24000746cb0Sthorpej ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 24100746cb0Sthorpej ifp->if_start = vlan_start; 24200746cb0Sthorpej ifp->if_ioctl = vlan_ioctl; 2435cbdcc50Sthorpej IFQ_SET_READY(&ifp->if_snd); 24400746cb0Sthorpej 24500746cb0Sthorpej if_attach(ifp); 24653c23f54Sthorpej vlan_reset_linkname(ifp); 24700746cb0Sthorpej 24800746cb0Sthorpej return (0); 24900746cb0Sthorpej } 25000746cb0Sthorpej 251b9c49ebfSpeter static int 25200746cb0Sthorpej vlan_clone_destroy(struct ifnet *ifp) 25300746cb0Sthorpej { 254efbead50Sthorpej struct ifvlan *ifv = ifp->if_softc; 25500746cb0Sthorpej int s; 25600746cb0Sthorpej 257efbead50Sthorpej s = splnet(); 25800746cb0Sthorpej LIST_REMOVE(ifv, ifv_list); 25900746cb0Sthorpej vlan_unconfig(ifp); 260efbead50Sthorpej splx(s); 26100746cb0Sthorpej 26200746cb0Sthorpej if_detach(ifp); 26300746cb0Sthorpej free(ifv, M_DEVBUF); 264b9c49ebfSpeter 265b9c49ebfSpeter return (0); 26600746cb0Sthorpej } 26700746cb0Sthorpej 268efbead50Sthorpej /* 269efbead50Sthorpej * Configure a VLAN interface. Must be called at splnet(). 270efbead50Sthorpej */ 27100746cb0Sthorpej static int 27200746cb0Sthorpej vlan_config(struct ifvlan *ifv, struct ifnet *p) 27300746cb0Sthorpej { 27496375e93Sthorpej struct ifnet *ifp = &ifv->ifv_if; 27596375e93Sthorpej int error; 27600746cb0Sthorpej 27700746cb0Sthorpej if (ifv->ifv_p != NULL) 27800746cb0Sthorpej return (EBUSY); 27996375e93Sthorpej 28096375e93Sthorpej switch (p->if_type) { 28196375e93Sthorpej case IFT_ETHER: 28296375e93Sthorpej { 28396375e93Sthorpej struct ethercom *ec = (void *) p; 28496375e93Sthorpej 28596375e93Sthorpej ifv->ifv_msw = &vlan_ether_multisw; 28696375e93Sthorpej ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 28796375e93Sthorpej ifv->ifv_mintu = ETHERMIN; 28896375e93Sthorpej 28996375e93Sthorpej /* 29096375e93Sthorpej * If the parent supports the VLAN_MTU capability, 29196375e93Sthorpej * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 29296375e93Sthorpej * enable it. 29396375e93Sthorpej */ 29496375e93Sthorpej if (ec->ec_nvlans++ == 0 && 29596375e93Sthorpej (ec->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) { 29696375e93Sthorpej /* 29796375e93Sthorpej * Enable Tx/Rx of VLAN-sized frames. 29896375e93Sthorpej */ 29996375e93Sthorpej ec->ec_capenable |= ETHERCAP_VLAN_MTU; 30096375e93Sthorpej if (p->if_flags & IFF_UP) { 30196375e93Sthorpej struct ifreq ifr; 30296375e93Sthorpej 30396375e93Sthorpej ifr.ifr_flags = p->if_flags; 30496375e93Sthorpej error = (*p->if_ioctl)(p, SIOCSIFFLAGS, 30596375e93Sthorpej (caddr_t) &ifr); 30696375e93Sthorpej if (error) { 30796375e93Sthorpej if (ec->ec_nvlans-- == 1) 30896375e93Sthorpej ec->ec_capenable &= 30996375e93Sthorpej ~ETHERCAP_VLAN_MTU; 31096375e93Sthorpej return (error); 31196375e93Sthorpej } 31296375e93Sthorpej } 31396375e93Sthorpej ifv->ifv_mtufudge = 0; 31496375e93Sthorpej } else if ((ec->ec_capabilities & ETHERCAP_VLAN_MTU) == 0) { 31596375e93Sthorpej /* 31696375e93Sthorpej * Fudge the MTU by the encapsulation size. This 31796375e93Sthorpej * makes us incompatible with strictly compliant 31896375e93Sthorpej * 802.1Q implementations, but allows us to use 31996375e93Sthorpej * the feature with other NetBSD implementations, 32096375e93Sthorpej * which might still be useful. 32196375e93Sthorpej */ 32296375e93Sthorpej ifv->ifv_mtufudge = ifv->ifv_encaplen; 32396375e93Sthorpej } 32496375e93Sthorpej 32596375e93Sthorpej /* 32642a2e688Sthorpej * If the parent interface can do hardware-assisted 32742a2e688Sthorpej * VLAN encapsulation, then propagate its hardware- 32842a2e688Sthorpej * assisted checksumming flags. 32942a2e688Sthorpej */ 33042a2e688Sthorpej if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) 33142a2e688Sthorpej ifp->if_capabilities = p->if_capabilities & 332330cc0a1Syamt (IFCAP_CSUM_IPv4_Tx|IFCAP_CSUM_IPv4_Rx| 333330cc0a1Syamt IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_TCPv4_Rx| 334330cc0a1Syamt IFCAP_CSUM_UDPv4_Tx|IFCAP_CSUM_UDPv4_Rx| 335330cc0a1Syamt IFCAP_CSUM_TCPv6_Tx|IFCAP_CSUM_TCPv6_Rx| 336330cc0a1Syamt IFCAP_CSUM_UDPv6_Tx|IFCAP_CSUM_UDPv6_Rx); 33742a2e688Sthorpej 33842a2e688Sthorpej /* 33996375e93Sthorpej * We inherit the parent's Ethernet address. 34096375e93Sthorpej */ 34196375e93Sthorpej ether_ifattach(ifp, LLADDR(p->if_sadl)); 34296375e93Sthorpej ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */ 34396375e93Sthorpej break; 34496375e93Sthorpej } 34596375e93Sthorpej 34696375e93Sthorpej default: 34796375e93Sthorpej return (EPROTONOSUPPORT); 34896375e93Sthorpej } 34996375e93Sthorpej 35000746cb0Sthorpej ifv->ifv_p = p; 35196375e93Sthorpej ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge; 3523659b305Sbouyer ifv->ifv_if.if_flags = p->if_flags & 3530d0fd771Sbouyer (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 35400746cb0Sthorpej 35500746cb0Sthorpej /* 35696375e93Sthorpej * Inherit the if_type from the parent. This allows us 35796375e93Sthorpej * to participate in bridges of that type. 35800746cb0Sthorpej */ 35996375e93Sthorpej ifv->ifv_if.if_type = p->if_type; 36096375e93Sthorpej 36100746cb0Sthorpej return (0); 36200746cb0Sthorpej } 36300746cb0Sthorpej 364efbead50Sthorpej /* 365efbead50Sthorpej * Unconfigure a VLAN interface. Must be called at splnet(). 366efbead50Sthorpej */ 367efbead50Sthorpej static void 36800746cb0Sthorpej vlan_unconfig(struct ifnet *ifp) 36900746cb0Sthorpej { 37096375e93Sthorpej struct ifvlan *ifv = ifp->if_softc; 37100746cb0Sthorpej 372a5028df3Senami if (ifv->ifv_p == NULL) 373efbead50Sthorpej return; 37400746cb0Sthorpej 37500746cb0Sthorpej /* 37600746cb0Sthorpej * Since the interface is being unconfigured, we need to empty the 37700746cb0Sthorpej * list of multicast groups that we may have joined while we were 37800746cb0Sthorpej * alive and remove them from the parent's list also. 37900746cb0Sthorpej */ 38096375e93Sthorpej (*ifv->ifv_msw->vmsw_purgemulti)(ifv); 38100746cb0Sthorpej 38200746cb0Sthorpej /* Disconnect from parent. */ 38396375e93Sthorpej switch (ifv->ifv_p->if_type) { 38496375e93Sthorpej case IFT_ETHER: 38596375e93Sthorpej { 38696375e93Sthorpej struct ethercom *ec = (void *) ifv->ifv_p; 38700746cb0Sthorpej 38896375e93Sthorpej if (ec->ec_nvlans-- == 1) { 38996375e93Sthorpej /* 39096375e93Sthorpej * Disable Tx/Rx of VLAN-sized frames. 39196375e93Sthorpej */ 39296375e93Sthorpej ec->ec_capenable &= ~ETHERCAP_VLAN_MTU; 39396375e93Sthorpej if (ifv->ifv_p->if_flags & IFF_UP) { 39496375e93Sthorpej struct ifreq ifr; 39596375e93Sthorpej 39696375e93Sthorpej ifr.ifr_flags = ifv->ifv_p->if_flags; 39796375e93Sthorpej (void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, 39896375e93Sthorpej SIOCSIFFLAGS, (caddr_t) &ifr); 39996375e93Sthorpej } 40096375e93Sthorpej } 40196375e93Sthorpej 40296375e93Sthorpej ether_ifdetach(ifp); 40353c23f54Sthorpej vlan_reset_linkname(ifp); 40496375e93Sthorpej break; 40596375e93Sthorpej } 40696375e93Sthorpej 40796375e93Sthorpej #ifdef DIAGNOSTIC 40896375e93Sthorpej default: 40996375e93Sthorpej panic("vlan_unconfig: impossible"); 41096375e93Sthorpej #endif 41196375e93Sthorpej } 41296375e93Sthorpej 41396375e93Sthorpej ifv->ifv_p = NULL; 41496375e93Sthorpej ifv->ifv_if.if_mtu = 0; 415c1e8f104Sthorpej ifv->ifv_flags = 0; 41600746cb0Sthorpej 417efbead50Sthorpej if_down(ifp); 418efbead50Sthorpej ifp->if_flags &= ~(IFF_UP|IFF_RUNNING); 41942a2e688Sthorpej ifp->if_capabilities = 0; 420efbead50Sthorpej } 421efbead50Sthorpej 422efbead50Sthorpej /* 423efbead50Sthorpej * Called when a parent interface is detaching; destroy any VLAN 424efbead50Sthorpej * configuration for the parent interface. 425efbead50Sthorpej */ 426efbead50Sthorpej void 427efbead50Sthorpej vlan_ifdetach(struct ifnet *p) 428efbead50Sthorpej { 429efbead50Sthorpej struct ifvlan *ifv; 430efbead50Sthorpej int s; 431efbead50Sthorpej 432efbead50Sthorpej s = splnet(); 433efbead50Sthorpej 434efbead50Sthorpej for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 435efbead50Sthorpej ifv = LIST_NEXT(ifv, ifv_list)) { 436efbead50Sthorpej if (ifv->ifv_p == p) 437efbead50Sthorpej vlan_unconfig(&ifv->ifv_if); 438efbead50Sthorpej } 439efbead50Sthorpej 44000746cb0Sthorpej splx(s); 44100746cb0Sthorpej } 44200746cb0Sthorpej 44300746cb0Sthorpej static int 444c1e8f104Sthorpej vlan_set_promisc(struct ifnet *ifp) 445c1e8f104Sthorpej { 446c1e8f104Sthorpej struct ifvlan *ifv = ifp->if_softc; 447d93a3cbcSenami int error = 0; 448c1e8f104Sthorpej 449c1e8f104Sthorpej if ((ifp->if_flags & IFF_PROMISC) != 0) { 450c1e8f104Sthorpej if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { 451c1e8f104Sthorpej error = ifpromisc(ifv->ifv_p, 1); 452c1e8f104Sthorpej if (error == 0) 453c1e8f104Sthorpej ifv->ifv_flags |= IFVF_PROMISC; 454c1e8f104Sthorpej } 455c1e8f104Sthorpej } else { 456c1e8f104Sthorpej if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { 457c1e8f104Sthorpej error = ifpromisc(ifv->ifv_p, 0); 458c1e8f104Sthorpej if (error == 0) 459c1e8f104Sthorpej ifv->ifv_flags &= ~IFVF_PROMISC; 460c1e8f104Sthorpej } 461c1e8f104Sthorpej } 462c1e8f104Sthorpej 463c1e8f104Sthorpej return (error); 464c1e8f104Sthorpej } 465c1e8f104Sthorpej 466c1e8f104Sthorpej static int 46700746cb0Sthorpej vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 46800746cb0Sthorpej { 46900746cb0Sthorpej struct proc *p = curproc; /* XXX */ 470efbead50Sthorpej struct ifvlan *ifv = ifp->if_softc; 471efbead50Sthorpej struct ifaddr *ifa = (struct ifaddr *) data; 472efbead50Sthorpej struct ifreq *ifr = (struct ifreq *) data; 47300746cb0Sthorpej struct ifnet *pr; 47400746cb0Sthorpej struct vlanreq vlr; 47500746cb0Sthorpej struct sockaddr *sa; 476efbead50Sthorpej int s, error = 0; 47700746cb0Sthorpej 478efbead50Sthorpej s = splnet(); 47900746cb0Sthorpej 48000746cb0Sthorpej switch (cmd) { 48100746cb0Sthorpej case SIOCSIFADDR: 48229fdeefaSbouyer if (ifv->ifv_p != NULL) { 48300746cb0Sthorpej ifp->if_flags |= IFF_UP; 48400746cb0Sthorpej 48500746cb0Sthorpej switch (ifa->ifa_addr->sa_family) { 48600746cb0Sthorpej #ifdef INET 48700746cb0Sthorpej case AF_INET: 48800746cb0Sthorpej arp_ifinit(ifp, ifa); 48900746cb0Sthorpej break; 49000746cb0Sthorpej #endif 49100746cb0Sthorpej default: 49200746cb0Sthorpej break; 49300746cb0Sthorpej } 49429fdeefaSbouyer } else { 49529fdeefaSbouyer error = EINVAL; 49629fdeefaSbouyer } 49700746cb0Sthorpej break; 49800746cb0Sthorpej 49900746cb0Sthorpej case SIOCGIFADDR: 50000746cb0Sthorpej sa = (struct sockaddr *)&ifr->ifr_data; 501a4ecde8cSthorpej memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ifp->if_addrlen); 50200746cb0Sthorpej break; 50300746cb0Sthorpej 50400746cb0Sthorpej case SIOCSIFMTU: 50500746cb0Sthorpej if (ifv->ifv_p != NULL) { 50696375e93Sthorpej if (ifr->ifr_mtu > 50796375e93Sthorpej (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) || 50896375e93Sthorpej ifr->ifr_mtu < 50996375e93Sthorpej (ifv->ifv_mintu - ifv->ifv_mtufudge)) 51000746cb0Sthorpej error = EINVAL; 51100746cb0Sthorpej else 51200746cb0Sthorpej ifp->if_mtu = ifr->ifr_mtu; 51300746cb0Sthorpej } else 51400746cb0Sthorpej error = EINVAL; 51500746cb0Sthorpej break; 51600746cb0Sthorpej 51700746cb0Sthorpej case SIOCSETVLAN: 518*874fef37Selad if ((error = kauth_authorize_generic(p->p_cred, KAUTH_GENERIC_ISSUSER, &p->p_acflag)) != 0) 51900746cb0Sthorpej break; 52000746cb0Sthorpej if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0) 52100746cb0Sthorpej break; 52200746cb0Sthorpej if (vlr.vlr_parent[0] == '\0') { 52300746cb0Sthorpej vlan_unconfig(ifp); 52400746cb0Sthorpej break; 52500746cb0Sthorpej } 52600746cb0Sthorpej if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) { 52700746cb0Sthorpej error = EINVAL; /* check for valid tag */ 52800746cb0Sthorpej break; 52900746cb0Sthorpej } 53000746cb0Sthorpej if ((pr = ifunit(vlr.vlr_parent)) == 0) { 53100746cb0Sthorpej error = ENOENT; 53200746cb0Sthorpej break; 53300746cb0Sthorpej } 53400746cb0Sthorpej if ((error = vlan_config(ifv, pr)) != 0) 53500746cb0Sthorpej break; 53600746cb0Sthorpej ifv->ifv_tag = vlr.vlr_tag; 53700746cb0Sthorpej ifp->if_flags |= IFF_RUNNING; 538c1e8f104Sthorpej 539c1e8f104Sthorpej /* Update promiscuous mode, if necessary. */ 540c1e8f104Sthorpej vlan_set_promisc(ifp); 54100746cb0Sthorpej break; 54200746cb0Sthorpej 54300746cb0Sthorpej case SIOCGETVLAN: 54400746cb0Sthorpej memset(&vlr, 0, sizeof(vlr)); 54500746cb0Sthorpej if (ifv->ifv_p != NULL) { 54600746cb0Sthorpej snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s", 54700746cb0Sthorpej ifv->ifv_p->if_xname); 54800746cb0Sthorpej vlr.vlr_tag = ifv->ifv_tag; 54900746cb0Sthorpej } 55000746cb0Sthorpej error = copyout(&vlr, ifr->ifr_data, sizeof(vlr)); 55100746cb0Sthorpej break; 55200746cb0Sthorpej 55300746cb0Sthorpej case SIOCSIFFLAGS: 55400746cb0Sthorpej /* 555c1e8f104Sthorpej * For promiscuous mode, we enable promiscuous mode on 556c1e8f104Sthorpej * the parent if we need promiscuous on the VLAN interface. 55700746cb0Sthorpej */ 558c1e8f104Sthorpej if (ifv->ifv_p != NULL) 559c1e8f104Sthorpej error = vlan_set_promisc(ifp); 56000746cb0Sthorpej break; 56100746cb0Sthorpej 56200746cb0Sthorpej case SIOCADDMULTI: 56323df92e3Sthorpej error = (ifv->ifv_p != NULL) ? 56423df92e3Sthorpej (*ifv->ifv_msw->vmsw_addmulti)(ifv, ifr) : EINVAL; 565177a2587Senami break; 566177a2587Senami 567177a2587Senami case SIOCDELMULTI: 56823df92e3Sthorpej error = (ifv->ifv_p != NULL) ? 56923df92e3Sthorpej (*ifv->ifv_msw->vmsw_delmulti)(ifv, ifr) : EINVAL; 57000746cb0Sthorpej break; 57100746cb0Sthorpej 57200746cb0Sthorpej default: 57300746cb0Sthorpej error = EINVAL; 57400746cb0Sthorpej } 57500746cb0Sthorpej 576efbead50Sthorpej splx(s); 577efbead50Sthorpej 57800746cb0Sthorpej return (error); 57900746cb0Sthorpej } 58000746cb0Sthorpej 58100746cb0Sthorpej static int 58296375e93Sthorpej vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr) 58300746cb0Sthorpej { 58400746cb0Sthorpej struct vlan_mc_entry *mc; 5858e1cd4f3Senami u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 58600746cb0Sthorpej int error; 58700746cb0Sthorpej 5888e1cd4f3Senami if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage)) 5898e1cd4f3Senami return (EINVAL); 59000746cb0Sthorpej 5918e1cd4f3Senami error = ether_addmulti(ifr, &ifv->ifv_ec); 5928e1cd4f3Senami if (error != ENETRESET) 59300746cb0Sthorpej return (error); 5948e1cd4f3Senami 5958e1cd4f3Senami /* 5968e1cd4f3Senami * This is new multicast address. We have to tell parent 5978e1cd4f3Senami * about it. Also, remember this multicast address so that 5988e1cd4f3Senami * we can delete them on unconfigure. 5998e1cd4f3Senami */ 6008e1cd4f3Senami MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry), 6018e1cd4f3Senami M_DEVBUF, M_NOWAIT); 6028e1cd4f3Senami if (mc == NULL) { 6038e1cd4f3Senami error = ENOMEM; 6048e1cd4f3Senami goto alloc_failed; 60500746cb0Sthorpej } 60600746cb0Sthorpej 6078e1cd4f3Senami /* 6088e1cd4f3Senami * As ether_addmulti() returns ENETRESET, following two 6098e1cd4f3Senami * statement shouldn't fail. 6108e1cd4f3Senami */ 6118e1cd4f3Senami (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi); 6128e1cd4f3Senami ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm); 6138e1cd4f3Senami memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len); 6148e1cd4f3Senami LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries); 6158e1cd4f3Senami 6168e1cd4f3Senami error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI, 6178e1cd4f3Senami (caddr_t)ifr); 6188e1cd4f3Senami if (error != 0) 6198e1cd4f3Senami goto ioctl_failed; 62000746cb0Sthorpej return (error); 6218e1cd4f3Senami 6228e1cd4f3Senami ioctl_failed: 6238e1cd4f3Senami LIST_REMOVE(mc, mc_entries); 6248e1cd4f3Senami FREE(mc, M_DEVBUF); 6258e1cd4f3Senami alloc_failed: 6268e1cd4f3Senami (void)ether_delmulti(ifr, &ifv->ifv_ec); 6278e1cd4f3Senami return (error); 62800746cb0Sthorpej } 62900746cb0Sthorpej 6308e1cd4f3Senami static int 63196375e93Sthorpej vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr) 6328e1cd4f3Senami { 6338e1cd4f3Senami struct ether_multi *enm; 6348e1cd4f3Senami struct vlan_mc_entry *mc; 6358e1cd4f3Senami u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 6368e1cd4f3Senami int error; 6378e1cd4f3Senami 6388e1cd4f3Senami /* 6398e1cd4f3Senami * Find a key to lookup vlan_mc_entry. We have to do this 6408e1cd4f3Senami * before calling ether_delmulti for obvious reason. 6418e1cd4f3Senami */ 6428e1cd4f3Senami if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0) 6438e1cd4f3Senami return (error); 6448e1cd4f3Senami ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm); 6458e1cd4f3Senami 6468e1cd4f3Senami error = ether_delmulti(ifr, &ifv->ifv_ec); 6478e1cd4f3Senami if (error != ENETRESET) 6488e1cd4f3Senami return (error); 6498e1cd4f3Senami 6508e1cd4f3Senami /* We no longer use this multicast address. Tell parent so. */ 6518e1cd4f3Senami error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI, 6528e1cd4f3Senami (caddr_t)ifr); 6538e1cd4f3Senami if (error == 0) { 6548e1cd4f3Senami /* And forget about this address. */ 6558e1cd4f3Senami for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL; 6568e1cd4f3Senami mc = LIST_NEXT(mc, mc_entries)) { 6578e1cd4f3Senami if (mc->mc_enm == enm) { 6588e1cd4f3Senami LIST_REMOVE(mc, mc_entries); 6598e1cd4f3Senami FREE(mc, M_DEVBUF); 6608e1cd4f3Senami break; 6618e1cd4f3Senami } 6628e1cd4f3Senami } 6638e1cd4f3Senami KASSERT(mc != NULL); 6648e1cd4f3Senami } else 6658e1cd4f3Senami (void)ether_addmulti(ifr, &ifv->ifv_ec); 6668e1cd4f3Senami return (error); 6678e1cd4f3Senami } 6688e1cd4f3Senami 6698e1cd4f3Senami /* 670dc325578Spooka * Delete any multicast address we have asked to add from parent 6718e1cd4f3Senami * interface. Called when the vlan is being unconfigured. 6728e1cd4f3Senami */ 6738e1cd4f3Senami static void 67496375e93Sthorpej vlan_ether_purgemulti(struct ifvlan *ifv) 6758e1cd4f3Senami { 6768e1cd4f3Senami struct ifnet *ifp = ifv->ifv_p; /* Parent. */ 6778e1cd4f3Senami struct vlan_mc_entry *mc; 6788e1cd4f3Senami union { 6798e1cd4f3Senami struct ifreq ifreq; 6808e1cd4f3Senami struct { 6818e1cd4f3Senami char ifr_name[IFNAMSIZ]; 68217707b76Senami struct sockaddr_storage ifr_ss; 6838e1cd4f3Senami } ifreq_storage; 6848e1cd4f3Senami } ifreq; 6858e1cd4f3Senami struct ifreq *ifr = &ifreq.ifreq; 6868e1cd4f3Senami 6878e1cd4f3Senami memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ); 6888e1cd4f3Senami while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) { 6898e1cd4f3Senami memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len); 6908e1cd4f3Senami (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr); 6918e1cd4f3Senami LIST_REMOVE(mc, mc_entries); 6928e1cd4f3Senami FREE(mc, M_DEVBUF); 6938e1cd4f3Senami } 69400746cb0Sthorpej } 69500746cb0Sthorpej 69600746cb0Sthorpej static void 69700746cb0Sthorpej vlan_start(struct ifnet *ifp) 69800746cb0Sthorpej { 699b817ce40Senami struct ifvlan *ifv = ifp->if_softc; 700b817ce40Senami struct ifnet *p = ifv->ifv_p; 7016e08061cSbouyer struct ethercom *ec = (void *) ifv->ifv_p; 70200746cb0Sthorpej struct mbuf *m; 7035cbdcc50Sthorpej int error; 7045cbdcc50Sthorpej ALTQ_DECL(struct altq_pktattr pktattr;) 70500746cb0Sthorpej 70600746cb0Sthorpej ifp->if_flags |= IFF_OACTIVE; 70700746cb0Sthorpej 70800746cb0Sthorpej for (;;) { 7095cbdcc50Sthorpej IFQ_DEQUEUE(&ifp->if_snd, m); 71000746cb0Sthorpej if (m == NULL) 71100746cb0Sthorpej break; 71200746cb0Sthorpej 7135cbdcc50Sthorpej #ifdef ALTQ 7145cbdcc50Sthorpej /* 7155cbdcc50Sthorpej * If ALTQ is enabled on the parent interface, do 7165cbdcc50Sthorpej * classification; the queueing discipline might 7175cbdcc50Sthorpej * not require classification, but might require 7185cbdcc50Sthorpej * the address family/header pointer in the pktattr. 7195cbdcc50Sthorpej */ 7205cbdcc50Sthorpej if (ALTQ_IS_ENABLED(&p->if_snd)) { 7215cbdcc50Sthorpej switch (p->if_type) { 7225cbdcc50Sthorpej case IFT_ETHER: 7235cbdcc50Sthorpej altq_etherclassify(&p->if_snd, m, &pktattr); 7245cbdcc50Sthorpej break; 7255cbdcc50Sthorpej #ifdef DIAGNOSTIC 7265cbdcc50Sthorpej default: 7275cbdcc50Sthorpej panic("vlan_start: impossible (altq)"); 7285cbdcc50Sthorpej #endif 7295cbdcc50Sthorpej } 7305cbdcc50Sthorpej } 7315cbdcc50Sthorpej #endif /* ALTQ */ 7325cbdcc50Sthorpej 73300746cb0Sthorpej #if NBPFILTER > 0 73400746cb0Sthorpej if (ifp->if_bpf) 73500746cb0Sthorpej bpf_mtap(ifp->if_bpf, m); 73600746cb0Sthorpej #endif 73700746cb0Sthorpej /* 7386e08061cSbouyer * If the parent can insert the tag itself, just mark 7396e08061cSbouyer * the tag in the mbuf header. 7406e08061cSbouyer */ 7416e08061cSbouyer if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) { 74240606ab8Sitojun struct m_tag *mtag; 74340606ab8Sitojun 74440606ab8Sitojun mtag = m_tag_get(PACKET_TAG_VLAN, sizeof(u_int), 74540606ab8Sitojun M_NOWAIT); 74640606ab8Sitojun if (mtag == NULL) { 7476e08061cSbouyer ifp->if_oerrors++; 7486e08061cSbouyer m_freem(m); 7496e08061cSbouyer continue; 7506e08061cSbouyer } 75140606ab8Sitojun *(u_int *)(mtag + 1) = ifv->ifv_tag; 7521face2ccSdrochner m_tag_prepend(m, mtag); 7536e08061cSbouyer } else { 7546e08061cSbouyer /* 755dc325578Spooka * insert the tag ourselves 75600746cb0Sthorpej */ 75796375e93Sthorpej M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT); 75800746cb0Sthorpej if (m == NULL) { 75996375e93Sthorpej printf("%s: unable to prepend encap header", 76096375e93Sthorpej ifv->ifv_p->if_xname); 76196375e93Sthorpej ifp->if_oerrors++; 76200746cb0Sthorpej continue; 76300746cb0Sthorpej } 76400746cb0Sthorpej 76596375e93Sthorpej switch (p->if_type) { 76696375e93Sthorpej case IFT_ETHER: 76796375e93Sthorpej { 76896375e93Sthorpej struct ether_vlan_header *evl; 76996375e93Sthorpej 7706e08061cSbouyer if (m->m_len < sizeof(struct ether_vlan_header)) 7716e08061cSbouyer m = m_pullup(m, 7726e08061cSbouyer sizeof(struct ether_vlan_header)); 7736e08061cSbouyer if (m == NULL) { 7746e08061cSbouyer printf("%s: unable to pullup encap " 7756e08061cSbouyer "header", ifv->ifv_p->if_xname); 77696375e93Sthorpej ifp->if_oerrors++; 77700746cb0Sthorpej continue; 77800746cb0Sthorpej } 77900746cb0Sthorpej 78000746cb0Sthorpej /* 7816e08061cSbouyer * Transform the Ethernet header into an 7826e08061cSbouyer * Ethernet header with 802.1Q encapsulation. 78300746cb0Sthorpej */ 78496375e93Sthorpej memmove(mtod(m, caddr_t), 78596375e93Sthorpej mtod(m, caddr_t) + ifv->ifv_encaplen, 78600746cb0Sthorpej sizeof(struct ether_header)); 78700746cb0Sthorpej evl = mtod(m, struct ether_vlan_header *); 78800746cb0Sthorpej evl->evl_proto = evl->evl_encap_proto; 78900746cb0Sthorpej evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 79000746cb0Sthorpej evl->evl_tag = htons(ifv->ifv_tag); 791a9760a89Sscw 792a9760a89Sscw /* 793a9760a89Sscw * To cater for VLAN-aware layer 2 ethernet 794a9760a89Sscw * switches which may need to strip the tag 795a9760a89Sscw * before forwarding the packet, make sure 796a9760a89Sscw * the packet+tag is at least 68 bytes long. 797a9760a89Sscw * This is necessary because our parent will 798a9760a89Sscw * only pad to 64 bytes (ETHER_MIN_LEN) and 799a9760a89Sscw * some switches will not pad by themselves 800a9760a89Sscw * after deleting a tag. 801a9760a89Sscw */ 802a9760a89Sscw if (m->m_pkthdr.len < 803a9760a89Sscw (ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN)) { 804a9760a89Sscw m_copyback(m, m->m_pkthdr.len, 805a9760a89Sscw (ETHER_MIN_LEN + 806a9760a89Sscw ETHER_VLAN_ENCAP_LEN) - 807a9760a89Sscw m->m_pkthdr.len, 808a9760a89Sscw vlan_zero_pad_buff); 809a9760a89Sscw } 81096375e93Sthorpej break; 81196375e93Sthorpej } 81296375e93Sthorpej 81396375e93Sthorpej #ifdef DIAGNOSTIC 81496375e93Sthorpej default: 81596375e93Sthorpej panic("vlan_start: impossible"); 81696375e93Sthorpej #endif 81796375e93Sthorpej } 8186e08061cSbouyer } 81900746cb0Sthorpej 82000746cb0Sthorpej /* 82196375e93Sthorpej * Send it, precisely as the parent's output routine 822dc325578Spooka * would have. We are already running at splnet. 82300746cb0Sthorpej */ 8245cbdcc50Sthorpej IFQ_ENQUEUE(&p->if_snd, m, &pktattr, error); 8255cbdcc50Sthorpej if (error) { 8265cbdcc50Sthorpej /* mbuf is already freed */ 82700746cb0Sthorpej ifp->if_oerrors++; 82800746cb0Sthorpej continue; 82900746cb0Sthorpej } 83000746cb0Sthorpej 8310d0fd771Sbouyer ifp->if_opackets++; 8327cd90d66Smycroft if ((p->if_flags & (IFF_RUNNING|IFF_OACTIVE)) == IFF_RUNNING) 833b817ce40Senami (*p->if_start)(p); 83400746cb0Sthorpej } 83500746cb0Sthorpej 83600746cb0Sthorpej ifp->if_flags &= ~IFF_OACTIVE; 83700746cb0Sthorpej } 83800746cb0Sthorpej 83900746cb0Sthorpej /* 84000746cb0Sthorpej * Given an Ethernet frame, find a valid vlan interface corresponding to the 8419bc855a9Ssimonb * given source interface and tag, then run the real packet through the 8429bc855a9Ssimonb * parent's input routine. 84300746cb0Sthorpej */ 84400746cb0Sthorpej void 84500746cb0Sthorpej vlan_input(struct ifnet *ifp, struct mbuf *m) 84600746cb0Sthorpej { 84700746cb0Sthorpej struct ifvlan *ifv; 84800746cb0Sthorpej u_int tag; 84940606ab8Sitojun struct m_tag *mtag; 85000746cb0Sthorpej 85140606ab8Sitojun mtag = m_tag_find(m, PACKET_TAG_VLAN, NULL); 85240606ab8Sitojun if (mtag != NULL) { 85340606ab8Sitojun /* m contains a normal ethernet frame, the tag is in mtag */ 8540c03ee54Syamt tag = EVL_VLANOFTAG(*(u_int *)(mtag + 1)); 85540606ab8Sitojun m_tag_delete(m, mtag); 8566e08061cSbouyer } else { 85796375e93Sthorpej switch (ifp->if_type) { 85896375e93Sthorpej case IFT_ETHER: 85996375e93Sthorpej { 86096375e93Sthorpej struct ether_vlan_header *evl; 86196375e93Sthorpej 86200746cb0Sthorpej if (m->m_len < sizeof(struct ether_vlan_header) && 86396375e93Sthorpej (m = m_pullup(m, 86496375e93Sthorpej sizeof(struct ether_vlan_header))) == NULL) { 86596375e93Sthorpej printf("%s: no memory for VLAN header, " 86696375e93Sthorpej "dropping packet.\n", ifp->if_xname); 86700746cb0Sthorpej return; 86800746cb0Sthorpej } 86900746cb0Sthorpej evl = mtod(m, struct ether_vlan_header *); 8703a739b04Sad KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN); 87100746cb0Sthorpej 87200746cb0Sthorpej tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 87300746cb0Sthorpej 87496375e93Sthorpej /* 87596375e93Sthorpej * Restore the original ethertype. We'll remove 87696375e93Sthorpej * the encapsulation after we've found the vlan 87796375e93Sthorpej * interface corresponding to the tag. 87896375e93Sthorpej */ 87996375e93Sthorpej evl->evl_encap_proto = evl->evl_proto; 88096375e93Sthorpej break; 88196375e93Sthorpej } 88296375e93Sthorpej 88396375e93Sthorpej default: 88496375e93Sthorpej tag = (u_int) -1; /* XXX GCC */ 88596375e93Sthorpej #ifdef DIAGNOSTIC 88696375e93Sthorpej panic("vlan_input: impossible"); 88796375e93Sthorpej #endif 88896375e93Sthorpej } 88924db4c5aSchristos } 89096375e93Sthorpej 89100746cb0Sthorpej for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 89200746cb0Sthorpej ifv = LIST_NEXT(ifv, ifv_list)) 89300746cb0Sthorpej if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) 89400746cb0Sthorpej break; 89500746cb0Sthorpej 89696375e93Sthorpej if (ifv == NULL || 89796375e93Sthorpej (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) != 89800746cb0Sthorpej (IFF_UP|IFF_RUNNING)) { 8992cc4a6b1Sitojun m_freem(m); 900b817ce40Senami ifp->if_noproto++; 90100746cb0Sthorpej return; 90200746cb0Sthorpej } 90324db4c5aSchristos 90424db4c5aSchristos /* 90524db4c5aSchristos * Now, remove the encapsulation header. The original 90624db4c5aSchristos * header has already been fixed up above. 90724db4c5aSchristos */ 90824db4c5aSchristos if (mtag == NULL) { 90924db4c5aSchristos memmove(mtod(m, caddr_t) + ifv->ifv_encaplen, 91024db4c5aSchristos mtod(m, caddr_t), sizeof(struct ether_header)); 91124db4c5aSchristos m_adj(m, ifv->ifv_encaplen); 91224db4c5aSchristos } 91324db4c5aSchristos 91400746cb0Sthorpej m->m_pkthdr.rcvif = &ifv->ifv_if; 91500746cb0Sthorpej ifv->ifv_if.if_ipackets++; 91600746cb0Sthorpej 91700746cb0Sthorpej #if NBPFILTER > 0 91800746cb0Sthorpej if (ifv->ifv_if.if_bpf) 91900746cb0Sthorpej bpf_mtap(ifv->ifv_if.if_bpf, m); 92000746cb0Sthorpej #endif 92100746cb0Sthorpej 92200746cb0Sthorpej /* Pass it back through the parent's input routine. */ 92300746cb0Sthorpej (*ifp->if_input)(&ifv->ifv_if, m); 92400746cb0Sthorpej } 925