Deleted Added
sdiff udiff text old ( 128618 ) new ( 128871 )
full compact
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 128871 2004-05-03 13:48:35Z andre $
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, VLANNAME, "802.1Q Virtual LAN Interface");
105static LIST_HEAD(, ifvlan) ifv_list;
106
107/*
108 * Locking: one lock is used to guard both the ifv_list and modification
109 * to vlan data structures. We are rather conservative here; probably
110 * more than necessary.
111 */
112static struct mtx ifv_mtx;
113#define VLAN_LOCK_INIT() mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF)
114#define VLAN_LOCK_DESTROY() mtx_destroy(&ifv_mtx)
115#define VLAN_LOCK_ASSERT() mtx_assert(&ifv_mtx, MA_OWNED)
116#define VLAN_LOCK() mtx_lock(&ifv_mtx)
117#define VLAN_UNLOCK() mtx_unlock(&ifv_mtx)
118
119static int vlan_clone_create(struct if_clone *, int);
120static void vlan_clone_destroy(struct ifnet *);
121static void vlan_start(struct ifnet *ifp);
122static void vlan_ifinit(void *foo);
123static void vlan_input(struct ifnet *ifp, struct mbuf *m);
124static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
125static int vlan_setmulti(struct ifnet *ifp);
126static int vlan_unconfig(struct ifnet *ifp);
127static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
128static void vlan_link_state(struct ifnet *ifp, int link);
129
130struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME,
131 vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);
132
133/*
134 * Program our multicast filter. What we're actually doing is
135 * programming the multicast filter of the parent. This has the
136 * side effect of causing the parent interface to receive multicast
137 * traffic that it doesn't really want, which ends up being discarded
138 * later by the upper protocol layers. Unfortunately, there's no way
139 * to avoid this: there really is only one physical interface.
140 */
141static int
142vlan_setmulti(struct ifnet *ifp)
143{
144 struct ifnet *ifp_p;
145 struct ifmultiaddr *ifma, *rifma = NULL;
146 struct ifvlan *sc;
147 struct vlan_mc_entry *mc = NULL;
148 struct sockaddr_dl sdl;
149 int error;
150
151 /* Find the parent. */
152 sc = ifp->if_softc;
153 ifp_p = sc->ifv_p;
154
155 /*
156 * If we don't have a parent, just remember the membership for
157 * when we do.
158 */
159 if (ifp_p == NULL)
160 return(0);
161
162 bzero((char *)&sdl, sizeof sdl);
163 sdl.sdl_len = sizeof sdl;
164 sdl.sdl_family = AF_LINK;
165 sdl.sdl_index = ifp_p->if_index;
166 sdl.sdl_type = IFT_ETHER;
167 sdl.sdl_alen = ETHER_ADDR_LEN;
168
169 /* First, remove any existing filter entries. */
170 while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
171 mc = SLIST_FIRST(&sc->vlan_mc_listhead);
172 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
173 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
174 if (error)
175 return(error);
176 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
177 free(mc, M_VLAN);
178 }
179
180 /* Now program new ones. */
181 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
182 if (ifma->ifma_addr->sa_family != AF_LINK)
183 continue;
184 mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK);
185 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
186 (char *)&mc->mc_addr, ETHER_ADDR_LEN);
187 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
188 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
189 LLADDR(&sdl), ETHER_ADDR_LEN);
190 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
191 if (error)
192 return(error);
193 }
194
195 return(0);
196}
197
198/*
199 * VLAN support can be loaded as a module. The only place in the
200 * system that's intimately aware of this is ether_input. We hook
201 * into this code through vlan_input_p which is defined there and
202 * set here. Noone else in the system should be aware of this so
203 * we use an explicit reference here.
204 *
205 * NB: Noone should ever need to check if vlan_input_p is null or
206 * not. This is because interfaces have a count of the number
207 * of active vlans (if_nvlans) and this should never be bumped
208 * except by vlan_config--which is in this module so therefore
209 * the module must be loaded and vlan_input_p must be non-NULL.
210 */
211extern void (*vlan_input_p)(struct ifnet *, struct mbuf *);
212
213/* For MII eyes only... */
214extern void (*vlan_link_state_p)(struct ifnet *, int);
215
216static int
217vlan_modevent(module_t mod, int type, void *data)
218{
219
220 switch (type) {
221 case MOD_LOAD:
222 LIST_INIT(&ifv_list);
223 VLAN_LOCK_INIT();
224 vlan_input_p = vlan_input;
225 vlan_link_state_p = vlan_link_state;
226 if_clone_attach(&vlan_cloner);
227 break;
228 case MOD_UNLOAD:
229 if_clone_detach(&vlan_cloner);
230 vlan_input_p = NULL;
231 vlan_link_state_p = NULL;
232 while (!LIST_EMPTY(&ifv_list))
233 vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
234 VLAN_LOCK_DESTROY();
235 break;
236 }
237 return 0;
238}
239
240static moduledata_t vlan_mod = {
241 "if_vlan",
242 vlan_modevent,
243 0
244};
245
246DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
247
248static int
249vlan_clone_create(struct if_clone *ifc, int unit)
250{
251 struct ifvlan *ifv;
252 struct ifnet *ifp;
253
254 ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
255 ifp = &ifv->ifv_if;
256 SLIST_INIT(&ifv->vlan_mc_listhead);
257
258 ifp->if_softc = ifv;
259 if_initname(ifp, ifc->ifc_name, unit);
260 /* NB: flags are not set here */
261 ifp->if_linkmib = &ifv->ifv_mib;
262 ifp->if_linkmiblen = sizeof ifv->ifv_mib;
263 /* NB: mtu is not set here */
264
265 ifp->if_init = vlan_ifinit;
266 ifp->if_start = vlan_start;
267 ifp->if_ioctl = vlan_ioctl;
268 ifp->if_snd.ifq_maxlen = ifqmaxlen;
269 ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr);
270 /* Now undo some of the damage... */
271 ifp->if_baudrate = 0;
272 ifp->if_type = IFT_L2VLAN;
273 ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
274
275 VLAN_LOCK();
276 LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
277 VLAN_UNLOCK();
278
279 return (0);
280}
281
282static void
283vlan_clone_destroy(struct ifnet *ifp)
284{
285 struct ifvlan *ifv = ifp->if_softc;
286
287 VLAN_LOCK();
288 LIST_REMOVE(ifv, ifv_list);
289 vlan_unconfig(ifp);
290 VLAN_UNLOCK();
291
292 ether_ifdetach(ifp);
293
294 free(ifv, M_VLAN);
295}
296
297static void
298vlan_ifinit(void *foo)
299{
300 return;
301}
302
303static void
304vlan_start(struct ifnet *ifp)
305{
306 struct ifvlan *ifv;
307 struct ifnet *p;
308 struct ether_vlan_header *evl;
309 struct mbuf *m;
310
311 ifv = ifp->if_softc;
312 p = ifv->ifv_p;
313
314 ifp->if_flags |= IFF_OACTIVE;
315 for (;;) {
316 IF_DEQUEUE(&ifp->if_snd, m);
317 if (m == 0)
318 break;
319 BPF_MTAP(ifp, m);
320
321 /*
322 * Do not run parent's if_start() if the parent is not up,
323 * or parent's driver will cause a system crash.
324 */
325 if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
326 (IFF_UP | IFF_RUNNING)) {
327 m_freem(m);
328 ifp->if_collisions++;
329 continue;
330 }
331
332 /*
333 * If underlying interface can do VLAN tag insertion itself,
334 * just pass the packet along. However, we need some way to
335 * tell the interface where the packet came from so that it
336 * knows how to find the VLAN tag to use, so we attach a
337 * packet tag that holds it.
338 */
339 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
340 struct m_tag *mtag = m_tag_alloc(MTAG_VLAN,
341 MTAG_VLAN_TAG,
342 sizeof (u_int),
343 M_NOWAIT);
344 if (mtag == NULL) {
345 ifp->if_oerrors++;
346 m_freem(m);
347 continue;
348 }
349 *(u_int*)(mtag+1) = ifv->ifv_tag;
350 m_tag_prepend(m, mtag);
351 } else {
352 M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
353 if (m == NULL) {
354 if_printf(ifp, "unable to prepend VLAN header");
355 ifp->if_oerrors++;
356 continue;
357 }
358 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
359
360 if (m->m_len < sizeof(*evl)) {
361 m = m_pullup(m, sizeof(*evl));
362 if (m == NULL) {
363 if_printf(ifp,
364 "cannot pullup VLAN header");
365 ifp->if_oerrors++;
366 continue;
367 }
368 }
369
370 /*
371 * Transform the Ethernet header into an Ethernet header
372 * with 802.1Q encapsulation.
373 */
374 bcopy(mtod(m, char *) + ifv->ifv_encaplen,
375 mtod(m, char *), ETHER_HDR_LEN);
376 evl = mtod(m, struct ether_vlan_header *);
377 evl->evl_proto = evl->evl_encap_proto;
378 evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
379 evl->evl_tag = htons(ifv->ifv_tag);
380#ifdef DEBUG
381 printf("vlan_start: %*D\n", (int)sizeof *evl,
382 (unsigned char *)evl, ":");
383#endif
384 }
385
386 /*
387 * Send it, precisely as ether_output() would have.
388 * We are already running at splimp.
389 */
390 if (IF_HANDOFF(&p->if_snd, m, p))
391 ifp->if_opackets++;
392 else
393 ifp->if_oerrors++;
394 }
395 ifp->if_flags &= ~IFF_OACTIVE;
396
397 return;
398}
399
400static void
401vlan_input(struct ifnet *ifp, struct mbuf *m)
402{
403 struct ether_vlan_header *evl;
404 struct ifvlan *ifv;
405 struct m_tag *mtag;
406 u_int tag;
407
408 mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL);
409 if (mtag != NULL) {
410 /*
411 * Packet is tagged, m contains a normal
412 * Ethernet frame; the tag is stored out-of-band.
413 */
414 tag = EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag));
415 m_tag_delete(m, mtag);
416 } else {
417 switch (ifp->if_type) {
418 case IFT_ETHER:
419 if (m->m_len < sizeof (*evl) &&
420 (m = m_pullup(m, sizeof (*evl))) == NULL) {
421 if_printf(ifp, "cannot pullup VLAN header\n");
422 return;
423 }
424 evl = mtod(m, struct ether_vlan_header *);
425 KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN,
426 ("vlan_input: bad encapsulated protocols (%u)",
427 ntohs(evl->evl_encap_proto)));
428
429 tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
430
431 /*
432 * Restore the original ethertype. We'll remove
433 * the encapsulation after we've found the vlan
434 * interface corresponding to the tag.
435 */
436 evl->evl_encap_proto = evl->evl_proto;
437 break;
438 default:
439 tag = (u_int) -1;
440#ifdef DIAGNOSTIC
441 panic("vlan_input: unsupported if type %u", ifp->if_type);
442#endif
443 break;
444 }
445 }
446
447 VLAN_LOCK();
448 LIST_FOREACH(ifv, &ifv_list, ifv_list)
449 if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
450 break;
451
452 if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
453 VLAN_UNLOCK();
454 m_freem(m);
455 ifp->if_noproto++;
456 return;
457 }
458 VLAN_UNLOCK(); /* XXX extend below? */
459
460 if (mtag == NULL) {
461 /*
462 * Packet had an in-line encapsulation header;
463 * remove it. The original header has already
464 * been fixed up above.
465 */
466 bcopy(mtod(m, caddr_t),
467 mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
468 ETHER_HDR_LEN);
469 m_adj(m, ETHER_VLAN_ENCAP_LEN);
470 }
471
472 m->m_pkthdr.rcvif = &ifv->ifv_if;
473 ifv->ifv_if.if_ipackets++;
474
475 /* Pass it back through the parent's input routine. */
476 (*ifp->if_input)(&ifv->ifv_if, m);
477}
478
479static int
480vlan_config(struct ifvlan *ifv, struct ifnet *p)
481{
482 struct ifaddr *ifa1, *ifa2;
483 struct sockaddr_dl *sdl1, *sdl2;
484
485 VLAN_LOCK_ASSERT();
486
487 if (p->if_data.ifi_type != IFT_ETHER)
488 return EPROTONOSUPPORT;
489 if (ifv->ifv_p)
490 return EBUSY;
491
492 ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
493 ifv->ifv_mintu = ETHERMIN;
494 ifv->ifv_flags = 0;
495
496 /*
497 * If the parent supports the VLAN_MTU capability,
498 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
499 * enable it.
500 */
501 p->if_nvlans++;
502 if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) {
503 /*
504 * Enable Tx/Rx of VLAN-sized frames.
505 */
506 p->if_capenable |= IFCAP_VLAN_MTU;
507 if (p->if_flags & IFF_UP) {
508 struct ifreq ifr;
509 int error;
510
511 ifr.ifr_flags = p->if_flags;
512 error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
513 (caddr_t) &ifr);
514 if (error) {
515 p->if_nvlans--;
516 if (p->if_nvlans == 0)
517 p->if_capenable &= ~IFCAP_VLAN_MTU;
518 return (error);
519 }
520 }
521 ifv->ifv_mtufudge = 0;
522 } else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) {
523 /*
524 * Fudge the MTU by the encapsulation size. This
525 * makes us incompatible with strictly compliant
526 * 802.1Q implementations, but allows us to use
527 * the feature with other NetBSD implementations,
528 * which might still be useful.
529 */
530 ifv->ifv_mtufudge = ifv->ifv_encaplen;
531 }
532
533 ifv->ifv_p = p;
534 ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
535 /*
536 * Copy only a selected subset of flags from the parent.
537 * Other flags are none of our business.
538 */
539 ifv->ifv_if.if_flags = (p->if_flags &
540 (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
541 ifv->ifv_if.if_link_state = p->if_link_state;
542
543 /*
544 * If the parent interface can do hardware-assisted
545 * VLAN encapsulation, then propagate its hardware-
546 * assisted checksumming flags.
547 */
548 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
549 ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
550
551 /*
552 * Set up our ``Ethernet address'' to reflect the underlying
553 * physical interface's.
554 */
555 ifa1 = ifaddr_byindex(ifv->ifv_if.if_index);
556 ifa2 = ifaddr_byindex(p->if_index);
557 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
558 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
559 sdl1->sdl_type = IFT_ETHER;
560 sdl1->sdl_alen = ETHER_ADDR_LEN;
561 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
562 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
563
564 /*
565 * Configure multicast addresses that may already be
566 * joined on the vlan device.
567 */
568 (void)vlan_setmulti(&ifv->ifv_if);
569
570 return 0;
571}
572
573static int
574vlan_unconfig(struct ifnet *ifp)
575{
576 struct ifaddr *ifa;
577 struct sockaddr_dl *sdl;
578 struct vlan_mc_entry *mc;
579 struct ifvlan *ifv;
580 struct ifnet *p;
581 int error;
582
583 VLAN_LOCK_ASSERT();
584
585 ifv = ifp->if_softc;
586 p = ifv->ifv_p;
587
588 if (p) {
589 struct sockaddr_dl sdl;
590
591 /*
592 * Since the interface is being unconfigured, we need to
593 * empty the list of multicast groups that we may have joined
594 * while we were alive from the parent's list.
595 */
596 bzero((char *)&sdl, sizeof sdl);
597 sdl.sdl_len = sizeof sdl;
598 sdl.sdl_family = AF_LINK;
599 sdl.sdl_index = p->if_index;
600 sdl.sdl_type = IFT_ETHER;
601 sdl.sdl_alen = ETHER_ADDR_LEN;
602
603 while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
604 mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
605 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
606 error = if_delmulti(p, (struct sockaddr *)&sdl);
607 if (error)
608 return(error);
609 SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
610 free(mc, M_VLAN);
611 }
612
613 p->if_nvlans--;
614 if (p->if_nvlans == 0) {
615 /*
616 * Disable Tx/Rx of VLAN-sized frames.
617 */
618 p->if_capenable &= ~IFCAP_VLAN_MTU;
619 if (p->if_flags & IFF_UP) {
620 struct ifreq ifr;
621
622 ifr.ifr_flags = p->if_flags;
623 (*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
624 }
625 }
626 }
627
628 /* Disconnect from parent. */
629 ifv->ifv_p = NULL;
630 ifv->ifv_if.if_mtu = ETHERMTU; /* XXX why not 0? */
631 ifv->ifv_flags = 0;
632 ifv->ifv_if.if_link_state = LINK_STATE_UNKNOWN;
633
634 /* Clear our MAC address. */
635 ifa = ifaddr_byindex(ifv->ifv_if.if_index);
636 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
637 sdl->sdl_type = IFT_ETHER;
638 sdl->sdl_alen = ETHER_ADDR_LEN;
639 bzero(LLADDR(sdl), ETHER_ADDR_LEN);
640 bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
641
642 return 0;
643}
644
645static int
646vlan_set_promisc(struct ifnet *ifp)
647{
648 struct ifvlan *ifv = ifp->if_softc;
649 int error = 0;
650
651 if ((ifp->if_flags & IFF_PROMISC) != 0) {
652 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
653 error = ifpromisc(ifv->ifv_p, 1);
654 if (error == 0)
655 ifv->ifv_flags |= IFVF_PROMISC;
656 }
657 } else {
658 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
659 error = ifpromisc(ifv->ifv_p, 0);
660 if (error == 0)
661 ifv->ifv_flags &= ~IFVF_PROMISC;
662 }
663 }
664
665 return (error);
666}
667
668/* Inform all vlans that their parent has changed link state */
669static void
670vlan_link_state(struct ifnet *ifp, int link)
671{
672 struct ifvlan *ifv;
673
674 VLAN_LOCK();
675 LIST_FOREACH(ifv, &ifv_list, ifv_list) {
676 if (ifv->ifv_p == ifp) {
677 ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
678 rt_ifmsg(&(ifv->ifv_if));
679 KNOTE(&ifp->if_klist, link);
680 }
681 }
682 VLAN_UNLOCK();
683}
684
685static int
686vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
687{
688 struct ifaddr *ifa;
689 struct ifnet *p;
690 struct ifreq *ifr;
691 struct ifvlan *ifv;
692 struct vlanreq vlr;
693 int error = 0;
694
695 ifr = (struct ifreq *)data;
696 ifa = (struct ifaddr *)data;
697 ifv = ifp->if_softc;
698
699 switch (cmd) {
700 case SIOCSIFADDR:
701 ifp->if_flags |= IFF_UP;
702
703 switch (ifa->ifa_addr->sa_family) {
704#ifdef INET
705 case AF_INET:
706 arp_ifinit(&ifv->ifv_if, ifa);
707 break;
708#endif
709 default:
710 break;
711 }
712 break;
713
714 case SIOCGIFADDR:
715 {
716 struct sockaddr *sa;
717
718 sa = (struct sockaddr *) &ifr->ifr_data;
719 bcopy(IFP2AC(ifp)->ac_enaddr,
720 (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
721 }
722 break;
723
724 case SIOCGIFMEDIA:
725 VLAN_LOCK();
726 if (ifv->ifv_p != NULL) {
727 error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
728 SIOCGIFMEDIA, data);
729 VLAN_UNLOCK();
730 /* Limit the result to the parent's current config. */
731 if (error == 0) {
732 struct ifmediareq *ifmr;
733
734 ifmr = (struct ifmediareq *) data;
735 if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
736 ifmr->ifm_count = 1;
737 error = copyout(&ifmr->ifm_current,
738 ifmr->ifm_ulist,
739 sizeof(int));
740 }
741 }
742 } else {
743 VLAN_UNLOCK();
744 error = EINVAL;
745 }
746 break;
747
748 case SIOCSIFMEDIA:
749 error = EINVAL;
750 break;
751
752 case SIOCSIFMTU:
753 /*
754 * Set the interface MTU.
755 */
756 VLAN_LOCK();
757 if (ifv->ifv_p != NULL) {
758 if (ifr->ifr_mtu >
759 (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
760 ifr->ifr_mtu <
761 (ifv->ifv_mintu - ifv->ifv_mtufudge))
762 error = EINVAL;
763 else
764 ifp->if_mtu = ifr->ifr_mtu;
765 } else
766 error = EINVAL;
767 VLAN_UNLOCK();
768 break;
769
770 case SIOCSETVLAN:
771 error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
772 if (error)
773 break;
774 if (vlr.vlr_parent[0] == '\0') {
775 VLAN_LOCK();
776 vlan_unconfig(ifp);
777 if (ifp->if_flags & IFF_UP)
778 if_down(ifp);
779 ifp->if_flags &= ~IFF_RUNNING;
780 VLAN_UNLOCK();
781 break;
782 }
783 p = ifunit(vlr.vlr_parent);
784 if (p == 0) {
785 error = ENOENT;
786 break;
787 }
788 /*
789 * Don't let the caller set up a VLAN tag with
790 * anything except VLID bits.
791 */
792 if (vlr.vlr_tag & ~EVL_VLID_MASK) {
793 error = EINVAL;
794 break;
795 }
796 VLAN_LOCK();
797 error = vlan_config(ifv, p);
798 if (error) {
799 VLAN_UNLOCK();
800 break;
801 }
802 ifv->ifv_tag = vlr.vlr_tag;
803 ifp->if_flags |= IFF_RUNNING;
804 VLAN_UNLOCK();
805
806 /* Update promiscuous mode, if necessary. */
807 vlan_set_promisc(ifp);
808 break;
809
810 case SIOCGETVLAN:
811 bzero(&vlr, sizeof vlr);
812 VLAN_LOCK();
813 if (ifv->ifv_p) {
814 strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname,
815 sizeof(vlr.vlr_parent));
816 vlr.vlr_tag = ifv->ifv_tag;
817 }
818 VLAN_UNLOCK();
819 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
820 break;
821
822 case SIOCSIFFLAGS:
823 /*
824 * For promiscuous mode, we enable promiscuous mode on
825 * the parent if we need promiscuous on the VLAN interface.
826 */
827 if (ifv->ifv_p != NULL)
828 error = vlan_set_promisc(ifp);
829 break;
830
831 case SIOCADDMULTI:
832 case SIOCDELMULTI:
833 error = vlan_setmulti(ifp);
834 break;
835 default:
836 error = EINVAL;
837 }
838 return error;
839}