if_vlan.c revision 109711
1214456Srpaulo/*
2214456Srpaulo * Copyright 1998 Massachusetts Institute of Technology
3214456Srpaulo *
4214456Srpaulo * Permission to use, copy, modify, and distribute this software and
5214456Srpaulo * its documentation for any purpose and without fee is hereby
6214456Srpaulo * granted, provided that both the above copyright notice and this
7214456Srpaulo * permission notice appear in all copies, that both the above
8214456Srpaulo * copyright notice and this permission notice appear in all
9214456Srpaulo * supporting documentation, and that the name of M.I.T. not be used
10214456Srpaulo * in advertising or publicity pertaining to distribution of the
11214456Srpaulo * software without specific, written prior permission.  M.I.T. makes
12214456Srpaulo * no representations about the suitability of this software for any
13214456Srpaulo * purpose.  It is provided "as is" without express or implied
14214456Srpaulo * warranty.
15214456Srpaulo *
16214456Srpaulo * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17214456Srpaulo * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18214456Srpaulo * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19214456Srpaulo * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20214456Srpaulo * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21214456Srpaulo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22214456Srpaulo * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23214456Srpaulo * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24214456Srpaulo * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25214456Srpaulo * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26214456Srpaulo * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27214456Srpaulo * SUCH DAMAGE.
28214456Srpaulo *
29214456Srpaulo * $FreeBSD: head/sys/net/if_vlan.c 109711 2003-01-22 23:30:26Z fenner $
30214456Srpaulo */
31214456Srpaulo
32214456Srpaulo/*
33214456Srpaulo * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
34214456Srpaulo * Might be extended some day to also handle IEEE 802.1p priority
35214456Srpaulo * tagging.  This is sort of sneaky in the implementation, since
36214456Srpaulo * we need to pretend to be enough of an Ethernet implementation
37214456Srpaulo * to make arp work.  The way we do this is by telling everyone
38214456Srpaulo * that we are an Ethernet, and then catch the packets that
39214456Srpaulo * ether_output() left on our output queue when it calls
40214456Srpaulo * if_start(), rewrite them for use by the real outgoing interface,
41214456Srpaulo * and ask it to send them.
42214456Srpaulo */
43214456Srpaulo
44214456Srpaulo#include "opt_inet.h"
45214456Srpaulo
46214456Srpaulo#include <sys/param.h>
47214456Srpaulo#include <sys/kernel.h>
48214456Srpaulo#include <sys/malloc.h>
49214456Srpaulo#include <sys/mbuf.h>
50214456Srpaulo#include <sys/module.h>
51214456Srpaulo#include <sys/queue.h>
52214456Srpaulo#include <sys/socket.h>
53214456Srpaulo#include <sys/sockio.h>
54214456Srpaulo#include <sys/sysctl.h>
55214456Srpaulo#include <sys/systm.h>
56214456Srpaulo
57214456Srpaulo#include <net/bpf.h>
58214456Srpaulo#include <net/ethernet.h>
59214456Srpaulo#include <net/if.h>
60214456Srpaulo#include <net/if_arp.h>
61214456Srpaulo#include <net/if_dl.h>
62214456Srpaulo#include <net/if_types.h>
63214456Srpaulo#include <net/if_vlan_var.h>
64214456Srpaulo
65214456Srpaulo#ifdef INET
66214456Srpaulo#include <netinet/in.h>
67214456Srpaulo#include <netinet/if_ether.h>
68214456Srpaulo#endif
69214456Srpaulo
70214456Srpaulo#define VLANNAME	"vlan"
71214456Srpaulo
72214456Srpaulostruct vlan_mc_entry {
73214456Srpaulo	struct ether_addr		mc_addr;
74214456Srpaulo	SLIST_ENTRY(vlan_mc_entry)	mc_entries;
75214456Srpaulo};
76214456Srpaulo
77214456Srpaulostruct	ifvlan {
78214456Srpaulo	struct	arpcom ifv_ac;	/* make this an interface */
79214456Srpaulo	struct	ifnet *ifv_p;	/* parent inteface of this vlan */
80214456Srpaulo	struct	ifv_linkmib {
81214456Srpaulo		int	ifvm_parent;
82214456Srpaulo		int	ifvm_encaplen;	/* encapsulation length */
83214456Srpaulo		int	ifvm_mtufudge;	/* MTU fudged by this much */
84214456Srpaulo		int	ifvm_mintu;	/* min transmission unit */
85214456Srpaulo		u_int16_t ifvm_proto; /* encapsulation ethertype */
86214456Srpaulo		u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
87214456Srpaulo	}	ifv_mib;
88214456Srpaulo	SLIST_HEAD(__vlan_mchead, vlan_mc_entry)	vlan_mc_listhead;
89214456Srpaulo	LIST_ENTRY(ifvlan) ifv_list;
90214456Srpaulo	int	ifv_flags;
91214456Srpaulo};
92214456Srpaulo#define	ifv_if	ifv_ac.ac_if
93214456Srpaulo#define	ifv_tag	ifv_mib.ifvm_tag
94214456Srpaulo#define	ifv_encaplen	ifv_mib.ifvm_encaplen
95214456Srpaulo#define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
96214456Srpaulo#define	ifv_mintu	ifv_mib.ifvm_mintu
97214456Srpaulo
98214456Srpaulo#define	IFVF_PROMISC	0x01		/* promiscuous mode enabled */
99214456Srpaulo
100214456SrpauloSYSCTL_DECL(_net_link);
101214456SrpauloSYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
102214456SrpauloSYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
103214456Srpaulo
104214456Srpaulostatic MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface");
105214456Srpaulostatic LIST_HEAD(, ifvlan) ifv_list;
106214456Srpaulo
107214456Srpaulostatic	int vlan_clone_create(struct if_clone *, int);
108214456Srpaulostatic	void vlan_clone_destroy(struct ifnet *);
109214456Srpaulostatic	void vlan_start(struct ifnet *ifp);
110214456Srpaulostatic	void vlan_ifinit(void *foo);
111214456Srpaulostatic	void vlan_input(struct ifnet *ifp, struct mbuf *m);
112214456Srpaulostatic	int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
113214456Srpaulostatic	int vlan_setmulti(struct ifnet *ifp);
114214456Srpaulostatic	int vlan_unconfig(struct ifnet *ifp);
115214456Srpaulostatic	int vlan_config(struct ifvlan *ifv, struct ifnet *p);
116214456Srpaulo
117214456Srpaulostruct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan",
118214456Srpaulo    vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);
119214456Srpaulo
120214456Srpaulo/*
121214456Srpaulo * Program our multicast filter. What we're actually doing is
122214456Srpaulo * programming the multicast filter of the parent. This has the
123214456Srpaulo * side effect of causing the parent interface to receive multicast
124214456Srpaulo * traffic that it doesn't really want, which ends up being discarded
125214456Srpaulo * later by the upper protocol layers. Unfortunately, there's no way
126214456Srpaulo * to avoid this: there really is only one physical interface.
127214456Srpaulo */
128214456Srpaulostatic int
129214456Srpaulovlan_setmulti(struct ifnet *ifp)
130214456Srpaulo{
131214456Srpaulo	struct ifnet		*ifp_p;
132214456Srpaulo	struct ifmultiaddr	*ifma, *rifma = NULL;
133214456Srpaulo	struct ifvlan		*sc;
134214456Srpaulo	struct vlan_mc_entry	*mc = NULL;
135214456Srpaulo	struct sockaddr_dl	sdl;
136214456Srpaulo	int			error;
137214456Srpaulo
138214456Srpaulo	/* Find the parent. */
139214456Srpaulo	sc = ifp->if_softc;
140214456Srpaulo	ifp_p = sc->ifv_p;
141214456Srpaulo
142214456Srpaulo	/*
143214456Srpaulo	 * If we don't have a parent, just remember the membership for
144214456Srpaulo	 * when we do.
145214456Srpaulo	 */
146214456Srpaulo	if (ifp_p == NULL)
147214456Srpaulo		return(0);
148214456Srpaulo
149214456Srpaulo	bzero((char *)&sdl, sizeof sdl);
150214456Srpaulo	sdl.sdl_len = sizeof sdl;
151214456Srpaulo	sdl.sdl_family = AF_LINK;
152214456Srpaulo	sdl.sdl_index = ifp_p->if_index;
153214456Srpaulo	sdl.sdl_type = IFT_ETHER;
154214456Srpaulo	sdl.sdl_alen = ETHER_ADDR_LEN;
155214456Srpaulo
156214456Srpaulo	/* First, remove any existing filter entries. */
157214456Srpaulo	while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
158214456Srpaulo		mc = SLIST_FIRST(&sc->vlan_mc_listhead);
159214456Srpaulo		bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
160214456Srpaulo		error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
161214456Srpaulo		if (error)
162214456Srpaulo			return(error);
163214456Srpaulo		SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
164214456Srpaulo		free(mc, M_VLAN);
165214456Srpaulo	}
166214456Srpaulo
167214456Srpaulo	/* Now program new ones. */
168214456Srpaulo	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
169214456Srpaulo		if (ifma->ifma_addr->sa_family != AF_LINK)
170214456Srpaulo			continue;
171214456Srpaulo		mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, 0);
172214456Srpaulo		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
173214456Srpaulo		    (char *)&mc->mc_addr, ETHER_ADDR_LEN);
174214456Srpaulo		SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
175214456Srpaulo		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
176214456Srpaulo		    LLADDR(&sdl), ETHER_ADDR_LEN);
177214456Srpaulo		error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
178214456Srpaulo		if (error)
179214456Srpaulo			return(error);
180214456Srpaulo	}
181214456Srpaulo
182214456Srpaulo	return(0);
183214456Srpaulo}
184214456Srpaulo
185214456Srpaulo/*
186214456Srpaulo * VLAN support can be loaded as a module.  The only place in the
187214456Srpaulo * system that's intimately aware of this is ether_input.  We hook
188214456Srpaulo * into this code through vlan_input_p which is defined there and
189214456Srpaulo * set here.  Noone else in the system should be aware of this so
190214456Srpaulo * we use an explicit reference here.
191214456Srpaulo *
192214456Srpaulo * NB: Noone should ever need to check if vlan_input_p is null or
193214456Srpaulo *     not.  This is because interfaces have a count of the number
194214456Srpaulo *     of active vlans (if_nvlans) and this should never be bumped
195214456Srpaulo *     except by vlan_config--which is in this module so therefore
196214456Srpaulo *     the module must be loaded and vlan_input_p must be non-NULL.
197214456Srpaulo */
198214456Srpauloextern	void (*vlan_input_p)(struct ifnet *, struct mbuf *);
199214456Srpaulo
200214456Srpaulostatic int
201214456Srpaulovlan_modevent(module_t mod, int type, void *data)
202214456Srpaulo{
203214456Srpaulo
204214456Srpaulo	switch (type) {
205214456Srpaulo	case MOD_LOAD:
206214456Srpaulo		LIST_INIT(&ifv_list);
207214456Srpaulo		vlan_input_p = vlan_input;
208214456Srpaulo		if_clone_attach(&vlan_cloner);
209214456Srpaulo		break;
210214456Srpaulo	case MOD_UNLOAD:
211214456Srpaulo		if_clone_detach(&vlan_cloner);
212214456Srpaulo		vlan_input_p = NULL;
213214456Srpaulo		while (!LIST_EMPTY(&ifv_list))
214214456Srpaulo			vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
215214456Srpaulo		break;
216214456Srpaulo	}
217214456Srpaulo	return 0;
218214456Srpaulo}
219214456Srpaulo
220214456Srpaulostatic moduledata_t vlan_mod = {
221214456Srpaulo	"if_vlan",
222214456Srpaulo	vlan_modevent,
223214456Srpaulo	0
224214456Srpaulo};
225214456Srpaulo
226214456SrpauloDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
227214456Srpaulo
228214456Srpaulostatic int
229214456Srpaulovlan_clone_create(struct if_clone *ifc, int unit)
230214456Srpaulo{
231214456Srpaulo	struct ifvlan *ifv;
232214456Srpaulo	struct ifnet *ifp;
233214456Srpaulo	int s;
234214456Srpaulo
235214456Srpaulo	ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_ZERO);
236214456Srpaulo	ifp = &ifv->ifv_if;
237214456Srpaulo	SLIST_INIT(&ifv->vlan_mc_listhead);
238214456Srpaulo
239214456Srpaulo	s = splnet();
240214456Srpaulo	LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
241214456Srpaulo	splx(s);
242214456Srpaulo
243214456Srpaulo	ifp->if_softc = ifv;
244214456Srpaulo	ifp->if_name = "vlan";
245214456Srpaulo	ifp->if_unit = unit;
246214456Srpaulo	/* NB: flags are not set here */
247214456Srpaulo	ifp->if_linkmib = &ifv->ifv_mib;
248214456Srpaulo	ifp->if_linkmiblen = sizeof ifv->ifv_mib;
249214456Srpaulo	/* NB: mtu is not set here */
250214456Srpaulo
251214456Srpaulo	ifp->if_init = vlan_ifinit;
252214456Srpaulo	ifp->if_start = vlan_start;
253214456Srpaulo	ifp->if_ioctl = vlan_ioctl;
254214456Srpaulo	ifp->if_snd.ifq_maxlen = ifqmaxlen;
255214456Srpaulo	ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr);
256214456Srpaulo	/* Now undo some of the damage... */
257214456Srpaulo	ifp->if_baudrate = 0;
258214456Srpaulo	ifp->if_type = IFT_L2VLAN;
259214456Srpaulo	ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
260214456Srpaulo
261214456Srpaulo	return (0);
262214456Srpaulo}
263214456Srpaulo
264214456Srpaulostatic void
265214456Srpaulovlan_clone_destroy(struct ifnet *ifp)
266214456Srpaulo{
267214456Srpaulo	struct ifvlan *ifv = ifp->if_softc;
268214456Srpaulo	int s;
269214456Srpaulo
270214456Srpaulo	s = splnet();
271214456Srpaulo	LIST_REMOVE(ifv, ifv_list);
272214456Srpaulo	vlan_unconfig(ifp);
273214456Srpaulo	splx(s);
274214456Srpaulo
275214456Srpaulo	ether_ifdetach(ifp);
276214456Srpaulo
277214456Srpaulo	free(ifv, M_VLAN);
278214456Srpaulo}
279214456Srpaulo
280214456Srpaulostatic void
281214456Srpaulovlan_ifinit(void *foo)
282214456Srpaulo{
283214456Srpaulo	return;
284214456Srpaulo}
285214456Srpaulo
286214456Srpaulostatic void
287214456Srpaulovlan_start(struct ifnet *ifp)
288214456Srpaulo{
289214456Srpaulo	struct ifvlan *ifv;
290214456Srpaulo	struct ifnet *p;
291214456Srpaulo	struct ether_vlan_header *evl;
292214456Srpaulo	struct mbuf *m;
293214456Srpaulo
294214456Srpaulo	ifv = ifp->if_softc;
295214456Srpaulo	p = ifv->ifv_p;
296214456Srpaulo
297214456Srpaulo	ifp->if_flags |= IFF_OACTIVE;
298214456Srpaulo	for (;;) {
299214456Srpaulo		IF_DEQUEUE(&ifp->if_snd, m);
300214456Srpaulo		if (m == 0)
301214456Srpaulo			break;
302214456Srpaulo		BPF_MTAP(ifp, m);
303214456Srpaulo
304214456Srpaulo		/*
305214456Srpaulo		 * Do not run parent's if_start() if the parent is not up,
306214456Srpaulo		 * or parent's driver will cause a system crash.
307214456Srpaulo		 */
308214456Srpaulo		if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
309214456Srpaulo					(IFF_UP | IFF_RUNNING)) {
310214456Srpaulo			m_freem(m);
311241235Sdelphij			ifp->if_collisions++;
312214456Srpaulo			continue;
313214456Srpaulo		}
314214456Srpaulo
315214456Srpaulo		/*
316214456Srpaulo		 * If underlying interface can do VLAN tag insertion itself,
317214456Srpaulo		 * just pass the packet along. However, we need some way to
318214456Srpaulo		 * tell the interface where the packet came from so that it
319214456Srpaulo		 * knows how to find the VLAN tag to use, so we attach a
320214456Srpaulo		 * packet tag that holds it.
321214456Srpaulo		 */
322214456Srpaulo		if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) {
323214456Srpaulo			struct m_tag *mtag = m_tag_alloc(MTAG_VLAN,
324214456Srpaulo							 MTAG_VLAN_TAG,
325214456Srpaulo							 sizeof (u_int),
326214456Srpaulo							 M_NOWAIT);
327214456Srpaulo			if (mtag == NULL) {
328214456Srpaulo				ifp->if_oerrors++;
329214456Srpaulo				m_freem(m);
330214456Srpaulo				continue;
331214456Srpaulo			}
332214456Srpaulo			*(u_int*)(mtag+1) = ifv->ifv_tag;
333214456Srpaulo			m_tag_prepend(m, mtag);
334214456Srpaulo		} else {
335214456Srpaulo			M_PREPEND(m, ifv->ifv_encaplen, M_NOWAIT);
336214456Srpaulo			if (m == NULL) {
337214456Srpaulo				if_printf(ifp, "unable to prepend VLAN header");
338214456Srpaulo				ifp->if_ierrors++;
339214456Srpaulo				continue;
340214456Srpaulo			}
341214456Srpaulo			/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
342214456Srpaulo
343214456Srpaulo			if (m->m_len < sizeof(*evl)) {
344214456Srpaulo				m = m_pullup(m, sizeof(*evl));
345214456Srpaulo				if (m == NULL) {
346214456Srpaulo					if_printf(ifp,
347214456Srpaulo					    "cannot pullup VLAN header");
348214456Srpaulo					ifp->if_ierrors++;
349214456Srpaulo					continue;
350214456Srpaulo				}
351214456Srpaulo			}
352214456Srpaulo
353214456Srpaulo			/*
354214456Srpaulo			 * Transform the Ethernet header into an Ethernet header
355214456Srpaulo			 * with 802.1Q encapsulation.
356214456Srpaulo			 */
357214456Srpaulo			bcopy(mtod(m, char *) + ifv->ifv_encaplen,
358214456Srpaulo			      mtod(m, char *), sizeof(struct ether_header));
359214456Srpaulo			evl = mtod(m, struct ether_vlan_header *);
360214456Srpaulo			evl->evl_proto = evl->evl_encap_proto;
361214456Srpaulo			evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
362214456Srpaulo			evl->evl_tag = htons(ifv->ifv_tag);
363214456Srpaulo#ifdef DEBUG
364214456Srpaulo			printf("vlan_start: %*D\n", (int)sizeof *evl,
365214456Srpaulo			    (unsigned char *)evl, ":");
366214456Srpaulo#endif
367214456Srpaulo		}
368214456Srpaulo
369214456Srpaulo		/*
370214456Srpaulo		 * Send it, precisely as ether_output() would have.
371214456Srpaulo		 * We are already running at splimp.
372214456Srpaulo		 */
373214456Srpaulo		if (IF_HANDOFF(&p->if_snd, m, p))
374214456Srpaulo			ifp->if_opackets++;
375214456Srpaulo		else
376214456Srpaulo			ifp->if_oerrors++;
377214456Srpaulo	}
378214456Srpaulo	ifp->if_flags &= ~IFF_OACTIVE;
379214456Srpaulo
380214456Srpaulo	return;
381214456Srpaulo}
382214456Srpaulo
383214456Srpaulostatic void
384214456Srpaulovlan_input(struct ifnet *ifp, struct mbuf *m)
385214456Srpaulo{
386214456Srpaulo	struct ether_vlan_header *evl;
387214456Srpaulo	struct ifvlan *ifv;
388214456Srpaulo	struct m_tag *mtag;
389214456Srpaulo	u_int tag;
390214456Srpaulo
391214456Srpaulo	mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL);
392214456Srpaulo	if (mtag != NULL) {
393214456Srpaulo		/*
394214456Srpaulo		 * Packet is tagged, m contains a normal
395214456Srpaulo		 * Ethernet frame; the tag is stored out-of-band.
396214456Srpaulo		 */
397214456Srpaulo		tag = *(u_int*)(mtag+1);
398214456Srpaulo		m_tag_delete(m, mtag);
399214456Srpaulo	} else {
400214456Srpaulo		switch (ifp->if_type) {
401214456Srpaulo		case IFT_ETHER:
402214456Srpaulo			if (m->m_len < sizeof (*evl) &&
403214456Srpaulo			    (m = m_pullup(m, sizeof (*evl))) == NULL) {
404214456Srpaulo				if_printf(ifp, "cannot pullup VLAN header\n");
405214456Srpaulo				return;
406214456Srpaulo			}
407214456Srpaulo			evl = mtod(m, struct ether_vlan_header *);
408214456Srpaulo			KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN,
409214456Srpaulo				("vlan_input: bad encapsulated protocols (%u)",
410214456Srpaulo				 ntohs(evl->evl_encap_proto)));
411214456Srpaulo
412214456Srpaulo			tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
413214456Srpaulo
414214456Srpaulo			/*
415214456Srpaulo			 * Restore the original ethertype.  We'll remove
416214456Srpaulo			 * the encapsulation after we've found the vlan
417214456Srpaulo			 * interface corresponding to the tag.
418214456Srpaulo			 */
419214456Srpaulo			evl->evl_encap_proto = evl->evl_proto;
420214456Srpaulo			break;
421214456Srpaulo		default:
422214456Srpaulo			tag = (u_int) -1;
423214456Srpaulo#ifdef DIAGNOSTIC
424214456Srpaulo			panic("vlan_input: unsupported if type %u", ifp->if_type);
425214456Srpaulo#endif
426214456Srpaulo			break;
427214456Srpaulo		}
428214456Srpaulo	}
429214456Srpaulo
430214456Srpaulo	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
431214456Srpaulo	    ifv = LIST_NEXT(ifv, ifv_list))
432214456Srpaulo		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
433214456Srpaulo			break;
434214456Srpaulo
435214456Srpaulo	if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
436214456Srpaulo		m_freem(m);
437214456Srpaulo		ifp->if_noproto++;
438214456Srpaulo		return;
439214456Srpaulo	}
440214456Srpaulo
441214456Srpaulo	if (mtag == NULL) {
442214456Srpaulo		/*
443214456Srpaulo		 * Packet had an in-line encapsulation header;
444214456Srpaulo		 * remove it.  The original header has already
445214456Srpaulo		 * been fixed up above.
446214456Srpaulo		 */
447214456Srpaulo		bcopy(mtod(m, caddr_t),
448214456Srpaulo		      mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
449214456Srpaulo		      sizeof (struct ether_header));
450214456Srpaulo		m_adj(m, ETHER_VLAN_ENCAP_LEN);
451214456Srpaulo	}
452214456Srpaulo
453214456Srpaulo	m->m_pkthdr.rcvif = &ifv->ifv_if;
454214456Srpaulo	ifv->ifv_if.if_ipackets++;
455214456Srpaulo
456214456Srpaulo	/* Pass it back through the parent's input routine. */
457214456Srpaulo	(*ifp->if_input)(&ifv->ifv_if, m);
458214456Srpaulo}
459214456Srpaulo
460214456Srpaulostatic int
461214456Srpaulovlan_config(struct ifvlan *ifv, struct ifnet *p)
462214456Srpaulo{
463214456Srpaulo	struct ifaddr *ifa1, *ifa2;
464214456Srpaulo	struct sockaddr_dl *sdl1, *sdl2;
465214456Srpaulo
466214456Srpaulo	if (p->if_data.ifi_type != IFT_ETHER)
467214456Srpaulo		return EPROTONOSUPPORT;
468214456Srpaulo	if (ifv->ifv_p)
469214456Srpaulo		return EBUSY;
470214456Srpaulo
471214456Srpaulo	ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
472214456Srpaulo	ifv->ifv_mintu = ETHERMIN;
473214456Srpaulo	ifv->ifv_flags = 0;
474214456Srpaulo
475214456Srpaulo	/*
476214456Srpaulo	 * If the parent supports the VLAN_MTU capability,
477214456Srpaulo	 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
478214456Srpaulo	 * enable it.
479214456Srpaulo	 */
480214456Srpaulo	p->if_nvlans++;
481214456Srpaulo	if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) {
482214456Srpaulo		/*
483214456Srpaulo		 * Enable Tx/Rx of VLAN-sized frames.
484214456Srpaulo		 */
485214456Srpaulo		p->if_capenable |= IFCAP_VLAN_MTU;
486214456Srpaulo		if (p->if_flags & IFF_UP) {
487214456Srpaulo			struct ifreq ifr;
488214456Srpaulo			int error;
489214456Srpaulo
490214456Srpaulo			ifr.ifr_flags = p->if_flags;
491214456Srpaulo			error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
492214456Srpaulo			    (caddr_t) &ifr);
493214456Srpaulo			if (error) {
494214456Srpaulo				p->if_nvlans--;
495214456Srpaulo				if (p->if_nvlans == 0)
496214456Srpaulo					p->if_capenable &= ~IFCAP_VLAN_MTU;
497214456Srpaulo				return (error);
498214456Srpaulo			}
499214456Srpaulo		}
500214456Srpaulo		ifv->ifv_mtufudge = 0;
501214456Srpaulo	} else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) {
502214456Srpaulo		/*
503214456Srpaulo		 * Fudge the MTU by the encapsulation size.  This
504214456Srpaulo		 * makes us incompatible with strictly compliant
505214456Srpaulo		 * 802.1Q implementations, but allows us to use
506214456Srpaulo		 * the feature with other NetBSD implementations,
507214456Srpaulo		 * which might still be useful.
508214456Srpaulo		 */
509214456Srpaulo		ifv->ifv_mtufudge = ifv->ifv_encaplen;
510214456Srpaulo	}
511214456Srpaulo
512214456Srpaulo	ifv->ifv_p = p;
513214456Srpaulo	ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
514214456Srpaulo	/*
515214456Srpaulo	 * Copy only a selected subset of flags from the parent.
516214456Srpaulo	 * Other flags are none of our business.
517214456Srpaulo	 */
518214456Srpaulo	ifv->ifv_if.if_flags = (p->if_flags &
519214456Srpaulo	    (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
520214456Srpaulo
521214456Srpaulo	/*
522214456Srpaulo	 * If the parent interface can do hardware-assisted
523214456Srpaulo	 * VLAN encapsulation, then propagate its hardware-
524214456Srpaulo	 * assisted checksumming flags.
525214456Srpaulo	 */
526214456Srpaulo	if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
527214456Srpaulo		ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
528214456Srpaulo
529214456Srpaulo	/*
530214456Srpaulo	 * Set up our ``Ethernet address'' to reflect the underlying
531214456Srpaulo	 * physical interface's.
532214456Srpaulo	 */
533214456Srpaulo	ifa1 = ifaddr_byindex(ifv->ifv_if.if_index);
534214456Srpaulo	ifa2 = ifaddr_byindex(p->if_index);
535214456Srpaulo	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
536214456Srpaulo	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
537214456Srpaulo	sdl1->sdl_type = IFT_ETHER;
538214456Srpaulo	sdl1->sdl_alen = ETHER_ADDR_LEN;
539214456Srpaulo	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
540214456Srpaulo	bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
541214456Srpaulo
542214456Srpaulo	/*
543214456Srpaulo	 * Configure multicast addresses that may already be
544214456Srpaulo	 * joined on the vlan device.
545214456Srpaulo	 */
546214456Srpaulo	(void)vlan_setmulti(&ifv->ifv_if);
547214456Srpaulo
548214456Srpaulo	return 0;
549214456Srpaulo}
550214456Srpaulo
551214456Srpaulostatic int
552214456Srpaulovlan_unconfig(struct ifnet *ifp)
553214456Srpaulo{
554214456Srpaulo	struct ifaddr *ifa;
555214456Srpaulo	struct sockaddr_dl *sdl;
556214456Srpaulo	struct vlan_mc_entry *mc;
557214456Srpaulo	struct ifvlan *ifv;
558214456Srpaulo	struct ifnet *p;
559214456Srpaulo	int error;
560214456Srpaulo
561214456Srpaulo	ifv = ifp->if_softc;
562214456Srpaulo	p = ifv->ifv_p;
563214456Srpaulo
564214456Srpaulo	if (p) {
565214456Srpaulo		struct sockaddr_dl sdl;
566214456Srpaulo
567214456Srpaulo		/*
568214456Srpaulo		 * Since the interface is being unconfigured, we need to
569214456Srpaulo		 * empty the list of multicast groups that we may have joined
570214456Srpaulo		 * while we were alive from the parent's list.
571214456Srpaulo		 */
572214456Srpaulo		bzero((char *)&sdl, sizeof sdl);
573214456Srpaulo		sdl.sdl_len = sizeof sdl;
574214456Srpaulo		sdl.sdl_family = AF_LINK;
575214456Srpaulo		sdl.sdl_index = p->if_index;
576214456Srpaulo		sdl.sdl_type = IFT_ETHER;
577214456Srpaulo		sdl.sdl_alen = ETHER_ADDR_LEN;
578214456Srpaulo
579214456Srpaulo		while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
580214456Srpaulo			mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
581214456Srpaulo			bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
582214456Srpaulo			error = if_delmulti(p, (struct sockaddr *)&sdl);
583214456Srpaulo			if (error)
584214456Srpaulo				return(error);
585214456Srpaulo			SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
586214456Srpaulo			free(mc, M_VLAN);
587214456Srpaulo		}
588235530Sdelphij
589214456Srpaulo		p->if_nvlans--;
590214456Srpaulo		if (p->if_nvlans == 0) {
591214456Srpaulo			/*
592214456Srpaulo			 * Disable Tx/Rx of VLAN-sized frames.
593214456Srpaulo			 */
594214456Srpaulo			p->if_capenable &= ~IFCAP_VLAN_MTU;
595214456Srpaulo			if (p->if_flags & IFF_UP) {
596214456Srpaulo				struct ifreq ifr;
597214456Srpaulo
598214456Srpaulo				ifr.ifr_flags = p->if_flags;
599214456Srpaulo				(*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
600214456Srpaulo			}
601214456Srpaulo		}
602214456Srpaulo	}
603214456Srpaulo
604214456Srpaulo	/* Disconnect from parent. */
605214456Srpaulo	ifv->ifv_p = NULL;
606214456Srpaulo	ifv->ifv_if.if_mtu = ETHERMTU;		/* XXX why not 0? */
607214456Srpaulo	ifv->ifv_flags = 0;
608214456Srpaulo
609214456Srpaulo	/* Clear our MAC address. */
610214456Srpaulo	ifa = ifaddr_byindex(ifv->ifv_if.if_index);
611214456Srpaulo	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
612214456Srpaulo	sdl->sdl_type = IFT_ETHER;
613214456Srpaulo	sdl->sdl_alen = ETHER_ADDR_LEN;
614214456Srpaulo	bzero(LLADDR(sdl), ETHER_ADDR_LEN);
615214456Srpaulo	bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
616214456Srpaulo
617214456Srpaulo	return 0;
618214456Srpaulo}
619214456Srpaulo
620214456Srpaulostatic int
621214456Srpaulovlan_set_promisc(struct ifnet *ifp)
622214456Srpaulo{
623214456Srpaulo	struct ifvlan *ifv = ifp->if_softc;
624214456Srpaulo	int error = 0;
625214456Srpaulo
626214456Srpaulo	if ((ifp->if_flags & IFF_PROMISC) != 0) {
627214456Srpaulo		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
628214456Srpaulo			error = ifpromisc(ifv->ifv_p, 1);
629214456Srpaulo			if (error == 0)
630214456Srpaulo				ifv->ifv_flags |= IFVF_PROMISC;
631214456Srpaulo		}
632214456Srpaulo	} else {
633214456Srpaulo		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
634214456Srpaulo			error = ifpromisc(ifv->ifv_p, 0);
635214456Srpaulo			if (error == 0)
636214456Srpaulo				ifv->ifv_flags &= ~IFVF_PROMISC;
637214456Srpaulo		}
638214456Srpaulo	}
639214456Srpaulo
640214456Srpaulo	return (error);
641214456Srpaulo}
642214456Srpaulo
643214456Srpaulostatic int
644214456Srpaulovlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
645214456Srpaulo{
646214456Srpaulo	struct ifaddr *ifa;
647214456Srpaulo	struct ifnet *p;
648214456Srpaulo	struct ifreq *ifr;
649214456Srpaulo	struct ifvlan *ifv;
650214456Srpaulo	struct vlanreq vlr;
651214456Srpaulo	int error = 0;
652214456Srpaulo
653214456Srpaulo	ifr = (struct ifreq *)data;
654214456Srpaulo	ifa = (struct ifaddr *)data;
655214456Srpaulo	ifv = ifp->if_softc;
656214456Srpaulo
657214456Srpaulo	switch (cmd) {
658214456Srpaulo	case SIOCSIFADDR:
659214456Srpaulo		ifp->if_flags |= IFF_UP;
660214456Srpaulo
661214456Srpaulo		switch (ifa->ifa_addr->sa_family) {
662214456Srpaulo#ifdef INET
663214456Srpaulo		case AF_INET:
664214456Srpaulo			arp_ifinit(&ifv->ifv_if, ifa);
665214456Srpaulo			break;
666214456Srpaulo#endif
667214456Srpaulo		default:
668214456Srpaulo			break;
669214456Srpaulo		}
670214456Srpaulo		break;
671214456Srpaulo
672214456Srpaulo	case SIOCGIFADDR:
673214456Srpaulo		{
674214456Srpaulo			struct sockaddr *sa;
675214456Srpaulo
676214456Srpaulo			sa = (struct sockaddr *) &ifr->ifr_data;
677214456Srpaulo			bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
678214456Srpaulo			      (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
679214456Srpaulo		}
680		break;
681
682	case SIOCGIFMEDIA:
683		if (ifv->ifv_p != NULL) {
684			error = (ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCGIFMEDIA, data);
685			/* Limit the result to the parent's current config. */
686			if (error == 0) {
687				struct ifmediareq *ifmr;
688
689				ifmr = (struct ifmediareq *) data;
690				if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
691					ifmr->ifm_count = 1;
692					error = copyout(&ifmr->ifm_current,
693						ifmr->ifm_ulist,
694						sizeof(int));
695				}
696			}
697		} else
698			error = EINVAL;
699		break;
700
701	case SIOCSIFMEDIA:
702		error = EINVAL;
703		break;
704
705	case SIOCSIFMTU:
706		/*
707		 * Set the interface MTU.
708		 */
709		if (ifv->ifv_p != NULL) {
710			if (ifr->ifr_mtu >
711			     (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
712			    ifr->ifr_mtu <
713			     (ifv->ifv_mintu - ifv->ifv_mtufudge))
714				error = EINVAL;
715			else
716				ifp->if_mtu = ifr->ifr_mtu;
717		} else
718			error = EINVAL;
719		break;
720
721	case SIOCSETVLAN:
722		error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
723		if (error)
724			break;
725		if (vlr.vlr_parent[0] == '\0') {
726			vlan_unconfig(ifp);
727			if (ifp->if_flags & IFF_UP) {
728				int s = splimp();
729				if_down(ifp);
730				splx(s);
731			}
732			ifp->if_flags &= ~IFF_RUNNING;
733			break;
734		}
735		p = ifunit(vlr.vlr_parent);
736		if (p == 0) {
737			error = ENOENT;
738			break;
739		}
740		error = vlan_config(ifv, p);
741		if (error)
742			break;
743		ifv->ifv_tag = vlr.vlr_tag;
744		ifp->if_flags |= IFF_RUNNING;
745
746		/* Update promiscuous mode, if necessary. */
747		vlan_set_promisc(ifp);
748		break;
749
750	case SIOCGETVLAN:
751		bzero(&vlr, sizeof vlr);
752		if (ifv->ifv_p) {
753			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
754			    "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
755			vlr.vlr_tag = ifv->ifv_tag;
756		}
757		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
758		break;
759
760	case SIOCSIFFLAGS:
761		/*
762		 * For promiscuous mode, we enable promiscuous mode on
763		 * the parent if we need promiscuous on the VLAN interface.
764		 */
765		if (ifv->ifv_p != NULL)
766			error = vlan_set_promisc(ifp);
767		break;
768
769	case SIOCADDMULTI:
770	case SIOCDELMULTI:
771		error = vlan_setmulti(ifp);
772		break;
773	default:
774		error = EINVAL;
775	}
776	return error;
777}
778