xref: /openbsd-src/sys/net/if_vlan.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: if_vlan.c,v 1.78 2009/01/27 15:56:58 naddy 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 u_long vlan_tagmask;
82 
83 #define TAG_HASH_SIZE	32
84 #define TAG_HASH(tag)	(tag & vlan_tagmask)
85 LIST_HEAD(, ifvlan)	*vlan_tagh;
86 
87 void	vlan_start (struct ifnet *ifp);
88 int	vlan_ioctl (struct ifnet *ifp, u_long cmd, caddr_t addr);
89 int	vlan_unconfig (struct ifnet *ifp);
90 int	vlan_config (struct ifvlan *, struct ifnet *, u_int16_t);
91 void	vlan_vlandev_state(void *);
92 void	vlanattach (int count);
93 int	vlan_set_promisc (struct ifnet *ifp);
94 int	vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
95 int	vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
96 void	vlan_ether_purgemulti(struct ifvlan *);
97 int	vlan_clone_create(struct if_clone *, int);
98 int	vlan_clone_destroy(struct ifnet *);
99 void	vlan_ifdetach(void *);
100 
101 struct if_clone vlan_cloner =
102     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
103 
104 /* ARGSUSED */
105 void
106 vlanattach(int count)
107 {
108 	vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, &vlan_tagmask);
109 	if (vlan_tagh == NULL)
110 		panic("vlanattach: hashinit");
111 
112 	if_clone_attach(&vlan_cloner);
113 }
114 
115 int
116 vlan_clone_create(struct if_clone *ifc, int unit)
117 {
118 	struct ifvlan *ifv;
119 	struct ifnet *ifp;
120 
121 	ifv = malloc(sizeof(*ifv), M_DEVBUF, M_NOWAIT|M_ZERO);
122 	if (!ifv)
123 		return (ENOMEM);
124 
125 	LIST_INIT(&ifv->vlan_mc_listhead);
126 	ifp = &ifv->ifv_if;
127 	ifp->if_softc = ifv;
128 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
129 	    unit);
130 	/* NB: flags are not set here */
131 	/* NB: mtu is not set here */
132 
133 	ifp->if_start = vlan_start;
134 	ifp->if_ioctl = vlan_ioctl;
135 	ifp->if_output = ether_output;
136 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
137 	IFQ_SET_READY(&ifp->if_snd);
138 	if_attach(ifp);
139 	ether_ifattach(ifp);
140 	/* Now undo some of the damage... */
141 	ifp->if_type = IFT_L2VLAN;
142 	ifp->if_hdrlen = EVL_ENCAPLEN;
143 
144 	return (0);
145 }
146 
147 int
148 vlan_clone_destroy(struct ifnet *ifp)
149 {
150 	struct ifvlan *ifv = ifp->if_softc;
151 
152 	vlan_unconfig(ifp);
153 	ether_ifdetach(ifp);
154 	if_detach(ifp);
155 
156 	free(ifv, M_DEVBUF);
157 	return (0);
158 }
159 
160 void
161 vlan_ifdetach(void *ptr)
162 {
163 	struct ifvlan *ifv = (struct ifvlan *)ptr;
164 	/*
165 	 * Destroy the vlan interface because the parent has been
166 	 * detached. Set the dh_cookie to NULL because we're running
167 	 * inside of dohooks which is told to disestablish the hook
168 	 * for us (otherwise we would kill the TAILQ element...).
169 	 */
170 	ifv->dh_cookie = NULL;
171 	vlan_clone_destroy(&ifv->ifv_if);
172 }
173 
174 void
175 vlan_start(struct ifnet *ifp)
176 {
177 	struct ifvlan *ifv;
178 	struct ifnet *p;
179 	struct mbuf *m;
180 	int error;
181 
182 	ifv = ifp->if_softc;
183 	p = ifv->ifv_p;
184 
185 	ifp->if_flags |= IFF_OACTIVE;
186 	for (;;) {
187 		IFQ_DEQUEUE(&ifp->if_snd, m);
188 		if (m == NULL)
189 			break;
190 
191 		if ((p->if_flags & (IFF_UP|IFF_RUNNING)) !=
192 		    (IFF_UP|IFF_RUNNING)) {
193 			IF_DROP(&p->if_snd);
194 				/* XXX stats */
195 			ifp->if_oerrors++;
196 			m_freem(m);
197 			continue;
198 		}
199 
200 #if NBPFILTER > 0
201 		if (ifp->if_bpf)
202 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
203 #endif
204 
205 		/*
206 		 * If the IFCAP_VLAN_HWTAGGING capability is set on the parent,
207 		 * it can do VLAN tag insertion itself and doesn't require us
208 	 	 * to create a special header for it. In this case, we just pass
209 		 * the packet along.
210 		 */
211 		if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
212 			m->m_pkthdr.ether_vtag = ifv->ifv_tag +
213 			    (ifv->ifv_prio << EVL_PRIO_BITS);
214 			m->m_flags |= M_VLANTAG;
215 		} else {
216 			struct ether_vlan_header evh;
217 
218 			m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh);
219 			evh.evl_proto = evh.evl_encap_proto;
220 			evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
221 			evh.evl_tag = htons(ifv->ifv_tag +
222 			    (ifv->ifv_prio << EVL_PRIO_BITS));
223 
224 			m_adj(m, ETHER_HDR_LEN);
225 			M_PREPEND(m, sizeof(evh), M_DONTWAIT);
226 			if (m == NULL) {
227 				ifp->if_oerrors++;
228 				continue;
229 			}
230 
231 			m_copyback(m, 0, sizeof(evh), &evh);
232 		}
233 
234 		/*
235 		 * Send it, precisely as ether_output() would have.
236 		 * We are already running at splnet.
237 		 */
238 		p->if_obytes += m->m_pkthdr.len;
239 		if (m->m_flags & M_MCAST)
240 			p->if_omcasts++;
241 		IFQ_ENQUEUE(&p->if_snd, m, NULL, error);
242 		if (error) {
243 			/* mbuf is already freed */
244 			ifp->if_oerrors++;
245 			continue;
246 		}
247 
248 		ifp->if_opackets++;
249 		if_start(p);
250 	}
251 	ifp->if_flags &= ~IFF_OACTIVE;
252 
253 	return;
254 }
255 
256 /*
257  * vlan_input() returns 0 if it has consumed the packet, 1 otherwise.
258  */
259 int
260 vlan_input(eh, m)
261 	struct ether_header *eh;
262 	struct mbuf *m;
263 {
264 	struct ifvlan *ifv;
265 	u_int tag;
266 	struct ifnet *ifp = m->m_pkthdr.rcvif;
267 
268 	if (m->m_flags & M_VLANTAG) {
269 		tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
270 	} else {
271 		if (m->m_len < EVL_ENCAPLEN &&
272 		    (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
273 			ifp->if_ierrors++;
274 			return (0);
275 		}
276 
277 		tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)));
278 	}
279 
280 	LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) {
281 		if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag)
282 			break;
283 	}
284 	if (ifv == NULL)
285 		return (1);
286 
287 	if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
288 	    (IFF_UP|IFF_RUNNING)) {
289 		m_freem(m);
290 		return (0);
291 	}
292 
293 	/*
294 	 * Having found a valid vlan interface corresponding to
295 	 * the given source interface and vlan tag, remove the
296 	 * encapsulation, and run the real packet through
297 	 * ether_input() a second time (it had better be
298 	 * reentrant!).
299 	 */
300 	m->m_pkthdr.rcvif = &ifv->ifv_if;
301 	if (m->m_flags & M_VLANTAG) {
302 		m->m_flags &= ~M_VLANTAG;
303 	} else {
304 		eh->ether_type = mtod(m, u_int16_t *)[1];
305 		m->m_len -= EVL_ENCAPLEN;
306 		m->m_data += EVL_ENCAPLEN;
307 		m->m_pkthdr.len -= EVL_ENCAPLEN;
308 	}
309 
310 #if NBPFILTER > 0
311 	if (ifv->ifv_if.if_bpf)
312 		bpf_mtap_hdr(ifv->ifv_if.if_bpf, (char *)eh, ETHER_HDR_LEN,
313 		    m, BPF_DIRECTION_IN);
314 #endif
315 
316 	/*
317 	 * Drop promiscuously received packets if we are not in
318 	 * promiscuous mode.
319 	 */
320 	if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 &&
321 	    (ifp->if_flags & IFF_PROMISC) &&
322 	    (ifv->ifv_if.if_flags & IFF_PROMISC) == 0) {
323 		struct arpcom *ac = &ifv->ifv_ac;
324 		if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN)) {
325 			m_freem(m);
326 			return (0);
327 		}
328 	}
329 
330 	ifv->ifv_if.if_ipackets++;
331 	ether_input(&ifv->ifv_if, eh, m);
332 
333 	return (0);
334 }
335 
336 int
337 vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
338 {
339 	struct ifaddr *ifa1, *ifa2;
340 	struct sockaddr_dl *sdl1, *sdl2;
341 	int s;
342 
343 	if (p->if_type != IFT_ETHER)
344 		return EPROTONOSUPPORT;
345 	if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */
346 		return (0);
347 	if (ifv->ifv_p)
348 		return EBUSY;
349 
350 	ifv->ifv_p = p;
351 
352 	if (p->if_capabilities & IFCAP_VLAN_MTU)
353 		ifv->ifv_if.if_mtu = p->if_mtu;
354 	else {
355 		/*
356 		 * This will be incompatible with strict
357 		 * 802.1Q implementations
358 		 */
359 		ifv->ifv_if.if_mtu = p->if_mtu - EVL_ENCAPLEN;
360 #ifdef DIAGNOSTIC
361 		printf("%s: initialized with non-standard mtu %lu (parent %s)\n",
362 		    ifv->ifv_if.if_xname, ifv->ifv_if.if_mtu,
363 		    ifv->ifv_p->if_xname);
364 #endif
365 	}
366 
367 	ifv->ifv_if.if_flags = p->if_flags &
368 	    (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
369 
370 	/*
371 	 * Inherit the if_type from the parent.  This allows us to
372 	 * participate in bridges of that type.
373 	 */
374 	ifv->ifv_if.if_type = p->if_type;
375 
376 	/*
377 	 * Inherit baudrate from the parent.  An SNMP agent would use this
378 	 * information.
379 	 */
380 	ifv->ifv_if.if_baudrate = p->if_baudrate;
381 
382 	/*
383 	 * If the parent interface can do hardware-assisted
384 	 * VLAN encapsulation, then propagate its hardware-
385 	 * assisted checksumming flags.
386 	 *
387 	 * If the card cannot handle hardware tagging, it cannot
388 	 * possibly compute the correct checksums for tagged packets.
389 	 *
390 	 * This brings up another possibility, do cards exist which
391 	 * have all of these capabilities but cannot utilize them together?
392 	 */
393 	if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
394 		ifv->ifv_if.if_capabilities = p->if_capabilities &
395 		    (IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4|
396 		    IFCAP_CSUM_UDPv4);
397 		/* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */
398 
399 	/*
400 	 * Set up our ``Ethernet address'' to reflect the underlying
401 	 * physical interface's.
402 	 */
403 	ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
404 	ifa2 = ifnet_addrs[p->if_index];
405 	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
406 	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
407 	sdl1->sdl_type = IFT_ETHER;
408 	sdl1->sdl_alen = ETHER_ADDR_LEN;
409 	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
410 	bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
411 
412 	ifv->ifv_tag = tag;
413 	s = splnet();
414 	LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list);
415 
416 	/* Register callback for physical link state changes */
417 	ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
418 	    vlan_vlandev_state, ifv);
419 
420 	/* Register callback if parent wants to unregister */
421 	ifv->dh_cookie = hook_establish(p->if_detachhooks, 1,
422 	    vlan_ifdetach, ifv);
423 
424 	vlan_vlandev_state(ifv);
425 	splx(s);
426 
427 	return 0;
428 }
429 
430 int
431 vlan_unconfig(struct ifnet *ifp)
432 {
433 	struct ifaddr *ifa;
434 	struct sockaddr_dl *sdl;
435 	struct ifvlan *ifv;
436 	struct ifnet *p;
437 	int s;
438 
439 	ifv = ifp->if_softc;
440 	p = ifv->ifv_p;
441 	if (p == NULL)
442 		return 0;
443 
444 	s = splnet();
445 	LIST_REMOVE(ifv, ifv_list);
446 	if (ifv->lh_cookie != NULL)
447 		hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie);
448 	/* The cookie is NULL if disestablished externally */
449 	if (ifv->dh_cookie != NULL)
450 		hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
451 	splx(s);
452 
453 	/*
454  	 * Since the interface is being unconfigured, we need to
455 	 * empty the list of multicast groups that we may have joined
456 	 * while we were alive and remove them from the parent's list
457 	 * as well.
458 	 */
459 	vlan_ether_purgemulti(ifv);
460 
461 	/* Disconnect from parent. */
462 	ifv->ifv_p = NULL;
463 	ifv->ifv_if.if_mtu = ETHERMTU;
464 
465 	/* Clear our MAC address. */
466 	ifa = ifnet_addrs[ifv->ifv_if.if_index];
467 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
468 	sdl->sdl_type = IFT_ETHER;
469 	sdl->sdl_alen = ETHER_ADDR_LEN;
470 	bzero(LLADDR(sdl), ETHER_ADDR_LEN);
471 	bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
472 
473 	return 0;
474 }
475 
476 void
477 vlan_vlandev_state(void *v)
478 {
479 	struct ifvlan *ifv = v;
480 
481 	if (ifv->ifv_if.if_link_state == ifv->ifv_p->if_link_state)
482 		return;
483 
484 	ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
485 	ifv->ifv_if.if_baudrate = ifv->ifv_p->if_baudrate;
486 	if_link_state_change(&ifv->ifv_if);
487 }
488 
489 int
490 vlan_set_promisc(struct ifnet *ifp)
491 {
492 	struct ifvlan *ifv = ifp->if_softc;
493 	int error = 0;
494 
495 	if ((ifp->if_flags & IFF_PROMISC) != 0) {
496 		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
497 			error = ifpromisc(ifv->ifv_p, 1);
498 			if (error == 0)
499 				ifv->ifv_flags |= IFVF_PROMISC;
500 		}
501 	} else {
502 		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
503 			error = ifpromisc(ifv->ifv_p, 0);
504 			if (error == 0)
505 				ifv->ifv_flags &= ~IFVF_PROMISC;
506 		}
507 	}
508 
509 	return (0);
510 }
511 
512 int
513 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
514 {
515 	struct proc *p = curproc;	/* XXX */
516 	struct ifaddr *ifa;
517 	struct ifnet *pr;
518 	struct ifreq *ifr;
519 	struct ifvlan *ifv;
520 	struct vlanreq vlr;
521 	int error = 0, p_mtu = 0, s;
522 
523 	ifr = (struct ifreq *)data;
524 	ifa = (struct ifaddr *)data;
525 	ifv = ifp->if_softc;
526 
527 	switch (cmd) {
528 	case SIOCSIFADDR:
529 		if (ifv->ifv_p != NULL) {
530 			ifp->if_flags |= IFF_UP;
531 
532 			switch (ifa->ifa_addr->sa_family) {
533 #ifdef INET
534 			case AF_INET:
535 				arp_ifinit(&ifv->ifv_ac, ifa);
536 				break;
537 #endif
538 			default:
539 				break;
540 			}
541 		} else {
542 			error = EINVAL;
543 		}
544 		break;
545 
546 	case SIOCGIFADDR:
547 		{
548 			struct sockaddr *sa;
549 
550 			sa = (struct sockaddr *) &ifr->ifr_data;
551 			bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
552 			    (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
553 		}
554 		break;
555 
556 	case SIOCSIFMTU:
557 		if (ifv->ifv_p != NULL) {
558 			if (ifv->ifv_p->if_capabilities & IFCAP_VLAN_MTU)
559 				p_mtu = ifv->ifv_p->if_mtu;
560 			else
561 				p_mtu = ifv->ifv_p->if_mtu - EVL_ENCAPLEN;
562 
563 			if (ifr->ifr_mtu > p_mtu || ifr->ifr_mtu < ETHERMIN)
564 				error = EINVAL;
565 			else
566 				ifp->if_mtu = ifr->ifr_mtu;
567 		} else
568 			error = EINVAL;
569 
570 		break;
571 
572 	case SIOCSETVLAN:
573 		if ((error = suser(p, 0)) != 0)
574 			break;
575 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
576 			break;
577 		if (vlr.vlr_parent[0] == '\0') {
578 			s = splnet();
579 			vlan_unconfig(ifp);
580 			if (ifp->if_flags & IFF_UP)
581 				if_down(ifp);
582 			ifp->if_flags &= ~IFF_RUNNING;
583 			splx(s);
584 			break;
585 		}
586 		pr = ifunit(vlr.vlr_parent);
587 		if (pr == NULL) {
588 			error = ENOENT;
589 			break;
590 		}
591 		/*
592 		 * Don't let the caller set up a VLAN tag with
593 		 * anything except VLID bits.
594 		 */
595 		if (vlr.vlr_tag & ~EVL_VLID_MASK) {
596 			error = EINVAL;
597 			break;
598 		}
599 		error = vlan_config(ifv, pr, vlr.vlr_tag);
600 		if (error)
601 			break;
602 		ifp->if_flags |= IFF_RUNNING;
603 
604 		/* Update promiscuous mode, if necessary. */
605 		vlan_set_promisc(ifp);
606 		break;
607 
608 	case SIOCGETVLAN:
609 		bzero(&vlr, sizeof vlr);
610 		if (ifv->ifv_p) {
611 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
612 			    "%s", ifv->ifv_p->if_xname);
613 			vlr.vlr_tag = ifv->ifv_tag;
614 		}
615 		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
616 		break;
617 	case SIOCSETVLANPRIO:
618 		if ((error = suser(p, 0)) != 0)
619 			break;
620 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
621 			break;
622 		/*
623 		 * Don't let the caller set up a VLAN priority
624 		 * outside the range 0-7
625 		 */
626 		if (vlr.vlr_tag > EVL_PRIO_MAX) {
627 			error = EINVAL;
628 			break;
629 		}
630 		ifv->ifv_prio = vlr.vlr_tag;
631 		break;
632 	case SIOCGETVLANPRIO:
633 		bzero(&vlr, sizeof vlr);
634 		if (ifv->ifv_p)
635 			strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname,
636                             sizeof(vlr.vlr_parent));
637 		vlr.vlr_tag = ifv->ifv_prio;
638 		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
639 		break;
640 	case SIOCSIFFLAGS:
641 		/*
642 		 * For promiscuous mode, we enable promiscuous mode on
643 		 * the parent if we need promiscuous on the VLAN interface.
644 		 */
645 		if (ifv->ifv_p != NULL)
646 			error = vlan_set_promisc(ifp);
647 		break;
648 
649 	case SIOCADDMULTI:
650 		error = (ifv->ifv_p != NULL) ?
651 		    vlan_ether_addmulti(ifv, ifr) : EINVAL;
652 		break;
653 
654 	case SIOCDELMULTI:
655 		error = (ifv->ifv_p != NULL) ?
656 		    vlan_ether_delmulti(ifv, ifr) : EINVAL;
657 		break;
658 	default:
659 		error = ENOTTY;
660 	}
661 	return error;
662 }
663 
664 
665 int
666 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
667 {
668 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
669 	struct vlan_mc_entry *mc;
670 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
671 	int error;
672 
673 	/* XXX: sa_len is too small for such comparison
674 	if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
675 		return (EINVAL);
676 	*/
677 
678 	error = ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
679 	if (error != ENETRESET)
680 		return (error);
681 
682 	/*
683 	 * This is new multicast address.  We have to tell parent
684 	 * about it.  Also, remember this multicast address so that
685 	 * we can delete them on unconfigure.
686 	 */
687 	mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT);
688 	if (mc == NULL) {
689 		error = ENOMEM;
690 		goto alloc_failed;
691 	}
692 
693 	/*
694 	 * As ether_addmulti() returns ENETRESET, following two
695 	 * statement shouldn't fail.
696 	 */
697 	(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
698 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, mc->mc_enm);
699 	memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
700 	LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
701 
702 	error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr);
703 	if (error != 0)
704 		goto ioctl_failed;
705 
706 	return (error);
707 
708  ioctl_failed:
709 	LIST_REMOVE(mc, mc_entries);
710 	free(mc, M_DEVBUF);
711  alloc_failed:
712 	(void)ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
713 
714 	return (error);
715 }
716 
717 int
718 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
719 {
720 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
721 	struct ether_multi *enm;
722 	struct vlan_mc_entry *mc;
723 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
724 	int error;
725 
726 	/*
727 	 * Find a key to lookup vlan_mc_entry.  We have to do this
728 	 * before calling ether_delmulti for obvious reason.
729 	 */
730 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
731 		return (error);
732 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, enm);
733 	if (enm == NULL)
734 		return (EINVAL);
735 
736 	LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries)
737 		if (mc->mc_enm == enm)
738 			break;
739 
740 	/* We won't delete entries we didn't add */
741 	if (mc == NULL)
742 		return (EINVAL);
743 
744 	error = ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
745 	if (error != ENETRESET)
746 		return (error);
747 
748 	/* We no longer use this multicast address.  Tell parent so. */
749 	error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
750 	if (error == 0) {
751 		/* And forget about this address. */
752 		LIST_REMOVE(mc, mc_entries);
753 		free(mc, M_DEVBUF);
754 	} else
755 		(void)ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
756 	return (error);
757 }
758 
759 /*
760  * Delete any multicast address we have asked to add from parent
761  * interface.  Called when the vlan is being unconfigured.
762  */
763 void
764 vlan_ether_purgemulti(struct ifvlan *ifv)
765 {
766 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
767 	struct vlan_mc_entry *mc;
768 	union {
769 		struct ifreq ifreq;
770 		struct {
771 			char ifr_name[IFNAMSIZ];
772 			struct sockaddr_storage ifr_ss;
773 		} ifreq_storage;
774 	} ifreq;
775 	struct ifreq *ifr = &ifreq.ifreq;
776 
777 	memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
778 	while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
779 		memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
780 		(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
781 		LIST_REMOVE(mc, mc_entries);
782 		free(mc, M_DEVBUF);
783 	}
784 }
785