if_vlan.c revision 117343
1/*
2 * Copyright 1998 Massachusetts Institute of Technology
3 *
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that both the above copyright notice and this
7 * permission notice appear in all copies, that both the above
8 * copyright notice and this permission notice appear in all
9 * supporting documentation, and that the name of M.I.T. not be used
10 * in advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission.  M.I.T. makes
12 * no representations about the suitability of this software for any
13 * purpose.  It is provided "as is" without express or implied
14 * warranty.
15 *
16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sys/net/if_vlan.c 117343 2003-07-08 21:54:20Z wpaul $
30 */
31
32/*
33 * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
34 * Might be extended some day to also handle IEEE 802.1p priority
35 * tagging.  This is sort of sneaky in the implementation, since
36 * we need to pretend to be enough of an Ethernet implementation
37 * to make arp work.  The way we do this is by telling everyone
38 * that we are an Ethernet, and then catch the packets that
39 * ether_output() left on our output queue when it calls
40 * if_start(), rewrite them for use by the real outgoing interface,
41 * and ask it to send them.
42 */
43
44#include "opt_inet.h"
45
46#include <sys/param.h>
47#include <sys/kernel.h>
48#include <sys/malloc.h>
49#include <sys/mbuf.h>
50#include <sys/module.h>
51#include <sys/queue.h>
52#include <sys/socket.h>
53#include <sys/sockio.h>
54#include <sys/sysctl.h>
55#include <sys/systm.h>
56
57#include <net/bpf.h>
58#include <net/ethernet.h>
59#include <net/if.h>
60#include <net/if_arp.h>
61#include <net/if_dl.h>
62#include <net/if_types.h>
63#include <net/if_vlan_var.h>
64
65#ifdef INET
66#include <netinet/in.h>
67#include <netinet/if_ether.h>
68#endif
69
70#define VLANNAME	"vlan"
71
72struct vlan_mc_entry {
73	struct ether_addr		mc_addr;
74	SLIST_ENTRY(vlan_mc_entry)	mc_entries;
75};
76
77struct	ifvlan {
78	struct	arpcom ifv_ac;	/* make this an interface */
79	struct	ifnet *ifv_p;	/* parent inteface of this vlan */
80	struct	ifv_linkmib {
81		int	ifvm_parent;
82		int	ifvm_encaplen;	/* encapsulation length */
83		int	ifvm_mtufudge;	/* MTU fudged by this much */
84		int	ifvm_mintu;	/* min transmission unit */
85		u_int16_t ifvm_proto; /* encapsulation ethertype */
86		u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
87	}	ifv_mib;
88	SLIST_HEAD(__vlan_mchead, vlan_mc_entry)	vlan_mc_listhead;
89	LIST_ENTRY(ifvlan) ifv_list;
90	int	ifv_flags;
91};
92#define	ifv_if	ifv_ac.ac_if
93#define	ifv_tag	ifv_mib.ifvm_tag
94#define	ifv_encaplen	ifv_mib.ifvm_encaplen
95#define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
96#define	ifv_mintu	ifv_mib.ifvm_mintu
97
98#define	IFVF_PROMISC	0x01		/* promiscuous mode enabled */
99
100SYSCTL_DECL(_net_link);
101SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
102SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
103
104static MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface");
105static LIST_HEAD(, ifvlan) ifv_list;
106
107static	int vlan_clone_create(struct if_clone *, int);
108static	void vlan_clone_destroy(struct ifnet *);
109static	void vlan_start(struct ifnet *ifp);
110static	void vlan_ifinit(void *foo);
111static	void vlan_input(struct ifnet *ifp, struct mbuf *m);
112static	int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
113static	int vlan_setmulti(struct ifnet *ifp);
114static	int vlan_unconfig(struct ifnet *ifp);
115static	int vlan_config(struct ifvlan *ifv, struct ifnet *p);
116
117struct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan",
118    vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);
119
120/*
121 * Program our multicast filter. What we're actually doing is
122 * programming the multicast filter of the parent. This has the
123 * side effect of causing the parent interface to receive multicast
124 * traffic that it doesn't really want, which ends up being discarded
125 * later by the upper protocol layers. Unfortunately, there's no way
126 * to avoid this: there really is only one physical interface.
127 */
128static int
129vlan_setmulti(struct ifnet *ifp)
130{
131	struct ifnet		*ifp_p;
132	struct ifmultiaddr	*ifma, *rifma = NULL;
133	struct ifvlan		*sc;
134	struct vlan_mc_entry	*mc = NULL;
135	struct sockaddr_dl	sdl;
136	int			error;
137
138	/* Find the parent. */
139	sc = ifp->if_softc;
140	ifp_p = sc->ifv_p;
141
142	/*
143	 * If we don't have a parent, just remember the membership for
144	 * when we do.
145	 */
146	if (ifp_p == NULL)
147		return(0);
148
149	bzero((char *)&sdl, sizeof sdl);
150	sdl.sdl_len = sizeof sdl;
151	sdl.sdl_family = AF_LINK;
152	sdl.sdl_index = ifp_p->if_index;
153	sdl.sdl_type = IFT_ETHER;
154	sdl.sdl_alen = ETHER_ADDR_LEN;
155
156	/* First, remove any existing filter entries. */
157	while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
158		mc = SLIST_FIRST(&sc->vlan_mc_listhead);
159		bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
160		error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
161		if (error)
162			return(error);
163		SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
164		free(mc, M_VLAN);
165	}
166
167	/* Now program new ones. */
168	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
169		if (ifma->ifma_addr->sa_family != AF_LINK)
170			continue;
171		mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK);
172		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
173		    (char *)&mc->mc_addr, ETHER_ADDR_LEN);
174		SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
175		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
176		    LLADDR(&sdl), ETHER_ADDR_LEN);
177		error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
178		if (error)
179			return(error);
180	}
181
182	return(0);
183}
184
185/*
186 * VLAN support can be loaded as a module.  The only place in the
187 * system that's intimately aware of this is ether_input.  We hook
188 * into this code through vlan_input_p which is defined there and
189 * set here.  Noone else in the system should be aware of this so
190 * we use an explicit reference here.
191 *
192 * NB: Noone should ever need to check if vlan_input_p is null or
193 *     not.  This is because interfaces have a count of the number
194 *     of active vlans (if_nvlans) and this should never be bumped
195 *     except by vlan_config--which is in this module so therefore
196 *     the module must be loaded and vlan_input_p must be non-NULL.
197 */
198extern	void (*vlan_input_p)(struct ifnet *, struct mbuf *);
199
200static int
201vlan_modevent(module_t mod, int type, void *data)
202{
203
204	switch (type) {
205	case MOD_LOAD:
206		LIST_INIT(&ifv_list);
207		vlan_input_p = vlan_input;
208		if_clone_attach(&vlan_cloner);
209		break;
210	case MOD_UNLOAD:
211		if_clone_detach(&vlan_cloner);
212		vlan_input_p = NULL;
213		while (!LIST_EMPTY(&ifv_list))
214			vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
215		break;
216	}
217	return 0;
218}
219
220static moduledata_t vlan_mod = {
221	"if_vlan",
222	vlan_modevent,
223	0
224};
225
226DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
227
228static int
229vlan_clone_create(struct if_clone *ifc, int unit)
230{
231	struct ifvlan *ifv;
232	struct ifnet *ifp;
233	int s;
234
235	ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
236	ifp = &ifv->ifv_if;
237	SLIST_INIT(&ifv->vlan_mc_listhead);
238
239	s = splnet();
240	LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
241	splx(s);
242
243	ifp->if_softc = ifv;
244	ifp->if_name = "vlan";
245	ifp->if_unit = unit;
246	/* NB: flags are not set here */
247	ifp->if_linkmib = &ifv->ifv_mib;
248	ifp->if_linkmiblen = sizeof ifv->ifv_mib;
249	/* NB: mtu is not set here */
250
251	ifp->if_init = vlan_ifinit;
252	ifp->if_start = vlan_start;
253	ifp->if_ioctl = vlan_ioctl;
254	ifp->if_snd.ifq_maxlen = ifqmaxlen;
255	ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr);
256	/* Now undo some of the damage... */
257	ifp->if_baudrate = 0;
258	ifp->if_type = IFT_L2VLAN;
259	ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
260
261	return (0);
262}
263
264static void
265vlan_clone_destroy(struct ifnet *ifp)
266{
267	struct ifvlan *ifv = ifp->if_softc;
268	int s;
269
270	s = splnet();
271	LIST_REMOVE(ifv, ifv_list);
272	vlan_unconfig(ifp);
273	splx(s);
274
275	ether_ifdetach(ifp);
276
277	free(ifv, M_VLAN);
278}
279
280static void
281vlan_ifinit(void *foo)
282{
283	return;
284}
285
286static void
287vlan_start(struct ifnet *ifp)
288{
289	struct ifvlan *ifv;
290	struct ifnet *p;
291	struct ether_vlan_header *evl;
292	struct mbuf *m;
293
294	ifv = ifp->if_softc;
295	p = ifv->ifv_p;
296
297	ifp->if_flags |= IFF_OACTIVE;
298	for (;;) {
299		IF_DEQUEUE(&ifp->if_snd, m);
300		if (m == 0)
301			break;
302		BPF_MTAP(ifp, m);
303
304		/*
305		 * Do not run parent's if_start() if the parent is not up,
306		 * or parent's driver will cause a system crash.
307		 */
308		if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
309					(IFF_UP | IFF_RUNNING)) {
310			m_freem(m);
311			ifp->if_collisions++;
312			continue;
313		}
314
315		/*
316		 * If underlying interface can do VLAN tag insertion itself,
317		 * just pass the packet along. However, we need some way to
318		 * tell the interface where the packet came from so that it
319		 * knows how to find the VLAN tag to use, so we attach a
320		 * packet tag that holds it.
321		 */
322		if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
323			struct m_tag *mtag = m_tag_alloc(MTAG_VLAN,
324							 MTAG_VLAN_TAG,
325							 sizeof (u_int),
326							 M_NOWAIT);
327			if (mtag == NULL) {
328				ifp->if_oerrors++;
329				m_freem(m);
330				continue;
331			}
332			*(u_int*)(mtag+1) = ifv->ifv_tag;
333			m_tag_prepend(m, mtag);
334		} else {
335			M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
336			if (m == NULL) {
337				if_printf(ifp, "unable to prepend VLAN header");
338				ifp->if_ierrors++;
339				continue;
340			}
341			/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
342
343			if (m->m_len < sizeof(*evl)) {
344				m = m_pullup(m, sizeof(*evl));
345				if (m == NULL) {
346					if_printf(ifp,
347					    "cannot pullup VLAN header");
348					ifp->if_ierrors++;
349					continue;
350				}
351			}
352
353			/*
354			 * Transform the Ethernet header into an Ethernet header
355			 * with 802.1Q encapsulation.
356			 */
357			bcopy(mtod(m, char *) + ifv->ifv_encaplen,
358			      mtod(m, char *), ETHER_HDR_LEN);
359			evl = mtod(m, struct ether_vlan_header *);
360			evl->evl_proto = evl->evl_encap_proto;
361			evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
362			evl->evl_tag = htons(ifv->ifv_tag);
363#ifdef DEBUG
364			printf("vlan_start: %*D\n", (int)sizeof *evl,
365			    (unsigned char *)evl, ":");
366#endif
367		}
368
369		/*
370		 * Send it, precisely as ether_output() would have.
371		 * We are already running at splimp.
372		 */
373		if (IF_HANDOFF(&p->if_snd, m, p))
374			ifp->if_opackets++;
375		else
376			ifp->if_oerrors++;
377	}
378	ifp->if_flags &= ~IFF_OACTIVE;
379
380	return;
381}
382
383static void
384vlan_input(struct ifnet *ifp, struct mbuf *m)
385{
386	struct ether_vlan_header *evl;
387	struct ifvlan *ifv;
388	struct m_tag *mtag;
389	u_int tag;
390
391	mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL);
392	if (mtag != NULL) {
393		/*
394		 * Packet is tagged, m contains a normal
395		 * Ethernet frame; the tag is stored out-of-band.
396		 */
397		tag = EVL_VLANOFTAG(*(u_int*)(mtag+1));
398		m_tag_delete(m, mtag);
399	} else {
400		switch (ifp->if_type) {
401		case IFT_ETHER:
402			if (m->m_len < sizeof (*evl) &&
403			    (m = m_pullup(m, sizeof (*evl))) == NULL) {
404				if_printf(ifp, "cannot pullup VLAN header\n");
405				return;
406			}
407			evl = mtod(m, struct ether_vlan_header *);
408			KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN,
409				("vlan_input: bad encapsulated protocols (%u)",
410				 ntohs(evl->evl_encap_proto)));
411
412			tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
413
414			/*
415			 * Restore the original ethertype.  We'll remove
416			 * the encapsulation after we've found the vlan
417			 * interface corresponding to the tag.
418			 */
419			evl->evl_encap_proto = evl->evl_proto;
420			break;
421		default:
422			tag = (u_int) -1;
423#ifdef DIAGNOSTIC
424			panic("vlan_input: unsupported if type %u", ifp->if_type);
425#endif
426			break;
427		}
428	}
429
430	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
431	    ifv = LIST_NEXT(ifv, ifv_list))
432		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
433			break;
434
435	if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
436		m_freem(m);
437		ifp->if_noproto++;
438		return;
439	}
440
441	if (mtag == NULL) {
442		/*
443		 * Packet had an in-line encapsulation header;
444		 * remove it.  The original header has already
445		 * been fixed up above.
446		 */
447		bcopy(mtod(m, caddr_t),
448		      mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
449		      ETHER_HDR_LEN);
450		m_adj(m, ETHER_VLAN_ENCAP_LEN);
451	}
452
453	m->m_pkthdr.rcvif = &ifv->ifv_if;
454	ifv->ifv_if.if_ipackets++;
455
456	/* Pass it back through the parent's input routine. */
457	(*ifp->if_input)(&ifv->ifv_if, m);
458}
459
460static int
461vlan_config(struct ifvlan *ifv, struct ifnet *p)
462{
463	struct ifaddr *ifa1, *ifa2;
464	struct sockaddr_dl *sdl1, *sdl2;
465
466	if (p->if_data.ifi_type != IFT_ETHER)
467		return EPROTONOSUPPORT;
468	if (ifv->ifv_p)
469		return EBUSY;
470
471	ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
472	ifv->ifv_mintu = ETHERMIN;
473	ifv->ifv_flags = 0;
474
475	/*
476	 * If the parent supports the VLAN_MTU capability,
477	 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
478	 * enable it.
479	 */
480	p->if_nvlans++;
481	if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) {
482		/*
483		 * Enable Tx/Rx of VLAN-sized frames.
484		 */
485		p->if_capenable |= IFCAP_VLAN_MTU;
486		if (p->if_flags & IFF_UP) {
487			struct ifreq ifr;
488			int error;
489
490			ifr.ifr_flags = p->if_flags;
491			error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
492			    (caddr_t) &ifr);
493			if (error) {
494				p->if_nvlans--;
495				if (p->if_nvlans == 0)
496					p->if_capenable &= ~IFCAP_VLAN_MTU;
497				return (error);
498			}
499		}
500		ifv->ifv_mtufudge = 0;
501	} else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) {
502		/*
503		 * Fudge the MTU by the encapsulation size.  This
504		 * makes us incompatible with strictly compliant
505		 * 802.1Q implementations, but allows us to use
506		 * the feature with other NetBSD implementations,
507		 * which might still be useful.
508		 */
509		ifv->ifv_mtufudge = ifv->ifv_encaplen;
510	}
511
512	ifv->ifv_p = p;
513	ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
514	/*
515	 * Copy only a selected subset of flags from the parent.
516	 * Other flags are none of our business.
517	 */
518	ifv->ifv_if.if_flags = (p->if_flags &
519	    (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
520
521	/*
522	 * If the parent interface can do hardware-assisted
523	 * VLAN encapsulation, then propagate its hardware-
524	 * assisted checksumming flags.
525	 */
526	if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
527		ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
528
529	/*
530	 * Set up our ``Ethernet address'' to reflect the underlying
531	 * physical interface's.
532	 */
533	ifa1 = ifaddr_byindex(ifv->ifv_if.if_index);
534	ifa2 = ifaddr_byindex(p->if_index);
535	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
536	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
537	sdl1->sdl_type = IFT_ETHER;
538	sdl1->sdl_alen = ETHER_ADDR_LEN;
539	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
540	bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
541
542	/*
543	 * Configure multicast addresses that may already be
544	 * joined on the vlan device.
545	 */
546	(void)vlan_setmulti(&ifv->ifv_if);
547
548	return 0;
549}
550
551static int
552vlan_unconfig(struct ifnet *ifp)
553{
554	struct ifaddr *ifa;
555	struct sockaddr_dl *sdl;
556	struct vlan_mc_entry *mc;
557	struct ifvlan *ifv;
558	struct ifnet *p;
559	int error;
560
561	ifv = ifp->if_softc;
562	p = ifv->ifv_p;
563
564	if (p) {
565		struct sockaddr_dl sdl;
566
567		/*
568		 * Since the interface is being unconfigured, we need to
569		 * empty the list of multicast groups that we may have joined
570		 * while we were alive from the parent's list.
571		 */
572		bzero((char *)&sdl, sizeof sdl);
573		sdl.sdl_len = sizeof sdl;
574		sdl.sdl_family = AF_LINK;
575		sdl.sdl_index = p->if_index;
576		sdl.sdl_type = IFT_ETHER;
577		sdl.sdl_alen = ETHER_ADDR_LEN;
578
579		while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
580			mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
581			bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
582			error = if_delmulti(p, (struct sockaddr *)&sdl);
583			if (error)
584				return(error);
585			SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
586			free(mc, M_VLAN);
587		}
588
589		p->if_nvlans--;
590		if (p->if_nvlans == 0) {
591			/*
592			 * Disable Tx/Rx of VLAN-sized frames.
593			 */
594			p->if_capenable &= ~IFCAP_VLAN_MTU;
595			if (p->if_flags & IFF_UP) {
596				struct ifreq ifr;
597
598				ifr.ifr_flags = p->if_flags;
599				(*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
600			}
601		}
602	}
603
604	/* Disconnect from parent. */
605	ifv->ifv_p = NULL;
606	ifv->ifv_if.if_mtu = ETHERMTU;		/* XXX why not 0? */
607	ifv->ifv_flags = 0;
608
609	/* Clear our MAC address. */
610	ifa = ifaddr_byindex(ifv->ifv_if.if_index);
611	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
612	sdl->sdl_type = IFT_ETHER;
613	sdl->sdl_alen = ETHER_ADDR_LEN;
614	bzero(LLADDR(sdl), ETHER_ADDR_LEN);
615	bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
616
617	return 0;
618}
619
620static int
621vlan_set_promisc(struct ifnet *ifp)
622{
623	struct ifvlan *ifv = ifp->if_softc;
624	int error = 0;
625
626	if ((ifp->if_flags & IFF_PROMISC) != 0) {
627		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
628			error = ifpromisc(ifv->ifv_p, 1);
629			if (error == 0)
630				ifv->ifv_flags |= IFVF_PROMISC;
631		}
632	} else {
633		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
634			error = ifpromisc(ifv->ifv_p, 0);
635			if (error == 0)
636				ifv->ifv_flags &= ~IFVF_PROMISC;
637		}
638	}
639
640	return (error);
641}
642
643static int
644vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
645{
646	struct ifaddr *ifa;
647	struct ifnet *p;
648	struct ifreq *ifr;
649	struct ifvlan *ifv;
650	struct vlanreq vlr;
651	int error = 0;
652
653	ifr = (struct ifreq *)data;
654	ifa = (struct ifaddr *)data;
655	ifv = ifp->if_softc;
656
657	switch (cmd) {
658	case SIOCSIFADDR:
659		ifp->if_flags |= IFF_UP;
660
661		switch (ifa->ifa_addr->sa_family) {
662#ifdef INET
663		case AF_INET:
664			arp_ifinit(&ifv->ifv_if, ifa);
665			break;
666#endif
667		default:
668			break;
669		}
670		break;
671
672	case SIOCGIFADDR:
673		{
674			struct sockaddr *sa;
675
676			sa = (struct sockaddr *) &ifr->ifr_data;
677			bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
678			      (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
679		}
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		/*
741		 * Don't let the caller set up a VLAN tag with
742		 * anything except VLID bits.
743		 */
744		if (vlr.vlr_tag & ~EVL_VLID_MASK) {
745			error = EINVAL;
746			break;
747		}
748		error = vlan_config(ifv, p);
749		if (error)
750			break;
751		ifv->ifv_tag = vlr.vlr_tag;
752		ifp->if_flags |= IFF_RUNNING;
753
754		/* Update promiscuous mode, if necessary. */
755		vlan_set_promisc(ifp);
756		break;
757
758	case SIOCGETVLAN:
759		bzero(&vlr, sizeof vlr);
760		if (ifv->ifv_p) {
761			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
762			    "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
763			vlr.vlr_tag = ifv->ifv_tag;
764		}
765		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
766		break;
767
768	case SIOCSIFFLAGS:
769		/*
770		 * For promiscuous mode, we enable promiscuous mode on
771		 * the parent if we need promiscuous on the VLAN interface.
772		 */
773		if (ifv->ifv_p != NULL)
774			error = vlan_set_promisc(ifp);
775		break;
776
777	case SIOCADDMULTI:
778	case SIOCDELMULTI:
779		error = vlan_setmulti(ifp);
780		break;
781	default:
782		error = EINVAL;
783	}
784	return error;
785}
786