xref: /netbsd/sys/net/if_vlan.c (revision 60b4fee9)
1*60b4fee9Syamaguchi /*	$NetBSD: if_vlan.c,v 1.170 2022/06/20 08:14:48 yamaguchi Exp $	*/
200746cb0Sthorpej 
3fd270400Smaxv /*
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*60b4fee9Syamaguchi __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.170 2022/06/20 08:14:48 yamaguchi Exp $");
8234d65a34Slukem 
83e66a68c0Sozaki-r #ifdef _KERNEL_OPT
8400746cb0Sthorpej #include "opt_inet.h"
85df6b1390Sozaki-r #include "opt_net_mpsafe.h"
86e66a68c0Sozaki-r #endif
8700746cb0Sthorpej 
8800746cb0Sthorpej #include <sys/param.h>
893043ffd9Sknakahara #include <sys/systm.h>
9000746cb0Sthorpej #include <sys/kernel.h>
9100746cb0Sthorpej #include <sys/mbuf.h>
9200746cb0Sthorpej #include <sys/queue.h>
9300746cb0Sthorpej #include <sys/socket.h>
9400746cb0Sthorpej #include <sys/sockio.h>
9500746cb0Sthorpej #include <sys/systm.h>
9600746cb0Sthorpej #include <sys/proc.h>
97874fef37Selad #include <sys/kauth.h>
983718f78aSozaki-r #include <sys/mutex.h>
993043ffd9Sknakahara #include <sys/kmem.h>
1003043ffd9Sknakahara #include <sys/cpu.h>
1013043ffd9Sknakahara #include <sys/pserialize.h>
1023043ffd9Sknakahara #include <sys/psref.h>
1033043ffd9Sknakahara #include <sys/pslist.h>
1043043ffd9Sknakahara #include <sys/atomic.h>
1054a3aea06Schristos #include <sys/device.h>
1064a3aea06Schristos #include <sys/module.h>
10700746cb0Sthorpej 
10800746cb0Sthorpej #include <net/bpf.h>
10900746cb0Sthorpej #include <net/if.h>
11000746cb0Sthorpej #include <net/if_dl.h>
11100746cb0Sthorpej #include <net/if_types.h>
11200746cb0Sthorpej #include <net/if_ether.h>
11300746cb0Sthorpej #include <net/if_vlanvar.h>
11400746cb0Sthorpej 
11500746cb0Sthorpej #ifdef INET
11600746cb0Sthorpej #include <netinet/in.h>
11700746cb0Sthorpej #include <netinet/if_inarp.h>
11800746cb0Sthorpej #endif
11901dd0176Sozaki-r #ifdef INET6
12001dd0176Sozaki-r #include <netinet6/in6_ifattach.h>
121b8372a5cSozaki-r #include <netinet6/in6_var.h>
122277e24bfSyamaguchi #include <netinet6/nd6.h>
12301dd0176Sozaki-r #endif
12400746cb0Sthorpej 
1254bf9ea45Schristos #include "ioconf.h"
1264bf9ea45Schristos 
12796375e93Sthorpej struct vlan_mc_entry {
12896375e93Sthorpej 	LIST_ENTRY(vlan_mc_entry)	mc_entries;
12996375e93Sthorpej 	/*
13096375e93Sthorpej 	 * A key to identify this entry.  The mc_addr below can't be
13196375e93Sthorpej 	 * used since multiple sockaddr may mapped into the same
13296375e93Sthorpej 	 * ether_multi (e.g., AF_UNSPEC).
13396375e93Sthorpej 	 */
134100a7af6Smsaitoh 	struct ether_multi	*mc_enm;
13596375e93Sthorpej 	struct sockaddr_storage		mc_addr;
13696375e93Sthorpej };
13796375e93Sthorpej 
1383043ffd9Sknakahara struct ifvlan_linkmib {
1393043ffd9Sknakahara 	struct ifvlan *ifvm_ifvlan;
14096375e93Sthorpej 	const struct vlan_multisw *ifvm_msw;
14196375e93Sthorpej 	int	ifvm_mtufudge;	/* MTU fudged by this much */
14296375e93Sthorpej 	int	ifvm_mintu;	/* min transmission unit */
1432b028087Smatt 	uint16_t ifvm_proto;	/* encapsulation ethertype */
1442b028087Smatt 	uint16_t ifvm_tag;	/* tag to apply on packets */
1453043ffd9Sknakahara 	struct ifnet *ifvm_p;	/* parent interface of this vlan */
1463043ffd9Sknakahara 
1473043ffd9Sknakahara 	struct psref_target ifvm_psref;
1483043ffd9Sknakahara };
1493043ffd9Sknakahara 
1503043ffd9Sknakahara struct ifvlan {
151100a7af6Smsaitoh 	struct ethercom ifv_ec;
1523043ffd9Sknakahara 	struct ifvlan_linkmib *ifv_mib;	/*
1533043ffd9Sknakahara 					 * reader must use vlan_getref_linkmib()
1543043ffd9Sknakahara 					 * instead of direct dereference
1553043ffd9Sknakahara 					 */
1563043ffd9Sknakahara 	kmutex_t ifv_lock;		/* writer lock for ifv_mib */
157c4d3aeb8Sjmcneill 	pserialize_t ifv_psz;
158cbb731a8Syamaguchi 	void *ifv_linkstate_hook;
159842c78c0Syamaguchi 	void *ifv_ifdetach_hook;
1603043ffd9Sknakahara 
16196375e93Sthorpej 	LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
1623043ffd9Sknakahara 	struct pslist_entry ifv_hash;
163c1e8f104Sthorpej 	int ifv_flags;
164842c78c0Syamaguchi 	bool ifv_stopping;
16596375e93Sthorpej };
16696375e93Sthorpej 
167c1e8f104Sthorpej #define	IFVF_PROMISC	0x01		/* promiscuous mode enabled */
168c1e8f104Sthorpej 
16996375e93Sthorpej #define	ifv_if		ifv_ec.ec_if
17096375e93Sthorpej 
17196375e93Sthorpej #define	ifv_msw		ifv_mib.ifvm_msw
17296375e93Sthorpej #define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
17396375e93Sthorpej #define	ifv_mintu	ifv_mib.ifvm_mintu
17496375e93Sthorpej #define	ifv_tag		ifv_mib.ifvm_tag
17596375e93Sthorpej 
17696375e93Sthorpej struct vlan_multisw {
17796375e93Sthorpej 	int	(*vmsw_addmulti)(struct ifvlan *, struct ifreq *);
17896375e93Sthorpej 	int	(*vmsw_delmulti)(struct ifvlan *, struct ifreq *);
17996375e93Sthorpej 	void	(*vmsw_purgemulti)(struct ifvlan *);
18096375e93Sthorpej };
18196375e93Sthorpej 
18296375e93Sthorpej static int	vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
18396375e93Sthorpej static int	vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
18496375e93Sthorpej static void	vlan_ether_purgemulti(struct ifvlan *);
18596375e93Sthorpej 
18696375e93Sthorpej const struct vlan_multisw vlan_ether_multisw = {
1870031abc3Smaxv 	.vmsw_addmulti = vlan_ether_addmulti,
1880031abc3Smaxv 	.vmsw_delmulti = vlan_ether_delmulti,
1890031abc3Smaxv 	.vmsw_purgemulti = vlan_ether_purgemulti,
19096375e93Sthorpej };
19196375e93Sthorpej 
19200746cb0Sthorpej static int	vlan_clone_create(struct if_clone *, int);
193b9c49ebfSpeter static int	vlan_clone_destroy(struct ifnet *);
19473fbed93Smsaitoh static int	vlan_config(struct ifvlan *, struct ifnet *, uint16_t);
19553524e44Schristos static int	vlan_ioctl(struct ifnet *, u_long, void *);
19600746cb0Sthorpej static void	vlan_start(struct ifnet *);
1973043ffd9Sknakahara static int	vlan_transmit(struct ifnet *, struct mbuf *);
198cbb731a8Syamaguchi static void	vlan_link_state_changed(void *);
199842c78c0Syamaguchi static void	vlan_ifdetach(void *);
200efbead50Sthorpej static void	vlan_unconfig(struct ifnet *);
20173fbed93Smsaitoh static int	vlan_unconfig_locked(struct ifvlan *, struct ifvlan_linkmib *);
2023043ffd9Sknakahara static void	vlan_hash_init(void);
2033043ffd9Sknakahara static int	vlan_hash_fini(void);
20466163bcfSmsaitoh static int	vlan_tag_hash(uint16_t, u_long);
2053a942150Syamaguchi static struct ifvlan_linkmib*
2063a942150Syamaguchi 		vlan_getref_linkmib(struct ifvlan *, struct psref *);
20773fbed93Smsaitoh static void	vlan_putref_linkmib(struct ifvlan_linkmib *, struct psref *);
20873fbed93Smsaitoh static void	vlan_linkmib_update(struct ifvlan *, struct ifvlan_linkmib *);
2093a942150Syamaguchi static struct ifvlan_linkmib*
2103a942150Syamaguchi 		vlan_lookup_tag_psref(struct ifnet *, uint16_t,
2113a942150Syamaguchi 		    struct psref *);
21296375e93Sthorpej 
2133043ffd9Sknakahara #if !defined(VLAN_TAG_HASH_SIZE)
2143043ffd9Sknakahara #define VLAN_TAG_HASH_SIZE 32
2153043ffd9Sknakahara #endif
2163043ffd9Sknakahara static struct {
2173043ffd9Sknakahara 	kmutex_t lock;
2183043ffd9Sknakahara 	struct pslist_head *lists;
2193043ffd9Sknakahara 	u_long mask;
2203043ffd9Sknakahara } ifv_hash __cacheline_aligned = {
2213043ffd9Sknakahara 	.lists = NULL,
2223043ffd9Sknakahara 	.mask = 0,
2233043ffd9Sknakahara };
2243043ffd9Sknakahara 
2253043ffd9Sknakahara pserialize_t vlan_psz __read_mostly;
2263043ffd9Sknakahara static struct psref_class *ifvm_psref_class __read_mostly;
2273718f78aSozaki-r 
22800746cb0Sthorpej struct if_clone vlan_cloner =
22900746cb0Sthorpej     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
23000746cb0Sthorpej 
2314dbbe025Syamaguchi static uint32_t nvlanifs;
2324dbbe025Syamaguchi 
233e0bc64f9Sozaki-r static inline int
vlan_safe_ifpromisc(struct ifnet * ifp,int pswitch)234e0bc64f9Sozaki-r vlan_safe_ifpromisc(struct ifnet *ifp, int pswitch)
235e0bc64f9Sozaki-r {
236e0bc64f9Sozaki-r 	int e;
237fd270400Smaxv 
238e0bc64f9Sozaki-r 	KERNEL_LOCK_UNLESS_NET_MPSAFE();
239e0bc64f9Sozaki-r 	e = ifpromisc(ifp, pswitch);
240e0bc64f9Sozaki-r 	KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
241fd270400Smaxv 
242e0bc64f9Sozaki-r 	return e;
243e0bc64f9Sozaki-r }
244e0bc64f9Sozaki-r 
245bd4c55ceShannken __unused static inline int
vlan_safe_ifpromisc_locked(struct ifnet * ifp,int pswitch)2469f1841adSozaki-r vlan_safe_ifpromisc_locked(struct ifnet *ifp, int pswitch)
2479f1841adSozaki-r {
2489f1841adSozaki-r 	int e;
249fd270400Smaxv 
2509f1841adSozaki-r 	KERNEL_LOCK_UNLESS_NET_MPSAFE();
2519f1841adSozaki-r 	e = ifpromisc_locked(ifp, pswitch);
2529f1841adSozaki-r 	KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
253fd270400Smaxv 
2549f1841adSozaki-r 	return e;
2559f1841adSozaki-r }
2569f1841adSozaki-r 
25700746cb0Sthorpej void
vlanattach(int n)258168cd830Schristos vlanattach(int n)
25900746cb0Sthorpej {
26000746cb0Sthorpej 
2614a3aea06Schristos 	/*
2624a3aea06Schristos 	 * Nothing to do here, initialization is handled by the
263fd270400Smaxv 	 * module initialization code in vlaninit() below.
2644a3aea06Schristos 	 */
2654a3aea06Schristos }
2664a3aea06Schristos 
2674a3aea06Schristos static void
vlaninit(void)2684a3aea06Schristos vlaninit(void)
2694a3aea06Schristos {
2704dbbe025Syamaguchi 	nvlanifs = 0;
2714a3aea06Schristos 
2723043ffd9Sknakahara 	mutex_init(&ifv_hash.lock, MUTEX_DEFAULT, IPL_NONE);
2733043ffd9Sknakahara 	vlan_psz = pserialize_create();
2743043ffd9Sknakahara 	ifvm_psref_class = psref_class_create("vlanlinkmib", IPL_SOFTNET);
27500746cb0Sthorpej 	if_clone_attach(&vlan_cloner);
2763043ffd9Sknakahara 
2773043ffd9Sknakahara 	vlan_hash_init();
278befa4c1fSpgoyette 	MODULE_HOOK_SET(if_vlan_vlan_input_hook, vlan_input);
27900746cb0Sthorpej }
28000746cb0Sthorpej 
2814a3aea06Schristos static int
vlandetach(void)2824a3aea06Schristos vlandetach(void)
2834a3aea06Schristos {
284fd270400Smaxv 	int error;
2854a3aea06Schristos 
2864dbbe025Syamaguchi 	if (nvlanifs > 0)
2873043ffd9Sknakahara 		return EBUSY;
2884a3aea06Schristos 
2893043ffd9Sknakahara 	error = vlan_hash_fini();
2903043ffd9Sknakahara 	if (error != 0)
2914a3aea06Schristos 		return error;
2923043ffd9Sknakahara 
2933043ffd9Sknakahara 	if_clone_detach(&vlan_cloner);
2943043ffd9Sknakahara 	psref_class_destroy(ifvm_psref_class);
2953043ffd9Sknakahara 	pserialize_destroy(vlan_psz);
2963043ffd9Sknakahara 	mutex_destroy(&ifv_hash.lock);
2973043ffd9Sknakahara 
298fb9792ffSpgoyette 	MODULE_HOOK_UNSET(if_vlan_vlan_input_hook);
2993043ffd9Sknakahara 	return 0;
3004a3aea06Schristos }
3014a3aea06Schristos 
30253c23f54Sthorpej static void
vlan_reset_linkname(struct ifnet * ifp)30353c23f54Sthorpej vlan_reset_linkname(struct ifnet *ifp)
30453c23f54Sthorpej {
30553c23f54Sthorpej 
30653c23f54Sthorpej 	/*
30753c23f54Sthorpej 	 * We start out with a "802.1Q VLAN" type and zero-length
30853c23f54Sthorpej 	 * addresses.  When we attach to a parent interface, we
30953c23f54Sthorpej 	 * inherit its type, address length, address, and data link
31053c23f54Sthorpej 	 * type.
31153c23f54Sthorpej 	 */
31253c23f54Sthorpej 
31353c23f54Sthorpej 	ifp->if_type = IFT_L2VLAN;
31453c23f54Sthorpej 	ifp->if_addrlen = 0;
31553c23f54Sthorpej 	ifp->if_dlt = DLT_NULL;
31653c23f54Sthorpej 	if_alloc_sadl(ifp);
31753c23f54Sthorpej }
31853c23f54Sthorpej 
31900746cb0Sthorpej static int
vlan_clone_create(struct if_clone * ifc,int unit)32000746cb0Sthorpej vlan_clone_create(struct if_clone *ifc, int unit)
32100746cb0Sthorpej {
32200746cb0Sthorpej 	struct ifvlan *ifv;
32300746cb0Sthorpej 	struct ifnet *ifp;
3243043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
32500746cb0Sthorpej 
326bc168f27Schristos 	ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK | M_ZERO);
3273043ffd9Sknakahara 	mib = kmem_zalloc(sizeof(struct ifvlan_linkmib), KM_SLEEP);
328b817ce40Senami 	ifp = &ifv->ifv_if;
3298e1cd4f3Senami 	LIST_INIT(&ifv->ifv_mc_listhead);
330efbead50Sthorpej 
3313043ffd9Sknakahara 	mib->ifvm_ifvlan = ifv;
3323043ffd9Sknakahara 	mib->ifvm_p = NULL;
3333043ffd9Sknakahara 	psref_target_init(&mib->ifvm_psref, ifvm_psref_class);
3343043ffd9Sknakahara 
3353043ffd9Sknakahara 	mutex_init(&ifv->ifv_lock, MUTEX_DEFAULT, IPL_NONE);
336c4d3aeb8Sjmcneill 	ifv->ifv_psz = pserialize_create();
3373043ffd9Sknakahara 	ifv->ifv_mib = mib;
3383043ffd9Sknakahara 
3394dbbe025Syamaguchi 	atomic_inc_uint(&nvlanifs);
34000746cb0Sthorpej 
341bc168f27Schristos 	if_initname(ifp, ifc->ifc_name, unit);
34200746cb0Sthorpej 	ifp->if_softc = ifv;
34300746cb0Sthorpej 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
344df6b1390Sozaki-r #ifdef NET_MPSAFE
3452f3349bcSroy 	ifp->if_extflags = IFEF_MPSAFE;
346df6b1390Sozaki-r #endif
34700746cb0Sthorpej 	ifp->if_start = vlan_start;
3483043ffd9Sknakahara 	ifp->if_transmit = vlan_transmit;
34900746cb0Sthorpej 	ifp->if_ioctl = vlan_ioctl;
3505cbdcc50Sthorpej 	IFQ_SET_READY(&ifp->if_snd);
351bbdda93bSriastradh 	if_initialize(ifp);
3522f3349bcSroy 	/*
3532f3349bcSroy 	 * Set the link state to down.
3542f3349bcSroy 	 * When the parent interface attaches we will use that link state.
3552f3349bcSroy 	 * When the parent interface link state changes, so will ours.
3566d782fd4Syamaguchi 	 * When the parent interface detaches, set the link state to down.
3572f3349bcSroy 	 */
3582f3349bcSroy 	ifp->if_link_state = LINK_STATE_DOWN;
3592f3349bcSroy 
36053c23f54Sthorpej 	vlan_reset_linkname(ifp);
361b8256fd8Sozaki-r 	if_register(ifp);
3624cec6d9cSmsaitoh 	return 0;
36300746cb0Sthorpej }
36400746cb0Sthorpej 
365b9c49ebfSpeter static int
vlan_clone_destroy(struct ifnet * ifp)36600746cb0Sthorpej vlan_clone_destroy(struct ifnet *ifp)
36700746cb0Sthorpej {
368efbead50Sthorpej 	struct ifvlan *ifv = ifp->if_softc;
36900746cb0Sthorpej 
3704dbbe025Syamaguchi 	atomic_dec_uint(&nvlanifs);
3713043ffd9Sknakahara 
372f1b40c06Sozaki-r 	IFNET_LOCK(ifp);
37300746cb0Sthorpej 	vlan_unconfig(ifp);
374f1b40c06Sozaki-r 	IFNET_UNLOCK(ifp);
37584bd0fddSozaki-r 	if_detach(ifp);
37600746cb0Sthorpej 
3773043ffd9Sknakahara 	psref_target_destroy(&ifv->ifv_mib->ifvm_psref, ifvm_psref_class);
3783043ffd9Sknakahara 	kmem_free(ifv->ifv_mib, sizeof(struct ifvlan_linkmib));
379406cef35Sknakahara 	pserialize_destroy(ifv->ifv_psz);
3803043ffd9Sknakahara 	mutex_destroy(&ifv->ifv_lock);
38100746cb0Sthorpej 	free(ifv, M_DEVBUF);
382b9c49ebfSpeter 
383fd270400Smaxv 	return 0;
38400746cb0Sthorpej }
38500746cb0Sthorpej 
386efbead50Sthorpej /*
3873043ffd9Sknakahara  * Configure a VLAN interface.
388efbead50Sthorpej  */
38900746cb0Sthorpej static int
vlan_config(struct ifvlan * ifv,struct ifnet * p,uint16_t tag)3903043ffd9Sknakahara vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag)
39100746cb0Sthorpej {
39296375e93Sthorpej 	struct ifnet *ifp = &ifv->ifv_if;
3933043ffd9Sknakahara 	struct ifvlan_linkmib *nmib = NULL;
3943043ffd9Sknakahara 	struct ifvlan_linkmib *omib = NULL;
395fd270400Smaxv 	struct ifvlan_linkmib *checkmib;
3963043ffd9Sknakahara 	struct psref_target *nmib_psref = NULL;
397fd270400Smaxv 	const uint16_t vid = EVL_VLANOFTAG(tag);
3983043ffd9Sknakahara 	int error = 0;
3993043ffd9Sknakahara 	int idx;
4003043ffd9Sknakahara 	bool omib_cleanup = false;
4015971648eSmsaitoh 	struct psref psref;
40200746cb0Sthorpej 
4036b1f0216Smsaitoh 	/* VLAN ID 0 and 4095 are reserved in the spec */
40460f7cd9eSmsaitoh 	if ((vid == 0) || (vid == 0xfff))
40560f7cd9eSmsaitoh 		return EINVAL;
40660f7cd9eSmsaitoh 
4073043ffd9Sknakahara 	nmib = kmem_alloc(sizeof(*nmib), KM_SLEEP);
4083043ffd9Sknakahara 	mutex_enter(&ifv->ifv_lock);
4093043ffd9Sknakahara 	omib = ifv->ifv_mib;
4103043ffd9Sknakahara 
4113043ffd9Sknakahara 	if (omib->ifvm_p != NULL) {
4123043ffd9Sknakahara 		error = EBUSY;
4133043ffd9Sknakahara 		goto done;
4143043ffd9Sknakahara 	}
4153043ffd9Sknakahara 
4165971648eSmsaitoh 	/* Duplicate check */
417d97a4d84Smsaitoh 	checkmib = vlan_lookup_tag_psref(p, vid, &psref);
4185971648eSmsaitoh 	if (checkmib != NULL) {
4195971648eSmsaitoh 		vlan_putref_linkmib(checkmib, &psref);
4205971648eSmsaitoh 		error = EEXIST;
4215971648eSmsaitoh 		goto done;
4225971648eSmsaitoh 	}
4235971648eSmsaitoh 
4243043ffd9Sknakahara 	*nmib = *omib;
4253043ffd9Sknakahara 	nmib_psref = &nmib->ifvm_psref;
4263043ffd9Sknakahara 
4273043ffd9Sknakahara 	psref_target_init(nmib_psref, ifvm_psref_class);
42896375e93Sthorpej 
42996375e93Sthorpej 	switch (p->if_type) {
43096375e93Sthorpej 	case IFT_ETHER:
43196375e93Sthorpej 	    {
43296375e93Sthorpej 		struct ethercom *ec = (void *)p;
433100a7af6Smsaitoh 
4343043ffd9Sknakahara 		nmib->ifvm_msw = &vlan_ether_multisw;
4353043ffd9Sknakahara 		nmib->ifvm_mintu = ETHERMIN;
43696375e93Sthorpej 
4375e343363Syamaguchi 		error = ether_add_vlantag(p, tag, NULL);
4384c46ae33Syamaguchi 		if (error != 0)
4393043ffd9Sknakahara 			goto done;
4404c46ae33Syamaguchi 
4415e343363Syamaguchi 		if (ec->ec_capenable & ETHERCAP_VLAN_MTU) {
4423043ffd9Sknakahara 			nmib->ifvm_mtufudge = 0;
44319f7cce9Schristos 		} else {
44496375e93Sthorpej 			/*
44596375e93Sthorpej 			 * Fudge the MTU by the encapsulation size. This
44696375e93Sthorpej 			 * makes us incompatible with strictly compliant
44796375e93Sthorpej 			 * 802.1Q implementations, but allows us to use
44819f7cce9Schristos 			 * the feature with other NetBSD
44919f7cce9Schristos 			 * implementations, which might still be useful.
45096375e93Sthorpej 			 */
45131ca9bf0Syamaguchi 			nmib->ifvm_mtufudge = ETHER_VLAN_ENCAP_LEN;
45296375e93Sthorpej 		}
453ebd7ae72Smsaitoh 
45496375e93Sthorpej 		/*
45542a2e688Sthorpej 		 * If the parent interface can do hardware-assisted
45642a2e688Sthorpej 		 * VLAN encapsulation, then propagate its hardware-
45749d3640aSdarran 		 * assisted checksumming flags and tcp segmentation
45849d3640aSdarran 		 * offload.
45942a2e688Sthorpej 		 */
46067bc0954Ssborrill 		if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
46142a2e688Sthorpej 			ifp->if_capabilities = p->if_capabilities &
4626cc8d64cSdarran 			    (IFCAP_TSOv4 | IFCAP_TSOv6 |
46349d3640aSdarran 				IFCAP_CSUM_IPv4_Tx  | IFCAP_CSUM_IPv4_Rx |
464330cc0a1Syamt 				IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
465330cc0a1Syamt 				IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx |
466330cc0a1Syamt 				IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_TCPv6_Rx |
467330cc0a1Syamt 				IFCAP_CSUM_UDPv6_Tx | IFCAP_CSUM_UDPv6_Rx);
46867bc0954Ssborrill 		}
469fd270400Smaxv 
47042a2e688Sthorpej 		/*
47196375e93Sthorpej 		 * We inherit the parent's Ethernet address.
47296375e93Sthorpej 		 */
4735204966aSdyoung 		ether_ifattach(ifp, CLLADDR(p->if_sadl));
47496375e93Sthorpej 		ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */
47596375e93Sthorpej 		break;
47696375e93Sthorpej 	    }
47796375e93Sthorpej 
47896375e93Sthorpej 	default:
4793043ffd9Sknakahara 		error = EPROTONOSUPPORT;
4803043ffd9Sknakahara 		goto done;
48196375e93Sthorpej 	}
48296375e93Sthorpej 
4833043ffd9Sknakahara 	nmib->ifvm_p = p;
484d97a4d84Smsaitoh 	nmib->ifvm_tag = vid;
4853043ffd9Sknakahara 	ifv->ifv_if.if_mtu = p->if_mtu - nmib->ifvm_mtufudge;
4863659b305Sbouyer 	ifv->ifv_if.if_flags = p->if_flags &
4870d0fd771Sbouyer 	    (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
48800746cb0Sthorpej 
48900746cb0Sthorpej 	/*
49096375e93Sthorpej 	 * Inherit the if_type from the parent.  This allows us
49196375e93Sthorpej 	 * to participate in bridges of that type.
49200746cb0Sthorpej 	 */
49396375e93Sthorpej 	ifv->ifv_if.if_type = p->if_type;
49496375e93Sthorpej 
495e2b0e46bSknakahara 	PSLIST_ENTRY_INIT(ifv, ifv_hash);
49666163bcfSmsaitoh 	idx = vlan_tag_hash(vid, ifv_hash.mask);
4973043ffd9Sknakahara 
4983043ffd9Sknakahara 	mutex_enter(&ifv_hash.lock);
4993043ffd9Sknakahara 	PSLIST_WRITER_INSERT_HEAD(&ifv_hash.lists[idx], ifv, ifv_hash);
5003043ffd9Sknakahara 	mutex_exit(&ifv_hash.lock);
5013043ffd9Sknakahara 
5023043ffd9Sknakahara 	vlan_linkmib_update(ifv, nmib);
5033043ffd9Sknakahara 	nmib = NULL;
5043043ffd9Sknakahara 	nmib_psref = NULL;
5053043ffd9Sknakahara 	omib_cleanup = true;
5063043ffd9Sknakahara 
507842c78c0Syamaguchi 	ifv->ifv_ifdetach_hook = ether_ifdetachhook_establish(p,
508842c78c0Syamaguchi 	    vlan_ifdetach, ifp);
509842c78c0Syamaguchi 
5102f3349bcSroy 	/*
5112f3349bcSroy 	 * We inherit the parents link state.
5122f3349bcSroy 	 */
513cbb731a8Syamaguchi 	ifv->ifv_linkstate_hook = if_linkstate_change_establish(p,
514cbb731a8Syamaguchi 	    vlan_link_state_changed, ifv);
5152f3349bcSroy 	if_link_state_change(&ifv->ifv_if, p->if_link_state);
5162f3349bcSroy 
5173043ffd9Sknakahara done:
5183043ffd9Sknakahara 	mutex_exit(&ifv->ifv_lock);
5193043ffd9Sknakahara 
5203043ffd9Sknakahara 	if (nmib_psref)
5213043ffd9Sknakahara 		psref_target_destroy(nmib_psref, ifvm_psref_class);
5223043ffd9Sknakahara 	if (nmib)
5233043ffd9Sknakahara 		kmem_free(nmib, sizeof(*nmib));
5243043ffd9Sknakahara 	if (omib_cleanup)
5253043ffd9Sknakahara 		kmem_free(omib, sizeof(*omib));
5263043ffd9Sknakahara 
5273043ffd9Sknakahara 	return error;
52800746cb0Sthorpej }
52900746cb0Sthorpej 
530efbead50Sthorpej /*
5313043ffd9Sknakahara  * Unconfigure a VLAN interface.
532efbead50Sthorpej  */
533efbead50Sthorpej static void
vlan_unconfig(struct ifnet * ifp)53400746cb0Sthorpej vlan_unconfig(struct ifnet *ifp)
53500746cb0Sthorpej {
53696375e93Sthorpej 	struct ifvlan *ifv = ifp->if_softc;
5373043ffd9Sknakahara 	struct ifvlan_linkmib *nmib = NULL;
5383043ffd9Sknakahara 	int error;
53900746cb0Sthorpej 
540f1b40c06Sozaki-r 	KASSERT(IFNET_LOCKED(ifp));
5419f1841adSozaki-r 
5423043ffd9Sknakahara 	nmib = kmem_alloc(sizeof(*nmib), KM_SLEEP);
5433043ffd9Sknakahara 
5443043ffd9Sknakahara 	mutex_enter(&ifv->ifv_lock);
5453043ffd9Sknakahara 	error = vlan_unconfig_locked(ifv, nmib);
5463043ffd9Sknakahara 	mutex_exit(&ifv->ifv_lock);
5473043ffd9Sknakahara 
5483043ffd9Sknakahara 	if (error)
5493043ffd9Sknakahara 		kmem_free(nmib, sizeof(*nmib));
5503043ffd9Sknakahara }
5513043ffd9Sknakahara static int
vlan_unconfig_locked(struct ifvlan * ifv,struct ifvlan_linkmib * nmib)5523043ffd9Sknakahara vlan_unconfig_locked(struct ifvlan *ifv, struct ifvlan_linkmib *nmib)
5533043ffd9Sknakahara {
5543043ffd9Sknakahara 	struct ifnet *p;
5553043ffd9Sknakahara 	struct ifnet *ifp = &ifv->ifv_if;
5563043ffd9Sknakahara 	struct psref_target *nmib_psref = NULL;
5573043ffd9Sknakahara 	struct ifvlan_linkmib *omib;
5583043ffd9Sknakahara 	int error = 0;
5593043ffd9Sknakahara 
560f1b40c06Sozaki-r 	KASSERT(IFNET_LOCKED(ifp));
5613043ffd9Sknakahara 	KASSERT(mutex_owned(&ifv->ifv_lock));
5623043ffd9Sknakahara 
563842c78c0Syamaguchi 	if (ifv->ifv_stopping) {
564842c78c0Syamaguchi 		error = -1;
565842c78c0Syamaguchi 		goto done;
566842c78c0Syamaguchi 	}
567842c78c0Syamaguchi 
568b2b82595Sozaki-r 	ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
569b2b82595Sozaki-r 
5703043ffd9Sknakahara 	omib = ifv->ifv_mib;
5713043ffd9Sknakahara 	p = omib->ifvm_p;
5723718f78aSozaki-r 
573b07fa576Sozaki-r 	if (p == NULL) {
5743043ffd9Sknakahara 		error = -1;
5753043ffd9Sknakahara 		goto done;
5763718f78aSozaki-r 	}
57700746cb0Sthorpej 
5783043ffd9Sknakahara 	*nmib = *omib;
5793043ffd9Sknakahara 	nmib_psref = &nmib->ifvm_psref;
5803043ffd9Sknakahara 	psref_target_init(nmib_psref, ifvm_psref_class);
5813043ffd9Sknakahara 
58200746cb0Sthorpej 	/*
58300746cb0Sthorpej 	 * Since the interface is being unconfigured, we need to empty the
58400746cb0Sthorpej 	 * list of multicast groups that we may have joined while we were
58500746cb0Sthorpej 	 * alive and remove them from the parent's list also.
58600746cb0Sthorpej 	 */
5873043ffd9Sknakahara 	(*nmib->ifvm_msw->vmsw_purgemulti)(ifv);
58800746cb0Sthorpej 
58900746cb0Sthorpej 	/* Disconnect from parent. */
590b07fa576Sozaki-r 	switch (p->if_type) {
59196375e93Sthorpej 	case IFT_ETHER:
59296375e93Sthorpej 	    {
5934c46ae33Syamaguchi 		(void)ether_del_vlantag(p, nmib->ifvm_tag);
59496375e93Sthorpej 
5952dbb13b4Sozaki-r 		/* XXX ether_ifdetach must not be called with IFNET_LOCK */
596842c78c0Syamaguchi 		ifv->ifv_stopping = true;
5972dbb13b4Sozaki-r 		mutex_exit(&ifv->ifv_lock);
5982dbb13b4Sozaki-r 		IFNET_UNLOCK(ifp);
59996375e93Sthorpej 		ether_ifdetach(ifp);
6002dbb13b4Sozaki-r 		IFNET_LOCK(ifp);
6012dbb13b4Sozaki-r 		mutex_enter(&ifv->ifv_lock);
602842c78c0Syamaguchi 		ifv->ifv_stopping = false;
6032dbb13b4Sozaki-r 
604655bf488Sknakahara 		/* if_free_sadl must be called with IFNET_LOCK */
605655bf488Sknakahara 		if_free_sadl(ifp, 1);
606655bf488Sknakahara 
607918ad35fSozaki-r 		/* Restore vlan_ioctl overwritten by ether_ifdetach */
608918ad35fSozaki-r 		ifp->if_ioctl = vlan_ioctl;
60953c23f54Sthorpej 		vlan_reset_linkname(ifp);
61096375e93Sthorpej 		break;
61196375e93Sthorpej 	    }
61296375e93Sthorpej 
61396375e93Sthorpej 	default:
614fd270400Smaxv 		panic("%s: impossible", __func__);
61596375e93Sthorpej 	}
61696375e93Sthorpej 
6173043ffd9Sknakahara 	nmib->ifvm_p = NULL;
61896375e93Sthorpej 	ifv->ifv_if.if_mtu = 0;
619c1e8f104Sthorpej 	ifv->ifv_flags = 0;
62000746cb0Sthorpej 
6213043ffd9Sknakahara 	mutex_enter(&ifv_hash.lock);
6223043ffd9Sknakahara 	PSLIST_WRITER_REMOVE(ifv, ifv_hash);
6233043ffd9Sknakahara 	pserialize_perform(vlan_psz);
6243043ffd9Sknakahara 	mutex_exit(&ifv_hash.lock);
625e2b0e46bSknakahara 	PSLIST_ENTRY_DESTROY(ifv, ifv_hash);
626cbb731a8Syamaguchi 	if_linkstate_change_disestablish(p,
627cbb731a8Syamaguchi 	    ifv->ifv_linkstate_hook, NULL);
6283043ffd9Sknakahara 
6293043ffd9Sknakahara 	vlan_linkmib_update(ifv, nmib);
6306d782fd4Syamaguchi 	if_link_state_change(ifp, LINK_STATE_DOWN);
6313043ffd9Sknakahara 
632842c78c0Syamaguchi 	/*XXX ether_ifdetachhook_disestablish must not called with IFNET_LOCK */
633842c78c0Syamaguchi 	IFNET_UNLOCK(ifp);
634842c78c0Syamaguchi 	ether_ifdetachhook_disestablish(p, ifv->ifv_ifdetach_hook,
635842c78c0Syamaguchi 	    &ifv->ifv_lock);
6363043ffd9Sknakahara 	mutex_exit(&ifv->ifv_lock);
637842c78c0Syamaguchi 	IFNET_LOCK(ifp);
6383043ffd9Sknakahara 
6393043ffd9Sknakahara 	nmib_psref = NULL;
6403043ffd9Sknakahara 	kmem_free(omib, sizeof(*omib));
6413043ffd9Sknakahara 
64201dd0176Sozaki-r #ifdef INET6
643e0bc64f9Sozaki-r 	KERNEL_LOCK_UNLESS_NET_MPSAFE();
64401dd0176Sozaki-r 	/* To delete v6 link local addresses */
645b8372a5cSozaki-r 	if (in6_present)
64601dd0176Sozaki-r 		in6_ifdetach(ifp);
647e0bc64f9Sozaki-r 	KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
64801dd0176Sozaki-r #endif
6493043ffd9Sknakahara 
6500a561f72Sozaki-r 	if_down_locked(ifp);
65142a2e688Sthorpej 	ifp->if_capabilities = 0;
6523043ffd9Sknakahara 	mutex_enter(&ifv->ifv_lock);
6533043ffd9Sknakahara done:
6543043ffd9Sknakahara 	if (nmib_psref)
6553043ffd9Sknakahara 		psref_target_destroy(nmib_psref, ifvm_psref_class);
6563043ffd9Sknakahara 
6573043ffd9Sknakahara 	return error;
6583043ffd9Sknakahara }
6593043ffd9Sknakahara 
6603043ffd9Sknakahara static void
vlan_hash_init(void)6613043ffd9Sknakahara vlan_hash_init(void)
6623043ffd9Sknakahara {
6633043ffd9Sknakahara 
6643043ffd9Sknakahara 	ifv_hash.lists = hashinit(VLAN_TAG_HASH_SIZE, HASH_PSLIST, true,
6653043ffd9Sknakahara 	    &ifv_hash.mask);
6663043ffd9Sknakahara }
6673043ffd9Sknakahara 
6683043ffd9Sknakahara static int
vlan_hash_fini(void)6693043ffd9Sknakahara vlan_hash_fini(void)
6703043ffd9Sknakahara {
6713043ffd9Sknakahara 	int i;
6723043ffd9Sknakahara 
6733043ffd9Sknakahara 	mutex_enter(&ifv_hash.lock);
6743043ffd9Sknakahara 
6753043ffd9Sknakahara 	for (i = 0; i < ifv_hash.mask + 1; i++) {
6763043ffd9Sknakahara 		if (PSLIST_WRITER_FIRST(&ifv_hash.lists[i], struct ifvlan,
6773043ffd9Sknakahara 		    ifv_hash) != NULL) {
6783043ffd9Sknakahara 			mutex_exit(&ifv_hash.lock);
6793043ffd9Sknakahara 			return EBUSY;
6803043ffd9Sknakahara 		}
6813043ffd9Sknakahara 	}
6823043ffd9Sknakahara 
6833043ffd9Sknakahara 	for (i = 0; i < ifv_hash.mask + 1; i++)
6843043ffd9Sknakahara 		PSLIST_DESTROY(&ifv_hash.lists[i]);
6853043ffd9Sknakahara 
6863043ffd9Sknakahara 	mutex_exit(&ifv_hash.lock);
6873043ffd9Sknakahara 
6883043ffd9Sknakahara 	hashdone(ifv_hash.lists, HASH_PSLIST, ifv_hash.mask);
6893043ffd9Sknakahara 
6903043ffd9Sknakahara 	ifv_hash.lists = NULL;
6913043ffd9Sknakahara 	ifv_hash.mask = 0;
6923043ffd9Sknakahara 
6933043ffd9Sknakahara 	return 0;
6943043ffd9Sknakahara }
6953043ffd9Sknakahara 
6963043ffd9Sknakahara static int
vlan_tag_hash(uint16_t tag,u_long mask)69766163bcfSmsaitoh vlan_tag_hash(uint16_t tag, u_long mask)
6983043ffd9Sknakahara {
6993043ffd9Sknakahara 	uint32_t hash;
7003043ffd9Sknakahara 
7013043ffd9Sknakahara 	hash = (tag >> 8) ^ tag;
7023043ffd9Sknakahara 	hash = (hash >> 2) ^ hash;
7033043ffd9Sknakahara 
7043043ffd9Sknakahara 	return hash & mask;
7053043ffd9Sknakahara }
7063043ffd9Sknakahara 
7073043ffd9Sknakahara static struct ifvlan_linkmib *
vlan_getref_linkmib(struct ifvlan * sc,struct psref * psref)7083043ffd9Sknakahara vlan_getref_linkmib(struct ifvlan *sc, struct psref *psref)
7093043ffd9Sknakahara {
7103043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
7113043ffd9Sknakahara 	int s;
7123043ffd9Sknakahara 
7133043ffd9Sknakahara 	s = pserialize_read_enter();
714d235ee80Sriastradh 	mib = atomic_load_consume(&sc->ifv_mib);
7153043ffd9Sknakahara 	if (mib == NULL) {
7163043ffd9Sknakahara 		pserialize_read_exit(s);
7173043ffd9Sknakahara 		return NULL;
7183043ffd9Sknakahara 	}
7193043ffd9Sknakahara 	psref_acquire(psref, &mib->ifvm_psref, ifvm_psref_class);
7203043ffd9Sknakahara 	pserialize_read_exit(s);
7213043ffd9Sknakahara 
7223043ffd9Sknakahara 	return mib;
7233043ffd9Sknakahara }
7243043ffd9Sknakahara 
7253043ffd9Sknakahara static void
vlan_putref_linkmib(struct ifvlan_linkmib * mib,struct psref * psref)7263043ffd9Sknakahara vlan_putref_linkmib(struct ifvlan_linkmib *mib, struct psref *psref)
7273043ffd9Sknakahara {
7283043ffd9Sknakahara 	if (mib == NULL)
7293043ffd9Sknakahara 		return;
7303043ffd9Sknakahara 	psref_release(psref, &mib->ifvm_psref, ifvm_psref_class);
7313043ffd9Sknakahara }
7323043ffd9Sknakahara 
7333043ffd9Sknakahara static struct ifvlan_linkmib *
vlan_lookup_tag_psref(struct ifnet * ifp,uint16_t tag,struct psref * psref)7343043ffd9Sknakahara vlan_lookup_tag_psref(struct ifnet *ifp, uint16_t tag, struct psref *psref)
7353043ffd9Sknakahara {
7363043ffd9Sknakahara 	int idx;
7373043ffd9Sknakahara 	int s;
7383043ffd9Sknakahara 	struct ifvlan *sc;
7393043ffd9Sknakahara 
74066163bcfSmsaitoh 	idx = vlan_tag_hash(tag, ifv_hash.mask);
7413043ffd9Sknakahara 
7423043ffd9Sknakahara 	s = pserialize_read_enter();
7433043ffd9Sknakahara 	PSLIST_READER_FOREACH(sc, &ifv_hash.lists[idx], struct ifvlan,
7443043ffd9Sknakahara 	    ifv_hash) {
745d235ee80Sriastradh 		struct ifvlan_linkmib *mib = atomic_load_consume(&sc->ifv_mib);
7463043ffd9Sknakahara 		if (mib == NULL)
7473043ffd9Sknakahara 			continue;
7483043ffd9Sknakahara 		if (mib->ifvm_tag != tag)
7493043ffd9Sknakahara 			continue;
7503043ffd9Sknakahara 		if (mib->ifvm_p != ifp)
7513043ffd9Sknakahara 			continue;
7523043ffd9Sknakahara 
7533043ffd9Sknakahara 		psref_acquire(psref, &mib->ifvm_psref, ifvm_psref_class);
7543043ffd9Sknakahara 		pserialize_read_exit(s);
7553043ffd9Sknakahara 		return mib;
7563043ffd9Sknakahara 	}
7573043ffd9Sknakahara 	pserialize_read_exit(s);
7583043ffd9Sknakahara 	return NULL;
7593043ffd9Sknakahara }
7603043ffd9Sknakahara 
7613043ffd9Sknakahara static void
vlan_linkmib_update(struct ifvlan * ifv,struct ifvlan_linkmib * nmib)7623043ffd9Sknakahara vlan_linkmib_update(struct ifvlan *ifv, struct ifvlan_linkmib *nmib)
7633043ffd9Sknakahara {
7643043ffd9Sknakahara 	struct ifvlan_linkmib *omib = ifv->ifv_mib;
7653043ffd9Sknakahara 
7663043ffd9Sknakahara 	KASSERT(mutex_owned(&ifv->ifv_lock));
7673043ffd9Sknakahara 
768d235ee80Sriastradh 	atomic_store_release(&ifv->ifv_mib, nmib);
7693043ffd9Sknakahara 
770c4d3aeb8Sjmcneill 	pserialize_perform(ifv->ifv_psz);
7713043ffd9Sknakahara 	psref_target_destroy(&omib->ifvm_psref, ifvm_psref_class);
772efbead50Sthorpej }
773efbead50Sthorpej 
774efbead50Sthorpej /*
775efbead50Sthorpej  * Called when a parent interface is detaching; destroy any VLAN
776efbead50Sthorpej  * configuration for the parent interface.
777efbead50Sthorpej  */
778842c78c0Syamaguchi static void
vlan_ifdetach(void * xifp)779842c78c0Syamaguchi vlan_ifdetach(void *xifp)
780efbead50Sthorpej {
781842c78c0Syamaguchi 	struct ifnet *ifp;
782efbead50Sthorpej 
783842c78c0Syamaguchi 	ifp = (struct ifnet *)xifp;
7849f1841adSozaki-r 
785fd270400Smaxv 	/* IFNET_LOCK must be held before ifv_lock. */
786f1b40c06Sozaki-r 	IFNET_LOCK(ifp);
787842c78c0Syamaguchi 	vlan_unconfig(ifp);
788f1b40c06Sozaki-r 	IFNET_UNLOCK(ifp);
7893043ffd9Sknakahara }
7903043ffd9Sknakahara 
79100746cb0Sthorpej static int
vlan_set_promisc(struct ifnet * ifp)792c1e8f104Sthorpej vlan_set_promisc(struct ifnet *ifp)
793c1e8f104Sthorpej {
794c1e8f104Sthorpej 	struct ifvlan *ifv = ifp->if_softc;
7953043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
7963043ffd9Sknakahara 	struct psref psref;
797d93a3cbcSenami 	int error = 0;
7983043ffd9Sknakahara 	int bound;
7993043ffd9Sknakahara 
8003043ffd9Sknakahara 	bound = curlwp_bind();
8013043ffd9Sknakahara 	mib = vlan_getref_linkmib(ifv, &psref);
8023043ffd9Sknakahara 	if (mib == NULL) {
8033043ffd9Sknakahara 		curlwp_bindx(bound);
8043043ffd9Sknakahara 		return EBUSY;
8053043ffd9Sknakahara 	}
806c1e8f104Sthorpej 
807c1e8f104Sthorpej 	if ((ifp->if_flags & IFF_PROMISC) != 0) {
808c1e8f104Sthorpej 		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
809e0bc64f9Sozaki-r 			error = vlan_safe_ifpromisc(mib->ifvm_p, 1);
810c1e8f104Sthorpej 			if (error == 0)
811c1e8f104Sthorpej 				ifv->ifv_flags |= IFVF_PROMISC;
812c1e8f104Sthorpej 		}
813c1e8f104Sthorpej 	} else {
814c1e8f104Sthorpej 		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
815e0bc64f9Sozaki-r 			error = vlan_safe_ifpromisc(mib->ifvm_p, 0);
816c1e8f104Sthorpej 			if (error == 0)
817c1e8f104Sthorpej 				ifv->ifv_flags &= ~IFVF_PROMISC;
818c1e8f104Sthorpej 		}
819c1e8f104Sthorpej 	}
8203043ffd9Sknakahara 	vlan_putref_linkmib(mib, &psref);
8213043ffd9Sknakahara 	curlwp_bindx(bound);
822c1e8f104Sthorpej 
823fd270400Smaxv 	return error;
824c1e8f104Sthorpej }
825c1e8f104Sthorpej 
826c1e8f104Sthorpej static int
vlan_ioctl(struct ifnet * ifp,u_long cmd,void * data)82753524e44Schristos vlan_ioctl(struct ifnet *ifp, u_long cmd, void *data)
82800746cb0Sthorpej {
829fd270400Smaxv 	struct lwp *l = curlwp;
830efbead50Sthorpej 	struct ifvlan *ifv = ifp->if_softc;
831efbead50Sthorpej 	struct ifaddr *ifa = (struct ifaddr *) data;
832efbead50Sthorpej 	struct ifreq *ifr = (struct ifreq *) data;
83300746cb0Sthorpej 	struct ifnet *pr;
83488948cf5Sbouyer 	struct ifcapreq *ifcr;
83500746cb0Sthorpej 	struct vlanreq vlr;
8363043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
8373043ffd9Sknakahara 	struct psref psref;
8383043ffd9Sknakahara 	int error = 0;
8393043ffd9Sknakahara 	int bound;
84000746cb0Sthorpej 
84100746cb0Sthorpej 	switch (cmd) {
84200746cb0Sthorpej 	case SIOCSIFMTU:
8433043ffd9Sknakahara 		bound = curlwp_bind();
8443043ffd9Sknakahara 		mib = vlan_getref_linkmib(ifv, &psref);
8453043ffd9Sknakahara 		if (mib == NULL) {
8463043ffd9Sknakahara 			curlwp_bindx(bound);
8473043ffd9Sknakahara 			error = EBUSY;
8483043ffd9Sknakahara 			break;
8493043ffd9Sknakahara 		}
8503043ffd9Sknakahara 
8513043ffd9Sknakahara 		if (mib->ifvm_p == NULL) {
8523043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
8533043ffd9Sknakahara 			curlwp_bindx(bound);
85400746cb0Sthorpej 			error = EINVAL;
8553043ffd9Sknakahara 		} else if (
8563043ffd9Sknakahara 		    ifr->ifr_mtu > (mib->ifvm_p->if_mtu - mib->ifvm_mtufudge) ||
8573043ffd9Sknakahara 		    ifr->ifr_mtu < (mib->ifvm_mintu - mib->ifvm_mtufudge)) {
8583043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
8593043ffd9Sknakahara 			curlwp_bindx(bound);
86000746cb0Sthorpej 			error = EINVAL;
8613043ffd9Sknakahara 		} else {
8623043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
8633043ffd9Sknakahara 			curlwp_bindx(bound);
8643043ffd9Sknakahara 
8653043ffd9Sknakahara 			error = ifioctl_common(ifp, cmd, data);
8663043ffd9Sknakahara 			if (error == ENETRESET)
8672ccede0aSdyoung 				error = 0;
8683043ffd9Sknakahara 		}
8693043ffd9Sknakahara 
87000746cb0Sthorpej 		break;
87100746cb0Sthorpej 
87200746cb0Sthorpej 	case SIOCSETVLAN:
8730a57b596Selad 		if ((error = kauth_authorize_network(l->l_cred,
8740a57b596Selad 		    KAUTH_NETWORK_INTERFACE,
8750a57b596Selad 		    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd,
8760a57b596Selad 		    NULL)) != 0)
87700746cb0Sthorpej 			break;
87800746cb0Sthorpej 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
87900746cb0Sthorpej 			break;
8803043ffd9Sknakahara 
88100746cb0Sthorpej 		if (vlr.vlr_parent[0] == '\0') {
8823043ffd9Sknakahara 			bound = curlwp_bind();
8833043ffd9Sknakahara 			mib = vlan_getref_linkmib(ifv, &psref);
8843043ffd9Sknakahara 			if (mib == NULL) {
8853043ffd9Sknakahara 				curlwp_bindx(bound);
8863043ffd9Sknakahara 				error = EBUSY;
8873043ffd9Sknakahara 				break;
8883043ffd9Sknakahara 			}
8893043ffd9Sknakahara 
8903043ffd9Sknakahara 			if (mib->ifvm_p != NULL &&
8913409f0d2Sozaki-r 			    (ifp->if_flags & IFF_PROMISC) != 0)
892e0bc64f9Sozaki-r 				error = vlan_safe_ifpromisc(mib->ifvm_p, 0);
8933043ffd9Sknakahara 
8943043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
8953043ffd9Sknakahara 			curlwp_bindx(bound);
8963043ffd9Sknakahara 
89700746cb0Sthorpej 			vlan_unconfig(ifp);
89800746cb0Sthorpej 			break;
89900746cb0Sthorpej 		}
90000746cb0Sthorpej 		if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
90100746cb0Sthorpej 			error = EINVAL;		 /* check for valid tag */
90200746cb0Sthorpej 			break;
90300746cb0Sthorpej 		}
90466163bcfSmsaitoh 		if ((pr = ifunit(vlr.vlr_parent)) == NULL) {
90500746cb0Sthorpej 			error = ENOENT;
90600746cb0Sthorpej 			break;
90700746cb0Sthorpej 		}
908100a7af6Smsaitoh 
9093043ffd9Sknakahara 		error = vlan_config(ifv, pr, vlr.vlr_tag);
91072a2bcdbSmsaitoh 		if (error != 0)
91100746cb0Sthorpej 			break;
912c1e8f104Sthorpej 
913c1e8f104Sthorpej 		/* Update promiscuous mode, if necessary. */
914c1e8f104Sthorpej 		vlan_set_promisc(ifp);
915b2b82595Sozaki-r 
916b2b82595Sozaki-r 		ifp->if_flags |= IFF_RUNNING;
91700746cb0Sthorpej 		break;
91800746cb0Sthorpej 
91900746cb0Sthorpej 	case SIOCGETVLAN:
92000746cb0Sthorpej 		memset(&vlr, 0, sizeof(vlr));
9213043ffd9Sknakahara 		bound = curlwp_bind();
9223043ffd9Sknakahara 		mib = vlan_getref_linkmib(ifv, &psref);
9233043ffd9Sknakahara 		if (mib == NULL) {
9243043ffd9Sknakahara 			curlwp_bindx(bound);
9253043ffd9Sknakahara 			error = EBUSY;
9263043ffd9Sknakahara 			break;
92700746cb0Sthorpej 		}
9283043ffd9Sknakahara 		if (mib->ifvm_p != NULL) {
9293043ffd9Sknakahara 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
9303043ffd9Sknakahara 			    mib->ifvm_p->if_xname);
9313043ffd9Sknakahara 			vlr.vlr_tag = mib->ifvm_tag;
9323043ffd9Sknakahara 		}
9333043ffd9Sknakahara 		vlan_putref_linkmib(mib, &psref);
9343043ffd9Sknakahara 		curlwp_bindx(bound);
93500746cb0Sthorpej 		error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
93600746cb0Sthorpej 		break;
93700746cb0Sthorpej 
93800746cb0Sthorpej 	case SIOCSIFFLAGS:
939de87fe67Sdyoung 		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
940de87fe67Sdyoung 			break;
94100746cb0Sthorpej 		/*
942c1e8f104Sthorpej 		 * For promiscuous mode, we enable promiscuous mode on
943c1e8f104Sthorpej 		 * the parent if we need promiscuous on the VLAN interface.
94400746cb0Sthorpej 		 */
9453043ffd9Sknakahara 		bound = curlwp_bind();
9463043ffd9Sknakahara 		mib = vlan_getref_linkmib(ifv, &psref);
9473043ffd9Sknakahara 		if (mib == NULL) {
9483043ffd9Sknakahara 			curlwp_bindx(bound);
9493043ffd9Sknakahara 			error = EBUSY;
9503043ffd9Sknakahara 			break;
9513043ffd9Sknakahara 		}
9523043ffd9Sknakahara 
9533043ffd9Sknakahara 		if (mib->ifvm_p != NULL)
954c1e8f104Sthorpej 			error = vlan_set_promisc(ifp);
9553043ffd9Sknakahara 		vlan_putref_linkmib(mib, &psref);
9563043ffd9Sknakahara 		curlwp_bindx(bound);
95700746cb0Sthorpej 		break;
95800746cb0Sthorpej 
95900746cb0Sthorpej 	case SIOCADDMULTI:
9603043ffd9Sknakahara 		mutex_enter(&ifv->ifv_lock);
9613043ffd9Sknakahara 		mib = ifv->ifv_mib;
9623043ffd9Sknakahara 		if (mib == NULL) {
9633043ffd9Sknakahara 			error = EBUSY;
9643043ffd9Sknakahara 			mutex_exit(&ifv->ifv_lock);
9653043ffd9Sknakahara 			break;
9663043ffd9Sknakahara 		}
9673043ffd9Sknakahara 
9683043ffd9Sknakahara 		error = (mib->ifvm_p != NULL) ?
9693043ffd9Sknakahara 		    (*mib->ifvm_msw->vmsw_addmulti)(ifv, ifr) : EINVAL;
9703043ffd9Sknakahara 		mib = NULL;
9713043ffd9Sknakahara 		mutex_exit(&ifv->ifv_lock);
972177a2587Senami 		break;
973177a2587Senami 
974177a2587Senami 	case SIOCDELMULTI:
9753043ffd9Sknakahara 		mutex_enter(&ifv->ifv_lock);
9763043ffd9Sknakahara 		mib = ifv->ifv_mib;
9773043ffd9Sknakahara 		if (mib == NULL) {
9783043ffd9Sknakahara 			error = EBUSY;
9793043ffd9Sknakahara 			mutex_exit(&ifv->ifv_lock);
9803043ffd9Sknakahara 			break;
9813043ffd9Sknakahara 		}
9823043ffd9Sknakahara 		error = (mib->ifvm_p != NULL) ?
9833043ffd9Sknakahara 		    (*mib->ifvm_msw->vmsw_delmulti)(ifv, ifr) : EINVAL;
9843043ffd9Sknakahara 		mib = NULL;
9853043ffd9Sknakahara 		mutex_exit(&ifv->ifv_lock);
98600746cb0Sthorpej 		break;
98700746cb0Sthorpej 
98888948cf5Sbouyer 	case SIOCSIFCAP:
98988948cf5Sbouyer 		ifcr = data;
99088948cf5Sbouyer 		/* make sure caps are enabled on parent */
9913043ffd9Sknakahara 		bound = curlwp_bind();
9923043ffd9Sknakahara 		mib = vlan_getref_linkmib(ifv, &psref);
9933043ffd9Sknakahara 		if (mib == NULL) {
9943043ffd9Sknakahara 			curlwp_bindx(bound);
9953043ffd9Sknakahara 			error = EBUSY;
9963043ffd9Sknakahara 			break;
9973043ffd9Sknakahara 		}
9983043ffd9Sknakahara 
9993043ffd9Sknakahara 		if (mib->ifvm_p == NULL) {
10003043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
10013043ffd9Sknakahara 			curlwp_bindx(bound);
1002fe703e0bSjoerg 			error = EINVAL;
1003fe703e0bSjoerg 			break;
1004fe703e0bSjoerg 		}
10053043ffd9Sknakahara 		if ((mib->ifvm_p->if_capenable & ifcr->ifcr_capenable) !=
100688948cf5Sbouyer 		    ifcr->ifcr_capenable) {
10073043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
10083043ffd9Sknakahara 			curlwp_bindx(bound);
100988948cf5Sbouyer 			error = EINVAL;
101088948cf5Sbouyer 			break;
101188948cf5Sbouyer 		}
10123043ffd9Sknakahara 
10133043ffd9Sknakahara 		vlan_putref_linkmib(mib, &psref);
10143043ffd9Sknakahara 		curlwp_bindx(bound);
10153043ffd9Sknakahara 
101688948cf5Sbouyer 		if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
101788948cf5Sbouyer 			error = 0;
101888948cf5Sbouyer 		break;
10194d45c70fSdyoung 	case SIOCINITIFADDR:
10203043ffd9Sknakahara 		bound = curlwp_bind();
10213043ffd9Sknakahara 		mib = vlan_getref_linkmib(ifv, &psref);
10223043ffd9Sknakahara 		if (mib == NULL) {
10233043ffd9Sknakahara 			curlwp_bindx(bound);
10243043ffd9Sknakahara 			error = EBUSY;
10254d45c70fSdyoung 			break;
10264d45c70fSdyoung 		}
10274d45c70fSdyoung 
10283043ffd9Sknakahara 		if (mib->ifvm_p == NULL) {
10293043ffd9Sknakahara 			error = EINVAL;
10303043ffd9Sknakahara 			vlan_putref_linkmib(mib, &psref);
10313043ffd9Sknakahara 			curlwp_bindx(bound);
10323043ffd9Sknakahara 			break;
10333043ffd9Sknakahara 		}
10343043ffd9Sknakahara 		vlan_putref_linkmib(mib, &psref);
10353043ffd9Sknakahara 		curlwp_bindx(bound);
10363043ffd9Sknakahara 
10374d45c70fSdyoung 		ifp->if_flags |= IFF_UP;
10384d45c70fSdyoung #ifdef INET
10394d45c70fSdyoung 		if (ifa->ifa_addr->sa_family == AF_INET)
10404d45c70fSdyoung 			arp_ifinit(ifp, ifa);
10414d45c70fSdyoung #endif
10424d45c70fSdyoung 		break;
10434d45c70fSdyoung 
104400746cb0Sthorpej 	default:
1045de87fe67Sdyoung 		error = ether_ioctl(ifp, cmd, data);
104600746cb0Sthorpej 	}
104700746cb0Sthorpej 
1048fd270400Smaxv 	return error;
104900746cb0Sthorpej }
105000746cb0Sthorpej 
105100746cb0Sthorpej static int
vlan_ether_addmulti(struct ifvlan * ifv,struct ifreq * ifr)105296375e93Sthorpej vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
105300746cb0Sthorpej {
105443390716Sdyoung 	const struct sockaddr *sa = ifreq_getaddr(SIOCADDMULTI, ifr);
105500746cb0Sthorpej 	struct vlan_mc_entry *mc;
10562b028087Smatt 	uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
10573043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
105800746cb0Sthorpej 	int error;
105900746cb0Sthorpej 
10603043ffd9Sknakahara 	KASSERT(mutex_owned(&ifv->ifv_lock));
10613043ffd9Sknakahara 
106243390716Sdyoung 	if (sa->sa_len > sizeof(struct sockaddr_storage))
1063fd270400Smaxv 		return EINVAL;
106400746cb0Sthorpej 
106543390716Sdyoung 	error = ether_addmulti(sa, &ifv->ifv_ec);
10668e1cd4f3Senami 	if (error != ENETRESET)
1067fd270400Smaxv 		return error;
10688e1cd4f3Senami 
10698e1cd4f3Senami 	/*
1070fd270400Smaxv 	 * This is a new multicast address.  We have to tell parent
10718e1cd4f3Senami 	 * about it.  Also, remember this multicast address so that
1072fd270400Smaxv 	 * we can delete it on unconfigure.
10738e1cd4f3Senami 	 */
10749b87d582Scegger 	mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT);
10758e1cd4f3Senami 	if (mc == NULL) {
10768e1cd4f3Senami 		error = ENOMEM;
10778e1cd4f3Senami 		goto alloc_failed;
107800746cb0Sthorpej 	}
107900746cb0Sthorpej 
10808e1cd4f3Senami 	/*
1081fd270400Smaxv 	 * Since ether_addmulti() returned ENETRESET, the following two
10820031abc3Smaxv 	 * statements shouldn't fail. Here ifv_ec is implicitly protected
10830031abc3Smaxv 	 * by the ifv_lock lock.
10848e1cd4f3Senami 	 */
10850031abc3Smaxv 	error = ether_multiaddr(sa, addrlo, addrhi);
10860031abc3Smaxv 	KASSERT(error == 0);
10875dd99733Syamaguchi 
10885dd99733Syamaguchi 	ETHER_LOCK(&ifv->ifv_ec);
1089f07cea33Syamaguchi 	mc->mc_enm = ether_lookup_multi(addrlo, addrhi, &ifv->ifv_ec);
10905dd99733Syamaguchi 	ETHER_UNLOCK(&ifv->ifv_ec);
10915dd99733Syamaguchi 
10920031abc3Smaxv 	KASSERT(mc->mc_enm != NULL);
10930031abc3Smaxv 
109443390716Sdyoung 	memcpy(&mc->mc_addr, sa, sa->sa_len);
10958e1cd4f3Senami 	LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
10968e1cd4f3Senami 
10973043ffd9Sknakahara 	mib = ifv->ifv_mib;
1098e0bc64f9Sozaki-r 
1099e0bc64f9Sozaki-r 	KERNEL_LOCK_UNLESS_IFP_MPSAFE(mib->ifvm_p);
11003043ffd9Sknakahara 	error = if_mcast_op(mib->ifvm_p, SIOCADDMULTI, sa);
1101e0bc64f9Sozaki-r 	KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(mib->ifvm_p);
11023043ffd9Sknakahara 
11038e1cd4f3Senami 	if (error != 0)
11048e1cd4f3Senami 		goto ioctl_failed;
1105fd270400Smaxv 	return error;
11068e1cd4f3Senami 
11078e1cd4f3Senami ioctl_failed:
11088e1cd4f3Senami 	LIST_REMOVE(mc, mc_entries);
11099b87d582Scegger 	free(mc, M_DEVBUF);
1110fd270400Smaxv 
11118e1cd4f3Senami alloc_failed:
111243390716Sdyoung 	(void)ether_delmulti(sa, &ifv->ifv_ec);
1113fd270400Smaxv 	return error;
111400746cb0Sthorpej }
111500746cb0Sthorpej 
11168e1cd4f3Senami static int
vlan_ether_delmulti(struct ifvlan * ifv,struct ifreq * ifr)111796375e93Sthorpej vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
11188e1cd4f3Senami {
111943390716Sdyoung 	const struct sockaddr *sa = ifreq_getaddr(SIOCDELMULTI, ifr);
11208e1cd4f3Senami 	struct ether_multi *enm;
11218e1cd4f3Senami 	struct vlan_mc_entry *mc;
11223043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
11232b028087Smatt 	uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
11248e1cd4f3Senami 	int error;
11258e1cd4f3Senami 
11263043ffd9Sknakahara 	KASSERT(mutex_owned(&ifv->ifv_lock));
11273043ffd9Sknakahara 
11288e1cd4f3Senami 	/*
11298e1cd4f3Senami 	 * Find a key to lookup vlan_mc_entry.  We have to do this
1130fd270400Smaxv 	 * before calling ether_delmulti for obvious reasons.
11318e1cd4f3Senami 	 */
113243390716Sdyoung 	if ((error = ether_multiaddr(sa, addrlo, addrhi)) != 0)
1133fd270400Smaxv 		return error;
11345dd99733Syamaguchi 
11355dd99733Syamaguchi 	ETHER_LOCK(&ifv->ifv_ec);
1136f07cea33Syamaguchi 	enm = ether_lookup_multi(addrlo, addrhi, &ifv->ifv_ec);
11375dd99733Syamaguchi 	ETHER_UNLOCK(&ifv->ifv_ec);
113848079a5cSyamaguchi 	if (enm == NULL)
113948079a5cSyamaguchi 		return EINVAL;
114048079a5cSyamaguchi 
114148079a5cSyamaguchi 	LIST_FOREACH(mc, &ifv->ifv_mc_listhead, mc_entries) {
114248079a5cSyamaguchi 		if (mc->mc_enm == enm)
114348079a5cSyamaguchi 			break;
114448079a5cSyamaguchi 	}
114548079a5cSyamaguchi 
114648079a5cSyamaguchi 	/* We woun't delete entries we didn't add */
114748079a5cSyamaguchi 	if (mc == NULL)
114848079a5cSyamaguchi 		return EINVAL;
11498e1cd4f3Senami 
115043390716Sdyoung 	error = ether_delmulti(sa, &ifv->ifv_ec);
11518e1cd4f3Senami 	if (error != ENETRESET)
1152fd270400Smaxv 		return error;
11538e1cd4f3Senami 
11548e1cd4f3Senami 	/* We no longer use this multicast address.  Tell parent so. */
11553043ffd9Sknakahara 	mib = ifv->ifv_mib;
11563043ffd9Sknakahara 	error = if_mcast_op(mib->ifvm_p, SIOCDELMULTI, sa);
11573043ffd9Sknakahara 
11588e1cd4f3Senami 	if (error == 0) {
11598e1cd4f3Senami 		/* And forget about this address. */
11608e1cd4f3Senami 		LIST_REMOVE(mc, mc_entries);
11619b87d582Scegger 		free(mc, M_DEVBUF);
116248079a5cSyamaguchi 	} else {
116343390716Sdyoung 		(void)ether_addmulti(sa, &ifv->ifv_ec);
116448079a5cSyamaguchi 	}
1165fd270400Smaxv 
1166fd270400Smaxv 	return error;
11678e1cd4f3Senami }
11688e1cd4f3Senami 
11698e1cd4f3Senami /*
1170dc325578Spooka  * Delete any multicast address we have asked to add from parent
11718e1cd4f3Senami  * interface.  Called when the vlan is being unconfigured.
11728e1cd4f3Senami  */
11738e1cd4f3Senami static void
vlan_ether_purgemulti(struct ifvlan * ifv)117496375e93Sthorpej vlan_ether_purgemulti(struct ifvlan *ifv)
11758e1cd4f3Senami {
11768e1cd4f3Senami 	struct vlan_mc_entry *mc;
11773043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
11783043ffd9Sknakahara 
11793043ffd9Sknakahara 	KASSERT(mutex_owned(&ifv->ifv_lock));
11803043ffd9Sknakahara 	mib = ifv->ifv_mib;
11813043ffd9Sknakahara 	if (mib == NULL) {
11823043ffd9Sknakahara 		return;
11833043ffd9Sknakahara 	}
11848e1cd4f3Senami 
11858e1cd4f3Senami 	while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
11863043ffd9Sknakahara 		(void)if_mcast_op(mib->ifvm_p, SIOCDELMULTI,
118748079a5cSyamaguchi 		    sstocsa(&mc->mc_addr));
11888e1cd4f3Senami 		LIST_REMOVE(mc, mc_entries);
11899b87d582Scegger 		free(mc, M_DEVBUF);
11908e1cd4f3Senami 	}
119100746cb0Sthorpej }
119200746cb0Sthorpej 
119300746cb0Sthorpej static void
vlan_start(struct ifnet * ifp)119400746cb0Sthorpej vlan_start(struct ifnet *ifp)
119500746cb0Sthorpej {
1196b817ce40Senami 	struct ifvlan *ifv = ifp->if_softc;
11973043ffd9Sknakahara 	struct ifnet *p;
11983043ffd9Sknakahara 	struct ethercom *ec;
119900746cb0Sthorpej 	struct mbuf *m;
12003043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
12013043ffd9Sknakahara 	struct psref psref;
1202fbe63cacSyamaguchi 	struct ether_header *eh;
1203df38aab1Syamaguchi 	int error, bound;
120400746cb0Sthorpej 
1205df38aab1Syamaguchi 	bound = curlwp_bind();
12063043ffd9Sknakahara 	mib = vlan_getref_linkmib(ifv, &psref);
1207df38aab1Syamaguchi 	if (mib == NULL) {
1208df38aab1Syamaguchi 		curlwp_bindx(bound);
12093043ffd9Sknakahara 		return;
1210df38aab1Syamaguchi 	}
1211d0b565bdSyamaguchi 
1212d0b565bdSyamaguchi 	if (__predict_false(mib->ifvm_p == NULL)) {
1213d0b565bdSyamaguchi 		vlan_putref_linkmib(mib, &psref);
1214df38aab1Syamaguchi 		curlwp_bindx(bound);
1215d0b565bdSyamaguchi 		return;
1216d0b565bdSyamaguchi 	}
1217d0b565bdSyamaguchi 
12183043ffd9Sknakahara 	p = mib->ifvm_p;
12193043ffd9Sknakahara 	ec = (void *)mib->ifvm_p;
12203043ffd9Sknakahara 
122100746cb0Sthorpej 	ifp->if_flags |= IFF_OACTIVE;
122200746cb0Sthorpej 
122300746cb0Sthorpej 	for (;;) {
12245cbdcc50Sthorpej 		IFQ_DEQUEUE(&ifp->if_snd, m);
122500746cb0Sthorpej 		if (m == NULL)
122600746cb0Sthorpej 			break;
122700746cb0Sthorpej 
1228fbe63cacSyamaguchi 		if (m->m_len < sizeof(*eh)) {
1229fbe63cacSyamaguchi 			m = m_pullup(m, sizeof(*eh));
1230fbe63cacSyamaguchi 			if (m == NULL) {
1231fbe63cacSyamaguchi 				if_statinc(ifp, if_oerrors);
1232fbe63cacSyamaguchi 				continue;
1233fbe63cacSyamaguchi 			}
1234fbe63cacSyamaguchi 		}
1235fbe63cacSyamaguchi 
1236fbe63cacSyamaguchi 		eh = mtod(m, struct ether_header *);
1237fbe63cacSyamaguchi 		if (ntohs(eh->ether_type) == ETHERTYPE_VLAN) {
1238fbe63cacSyamaguchi 			m_freem(m);
1239fbe63cacSyamaguchi 			if_statinc(ifp, if_noproto);
1240fbe63cacSyamaguchi 			continue;
1241fbe63cacSyamaguchi 		}
1242fbe63cacSyamaguchi 
12435cbdcc50Sthorpej #ifdef ALTQ
12445cbdcc50Sthorpej 		/*
1245fd270400Smaxv 		 * KERNEL_LOCK is required for ALTQ even if NET_MPSAFE is
1246fd270400Smaxv 		 * defined.
1247202390b5Sknakahara 		 */
1248202390b5Sknakahara 		KERNEL_LOCK(1, NULL);
1249202390b5Sknakahara 		/*
12505cbdcc50Sthorpej 		 * If ALTQ is enabled on the parent interface, do
12515cbdcc50Sthorpej 		 * classification; the queueing discipline might
12525cbdcc50Sthorpej 		 * not require classification, but might require
12535cbdcc50Sthorpej 		 * the address family/header pointer in the pktattr.
12545cbdcc50Sthorpej 		 */
12555cbdcc50Sthorpej 		if (ALTQ_IS_ENABLED(&p->if_snd)) {
12565cbdcc50Sthorpej 			switch (p->if_type) {
12575cbdcc50Sthorpej 			case IFT_ETHER:
125825937c0dSknakahara 				altq_etherclassify(&p->if_snd, m);
12595cbdcc50Sthorpej 				break;
12605cbdcc50Sthorpej 			default:
1261fd270400Smaxv 				panic("%s: impossible (altq)", __func__);
12625cbdcc50Sthorpej 			}
12635cbdcc50Sthorpej 		}
1264202390b5Sknakahara 		KERNEL_UNLOCK_ONE(NULL);
12655cbdcc50Sthorpej #endif /* ALTQ */
12665cbdcc50Sthorpej 
12678517c9d1Smsaitoh 		bpf_mtap(ifp, m, BPF_D_OUT);
126800746cb0Sthorpej 		/*
12696e08061cSbouyer 		 * If the parent can insert the tag itself, just mark
12706e08061cSbouyer 		 * the tag in the mbuf header.
12716e08061cSbouyer 		 */
1272888f3a28Smsaitoh 		if (ec->ec_capenable & ETHERCAP_VLAN_HWTAGGING) {
1273632539d6Sknakahara 			vlan_set_tag(m, mib->ifvm_tag);
12746e08061cSbouyer 		} else {
12756e08061cSbouyer 			/*
1276dc325578Spooka 			 * insert the tag ourselves
127700746cb0Sthorpej 			 */
127800746cb0Sthorpej 
127996375e93Sthorpej 			switch (p->if_type) {
128096375e93Sthorpej 			case IFT_ETHER:
1281*60b4fee9Syamaguchi 				(void)ether_inject_vlantag(&m,
1282*60b4fee9Syamaguchi 				    ETHERTYPE_VLAN, mib->ifvm_tag);
128331ca9bf0Syamaguchi 				if (m == NULL) {
1284*60b4fee9Syamaguchi 					printf("%s: unable to inject VLAN tag",
128531ca9bf0Syamaguchi 					    p->if_xname);
128631ca9bf0Syamaguchi 					continue;
128731ca9bf0Syamaguchi 				}
128896375e93Sthorpej 				break;
128996375e93Sthorpej 
129096375e93Sthorpej 			default:
1291fd270400Smaxv 				panic("%s: impossible", __func__);
129296375e93Sthorpej 			}
12936e08061cSbouyer 		}
129400746cb0Sthorpej 
1295e8fa4b9dSozaki-r 		if ((p->if_flags & IFF_RUNNING) == 0) {
1296e8fa4b9dSozaki-r 			m_freem(m);
1297e8fa4b9dSozaki-r 			continue;
1298e8fa4b9dSozaki-r 		}
1299e8fa4b9dSozaki-r 
1300202390b5Sknakahara 		error = if_transmit_lock(p, m);
13015cbdcc50Sthorpej 		if (error) {
13025cbdcc50Sthorpej 			/* mbuf is already freed */
130371aab90dSthorpej 			if_statinc(ifp, if_oerrors);
130400746cb0Sthorpej 			continue;
130500746cb0Sthorpej 		}
130671aab90dSthorpej 		if_statinc(ifp, if_opackets);
130700746cb0Sthorpej 	}
130800746cb0Sthorpej 
130900746cb0Sthorpej 	ifp->if_flags &= ~IFF_OACTIVE;
13103043ffd9Sknakahara 
13113043ffd9Sknakahara 	/* Remove reference to mib before release */
13123043ffd9Sknakahara 	vlan_putref_linkmib(mib, &psref);
1313df38aab1Syamaguchi 	curlwp_bindx(bound);
13143043ffd9Sknakahara }
13153043ffd9Sknakahara 
13163043ffd9Sknakahara static int
vlan_transmit(struct ifnet * ifp,struct mbuf * m)13173043ffd9Sknakahara vlan_transmit(struct ifnet *ifp, struct mbuf *m)
13183043ffd9Sknakahara {
13193043ffd9Sknakahara 	struct ifvlan *ifv = ifp->if_softc;
13203043ffd9Sknakahara 	struct ifnet *p;
13213043ffd9Sknakahara 	struct ethercom *ec;
13223043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
13233043ffd9Sknakahara 	struct psref psref;
1324fbe63cacSyamaguchi 	struct ether_header *eh;
1325df38aab1Syamaguchi 	int error, bound;
132698bbb0f4Sknakahara 	size_t pktlen = m->m_pkthdr.len;
132798bbb0f4Sknakahara 	bool mcast = (m->m_flags & M_MCAST) != 0;
13283043ffd9Sknakahara 
1329fbe63cacSyamaguchi 	if (m->m_len < sizeof(*eh)) {
1330fbe63cacSyamaguchi 		m = m_pullup(m, sizeof(*eh));
1331fbe63cacSyamaguchi 		if (m == NULL) {
1332fbe63cacSyamaguchi 			if_statinc(ifp, if_oerrors);
1333fbe63cacSyamaguchi 			return ENOBUFS;
1334fbe63cacSyamaguchi 		}
1335fbe63cacSyamaguchi 	}
1336fbe63cacSyamaguchi 
1337fbe63cacSyamaguchi 	eh = mtod(m, struct ether_header *);
1338fbe63cacSyamaguchi 	if (ntohs(eh->ether_type) == ETHERTYPE_VLAN) {
1339fbe63cacSyamaguchi 		m_freem(m);
1340fbe63cacSyamaguchi 		if_statinc(ifp, if_noproto);
1341fbe63cacSyamaguchi 		return EPROTONOSUPPORT;
1342fbe63cacSyamaguchi 	}
1343fbe63cacSyamaguchi 
1344df38aab1Syamaguchi 	bound = curlwp_bind();
13453043ffd9Sknakahara 	mib = vlan_getref_linkmib(ifv, &psref);
13463043ffd9Sknakahara 	if (mib == NULL) {
1347df38aab1Syamaguchi 		curlwp_bindx(bound);
13483043ffd9Sknakahara 		m_freem(m);
13493043ffd9Sknakahara 		return ENETDOWN;
13503043ffd9Sknakahara 	}
13513043ffd9Sknakahara 
1352d0b565bdSyamaguchi 	if (__predict_false(mib->ifvm_p == NULL)) {
1353d0b565bdSyamaguchi 		vlan_putref_linkmib(mib, &psref);
1354df38aab1Syamaguchi 		curlwp_bindx(bound);
1355d0b565bdSyamaguchi 		m_freem(m);
1356d0b565bdSyamaguchi 		return ENETDOWN;
1357d0b565bdSyamaguchi 	}
1358d0b565bdSyamaguchi 
13593043ffd9Sknakahara 	p = mib->ifvm_p;
13603043ffd9Sknakahara 	ec = (void *)mib->ifvm_p;
13613043ffd9Sknakahara 
13628517c9d1Smsaitoh 	bpf_mtap(ifp, m, BPF_D_OUT);
13631403b07cSjmcneill 
13649e10f27aStih 	if ((error = pfil_run_hooks(ifp->if_pfil, &m, ifp, PFIL_OUT)) != 0)
13651403b07cSjmcneill 		goto out;
13669e10f27aStih 	if (m == NULL)
13679e10f27aStih 		goto out;
13681403b07cSjmcneill 
13693043ffd9Sknakahara 	/*
13703043ffd9Sknakahara 	 * If the parent can insert the tag itself, just mark
13713043ffd9Sknakahara 	 * the tag in the mbuf header.
13723043ffd9Sknakahara 	 */
1373888f3a28Smsaitoh 	if (ec->ec_capenable & ETHERCAP_VLAN_HWTAGGING) {
1374632539d6Sknakahara 		vlan_set_tag(m, mib->ifvm_tag);
13753043ffd9Sknakahara 	} else {
13763043ffd9Sknakahara 		/*
13773043ffd9Sknakahara 		 * insert the tag ourselves
13783043ffd9Sknakahara 		 */
137931ca9bf0Syamaguchi 		switch (p->if_type) {
138031ca9bf0Syamaguchi 		case IFT_ETHER:
1381*60b4fee9Syamaguchi 			error = ether_inject_vlantag(&m,
1382*60b4fee9Syamaguchi 			    ETHERTYPE_VLAN, mib->ifvm_tag);
1383*60b4fee9Syamaguchi 			if (error != 0) {
1384*60b4fee9Syamaguchi 				KASSERT(m == NULL);
1385*60b4fee9Syamaguchi 				printf("%s: unable to inject VLAN tag",
13863043ffd9Sknakahara 				    p->if_xname);
13873043ffd9Sknakahara 				goto out;
13883043ffd9Sknakahara 			}
13893043ffd9Sknakahara 			break;
13903043ffd9Sknakahara 
13913043ffd9Sknakahara 		default:
1392fd270400Smaxv 			panic("%s: impossible", __func__);
13933043ffd9Sknakahara 		}
13943043ffd9Sknakahara 	}
13953043ffd9Sknakahara 
13963043ffd9Sknakahara 	if ((p->if_flags & IFF_RUNNING) == 0) {
13973043ffd9Sknakahara 		m_freem(m);
13983043ffd9Sknakahara 		error = ENETDOWN;
13993043ffd9Sknakahara 		goto out;
14003043ffd9Sknakahara 	}
14013043ffd9Sknakahara 
14023043ffd9Sknakahara 	error = if_transmit_lock(p, m);
140371aab90dSthorpej 	net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
14043043ffd9Sknakahara 	if (error) {
14053043ffd9Sknakahara 		/* mbuf is already freed */
140671aab90dSthorpej 		if_statinc_ref(nsr, if_oerrors);
14073043ffd9Sknakahara 	} else {
140871aab90dSthorpej 		if_statinc_ref(nsr, if_opackets);
140971aab90dSthorpej 		if_statadd_ref(nsr, if_obytes, pktlen);
141098bbb0f4Sknakahara 		if (mcast)
141171aab90dSthorpej 			if_statinc_ref(nsr, if_omcasts);
14123043ffd9Sknakahara 	}
141371aab90dSthorpej 	IF_STAT_PUTREF(ifp);
14143043ffd9Sknakahara 
14153043ffd9Sknakahara out:
14163043ffd9Sknakahara 	/* Remove reference to mib before release */
14173043ffd9Sknakahara 	vlan_putref_linkmib(mib, &psref);
1418df38aab1Syamaguchi 	curlwp_bindx(bound);
1419df38aab1Syamaguchi 
14203043ffd9Sknakahara 	return error;
142100746cb0Sthorpej }
142200746cb0Sthorpej 
142300746cb0Sthorpej /*
142400746cb0Sthorpej  * Given an Ethernet frame, find a valid vlan interface corresponding to the
14259bc855a9Ssimonb  * given source interface and tag, then run the real packet through the
14269bc855a9Ssimonb  * parent's input routine.
142700746cb0Sthorpej  */
14283a942150Syamaguchi struct mbuf *
vlan_input(struct ifnet * ifp,struct mbuf * m)142900746cb0Sthorpej vlan_input(struct ifnet *ifp, struct mbuf *m)
143000746cb0Sthorpej {
143100746cb0Sthorpej 	struct ifvlan *ifv;
1432d97a4d84Smsaitoh 	uint16_t vid;
14333043ffd9Sknakahara 	struct ifvlan_linkmib *mib;
14343043ffd9Sknakahara 	struct psref psref;
143500746cb0Sthorpej 
1436*60b4fee9Syamaguchi 	KASSERT(vlan_has_tag(m));
1437d97a4d84Smsaitoh 	vid = EVL_VLANOFTAG(vlan_get_tag(m));
14383a942150Syamaguchi 	KASSERT(vid != 0);
1439*60b4fee9Syamaguchi 
1440d97a4d84Smsaitoh 	mib = vlan_lookup_tag_psref(ifp, vid, &psref);
14413043ffd9Sknakahara 	if (mib == NULL) {
14423a942150Syamaguchi 		return m;
144300746cb0Sthorpej 	}
144424db4c5aSchristos 
14453043ffd9Sknakahara 	ifv = mib->ifvm_ifvlan;
14463043ffd9Sknakahara 	if ((ifv->ifv_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
14473043ffd9Sknakahara 	    (IFF_UP | IFF_RUNNING)) {
14483043ffd9Sknakahara 		m_freem(m);
144971aab90dSthorpej 		if_statinc(ifp, if_noproto);
14503043ffd9Sknakahara 		goto out;
14513043ffd9Sknakahara 	}
14523043ffd9Sknakahara 
145324db4c5aSchristos 	/*
145431ca9bf0Syamaguchi 	 * Having found a valid vlan interface corresponding to
145531ca9bf0Syamaguchi 	 * the given source interface and vlan tag.
145631ca9bf0Syamaguchi 	 * remove the vlan tag.
145724db4c5aSchristos 	 */
145831ca9bf0Syamaguchi 	m->m_flags &= ~M_VLANTAG;
145924db4c5aSchristos 
14600a850d90Syamaguchi 	/*
14610a850d90Syamaguchi 	 * Drop promiscuously received packets if we are not in
14620a850d90Syamaguchi 	 * promiscuous mode
14630a850d90Syamaguchi 	 */
14640a850d90Syamaguchi 	if ((m->m_flags & (M_BCAST | M_MCAST)) == 0 &&
14650a850d90Syamaguchi 	    (ifp->if_flags & IFF_PROMISC) &&
14660a850d90Syamaguchi 	    (ifv->ifv_if.if_flags & IFF_PROMISC) == 0) {
14670a850d90Syamaguchi 		struct ether_header *eh;
14680a850d90Syamaguchi 
14690a850d90Syamaguchi 		eh = mtod(m, struct ether_header *);
14700a850d90Syamaguchi 		if (memcmp(CLLADDR(ifv->ifv_if.if_sadl),
14710a850d90Syamaguchi 		    eh->ether_dhost, ETHER_ADDR_LEN) != 0) {
14720a850d90Syamaguchi 			m_freem(m);
14730a850d90Syamaguchi 			if_statinc(&ifv->ifv_if, if_ierrors);
14740a850d90Syamaguchi 			goto out;
14750a850d90Syamaguchi 		}
14760a850d90Syamaguchi 	}
14770a850d90Syamaguchi 
1478758ba73eSozaki-r 	m_set_rcvif(m, &ifv->ifv_if);
147900746cb0Sthorpej 
14809e10f27aStih 	if (pfil_run_hooks(ifp->if_pfil, &m, ifp, PFIL_IN) != 0)
14811403b07cSjmcneill 		goto out;
14829e10f27aStih 	if (m == NULL)
14839e10f27aStih 		goto out;
14841403b07cSjmcneill 
1485a743bc13Sozaki-r 	m->m_flags &= ~M_PROMISC;
1486b8256fd8Sozaki-r 	if_input(&ifv->ifv_if, m);
14873043ffd9Sknakahara out:
14883043ffd9Sknakahara 	vlan_putref_linkmib(mib, &psref);
14893a942150Syamaguchi 	return NULL;
149000746cb0Sthorpej }
14914a3aea06Schristos 
14924a3aea06Schristos /*
14932f3349bcSroy  * If the parent link state changed, the vlan link state should change also.
14942f3349bcSroy  */
1495cbb731a8Syamaguchi static void
vlan_link_state_changed(void * xifv)1496cbb731a8Syamaguchi vlan_link_state_changed(void *xifv)
14972f3349bcSroy {
1498cbb731a8Syamaguchi 	struct ifvlan *ifv = xifv;
1499cbb731a8Syamaguchi 	struct ifnet *ifp, *p;
15002f3349bcSroy 	struct ifvlan_linkmib *mib;
15012f3349bcSroy 	struct psref psref;
1502df38aab1Syamaguchi 	int bound;
15032f3349bcSroy 
1504df38aab1Syamaguchi 	bound = curlwp_bind();
15052f3349bcSroy 	mib = vlan_getref_linkmib(ifv, &psref);
1506df38aab1Syamaguchi 	if (mib == NULL) {
1507df38aab1Syamaguchi 		curlwp_bindx(bound);
1508cbb731a8Syamaguchi 		return;
1509df38aab1Syamaguchi 	}
15102f3349bcSroy 
1511cbb731a8Syamaguchi 	if (mib->ifvm_p == NULL) {
1512cbb731a8Syamaguchi 		vlan_putref_linkmib(mib, &psref);
1513df38aab1Syamaguchi 		curlwp_bindx(bound);
1514cbb731a8Syamaguchi 		return;
15152f3349bcSroy 	}
15162f3349bcSroy 
1517cbb731a8Syamaguchi 	ifp = &ifv->ifv_if;
1518cbb731a8Syamaguchi 	p = mib->ifvm_p;
1519cbb731a8Syamaguchi 	if_link_state_change(ifp, p->if_link_state);
1520cbb731a8Syamaguchi 
15212f3349bcSroy 	vlan_putref_linkmib(mib, &psref);
1522df38aab1Syamaguchi 	curlwp_bindx(bound);
15232f3349bcSroy }
15242f3349bcSroy 
15252f3349bcSroy /*
15264a3aea06Schristos  * Module infrastructure
15274a3aea06Schristos  */
15284a3aea06Schristos #include "if_module.h"
15294a3aea06Schristos 
153015934ce1Spgoyette IF_MODULE(MODULE_CLASS_DRIVER, vlan, NULL)
1531