1139823Simp/*-
234649Swollman * Copyright 1998 Massachusetts Institute of Technology
334649Swollman *
434649Swollman * Permission to use, copy, modify, and distribute this software and
534649Swollman * its documentation for any purpose and without fee is hereby
634649Swollman * granted, provided that both the above copyright notice and this
734649Swollman * permission notice appear in all copies, that both the above
834649Swollman * copyright notice and this permission notice appear in all
934649Swollman * supporting documentation, and that the name of M.I.T. not be used
1034649Swollman * in advertising or publicity pertaining to distribution of the
1134649Swollman * software without specific, written prior permission.  M.I.T. makes
1234649Swollman * no representations about the suitability of this software for any
1334649Swollman * purpose.  It is provided "as is" without express or implied
1434649Swollman * warranty.
1534649Swollman *
1634649Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1734649Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1834649Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1934649Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2034649Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2134649Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2234649Swollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2334649Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2434649Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2534649Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2634649Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2734649Swollman * SUCH DAMAGE.
2834649Swollman */
2934649Swollman
3034649Swollman/*
3134649Swollman * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
3234649Swollman * Might be extended some day to also handle IEEE 802.1p priority
3334649Swollman * tagging.  This is sort of sneaky in the implementation, since
3434649Swollman * we need to pretend to be enough of an Ethernet implementation
3534649Swollman * to make arp work.  The way we do this is by telling everyone
3634649Swollman * that we are an Ethernet, and then catch the packets that
37228089Sjhb * ether_output() sends to us via if_transmit(), rewrite them for
38228089Sjhb * use by the real outgoing interface, and ask it to send them.
3934649Swollman */
4034649Swollman
41204156Syongari#include <sys/cdefs.h>
42204156Syongari__FBSDID("$FreeBSD$");
43204156Syongari
44249628Soleg#include "opt_inet.h"
45155051Sglebius#include "opt_vlan.h"
4634649Swollman
4734649Swollman#include <sys/param.h>
4834649Swollman#include <sys/kernel.h>
49155051Sglebius#include <sys/lock.h>
5044763Swpaul#include <sys/malloc.h>
5134649Swollman#include <sys/mbuf.h>
5271862Speter#include <sys/module.h>
53155051Sglebius#include <sys/rwlock.h>
5444763Swpaul#include <sys/queue.h>
5534649Swollman#include <sys/socket.h>
5634649Swollman#include <sys/sockio.h>
5734649Swollman#include <sys/sysctl.h>
5834649Swollman#include <sys/systm.h>
59219819Sjeff#include <sys/sx.h>
6034649Swollman
6134649Swollman#include <net/bpf.h>
6234649Swollman#include <net/ethernet.h>
6334649Swollman#include <net/if.h>
64130933Sbrooks#include <net/if_clone.h>
6534649Swollman#include <net/if_dl.h>
6634649Swollman#include <net/if_types.h>
6734649Swollman#include <net/if_vlan_var.h>
68185571Sbz#include <net/vnet.h>
6934649Swollman
70249628Soleg#ifdef INET
71249628Soleg#include <netinet/in.h>
72249628Soleg#include <netinet/if_ether.h>
73249628Soleg#endif
74249628Soleg
75155051Sglebius#define	VLAN_DEF_HWIDTH	4
76155114Syar#define	VLAN_IFFLAGS	(IFF_BROADCAST | IFF_MULTICAST)
7783115Sbrooks
78165662Syar#define	UP_AND_RUNNING(ifp) \
79165662Syar    ((ifp)->if_flags & IFF_UP && (ifp)->if_drv_flags & IFF_DRV_RUNNING)
80165662Syar
81155051SglebiusLIST_HEAD(ifvlanhead, ifvlan);
82155051Sglebius
83155051Sglebiusstruct ifvlantrunk {
84155051Sglebius	struct	ifnet   *parent;	/* parent interface of this trunk */
85155051Sglebius	struct	rwlock	rw;
86155051Sglebius#ifdef VLAN_ARRAY
87159823Syar#define	VLAN_ARRAY_SIZE	(EVL_VLID_MASK + 1)
88159823Syar	struct	ifvlan	*vlans[VLAN_ARRAY_SIZE]; /* static table */
89155051Sglebius#else
90155051Sglebius	struct	ifvlanhead *hash;	/* dynamic hash-list table */
91155051Sglebius	uint16_t	hmask;
92155051Sglebius	uint16_t	hwidth;
93155051Sglebius#endif
94155051Sglebius	int		refcnt;
95155051Sglebius};
96155051Sglebius
97106932Ssamstruct vlan_mc_entry {
98219819Sjeff	struct sockaddr_dl		mc_addr;
99106932Ssam	SLIST_ENTRY(vlan_mc_entry)	mc_entries;
100106932Ssam};
101106932Ssam
102106932Ssamstruct	ifvlan {
103155051Sglebius	struct	ifvlantrunk *ifv_trunk;
104147256Sbrooks	struct	ifnet *ifv_ifp;
105219819Sjeff	void	*ifv_cookie;
106155051Sglebius#define	TRUNK(ifv)	((ifv)->ifv_trunk)
107155051Sglebius#define	PARENT(ifv)	((ifv)->ifv_trunk->parent)
108150846Syar	int	ifv_pflags;	/* special flags we have set on parent */
109106932Ssam	struct	ifv_linkmib {
110106932Ssam		int	ifvm_encaplen;	/* encapsulation length */
111106932Ssam		int	ifvm_mtufudge;	/* MTU fudged by this much */
112106932Ssam		int	ifvm_mintu;	/* min transmission unit */
113161326Syar		uint16_t ifvm_proto;	/* encapsulation ethertype */
114155051Sglebius		uint16_t ifvm_tag;	/* tag to apply on packets leaving if */
115106932Ssam	}	ifv_mib;
116160019Syar	SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
117167483Syar#ifndef VLAN_ARRAY
118106932Ssam	LIST_ENTRY(ifvlan) ifv_list;
119167483Syar#endif
120106932Ssam};
121161326Syar#define	ifv_proto	ifv_mib.ifvm_proto
122230026Srwatson#define	ifv_vid		ifv_mib.ifvm_tag
123106932Ssam#define	ifv_encaplen	ifv_mib.ifvm_encaplen
124106932Ssam#define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
125106932Ssam#define	ifv_mintu	ifv_mib.ifvm_mintu
126106932Ssam
127155051Sglebius/* Special flags we should propagate to parent. */
128150846Syarstatic struct {
129150846Syar	int flag;
130150846Syar	int (*func)(struct ifnet *, int);
131150846Syar} vlan_pflags[] = {
132150846Syar	{IFF_PROMISC, ifpromisc},
133150846Syar	{IFF_ALLMULTI, if_allmulti},
134150846Syar	{0, NULL}
135150846Syar};
136106932Ssam
13745451SwpaulSYSCTL_DECL(_net_link);
138227309Sedstatic SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0,
139227309Sed    "IEEE 802.1Q VLAN");
140227309Sedstatic SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0,
141227309Sed    "for consistency");
14234649Swollman
143161210Syarstatic int soft_pad = 0;
144161210SyarSYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &soft_pad, 0,
145161210Syar	   "pad short frames before tagging");
146161210Syar
147241610Sglebiusstatic const char vlanname[] = "vlan";
148241610Sglebiusstatic MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface");
14934649Swollman
150159823Syarstatic eventhandler_tag ifdetach_tag;
151202588Sthompsastatic eventhandler_tag iflladdr_tag;
152159823Syar
153119780Ssam/*
154155051Sglebius * We have a global mutex, that is used to serialize configuration
155155051Sglebius * changes and isn't used in normal packet delivery.
156155051Sglebius *
157155051Sglebius * We also have a per-trunk rwlock, that is locked shared on packet
158155051Sglebius * processing and exclusive when configuration is changed.
159155051Sglebius *
160155051Sglebius * The VLAN_ARRAY substitutes the dynamic hash with a static array
161161603Sthompsa * with 4096 entries. In theory this can give a boost in processing,
162155051Sglebius * however on practice it does not. Probably this is because array
163155051Sglebius * is too big to fit into CPU cache.
164119780Ssam */
165219819Sjeffstatic struct sx ifv_lock;
166219819Sjeff#define	VLAN_LOCK_INIT()	sx_init(&ifv_lock, "vlan_global")
167219819Sjeff#define	VLAN_LOCK_DESTROY()	sx_destroy(&ifv_lock)
168219819Sjeff#define	VLAN_LOCK_ASSERT()	sx_assert(&ifv_lock, SA_LOCKED)
169219819Sjeff#define	VLAN_LOCK()		sx_xlock(&ifv_lock)
170219819Sjeff#define	VLAN_UNLOCK()		sx_xunlock(&ifv_lock)
171241610Sglebius#define	TRUNK_LOCK_INIT(trunk)	rw_init(&(trunk)->rw, vlanname)
172155051Sglebius#define	TRUNK_LOCK_DESTROY(trunk) rw_destroy(&(trunk)->rw)
173155051Sglebius#define	TRUNK_LOCK(trunk)	rw_wlock(&(trunk)->rw)
174155051Sglebius#define	TRUNK_UNLOCK(trunk)	rw_wunlock(&(trunk)->rw)
175155051Sglebius#define	TRUNK_LOCK_ASSERT(trunk) rw_assert(&(trunk)->rw, RA_WLOCKED)
176155051Sglebius#define	TRUNK_RLOCK(trunk)	rw_rlock(&(trunk)->rw)
177155051Sglebius#define	TRUNK_RUNLOCK(trunk)	rw_runlock(&(trunk)->rw)
178155051Sglebius#define	TRUNK_LOCK_RASSERT(trunk) rw_assert(&(trunk)->rw, RA_RLOCKED)
179119780Ssam
180155051Sglebius#ifndef VLAN_ARRAY
181155051Sglebiusstatic	void vlan_inithash(struct ifvlantrunk *trunk);
182155051Sglebiusstatic	void vlan_freehash(struct ifvlantrunk *trunk);
183155051Sglebiusstatic	int vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv);
184155051Sglebiusstatic	int vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv);
185155051Sglebiusstatic	void vlan_growhash(struct ifvlantrunk *trunk, int howmuch);
186155051Sglebiusstatic __inline struct ifvlan * vlan_gethash(struct ifvlantrunk *trunk,
187230026Srwatson	uint16_t vid);
188155051Sglebius#endif
189155051Sglebiusstatic	void trunk_destroy(struct ifvlantrunk *trunk);
190155051Sglebius
191160019Syarstatic	void vlan_init(void *foo);
192106932Ssamstatic	void vlan_input(struct ifnet *ifp, struct mbuf *m);
19338482Swollmanstatic	int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
194228089Sjhbstatic	void vlan_qflush(struct ifnet *ifp);
195150846Syarstatic	int vlan_setflag(struct ifnet *ifp, int flag, int status,
196150846Syar    int (*func)(struct ifnet *, int));
197150846Syarstatic	int vlan_setflags(struct ifnet *ifp, int status);
19844763Swpaulstatic	int vlan_setmulti(struct ifnet *ifp);
199228089Sjhbstatic	int vlan_transmit(struct ifnet *ifp, struct mbuf *m);
200208212Sjhbstatic	void vlan_unconfig(struct ifnet *ifp);
201239440Sjhbstatic	void vlan_unconfig_locked(struct ifnet *ifp, int departing);
202155051Sglebiusstatic	int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag);
203201350Sbrooksstatic	void vlan_link_state(struct ifnet *ifp);
204155051Sglebiusstatic	void vlan_capabilities(struct ifvlan *ifv);
205155051Sglebiusstatic	void vlan_trunk_capabilities(struct ifnet *ifp);
20634649Swollman
207230026Srwatsonstatic	struct ifnet *vlan_clone_match_ethervid(struct if_clone *,
208130933Sbrooks    const char *, int *);
209130933Sbrooksstatic	int vlan_clone_match(struct if_clone *, const char *);
210160195Ssamstatic	int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t);
211130933Sbrooksstatic	int vlan_clone_destroy(struct if_clone *, struct ifnet *);
21283115Sbrooks
213159823Syarstatic	void vlan_ifdetach(void *arg, struct ifnet *ifp);
214202588Sthompsastatic  void vlan_iflladdr(void *arg, struct ifnet *ifp);
215159823Syar
216241610Sglebiusstatic struct if_clone *vlan_cloner;
217130933Sbrooks
218215726Szec#ifdef VIMAGE
219241610Sglebiusstatic VNET_DEFINE(struct if_clone *, vlan_cloner);
220215726Szec#define	V_vlan_cloner	VNET(vlan_cloner)
221215726Szec#endif
222215726Szec
223155051Sglebius#ifndef VLAN_ARRAY
224155051Sglebius#define HASH(n, m)	((((n) >> 8) ^ ((n) >> 4) ^ (n)) & (m))
225160019Syar
226155051Sglebiusstatic void
227155051Sglebiusvlan_inithash(struct ifvlantrunk *trunk)
228155051Sglebius{
229155051Sglebius	int i, n;
230155051Sglebius
231155051Sglebius	/*
232155051Sglebius	 * The trunk must not be locked here since we call malloc(M_WAITOK).
233155051Sglebius	 * It is OK in case this function is called before the trunk struct
234155051Sglebius	 * gets hooked up and becomes visible from other threads.
235155051Sglebius	 */
236155051Sglebius
237155051Sglebius	KASSERT(trunk->hwidth == 0 && trunk->hash == NULL,
238155051Sglebius	    ("%s: hash already initialized", __func__));
239155051Sglebius
240155051Sglebius	trunk->hwidth = VLAN_DEF_HWIDTH;
241155051Sglebius	n = 1 << trunk->hwidth;
242155051Sglebius	trunk->hmask = n - 1;
243155051Sglebius	trunk->hash = malloc(sizeof(struct ifvlanhead) * n, M_VLAN, M_WAITOK);
244155051Sglebius	for (i = 0; i < n; i++)
245155051Sglebius		LIST_INIT(&trunk->hash[i]);
246155051Sglebius}
247155051Sglebius
248155051Sglebiusstatic void
249155051Sglebiusvlan_freehash(struct ifvlantrunk *trunk)
250155051Sglebius{
251155051Sglebius#ifdef INVARIANTS
252155051Sglebius	int i;
253155051Sglebius
254155051Sglebius	KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__));
255155051Sglebius	for (i = 0; i < (1 << trunk->hwidth); i++)
256155051Sglebius		KASSERT(LIST_EMPTY(&trunk->hash[i]),
257155051Sglebius		    ("%s: hash table not empty", __func__));
258155051Sglebius#endif
259155051Sglebius	free(trunk->hash, M_VLAN);
260155051Sglebius	trunk->hash = NULL;
261155051Sglebius	trunk->hwidth = trunk->hmask = 0;
262155051Sglebius}
263155051Sglebius
264155051Sglebiusstatic int
265155051Sglebiusvlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
266155051Sglebius{
267155051Sglebius	int i, b;
268155051Sglebius	struct ifvlan *ifv2;
269155051Sglebius
270155051Sglebius	TRUNK_LOCK_ASSERT(trunk);
271155051Sglebius	KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__));
272155051Sglebius
273155051Sglebius	b = 1 << trunk->hwidth;
274230026Srwatson	i = HASH(ifv->ifv_vid, trunk->hmask);
275155051Sglebius	LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list)
276230026Srwatson		if (ifv->ifv_vid == ifv2->ifv_vid)
277155051Sglebius			return (EEXIST);
278155051Sglebius
279155051Sglebius	/*
280155051Sglebius	 * Grow the hash when the number of vlans exceeds half of the number of
281155051Sglebius	 * hash buckets squared. This will make the average linked-list length
282155051Sglebius	 * buckets/2.
283155051Sglebius	 */
284155051Sglebius	if (trunk->refcnt > (b * b) / 2) {
285155051Sglebius		vlan_growhash(trunk, 1);
286230026Srwatson		i = HASH(ifv->ifv_vid, trunk->hmask);
287155051Sglebius	}
288155051Sglebius	LIST_INSERT_HEAD(&trunk->hash[i], ifv, ifv_list);
289155051Sglebius	trunk->refcnt++;
290155051Sglebius
291155051Sglebius	return (0);
292155051Sglebius}
293155051Sglebius
294155051Sglebiusstatic int
295155051Sglebiusvlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
296155051Sglebius{
297155051Sglebius	int i, b;
298155051Sglebius	struct ifvlan *ifv2;
299155051Sglebius
300155051Sglebius	TRUNK_LOCK_ASSERT(trunk);
301155051Sglebius	KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__));
302155051Sglebius
303155051Sglebius	b = 1 << trunk->hwidth;
304230026Srwatson	i = HASH(ifv->ifv_vid, trunk->hmask);
305155051Sglebius	LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list)
306155051Sglebius		if (ifv2 == ifv) {
307155051Sglebius			trunk->refcnt--;
308155051Sglebius			LIST_REMOVE(ifv2, ifv_list);
309155051Sglebius			if (trunk->refcnt < (b * b) / 2)
310155051Sglebius				vlan_growhash(trunk, -1);
311155051Sglebius			return (0);
312155051Sglebius		}
313155051Sglebius
314155051Sglebius	panic("%s: vlan not found\n", __func__);
315155051Sglebius	return (ENOENT); /*NOTREACHED*/
316155051Sglebius}
317155051Sglebius
31844763Swpaul/*
319155051Sglebius * Grow the hash larger or smaller if memory permits.
320155051Sglebius */
321155051Sglebiusstatic void
322155051Sglebiusvlan_growhash(struct ifvlantrunk *trunk, int howmuch)
323155051Sglebius{
324155051Sglebius	struct ifvlan *ifv;
325155051Sglebius	struct ifvlanhead *hash2;
326155051Sglebius	int hwidth2, i, j, n, n2;
327155051Sglebius
328155051Sglebius	TRUNK_LOCK_ASSERT(trunk);
329155051Sglebius	KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__));
330155051Sglebius
331155051Sglebius	if (howmuch == 0) {
332155051Sglebius		/* Harmless yet obvious coding error */
333155051Sglebius		printf("%s: howmuch is 0\n", __func__);
334155051Sglebius		return;
335155051Sglebius	}
336155051Sglebius
337155051Sglebius	hwidth2 = trunk->hwidth + howmuch;
338155051Sglebius	n = 1 << trunk->hwidth;
339155051Sglebius	n2 = 1 << hwidth2;
340155051Sglebius	/* Do not shrink the table below the default */
341155051Sglebius	if (hwidth2 < VLAN_DEF_HWIDTH)
342155051Sglebius		return;
343155051Sglebius
344155051Sglebius	/* M_NOWAIT because we're called with trunk mutex held */
345155051Sglebius	hash2 = malloc(sizeof(struct ifvlanhead) * n2, M_VLAN, M_NOWAIT);
346155051Sglebius	if (hash2 == NULL) {
347155051Sglebius		printf("%s: out of memory -- hash size not changed\n",
348155051Sglebius		    __func__);
349155051Sglebius		return;		/* We can live with the old hash table */
350155051Sglebius	}
351155051Sglebius	for (j = 0; j < n2; j++)
352155051Sglebius		LIST_INIT(&hash2[j]);
353155051Sglebius	for (i = 0; i < n; i++)
354167483Syar		while ((ifv = LIST_FIRST(&trunk->hash[i])) != NULL) {
355155051Sglebius			LIST_REMOVE(ifv, ifv_list);
356230026Srwatson			j = HASH(ifv->ifv_vid, n2 - 1);
357155051Sglebius			LIST_INSERT_HEAD(&hash2[j], ifv, ifv_list);
358155051Sglebius		}
359155051Sglebius	free(trunk->hash, M_VLAN);
360155051Sglebius	trunk->hash = hash2;
361155051Sglebius	trunk->hwidth = hwidth2;
362155051Sglebius	trunk->hmask = n2 - 1;
363167601Syar
364167601Syar	if (bootverbose)
365167601Syar		if_printf(trunk->parent,
366167601Syar		    "VLAN hash table resized from %d to %d buckets\n", n, n2);
367155051Sglebius}
368155051Sglebius
369155051Sglebiusstatic __inline struct ifvlan *
370230026Srwatsonvlan_gethash(struct ifvlantrunk *trunk, uint16_t vid)
371155051Sglebius{
372155051Sglebius	struct ifvlan *ifv;
373155051Sglebius
374155051Sglebius	TRUNK_LOCK_RASSERT(trunk);
375155051Sglebius
376230026Srwatson	LIST_FOREACH(ifv, &trunk->hash[HASH(vid, trunk->hmask)], ifv_list)
377230026Srwatson		if (ifv->ifv_vid == vid)
378155051Sglebius			return (ifv);
379155051Sglebius	return (NULL);
380155051Sglebius}
381155051Sglebius
382155051Sglebius#if 0
383155051Sglebius/* Debugging code to view the hashtables. */
384155051Sglebiusstatic void
385155051Sglebiusvlan_dumphash(struct ifvlantrunk *trunk)
386155051Sglebius{
387155051Sglebius	int i;
388155051Sglebius	struct ifvlan *ifv;
389155051Sglebius
390155051Sglebius	for (i = 0; i < (1 << trunk->hwidth); i++) {
391155051Sglebius		printf("%d: ", i);
392155051Sglebius		LIST_FOREACH(ifv, &trunk->hash[i], ifv_list)
393155051Sglebius			printf("%s ", ifv->ifv_ifp->if_xname);
394155051Sglebius		printf("\n");
395155051Sglebius	}
396155051Sglebius}
397155051Sglebius#endif /* 0 */
398219819Sjeff#else
399219819Sjeff
400219819Sjeffstatic __inline struct ifvlan *
401230026Srwatsonvlan_gethash(struct ifvlantrunk *trunk, uint16_t vid)
402219819Sjeff{
403219819Sjeff
404230026Srwatson	return trunk->vlans[vid];
405219819Sjeff}
406219819Sjeff
407219819Sjeffstatic __inline int
408219819Sjeffvlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
409219819Sjeff{
410219819Sjeff
411230026Srwatson	if (trunk->vlans[ifv->ifv_vid] != NULL)
412219819Sjeff		return EEXIST;
413230026Srwatson	trunk->vlans[ifv->ifv_vid] = ifv;
414219819Sjeff	trunk->refcnt++;
415219819Sjeff
416219819Sjeff	return (0);
417219819Sjeff}
418219819Sjeff
419219819Sjeffstatic __inline int
420219819Sjeffvlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
421219819Sjeff{
422219819Sjeff
423230026Srwatson	trunk->vlans[ifv->ifv_vid] = NULL;
424219819Sjeff	trunk->refcnt--;
425219819Sjeff
426219819Sjeff	return (0);
427219819Sjeff}
428219819Sjeff
429219819Sjeffstatic __inline void
430219819Sjeffvlan_freehash(struct ifvlantrunk *trunk)
431219819Sjeff{
432219819Sjeff}
433219819Sjeff
434219819Sjeffstatic __inline void
435219819Sjeffvlan_inithash(struct ifvlantrunk *trunk)
436219819Sjeff{
437219819Sjeff}
438219819Sjeff
439155051Sglebius#endif /* !VLAN_ARRAY */
440155051Sglebius
441155051Sglebiusstatic void
442155051Sglebiustrunk_destroy(struct ifvlantrunk *trunk)
443155051Sglebius{
444155051Sglebius	VLAN_LOCK_ASSERT();
445155051Sglebius
446155051Sglebius	TRUNK_LOCK(trunk);
447155051Sglebius	vlan_freehash(trunk);
448155986Syar	trunk->parent->if_vlantrunk = NULL;
449155986Syar	TRUNK_UNLOCK(trunk);
450155051Sglebius	TRUNK_LOCK_DESTROY(trunk);
451155051Sglebius	free(trunk, M_VLAN);
452155051Sglebius}
453155051Sglebius
454155051Sglebius/*
45544763Swpaul * Program our multicast filter. What we're actually doing is
45644763Swpaul * programming the multicast filter of the parent. This has the
45744763Swpaul * side effect of causing the parent interface to receive multicast
45844763Swpaul * traffic that it doesn't really want, which ends up being discarded
45944763Swpaul * later by the upper protocol layers. Unfortunately, there's no way
46044763Swpaul * to avoid this: there really is only one physical interface.
46144763Swpaul */
46271862Speterstatic int
46371862Spetervlan_setmulti(struct ifnet *ifp)
46444763Swpaul{
46544763Swpaul	struct ifnet		*ifp_p;
466270136Smav	struct ifmultiaddr	*ifma;
46744763Swpaul	struct ifvlan		*sc;
468167483Syar	struct vlan_mc_entry	*mc;
46944763Swpaul	int			error;
47044763Swpaul
47144763Swpaul	/* Find the parent. */
47244763Swpaul	sc = ifp->if_softc;
473270136Smav	TRUNK_LOCK_ASSERT(TRUNK(sc));
474155051Sglebius	ifp_p = PARENT(sc);
47544763Swpaul
476183550Szec	CURVNET_SET_QUIET(ifp_p->if_vnet);
477183550Szec
47844763Swpaul	/* First, remove any existing filter entries. */
479167483Syar	while ((mc = SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) {
48044763Swpaul		SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
481270136Smav		(void)if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr);
48283115Sbrooks		free(mc, M_VLAN);
48344763Swpaul	}
48444763Swpaul
48544763Swpaul	/* Now program new ones. */
486270136Smav	IF_ADDR_WLOCK(ifp);
48772084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
48844763Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
48944763Swpaul			continue;
490131586Sbms		mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT);
491270136Smav		if (mc == NULL) {
492270136Smav			IF_ADDR_WUNLOCK(ifp);
493131586Sbms			return (ENOMEM);
494270136Smav		}
495219819Sjeff		bcopy(ifma->ifma_addr, &mc->mc_addr, ifma->ifma_addr->sa_len);
496219819Sjeff		mc->mc_addr.sdl_index = ifp_p->if_index;
49744763Swpaul		SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
498270136Smav	}
499270136Smav	IF_ADDR_WUNLOCK(ifp);
500270136Smav	SLIST_FOREACH (mc, &sc->vlan_mc_listhead, mc_entries) {
501219819Sjeff		error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr,
502270136Smav		    NULL);
50344763Swpaul		if (error)
504131580Sbms			return (error);
50544763Swpaul	}
50644763Swpaul
507183550Szec	CURVNET_RESTORE();
508131580Sbms	return (0);
50944763Swpaul}
51044763Swpaul
511106932Ssam/*
512202588Sthompsa * A handler for parent interface link layer address changes.
513202588Sthompsa * If the parent interface link layer address is changed we
514202588Sthompsa * should also change it on all children vlans.
515202588Sthompsa */
516202588Sthompsastatic void
517202588Sthompsavlan_iflladdr(void *arg __unused, struct ifnet *ifp)
518202588Sthompsa{
519202588Sthompsa	struct ifvlan *ifv;
520202611Sthompsa#ifndef VLAN_ARRAY
521202611Sthompsa	struct ifvlan *next;
522202611Sthompsa#endif
523202588Sthompsa	int i;
524202588Sthompsa
525202588Sthompsa	/*
526202588Sthompsa	 * Check if it's a trunk interface first of all
527202588Sthompsa	 * to avoid needless locking.
528202588Sthompsa	 */
529202588Sthompsa	if (ifp->if_vlantrunk == NULL)
530202588Sthompsa		return;
531202588Sthompsa
532202588Sthompsa	VLAN_LOCK();
533202588Sthompsa	/*
534202588Sthompsa	 * OK, it's a trunk.  Loop over and change all vlan's lladdrs on it.
535202588Sthompsa	 */
536202588Sthompsa#ifdef VLAN_ARRAY
537202588Sthompsa	for (i = 0; i < VLAN_ARRAY_SIZE; i++)
538202611Sthompsa		if ((ifv = ifp->if_vlantrunk->vlans[i])) {
539202588Sthompsa#else /* VLAN_ARRAY */
540202588Sthompsa	for (i = 0; i < (1 << ifp->if_vlantrunk->hwidth); i++)
541202611Sthompsa		LIST_FOREACH_SAFE(ifv, &ifp->if_vlantrunk->hash[i], ifv_list, next) {
542202611Sthompsa#endif /* VLAN_ARRAY */
543202611Sthompsa			VLAN_UNLOCK();
544219819Sjeff			if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp),
545219819Sjeff			    ifp->if_addrlen);
546202611Sthompsa			VLAN_LOCK();
547202611Sthompsa		}
548202588Sthompsa	VLAN_UNLOCK();
549202588Sthompsa
550202588Sthompsa}
551202588Sthompsa
552202588Sthompsa/*
553159823Syar * A handler for network interface departure events.
554159823Syar * Track departure of trunks here so that we don't access invalid
555159823Syar * pointers or whatever if a trunk is ripped from under us, e.g.,
556201196Sjhb * by ejecting its hot-plug card.  However, if an ifnet is simply
557201196Sjhb * being renamed, then there's no need to tear down the state.
558159823Syar */
559159823Syarstatic void
560159823Syarvlan_ifdetach(void *arg __unused, struct ifnet *ifp)
561159823Syar{
562159823Syar	struct ifvlan *ifv;
563159823Syar	int i;
564159823Syar
565159823Syar	/*
566159823Syar	 * Check if it's a trunk interface first of all
567159823Syar	 * to avoid needless locking.
568159823Syar	 */
569159823Syar	if (ifp->if_vlantrunk == NULL)
570159823Syar		return;
571159823Syar
572201196Sjhb	/* If the ifnet is just being renamed, don't do anything. */
573201196Sjhb	if (ifp->if_flags & IFF_RENAMING)
574201196Sjhb		return;
575201196Sjhb
576159823Syar	VLAN_LOCK();
577159823Syar	/*
578159823Syar	 * OK, it's a trunk.  Loop over and detach all vlan's on it.
579159823Syar	 * Check trunk pointer after each vlan_unconfig() as it will
580159823Syar	 * free it and set to NULL after the last vlan was detached.
581159823Syar	 */
582159823Syar#ifdef VLAN_ARRAY
583159823Syar	for (i = 0; i < VLAN_ARRAY_SIZE; i++)
584159823Syar		if ((ifv = ifp->if_vlantrunk->vlans[i])) {
585239440Sjhb			vlan_unconfig_locked(ifv->ifv_ifp, 1);
586159823Syar			if (ifp->if_vlantrunk == NULL)
587159823Syar				break;
588159823Syar		}
589159823Syar#else /* VLAN_ARRAY */
590159823Syarrestart:
591159823Syar	for (i = 0; i < (1 << ifp->if_vlantrunk->hwidth); i++)
592159823Syar		if ((ifv = LIST_FIRST(&ifp->if_vlantrunk->hash[i]))) {
593239440Sjhb			vlan_unconfig_locked(ifv->ifv_ifp, 1);
594159823Syar			if (ifp->if_vlantrunk)
595159823Syar				goto restart;	/* trunk->hwidth can change */
596159823Syar			else
597159823Syar				break;
598159823Syar		}
599159823Syar#endif /* VLAN_ARRAY */
600159823Syar	/* Trunk should have been destroyed in vlan_unconfig(). */
601159823Syar	KASSERT(ifp->if_vlantrunk == NULL, ("%s: purge failed", __func__));
602159823Syar	VLAN_UNLOCK();
603159823Syar}
604159823Syar
605159823Syar/*
606219819Sjeff * Return the trunk device for a virtual interface.
607219819Sjeff */
608219819Sjeffstatic struct ifnet  *
609219819Sjeffvlan_trunkdev(struct ifnet *ifp)
610219819Sjeff{
611219819Sjeff	struct ifvlan *ifv;
612219819Sjeff
613219819Sjeff	if (ifp->if_type != IFT_L2VLAN)
614219819Sjeff		return (NULL);
615219819Sjeff	ifv = ifp->if_softc;
616219819Sjeff	ifp = NULL;
617219819Sjeff	VLAN_LOCK();
618219819Sjeff	if (ifv->ifv_trunk)
619219819Sjeff		ifp = PARENT(ifv);
620219819Sjeff	VLAN_UNLOCK();
621219819Sjeff	return (ifp);
622219819Sjeff}
623219819Sjeff
624219819Sjeff/*
625230026Srwatson * Return the 12-bit VLAN VID for this interface, for use by external
626230026Srwatson * components such as Infiniband.
627230026Srwatson *
628230026Srwatson * XXXRW: Note that the function name here is historical; it should be named
629230026Srwatson * vlan_vid().
630219819Sjeff */
631219819Sjeffstatic int
632230026Srwatsonvlan_tag(struct ifnet *ifp, uint16_t *vidp)
633219819Sjeff{
634219819Sjeff	struct ifvlan *ifv;
635219819Sjeff
636219819Sjeff	if (ifp->if_type != IFT_L2VLAN)
637219819Sjeff		return (EINVAL);
638219819Sjeff	ifv = ifp->if_softc;
639230026Srwatson	*vidp = ifv->ifv_vid;
640219819Sjeff	return (0);
641219819Sjeff}
642219819Sjeff
643219819Sjeff/*
644219819Sjeff * Return a driver specific cookie for this interface.  Synchronization
645219819Sjeff * with setcookie must be provided by the driver.
646219819Sjeff */
647219819Sjeffstatic void *
648219819Sjeffvlan_cookie(struct ifnet *ifp)
649219819Sjeff{
650219819Sjeff	struct ifvlan *ifv;
651219819Sjeff
652219819Sjeff	if (ifp->if_type != IFT_L2VLAN)
653219819Sjeff		return (NULL);
654219819Sjeff	ifv = ifp->if_softc;
655219819Sjeff	return (ifv->ifv_cookie);
656219819Sjeff}
657219819Sjeff
658219819Sjeff/*
659219819Sjeff * Store a cookie in our softc that drivers can use to store driver
660219819Sjeff * private per-instance data in.
661219819Sjeff */
662219819Sjeffstatic int
663219819Sjeffvlan_setcookie(struct ifnet *ifp, void *cookie)
664219819Sjeff{
665219819Sjeff	struct ifvlan *ifv;
666219819Sjeff
667219819Sjeff	if (ifp->if_type != IFT_L2VLAN)
668219819Sjeff		return (EINVAL);
669219819Sjeff	ifv = ifp->if_softc;
670219819Sjeff	ifv->ifv_cookie = cookie;
671219819Sjeff	return (0);
672219819Sjeff}
673219819Sjeff
674219819Sjeff/*
675230026Srwatson * Return the vlan device present at the specific VID.
676219819Sjeff */
677219819Sjeffstatic struct ifnet *
678230026Srwatsonvlan_devat(struct ifnet *ifp, uint16_t vid)
679219819Sjeff{
680219819Sjeff	struct ifvlantrunk *trunk;
681219819Sjeff	struct ifvlan *ifv;
682219819Sjeff
683219819Sjeff	trunk = ifp->if_vlantrunk;
684219819Sjeff	if (trunk == NULL)
685219819Sjeff		return (NULL);
686219819Sjeff	ifp = NULL;
687219819Sjeff	TRUNK_RLOCK(trunk);
688230026Srwatson	ifv = vlan_gethash(trunk, vid);
689219819Sjeff	if (ifv)
690219819Sjeff		ifp = ifv->ifv_ifp;
691219819Sjeff	TRUNK_RUNLOCK(trunk);
692219819Sjeff	return (ifp);
693219819Sjeff}
694219819Sjeff
695219819Sjeff/*
696106932Ssam * VLAN support can be loaded as a module.  The only place in the
697106932Ssam * system that's intimately aware of this is ether_input.  We hook
698106932Ssam * into this code through vlan_input_p which is defined there and
699106932Ssam * set here.  Noone else in the system should be aware of this so
700106932Ssam * we use an explicit reference here.
701106932Ssam */
702106932Ssamextern	void (*vlan_input_p)(struct ifnet *, struct mbuf *);
703106932Ssam
704145323Sglebius/* For if_link_state_change() eyes only... */
705201350Sbrooksextern	void (*vlan_link_state_p)(struct ifnet *);
706128871Sandre
70771862Speterstatic int
708131580Sbmsvlan_modevent(module_t mod, int type, void *data)
709131580Sbms{
71083115Sbrooks
711131580Sbms	switch (type) {
712131580Sbms	case MOD_LOAD:
713159823Syar		ifdetach_tag = EVENTHANDLER_REGISTER(ifnet_departure_event,
714159823Syar		    vlan_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
715159823Syar		if (ifdetach_tag == NULL)
716159823Syar			return (ENOMEM);
717202588Sthompsa		iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event,
718202588Sthompsa		    vlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY);
719202588Sthompsa		if (iflladdr_tag == NULL)
720202588Sthompsa			return (ENOMEM);
721119780Ssam		VLAN_LOCK_INIT();
72283115Sbrooks		vlan_input_p = vlan_input;
723128871Sandre		vlan_link_state_p = vlan_link_state;
724155051Sglebius		vlan_trunk_cap_p = vlan_trunk_capabilities;
725219819Sjeff		vlan_trunkdev_p = vlan_trunkdev;
726219819Sjeff		vlan_cookie_p = vlan_cookie;
727219819Sjeff		vlan_setcookie_p = vlan_setcookie;
728219819Sjeff		vlan_tag_p = vlan_tag;
729219819Sjeff		vlan_devat_p = vlan_devat;
730215726Szec#ifndef VIMAGE
731241610Sglebius		vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match,
732241610Sglebius		    vlan_clone_create, vlan_clone_destroy);
733215726Szec#endif
734167484Syar		if (bootverbose)
735167484Syar			printf("vlan: initialized, using "
736167484Syar#ifdef VLAN_ARRAY
737167484Syar			       "full-size arrays"
738167484Syar#else
739167484Syar			       "hash tables with chaining"
740167484Syar#endif
741167484Syar
742167484Syar			       "\n");
743131580Sbms		break;
744131580Sbms	case MOD_UNLOAD:
745215726Szec#ifndef VIMAGE
746241610Sglebius		if_clone_detach(vlan_cloner);
747215726Szec#endif
748159823Syar		EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag);
749202588Sthompsa		EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag);
75083115Sbrooks		vlan_input_p = NULL;
751128871Sandre		vlan_link_state_p = NULL;
752155051Sglebius		vlan_trunk_cap_p = NULL;
753219819Sjeff		vlan_trunkdev_p = NULL;
754219819Sjeff		vlan_tag_p = NULL;
755237263Snp		vlan_cookie_p = NULL;
756237263Snp		vlan_setcookie_p = NULL;
757219819Sjeff		vlan_devat_p = NULL;
758119780Ssam		VLAN_LOCK_DESTROY();
759167484Syar		if (bootverbose)
760167484Syar			printf("vlan: unloaded\n");
76183115Sbrooks		break;
762132199Sphk	default:
763132199Sphk		return (EOPNOTSUPP);
764131580Sbms	}
765131580Sbms	return (0);
766131580Sbms}
76771862Speter
768131580Sbmsstatic moduledata_t vlan_mod = {
769131580Sbms	"if_vlan",
770131580Sbms	vlan_modevent,
771241394Skevlo	0
772131580Sbms};
77371862Speter
77471862SpeterDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
775155509SemasteMODULE_VERSION(if_vlan, 3);
77671862Speter
777215726Szec#ifdef VIMAGE
778215726Szecstatic void
779215726Szecvnet_vlan_init(const void *unused __unused)
780215726Szec{
781215726Szec
782241610Sglebius	vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match,
783241610Sglebius		    vlan_clone_create, vlan_clone_destroy);
784215726Szec	V_vlan_cloner = vlan_cloner;
785215726Szec}
786215726SzecVNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
787215726Szec    vnet_vlan_init, NULL);
788215726Szec
789215726Szecstatic void
790215726Szecvnet_vlan_uninit(const void *unused __unused)
791215726Szec{
792215726Szec
793241610Sglebius	if_clone_detach(V_vlan_cloner);
794215726Szec}
795215726SzecVNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST,
796215726Szec    vnet_vlan_uninit, NULL);
797215726Szec#endif
798215726Szec
799130933Sbrooksstatic struct ifnet *
800230026Srwatsonvlan_clone_match_ethervid(struct if_clone *ifc, const char *name, int *vidp)
801130933Sbrooks{
802130933Sbrooks	const char *cp;
803130933Sbrooks	struct ifnet *ifp;
804230026Srwatson	int vid;
805130933Sbrooks
806130933Sbrooks	/* Check for <etherif>.<vlan> style interface names. */
807196481Srwatson	IFNET_RLOCK_NOSLEEP();
808181803Sbz	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
809219819Sjeff		/*
810219819Sjeff		 * We can handle non-ethernet hardware types as long as
811219819Sjeff		 * they handle the tagging and headers themselves.
812219819Sjeff		 */
813219819Sjeff		if (ifp->if_type != IFT_ETHER &&
814219819Sjeff		    (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
815130933Sbrooks			continue;
816130933Sbrooks		if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0)
817130933Sbrooks			continue;
818130933Sbrooks		cp = name + strlen(ifp->if_xname);
819201351Sjhb		if (*cp++ != '.')
820130933Sbrooks			continue;
821201351Sjhb		if (*cp == '\0')
822201351Sjhb			continue;
823230026Srwatson		vid = 0;
824201351Sjhb		for(; *cp >= '0' && *cp <= '9'; cp++)
825230026Srwatson			vid = (vid * 10) + (*cp - '0');
826201351Sjhb		if (*cp != '\0')
827201351Sjhb			continue;
828230026Srwatson		if (vidp != NULL)
829230026Srwatson			*vidp = vid;
830130933Sbrooks		break;
831130933Sbrooks	}
832196481Srwatson	IFNET_RUNLOCK_NOSLEEP();
833130933Sbrooks
834131580Sbms	return (ifp);
835130933Sbrooks}
836130933Sbrooks
83783115Sbrooksstatic int
838130933Sbrooksvlan_clone_match(struct if_clone *ifc, const char *name)
83983115Sbrooks{
840130933Sbrooks	const char *cp;
841130933Sbrooks
842230026Srwatson	if (vlan_clone_match_ethervid(ifc, name, NULL) != NULL)
843130933Sbrooks		return (1);
844130933Sbrooks
845241610Sglebius	if (strncmp(vlanname, name, strlen(vlanname)) != 0)
846130933Sbrooks		return (0);
847130933Sbrooks	for (cp = name + 4; *cp != '\0'; cp++) {
848130933Sbrooks		if (*cp < '0' || *cp > '9')
849130933Sbrooks			return (0);
850130933Sbrooks	}
851130933Sbrooks
852130933Sbrooks	return (1);
853130933Sbrooks}
854130933Sbrooks
855130933Sbrooksstatic int
856160195Ssamvlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
857130933Sbrooks{
858130933Sbrooks	char *dp;
859130933Sbrooks	int wildcard;
860130933Sbrooks	int unit;
861130933Sbrooks	int error;
862230026Srwatson	int vid;
863130933Sbrooks	int ethertag;
86483115Sbrooks	struct ifvlan *ifv;
86583115Sbrooks	struct ifnet *ifp;
866130933Sbrooks	struct ifnet *p;
867210937Sjhb	struct ifaddr *ifa;
868210937Sjhb	struct sockaddr_dl *sdl;
869160195Ssam	struct vlanreq vlr;
870167708Syar	static const u_char eaddr[ETHER_ADDR_LEN];	/* 00:00:00:00:00:00 */
87183115Sbrooks
872160195Ssam	/*
873160195Ssam	 * There are 3 (ugh) ways to specify the cloned device:
874160195Ssam	 * o pass a parameter block with the clone request.
875160195Ssam	 * o specify parameters in the text of the clone device name
876160195Ssam	 * o specify no parameters and get an unattached device that
877160195Ssam	 *   must be configured separately.
878160195Ssam	 * The first technique is preferred; the latter two are
879160195Ssam	 * supported for backwards compatibilty.
880230026Srwatson	 *
881230026Srwatson	 * XXXRW: Note historic use of the word "tag" here.  New ioctls may be
882230026Srwatson	 * called for.
883160195Ssam	 */
884160195Ssam	if (params) {
885160195Ssam		error = copyin(params, &vlr, sizeof(vlr));
886160195Ssam		if (error)
887160195Ssam			return error;
888160195Ssam		p = ifunit(vlr.vlr_parent);
889160195Ssam		if (p == NULL)
890160195Ssam			return ENXIO;
891160195Ssam		/*
892230026Srwatson		 * Don't let the caller set up a VLAN VID with
893160195Ssam		 * anything except VLID bits.
894160195Ssam		 */
895160195Ssam		if (vlr.vlr_tag & ~EVL_VLID_MASK)
896160195Ssam			return (EINVAL);
897160195Ssam		error = ifc_name2unit(name, &unit);
898160195Ssam		if (error != 0)
899160195Ssam			return (error);
900160195Ssam
901130933Sbrooks		ethertag = 1;
902230026Srwatson		vid = vlr.vlr_tag;
903160195Ssam		wildcard = (unit < 0);
904230026Srwatson	} else if ((p = vlan_clone_match_ethervid(ifc, name, &vid)) != NULL) {
905160195Ssam		ethertag = 1;
906130933Sbrooks		unit = -1;
907130933Sbrooks		wildcard = 0;
908130933Sbrooks
909130933Sbrooks		/*
910230026Srwatson		 * Don't let the caller set up a VLAN VID with
911130933Sbrooks		 * anything except VLID bits.
912130933Sbrooks		 */
913230026Srwatson		if (vid & ~EVL_VLID_MASK)
914130933Sbrooks			return (EINVAL);
915130933Sbrooks	} else {
916130933Sbrooks		ethertag = 0;
917130933Sbrooks
918130933Sbrooks		error = ifc_name2unit(name, &unit);
919130933Sbrooks		if (error != 0)
920130933Sbrooks			return (error);
921130933Sbrooks
922130933Sbrooks		wildcard = (unit < 0);
923130933Sbrooks	}
924130933Sbrooks
925130933Sbrooks	error = ifc_alloc_unit(ifc, &unit);
926130933Sbrooks	if (error != 0)
927130933Sbrooks		return (error);
928130933Sbrooks
929130933Sbrooks	/* In the wildcard case, we need to update the name. */
930130933Sbrooks	if (wildcard) {
931130933Sbrooks		for (dp = name; *dp != '\0'; dp++);
932130933Sbrooks		if (snprintf(dp, len - (dp-name), "%d", unit) >
933130933Sbrooks		    len - (dp-name) - 1) {
934130933Sbrooks			panic("%s: interface name too long", __func__);
935130933Sbrooks		}
936130933Sbrooks	}
937130933Sbrooks
938111119Simp	ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
939147256Sbrooks	ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER);
940147256Sbrooks	if (ifp == NULL) {
941147256Sbrooks		ifc_free_unit(ifc, unit);
942147256Sbrooks		free(ifv, M_VLAN);
943147256Sbrooks		return (ENOSPC);
944147256Sbrooks	}
94583115Sbrooks	SLIST_INIT(&ifv->vlan_mc_listhead);
94683115Sbrooks
94783115Sbrooks	ifp->if_softc = ifv;
948130933Sbrooks	/*
949140745Syar	 * Set the name manually rather than using if_initname because
950130933Sbrooks	 * we don't conform to the default naming convention for interfaces.
951130933Sbrooks	 */
952130933Sbrooks	strlcpy(ifp->if_xname, name, IFNAMSIZ);
953241610Sglebius	ifp->if_dname = vlanname;
954130933Sbrooks	ifp->if_dunit = unit;
95583115Sbrooks	/* NB: flags are not set here */
95683115Sbrooks	ifp->if_linkmib = &ifv->ifv_mib;
957131580Sbms	ifp->if_linkmiblen = sizeof(ifv->ifv_mib);
95883115Sbrooks	/* NB: mtu is not set here */
95983115Sbrooks
960160019Syar	ifp->if_init = vlan_init;
961228089Sjhb	ifp->if_transmit = vlan_transmit;
962228089Sjhb	ifp->if_qflush = vlan_qflush;
96383115Sbrooks	ifp->if_ioctl = vlan_ioctl;
964155114Syar	ifp->if_flags = VLAN_IFFLAGS;
965147256Sbrooks	ether_ifattach(ifp, eaddr);
96683115Sbrooks	/* Now undo some of the damage... */
96785005Sfenner	ifp->if_baudrate = 0;
968106932Ssam	ifp->if_type = IFT_L2VLAN;
969106932Ssam	ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
970210937Sjhb	ifa = ifp->if_addr;
971210937Sjhb	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
972210937Sjhb	sdl->sdl_type = IFT_L2VLAN;
97383115Sbrooks
974130933Sbrooks	if (ethertag) {
975230026Srwatson		error = vlan_config(ifv, p, vid);
976130933Sbrooks		if (error != 0) {
977130933Sbrooks			/*
978239440Sjhb			 * Since we've partially failed, we need to back
979130933Sbrooks			 * out all the way, otherwise userland could get
980130933Sbrooks			 * confused.  Thus, we destroy the interface.
981130933Sbrooks			 */
982160020Syar			ether_ifdetach(ifp);
983130933Sbrooks			vlan_unconfig(ifp);
984227459Sbrooks			if_free(ifp);
985188575Smaxim			ifc_free_unit(ifc, unit);
986130933Sbrooks			free(ifv, M_VLAN);
987130933Sbrooks
988130933Sbrooks			return (error);
989130933Sbrooks		}
990130933Sbrooks
991150846Syar		/* Update flags on the parent, if necessary. */
992150846Syar		vlan_setflags(ifp, 1);
993130933Sbrooks	}
994130933Sbrooks
99583115Sbrooks	return (0);
99683115Sbrooks}
99783115Sbrooks
998130933Sbrooksstatic int
999130933Sbrooksvlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
100083115Sbrooks{
100183115Sbrooks	struct ifvlan *ifv = ifp->if_softc;
1002160019Syar	int unit = ifp->if_dunit;
100383115Sbrooks
1004160020Syar	ether_ifdetach(ifp);	/* first, remove it from system-wide lists */
1005160020Syar	vlan_unconfig(ifp);	/* now it can be unconfigured and freed */
1006227459Sbrooks	if_free(ifp);
100783115Sbrooks	free(ifv, M_VLAN);
1008132557Sbrooks	ifc_free_unit(ifc, unit);
1009132557Sbrooks
1010130933Sbrooks	return (0);
101183115Sbrooks}
101283115Sbrooks
1013131580Sbms/*
1014131580Sbms * The ifp->if_init entry point for vlan(4) is a no-op.
1015131580Sbms */
101683115Sbrooksstatic void
1017160019Syarvlan_init(void *foo __unused)
101834649Swollman{
101934649Swollman}
102034649Swollman
1021152139Sglebius/*
1022228089Sjhb * The if_transmit method for vlan(4) interface.
1023152139Sglebius */
1024228089Sjhbstatic int
1025228089Sjhbvlan_transmit(struct ifnet *ifp, struct mbuf *m)
102634649Swollman{
102734649Swollman	struct ifvlan *ifv;
102834649Swollman	struct ifnet *p;
1029228967Syongari	int error, len, mcast;
103034649Swollman
103134649Swollman	ifv = ifp->if_softc;
1032155051Sglebius	p = PARENT(ifv);
1033228967Syongari	len = m->m_pkthdr.len;
1034228967Syongari	mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0;
103534649Swollman
1036228089Sjhb	BPF_MTAP(ifp, m);
103734649Swollman
1038228089Sjhb	/*
1039228089Sjhb	 * Do not run parent's if_transmit() if the parent is not up,
1040228089Sjhb	 * or parent's driver will cause a system crash.
1041228089Sjhb	 */
1042228089Sjhb	if (!UP_AND_RUNNING(p)) {
1043228089Sjhb		m_freem(m);
1044228967Syongari		ifp->if_oerrors++;
1045251799Shrs		return (ENETDOWN);
1046228089Sjhb	}
104774943Syar
1048228089Sjhb	/*
1049228089Sjhb	 * Pad the frame to the minimum size allowed if told to.
1050228089Sjhb	 * This option is in accord with IEEE Std 802.1Q, 2003 Ed.,
1051228089Sjhb	 * paragraph C.4.4.3.b.  It can help to work around buggy
1052228089Sjhb	 * bridges that violate paragraph C.4.4.3.a from the same
1053228089Sjhb	 * document, i.e., fail to pad short frames after untagging.
1054228089Sjhb	 * E.g., a tagged frame 66 bytes long (incl. FCS) is OK, but
1055228089Sjhb	 * untagging it will produce a 62-byte frame, which is a runt
1056228089Sjhb	 * and requires padding.  There are VLAN-enabled network
1057228089Sjhb	 * devices that just discard such runts instead or mishandle
1058228089Sjhb	 * them somehow.
1059228089Sjhb	 */
1060228089Sjhb	if (soft_pad && p->if_type == IFT_ETHER) {
1061228089Sjhb		static char pad[8];	/* just zeros */
1062228089Sjhb		int n;
1063161210Syar
1064228089Sjhb		for (n = ETHERMIN + ETHER_HDR_LEN - m->m_pkthdr.len;
1065228089Sjhb		     n > 0; n -= sizeof(pad))
1066228089Sjhb			if (!m_append(m, min(n, sizeof(pad)), pad))
1067228089Sjhb				break;
1068161210Syar
1069228089Sjhb		if (n > 0) {
1070228089Sjhb			if_printf(ifp, "cannot pad short frame\n");
1071228089Sjhb			ifp->if_oerrors++;
1072228089Sjhb			m_freem(m);
1073228089Sjhb			return (0);
1074161210Syar		}
1075228089Sjhb	}
1076161210Syar
1077228089Sjhb	/*
1078228089Sjhb	 * If underlying interface can do VLAN tag insertion itself,
1079228089Sjhb	 * just pass the packet along. However, we need some way to
1080228089Sjhb	 * tell the interface where the packet came from so that it
1081228089Sjhb	 * knows how to find the VLAN tag to use, so we attach a
1082228089Sjhb	 * packet tag that holds it.
1083228089Sjhb	 */
1084228089Sjhb	if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
1085230026Srwatson		m->m_pkthdr.ether_vtag = ifv->ifv_vid;
1086228089Sjhb		m->m_flags |= M_VLANTAG;
1087228089Sjhb	} else {
1088230026Srwatson		m = ether_vlanencap(m, ifv->ifv_vid);
1089228089Sjhb		if (m == NULL) {
1090228089Sjhb			if_printf(ifp, "unable to prepend VLAN header\n");
1091228089Sjhb			ifp->if_oerrors++;
1092228089Sjhb			return (0);
109344763Swpaul		}
1094228089Sjhb	}
109544763Swpaul
1096228089Sjhb	/*
1097228089Sjhb	 * Send it, precisely as ether_output() would have.
1098228089Sjhb	 */
1099228089Sjhb	error = (p->if_transmit)(p, m);
1100228967Syongari	if (!error) {
1101228089Sjhb		ifp->if_opackets++;
1102228967Syongari		ifp->if_omcasts += mcast;
1103228967Syongari		ifp->if_obytes += len;
1104228967Syongari	} else
1105228089Sjhb		ifp->if_oerrors++;
1106228089Sjhb	return (error);
110734649Swollman}
110834649Swollman
1109228089Sjhb/*
1110228089Sjhb * The ifp->if_qflush entry point for vlan(4) is a no-op.
1111228089Sjhb */
1112106932Ssamstatic void
1113228089Sjhbvlan_qflush(struct ifnet *ifp __unused)
1114228089Sjhb{
1115228089Sjhb}
1116228089Sjhb
1117228089Sjhbstatic void
1118106932Ssamvlan_input(struct ifnet *ifp, struct mbuf *m)
111944763Swpaul{
1120155051Sglebius	struct ifvlantrunk *trunk = ifp->if_vlantrunk;
112144763Swpaul	struct ifvlan *ifv;
1122230026Srwatson	uint16_t vid;
112344763Swpaul
1124155051Sglebius	KASSERT(trunk != NULL, ("%s: no trunk", __func__));
1125155051Sglebius
1126150216Syar	if (m->m_flags & M_VLANTAG) {
1127106932Ssam		/*
1128150217Syar		 * Packet is tagged, but m contains a normal
1129106932Ssam		 * Ethernet frame; the tag is stored out-of-band.
1130106932Ssam		 */
1131230026Srwatson		vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
1132142069Sru		m->m_flags &= ~M_VLANTAG;
1133106932Ssam	} else {
1134155051Sglebius		struct ether_vlan_header *evl;
1135155051Sglebius
1136150217Syar		/*
1137150217Syar		 * Packet is tagged in-band as specified by 802.1q.
1138150217Syar		 */
1139106932Ssam		switch (ifp->if_type) {
1140106932Ssam		case IFT_ETHER:
1141131580Sbms			if (m->m_len < sizeof(*evl) &&
1142131580Sbms			    (m = m_pullup(m, sizeof(*evl))) == NULL) {
1143106932Ssam				if_printf(ifp, "cannot pullup VLAN header\n");
1144106932Ssam				return;
1145106932Ssam			}
1146106932Ssam			evl = mtod(m, struct ether_vlan_header *);
1147230026Srwatson			vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
1148160950Syar
1149160950Syar			/*
1150165662Syar			 * Remove the 802.1q header by copying the Ethernet
1151165662Syar			 * addresses over it and adjusting the beginning of
1152165662Syar			 * the data in the mbuf.  The encapsulated Ethernet
1153165662Syar			 * type field is already in place.
1154160950Syar			 */
1155165662Syar			bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
1156165662Syar			      ETHER_HDR_LEN - ETHER_TYPE_LEN);
1157165662Syar			m_adj(m, ETHER_VLAN_ENCAP_LEN);
1158106932Ssam			break;
1159165662Syar
1160106932Ssam		default:
1161160950Syar#ifdef INVARIANTS
1162160951Syar			panic("%s: %s has unsupported if_type %u",
1163160951Syar			      __func__, ifp->if_xname, ifp->if_type);
1164106932Ssam#endif
1165160951Syar			m_freem(m);
1166160951Syar			ifp->if_noproto++;
1167160951Syar			return;
1168106932Ssam		}
116991272Sbrooks	}
117091272Sbrooks
1171159838Syar	TRUNK_RLOCK(trunk);
1172230026Srwatson	ifv = vlan_gethash(trunk, vid);
1173165662Syar	if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) {
1174155051Sglebius		TRUNK_RUNLOCK(trunk);
1175155051Sglebius		m_freem(m);
1176155051Sglebius		ifp->if_noproto++;
1177155051Sglebius		return;
1178155051Sglebius	}
1179155051Sglebius	TRUNK_RUNLOCK(trunk);
118044763Swpaul
1181147256Sbrooks	m->m_pkthdr.rcvif = ifv->ifv_ifp;
1182147256Sbrooks	ifv->ifv_ifp->if_ipackets++;
118334649Swollman
1184106932Ssam	/* Pass it back through the parent's input routine. */
1185147256Sbrooks	(*ifp->if_input)(ifv->ifv_ifp, m);
118634649Swollman}
118734649Swollman
118834649Swollmanstatic int
1189230026Srwatsonvlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid)
119034649Swollman{
1191155051Sglebius	struct ifvlantrunk *trunk;
1192150846Syar	struct ifnet *ifp;
1193155051Sglebius	int error = 0;
119434649Swollman
1195155051Sglebius	/* VID numbers 0x0 and 0xFFF are reserved */
1196230026Srwatson	if (vid == 0 || vid == 0xFFF)
1197155051Sglebius		return (EINVAL);
1198219819Sjeff	if (p->if_type != IFT_ETHER &&
1199219819Sjeff	    (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
1200131580Sbms		return (EPROTONOSUPPORT);
1201155114Syar	if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS)
1202155114Syar		return (EPROTONOSUPPORT);
1203155051Sglebius	if (ifv->ifv_trunk)
1204131580Sbms		return (EBUSY);
120534649Swollman
1206155051Sglebius	if (p->if_vlantrunk == NULL) {
1207155051Sglebius		trunk = malloc(sizeof(struct ifvlantrunk),
1208155051Sglebius		    M_VLAN, M_WAITOK | M_ZERO);
1209155231Sglebius		vlan_inithash(trunk);
1210155051Sglebius		VLAN_LOCK();
1211155051Sglebius		if (p->if_vlantrunk != NULL) {
1212155051Sglebius			/* A race that that is very unlikely to be hit. */
1213155231Sglebius			vlan_freehash(trunk);
1214155051Sglebius			free(trunk, M_VLAN);
1215155051Sglebius			goto exists;
1216155051Sglebius		}
1217155051Sglebius		TRUNK_LOCK_INIT(trunk);
1218155051Sglebius		TRUNK_LOCK(trunk);
1219155051Sglebius		p->if_vlantrunk = trunk;
1220155051Sglebius		trunk->parent = p;
1221155051Sglebius	} else {
1222155051Sglebius		VLAN_LOCK();
1223155051Sglebiusexists:
1224155051Sglebius		trunk = p->if_vlantrunk;
1225155051Sglebius		TRUNK_LOCK(trunk);
1226155051Sglebius	}
1227155051Sglebius
1228230026Srwatson	ifv->ifv_vid = vid;	/* must set this before vlan_inshash() */
1229155051Sglebius	error = vlan_inshash(trunk, ifv);
1230155051Sglebius	if (error)
1231155051Sglebius		goto done;
1232161326Syar	ifv->ifv_proto = ETHERTYPE_VLAN;
1233106932Ssam	ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
1234106932Ssam	ifv->ifv_mintu = ETHERMIN;
1235150846Syar	ifv->ifv_pflags = 0;
1236106932Ssam
123734649Swollman	/*
1238106932Ssam	 * If the parent supports the VLAN_MTU capability,
1239106932Ssam	 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
1240129717Syar	 * use it.
1241106932Ssam	 */
1242129717Syar	if (p->if_capenable & IFCAP_VLAN_MTU) {
1243106932Ssam		/*
1244129717Syar		 * No need to fudge the MTU since the parent can
1245129717Syar		 * handle extended frames.
1246106932Ssam		 */
1247106932Ssam		ifv->ifv_mtufudge = 0;
1248129717Syar	} else {
1249106932Ssam		/*
1250106932Ssam		 * Fudge the MTU by the encapsulation size.  This
1251106932Ssam		 * makes us incompatible with strictly compliant
1252106932Ssam		 * 802.1Q implementations, but allows us to use
1253106932Ssam		 * the feature with other NetBSD implementations,
1254106932Ssam		 * which might still be useful.
1255106932Ssam		 */
1256106932Ssam		ifv->ifv_mtufudge = ifv->ifv_encaplen;
1257106932Ssam	}
1258106932Ssam
1259155051Sglebius	ifv->ifv_trunk = trunk;
1260150846Syar	ifp = ifv->ifv_ifp;
1261219819Sjeff	/*
1262219819Sjeff	 * Initialize fields from our parent.  This duplicates some
1263219819Sjeff	 * work with ether_ifattach() but allows for non-ethernet
1264219819Sjeff	 * interfaces to also work.
1265219819Sjeff	 */
1266150846Syar	ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge;
1267155051Sglebius	ifp->if_baudrate = p->if_baudrate;
1268219819Sjeff	ifp->if_output = p->if_output;
1269219819Sjeff	ifp->if_input = p->if_input;
1270219819Sjeff	ifp->if_resolvemulti = p->if_resolvemulti;
1271219819Sjeff	ifp->if_addrlen = p->if_addrlen;
1272219819Sjeff	ifp->if_broadcastaddr = p->if_broadcastaddr;
1273219819Sjeff
1274106932Ssam	/*
127574943Syar	 * Copy only a selected subset of flags from the parent.
127674943Syar	 * Other flags are none of our business.
127734649Swollman	 */
1278155114Syar#define VLAN_COPY_FLAGS (IFF_SIMPLEX)
1279150846Syar	ifp->if_flags &= ~VLAN_COPY_FLAGS;
1280150846Syar	ifp->if_flags |= p->if_flags & VLAN_COPY_FLAGS;
1281150846Syar#undef VLAN_COPY_FLAGS
128234649Swollman
1283150846Syar	ifp->if_link_state = p->if_link_state;
1284150846Syar
1285155051Sglebius	vlan_capabilities(ifv);
1286106932Ssam
1287106932Ssam	/*
1288219819Sjeff	 * Set up our interface address to reflect the underlying
128934649Swollman	 * physical interface's.
129034649Swollman	 */
1291219819Sjeff	bcopy(IF_LLADDR(p), IF_LLADDR(ifp), p->if_addrlen);
1292219819Sjeff	((struct sockaddr_dl *)ifp->if_addr->ifa_addr)->sdl_alen =
1293219819Sjeff	    p->if_addrlen;
129480296Sfenner
129580296Sfenner	/*
129680296Sfenner	 * Configure multicast addresses that may already be
129780296Sfenner	 * joined on the vlan device.
129880296Sfenner	 */
1299150846Syar	(void)vlan_setmulti(ifp); /* XXX: VLAN lock held */
1300161321Syar
1301161321Syar	/* We are ready for operation now. */
1302161321Syar	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1303155051Sglebiusdone:
1304155051Sglebius	TRUNK_UNLOCK(trunk);
1305180511Sjfv	if (error == 0)
1306230026Srwatson		EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid);
1307155051Sglebius	VLAN_UNLOCK();
1308155051Sglebius
1309155051Sglebius	return (error);
131034649Swollman}
131134649Swollman
1312208212Sjhbstatic void
131344763Swpaulvlan_unconfig(struct ifnet *ifp)
131444763Swpaul{
1315159823Syar
1316159823Syar	VLAN_LOCK();
1317239440Sjhb	vlan_unconfig_locked(ifp, 0);
1318159823Syar	VLAN_UNLOCK();
1319159823Syar}
1320159823Syar
1321208212Sjhbstatic void
1322239440Sjhbvlan_unconfig_locked(struct ifnet *ifp, int departing)
1323159823Syar{
1324155051Sglebius	struct ifvlantrunk *trunk;
132544763Swpaul	struct vlan_mc_entry *mc;
132644763Swpaul	struct ifvlan *ifv;
1327180511Sjfv	struct ifnet  *parent;
1328239440Sjhb	int error;
132944763Swpaul
1330159823Syar	VLAN_LOCK_ASSERT();
1331119780Ssam
133244763Swpaul	ifv = ifp->if_softc;
1333155051Sglebius	trunk = ifv->ifv_trunk;
1334182413Sjfv	parent = NULL;
133544763Swpaul
1336182413Sjfv	if (trunk != NULL) {
133744763Swpaul
1338155051Sglebius		TRUNK_LOCK(trunk);
1339182413Sjfv		parent = trunk->parent;
1340155051Sglebius
134180296Sfenner		/*
134280296Sfenner		 * Since the interface is being unconfigured, we need to
134380296Sfenner		 * empty the list of multicast groups that we may have joined
134480296Sfenner		 * while we were alive from the parent's list.
134580296Sfenner		 */
1346167483Syar		while ((mc = SLIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
1347208212Sjhb			/*
1348239440Sjhb			 * If the parent interface is being detached,
1349239519Sjhb			 * all its multicast addresses have already
1350239440Sjhb			 * been removed.  Warn about errors if
1351239440Sjhb			 * if_delmulti() does fail, but don't abort as
1352239440Sjhb			 * all callers expect vlan destruction to
1353239440Sjhb			 * succeed.
1354208212Sjhb			 */
1355239440Sjhb			if (!departing) {
1356239440Sjhb				error = if_delmulti(parent,
1357239440Sjhb				    (struct sockaddr *)&mc->mc_addr);
1358239440Sjhb				if (error)
1359239440Sjhb					if_printf(ifp,
1360239440Sjhb		    "Failed to delete multicast address from parent: %d\n",
1361239440Sjhb					    error);
1362239440Sjhb			}
136380296Sfenner			SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
136483115Sbrooks			free(mc, M_VLAN);
136580296Sfenner		}
1366106932Ssam
1367150846Syar		vlan_setflags(ifp, 0); /* clear special flags on parent */
1368155051Sglebius		vlan_remhash(trunk, ifv);
1369155051Sglebius		ifv->ifv_trunk = NULL;
1370155051Sglebius
1371155051Sglebius		/*
1372155051Sglebius		 * Check if we were the last.
1373155051Sglebius		 */
1374155051Sglebius		if (trunk->refcnt == 0) {
1375270136Smav			parent->if_vlantrunk = NULL;
1376155051Sglebius			/*
1377155051Sglebius			 * XXXGL: If some ithread has already entered
1378155051Sglebius			 * vlan_input() and is now blocked on the trunk
1379155051Sglebius			 * lock, then it should preempt us right after
1380155051Sglebius			 * unlock and finish its work. Then we will acquire
1381155051Sglebius			 * lock again in trunk_destroy().
1382155051Sglebius			 */
1383155051Sglebius			TRUNK_UNLOCK(trunk);
1384155051Sglebius			trunk_destroy(trunk);
1385155051Sglebius		} else
1386155051Sglebius			TRUNK_UNLOCK(trunk);
138744763Swpaul	}
138844763Swpaul
138944763Swpaul	/* Disconnect from parent. */
1390150846Syar	if (ifv->ifv_pflags)
1391150846Syar		if_printf(ifp, "%s: ifv_pflags unclean\n", __func__);
1392159823Syar	ifp->if_mtu = ETHERMTU;
1393159823Syar	ifp->if_link_state = LINK_STATE_UNKNOWN;
1394159823Syar	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
139544763Swpaul
1396182413Sjfv	/*
1397182413Sjfv	 * Only dispatch an event if vlan was
1398182413Sjfv	 * attached, otherwise there is nothing
1399182413Sjfv	 * to cleanup anyway.
1400182413Sjfv	 */
1401182413Sjfv	if (parent != NULL)
1402230026Srwatson		EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_vid);
140344763Swpaul}
140444763Swpaul
1405150846Syar/* Handle a reference counted flag that should be set on the parent as well */
140644763Swpaulstatic int
1407150846Syarvlan_setflag(struct ifnet *ifp, int flag, int status,
1408150846Syar	     int (*func)(struct ifnet *, int))
1409106932Ssam{
1410150846Syar	struct ifvlan *ifv;
1411150846Syar	int error;
1412106932Ssam
1413150846Syar	/* XXX VLAN_LOCK_ASSERT(); */
1414150846Syar
1415150846Syar	ifv = ifp->if_softc;
1416150846Syar	status = status ? (ifp->if_flags & flag) : 0;
1417150846Syar	/* Now "status" contains the flag value or 0 */
1418150846Syar
1419150846Syar	/*
1420150846Syar	 * See if recorded parent's status is different from what
1421150846Syar	 * we want it to be.  If it is, flip it.  We record parent's
1422150846Syar	 * status in ifv_pflags so that we won't clear parent's flag
1423150846Syar	 * we haven't set.  In fact, we don't clear or set parent's
1424150846Syar	 * flags directly, but get or release references to them.
1425150846Syar	 * That's why we can be sure that recorded flags still are
1426150846Syar	 * in accord with actual parent's flags.
1427150846Syar	 */
1428150846Syar	if (status != (ifv->ifv_pflags & flag)) {
1429155051Sglebius		error = (*func)(PARENT(ifv), status);
1430150846Syar		if (error)
1431150846Syar			return (error);
1432150846Syar		ifv->ifv_pflags &= ~flag;
1433150846Syar		ifv->ifv_pflags |= status;
1434106932Ssam	}
1435150846Syar	return (0);
1436150846Syar}
1437106932Ssam
1438150846Syar/*
1439150846Syar * Handle IFF_* flags that require certain changes on the parent:
1440150846Syar * if "status" is true, update parent's flags respective to our if_flags;
1441150846Syar * if "status" is false, forcedly clear the flags set on parent.
1442150846Syar */
1443150846Syarstatic int
1444150846Syarvlan_setflags(struct ifnet *ifp, int status)
1445150846Syar{
1446150846Syar	int error, i;
1447150846Syar
1448150846Syar	for (i = 0; vlan_pflags[i].flag; i++) {
1449150846Syar		error = vlan_setflag(ifp, vlan_pflags[i].flag,
1450150846Syar				     status, vlan_pflags[i].func);
1451150846Syar		if (error)
1452150846Syar			return (error);
1453150846Syar	}
1454150846Syar	return (0);
1455106932Ssam}
1456106932Ssam
1457128871Sandre/* Inform all vlans that their parent has changed link state */
1458128871Sandrestatic void
1459201350Sbrooksvlan_link_state(struct ifnet *ifp)
1460128871Sandre{
1461155051Sglebius	struct ifvlantrunk *trunk = ifp->if_vlantrunk;
1462128871Sandre	struct ifvlan *ifv;
1463155051Sglebius	int i;
1464128871Sandre
1465155051Sglebius	TRUNK_LOCK(trunk);
1466155051Sglebius#ifdef VLAN_ARRAY
1467159838Syar	for (i = 0; i < VLAN_ARRAY_SIZE; i++)
1468155051Sglebius		if (trunk->vlans[i] != NULL) {
1469155051Sglebius			ifv = trunk->vlans[i];
1470155051Sglebius#else
1471163232Sglebius	for (i = 0; i < (1 << trunk->hwidth); i++)
1472163232Sglebius		LIST_FOREACH(ifv, &trunk->hash[i], ifv_list) {
1473155051Sglebius#endif
1474163232Sglebius			ifv->ifv_ifp->if_baudrate = trunk->parent->if_baudrate;
1475147256Sbrooks			if_link_state_change(ifv->ifv_ifp,
1476155051Sglebius			    trunk->parent->if_link_state);
1477163232Sglebius		}
1478155051Sglebius	TRUNK_UNLOCK(trunk);
1479128871Sandre}
1480128871Sandre
1481155051Sglebiusstatic void
1482155051Sglebiusvlan_capabilities(struct ifvlan *ifv)
1483155051Sglebius{
1484155051Sglebius	struct ifnet *p = PARENT(ifv);
1485155051Sglebius	struct ifnet *ifp = ifv->ifv_ifp;
1486155051Sglebius
1487155051Sglebius	TRUNK_LOCK_ASSERT(TRUNK(ifv));
1488155051Sglebius
1489155051Sglebius	/*
1490155051Sglebius	 * If the parent interface can do checksum offloading
1491155051Sglebius	 * on VLANs, then propagate its hardware-assisted
1492155051Sglebius	 * checksumming flags. Also assert that checksum
1493155051Sglebius	 * offloading requires hardware VLAN tagging.
1494155051Sglebius	 */
1495155051Sglebius	if (p->if_capabilities & IFCAP_VLAN_HWCSUM)
1496155051Sglebius		ifp->if_capabilities = p->if_capabilities & IFCAP_HWCSUM;
1497155051Sglebius
1498155051Sglebius	if (p->if_capenable & IFCAP_VLAN_HWCSUM &&
1499155051Sglebius	    p->if_capenable & IFCAP_VLAN_HWTAGGING) {
1500155051Sglebius		ifp->if_capenable = p->if_capenable & IFCAP_HWCSUM;
1501204149Syongari		ifp->if_hwassist = p->if_hwassist & (CSUM_IP | CSUM_TCP |
1502243624Sandre		    CSUM_UDP | CSUM_SCTP | CSUM_FRAGMENT);
1503155051Sglebius	} else {
1504155051Sglebius		ifp->if_capenable = 0;
1505155051Sglebius		ifp->if_hwassist = 0;
1506155051Sglebius	}
1507204149Syongari	/*
1508204149Syongari	 * If the parent interface can do TSO on VLANs then
1509204149Syongari	 * propagate the hardware-assisted flag. TSO on VLANs
1510204149Syongari	 * does not necessarily require hardware VLAN tagging.
1511204149Syongari	 */
1512265413Srmacklem	if (p->if_hw_tsomax > 0)
1513265413Srmacklem		ifp->if_hw_tsomax = p->if_hw_tsomax;
1514204149Syongari	if (p->if_capabilities & IFCAP_VLAN_HWTSO)
1515204149Syongari		ifp->if_capabilities |= p->if_capabilities & IFCAP_TSO;
1516204149Syongari	if (p->if_capenable & IFCAP_VLAN_HWTSO) {
1517204149Syongari		ifp->if_capenable |= p->if_capenable & IFCAP_TSO;
1518204149Syongari		ifp->if_hwassist |= p->if_hwassist & CSUM_TSO;
1519204149Syongari	} else {
1520204149Syongari		ifp->if_capenable &= ~(p->if_capenable & IFCAP_TSO);
1521204149Syongari		ifp->if_hwassist &= ~(p->if_hwassist & CSUM_TSO);
1522204149Syongari	}
1523237263Snp
1524237263Snp	/*
1525237263Snp	 * If the parent interface can offload TCP connections over VLANs then
1526237263Snp	 * propagate its TOE capability to the VLAN interface.
1527237263Snp	 *
1528237263Snp	 * All TOE drivers in the tree today can deal with VLANs.  If this
1529237263Snp	 * changes then IFCAP_VLAN_TOE should be promoted to a full capability
1530237263Snp	 * with its own bit.
1531237263Snp	 */
1532237263Snp#define	IFCAP_VLAN_TOE IFCAP_TOE
1533237263Snp	if (p->if_capabilities & IFCAP_VLAN_TOE)
1534237263Snp		ifp->if_capabilities |= p->if_capabilities & IFCAP_TOE;
1535237263Snp	if (p->if_capenable & IFCAP_VLAN_TOE) {
1536237263Snp		TOEDEV(ifp) = TOEDEV(p);
1537237263Snp		ifp->if_capenable |= p->if_capenable & IFCAP_TOE;
1538237263Snp	}
1539155051Sglebius}
1540155051Sglebius
1541155051Sglebiusstatic void
1542155051Sglebiusvlan_trunk_capabilities(struct ifnet *ifp)
1543155051Sglebius{
1544155051Sglebius	struct ifvlantrunk *trunk = ifp->if_vlantrunk;
1545155051Sglebius	struct ifvlan *ifv;
1546155051Sglebius	int i;
1547155051Sglebius
1548155051Sglebius	TRUNK_LOCK(trunk);
1549155051Sglebius#ifdef VLAN_ARRAY
1550159838Syar	for (i = 0; i < VLAN_ARRAY_SIZE; i++)
1551155051Sglebius		if (trunk->vlans[i] != NULL) {
1552155051Sglebius			ifv = trunk->vlans[i];
1553155051Sglebius#else
1554155051Sglebius	for (i = 0; i < (1 << trunk->hwidth); i++) {
1555155051Sglebius		LIST_FOREACH(ifv, &trunk->hash[i], ifv_list)
1556155051Sglebius#endif
1557155051Sglebius			vlan_capabilities(ifv);
1558155051Sglebius	}
1559155051Sglebius	TRUNK_UNLOCK(trunk);
1560155051Sglebius}
1561155051Sglebius
1562106932Ssamstatic int
156338482Swollmanvlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
156434649Swollman{
156534649Swollman	struct ifnet *p;
156634649Swollman	struct ifreq *ifr;
1567219819Sjeff	struct ifaddr *ifa;
156834649Swollman	struct ifvlan *ifv;
1569270136Smav	struct ifvlantrunk *trunk;
157034649Swollman	struct vlanreq vlr;
157134649Swollman	int error = 0;
157234649Swollman
157334649Swollman	ifr = (struct ifreq *)data;
1574219819Sjeff	ifa = (struct ifaddr *) data;
157534649Swollman	ifv = ifp->if_softc;
157634649Swollman
157734649Swollman	switch (cmd) {
1578219819Sjeff	case SIOCSIFADDR:
1579219819Sjeff		ifp->if_flags |= IFF_UP;
1580219819Sjeff#ifdef INET
1581219819Sjeff		if (ifa->ifa_addr->sa_family == AF_INET)
1582219819Sjeff			arp_ifinit(ifp, ifa);
1583219819Sjeff#endif
1584219819Sjeff		break;
1585219819Sjeff	case SIOCGIFADDR:
1586219819Sjeff                {
1587219819Sjeff			struct sockaddr *sa;
1588219819Sjeff
1589219819Sjeff			sa = (struct sockaddr *)&ifr->ifr_data;
1590219819Sjeff			bcopy(IF_LLADDR(ifp), sa->sa_data, ifp->if_addrlen);
1591219819Sjeff                }
1592219819Sjeff		break;
1593109711Sfenner	case SIOCGIFMEDIA:
1594119780Ssam		VLAN_LOCK();
1595155051Sglebius		if (TRUNK(ifv) != NULL) {
1596205411Semaste			p = PARENT(ifv);
1597119780Ssam			VLAN_UNLOCK();
1598205411Semaste			error = (*p->if_ioctl)(p, SIOCGIFMEDIA, data);
1599109711Sfenner			/* Limit the result to the parent's current config. */
1600109711Sfenner			if (error == 0) {
1601109711Sfenner				struct ifmediareq *ifmr;
1602109711Sfenner
1603131580Sbms				ifmr = (struct ifmediareq *)data;
1604109711Sfenner				if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
1605109711Sfenner					ifmr->ifm_count = 1;
1606109711Sfenner					error = copyout(&ifmr->ifm_current,
1607131580Sbms						ifmr->ifm_ulist,
1608109711Sfenner						sizeof(int));
1609109711Sfenner				}
1610109711Sfenner			}
1611119780Ssam		} else {
1612119780Ssam			VLAN_UNLOCK();
1613109711Sfenner			error = EINVAL;
1614119780Ssam		}
1615109711Sfenner		break;
1616109711Sfenner
1617109711Sfenner	case SIOCSIFMEDIA:
1618109711Sfenner		error = EINVAL;
1619109711Sfenner		break;
1620109711Sfenner
162134649Swollman	case SIOCSIFMTU:
162234649Swollman		/*
162334649Swollman		 * Set the interface MTU.
162434649Swollman		 */
1625119780Ssam		VLAN_LOCK();
1626155051Sglebius		if (TRUNK(ifv) != NULL) {
1627106932Ssam			if (ifr->ifr_mtu >
1628155051Sglebius			     (PARENT(ifv)->if_mtu - ifv->ifv_mtufudge) ||
1629106932Ssam			    ifr->ifr_mtu <
1630106932Ssam			     (ifv->ifv_mintu - ifv->ifv_mtufudge))
1631106932Ssam				error = EINVAL;
1632106932Ssam			else
1633106932Ssam				ifp->if_mtu = ifr->ifr_mtu;
1634106932Ssam		} else
163534649Swollman			error = EINVAL;
1636119780Ssam		VLAN_UNLOCK();
163734649Swollman		break;
163834649Swollman
163934649Swollman	case SIOCSETVLAN:
1640215726Szec#ifdef VIMAGE
1641229586Srwatson		/*
1642229586Srwatson		 * XXXRW/XXXBZ: The goal in these checks is to allow a VLAN
1643229586Srwatson		 * interface to be delegated to a jail without allowing the
1644229586Srwatson		 * jail to change what underlying interface/VID it is
1645229586Srwatson		 * associated with.  We are not entirely convinced that this
1646229587Srwatson		 * is the right way to accomplish that policy goal.
1647229586Srwatson		 */
1648215726Szec		if (ifp->if_vnet != ifp->if_home_vnet) {
1649215726Szec			error = EPERM;
1650215726Szec			break;
1651215726Szec		}
1652215726Szec#endif
1653131580Sbms		error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
165434649Swollman		if (error)
165534649Swollman			break;
165634649Swollman		if (vlr.vlr_parent[0] == '\0') {
165744763Swpaul			vlan_unconfig(ifp);
165834649Swollman			break;
165934649Swollman		}
166034649Swollman		p = ifunit(vlr.vlr_parent);
1661197010Semaste		if (p == NULL) {
166234649Swollman			error = ENOENT;
166334649Swollman			break;
166434649Swollman		}
1665117343Swpaul		/*
1666230026Srwatson		 * Don't let the caller set up a VLAN VID with
1667117343Swpaul		 * anything except VLID bits.
1668117343Swpaul		 */
1669117343Swpaul		if (vlr.vlr_tag & ~EVL_VLID_MASK) {
1670117343Swpaul			error = EINVAL;
1671117343Swpaul			break;
1672117343Swpaul		}
1673155051Sglebius		error = vlan_config(ifv, p, vlr.vlr_tag);
1674155051Sglebius		if (error)
167534649Swollman			break;
1676106932Ssam
1677150846Syar		/* Update flags on the parent, if necessary. */
1678150846Syar		vlan_setflags(ifp, 1);
167934649Swollman		break;
1680131580Sbms
168134649Swollman	case SIOCGETVLAN:
1682215726Szec#ifdef VIMAGE
1683215726Szec		if (ifp->if_vnet != ifp->if_home_vnet) {
1684215726Szec			error = EPERM;
1685215726Szec			break;
1686215726Szec		}
1687215726Szec#endif
1688131580Sbms		bzero(&vlr, sizeof(vlr));
1689119780Ssam		VLAN_LOCK();
1690155051Sglebius		if (TRUNK(ifv) != NULL) {
1691155051Sglebius			strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname,
1692121816Sbrooks			    sizeof(vlr.vlr_parent));
1693230026Srwatson			vlr.vlr_tag = ifv->ifv_vid;
169434649Swollman		}
1695119780Ssam		VLAN_UNLOCK();
1696131580Sbms		error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
169734649Swollman		break;
169834649Swollman
169934649Swollman	case SIOCSIFFLAGS:
170034649Swollman		/*
1701150846Syar		 * We should propagate selected flags to the parent,
1702150846Syar		 * e.g., promiscuous mode.
170334649Swollman		 */
1704155051Sglebius		if (TRUNK(ifv) != NULL)
1705150846Syar			error = vlan_setflags(ifp, 1);
170634649Swollman		break;
1707106932Ssam
170844763Swpaul	case SIOCADDMULTI:
170944763Swpaul	case SIOCDELMULTI:
1710155051Sglebius		/*
1711155051Sglebius		 * If we don't have a parent, just remember the membership for
1712155051Sglebius		 * when we do.
1713155051Sglebius		 */
1714270136Smav		trunk = TRUNK(ifv);
1715270136Smav		if (trunk != NULL) {
1716270136Smav			TRUNK_LOCK(trunk);
1717155051Sglebius			error = vlan_setmulti(ifp);
1718270136Smav			TRUNK_UNLOCK(trunk);
1719270136Smav		}
172044763Swpaul		break;
1721155051Sglebius
172234649Swollman	default:
1723219819Sjeff		error = EINVAL;
1724219819Sjeff		break;
172534649Swollman	}
1726131580Sbms
1727131580Sbms	return (error);
172834649Swollman}
1729