xref: /openbsd-src/sys/net/if_vlan.c (revision 799f675f6700f14e59124f9825c723e9f2ce19dc)
1 /*	$OpenBSD: if_vlan.c,v 1.68 2006/05/22 23:25:15 krw Exp $	*/
2 
3 /*
4  * Copyright 1998 Massachusetts Institute of Technology
5  *
6  * Permission to use, copy, modify, and distribute this software and
7  * its documentation for any purpose and without fee is hereby
8  * granted, provided that both the above copyright notice and this
9  * permission notice appear in all copies, that both the above
10  * copyright notice and this permission notice appear in all
11  * supporting documentation, and that the name of M.I.T. not be used
12  * in advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.  M.I.T. makes
14  * no representations about the suitability of this software for any
15  * purpose.  It is provided "as is" without express or implied
16  * warranty.
17  *
18  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
19  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
22  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD: src/sys/net/if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp $
32  */
33 
34 /*
35  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
36  * Might be extended some day to also handle IEEE 802.1p priority
37  * tagging.  This is sort of sneaky in the implementation, since
38  * we need to pretend to be enough of an Ethernet implementation
39  * to make arp work.  The way we do this is by telling everyone
40  * that we are an Ethernet, and then catch the packets that
41  * ether_output() left on our output queue when it calls
42  * if_start(), rewrite them for use by the real outgoing interface,
43  * and ask it to send them.
44  *
45  * Some devices support 802.1Q tag insertion in firmware.  The
46  * vlan interface behavior changes when the IFCAP_VLAN_HWTAGGING
47  * capability is set on the parent.  In this case, vlan_start()
48  * will not modify the ethernet header.
49  */
50 
51 #include "vlan.h"
52 
53 #include <sys/param.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/mbuf.h>
57 #include <sys/queue.h>
58 #include <sys/socket.h>
59 #include <sys/sockio.h>
60 #include <sys/sysctl.h>
61 #include <sys/systm.h>
62 #include <sys/proc.h>
63 
64 #include "bpfilter.h"
65 #if NBPFILTER > 0
66 #include <net/bpf.h>
67 #endif
68 
69 #include <net/if.h>
70 #include <net/if_dl.h>
71 #include <net/if_types.h>
72 
73 #ifdef INET
74 #include <netinet/in.h>
75 #include <netinet/if_ether.h>
76 #endif
77 
78 #include <net/if_vlan_var.h>
79 
80 extern struct	ifaddr	**ifnet_addrs;
81 extern int ifqmaxlen;
82 u_long vlan_tagmask;
83 
84 #define TAG_HASH_SIZE	32
85 #define TAG_HASH(tag)	(tag & vlan_tagmask)
86 LIST_HEAD(, ifvlan)	*vlan_tagh;
87 
88 void	vlan_start (struct ifnet *ifp);
89 int	vlan_ioctl (struct ifnet *ifp, u_long cmd, caddr_t addr);
90 int	vlan_unconfig (struct ifnet *ifp);
91 int	vlan_config (struct ifvlan *, struct ifnet *, u_int16_t);
92 void	vlan_vlandev_state(void *);
93 void	vlanattach (int count);
94 int	vlan_set_promisc (struct ifnet *ifp);
95 int	vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
96 int	vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
97 void	vlan_ether_purgemulti(struct ifvlan *);
98 int	vlan_clone_create(struct if_clone *, int);
99 int	vlan_clone_destroy(struct ifnet *);
100 void	vlan_ifdetach(void *);
101 
102 struct if_clone vlan_cloner =
103     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
104 
105 /* ARGSUSED */
106 void
107 vlanattach(int count)
108 {
109 	vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, &vlan_tagmask);
110 	if (vlan_tagh == NULL)
111 		panic("vlanattach: hashinit");
112 
113 	if_clone_attach(&vlan_cloner);
114 }
115 
116 int
117 vlan_clone_create(struct if_clone *ifc, int unit)
118 {
119 	struct ifvlan *ifv;
120 	struct ifnet *ifp;
121 
122 	ifv = malloc(sizeof(*ifv), M_DEVBUF, M_NOWAIT);
123 	if (!ifv)
124 		return (ENOMEM);
125 	bzero(ifv, sizeof(*ifv));
126 
127 	LIST_INIT(&ifv->vlan_mc_listhead);
128 	ifp = &ifv->ifv_if;
129 	ifp->if_softc = ifv;
130 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
131 	    unit);
132 	/* NB: flags are not set here */
133 	/* NB: mtu is not set here */
134 
135 	ifp->if_start = vlan_start;
136 	ifp->if_ioctl = vlan_ioctl;
137 	ifp->if_output = ether_output;
138 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
139 	IFQ_SET_READY(&ifp->if_snd);
140 	if_attach(ifp);
141 	ether_ifattach(ifp);
142 	/* Now undo some of the damage... */
143 	ifp->if_type = IFT_L2VLAN;
144 	ifp->if_hdrlen = EVL_ENCAPLEN;
145 
146 	return (0);
147 }
148 
149 int
150 vlan_clone_destroy(struct ifnet *ifp)
151 {
152 	struct ifvlan *ifv = ifp->if_softc;
153 
154 	vlan_unconfig(ifp);
155 	ether_ifdetach(ifp);
156 	if_detach(ifp);
157 
158 	free(ifv, M_DEVBUF);
159 	return (0);
160 }
161 
162 void
163 vlan_ifdetach(void *ptr)
164 {
165 	struct ifvlan *ifv = (struct ifvlan *)ptr;
166 	/*
167 	 * Destroy the vlan interface because the parent has been
168 	 * detached. Set the dh_cookie to NULL because we're running
169 	 * inside of dohooks which is told to disestablish the hook
170 	 * for us (otherwise we would kill the TAILQ element...).
171 	 */
172 	ifv->dh_cookie = NULL;
173 	vlan_clone_destroy(&ifv->ifv_if);
174 }
175 
176 void
177 vlan_start(struct ifnet *ifp)
178 {
179 	struct ifvlan *ifv;
180 	struct ifnet *p;
181 	struct mbuf *m;
182 	int error;
183 
184 	ifv = ifp->if_softc;
185 	p = ifv->ifv_p;
186 
187 	ifp->if_flags |= IFF_OACTIVE;
188 	for (;;) {
189 		IFQ_DEQUEUE(&ifp->if_snd, m);
190 		if (m == NULL)
191 			break;
192 
193 		if ((p->if_flags & (IFF_UP|IFF_RUNNING)) !=
194 		    (IFF_UP|IFF_RUNNING)) {
195 			IF_DROP(&p->if_snd);
196 				/* XXX stats */
197 			ifp->if_oerrors++;
198 			m_freem(m);
199 			continue;
200 		}
201 
202 #if NBPFILTER > 0
203 		if (ifp->if_bpf)
204 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
205 #endif
206 
207 		/*
208 		 * If the IFCAP_VLAN_HWTAGGING capability is set on the parent,
209 		 * it can do VLAN tag insertion itself and doesn't require us
210 	 	 * to create a special header for it. In this case, we just pass
211 		 * the packet along. However, we need some way to tell the
212 		 * interface where the packet came from so that it knows how
213 		 * to find the VLAN tag to use, so we set the rcvif in the
214 		 * mbuf header to our ifnet.
215 		 *
216 		 * Note: we also set the M_PROTO1 flag in the mbuf to let
217 		 * the parent driver know that the rcvif pointer is really
218 		 * valid. We need to do this because sometimes mbufs will
219 		 * be allocated by other parts of the system that contain
220 		 * garbage in the rcvif pointer. Using the M_PROTO1 flag
221 		 * lets the driver perform a proper sanity check and avoid
222 		 * following potentially bogus rcvif pointers off into
223 		 * never-never land.
224 		 */
225 		if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
226 			m->m_pkthdr.rcvif = ifp;
227 			m->m_flags |= M_PROTO1;
228 		} else {
229 			struct ether_vlan_header evh;
230 
231 			m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh);
232 			evh.evl_proto = evh.evl_encap_proto;
233 			evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
234 			evh.evl_tag = htons(ifv->ifv_tag +
235 			    (ifv->ifv_prio << EVL_PRIO_BITS));
236 
237 			m_adj(m, ETHER_HDR_LEN);
238 			M_PREPEND(m, sizeof(evh), M_DONTWAIT);
239 			if (m == NULL) {
240 				ifp->if_oerrors++;
241 				continue;
242 			}
243 
244 			m_copyback(m, 0, sizeof(evh), &evh);
245 		}
246 
247 		/*
248 		 * Send it, precisely as ether_output() would have.
249 		 * We are already running at splnet.
250 		 */
251 		p->if_obytes += m->m_pkthdr.len;
252 		if (m->m_flags & M_MCAST)
253 			p->if_omcasts++;
254 		IFQ_ENQUEUE(&p->if_snd, m, NULL, error);
255 		if (error) {
256 			/* mbuf is already freed */
257 			ifp->if_oerrors++;
258 			continue;
259 		}
260 
261 		ifp->if_opackets++;
262 		if ((p->if_flags & (IFF_RUNNING|IFF_OACTIVE)) == IFF_RUNNING)
263 			p->if_start(p);
264 	}
265 	ifp->if_flags &= ~IFF_OACTIVE;
266 
267 	return;
268 }
269 
270 /*
271  * vlan_input() returns 0 if it has consumed the packet, 1 otherwise.
272  */
273 int
274 vlan_input(eh, m)
275 	struct ether_header *eh;
276 	struct mbuf *m;
277 {
278 	struct ifvlan *ifv;
279 	u_int tag;
280 	struct ifnet *ifp = m->m_pkthdr.rcvif;
281 
282 	if (m->m_len < EVL_ENCAPLEN &&
283 	    (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
284 		ifp->if_ierrors++;
285 		return (0);
286 	}
287 
288 	tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)));
289 
290 	LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) {
291 		if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag)
292 			break;
293 	}
294 	if (ifv == NULL)
295 		return (1);
296 
297 	if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
298 	    (IFF_UP|IFF_RUNNING)) {
299 		m_freem(m);
300 		return (0);
301 	}
302 
303 	/*
304 	 * Having found a valid vlan interface corresponding to
305 	 * the given source interface and vlan tag, remove the
306 	 * encapsulation, and run the real packet through
307 	 * ether_input() a second time (it had better be
308 	 * reentrant!).
309 	 */
310 	m->m_pkthdr.rcvif = &ifv->ifv_if;
311 	eh->ether_type = mtod(m, u_int16_t *)[1];
312 	m->m_len -= EVL_ENCAPLEN;
313 	m->m_data += EVL_ENCAPLEN;
314 	m->m_pkthdr.len -= EVL_ENCAPLEN;
315 
316 #if NBPFILTER > 0
317 	if (ifv->ifv_if.if_bpf)
318 		bpf_mtap_hdr(ifv->ifv_if.if_bpf, (char *)eh, ETHER_HDR_LEN,
319 		    m, BPF_DIRECTION_IN);
320 #endif
321 	ifv->ifv_if.if_ipackets++;
322 	ether_input(&ifv->ifv_if, eh, m);
323 
324 	return (0);
325 }
326 
327 int
328 vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
329 {
330 	struct ifaddr *ifa1, *ifa2;
331 	struct sockaddr_dl *sdl1, *sdl2;
332 	int s;
333 
334 	if (p->if_type != IFT_ETHER)
335 		return EPROTONOSUPPORT;
336 	if (ifv->ifv_p)
337 		return EBUSY;
338 
339 	ifv->ifv_p = p;
340 
341 	if (p->if_capabilities & IFCAP_VLAN_MTU)
342 		ifv->ifv_if.if_mtu = p->if_mtu;
343 	else {
344 		/*
345 		 * This will be incompatible with strict
346 		 * 802.1Q implementations
347 		 */
348 		ifv->ifv_if.if_mtu = p->if_mtu - EVL_ENCAPLEN;
349 #ifdef DIAGNOSTIC
350 		printf("%s: initialized with non-standard mtu %lu (parent %s)\n",
351 		    ifv->ifv_if.if_xname, ifv->ifv_if.if_mtu,
352 		    ifv->ifv_p->if_xname);
353 #endif
354 	}
355 
356 	ifv->ifv_if.if_flags = p->if_flags &
357 	    (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
358 
359 	/*
360 	 * Inherit the if_type from the parent.  This allows us to
361 	 * participate in bridges of that type.
362 	 */
363 	ifv->ifv_if.if_type = p->if_type;
364 
365 	/*
366 	 * Inherit baudrate from the parent.  An SNMP agent would use this
367 	 * information.
368 	 */
369 	ifv->ifv_if.if_baudrate = p->if_baudrate;
370 
371 	/*
372 	 * If the parent interface can do hardware-assisted
373 	 * VLAN encapsulation, then propagate its hardware-
374 	 * assisted checksumming flags.
375 	 *
376 	 * If the card cannot handle hardware tagging, it cannot
377 	 * possibly compute the correct checksums for tagged packets.
378 	 *
379 	 * This brings up another possibility, do cards exist which
380 	 * have all of these capabilities but cannot utilize them together?
381 	 */
382 	if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
383 		ifv->ifv_if.if_capabilities = p->if_capabilities &
384 		    (IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4|
385 		    IFCAP_CSUM_UDPv4);
386 		/* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */
387 
388 	/*
389 	 * Set up our ``Ethernet address'' to reflect the underlying
390 	 * physical interface's.
391 	 */
392 	ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
393 	ifa2 = ifnet_addrs[p->if_index];
394 	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
395 	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
396 	sdl1->sdl_type = IFT_ETHER;
397 	sdl1->sdl_alen = ETHER_ADDR_LEN;
398 	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
399 	bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
400 
401 	ifv->ifv_tag = tag;
402 	s = splnet();
403 	LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list);
404 
405 	/* Register callback for physical link state changes */
406 	ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
407 	    vlan_vlandev_state, ifv);
408 
409 	/* Register callback if parent wants to unregister */
410 	ifv->dh_cookie = hook_establish(p->if_detachhooks, 1,
411 	    vlan_ifdetach, ifv);
412 
413 	vlan_vlandev_state(ifv);
414 	splx(s);
415 
416 	return 0;
417 }
418 
419 int
420 vlan_unconfig(struct ifnet *ifp)
421 {
422 	struct ifaddr *ifa;
423 	struct sockaddr_dl *sdl;
424 	struct ifvlan *ifv;
425 	struct ifnet *p;
426 	struct ifreq *ifr, *ifr_p;
427 	int s;
428 
429 	ifv = ifp->if_softc;
430 	p = ifv->ifv_p;
431 	if (p == NULL)
432 		return 0;
433 
434 	ifr = (struct ifreq *)&ifp->if_data;
435 	ifr_p = (struct ifreq *)&ifv->ifv_p->if_data;
436 
437 	s = splnet();
438 	LIST_REMOVE(ifv, ifv_list);
439 	if (ifv->lh_cookie != NULL)
440 		hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie);
441 	/* The cookie is NULL if disestablished externally */
442 	if (ifv->dh_cookie != NULL)
443 		hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
444 	splx(s);
445 
446 	/*
447  	 * Since the interface is being unconfigured, we need to
448 	 * empty the list of multicast groups that we may have joined
449 	 * while we were alive and remove them from the parent's list
450 	 * as well.
451 	 */
452 	vlan_ether_purgemulti(ifv);
453 
454 	/* Disconnect from parent. */
455 	ifv->ifv_p = NULL;
456 	ifv->ifv_if.if_mtu = ETHERMTU;
457 
458 	/* Clear our MAC address. */
459 	ifa = ifnet_addrs[ifv->ifv_if.if_index];
460 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
461 	sdl->sdl_type = IFT_ETHER;
462 	sdl->sdl_alen = ETHER_ADDR_LEN;
463 	bzero(LLADDR(sdl), ETHER_ADDR_LEN);
464 	bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
465 
466 	return 0;
467 }
468 
469 void
470 vlan_vlandev_state(void *v)
471 {
472 	struct ifvlan *ifv = v;
473 
474 	if (ifv->ifv_if.if_link_state == ifv->ifv_p->if_link_state)
475 		return;
476 
477 	ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
478 	ifv->ifv_if.if_baudrate = ifv->ifv_p->if_baudrate;
479 	if_link_state_change(&ifv->ifv_if);
480 }
481 
482 int
483 vlan_set_promisc(struct ifnet *ifp)
484 {
485 	struct ifvlan *ifv = ifp->if_softc;
486 	int error = 0;
487 
488 	if ((ifp->if_flags & IFF_PROMISC) != 0) {
489 		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
490 			error = ifpromisc(ifv->ifv_p, 1);
491 			if (error == 0)
492 				ifv->ifv_flags |= IFVF_PROMISC;
493 		}
494 	} else {
495 		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
496 			error = ifpromisc(ifv->ifv_p, 0);
497 			if (error == 0)
498 				ifv->ifv_flags &= ~IFVF_PROMISC;
499 		}
500 	}
501 
502 	return (0);
503 }
504 
505 int
506 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
507 {
508 	struct proc *p = curproc;	/* XXX */
509 	struct ifaddr *ifa;
510 	struct ifnet *pr;
511 	struct ifreq *ifr;
512 	struct ifvlan *ifv;
513 	struct vlanreq vlr;
514 	int error = 0, p_mtu = 0, s;
515 
516 	ifr = (struct ifreq *)data;
517 	ifa = (struct ifaddr *)data;
518 	ifv = ifp->if_softc;
519 
520 	switch (cmd) {
521 	case SIOCSIFADDR:
522 		if (ifv->ifv_p != NULL) {
523 			ifp->if_flags |= IFF_UP;
524 
525 			switch (ifa->ifa_addr->sa_family) {
526 #ifdef INET
527 			case AF_INET:
528 				arp_ifinit(&ifv->ifv_ac, ifa);
529 				break;
530 #endif
531 			default:
532 				break;
533 			}
534 		} else {
535 			error = EINVAL;
536 		}
537 		break;
538 
539 	case SIOCGIFADDR:
540 		{
541 			struct sockaddr *sa;
542 
543 			sa = (struct sockaddr *) &ifr->ifr_data;
544 			bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
545 			    (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
546 		}
547 		break;
548 
549 	case SIOCSIFMTU:
550 		if (ifv->ifv_p != NULL) {
551 			if (ifv->ifv_p->if_capabilities & IFCAP_VLAN_MTU)
552 				p_mtu = ifv->ifv_p->if_mtu;
553 			else
554 				p_mtu = ifv->ifv_p->if_mtu - EVL_ENCAPLEN;
555 
556 			if (ifr->ifr_mtu > p_mtu || ifr->ifr_mtu < ETHERMIN)
557 				error = EINVAL;
558 			else
559 				ifp->if_mtu = ifr->ifr_mtu;
560 		} else
561 			error = EINVAL;
562 
563 		break;
564 
565 	case SIOCSETVLAN:
566 		if ((error = suser(p, 0)) != 0)
567 			break;
568 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
569 			break;
570 		if (vlr.vlr_parent[0] == '\0') {
571 			s = splnet();
572 			vlan_unconfig(ifp);
573 			if (ifp->if_flags & IFF_UP)
574 				if_down(ifp);
575 			ifp->if_flags &= ~IFF_RUNNING;
576 			splx(s);
577 			break;
578 		}
579 		pr = ifunit(vlr.vlr_parent);
580 		if (pr == NULL) {
581 			error = ENOENT;
582 			break;
583 		}
584 		/*
585 		 * Don't let the caller set up a VLAN tag with
586 		 * anything except VLID bits.
587 		 */
588 		if (vlr.vlr_tag & ~EVL_VLID_MASK) {
589 			error = EINVAL;
590 			break;
591 		}
592 		error = vlan_config(ifv, pr, vlr.vlr_tag);
593 		if (error)
594 			break;
595 		ifp->if_flags |= IFF_RUNNING;
596 
597 		/* Update promiscuous mode, if necessary. */
598 		vlan_set_promisc(ifp);
599 		break;
600 
601 	case SIOCGETVLAN:
602 		bzero(&vlr, sizeof vlr);
603 		if (ifv->ifv_p) {
604 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
605 			    "%s", ifv->ifv_p->if_xname);
606 			vlr.vlr_tag = ifv->ifv_tag;
607 		}
608 		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
609 		break;
610 	case SIOCSETVLANPRIO:
611 		if ((error = suser(p, 0)) != 0)
612 			break;
613 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
614 			break;
615 		/*
616 		 * Don't let the caller set up a VLAN priority
617 		 * outside the range 0-7
618 		 */
619 		if (vlr.vlr_tag > EVL_PRIO_MAX) {
620 			error = EINVAL;
621 			break;
622 		}
623 		ifv->ifv_prio = vlr.vlr_tag;
624 		break;
625 	case SIOCGETVLANPRIO:
626 		bzero(&vlr, sizeof vlr);
627 		if (ifv->ifv_p)
628 			strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname,
629                             sizeof(vlr.vlr_parent));
630 		vlr.vlr_tag = ifv->ifv_prio;
631 		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
632 		break;
633 	case SIOCSIFFLAGS:
634 		/*
635 		 * For promiscuous mode, we enable promiscuous mode on
636 		 * the parent if we need promiscuous on the VLAN interface.
637 		 */
638 		if (ifv->ifv_p != NULL)
639 			error = vlan_set_promisc(ifp);
640 		break;
641 
642 	case SIOCADDMULTI:
643 		error = (ifv->ifv_p != NULL) ?
644 		    vlan_ether_addmulti(ifv, ifr) : EINVAL;
645 		break;
646 
647 	case SIOCDELMULTI:
648 		error = (ifv->ifv_p != NULL) ?
649 		    vlan_ether_delmulti(ifv, ifr) : EINVAL;
650 		break;
651 	default:
652 		error = EINVAL;
653 	}
654 	return error;
655 }
656 
657 
658 int
659 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
660 {
661 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
662 	struct vlan_mc_entry *mc;
663 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
664 	int error;
665 
666 	/* XXX: sa_len is too small for such comparison
667 	if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
668 		return (EINVAL);
669 	*/
670 
671 	error = ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
672 	if (error != ENETRESET)
673 		return (error);
674 
675 	/*
676 	 * This is new multicast address.  We have to tell parent
677 	 * about it.  Also, remember this multicast address so that
678 	 * we can delete them on unconfigure.
679 	 */
680 	MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
681 	    M_DEVBUF, M_NOWAIT);
682 	if (mc == NULL) {
683 		error = ENOMEM;
684 		goto alloc_failed;
685 	}
686 
687 	/*
688 	 * As ether_addmulti() returns ENETRESET, following two
689 	 * statement shouldn't fail.
690 	 */
691 	(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
692 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, mc->mc_enm);
693 	memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
694 	LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
695 
696 	error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr);
697 	if (error != 0)
698 		goto ioctl_failed;
699 
700 	return (error);
701 
702  ioctl_failed:
703 	LIST_REMOVE(mc, mc_entries);
704 	FREE(mc, M_DEVBUF);
705  alloc_failed:
706 	(void)ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
707 
708 	return (error);
709 }
710 
711 int
712 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
713 {
714 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
715 	struct ether_multi *enm;
716 	struct vlan_mc_entry *mc;
717 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
718 	int error;
719 
720 	/*
721 	 * Find a key to lookup vlan_mc_entry.  We have to do this
722 	 * before calling ether_delmulti for obvious reason.
723 	 */
724 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
725 		return (error);
726 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, enm);
727 	if (enm == NULL)
728 		return (EINVAL);
729 
730 	LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries)
731 		if (mc->mc_enm == enm)
732 			break;
733 
734 	/* We won't delete entries we didn't add */
735 	if (mc == NULL)
736 		return (EINVAL);
737 
738 	error = ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
739 	if (error != ENETRESET)
740 		return (error);
741 
742 	/* We no longer use this multicast address.  Tell parent so. */
743 	error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
744 	if (error == 0) {
745 		/* And forget about this address. */
746 		LIST_REMOVE(mc, mc_entries);
747 		FREE(mc, M_DEVBUF);
748 	} else
749 		(void)ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
750 	return (error);
751 }
752 
753 /*
754  * Delete any multicast address we have asked to add from parent
755  * interface.  Called when the vlan is being unconfigured.
756  */
757 void
758 vlan_ether_purgemulti(struct ifvlan *ifv)
759 {
760 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
761 	struct vlan_mc_entry *mc;
762 	union {
763 		struct ifreq ifreq;
764 		struct {
765 			char ifr_name[IFNAMSIZ];
766 			struct sockaddr_storage ifr_ss;
767 		} ifreq_storage;
768 	} ifreq;
769 	struct ifreq *ifr = &ifreq.ifreq;
770 
771 	memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
772 	while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
773 		memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
774 		(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
775 		LIST_REMOVE(mc, mc_entries);
776 		FREE(mc, M_DEVBUF);
777 	}
778 }
779