12cc2df49SGarrett Wollman /* 22cc2df49SGarrett Wollman * Copyright 1998 Massachusetts Institute of Technology 32cc2df49SGarrett Wollman * 42cc2df49SGarrett Wollman * Permission to use, copy, modify, and distribute this software and 52cc2df49SGarrett Wollman * its documentation for any purpose and without fee is hereby 62cc2df49SGarrett Wollman * granted, provided that both the above copyright notice and this 72cc2df49SGarrett Wollman * permission notice appear in all copies, that both the above 82cc2df49SGarrett Wollman * copyright notice and this permission notice appear in all 92cc2df49SGarrett Wollman * supporting documentation, and that the name of M.I.T. not be used 102cc2df49SGarrett Wollman * in advertising or publicity pertaining to distribution of the 112cc2df49SGarrett Wollman * software without specific, written prior permission. M.I.T. makes 122cc2df49SGarrett Wollman * no representations about the suitability of this software for any 132cc2df49SGarrett Wollman * purpose. It is provided "as is" without express or implied 142cc2df49SGarrett Wollman * warranty. 152cc2df49SGarrett Wollman * 162cc2df49SGarrett Wollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 172cc2df49SGarrett Wollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 182cc2df49SGarrett Wollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 192cc2df49SGarrett Wollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 202cc2df49SGarrett Wollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212cc2df49SGarrett Wollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 222cc2df49SGarrett Wollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 232cc2df49SGarrett Wollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 242cc2df49SGarrett Wollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 252cc2df49SGarrett Wollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 262cc2df49SGarrett Wollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 272cc2df49SGarrett Wollman * SUCH DAMAGE. 282cc2df49SGarrett Wollman * 29c3aac50fSPeter Wemm * $FreeBSD$ 302cc2df49SGarrett Wollman */ 312cc2df49SGarrett Wollman 322cc2df49SGarrett Wollman /* 332cc2df49SGarrett Wollman * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 342cc2df49SGarrett Wollman * Might be extended some day to also handle IEEE 802.1p priority 352cc2df49SGarrett Wollman * tagging. This is sort of sneaky in the implementation, since 362cc2df49SGarrett Wollman * we need to pretend to be enough of an Ethernet implementation 372cc2df49SGarrett Wollman * to make arp work. The way we do this is by telling everyone 382cc2df49SGarrett Wollman * that we are an Ethernet, and then catch the packets that 3946a32e61SPhilippe Charnier * ether_output() left on our output queue when it calls 402cc2df49SGarrett Wollman * if_start(), rewrite them for use by the real outgoing interface, 412cc2df49SGarrett Wollman * and ask it to send them. 422cc2df49SGarrett Wollman */ 432cc2df49SGarrett Wollman 442cc2df49SGarrett Wollman #include "opt_inet.h" 452cc2df49SGarrett Wollman 462cc2df49SGarrett Wollman #include <sys/param.h> 472cc2df49SGarrett Wollman #include <sys/kernel.h> 48f731f104SBill Paul #include <sys/malloc.h> 492cc2df49SGarrett Wollman #include <sys/mbuf.h> 502b120974SPeter Wemm #include <sys/module.h> 51f731f104SBill Paul #include <sys/queue.h> 522cc2df49SGarrett Wollman #include <sys/socket.h> 532cc2df49SGarrett Wollman #include <sys/sockio.h> 542cc2df49SGarrett Wollman #include <sys/sysctl.h> 552cc2df49SGarrett Wollman #include <sys/systm.h> 562cc2df49SGarrett Wollman 572cc2df49SGarrett Wollman #include <net/bpf.h> 582cc2df49SGarrett Wollman #include <net/ethernet.h> 592cc2df49SGarrett Wollman #include <net/if.h> 602cc2df49SGarrett Wollman #include <net/if_arp.h> 612cc2df49SGarrett Wollman #include <net/if_dl.h> 622cc2df49SGarrett Wollman #include <net/if_types.h> 632cc2df49SGarrett Wollman #include <net/if_vlan_var.h> 642cc2df49SGarrett Wollman 652cc2df49SGarrett Wollman #ifdef INET 662cc2df49SGarrett Wollman #include <netinet/in.h> 672cc2df49SGarrett Wollman #include <netinet/if_ether.h> 682cc2df49SGarrett Wollman #endif 692cc2df49SGarrett Wollman 709d4fe4b2SBrooks Davis #define VLANNAME "vlan" 719d4fe4b2SBrooks Davis 72a3814acfSSam Leffler struct vlan_mc_entry { 73a3814acfSSam Leffler struct ether_addr mc_addr; 74a3814acfSSam Leffler SLIST_ENTRY(vlan_mc_entry) mc_entries; 75a3814acfSSam Leffler }; 76a3814acfSSam Leffler 77a3814acfSSam Leffler struct ifvlan { 78a3814acfSSam Leffler struct arpcom ifv_ac; /* make this an interface */ 79a3814acfSSam Leffler struct ifnet *ifv_p; /* parent inteface of this vlan */ 80a3814acfSSam Leffler struct ifv_linkmib { 81a3814acfSSam Leffler int ifvm_parent; 82a3814acfSSam Leffler int ifvm_encaplen; /* encapsulation length */ 83a3814acfSSam Leffler int ifvm_mtufudge; /* MTU fudged by this much */ 84a3814acfSSam Leffler int ifvm_mintu; /* min transmission unit */ 85a3814acfSSam Leffler u_int16_t ifvm_proto; /* encapsulation ethertype */ 86a3814acfSSam Leffler u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ 87a3814acfSSam Leffler } ifv_mib; 88a3814acfSSam Leffler SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; 89a3814acfSSam Leffler LIST_ENTRY(ifvlan) ifv_list; 90a3814acfSSam Leffler int ifv_flags; 91a3814acfSSam Leffler }; 92a3814acfSSam Leffler #define ifv_if ifv_ac.ac_if 93a3814acfSSam Leffler #define ifv_tag ifv_mib.ifvm_tag 94a3814acfSSam Leffler #define ifv_encaplen ifv_mib.ifvm_encaplen 95a3814acfSSam Leffler #define ifv_mtufudge ifv_mib.ifvm_mtufudge 96a3814acfSSam Leffler #define ifv_mintu ifv_mib.ifvm_mintu 97a3814acfSSam Leffler 98a3814acfSSam Leffler #define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ 99a3814acfSSam Leffler 1004a408dcbSBill Paul SYSCTL_DECL(_net_link); 101d2a75853SBill Fenner SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 1022cc2df49SGarrett Wollman SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 1032cc2df49SGarrett Wollman 1049d4fe4b2SBrooks Davis static MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface"); 1059d4fe4b2SBrooks Davis static LIST_HEAD(, ifvlan) ifv_list; 1062cc2df49SGarrett Wollman 1073b16e7b2SMaxime Henrion static int vlan_clone_create(struct if_clone *, int); 108ae5a19beSBrooks Davis static void vlan_clone_destroy(struct ifnet *); 1092cc2df49SGarrett Wollman static void vlan_start(struct ifnet *ifp); 1102cc2df49SGarrett Wollman static void vlan_ifinit(void *foo); 111a3814acfSSam Leffler static void vlan_input(struct ifnet *ifp, struct mbuf *m); 112cfe8b629SGarrett Wollman static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 113f731f104SBill Paul static int vlan_setmulti(struct ifnet *ifp); 114f731f104SBill Paul static int vlan_unconfig(struct ifnet *ifp); 115f731f104SBill Paul static int vlan_config(struct ifvlan *ifv, struct ifnet *p); 116f731f104SBill Paul 1173b16e7b2SMaxime Henrion struct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan", 118ae5a19beSBrooks Davis vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT); 1199d4fe4b2SBrooks Davis 120f731f104SBill Paul /* 121f731f104SBill Paul * Program our multicast filter. What we're actually doing is 122f731f104SBill Paul * programming the multicast filter of the parent. This has the 123f731f104SBill Paul * side effect of causing the parent interface to receive multicast 124f731f104SBill Paul * traffic that it doesn't really want, which ends up being discarded 125f731f104SBill Paul * later by the upper protocol layers. Unfortunately, there's no way 126f731f104SBill Paul * to avoid this: there really is only one physical interface. 127f731f104SBill Paul */ 1282b120974SPeter Wemm static int 1292b120974SPeter Wemm vlan_setmulti(struct ifnet *ifp) 130f731f104SBill Paul { 131f731f104SBill Paul struct ifnet *ifp_p; 132f731f104SBill Paul struct ifmultiaddr *ifma, *rifma = NULL; 133f731f104SBill Paul struct ifvlan *sc; 134f731f104SBill Paul struct vlan_mc_entry *mc = NULL; 135f731f104SBill Paul struct sockaddr_dl sdl; 136f731f104SBill Paul int error; 137f731f104SBill Paul 138f731f104SBill Paul /* Find the parent. */ 139f731f104SBill Paul sc = ifp->if_softc; 140f731f104SBill Paul ifp_p = sc->ifv_p; 141f731f104SBill Paul 1421b2a4f7aSBill Fenner /* 1431b2a4f7aSBill Fenner * If we don't have a parent, just remember the membership for 1441b2a4f7aSBill Fenner * when we do. 1451b2a4f7aSBill Fenner */ 1461b2a4f7aSBill Fenner if (ifp_p == NULL) 1471b2a4f7aSBill Fenner return(0); 1481b2a4f7aSBill Fenner 14924993214SYaroslav Tykhiy bzero((char *)&sdl, sizeof sdl); 15024993214SYaroslav Tykhiy sdl.sdl_len = sizeof sdl; 151f731f104SBill Paul sdl.sdl_family = AF_LINK; 15226e30963SBill Fenner sdl.sdl_index = ifp_p->if_index; 15326e30963SBill Fenner sdl.sdl_type = IFT_ETHER; 15424993214SYaroslav Tykhiy sdl.sdl_alen = ETHER_ADDR_LEN; 155f731f104SBill Paul 156f731f104SBill Paul /* First, remove any existing filter entries. */ 15722f29826SPoul-Henning Kamp while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 15822f29826SPoul-Henning Kamp mc = SLIST_FIRST(&sc->vlan_mc_listhead); 159f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 160f731f104SBill Paul error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 161f731f104SBill Paul if (error) 162f731f104SBill Paul return(error); 163f731f104SBill Paul SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 1649d4fe4b2SBrooks Davis free(mc, M_VLAN); 165f731f104SBill Paul } 166f731f104SBill Paul 167f731f104SBill Paul /* Now program new ones. */ 1686817526dSPoul-Henning Kamp TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 169f731f104SBill Paul if (ifma->ifma_addr->sa_family != AF_LINK) 170f731f104SBill Paul continue; 17144956c98SAlfred Perlstein mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, 0); 172f731f104SBill Paul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 173f731f104SBill Paul (char *)&mc->mc_addr, ETHER_ADDR_LEN); 174f731f104SBill Paul SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 17526e30963SBill Fenner bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 17626e30963SBill Fenner LLADDR(&sdl), ETHER_ADDR_LEN); 177f731f104SBill Paul error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 178f731f104SBill Paul if (error) 179f731f104SBill Paul return(error); 180f731f104SBill Paul } 181f731f104SBill Paul 182f731f104SBill Paul return(0); 183f731f104SBill Paul } 1842cc2df49SGarrett Wollman 185a3814acfSSam Leffler /* 186a3814acfSSam Leffler * VLAN support can be loaded as a module. The only place in the 187a3814acfSSam Leffler * system that's intimately aware of this is ether_input. We hook 188a3814acfSSam Leffler * into this code through vlan_input_p which is defined there and 189a3814acfSSam Leffler * set here. Noone else in the system should be aware of this so 190a3814acfSSam Leffler * we use an explicit reference here. 191a3814acfSSam Leffler * 192a3814acfSSam Leffler * NB: Noone should ever need to check if vlan_input_p is null or 193a3814acfSSam Leffler * not. This is because interfaces have a count of the number 194a3814acfSSam Leffler * of active vlans (if_nvlans) and this should never be bumped 195a3814acfSSam Leffler * except by vlan_config--which is in this module so therefore 196a3814acfSSam Leffler * the module must be loaded and vlan_input_p must be non-NULL. 197a3814acfSSam Leffler */ 198a3814acfSSam Leffler extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); 199a3814acfSSam Leffler 2002b120974SPeter Wemm static int 2012b120974SPeter Wemm vlan_modevent(module_t mod, int type, void *data) 2022b120974SPeter Wemm { 2039d4fe4b2SBrooks Davis 2042b120974SPeter Wemm switch (type) { 2052b120974SPeter Wemm case MOD_LOAD: 2069d4fe4b2SBrooks Davis LIST_INIT(&ifv_list); 2079d4fe4b2SBrooks Davis vlan_input_p = vlan_input; 2089d4fe4b2SBrooks Davis if_clone_attach(&vlan_cloner); 2092b120974SPeter Wemm break; 2102b120974SPeter Wemm case MOD_UNLOAD: 2119d4fe4b2SBrooks Davis if_clone_detach(&vlan_cloner); 2129d4fe4b2SBrooks Davis vlan_input_p = NULL; 2139d4fe4b2SBrooks Davis while (!LIST_EMPTY(&ifv_list)) 2149d4fe4b2SBrooks Davis vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if); 2159d4fe4b2SBrooks Davis break; 2162b120974SPeter Wemm } 2172b120974SPeter Wemm return 0; 2182b120974SPeter Wemm } 2192b120974SPeter Wemm 2202b120974SPeter Wemm static moduledata_t vlan_mod = { 2212b120974SPeter Wemm "if_vlan", 2222b120974SPeter Wemm vlan_modevent, 2232b120974SPeter Wemm 0 2242b120974SPeter Wemm }; 2252b120974SPeter Wemm 2262b120974SPeter Wemm DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2272cc2df49SGarrett Wollman 2289d4fe4b2SBrooks Davis static int 2293b16e7b2SMaxime Henrion vlan_clone_create(struct if_clone *ifc, int unit) 2309d4fe4b2SBrooks Davis { 2319d4fe4b2SBrooks Davis struct ifvlan *ifv; 2329d4fe4b2SBrooks Davis struct ifnet *ifp; 2339d4fe4b2SBrooks Davis int s; 2349d4fe4b2SBrooks Davis 23544956c98SAlfred Perlstein ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_ZERO); 2369d4fe4b2SBrooks Davis ifp = &ifv->ifv_if; 2379d4fe4b2SBrooks Davis SLIST_INIT(&ifv->vlan_mc_listhead); 2389d4fe4b2SBrooks Davis 2399d4fe4b2SBrooks Davis s = splnet(); 2409d4fe4b2SBrooks Davis LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 2419d4fe4b2SBrooks Davis splx(s); 2429d4fe4b2SBrooks Davis 2439d4fe4b2SBrooks Davis ifp->if_softc = ifv; 2449d4fe4b2SBrooks Davis ifp->if_name = "vlan"; 2453b16e7b2SMaxime Henrion ifp->if_unit = unit; 2469d4fe4b2SBrooks Davis /* NB: flags are not set here */ 2479d4fe4b2SBrooks Davis ifp->if_linkmib = &ifv->ifv_mib; 2489d4fe4b2SBrooks Davis ifp->if_linkmiblen = sizeof ifv->ifv_mib; 2499d4fe4b2SBrooks Davis /* NB: mtu is not set here */ 2509d4fe4b2SBrooks Davis 2519d4fe4b2SBrooks Davis ifp->if_init = vlan_ifinit; 2529d4fe4b2SBrooks Davis ifp->if_start = vlan_start; 2539d4fe4b2SBrooks Davis ifp->if_ioctl = vlan_ioctl; 2549d4fe4b2SBrooks Davis ifp->if_snd.ifq_maxlen = ifqmaxlen; 255a3814acfSSam Leffler ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr); 2569d4fe4b2SBrooks Davis /* Now undo some of the damage... */ 257211f625aSBill Fenner ifp->if_baudrate = 0; 258a3814acfSSam Leffler ifp->if_type = IFT_L2VLAN; 259a3814acfSSam Leffler ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; 2609d4fe4b2SBrooks Davis 2619d4fe4b2SBrooks Davis return (0); 2629d4fe4b2SBrooks Davis } 2639d4fe4b2SBrooks Davis 264ae5a19beSBrooks Davis static void 2659d4fe4b2SBrooks Davis vlan_clone_destroy(struct ifnet *ifp) 2669d4fe4b2SBrooks Davis { 2679d4fe4b2SBrooks Davis struct ifvlan *ifv = ifp->if_softc; 2689d4fe4b2SBrooks Davis int s; 2699d4fe4b2SBrooks Davis 2709d4fe4b2SBrooks Davis s = splnet(); 2719d4fe4b2SBrooks Davis LIST_REMOVE(ifv, ifv_list); 2729d4fe4b2SBrooks Davis vlan_unconfig(ifp); 2739d4fe4b2SBrooks Davis splx(s); 2749d4fe4b2SBrooks Davis 275a3814acfSSam Leffler ether_ifdetach(ifp); 2769d4fe4b2SBrooks Davis 2779d4fe4b2SBrooks Davis free(ifv, M_VLAN); 2789d4fe4b2SBrooks Davis } 2799d4fe4b2SBrooks Davis 2802cc2df49SGarrett Wollman static void 2812cc2df49SGarrett Wollman vlan_ifinit(void *foo) 2822cc2df49SGarrett Wollman { 283f731f104SBill Paul return; 2842cc2df49SGarrett Wollman } 2852cc2df49SGarrett Wollman 2862cc2df49SGarrett Wollman static void 2872cc2df49SGarrett Wollman vlan_start(struct ifnet *ifp) 2882cc2df49SGarrett Wollman { 2892cc2df49SGarrett Wollman struct ifvlan *ifv; 2902cc2df49SGarrett Wollman struct ifnet *p; 2912cc2df49SGarrett Wollman struct ether_vlan_header *evl; 2925326b23cSMatthew N. Dodd struct mbuf *m; 2932cc2df49SGarrett Wollman 2942cc2df49SGarrett Wollman ifv = ifp->if_softc; 2952cc2df49SGarrett Wollman p = ifv->ifv_p; 2962cc2df49SGarrett Wollman 2972cc2df49SGarrett Wollman ifp->if_flags |= IFF_OACTIVE; 2982cc2df49SGarrett Wollman for (;;) { 2992cc2df49SGarrett Wollman IF_DEQUEUE(&ifp->if_snd, m); 3002cc2df49SGarrett Wollman if (m == 0) 3012cc2df49SGarrett Wollman break; 302a3814acfSSam Leffler BPF_MTAP(ifp, m); 3032cc2df49SGarrett Wollman 304f731f104SBill Paul /* 30524993214SYaroslav Tykhiy * Do not run parent's if_start() if the parent is not up, 30624993214SYaroslav Tykhiy * or parent's driver will cause a system crash. 30724993214SYaroslav Tykhiy */ 30824993214SYaroslav Tykhiy if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 30924993214SYaroslav Tykhiy (IFF_UP | IFF_RUNNING)) { 31024993214SYaroslav Tykhiy m_freem(m); 311a3814acfSSam Leffler ifp->if_collisions++; 31224993214SYaroslav Tykhiy continue; 31324993214SYaroslav Tykhiy } 31424993214SYaroslav Tykhiy 31524993214SYaroslav Tykhiy /* 316a3814acfSSam Leffler * If underlying interface can do VLAN tag insertion itself, 317a3814acfSSam Leffler * just pass the packet along. However, we need some way to 318a3814acfSSam Leffler * tell the interface where the packet came from so that it 319a3814acfSSam Leffler * knows how to find the VLAN tag to use, so we attach a 320a3814acfSSam Leffler * packet tag that holds it. 321f731f104SBill Paul */ 322a3814acfSSam Leffler if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) { 323a3814acfSSam Leffler struct m_tag *mtag = m_tag_alloc(MTAG_VLAN, 324a3814acfSSam Leffler MTAG_VLAN_TAG, 325a3814acfSSam Leffler sizeof (u_int), 32644956c98SAlfred Perlstein M_NOWAIT); 327a3814acfSSam Leffler if (mtag == NULL) { 328a3814acfSSam Leffler ifp->if_oerrors++; 329a3814acfSSam Leffler m_freem(m); 330a3814acfSSam Leffler continue; 331a3814acfSSam Leffler } 332a3814acfSSam Leffler *(u_int*)(mtag+1) = ifv->ifv_tag; 333a3814acfSSam Leffler m_tag_prepend(m, mtag); 334f731f104SBill Paul } else { 33544956c98SAlfred Perlstein M_PREPEND(m, ifv->ifv_encaplen, M_NOWAIT); 3364af90a4dSMatthew N. Dodd if (m == NULL) { 337a3814acfSSam Leffler if_printf(ifp, "unable to prepend VLAN header"); 3384af90a4dSMatthew N. Dodd ifp->if_ierrors++; 3392cc2df49SGarrett Wollman continue; 3404af90a4dSMatthew N. Dodd } 3412cc2df49SGarrett Wollman /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 3422cc2df49SGarrett Wollman 343a3814acfSSam Leffler if (m->m_len < sizeof(*evl)) { 344a3814acfSSam Leffler m = m_pullup(m, sizeof(*evl)); 3455326b23cSMatthew N. Dodd if (m == NULL) { 346a3814acfSSam Leffler if_printf(ifp, 347a3814acfSSam Leffler "cannot pullup VLAN header"); 3484af90a4dSMatthew N. Dodd ifp->if_ierrors++; 3494af90a4dSMatthew N. Dodd continue; 3504af90a4dSMatthew N. Dodd } 351a3814acfSSam Leffler } 3524af90a4dSMatthew N. Dodd 3532cc2df49SGarrett Wollman /* 3542cc2df49SGarrett Wollman * Transform the Ethernet header into an Ethernet header 3552cc2df49SGarrett Wollman * with 802.1Q encapsulation. 3562cc2df49SGarrett Wollman */ 357a3814acfSSam Leffler bcopy(mtod(m, char *) + ifv->ifv_encaplen, 358a3814acfSSam Leffler mtod(m, char *), sizeof(struct ether_header)); 3592cc2df49SGarrett Wollman evl = mtod(m, struct ether_vlan_header *); 3602cc2df49SGarrett Wollman evl->evl_proto = evl->evl_encap_proto; 3619d4fe4b2SBrooks Davis evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 3622cc2df49SGarrett Wollman evl->evl_tag = htons(ifv->ifv_tag); 363f731f104SBill Paul #ifdef DEBUG 364a9283398SJohn Baldwin printf("vlan_start: %*D\n", (int)sizeof *evl, 36565f1bb65SPeter Wemm (unsigned char *)evl, ":"); 366f731f104SBill Paul #endif 367f731f104SBill Paul } 3682cc2df49SGarrett Wollman 3692cc2df49SGarrett Wollman /* 3702cc2df49SGarrett Wollman * Send it, precisely as ether_output() would have. 3712cc2df49SGarrett Wollman * We are already running at splimp. 3722cc2df49SGarrett Wollman */ 373df5e1987SJonathan Lemon if (IF_HANDOFF(&p->if_snd, m, p)) 374f731f104SBill Paul ifp->if_opackets++; 375df5e1987SJonathan Lemon else 376df5e1987SJonathan Lemon ifp->if_oerrors++; 3772cc2df49SGarrett Wollman } 3782cc2df49SGarrett Wollman ifp->if_flags &= ~IFF_OACTIVE; 379f731f104SBill Paul 380f731f104SBill Paul return; 381f731f104SBill Paul } 382f731f104SBill Paul 383a3814acfSSam Leffler static void 384a3814acfSSam Leffler vlan_input(struct ifnet *ifp, struct mbuf *m) 385f731f104SBill Paul { 386a3814acfSSam Leffler struct ether_vlan_header *evl; 387f731f104SBill Paul struct ifvlan *ifv; 388a3814acfSSam Leffler struct m_tag *mtag; 389a3814acfSSam Leffler u_int tag; 390a3814acfSSam Leffler 391a3814acfSSam Leffler mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL); 392a3814acfSSam Leffler if (mtag != NULL) { 393a3814acfSSam Leffler /* 394a3814acfSSam Leffler * Packet is tagged, m contains a normal 395a3814acfSSam Leffler * Ethernet frame; the tag is stored out-of-band. 396a3814acfSSam Leffler */ 397a3814acfSSam Leffler tag = *(u_int*)(mtag+1); 398a3814acfSSam Leffler m_tag_delete(m, mtag); 399a3814acfSSam Leffler } else { 400a3814acfSSam Leffler switch (ifp->if_type) { 401a3814acfSSam Leffler case IFT_ETHER: 402a3814acfSSam Leffler if (m->m_len < sizeof (*evl) && 403a3814acfSSam Leffler (m = m_pullup(m, sizeof (*evl))) == NULL) { 404a3814acfSSam Leffler if_printf(ifp, "cannot pullup VLAN header\n"); 405a3814acfSSam Leffler return; 406a3814acfSSam Leffler } 407a3814acfSSam Leffler evl = mtod(m, struct ether_vlan_header *); 408a3814acfSSam Leffler KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN, 409a3814acfSSam Leffler ("vlan_input: bad encapsulated protocols (%u)", 410a3814acfSSam Leffler ntohs(evl->evl_encap_proto))); 411a3814acfSSam Leffler 412a3814acfSSam Leffler tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 413f731f104SBill Paul 4147a46ec8fSBrooks Davis /* 415a3814acfSSam Leffler * Restore the original ethertype. We'll remove 416a3814acfSSam Leffler * the encapsulation after we've found the vlan 417a3814acfSSam Leffler * interface corresponding to the tag. 4187a46ec8fSBrooks Davis */ 419a3814acfSSam Leffler evl->evl_encap_proto = evl->evl_proto; 420a3814acfSSam Leffler break; 421a3814acfSSam Leffler default: 422a3814acfSSam Leffler tag = (u_int) -1; 423a3814acfSSam Leffler #ifdef DIAGNOSTIC 424a3814acfSSam Leffler panic("vlan_input: unsupported if type %u", ifp->if_type); 425a3814acfSSam Leffler #endif 426a3814acfSSam Leffler break; 427a3814acfSSam Leffler } 4287a46ec8fSBrooks Davis } 4297a46ec8fSBrooks Davis 4309d4fe4b2SBrooks Davis for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 431a3814acfSSam Leffler ifv = LIST_NEXT(ifv, ifv_list)) 432a3814acfSSam Leffler if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) 433f731f104SBill Paul break; 434f731f104SBill Paul 435242c766bSBill Fenner if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 4367d3e4c6eSLuigi Rizzo m_freem(m); 437a3814acfSSam Leffler ifp->if_noproto++; 438a3814acfSSam Leffler return; 439f731f104SBill Paul } 440f731f104SBill Paul 441a3814acfSSam Leffler if (mtag == NULL) { 442f731f104SBill Paul /* 443a3814acfSSam Leffler * Packet had an in-line encapsulation header; 444a3814acfSSam Leffler * remove it. The original header has already 445a3814acfSSam Leffler * been fixed up above. 446f731f104SBill Paul */ 447a3814acfSSam Leffler bcopy(mtod(m, caddr_t), 448a3814acfSSam Leffler mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN, 449a3814acfSSam Leffler sizeof (struct ether_header)); 450a3814acfSSam Leffler m_adj(m, ETHER_VLAN_ENCAP_LEN); 451a3814acfSSam Leffler } 452a3814acfSSam Leffler 453f731f104SBill Paul m->m_pkthdr.rcvif = &ifv->ifv_if; 454f731f104SBill Paul ifv->ifv_if.if_ipackets++; 4552cc2df49SGarrett Wollman 456a3814acfSSam Leffler /* Pass it back through the parent's input routine. */ 457a3814acfSSam Leffler (*ifp->if_input)(&ifv->ifv_if, m); 4582cc2df49SGarrett Wollman } 4592cc2df49SGarrett Wollman 4602cc2df49SGarrett Wollman static int 4612cc2df49SGarrett Wollman vlan_config(struct ifvlan *ifv, struct ifnet *p) 4622cc2df49SGarrett Wollman { 4632cc2df49SGarrett Wollman struct ifaddr *ifa1, *ifa2; 4642cc2df49SGarrett Wollman struct sockaddr_dl *sdl1, *sdl2; 4652cc2df49SGarrett Wollman 4662cc2df49SGarrett Wollman if (p->if_data.ifi_type != IFT_ETHER) 4672cc2df49SGarrett Wollman return EPROTONOSUPPORT; 4682cc2df49SGarrett Wollman if (ifv->ifv_p) 4692cc2df49SGarrett Wollman return EBUSY; 4702cc2df49SGarrett Wollman 471a3814acfSSam Leffler ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 472a3814acfSSam Leffler ifv->ifv_mintu = ETHERMIN; 473a3814acfSSam Leffler ifv->ifv_flags = 0; 474a3814acfSSam Leffler 475a3814acfSSam Leffler /* 476a3814acfSSam Leffler * If the parent supports the VLAN_MTU capability, 477a3814acfSSam Leffler * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 478a3814acfSSam Leffler * enable it. 479a3814acfSSam Leffler */ 480a3814acfSSam Leffler p->if_nvlans++; 481a3814acfSSam Leffler if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) { 482a3814acfSSam Leffler /* 483a3814acfSSam Leffler * Enable Tx/Rx of VLAN-sized frames. 484a3814acfSSam Leffler */ 485a3814acfSSam Leffler p->if_capenable |= IFCAP_VLAN_MTU; 486a3814acfSSam Leffler if (p->if_flags & IFF_UP) { 487a3814acfSSam Leffler struct ifreq ifr; 488a3814acfSSam Leffler int error; 489a3814acfSSam Leffler 490a3814acfSSam Leffler ifr.ifr_flags = p->if_flags; 491a3814acfSSam Leffler error = (*p->if_ioctl)(p, SIOCSIFFLAGS, 492a3814acfSSam Leffler (caddr_t) &ifr); 493a3814acfSSam Leffler if (error) { 494a3814acfSSam Leffler p->if_nvlans--; 495a3814acfSSam Leffler if (p->if_nvlans == 0) 496a3814acfSSam Leffler p->if_capenable &= ~IFCAP_VLAN_MTU; 497a3814acfSSam Leffler return (error); 498a3814acfSSam Leffler } 499a3814acfSSam Leffler } 500a3814acfSSam Leffler ifv->ifv_mtufudge = 0; 501a3814acfSSam Leffler } else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) { 502a3814acfSSam Leffler /* 503a3814acfSSam Leffler * Fudge the MTU by the encapsulation size. This 504a3814acfSSam Leffler * makes us incompatible with strictly compliant 505a3814acfSSam Leffler * 802.1Q implementations, but allows us to use 506a3814acfSSam Leffler * the feature with other NetBSD implementations, 507a3814acfSSam Leffler * which might still be useful. 508a3814acfSSam Leffler */ 509a3814acfSSam Leffler ifv->ifv_mtufudge = ifv->ifv_encaplen; 510a3814acfSSam Leffler } 511a3814acfSSam Leffler 512a3814acfSSam Leffler ifv->ifv_p = p; 513a3814acfSSam Leffler ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge; 5142cc2df49SGarrett Wollman /* 51524993214SYaroslav Tykhiy * Copy only a selected subset of flags from the parent. 51624993214SYaroslav Tykhiy * Other flags are none of our business. 5172cc2df49SGarrett Wollman */ 51824993214SYaroslav Tykhiy ifv->ifv_if.if_flags = (p->if_flags & 51924993214SYaroslav Tykhiy (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 5202cc2df49SGarrett Wollman 5212cc2df49SGarrett Wollman /* 522a3814acfSSam Leffler * If the parent interface can do hardware-assisted 523a3814acfSSam Leffler * VLAN encapsulation, then propagate its hardware- 524a3814acfSSam Leffler * assisted checksumming flags. 525a3814acfSSam Leffler */ 526a3814acfSSam Leffler if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) 527a3814acfSSam Leffler ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM; 528a3814acfSSam Leffler 529a3814acfSSam Leffler /* 5302cc2df49SGarrett Wollman * Set up our ``Ethernet address'' to reflect the underlying 5312cc2df49SGarrett Wollman * physical interface's. 5322cc2df49SGarrett Wollman */ 533f9132cebSJonathan Lemon ifa1 = ifaddr_byindex(ifv->ifv_if.if_index); 534f9132cebSJonathan Lemon ifa2 = ifaddr_byindex(p->if_index); 5352cc2df49SGarrett Wollman sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 5362cc2df49SGarrett Wollman sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 5372cc2df49SGarrett Wollman sdl1->sdl_type = IFT_ETHER; 5382cc2df49SGarrett Wollman sdl1->sdl_alen = ETHER_ADDR_LEN; 5392cc2df49SGarrett Wollman bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 5402cc2df49SGarrett Wollman bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 5411b2a4f7aSBill Fenner 5421b2a4f7aSBill Fenner /* 5431b2a4f7aSBill Fenner * Configure multicast addresses that may already be 5441b2a4f7aSBill Fenner * joined on the vlan device. 5451b2a4f7aSBill Fenner */ 5461b2a4f7aSBill Fenner (void)vlan_setmulti(&ifv->ifv_if); 5471b2a4f7aSBill Fenner 5482cc2df49SGarrett Wollman return 0; 5492cc2df49SGarrett Wollman } 5502cc2df49SGarrett Wollman 5512cc2df49SGarrett Wollman static int 552f731f104SBill Paul vlan_unconfig(struct ifnet *ifp) 553f731f104SBill Paul { 554f731f104SBill Paul struct ifaddr *ifa; 555f731f104SBill Paul struct sockaddr_dl *sdl; 556f731f104SBill Paul struct vlan_mc_entry *mc; 557f731f104SBill Paul struct ifvlan *ifv; 558f731f104SBill Paul struct ifnet *p; 559f731f104SBill Paul int error; 560f731f104SBill Paul 561f731f104SBill Paul ifv = ifp->if_softc; 562f731f104SBill Paul p = ifv->ifv_p; 563f731f104SBill Paul 5641b2a4f7aSBill Fenner if (p) { 5651b2a4f7aSBill Fenner struct sockaddr_dl sdl; 5661b2a4f7aSBill Fenner 567f731f104SBill Paul /* 568f731f104SBill Paul * Since the interface is being unconfigured, we need to 569f731f104SBill Paul * empty the list of multicast groups that we may have joined 5701b2a4f7aSBill Fenner * while we were alive from the parent's list. 571f731f104SBill Paul */ 5721b2a4f7aSBill Fenner bzero((char *)&sdl, sizeof sdl); 5731b2a4f7aSBill Fenner sdl.sdl_len = sizeof sdl; 574f731f104SBill Paul sdl.sdl_family = AF_LINK; 5751b2a4f7aSBill Fenner sdl.sdl_index = p->if_index; 5761b2a4f7aSBill Fenner sdl.sdl_type = IFT_ETHER; 5771b2a4f7aSBill Fenner sdl.sdl_alen = ETHER_ADDR_LEN; 5781b2a4f7aSBill Fenner 5791b2a4f7aSBill Fenner while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 58022f29826SPoul-Henning Kamp mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 581f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 582f731f104SBill Paul error = if_delmulti(p, (struct sockaddr *)&sdl); 583f731f104SBill Paul if (error) 584f731f104SBill Paul return(error); 585f731f104SBill Paul SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 5869d4fe4b2SBrooks Davis free(mc, M_VLAN); 587f731f104SBill Paul } 588a3814acfSSam Leffler 589a3814acfSSam Leffler p->if_nvlans--; 590a3814acfSSam Leffler if (p->if_nvlans == 0) { 591a3814acfSSam Leffler /* 592a3814acfSSam Leffler * Disable Tx/Rx of VLAN-sized frames. 593a3814acfSSam Leffler */ 594a3814acfSSam Leffler p->if_capenable &= ~IFCAP_VLAN_MTU; 595a3814acfSSam Leffler if (p->if_flags & IFF_UP) { 596a3814acfSSam Leffler struct ifreq ifr; 597a3814acfSSam Leffler 598a3814acfSSam Leffler ifr.ifr_flags = p->if_flags; 599a3814acfSSam Leffler (*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr); 600a3814acfSSam Leffler } 601a3814acfSSam Leffler } 6021b2a4f7aSBill Fenner } 603f731f104SBill Paul 604f731f104SBill Paul /* Disconnect from parent. */ 605f731f104SBill Paul ifv->ifv_p = NULL; 606a3814acfSSam Leffler ifv->ifv_if.if_mtu = ETHERMTU; /* XXX why not 0? */ 607a3814acfSSam Leffler ifv->ifv_flags = 0; 608f731f104SBill Paul 609f731f104SBill Paul /* Clear our MAC address. */ 610f9132cebSJonathan Lemon ifa = ifaddr_byindex(ifv->ifv_if.if_index); 611f731f104SBill Paul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 612f731f104SBill Paul sdl->sdl_type = IFT_ETHER; 613f731f104SBill Paul sdl->sdl_alen = ETHER_ADDR_LEN; 614f731f104SBill Paul bzero(LLADDR(sdl), ETHER_ADDR_LEN); 615f731f104SBill Paul bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 616f731f104SBill Paul 617f731f104SBill Paul return 0; 618f731f104SBill Paul } 619f731f104SBill Paul 620f731f104SBill Paul static int 621a3814acfSSam Leffler vlan_set_promisc(struct ifnet *ifp) 622a3814acfSSam Leffler { 623a3814acfSSam Leffler struct ifvlan *ifv = ifp->if_softc; 624a3814acfSSam Leffler int error = 0; 625a3814acfSSam Leffler 626a3814acfSSam Leffler if ((ifp->if_flags & IFF_PROMISC) != 0) { 627a3814acfSSam Leffler if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { 628a3814acfSSam Leffler error = ifpromisc(ifv->ifv_p, 1); 629a3814acfSSam Leffler if (error == 0) 630a3814acfSSam Leffler ifv->ifv_flags |= IFVF_PROMISC; 631a3814acfSSam Leffler } 632a3814acfSSam Leffler } else { 633a3814acfSSam Leffler if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { 634a3814acfSSam Leffler error = ifpromisc(ifv->ifv_p, 0); 635a3814acfSSam Leffler if (error == 0) 636a3814acfSSam Leffler ifv->ifv_flags &= ~IFVF_PROMISC; 637a3814acfSSam Leffler } 638a3814acfSSam Leffler } 639a3814acfSSam Leffler 640a3814acfSSam Leffler return (error); 641a3814acfSSam Leffler } 642a3814acfSSam Leffler 643a3814acfSSam Leffler static int 644cfe8b629SGarrett Wollman vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 6452cc2df49SGarrett Wollman { 6462cc2df49SGarrett Wollman struct ifaddr *ifa; 6472cc2df49SGarrett Wollman struct ifnet *p; 6482cc2df49SGarrett Wollman struct ifreq *ifr; 6492cc2df49SGarrett Wollman struct ifvlan *ifv; 6502cc2df49SGarrett Wollman struct vlanreq vlr; 6512cc2df49SGarrett Wollman int error = 0; 6522cc2df49SGarrett Wollman 6532cc2df49SGarrett Wollman ifr = (struct ifreq *)data; 6542cc2df49SGarrett Wollman ifa = (struct ifaddr *)data; 6552cc2df49SGarrett Wollman ifv = ifp->if_softc; 6562cc2df49SGarrett Wollman 6572cc2df49SGarrett Wollman switch (cmd) { 6582cc2df49SGarrett Wollman case SIOCSIFADDR: 6592cc2df49SGarrett Wollman ifp->if_flags |= IFF_UP; 6602cc2df49SGarrett Wollman 6612cc2df49SGarrett Wollman switch (ifa->ifa_addr->sa_family) { 6622cc2df49SGarrett Wollman #ifdef INET 6632cc2df49SGarrett Wollman case AF_INET: 664322dcb8dSMax Khon arp_ifinit(&ifv->ifv_if, ifa); 6652cc2df49SGarrett Wollman break; 6662cc2df49SGarrett Wollman #endif 6672cc2df49SGarrett Wollman default: 6682cc2df49SGarrett Wollman break; 6692cc2df49SGarrett Wollman } 6702cc2df49SGarrett Wollman break; 6712cc2df49SGarrett Wollman 6722cc2df49SGarrett Wollman case SIOCGIFADDR: 6732cc2df49SGarrett Wollman { 6742cc2df49SGarrett Wollman struct sockaddr *sa; 6752cc2df49SGarrett Wollman 6762cc2df49SGarrett Wollman sa = (struct sockaddr *) &ifr->ifr_data; 6772cc2df49SGarrett Wollman bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 6782cc2df49SGarrett Wollman (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 6792cc2df49SGarrett Wollman } 6802cc2df49SGarrett Wollman break; 6812cc2df49SGarrett Wollman 682b3cca108SBill Fenner case SIOCGIFMEDIA: 683b3cca108SBill Fenner if (ifv->ifv_p != NULL) { 684b3cca108SBill Fenner error = (ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCGIFMEDIA, data); 685b3cca108SBill Fenner /* Limit the result to the parent's current config. */ 686b3cca108SBill Fenner if (error == 0) { 687b3cca108SBill Fenner struct ifmediareq *ifmr; 688b3cca108SBill Fenner 689b3cca108SBill Fenner ifmr = (struct ifmediareq *) data; 690b3cca108SBill Fenner if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { 691b3cca108SBill Fenner ifmr->ifm_count = 1; 692b3cca108SBill Fenner error = copyout(&ifmr->ifm_current, 693b3cca108SBill Fenner ifmr->ifm_ulist, 694b3cca108SBill Fenner sizeof(int)); 695b3cca108SBill Fenner } 696b3cca108SBill Fenner } 697b3cca108SBill Fenner } else 698b3cca108SBill Fenner error = EINVAL; 699b3cca108SBill Fenner break; 700b3cca108SBill Fenner 701b3cca108SBill Fenner case SIOCSIFMEDIA: 702b3cca108SBill Fenner error = EINVAL; 703b3cca108SBill Fenner break; 704b3cca108SBill Fenner 7052cc2df49SGarrett Wollman case SIOCSIFMTU: 7062cc2df49SGarrett Wollman /* 7072cc2df49SGarrett Wollman * Set the interface MTU. 7082cc2df49SGarrett Wollman */ 709a3814acfSSam Leffler if (ifv->ifv_p != NULL) { 710a3814acfSSam Leffler if (ifr->ifr_mtu > 711a3814acfSSam Leffler (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) || 712a3814acfSSam Leffler ifr->ifr_mtu < 713a3814acfSSam Leffler (ifv->ifv_mintu - ifv->ifv_mtufudge)) 7142cc2df49SGarrett Wollman error = EINVAL; 715a3814acfSSam Leffler else 7162cc2df49SGarrett Wollman ifp->if_mtu = ifr->ifr_mtu; 717a3814acfSSam Leffler } else 718a3814acfSSam Leffler error = EINVAL; 7192cc2df49SGarrett Wollman break; 7202cc2df49SGarrett Wollman 7212cc2df49SGarrett Wollman case SIOCSETVLAN: 7222cc2df49SGarrett Wollman error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 7232cc2df49SGarrett Wollman if (error) 7242cc2df49SGarrett Wollman break; 7252cc2df49SGarrett Wollman if (vlr.vlr_parent[0] == '\0') { 726f731f104SBill Paul vlan_unconfig(ifp); 72724993214SYaroslav Tykhiy if (ifp->if_flags & IFF_UP) { 72824993214SYaroslav Tykhiy int s = splimp(); 7292cc2df49SGarrett Wollman if_down(ifp); 73024993214SYaroslav Tykhiy splx(s); 73124993214SYaroslav Tykhiy } 73224993214SYaroslav Tykhiy ifp->if_flags &= ~IFF_RUNNING; 7332cc2df49SGarrett Wollman break; 7342cc2df49SGarrett Wollman } 7352cc2df49SGarrett Wollman p = ifunit(vlr.vlr_parent); 7362cc2df49SGarrett Wollman if (p == 0) { 7372cc2df49SGarrett Wollman error = ENOENT; 7382cc2df49SGarrett Wollman break; 7392cc2df49SGarrett Wollman } 7402cc2df49SGarrett Wollman error = vlan_config(ifv, p); 7412cc2df49SGarrett Wollman if (error) 7422cc2df49SGarrett Wollman break; 7432cc2df49SGarrett Wollman ifv->ifv_tag = vlr.vlr_tag; 744ae290324SJordan K. Hubbard ifp->if_flags |= IFF_RUNNING; 745a3814acfSSam Leffler 746a3814acfSSam Leffler /* Update promiscuous mode, if necessary. */ 747a3814acfSSam Leffler vlan_set_promisc(ifp); 7482cc2df49SGarrett Wollman break; 7492cc2df49SGarrett Wollman 7502cc2df49SGarrett Wollman case SIOCGETVLAN: 7512cc2df49SGarrett Wollman bzero(&vlr, sizeof vlr); 7522cc2df49SGarrett Wollman if (ifv->ifv_p) { 7532127f260SArchie Cobbs snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 7542127f260SArchie Cobbs "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 7552cc2df49SGarrett Wollman vlr.vlr_tag = ifv->ifv_tag; 7562cc2df49SGarrett Wollman } 7572cc2df49SGarrett Wollman error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 7582cc2df49SGarrett Wollman break; 7592cc2df49SGarrett Wollman 7602cc2df49SGarrett Wollman case SIOCSIFFLAGS: 7612cc2df49SGarrett Wollman /* 762a3814acfSSam Leffler * For promiscuous mode, we enable promiscuous mode on 763a3814acfSSam Leffler * the parent if we need promiscuous on the VLAN interface. 7642cc2df49SGarrett Wollman */ 765a3814acfSSam Leffler if (ifv->ifv_p != NULL) 766a3814acfSSam Leffler error = vlan_set_promisc(ifp); 7672cc2df49SGarrett Wollman break; 768a3814acfSSam Leffler 769f731f104SBill Paul case SIOCADDMULTI: 770f731f104SBill Paul case SIOCDELMULTI: 771f731f104SBill Paul error = vlan_setmulti(ifp); 772f731f104SBill Paul break; 7732cc2df49SGarrett Wollman default: 7742cc2df49SGarrett Wollman error = EINVAL; 7752cc2df49SGarrett Wollman } 7762cc2df49SGarrett Wollman return error; 7772cc2df49SGarrett Wollman } 778