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