1*88948cf5Sbouyer /* $NetBSD: if_vlan.c,v 1.60 2008/10/11 17:19:41 bouyer 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 * 1900746cb0Sthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2000746cb0Sthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2100746cb0Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2200746cb0Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2300746cb0Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2400746cb0Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2500746cb0Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2600746cb0Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2700746cb0Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2800746cb0Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2900746cb0Sthorpej * POSSIBILITY OF SUCH DAMAGE. 3000746cb0Sthorpej */ 3100746cb0Sthorpej 3200746cb0Sthorpej /* 3300746cb0Sthorpej * Copyright 1998 Massachusetts Institute of Technology 3400746cb0Sthorpej * 3500746cb0Sthorpej * Permission to use, copy, modify, and distribute this software and 3600746cb0Sthorpej * its documentation for any purpose and without fee is hereby 3700746cb0Sthorpej * granted, provided that both the above copyright notice and this 3800746cb0Sthorpej * permission notice appear in all copies, that both the above 3900746cb0Sthorpej * copyright notice and this permission notice appear in all 4000746cb0Sthorpej * supporting documentation, and that the name of M.I.T. not be used 4100746cb0Sthorpej * in advertising or publicity pertaining to distribution of the 4200746cb0Sthorpej * software without specific, written prior permission. M.I.T. makes 4300746cb0Sthorpej * no representations about the suitability of this software for any 4400746cb0Sthorpej * purpose. It is provided "as is" without express or implied 4500746cb0Sthorpej * warranty. 4600746cb0Sthorpej * 4700746cb0Sthorpej * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 4800746cb0Sthorpej * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 4900746cb0Sthorpej * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 5000746cb0Sthorpej * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 5100746cb0Sthorpej * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 5200746cb0Sthorpej * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 5300746cb0Sthorpej * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 5400746cb0Sthorpej * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 5500746cb0Sthorpej * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 5600746cb0Sthorpej * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 5700746cb0Sthorpej * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5800746cb0Sthorpej * SUCH DAMAGE. 5900746cb0Sthorpej * 6000746cb0Sthorpej * from FreeBSD: if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp 6100746cb0Sthorpej * via OpenBSD: if_vlan.c,v 1.4 2000/05/15 19:15:00 chris Exp 6200746cb0Sthorpej */ 6300746cb0Sthorpej 6400746cb0Sthorpej /* 6500746cb0Sthorpej * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. Might be 6600746cb0Sthorpej * extended some day to also handle IEEE 802.1P priority tagging. This is 6700746cb0Sthorpej * sort of sneaky in the implementation, since we need to pretend to be 6800746cb0Sthorpej * enough of an Ethernet implementation to make ARP work. The way we do 6900746cb0Sthorpej * this is by telling everyone that we are an Ethernet interface, and then 7000746cb0Sthorpej * catch the packets that ether_output() left on our output queue when it 7100746cb0Sthorpej * calls if_start(), rewrite them for use by the real outgoing interface, 7200746cb0Sthorpej * and ask it to send them. 7300746cb0Sthorpej * 7400746cb0Sthorpej * TODO: 7500746cb0Sthorpej * 7600746cb0Sthorpej * - Need some way to notify vlan interfaces when the parent 7700746cb0Sthorpej * interface changes MTU. 7800746cb0Sthorpej */ 7900746cb0Sthorpej 8034d65a34Slukem #include <sys/cdefs.h> 81*88948cf5Sbouyer __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.60 2008/10/11 17:19:41 bouyer Exp $"); 8234d65a34Slukem 8300746cb0Sthorpej #include "opt_inet.h" 8400746cb0Sthorpej #include "bpfilter.h" 8500746cb0Sthorpej 8600746cb0Sthorpej #include <sys/param.h> 8700746cb0Sthorpej #include <sys/kernel.h> 8800746cb0Sthorpej #include <sys/mbuf.h> 8900746cb0Sthorpej #include <sys/queue.h> 9000746cb0Sthorpej #include <sys/socket.h> 9100746cb0Sthorpej #include <sys/sockio.h> 9200746cb0Sthorpej #include <sys/systm.h> 9300746cb0Sthorpej #include <sys/proc.h> 94874fef37Selad #include <sys/kauth.h> 9500746cb0Sthorpej 9600746cb0Sthorpej #if NBPFILTER > 0 9700746cb0Sthorpej #include <net/bpf.h> 9800746cb0Sthorpej #endif 9900746cb0Sthorpej #include <net/if.h> 10000746cb0Sthorpej #include <net/if_dl.h> 10100746cb0Sthorpej #include <net/if_types.h> 10200746cb0Sthorpej #include <net/if_ether.h> 10300746cb0Sthorpej #include <net/if_vlanvar.h> 10400746cb0Sthorpej 10500746cb0Sthorpej #ifdef INET 10600746cb0Sthorpej #include <netinet/in.h> 10700746cb0Sthorpej #include <netinet/if_inarp.h> 10800746cb0Sthorpej #endif 10900746cb0Sthorpej 11096375e93Sthorpej struct vlan_mc_entry { 11196375e93Sthorpej LIST_ENTRY(vlan_mc_entry) mc_entries; 11296375e93Sthorpej /* 11396375e93Sthorpej * A key to identify this entry. The mc_addr below can't be 11496375e93Sthorpej * used since multiple sockaddr may mapped into the same 11596375e93Sthorpej * ether_multi (e.g., AF_UNSPEC). 11696375e93Sthorpej */ 11796375e93Sthorpej union { 11896375e93Sthorpej struct ether_multi *mcu_enm; 11996375e93Sthorpej } mc_u; 12096375e93Sthorpej struct sockaddr_storage mc_addr; 12196375e93Sthorpej }; 12296375e93Sthorpej 12396375e93Sthorpej #define mc_enm mc_u.mcu_enm 12496375e93Sthorpej 12596375e93Sthorpej struct ifvlan { 12696375e93Sthorpej union { 12796375e93Sthorpej struct ethercom ifvu_ec; 12896375e93Sthorpej } ifv_u; 12996375e93Sthorpej struct ifnet *ifv_p; /* parent interface of this vlan */ 13096375e93Sthorpej struct ifv_linkmib { 13196375e93Sthorpej const struct vlan_multisw *ifvm_msw; 13296375e93Sthorpej int ifvm_encaplen; /* encapsulation length */ 13396375e93Sthorpej int ifvm_mtufudge; /* MTU fudged by this much */ 13496375e93Sthorpej int ifvm_mintu; /* min transmission unit */ 1352b028087Smatt uint16_t ifvm_proto; /* encapsulation ethertype */ 1362b028087Smatt uint16_t ifvm_tag; /* tag to apply on packets */ 13796375e93Sthorpej } ifv_mib; 13896375e93Sthorpej LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead; 13996375e93Sthorpej LIST_ENTRY(ifvlan) ifv_list; 140c1e8f104Sthorpej int ifv_flags; 14196375e93Sthorpej }; 14296375e93Sthorpej 143c1e8f104Sthorpej #define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ 144c1e8f104Sthorpej 14596375e93Sthorpej #define ifv_ec ifv_u.ifvu_ec 14696375e93Sthorpej 14796375e93Sthorpej #define ifv_if ifv_ec.ec_if 14896375e93Sthorpej 14996375e93Sthorpej #define ifv_msw ifv_mib.ifvm_msw 15096375e93Sthorpej #define ifv_encaplen ifv_mib.ifvm_encaplen 15196375e93Sthorpej #define ifv_mtufudge ifv_mib.ifvm_mtufudge 15296375e93Sthorpej #define ifv_mintu ifv_mib.ifvm_mintu 15396375e93Sthorpej #define ifv_tag ifv_mib.ifvm_tag 15496375e93Sthorpej 15596375e93Sthorpej struct vlan_multisw { 15696375e93Sthorpej int (*vmsw_addmulti)(struct ifvlan *, struct ifreq *); 15796375e93Sthorpej int (*vmsw_delmulti)(struct ifvlan *, struct ifreq *); 15896375e93Sthorpej void (*vmsw_purgemulti)(struct ifvlan *); 15996375e93Sthorpej }; 16096375e93Sthorpej 16196375e93Sthorpej static int vlan_ether_addmulti(struct ifvlan *, struct ifreq *); 16296375e93Sthorpej static int vlan_ether_delmulti(struct ifvlan *, struct ifreq *); 16396375e93Sthorpej static void vlan_ether_purgemulti(struct ifvlan *); 16496375e93Sthorpej 16596375e93Sthorpej const struct vlan_multisw vlan_ether_multisw = { 16696375e93Sthorpej vlan_ether_addmulti, 16796375e93Sthorpej vlan_ether_delmulti, 16896375e93Sthorpej vlan_ether_purgemulti, 16996375e93Sthorpej }; 17096375e93Sthorpej 17100746cb0Sthorpej static int vlan_clone_create(struct if_clone *, int); 172b9c49ebfSpeter static int vlan_clone_destroy(struct ifnet *); 17300746cb0Sthorpej static int vlan_config(struct ifvlan *, struct ifnet *); 17453524e44Schristos static int vlan_ioctl(struct ifnet *, u_long, void *); 17500746cb0Sthorpej static void vlan_start(struct ifnet *); 176efbead50Sthorpej static void vlan_unconfig(struct ifnet *); 17796375e93Sthorpej 17800746cb0Sthorpej void vlanattach(int); 17900746cb0Sthorpej 18000746cb0Sthorpej /* XXX This should be a hash table with the tag as the basis of the key. */ 18100746cb0Sthorpej static LIST_HEAD(, ifvlan) ifv_list; 18200746cb0Sthorpej 18300746cb0Sthorpej struct if_clone vlan_cloner = 18400746cb0Sthorpej IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy); 18500746cb0Sthorpej 186a9760a89Sscw /* Used to pad ethernet frames with < ETHER_MIN_LEN bytes */ 187a9760a89Sscw static char vlan_zero_pad_buff[ETHER_MIN_LEN]; 188a9760a89Sscw 18900746cb0Sthorpej void 190168cd830Schristos vlanattach(int n) 19100746cb0Sthorpej { 19200746cb0Sthorpej 19300746cb0Sthorpej LIST_INIT(&ifv_list); 19400746cb0Sthorpej if_clone_attach(&vlan_cloner); 19500746cb0Sthorpej } 19600746cb0Sthorpej 19753c23f54Sthorpej static void 19853c23f54Sthorpej vlan_reset_linkname(struct ifnet *ifp) 19953c23f54Sthorpej { 20053c23f54Sthorpej 20153c23f54Sthorpej /* 20253c23f54Sthorpej * We start out with a "802.1Q VLAN" type and zero-length 20353c23f54Sthorpej * addresses. When we attach to a parent interface, we 20453c23f54Sthorpej * inherit its type, address length, address, and data link 20553c23f54Sthorpej * type. 20653c23f54Sthorpej */ 20753c23f54Sthorpej 20853c23f54Sthorpej ifp->if_type = IFT_L2VLAN; 20953c23f54Sthorpej ifp->if_addrlen = 0; 21053c23f54Sthorpej ifp->if_dlt = DLT_NULL; 21153c23f54Sthorpej if_alloc_sadl(ifp); 21253c23f54Sthorpej } 21353c23f54Sthorpej 21400746cb0Sthorpej static int 21500746cb0Sthorpej vlan_clone_create(struct if_clone *ifc, int unit) 21600746cb0Sthorpej { 21700746cb0Sthorpej struct ifvlan *ifv; 21800746cb0Sthorpej struct ifnet *ifp; 219efbead50Sthorpej int s; 22000746cb0Sthorpej 221bc168f27Schristos ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK|M_ZERO); 222b817ce40Senami ifp = &ifv->ifv_if; 2238e1cd4f3Senami LIST_INIT(&ifv->ifv_mc_listhead); 224efbead50Sthorpej 225efbead50Sthorpej s = splnet(); 22600746cb0Sthorpej LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 227efbead50Sthorpej splx(s); 22800746cb0Sthorpej 229bc168f27Schristos if_initname(ifp, ifc->ifc_name, unit); 23000746cb0Sthorpej ifp->if_softc = ifv; 23100746cb0Sthorpej ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 23200746cb0Sthorpej ifp->if_start = vlan_start; 23300746cb0Sthorpej ifp->if_ioctl = vlan_ioctl; 2345cbdcc50Sthorpej IFQ_SET_READY(&ifp->if_snd); 23500746cb0Sthorpej 23600746cb0Sthorpej if_attach(ifp); 23753c23f54Sthorpej vlan_reset_linkname(ifp); 23800746cb0Sthorpej 23900746cb0Sthorpej return (0); 24000746cb0Sthorpej } 24100746cb0Sthorpej 242b9c49ebfSpeter static int 24300746cb0Sthorpej vlan_clone_destroy(struct ifnet *ifp) 24400746cb0Sthorpej { 245efbead50Sthorpej struct ifvlan *ifv = ifp->if_softc; 24600746cb0Sthorpej int s; 24700746cb0Sthorpej 248efbead50Sthorpej s = splnet(); 24900746cb0Sthorpej LIST_REMOVE(ifv, ifv_list); 25000746cb0Sthorpej vlan_unconfig(ifp); 251efbead50Sthorpej splx(s); 25200746cb0Sthorpej 25300746cb0Sthorpej if_detach(ifp); 25400746cb0Sthorpej free(ifv, M_DEVBUF); 255b9c49ebfSpeter 256b9c49ebfSpeter return (0); 25700746cb0Sthorpej } 25800746cb0Sthorpej 259efbead50Sthorpej /* 260efbead50Sthorpej * Configure a VLAN interface. Must be called at splnet(). 261efbead50Sthorpej */ 26200746cb0Sthorpej static int 26300746cb0Sthorpej vlan_config(struct ifvlan *ifv, struct ifnet *p) 26400746cb0Sthorpej { 26596375e93Sthorpej struct ifnet *ifp = &ifv->ifv_if; 26696375e93Sthorpej int error; 26700746cb0Sthorpej 26800746cb0Sthorpej if (ifv->ifv_p != NULL) 26900746cb0Sthorpej return (EBUSY); 27096375e93Sthorpej 27196375e93Sthorpej switch (p->if_type) { 27296375e93Sthorpej case IFT_ETHER: 27396375e93Sthorpej { 27496375e93Sthorpej struct ethercom *ec = (void *) p; 27596375e93Sthorpej 27696375e93Sthorpej ifv->ifv_msw = &vlan_ether_multisw; 27796375e93Sthorpej ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 27896375e93Sthorpej ifv->ifv_mintu = ETHERMIN; 27996375e93Sthorpej 28096375e93Sthorpej /* 28196375e93Sthorpej * If the parent supports the VLAN_MTU capability, 28296375e93Sthorpej * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 28396375e93Sthorpej * enable it. 28496375e93Sthorpej */ 28596375e93Sthorpej if (ec->ec_nvlans++ == 0 && 28696375e93Sthorpej (ec->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) { 28796375e93Sthorpej /* 28896375e93Sthorpej * Enable Tx/Rx of VLAN-sized frames. 28996375e93Sthorpej */ 29096375e93Sthorpej ec->ec_capenable |= ETHERCAP_VLAN_MTU; 29196375e93Sthorpej if (p->if_flags & IFF_UP) { 29296375e93Sthorpej struct ifreq ifr; 29396375e93Sthorpej 29496375e93Sthorpej ifr.ifr_flags = p->if_flags; 29596375e93Sthorpej error = (*p->if_ioctl)(p, SIOCSIFFLAGS, 29653524e44Schristos (void *) &ifr); 29796375e93Sthorpej if (error) { 29896375e93Sthorpej if (ec->ec_nvlans-- == 1) 29996375e93Sthorpej ec->ec_capenable &= 30096375e93Sthorpej ~ETHERCAP_VLAN_MTU; 30196375e93Sthorpej return (error); 30296375e93Sthorpej } 30396375e93Sthorpej } 30496375e93Sthorpej ifv->ifv_mtufudge = 0; 30596375e93Sthorpej } else if ((ec->ec_capabilities & ETHERCAP_VLAN_MTU) == 0) { 30696375e93Sthorpej /* 30796375e93Sthorpej * Fudge the MTU by the encapsulation size. This 30896375e93Sthorpej * makes us incompatible with strictly compliant 30996375e93Sthorpej * 802.1Q implementations, but allows us to use 31096375e93Sthorpej * the feature with other NetBSD implementations, 31196375e93Sthorpej * which might still be useful. 31296375e93Sthorpej */ 31396375e93Sthorpej ifv->ifv_mtufudge = ifv->ifv_encaplen; 31496375e93Sthorpej } 31596375e93Sthorpej 31696375e93Sthorpej /* 31742a2e688Sthorpej * If the parent interface can do hardware-assisted 31842a2e688Sthorpej * VLAN encapsulation, then propagate its hardware- 31942a2e688Sthorpej * assisted checksumming flags. 32042a2e688Sthorpej */ 32142a2e688Sthorpej if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) 32242a2e688Sthorpej ifp->if_capabilities = p->if_capabilities & 323330cc0a1Syamt (IFCAP_CSUM_IPv4_Tx|IFCAP_CSUM_IPv4_Rx| 324330cc0a1Syamt IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_TCPv4_Rx| 325330cc0a1Syamt IFCAP_CSUM_UDPv4_Tx|IFCAP_CSUM_UDPv4_Rx| 326330cc0a1Syamt IFCAP_CSUM_TCPv6_Tx|IFCAP_CSUM_TCPv6_Rx| 327330cc0a1Syamt IFCAP_CSUM_UDPv6_Tx|IFCAP_CSUM_UDPv6_Rx); 32842a2e688Sthorpej 32942a2e688Sthorpej /* 33096375e93Sthorpej * We inherit the parent's Ethernet address. 33196375e93Sthorpej */ 3325204966aSdyoung ether_ifattach(ifp, CLLADDR(p->if_sadl)); 33396375e93Sthorpej ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */ 33496375e93Sthorpej break; 33596375e93Sthorpej } 33696375e93Sthorpej 33796375e93Sthorpej default: 33896375e93Sthorpej return (EPROTONOSUPPORT); 33996375e93Sthorpej } 34096375e93Sthorpej 34100746cb0Sthorpej ifv->ifv_p = p; 34296375e93Sthorpej ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge; 3433659b305Sbouyer ifv->ifv_if.if_flags = p->if_flags & 3440d0fd771Sbouyer (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 34500746cb0Sthorpej 34600746cb0Sthorpej /* 34796375e93Sthorpej * Inherit the if_type from the parent. This allows us 34896375e93Sthorpej * to participate in bridges of that type. 34900746cb0Sthorpej */ 35096375e93Sthorpej ifv->ifv_if.if_type = p->if_type; 35196375e93Sthorpej 35200746cb0Sthorpej return (0); 35300746cb0Sthorpej } 35400746cb0Sthorpej 355efbead50Sthorpej /* 356efbead50Sthorpej * Unconfigure a VLAN interface. Must be called at splnet(). 357efbead50Sthorpej */ 358efbead50Sthorpej static void 35900746cb0Sthorpej vlan_unconfig(struct ifnet *ifp) 36000746cb0Sthorpej { 36196375e93Sthorpej struct ifvlan *ifv = ifp->if_softc; 36200746cb0Sthorpej 363a5028df3Senami if (ifv->ifv_p == NULL) 364efbead50Sthorpej return; 36500746cb0Sthorpej 36600746cb0Sthorpej /* 36700746cb0Sthorpej * Since the interface is being unconfigured, we need to empty the 36800746cb0Sthorpej * list of multicast groups that we may have joined while we were 36900746cb0Sthorpej * alive and remove them from the parent's list also. 37000746cb0Sthorpej */ 37196375e93Sthorpej (*ifv->ifv_msw->vmsw_purgemulti)(ifv); 37200746cb0Sthorpej 37300746cb0Sthorpej /* Disconnect from parent. */ 37496375e93Sthorpej switch (ifv->ifv_p->if_type) { 37596375e93Sthorpej case IFT_ETHER: 37696375e93Sthorpej { 37796375e93Sthorpej struct ethercom *ec = (void *) ifv->ifv_p; 37800746cb0Sthorpej 37996375e93Sthorpej if (ec->ec_nvlans-- == 1) { 38096375e93Sthorpej /* 38196375e93Sthorpej * Disable Tx/Rx of VLAN-sized frames. 38296375e93Sthorpej */ 38396375e93Sthorpej ec->ec_capenable &= ~ETHERCAP_VLAN_MTU; 38496375e93Sthorpej if (ifv->ifv_p->if_flags & IFF_UP) { 38596375e93Sthorpej struct ifreq ifr; 38696375e93Sthorpej 38796375e93Sthorpej ifr.ifr_flags = ifv->ifv_p->if_flags; 38896375e93Sthorpej (void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, 38953524e44Schristos SIOCSIFFLAGS, (void *) &ifr); 39096375e93Sthorpej } 39196375e93Sthorpej } 39296375e93Sthorpej 39396375e93Sthorpej ether_ifdetach(ifp); 39453c23f54Sthorpej vlan_reset_linkname(ifp); 39596375e93Sthorpej break; 39696375e93Sthorpej } 39796375e93Sthorpej 39896375e93Sthorpej #ifdef DIAGNOSTIC 39996375e93Sthorpej default: 40096375e93Sthorpej panic("vlan_unconfig: impossible"); 40196375e93Sthorpej #endif 40296375e93Sthorpej } 40396375e93Sthorpej 40496375e93Sthorpej ifv->ifv_p = NULL; 40596375e93Sthorpej ifv->ifv_if.if_mtu = 0; 406c1e8f104Sthorpej ifv->ifv_flags = 0; 40700746cb0Sthorpej 408efbead50Sthorpej if_down(ifp); 409efbead50Sthorpej ifp->if_flags &= ~(IFF_UP|IFF_RUNNING); 41042a2e688Sthorpej ifp->if_capabilities = 0; 411efbead50Sthorpej } 412efbead50Sthorpej 413efbead50Sthorpej /* 414efbead50Sthorpej * Called when a parent interface is detaching; destroy any VLAN 415efbead50Sthorpej * configuration for the parent interface. 416efbead50Sthorpej */ 417efbead50Sthorpej void 418efbead50Sthorpej vlan_ifdetach(struct ifnet *p) 419efbead50Sthorpej { 420efbead50Sthorpej struct ifvlan *ifv; 421efbead50Sthorpej int s; 422efbead50Sthorpej 423efbead50Sthorpej s = splnet(); 424efbead50Sthorpej 425efbead50Sthorpej for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 426efbead50Sthorpej ifv = LIST_NEXT(ifv, ifv_list)) { 427efbead50Sthorpej if (ifv->ifv_p == p) 428efbead50Sthorpej vlan_unconfig(&ifv->ifv_if); 429efbead50Sthorpej } 430efbead50Sthorpej 43100746cb0Sthorpej splx(s); 43200746cb0Sthorpej } 43300746cb0Sthorpej 43400746cb0Sthorpej static int 435c1e8f104Sthorpej vlan_set_promisc(struct ifnet *ifp) 436c1e8f104Sthorpej { 437c1e8f104Sthorpej struct ifvlan *ifv = ifp->if_softc; 438d93a3cbcSenami int error = 0; 439c1e8f104Sthorpej 440c1e8f104Sthorpej if ((ifp->if_flags & IFF_PROMISC) != 0) { 441c1e8f104Sthorpej if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { 442c1e8f104Sthorpej error = ifpromisc(ifv->ifv_p, 1); 443c1e8f104Sthorpej if (error == 0) 444c1e8f104Sthorpej ifv->ifv_flags |= IFVF_PROMISC; 445c1e8f104Sthorpej } 446c1e8f104Sthorpej } else { 447c1e8f104Sthorpej if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { 448c1e8f104Sthorpej error = ifpromisc(ifv->ifv_p, 0); 449c1e8f104Sthorpej if (error == 0) 450c1e8f104Sthorpej ifv->ifv_flags &= ~IFVF_PROMISC; 451c1e8f104Sthorpej } 452c1e8f104Sthorpej } 453c1e8f104Sthorpej 454c1e8f104Sthorpej return (error); 455c1e8f104Sthorpej } 456c1e8f104Sthorpej 457c1e8f104Sthorpej static int 45853524e44Schristos vlan_ioctl(struct ifnet *ifp, u_long cmd, void *data) 45900746cb0Sthorpej { 460f474dcebSad struct lwp *l = curlwp; /* XXX */ 461efbead50Sthorpej struct ifvlan *ifv = ifp->if_softc; 462efbead50Sthorpej struct ifaddr *ifa = (struct ifaddr *) data; 463efbead50Sthorpej struct ifreq *ifr = (struct ifreq *) data; 46400746cb0Sthorpej struct ifnet *pr; 465*88948cf5Sbouyer struct ifcapreq *ifcr; 46600746cb0Sthorpej struct vlanreq vlr; 46700746cb0Sthorpej struct sockaddr *sa; 468efbead50Sthorpej int s, error = 0; 46900746cb0Sthorpej 470efbead50Sthorpej s = splnet(); 47100746cb0Sthorpej 47200746cb0Sthorpej switch (cmd) { 47300746cb0Sthorpej case SIOCSIFADDR: 47429fdeefaSbouyer if (ifv->ifv_p != NULL) { 47500746cb0Sthorpej ifp->if_flags |= IFF_UP; 47600746cb0Sthorpej 47700746cb0Sthorpej switch (ifa->ifa_addr->sa_family) { 47800746cb0Sthorpej #ifdef INET 47900746cb0Sthorpej case AF_INET: 48000746cb0Sthorpej arp_ifinit(ifp, ifa); 48100746cb0Sthorpej break; 48200746cb0Sthorpej #endif 48300746cb0Sthorpej default: 48400746cb0Sthorpej break; 48500746cb0Sthorpej } 48629fdeefaSbouyer } else { 48729fdeefaSbouyer error = EINVAL; 48829fdeefaSbouyer } 48900746cb0Sthorpej break; 49000746cb0Sthorpej 49100746cb0Sthorpej case SIOCGIFADDR: 49200746cb0Sthorpej sa = (struct sockaddr *)&ifr->ifr_data; 4935204966aSdyoung memcpy(sa->sa_data, CLLADDR(ifp->if_sadl), ifp->if_addrlen); 49400746cb0Sthorpej break; 49500746cb0Sthorpej 49600746cb0Sthorpej case SIOCSIFMTU: 4972ccede0aSdyoung if (ifv->ifv_p == NULL) 49800746cb0Sthorpej error = EINVAL; 4992ccede0aSdyoung else if ( 5002ccede0aSdyoung ifr->ifr_mtu > (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) || 5012ccede0aSdyoung ifr->ifr_mtu < (ifv->ifv_mintu - ifv->ifv_mtufudge)) 50200746cb0Sthorpej error = EINVAL; 5032ccede0aSdyoung else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET) 5042ccede0aSdyoung error = 0; 50500746cb0Sthorpej break; 50600746cb0Sthorpej 50700746cb0Sthorpej case SIOCSETVLAN: 5080a57b596Selad if ((error = kauth_authorize_network(l->l_cred, 5090a57b596Selad KAUTH_NETWORK_INTERFACE, 5100a57b596Selad KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, 5110a57b596Selad NULL)) != 0) 51200746cb0Sthorpej break; 51300746cb0Sthorpej if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0) 51400746cb0Sthorpej break; 51500746cb0Sthorpej if (vlr.vlr_parent[0] == '\0') { 51600746cb0Sthorpej vlan_unconfig(ifp); 51700746cb0Sthorpej break; 51800746cb0Sthorpej } 51900746cb0Sthorpej if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) { 52000746cb0Sthorpej error = EINVAL; /* check for valid tag */ 52100746cb0Sthorpej break; 52200746cb0Sthorpej } 52300746cb0Sthorpej if ((pr = ifunit(vlr.vlr_parent)) == 0) { 52400746cb0Sthorpej error = ENOENT; 52500746cb0Sthorpej break; 52600746cb0Sthorpej } 52700746cb0Sthorpej if ((error = vlan_config(ifv, pr)) != 0) 52800746cb0Sthorpej break; 52900746cb0Sthorpej ifv->ifv_tag = vlr.vlr_tag; 53000746cb0Sthorpej ifp->if_flags |= IFF_RUNNING; 531c1e8f104Sthorpej 532c1e8f104Sthorpej /* Update promiscuous mode, if necessary. */ 533c1e8f104Sthorpej vlan_set_promisc(ifp); 53400746cb0Sthorpej break; 53500746cb0Sthorpej 53600746cb0Sthorpej case SIOCGETVLAN: 53700746cb0Sthorpej memset(&vlr, 0, sizeof(vlr)); 53800746cb0Sthorpej if (ifv->ifv_p != NULL) { 53900746cb0Sthorpej snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s", 54000746cb0Sthorpej ifv->ifv_p->if_xname); 54100746cb0Sthorpej vlr.vlr_tag = ifv->ifv_tag; 54200746cb0Sthorpej } 54300746cb0Sthorpej error = copyout(&vlr, ifr->ifr_data, sizeof(vlr)); 54400746cb0Sthorpej break; 54500746cb0Sthorpej 54600746cb0Sthorpej case SIOCSIFFLAGS: 54700746cb0Sthorpej /* 548c1e8f104Sthorpej * For promiscuous mode, we enable promiscuous mode on 549c1e8f104Sthorpej * the parent if we need promiscuous on the VLAN interface. 55000746cb0Sthorpej */ 551c1e8f104Sthorpej if (ifv->ifv_p != NULL) 552c1e8f104Sthorpej error = vlan_set_promisc(ifp); 55300746cb0Sthorpej break; 55400746cb0Sthorpej 55500746cb0Sthorpej case SIOCADDMULTI: 55623df92e3Sthorpej error = (ifv->ifv_p != NULL) ? 55723df92e3Sthorpej (*ifv->ifv_msw->vmsw_addmulti)(ifv, ifr) : EINVAL; 558177a2587Senami break; 559177a2587Senami 560177a2587Senami case SIOCDELMULTI: 56123df92e3Sthorpej error = (ifv->ifv_p != NULL) ? 56223df92e3Sthorpej (*ifv->ifv_msw->vmsw_delmulti)(ifv, ifr) : EINVAL; 56300746cb0Sthorpej break; 56400746cb0Sthorpej 565*88948cf5Sbouyer case SIOCSIFCAP: 566*88948cf5Sbouyer ifcr = data; 567*88948cf5Sbouyer /* make sure caps are enabled on parent */ 568*88948cf5Sbouyer if ((ifv->ifv_p->if_capenable & ifcr->ifcr_capenable) != 569*88948cf5Sbouyer ifcr->ifcr_capenable) { 570*88948cf5Sbouyer error = EINVAL; 571*88948cf5Sbouyer break; 572*88948cf5Sbouyer } 573*88948cf5Sbouyer if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET) 574*88948cf5Sbouyer error = 0; 575*88948cf5Sbouyer break; 57600746cb0Sthorpej default: 57700746cb0Sthorpej error = EINVAL; 57800746cb0Sthorpej } 57900746cb0Sthorpej 580efbead50Sthorpej splx(s); 581efbead50Sthorpej 58200746cb0Sthorpej return (error); 58300746cb0Sthorpej } 58400746cb0Sthorpej 58500746cb0Sthorpej static int 58696375e93Sthorpej vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr) 58700746cb0Sthorpej { 58843390716Sdyoung const struct sockaddr *sa = ifreq_getaddr(SIOCADDMULTI, ifr); 58900746cb0Sthorpej struct vlan_mc_entry *mc; 5902b028087Smatt uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 59100746cb0Sthorpej int error; 59200746cb0Sthorpej 59343390716Sdyoung if (sa->sa_len > sizeof(struct sockaddr_storage)) 5948e1cd4f3Senami return (EINVAL); 59500746cb0Sthorpej 59643390716Sdyoung error = ether_addmulti(sa, &ifv->ifv_ec); 5978e1cd4f3Senami if (error != ENETRESET) 59800746cb0Sthorpej return (error); 5998e1cd4f3Senami 6008e1cd4f3Senami /* 6018e1cd4f3Senami * This is new multicast address. We have to tell parent 6028e1cd4f3Senami * about it. Also, remember this multicast address so that 6038e1cd4f3Senami * we can delete them on unconfigure. 6048e1cd4f3Senami */ 6058e1cd4f3Senami MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry), 6068e1cd4f3Senami M_DEVBUF, M_NOWAIT); 6078e1cd4f3Senami if (mc == NULL) { 6088e1cd4f3Senami error = ENOMEM; 6098e1cd4f3Senami goto alloc_failed; 61000746cb0Sthorpej } 61100746cb0Sthorpej 6128e1cd4f3Senami /* 6138e1cd4f3Senami * As ether_addmulti() returns ENETRESET, following two 6148e1cd4f3Senami * statement shouldn't fail. 6158e1cd4f3Senami */ 61643390716Sdyoung (void)ether_multiaddr(sa, addrlo, addrhi); 6178e1cd4f3Senami ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm); 61843390716Sdyoung memcpy(&mc->mc_addr, sa, sa->sa_len); 6198e1cd4f3Senami LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries); 6208e1cd4f3Senami 6218e1cd4f3Senami error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI, 62253524e44Schristos (void *)ifr); 6238e1cd4f3Senami if (error != 0) 6248e1cd4f3Senami goto ioctl_failed; 62500746cb0Sthorpej return (error); 6268e1cd4f3Senami 6278e1cd4f3Senami ioctl_failed: 6288e1cd4f3Senami LIST_REMOVE(mc, mc_entries); 6298e1cd4f3Senami FREE(mc, M_DEVBUF); 6308e1cd4f3Senami alloc_failed: 63143390716Sdyoung (void)ether_delmulti(sa, &ifv->ifv_ec); 6328e1cd4f3Senami return (error); 63300746cb0Sthorpej } 63400746cb0Sthorpej 6358e1cd4f3Senami static int 63696375e93Sthorpej vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr) 6378e1cd4f3Senami { 63843390716Sdyoung const struct sockaddr *sa = ifreq_getaddr(SIOCDELMULTI, ifr); 6398e1cd4f3Senami struct ether_multi *enm; 6408e1cd4f3Senami struct vlan_mc_entry *mc; 6412b028087Smatt uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 6428e1cd4f3Senami int error; 6438e1cd4f3Senami 6448e1cd4f3Senami /* 6458e1cd4f3Senami * Find a key to lookup vlan_mc_entry. We have to do this 6468e1cd4f3Senami * before calling ether_delmulti for obvious reason. 6478e1cd4f3Senami */ 64843390716Sdyoung if ((error = ether_multiaddr(sa, addrlo, addrhi)) != 0) 6498e1cd4f3Senami return (error); 6508e1cd4f3Senami ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm); 6518e1cd4f3Senami 65243390716Sdyoung error = ether_delmulti(sa, &ifv->ifv_ec); 6538e1cd4f3Senami if (error != ENETRESET) 6548e1cd4f3Senami return (error); 6558e1cd4f3Senami 6568e1cd4f3Senami /* We no longer use this multicast address. Tell parent so. */ 6578e1cd4f3Senami error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI, 65853524e44Schristos (void *)ifr); 6598e1cd4f3Senami if (error == 0) { 6608e1cd4f3Senami /* And forget about this address. */ 6618e1cd4f3Senami for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL; 6628e1cd4f3Senami mc = LIST_NEXT(mc, mc_entries)) { 6638e1cd4f3Senami if (mc->mc_enm == enm) { 6648e1cd4f3Senami LIST_REMOVE(mc, mc_entries); 6658e1cd4f3Senami FREE(mc, M_DEVBUF); 6668e1cd4f3Senami break; 6678e1cd4f3Senami } 6688e1cd4f3Senami } 6698e1cd4f3Senami KASSERT(mc != NULL); 6708e1cd4f3Senami } else 67143390716Sdyoung (void)ether_addmulti(sa, &ifv->ifv_ec); 6728e1cd4f3Senami return (error); 6738e1cd4f3Senami } 6748e1cd4f3Senami 6758e1cd4f3Senami /* 676dc325578Spooka * Delete any multicast address we have asked to add from parent 6778e1cd4f3Senami * interface. Called when the vlan is being unconfigured. 6788e1cd4f3Senami */ 6798e1cd4f3Senami static void 68096375e93Sthorpej vlan_ether_purgemulti(struct ifvlan *ifv) 6818e1cd4f3Senami { 6828e1cd4f3Senami struct ifnet *ifp = ifv->ifv_p; /* Parent. */ 6838e1cd4f3Senami struct vlan_mc_entry *mc; 6848e1cd4f3Senami union { 6858e1cd4f3Senami struct ifreq ifreq; 6868e1cd4f3Senami struct { 6878e1cd4f3Senami char ifr_name[IFNAMSIZ]; 68817707b76Senami struct sockaddr_storage ifr_ss; 6898e1cd4f3Senami } ifreq_storage; 6908e1cd4f3Senami } ifreq; 6918e1cd4f3Senami struct ifreq *ifr = &ifreq.ifreq; 6928e1cd4f3Senami 6938e1cd4f3Senami memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ); 6948e1cd4f3Senami while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) { 69543390716Sdyoung ifreq_setaddr(SIOCDELMULTI, ifr, 69643390716Sdyoung (const struct sockaddr *)&mc->mc_addr); 69753524e44Schristos (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (void *)ifr); 6988e1cd4f3Senami LIST_REMOVE(mc, mc_entries); 6998e1cd4f3Senami FREE(mc, M_DEVBUF); 7008e1cd4f3Senami } 70100746cb0Sthorpej } 70200746cb0Sthorpej 70300746cb0Sthorpej static void 70400746cb0Sthorpej vlan_start(struct ifnet *ifp) 70500746cb0Sthorpej { 706b817ce40Senami struct ifvlan *ifv = ifp->if_softc; 707b817ce40Senami struct ifnet *p = ifv->ifv_p; 7086e08061cSbouyer struct ethercom *ec = (void *) ifv->ifv_p; 70900746cb0Sthorpej struct mbuf *m; 7105cbdcc50Sthorpej int error; 7115cbdcc50Sthorpej ALTQ_DECL(struct altq_pktattr pktattr;) 71200746cb0Sthorpej 71300746cb0Sthorpej ifp->if_flags |= IFF_OACTIVE; 71400746cb0Sthorpej 71500746cb0Sthorpej for (;;) { 7165cbdcc50Sthorpej IFQ_DEQUEUE(&ifp->if_snd, m); 71700746cb0Sthorpej if (m == NULL) 71800746cb0Sthorpej break; 71900746cb0Sthorpej 7205cbdcc50Sthorpej #ifdef ALTQ 7215cbdcc50Sthorpej /* 7225cbdcc50Sthorpej * If ALTQ is enabled on the parent interface, do 7235cbdcc50Sthorpej * classification; the queueing discipline might 7245cbdcc50Sthorpej * not require classification, but might require 7255cbdcc50Sthorpej * the address family/header pointer in the pktattr. 7265cbdcc50Sthorpej */ 7275cbdcc50Sthorpej if (ALTQ_IS_ENABLED(&p->if_snd)) { 7285cbdcc50Sthorpej switch (p->if_type) { 7295cbdcc50Sthorpej case IFT_ETHER: 7305cbdcc50Sthorpej altq_etherclassify(&p->if_snd, m, &pktattr); 7315cbdcc50Sthorpej break; 7325cbdcc50Sthorpej #ifdef DIAGNOSTIC 7335cbdcc50Sthorpej default: 7345cbdcc50Sthorpej panic("vlan_start: impossible (altq)"); 7355cbdcc50Sthorpej #endif 7365cbdcc50Sthorpej } 7375cbdcc50Sthorpej } 7385cbdcc50Sthorpej #endif /* ALTQ */ 7395cbdcc50Sthorpej 74000746cb0Sthorpej #if NBPFILTER > 0 74100746cb0Sthorpej if (ifp->if_bpf) 74200746cb0Sthorpej bpf_mtap(ifp->if_bpf, m); 74300746cb0Sthorpej #endif 74400746cb0Sthorpej /* 7456e08061cSbouyer * If the parent can insert the tag itself, just mark 7466e08061cSbouyer * the tag in the mbuf header. 7476e08061cSbouyer */ 7486e08061cSbouyer if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) { 74940606ab8Sitojun struct m_tag *mtag; 75040606ab8Sitojun 75140606ab8Sitojun mtag = m_tag_get(PACKET_TAG_VLAN, sizeof(u_int), 75240606ab8Sitojun M_NOWAIT); 75340606ab8Sitojun if (mtag == NULL) { 7546e08061cSbouyer ifp->if_oerrors++; 7556e08061cSbouyer m_freem(m); 7566e08061cSbouyer continue; 7576e08061cSbouyer } 75840606ab8Sitojun *(u_int *)(mtag + 1) = ifv->ifv_tag; 7591face2ccSdrochner m_tag_prepend(m, mtag); 7606e08061cSbouyer } else { 7616e08061cSbouyer /* 762dc325578Spooka * insert the tag ourselves 76300746cb0Sthorpej */ 76496375e93Sthorpej M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT); 76500746cb0Sthorpej if (m == NULL) { 76696375e93Sthorpej printf("%s: unable to prepend encap header", 76796375e93Sthorpej ifv->ifv_p->if_xname); 76896375e93Sthorpej ifp->if_oerrors++; 76900746cb0Sthorpej continue; 77000746cb0Sthorpej } 77100746cb0Sthorpej 77296375e93Sthorpej switch (p->if_type) { 77396375e93Sthorpej case IFT_ETHER: 77496375e93Sthorpej { 77596375e93Sthorpej struct ether_vlan_header *evl; 77696375e93Sthorpej 7776e08061cSbouyer if (m->m_len < sizeof(struct ether_vlan_header)) 7786e08061cSbouyer m = m_pullup(m, 7796e08061cSbouyer sizeof(struct ether_vlan_header)); 7806e08061cSbouyer if (m == NULL) { 7816e08061cSbouyer printf("%s: unable to pullup encap " 7826e08061cSbouyer "header", ifv->ifv_p->if_xname); 78396375e93Sthorpej ifp->if_oerrors++; 78400746cb0Sthorpej continue; 78500746cb0Sthorpej } 78600746cb0Sthorpej 78700746cb0Sthorpej /* 7886e08061cSbouyer * Transform the Ethernet header into an 7896e08061cSbouyer * Ethernet header with 802.1Q encapsulation. 79000746cb0Sthorpej */ 79153524e44Schristos memmove(mtod(m, void *), 79253524e44Schristos mtod(m, char *) + ifv->ifv_encaplen, 79300746cb0Sthorpej sizeof(struct ether_header)); 79400746cb0Sthorpej evl = mtod(m, struct ether_vlan_header *); 79500746cb0Sthorpej evl->evl_proto = evl->evl_encap_proto; 79600746cb0Sthorpej evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 79700746cb0Sthorpej evl->evl_tag = htons(ifv->ifv_tag); 798a9760a89Sscw 799a9760a89Sscw /* 800a9760a89Sscw * To cater for VLAN-aware layer 2 ethernet 801a9760a89Sscw * switches which may need to strip the tag 802a9760a89Sscw * before forwarding the packet, make sure 803a9760a89Sscw * the packet+tag is at least 68 bytes long. 804a9760a89Sscw * This is necessary because our parent will 805a9760a89Sscw * only pad to 64 bytes (ETHER_MIN_LEN) and 806a9760a89Sscw * some switches will not pad by themselves 807a9760a89Sscw * after deleting a tag. 808a9760a89Sscw */ 809a9760a89Sscw if (m->m_pkthdr.len < 810a9760a89Sscw (ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN)) { 811a9760a89Sscw m_copyback(m, m->m_pkthdr.len, 812a9760a89Sscw (ETHER_MIN_LEN + 813a9760a89Sscw ETHER_VLAN_ENCAP_LEN) - 814a9760a89Sscw m->m_pkthdr.len, 815a9760a89Sscw vlan_zero_pad_buff); 816a9760a89Sscw } 81796375e93Sthorpej break; 81896375e93Sthorpej } 81996375e93Sthorpej 82096375e93Sthorpej #ifdef DIAGNOSTIC 82196375e93Sthorpej default: 82296375e93Sthorpej panic("vlan_start: impossible"); 82396375e93Sthorpej #endif 82496375e93Sthorpej } 8256e08061cSbouyer } 82600746cb0Sthorpej 82700746cb0Sthorpej /* 82896375e93Sthorpej * Send it, precisely as the parent's output routine 829dc325578Spooka * would have. We are already running at splnet. 83000746cb0Sthorpej */ 8315cbdcc50Sthorpej IFQ_ENQUEUE(&p->if_snd, m, &pktattr, error); 8325cbdcc50Sthorpej if (error) { 8335cbdcc50Sthorpej /* mbuf is already freed */ 83400746cb0Sthorpej ifp->if_oerrors++; 83500746cb0Sthorpej continue; 83600746cb0Sthorpej } 83700746cb0Sthorpej 8380d0fd771Sbouyer ifp->if_opackets++; 8397cd90d66Smycroft if ((p->if_flags & (IFF_RUNNING|IFF_OACTIVE)) == IFF_RUNNING) 840b817ce40Senami (*p->if_start)(p); 84100746cb0Sthorpej } 84200746cb0Sthorpej 84300746cb0Sthorpej ifp->if_flags &= ~IFF_OACTIVE; 84400746cb0Sthorpej } 84500746cb0Sthorpej 84600746cb0Sthorpej /* 84700746cb0Sthorpej * Given an Ethernet frame, find a valid vlan interface corresponding to the 8489bc855a9Ssimonb * given source interface and tag, then run the real packet through the 8499bc855a9Ssimonb * parent's input routine. 85000746cb0Sthorpej */ 85100746cb0Sthorpej void 85200746cb0Sthorpej vlan_input(struct ifnet *ifp, struct mbuf *m) 85300746cb0Sthorpej { 85400746cb0Sthorpej struct ifvlan *ifv; 85500746cb0Sthorpej u_int tag; 85640606ab8Sitojun struct m_tag *mtag; 85700746cb0Sthorpej 85840606ab8Sitojun mtag = m_tag_find(m, PACKET_TAG_VLAN, NULL); 85940606ab8Sitojun if (mtag != NULL) { 86040606ab8Sitojun /* m contains a normal ethernet frame, the tag is in mtag */ 8610c03ee54Syamt tag = EVL_VLANOFTAG(*(u_int *)(mtag + 1)); 86240606ab8Sitojun m_tag_delete(m, mtag); 8636e08061cSbouyer } else { 86496375e93Sthorpej switch (ifp->if_type) { 86596375e93Sthorpej case IFT_ETHER: 86696375e93Sthorpej { 86796375e93Sthorpej struct ether_vlan_header *evl; 86896375e93Sthorpej 86900746cb0Sthorpej if (m->m_len < sizeof(struct ether_vlan_header) && 87096375e93Sthorpej (m = m_pullup(m, 87196375e93Sthorpej sizeof(struct ether_vlan_header))) == NULL) { 87296375e93Sthorpej printf("%s: no memory for VLAN header, " 87396375e93Sthorpej "dropping packet.\n", ifp->if_xname); 87400746cb0Sthorpej return; 87500746cb0Sthorpej } 87600746cb0Sthorpej evl = mtod(m, struct ether_vlan_header *); 8773a739b04Sad KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN); 87800746cb0Sthorpej 87900746cb0Sthorpej tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 88000746cb0Sthorpej 88196375e93Sthorpej /* 88296375e93Sthorpej * Restore the original ethertype. We'll remove 88396375e93Sthorpej * the encapsulation after we've found the vlan 88496375e93Sthorpej * interface corresponding to the tag. 88596375e93Sthorpej */ 88696375e93Sthorpej evl->evl_encap_proto = evl->evl_proto; 88796375e93Sthorpej break; 88896375e93Sthorpej } 88996375e93Sthorpej 89096375e93Sthorpej default: 89196375e93Sthorpej tag = (u_int) -1; /* XXX GCC */ 89296375e93Sthorpej #ifdef DIAGNOSTIC 89396375e93Sthorpej panic("vlan_input: impossible"); 89496375e93Sthorpej #endif 89596375e93Sthorpej } 89624db4c5aSchristos } 89796375e93Sthorpej 89800746cb0Sthorpej for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 89900746cb0Sthorpej ifv = LIST_NEXT(ifv, ifv_list)) 90000746cb0Sthorpej if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) 90100746cb0Sthorpej break; 90200746cb0Sthorpej 90396375e93Sthorpej if (ifv == NULL || 90496375e93Sthorpej (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) != 90500746cb0Sthorpej (IFF_UP|IFF_RUNNING)) { 9062cc4a6b1Sitojun m_freem(m); 907b817ce40Senami ifp->if_noproto++; 90800746cb0Sthorpej return; 90900746cb0Sthorpej } 91024db4c5aSchristos 91124db4c5aSchristos /* 91224db4c5aSchristos * Now, remove the encapsulation header. The original 91324db4c5aSchristos * header has already been fixed up above. 91424db4c5aSchristos */ 91524db4c5aSchristos if (mtag == NULL) { 91653524e44Schristos memmove(mtod(m, char *) + ifv->ifv_encaplen, 91753524e44Schristos mtod(m, void *), sizeof(struct ether_header)); 91824db4c5aSchristos m_adj(m, ifv->ifv_encaplen); 91924db4c5aSchristos } 92024db4c5aSchristos 92100746cb0Sthorpej m->m_pkthdr.rcvif = &ifv->ifv_if; 92200746cb0Sthorpej ifv->ifv_if.if_ipackets++; 92300746cb0Sthorpej 92400746cb0Sthorpej #if NBPFILTER > 0 92500746cb0Sthorpej if (ifv->ifv_if.if_bpf) 92600746cb0Sthorpej bpf_mtap(ifv->ifv_if.if_bpf, m); 92700746cb0Sthorpej #endif 92800746cb0Sthorpej 92900746cb0Sthorpej /* Pass it back through the parent's input routine. */ 93000746cb0Sthorpej (*ifp->if_input)(&ifv->ifv_if, m); 93100746cb0Sthorpej } 932