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