xref: /netbsd-src/sys/net/if_vlan.c (revision bcc8ec9959e7b01e313d813067bfb43a3ad70551)
1 /*	$NetBSD: if_vlan.c,v 1.26 2000/12/18 19:36:41 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran, and by Jason R. Thorpe of Zembu Labs, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright 1998 Massachusetts Institute of Technology
41  *
42  * Permission to use, copy, modify, and distribute this software and
43  * its documentation for any purpose and without fee is hereby
44  * granted, provided that both the above copyright notice and this
45  * permission notice appear in all copies, that both the above
46  * copyright notice and this permission notice appear in all
47  * supporting documentation, and that the name of M.I.T. not be used
48  * in advertising or publicity pertaining to distribution of the
49  * software without specific, written prior permission.  M.I.T. makes
50  * no representations about the suitability of this software for any
51  * purpose.  It is provided "as is" without express or implied
52  * warranty.
53  *
54  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
55  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
56  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
57  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
58  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
60  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
61  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
62  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
63  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
64  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  *
67  * from FreeBSD: if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp
68  * via OpenBSD: if_vlan.c,v 1.4 2000/05/15 19:15:00 chris Exp
69  */
70 
71 /*
72  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.  Might be
73  * extended some day to also handle IEEE 802.1P priority tagging.  This is
74  * sort of sneaky in the implementation, since we need to pretend to be
75  * enough of an Ethernet implementation to make ARP work.  The way we do
76  * this is by telling everyone that we are an Ethernet interface, and then
77  * catch the packets that ether_output() left on our output queue when it
78  * calls if_start(), rewrite them for use by the real outgoing interface,
79  * and ask it to send them.
80  *
81  * TODO:
82  *
83  *	- Need some way to notify vlan interfaces when the parent
84  *	  interface changes MTU.
85  */
86 
87 #include "opt_inet.h"
88 #include "bpfilter.h"
89 
90 #include <sys/param.h>
91 #include <sys/kernel.h>
92 #include <sys/mbuf.h>
93 #include <sys/queue.h>
94 #include <sys/socket.h>
95 #include <sys/sockio.h>
96 #include <sys/systm.h>
97 #include <sys/proc.h>
98 
99 #if NBPFILTER > 0
100 #include <net/bpf.h>
101 #endif
102 #include <net/if.h>
103 #include <net/if_dl.h>
104 #include <net/if_types.h>
105 #include <net/if_ether.h>
106 #include <net/if_vlanvar.h>
107 
108 #ifdef INET
109 #include <netinet/in.h>
110 #include <netinet/if_inarp.h>
111 #endif
112 
113 extern struct	ifaddr **ifnet_addrs;	/* XXX if.c */
114 
115 struct vlan_mc_entry {
116 	LIST_ENTRY(vlan_mc_entry)	mc_entries;
117 	/*
118 	 * A key to identify this entry.  The mc_addr below can't be
119 	 * used since multiple sockaddr may mapped into the same
120 	 * ether_multi (e.g., AF_UNSPEC).
121 	 */
122 	union {
123 		struct ether_multi	*mcu_enm;
124 	} mc_u;
125 	struct sockaddr_storage		mc_addr;
126 };
127 
128 #define	mc_enm		mc_u.mcu_enm
129 
130 struct ifvlan {
131 	union {
132 		struct ethercom ifvu_ec;
133 	} ifv_u;
134 	struct ifnet *ifv_p;	/* parent interface of this vlan */
135 	struct ifv_linkmib {
136 		const struct vlan_multisw *ifvm_msw;
137 		int	ifvm_encaplen;	/* encapsulation length */
138 		int	ifvm_mtufudge;	/* MTU fudged by this much */
139 		int	ifvm_mintu;	/* min transmission unit */
140 		u_int16_t ifvm_proto;	/* encapsulation ethertype */
141 		u_int16_t ifvm_tag;	/* tag to apply on packets */
142 	} ifv_mib;
143 	LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
144 	LIST_ENTRY(ifvlan) ifv_list;
145 	int ifv_flags;
146 };
147 
148 #define	IFVF_PROMISC	0x01		/* promiscuous mode enabled */
149 
150 #define	ifv_ec		ifv_u.ifvu_ec
151 
152 #define	ifv_if		ifv_ec.ec_if
153 
154 #define	ifv_msw		ifv_mib.ifvm_msw
155 #define	ifv_encaplen	ifv_mib.ifvm_encaplen
156 #define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
157 #define	ifv_mintu	ifv_mib.ifvm_mintu
158 #define	ifv_tag		ifv_mib.ifvm_tag
159 
160 struct vlan_multisw {
161 	int	(*vmsw_addmulti)(struct ifvlan *, struct ifreq *);
162 	int	(*vmsw_delmulti)(struct ifvlan *, struct ifreq *);
163 	void	(*vmsw_purgemulti)(struct ifvlan *);
164 };
165 
166 static int	vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
167 static int	vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
168 static void	vlan_ether_purgemulti(struct ifvlan *);
169 
170 const struct vlan_multisw vlan_ether_multisw = {
171 	vlan_ether_addmulti,
172 	vlan_ether_delmulti,
173 	vlan_ether_purgemulti,
174 };
175 
176 static int	vlan_clone_create(struct if_clone *, int);
177 static void	vlan_clone_destroy(struct ifnet *);
178 static int	vlan_config(struct ifvlan *, struct ifnet *);
179 static int	vlan_ioctl(struct ifnet *, u_long, caddr_t);
180 static void	vlan_start(struct ifnet *);
181 static void	vlan_unconfig(struct ifnet *);
182 
183 void		vlanattach(int);
184 
185 /* XXX This should be a hash table with the tag as the basis of the key. */
186 static LIST_HEAD(, ifvlan) ifv_list;
187 
188 struct if_clone vlan_cloner =
189     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
190 
191 void
192 vlanattach(int n)
193 {
194 
195 	LIST_INIT(&ifv_list);
196 	if_clone_attach(&vlan_cloner);
197 }
198 
199 static int
200 vlan_clone_create(struct if_clone *ifc, int unit)
201 {
202 	struct ifvlan *ifv;
203 	struct ifnet *ifp;
204 	int s;
205 
206 	ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK);
207 	memset(ifv, 0, sizeof(struct ifvlan));
208 	ifp = &ifv->ifv_if;
209 	LIST_INIT(&ifv->ifv_mc_listhead);
210 
211 	s = splnet();
212 	LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
213 	splx(s);
214 
215 	sprintf(ifp->if_xname, "%s%d", ifc->ifc_name, unit);
216 	ifp->if_softc = ifv;
217 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
218 	ifp->if_start = vlan_start;
219 	ifp->if_ioctl = vlan_ioctl;
220 
221 	if_attach(ifp);
222 
223 	return (0);
224 }
225 
226 static void
227 vlan_clone_destroy(struct ifnet *ifp)
228 {
229 	struct ifvlan *ifv = ifp->if_softc;
230 	int s;
231 
232 	s = splnet();
233 	LIST_REMOVE(ifv, ifv_list);
234 	vlan_unconfig(ifp);
235 	splx(s);
236 
237 	if_detach(ifp);
238 	free(ifv, M_DEVBUF);
239 }
240 
241 /*
242  * Configure a VLAN interface.  Must be called at splnet().
243  */
244 static int
245 vlan_config(struct ifvlan *ifv, struct ifnet *p)
246 {
247 	struct ifnet *ifp = &ifv->ifv_if;
248 	int error;
249 
250 	if (ifv->ifv_p != NULL)
251 		return (EBUSY);
252 
253 	switch (p->if_type) {
254 	case IFT_ETHER:
255 	    {
256 		struct ethercom *ec = (void *) p;
257 
258 		ifv->ifv_msw = &vlan_ether_multisw;
259 		ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
260 		ifv->ifv_mintu = ETHERMIN;
261 
262 		/*
263 		 * If the parent supports the VLAN_MTU capability,
264 		 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
265 		 * enable it.
266 		 */
267 		if (ec->ec_nvlans++ == 0 &&
268 		    (ec->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) {
269 			/*
270 			 * Enable Tx/Rx of VLAN-sized frames.
271 			 */
272 			ec->ec_capenable |= ETHERCAP_VLAN_MTU;
273 			if (p->if_flags & IFF_UP) {
274 				struct ifreq ifr;
275 
276 				ifr.ifr_flags = p->if_flags;
277 				error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
278 				    (caddr_t) &ifr);
279 				if (error) {
280 					if (ec->ec_nvlans-- == 1)
281 						ec->ec_capenable &=
282 						    ~ETHERCAP_VLAN_MTU;
283 					return (error);
284 				}
285 			}
286 			ifv->ifv_mtufudge = 0;
287 		} else if ((ec->ec_capabilities & ETHERCAP_VLAN_MTU) == 0) {
288 			/*
289 			 * Fudge the MTU by the encapsulation size.  This
290 			 * makes us incompatible with strictly compliant
291 			 * 802.1Q implementations, but allows us to use
292 			 * the feature with other NetBSD implementations,
293 			 * which might still be useful.
294 			 */
295 			ifv->ifv_mtufudge = ifv->ifv_encaplen;
296 		}
297 
298 		/*
299 		 * We inherit the parent's Ethernet address.
300 		 */
301 		ether_ifattach(ifp, LLADDR(p->if_sadl));
302 		ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */
303 		break;
304 	    }
305 
306 	default:
307 		return (EPROTONOSUPPORT);
308 	}
309 
310 	ifv->ifv_p = p;
311 	ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
312 	ifv->ifv_if.if_flags = p->if_flags &
313 	    (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
314 
315 	/*
316 	 * Inherit the if_type from the parent.  This allows us
317 	 * to participate in bridges of that type.
318 	 */
319 	ifv->ifv_if.if_type = p->if_type;
320 
321 	return (0);
322 }
323 
324 /*
325  * Unconfigure a VLAN interface.  Must be called at splnet().
326  */
327 static void
328 vlan_unconfig(struct ifnet *ifp)
329 {
330 	struct ifvlan *ifv = ifp->if_softc;
331 
332 	if (ifv->ifv_p == NULL)
333 		return;
334 
335 	/*
336  	 * Since the interface is being unconfigured, we need to empty the
337 	 * list of multicast groups that we may have joined while we were
338 	 * alive and remove them from the parent's list also.
339 	 */
340 	(*ifv->ifv_msw->vmsw_purgemulti)(ifv);
341 
342 	/* Disconnect from parent. */
343 	switch (ifv->ifv_p->if_type) {
344 	case IFT_ETHER:
345 	    {
346 		struct ethercom *ec = (void *) ifv->ifv_p;
347 
348 		if (ec->ec_nvlans-- == 1) {
349 			/*
350 			 * Disable Tx/Rx of VLAN-sized frames.
351 			 */
352 			ec->ec_capenable &= ~ETHERCAP_VLAN_MTU;
353 			if (ifv->ifv_p->if_flags & IFF_UP) {
354 				struct ifreq ifr;
355 
356 				ifr.ifr_flags = ifv->ifv_p->if_flags;
357 				(void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
358 				    SIOCSIFFLAGS, (caddr_t) &ifr);
359 			}
360 		}
361 
362 		ether_ifdetach(ifp);
363 		break;
364 	    }
365 
366 #ifdef DIAGNOSTIC
367 	default:
368 		panic("vlan_unconfig: impossible");
369 #endif
370 	}
371 
372 	ifv->ifv_p = NULL;
373 	ifv->ifv_if.if_mtu = 0;
374 	ifv->ifv_flags = 0;
375 
376 	if_down(ifp);
377 	ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
378 }
379 
380 /*
381  * Called when a parent interface is detaching; destroy any VLAN
382  * configuration for the parent interface.
383  */
384 void
385 vlan_ifdetach(struct ifnet *p)
386 {
387 	struct ifvlan *ifv;
388 	int s;
389 
390 	s = splnet();
391 
392 	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
393 	     ifv = LIST_NEXT(ifv, ifv_list)) {
394 		if (ifv->ifv_p == p)
395 			vlan_unconfig(&ifv->ifv_if);
396 	}
397 
398 	splx(s);
399 }
400 
401 static int
402 vlan_set_promisc(struct ifnet *ifp)
403 {
404 	struct ifvlan *ifv = ifp->if_softc;
405 	int error = 0;
406 
407 	if ((ifp->if_flags & IFF_PROMISC) != 0) {
408 		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
409 			error = ifpromisc(ifv->ifv_p, 1);
410 			if (error == 0)
411 				ifv->ifv_flags |= IFVF_PROMISC;
412 		}
413 	} else {
414 		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
415 			error = ifpromisc(ifv->ifv_p, 0);
416 			if (error == 0)
417 				ifv->ifv_flags &= ~IFVF_PROMISC;
418 		}
419 	}
420 
421 	return (error);
422 }
423 
424 static int
425 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
426 {
427 	struct proc *p = curproc;	/* XXX */
428 	struct ifvlan *ifv = ifp->if_softc;
429 	struct ifaddr *ifa = (struct ifaddr *) data;
430 	struct ifreq *ifr = (struct ifreq *) data;
431 	struct ifnet *pr;
432 	struct vlanreq vlr;
433 	struct sockaddr *sa;
434 	int s, error = 0;
435 
436 	s = splnet();
437 
438 	switch (cmd) {
439 	case SIOCSIFADDR:
440 		if (ifv->ifv_p != NULL) {
441 			ifp->if_flags |= IFF_UP;
442 
443 			switch (ifa->ifa_addr->sa_family) {
444 #ifdef INET
445 			case AF_INET:
446 				arp_ifinit(ifp, ifa);
447 				break;
448 #endif
449 			default:
450 				break;
451 			}
452 		} else {
453 			error = EINVAL;
454 		}
455 		break;
456 
457 	case SIOCGIFADDR:
458 		sa = (struct sockaddr *)&ifr->ifr_data;
459 		memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
460 		break;
461 
462 	case SIOCSIFMTU:
463 		if (ifv->ifv_p != NULL) {
464 			if (ifr->ifr_mtu >
465 			     (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
466 			    ifr->ifr_mtu <
467 			     (ifv->ifv_mintu - ifv->ifv_mtufudge))
468 				error = EINVAL;
469 			else
470 				ifp->if_mtu = ifr->ifr_mtu;
471 		} else
472 			error = EINVAL;
473 		break;
474 
475 	case SIOCSETVLAN:
476 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
477 			break;
478 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
479 			break;
480 		if (vlr.vlr_parent[0] == '\0') {
481 			vlan_unconfig(ifp);
482 			break;
483 		}
484 		if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
485 			error = EINVAL;		 /* check for valid tag */
486 			break;
487 		}
488 		if ((pr = ifunit(vlr.vlr_parent)) == 0) {
489 			error = ENOENT;
490 			break;
491 		}
492 		if ((error = vlan_config(ifv, pr)) != 0)
493 			break;
494 		ifv->ifv_tag = vlr.vlr_tag;
495 		ifp->if_flags |= IFF_RUNNING;
496 
497 		/* Update promiscuous mode, if necessary. */
498 		vlan_set_promisc(ifp);
499 		break;
500 
501 	case SIOCGETVLAN:
502 		memset(&vlr, 0, sizeof(vlr));
503 		if (ifv->ifv_p != NULL) {
504 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
505 			    ifv->ifv_p->if_xname);
506 			vlr.vlr_tag = ifv->ifv_tag;
507 		}
508 		error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
509 		break;
510 
511 	case SIOCSIFFLAGS:
512 		/*
513 		 * For promiscuous mode, we enable promiscuous mode on
514 		 * the parent if we need promiscuous on the VLAN interface.
515 		 */
516 		if (ifv->ifv_p != NULL)
517 			error = vlan_set_promisc(ifp);
518 		break;
519 
520 	case SIOCADDMULTI:
521 		error = (ifv->ifv_p != NULL) ?
522 		    (*ifv->ifv_msw->vmsw_addmulti)(ifv, ifr) : EINVAL;
523 		break;
524 
525 	case SIOCDELMULTI:
526 		error = (ifv->ifv_p != NULL) ?
527 		    (*ifv->ifv_msw->vmsw_delmulti)(ifv, ifr) : EINVAL;
528 		break;
529 
530 	default:
531 		error = EINVAL;
532 	}
533 
534 	splx(s);
535 
536 	return (error);
537 }
538 
539 static int
540 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
541 {
542 	struct vlan_mc_entry *mc;
543 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
544 	int error;
545 
546 	if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
547 		return (EINVAL);
548 
549 	error = ether_addmulti(ifr, &ifv->ifv_ec);
550 	if (error != ENETRESET)
551 		return (error);
552 
553 	/*
554 	 * This is new multicast address.  We have to tell parent
555 	 * about it.  Also, remember this multicast address so that
556 	 * we can delete them on unconfigure.
557 	 */
558 	MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
559 	    M_DEVBUF, M_NOWAIT);
560 	if (mc == NULL) {
561 		error = ENOMEM;
562 		goto alloc_failed;
563 	}
564 
565 	/*
566 	 * As ether_addmulti() returns ENETRESET, following two
567 	 * statement shouldn't fail.
568 	 */
569 	(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
570 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm);
571 	memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
572 	LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
573 
574 	error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI,
575 	    (caddr_t)ifr);
576 	if (error != 0)
577 		goto ioctl_failed;
578 	return (error);
579 
580  ioctl_failed:
581 	LIST_REMOVE(mc, mc_entries);
582 	FREE(mc, M_DEVBUF);
583  alloc_failed:
584 	(void)ether_delmulti(ifr, &ifv->ifv_ec);
585 	return (error);
586 }
587 
588 static int
589 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
590 {
591 	struct ether_multi *enm;
592 	struct vlan_mc_entry *mc;
593 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
594 	int error;
595 
596 	/*
597 	 * Find a key to lookup vlan_mc_entry.  We have to do this
598 	 * before calling ether_delmulti for obvious reason.
599 	 */
600 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
601 		return (error);
602 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm);
603 
604 	error = ether_delmulti(ifr, &ifv->ifv_ec);
605 	if (error != ENETRESET)
606 		return (error);
607 
608 	/* We no longer use this multicast address.  Tell parent so. */
609 	error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI,
610 	    (caddr_t)ifr);
611 	if (error == 0) {
612 		/* And forget about this address. */
613 		for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL;
614 		    mc = LIST_NEXT(mc, mc_entries)) {
615 			if (mc->mc_enm == enm) {
616 				LIST_REMOVE(mc, mc_entries);
617 				FREE(mc, M_DEVBUF);
618 				break;
619 			}
620 		}
621 		KASSERT(mc != NULL);
622 	} else
623 		(void)ether_addmulti(ifr, &ifv->ifv_ec);
624 	return (error);
625 }
626 
627 /*
628  * Delete any multicast address we have asked to add form parent
629  * interface.  Called when the vlan is being unconfigured.
630  */
631 static void
632 vlan_ether_purgemulti(struct ifvlan *ifv)
633 {
634 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
635 	struct vlan_mc_entry *mc;
636 	union {
637 		struct ifreq ifreq;
638 		struct {
639 			char ifr_name[IFNAMSIZ];
640 			struct sockaddr_storage ifr_ss;
641 		} ifreq_storage;
642 	} ifreq;
643 	struct ifreq *ifr = &ifreq.ifreq;
644 
645 	memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
646 	while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
647 		memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
648 		(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
649 		LIST_REMOVE(mc, mc_entries);
650 		FREE(mc, M_DEVBUF);
651 	}
652 }
653 
654 static void
655 vlan_start(struct ifnet *ifp)
656 {
657 	struct ifvlan *ifv = ifp->if_softc;
658 	struct ifnet *p = ifv->ifv_p;
659 	struct ethercom *ec = (void *) ifv->ifv_p;
660 	struct mbuf *m;
661 
662 	ifp->if_flags |= IFF_OACTIVE;
663 
664 	for (;;) {
665 		IF_DEQUEUE(&ifp->if_snd, m);
666 		if (m == NULL)
667 			break;
668 
669 #if NBPFILTER > 0
670 		if (ifp->if_bpf)
671 			bpf_mtap(ifp->if_bpf, m);
672 #endif
673 		/*
674 		 * If the parent can insert the tag itself, just mark
675 		 * the tag in the mbuf header.
676 		 */
677 		if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
678 			struct mbuf *n;
679 			n = m_aux_add(m, AF_LINK, ETHERTYPE_VLAN);
680 			if (n == NULL) {
681 				ifp->if_oerrors++;
682 				m_freem(m);
683 				continue;
684 			}
685 			*mtod(n, int *) = ifv->ifv_tag;
686 			n->m_len = sizeof(int);
687 		} else {
688 			/*
689 			 * insert the tag ourselve
690 			 */
691 			M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
692 			if (m == NULL) {
693 				printf("%s: unable to prepend encap header",
694 				    ifv->ifv_p->if_xname);
695 				ifp->if_oerrors++;
696 				continue;
697 			}
698 
699 			switch (p->if_type) {
700 			case IFT_ETHER:
701 			    {
702 				struct ether_vlan_header *evl;
703 
704 				if (m->m_len < sizeof(struct ether_vlan_header))
705 					m = m_pullup(m,
706 					    sizeof(struct ether_vlan_header));
707 				if (m == NULL) {
708 					printf("%s: unable to pullup encap "
709 					    "header", ifv->ifv_p->if_xname);
710 					ifp->if_oerrors++;
711 					continue;
712 				}
713 
714 				/*
715 				 * Transform the Ethernet header into an
716 				 * Ethernet header with 802.1Q encapsulation.
717 				 */
718 				memmove(mtod(m, caddr_t),
719 				    mtod(m, caddr_t) + ifv->ifv_encaplen,
720 				    sizeof(struct ether_header));
721 				evl = mtod(m, struct ether_vlan_header *);
722 				evl->evl_proto = evl->evl_encap_proto;
723 				evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
724 				evl->evl_tag = htons(ifv->ifv_tag);
725 				break;
726 			    }
727 
728 #ifdef DIAGNOSTIC
729 			default:
730 				panic("vlan_start: impossible");
731 #endif
732 			}
733 		}
734 
735 		/*
736 		 * Send it, precisely as the parent's output routine
737 		 * would have.  We are already running at splimp.
738 		 */
739 		if (IF_QFULL(&p->if_snd)) {
740 			IF_DROP(&p->if_snd);
741 			/* XXX stats */
742 			ifp->if_oerrors++;
743 			m_freem(m);
744 			continue;
745 		}
746 
747 		IF_ENQUEUE(&p->if_snd, m);
748 		ifp->if_opackets++;
749 		if ((p->if_flags & IFF_OACTIVE) == 0) {
750 			(*p->if_start)(p);
751 		}
752 	}
753 
754 	ifp->if_flags &= ~IFF_OACTIVE;
755 }
756 
757 /*
758  * Given an Ethernet frame, find a valid vlan interface corresponding to the
759  * given source interface and tag, then run the the real packet through
760  * the parent's input routine.
761  */
762 void
763 vlan_input(struct ifnet *ifp, struct mbuf *m)
764 {
765 	struct ifvlan *ifv;
766 	u_int tag;
767 	struct mbuf *n;
768 
769 	n = m_aux_find(m, AF_LINK, ETHERTYPE_VLAN);
770 	if (n) {
771 		/* m contains a normal ethernet frame, the tag is in m_aux */
772 		tag = *mtod(n, int *);
773 		m_aux_delete(m, n);
774 		for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
775 		    ifv = LIST_NEXT(ifv, ifv_list))
776 			if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
777 				break;
778 	} else {
779 		switch (ifp->if_type) {
780 		case IFT_ETHER:
781 		    {
782 			struct ether_vlan_header *evl;
783 
784 			if (m->m_len < sizeof(struct ether_vlan_header) &&
785 			    (m = m_pullup(m,
786 			     sizeof(struct ether_vlan_header))) == NULL) {
787 				printf("%s: no memory for VLAN header, "
788 				    "dropping packet.\n", ifp->if_xname);
789 				return;
790 			}
791 			evl = mtod(m, struct ether_vlan_header *);
792 			KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
793 
794 			tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
795 
796 			/*
797 			 * Restore the original ethertype.  We'll remove
798 			 * the encapsulation after we've found the vlan
799 			 * interface corresponding to the tag.
800 			 */
801 			evl->evl_encap_proto = evl->evl_proto;
802 			break;
803 		    }
804 
805 		default:
806 			tag = (u_int) -1;	/* XXX GCC */
807 #ifdef DIAGNOSTIC
808 			panic("vlan_input: impossible");
809 #endif
810 		}
811 
812 		for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
813 		     ifv = LIST_NEXT(ifv, ifv_list))
814 			if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
815 				break;
816 
817 
818 		/*
819 		 * Now, remove the encapsulation header.  The original
820 		 * header has already been fixed up above.
821 		 */
822 		if (ifv) {
823 			memmove(mtod(m, caddr_t) + ifv->ifv_encaplen,
824 			    mtod(m, caddr_t), sizeof(struct ether_header));
825 			m_adj(m, ifv->ifv_encaplen);
826 		}
827 	}
828 
829 	if (ifv == NULL ||
830 	    (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
831 	     (IFF_UP|IFF_RUNNING)) {
832 		m_free(m);
833 		ifp->if_noproto++;
834 		return;
835 	}
836 	m->m_pkthdr.rcvif = &ifv->ifv_if;
837 	ifv->ifv_if.if_ipackets++;
838 
839 #if NBPFILTER > 0
840 	if (ifv->ifv_if.if_bpf)
841 		bpf_mtap(ifv->ifv_if.if_bpf, m);
842 #endif
843 
844 	/* Pass it back through the parent's input routine. */
845 	(*ifp->if_input)(&ifv->ifv_if, m);
846 }
847