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