1 /* $OpenBSD: if_vlan.c,v 1.78 2009/01/27 15:56:58 naddy 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|M_ZERO); 122 if (!ifv) 123 return (ENOMEM); 124 125 LIST_INIT(&ifv->vlan_mc_listhead); 126 ifp = &ifv->ifv_if; 127 ifp->if_softc = ifv; 128 snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name, 129 unit); 130 /* NB: flags are not set here */ 131 /* NB: mtu is not set here */ 132 133 ifp->if_start = vlan_start; 134 ifp->if_ioctl = vlan_ioctl; 135 ifp->if_output = ether_output; 136 IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 137 IFQ_SET_READY(&ifp->if_snd); 138 if_attach(ifp); 139 ether_ifattach(ifp); 140 /* Now undo some of the damage... */ 141 ifp->if_type = IFT_L2VLAN; 142 ifp->if_hdrlen = EVL_ENCAPLEN; 143 144 return (0); 145 } 146 147 int 148 vlan_clone_destroy(struct ifnet *ifp) 149 { 150 struct ifvlan *ifv = ifp->if_softc; 151 152 vlan_unconfig(ifp); 153 ether_ifdetach(ifp); 154 if_detach(ifp); 155 156 free(ifv, M_DEVBUF); 157 return (0); 158 } 159 160 void 161 vlan_ifdetach(void *ptr) 162 { 163 struct ifvlan *ifv = (struct ifvlan *)ptr; 164 /* 165 * Destroy the vlan interface because the parent has been 166 * detached. Set the dh_cookie to NULL because we're running 167 * inside of dohooks which is told to disestablish the hook 168 * for us (otherwise we would kill the TAILQ element...). 169 */ 170 ifv->dh_cookie = NULL; 171 vlan_clone_destroy(&ifv->ifv_if); 172 } 173 174 void 175 vlan_start(struct ifnet *ifp) 176 { 177 struct ifvlan *ifv; 178 struct ifnet *p; 179 struct mbuf *m; 180 int error; 181 182 ifv = ifp->if_softc; 183 p = ifv->ifv_p; 184 185 ifp->if_flags |= IFF_OACTIVE; 186 for (;;) { 187 IFQ_DEQUEUE(&ifp->if_snd, m); 188 if (m == NULL) 189 break; 190 191 if ((p->if_flags & (IFF_UP|IFF_RUNNING)) != 192 (IFF_UP|IFF_RUNNING)) { 193 IF_DROP(&p->if_snd); 194 /* XXX stats */ 195 ifp->if_oerrors++; 196 m_freem(m); 197 continue; 198 } 199 200 #if NBPFILTER > 0 201 if (ifp->if_bpf) 202 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 203 #endif 204 205 /* 206 * If the IFCAP_VLAN_HWTAGGING capability is set on the parent, 207 * it can do VLAN tag insertion itself and doesn't require us 208 * to create a special header for it. In this case, we just pass 209 * the packet along. 210 */ 211 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) { 212 m->m_pkthdr.ether_vtag = ifv->ifv_tag + 213 (ifv->ifv_prio << EVL_PRIO_BITS); 214 m->m_flags |= M_VLANTAG; 215 } else { 216 struct ether_vlan_header evh; 217 218 m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh); 219 evh.evl_proto = evh.evl_encap_proto; 220 evh.evl_encap_proto = htons(ETHERTYPE_VLAN); 221 evh.evl_tag = htons(ifv->ifv_tag + 222 (ifv->ifv_prio << EVL_PRIO_BITS)); 223 224 m_adj(m, ETHER_HDR_LEN); 225 M_PREPEND(m, sizeof(evh), M_DONTWAIT); 226 if (m == NULL) { 227 ifp->if_oerrors++; 228 continue; 229 } 230 231 m_copyback(m, 0, sizeof(evh), &evh); 232 } 233 234 /* 235 * Send it, precisely as ether_output() would have. 236 * We are already running at splnet. 237 */ 238 p->if_obytes += m->m_pkthdr.len; 239 if (m->m_flags & M_MCAST) 240 p->if_omcasts++; 241 IFQ_ENQUEUE(&p->if_snd, m, NULL, error); 242 if (error) { 243 /* mbuf is already freed */ 244 ifp->if_oerrors++; 245 continue; 246 } 247 248 ifp->if_opackets++; 249 if_start(p); 250 } 251 ifp->if_flags &= ~IFF_OACTIVE; 252 253 return; 254 } 255 256 /* 257 * vlan_input() returns 0 if it has consumed the packet, 1 otherwise. 258 */ 259 int 260 vlan_input(eh, m) 261 struct ether_header *eh; 262 struct mbuf *m; 263 { 264 struct ifvlan *ifv; 265 u_int tag; 266 struct ifnet *ifp = m->m_pkthdr.rcvif; 267 268 if (m->m_flags & M_VLANTAG) { 269 tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 270 } else { 271 if (m->m_len < EVL_ENCAPLEN && 272 (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) { 273 ifp->if_ierrors++; 274 return (0); 275 } 276 277 tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))); 278 } 279 280 LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) { 281 if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag) 282 break; 283 } 284 if (ifv == NULL) 285 return (1); 286 287 if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) != 288 (IFF_UP|IFF_RUNNING)) { 289 m_freem(m); 290 return (0); 291 } 292 293 /* 294 * Having found a valid vlan interface corresponding to 295 * the given source interface and vlan tag, remove the 296 * encapsulation, and run the real packet through 297 * ether_input() a second time (it had better be 298 * reentrant!). 299 */ 300 m->m_pkthdr.rcvif = &ifv->ifv_if; 301 if (m->m_flags & M_VLANTAG) { 302 m->m_flags &= ~M_VLANTAG; 303 } else { 304 eh->ether_type = mtod(m, u_int16_t *)[1]; 305 m->m_len -= EVL_ENCAPLEN; 306 m->m_data += EVL_ENCAPLEN; 307 m->m_pkthdr.len -= EVL_ENCAPLEN; 308 } 309 310 #if NBPFILTER > 0 311 if (ifv->ifv_if.if_bpf) 312 bpf_mtap_hdr(ifv->ifv_if.if_bpf, (char *)eh, ETHER_HDR_LEN, 313 m, BPF_DIRECTION_IN); 314 #endif 315 316 /* 317 * Drop promiscuously received packets if we are not in 318 * promiscuous mode. 319 */ 320 if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 && 321 (ifp->if_flags & IFF_PROMISC) && 322 (ifv->ifv_if.if_flags & IFF_PROMISC) == 0) { 323 struct arpcom *ac = &ifv->ifv_ac; 324 if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN)) { 325 m_freem(m); 326 return (0); 327 } 328 } 329 330 ifv->ifv_if.if_ipackets++; 331 ether_input(&ifv->ifv_if, eh, m); 332 333 return (0); 334 } 335 336 int 337 vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag) 338 { 339 struct ifaddr *ifa1, *ifa2; 340 struct sockaddr_dl *sdl1, *sdl2; 341 int s; 342 343 if (p->if_type != IFT_ETHER) 344 return EPROTONOSUPPORT; 345 if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */ 346 return (0); 347 if (ifv->ifv_p) 348 return EBUSY; 349 350 ifv->ifv_p = p; 351 352 if (p->if_capabilities & IFCAP_VLAN_MTU) 353 ifv->ifv_if.if_mtu = p->if_mtu; 354 else { 355 /* 356 * This will be incompatible with strict 357 * 802.1Q implementations 358 */ 359 ifv->ifv_if.if_mtu = p->if_mtu - EVL_ENCAPLEN; 360 #ifdef DIAGNOSTIC 361 printf("%s: initialized with non-standard mtu %lu (parent %s)\n", 362 ifv->ifv_if.if_xname, ifv->ifv_if.if_mtu, 363 ifv->ifv_p->if_xname); 364 #endif 365 } 366 367 ifv->ifv_if.if_flags = p->if_flags & 368 (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 369 370 /* 371 * Inherit the if_type from the parent. This allows us to 372 * participate in bridges of that type. 373 */ 374 ifv->ifv_if.if_type = p->if_type; 375 376 /* 377 * Inherit baudrate from the parent. An SNMP agent would use this 378 * information. 379 */ 380 ifv->ifv_if.if_baudrate = p->if_baudrate; 381 382 /* 383 * If the parent interface can do hardware-assisted 384 * VLAN encapsulation, then propagate its hardware- 385 * assisted checksumming flags. 386 * 387 * If the card cannot handle hardware tagging, it cannot 388 * possibly compute the correct checksums for tagged packets. 389 * 390 * This brings up another possibility, do cards exist which 391 * have all of these capabilities but cannot utilize them together? 392 */ 393 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) 394 ifv->ifv_if.if_capabilities = p->if_capabilities & 395 (IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4| 396 IFCAP_CSUM_UDPv4); 397 /* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */ 398 399 /* 400 * Set up our ``Ethernet address'' to reflect the underlying 401 * physical interface's. 402 */ 403 ifa1 = ifnet_addrs[ifv->ifv_if.if_index]; 404 ifa2 = ifnet_addrs[p->if_index]; 405 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 406 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 407 sdl1->sdl_type = IFT_ETHER; 408 sdl1->sdl_alen = ETHER_ADDR_LEN; 409 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 410 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 411 412 ifv->ifv_tag = tag; 413 s = splnet(); 414 LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list); 415 416 /* Register callback for physical link state changes */ 417 ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1, 418 vlan_vlandev_state, ifv); 419 420 /* Register callback if parent wants to unregister */ 421 ifv->dh_cookie = hook_establish(p->if_detachhooks, 1, 422 vlan_ifdetach, ifv); 423 424 vlan_vlandev_state(ifv); 425 splx(s); 426 427 return 0; 428 } 429 430 int 431 vlan_unconfig(struct ifnet *ifp) 432 { 433 struct ifaddr *ifa; 434 struct sockaddr_dl *sdl; 435 struct ifvlan *ifv; 436 struct ifnet *p; 437 int s; 438 439 ifv = ifp->if_softc; 440 p = ifv->ifv_p; 441 if (p == NULL) 442 return 0; 443 444 s = splnet(); 445 LIST_REMOVE(ifv, ifv_list); 446 if (ifv->lh_cookie != NULL) 447 hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie); 448 /* The cookie is NULL if disestablished externally */ 449 if (ifv->dh_cookie != NULL) 450 hook_disestablish(p->if_detachhooks, ifv->dh_cookie); 451 splx(s); 452 453 /* 454 * Since the interface is being unconfigured, we need to 455 * empty the list of multicast groups that we may have joined 456 * while we were alive and remove them from the parent's list 457 * as well. 458 */ 459 vlan_ether_purgemulti(ifv); 460 461 /* Disconnect from parent. */ 462 ifv->ifv_p = NULL; 463 ifv->ifv_if.if_mtu = ETHERMTU; 464 465 /* Clear our MAC address. */ 466 ifa = ifnet_addrs[ifv->ifv_if.if_index]; 467 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 468 sdl->sdl_type = IFT_ETHER; 469 sdl->sdl_alen = ETHER_ADDR_LEN; 470 bzero(LLADDR(sdl), ETHER_ADDR_LEN); 471 bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 472 473 return 0; 474 } 475 476 void 477 vlan_vlandev_state(void *v) 478 { 479 struct ifvlan *ifv = v; 480 481 if (ifv->ifv_if.if_link_state == ifv->ifv_p->if_link_state) 482 return; 483 484 ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state; 485 ifv->ifv_if.if_baudrate = ifv->ifv_p->if_baudrate; 486 if_link_state_change(&ifv->ifv_if); 487 } 488 489 int 490 vlan_set_promisc(struct ifnet *ifp) 491 { 492 struct ifvlan *ifv = ifp->if_softc; 493 int error = 0; 494 495 if ((ifp->if_flags & IFF_PROMISC) != 0) { 496 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { 497 error = ifpromisc(ifv->ifv_p, 1); 498 if (error == 0) 499 ifv->ifv_flags |= IFVF_PROMISC; 500 } 501 } else { 502 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { 503 error = ifpromisc(ifv->ifv_p, 0); 504 if (error == 0) 505 ifv->ifv_flags &= ~IFVF_PROMISC; 506 } 507 } 508 509 return (0); 510 } 511 512 int 513 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 514 { 515 struct proc *p = curproc; /* XXX */ 516 struct ifaddr *ifa; 517 struct ifnet *pr; 518 struct ifreq *ifr; 519 struct ifvlan *ifv; 520 struct vlanreq vlr; 521 int error = 0, p_mtu = 0, s; 522 523 ifr = (struct ifreq *)data; 524 ifa = (struct ifaddr *)data; 525 ifv = ifp->if_softc; 526 527 switch (cmd) { 528 case SIOCSIFADDR: 529 if (ifv->ifv_p != NULL) { 530 ifp->if_flags |= IFF_UP; 531 532 switch (ifa->ifa_addr->sa_family) { 533 #ifdef INET 534 case AF_INET: 535 arp_ifinit(&ifv->ifv_ac, ifa); 536 break; 537 #endif 538 default: 539 break; 540 } 541 } else { 542 error = EINVAL; 543 } 544 break; 545 546 case SIOCGIFADDR: 547 { 548 struct sockaddr *sa; 549 550 sa = (struct sockaddr *) &ifr->ifr_data; 551 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 552 (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 553 } 554 break; 555 556 case SIOCSIFMTU: 557 if (ifv->ifv_p != NULL) { 558 if (ifv->ifv_p->if_capabilities & IFCAP_VLAN_MTU) 559 p_mtu = ifv->ifv_p->if_mtu; 560 else 561 p_mtu = ifv->ifv_p->if_mtu - EVL_ENCAPLEN; 562 563 if (ifr->ifr_mtu > p_mtu || ifr->ifr_mtu < ETHERMIN) 564 error = EINVAL; 565 else 566 ifp->if_mtu = ifr->ifr_mtu; 567 } else 568 error = EINVAL; 569 570 break; 571 572 case SIOCSETVLAN: 573 if ((error = suser(p, 0)) != 0) 574 break; 575 if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr))) 576 break; 577 if (vlr.vlr_parent[0] == '\0') { 578 s = splnet(); 579 vlan_unconfig(ifp); 580 if (ifp->if_flags & IFF_UP) 581 if_down(ifp); 582 ifp->if_flags &= ~IFF_RUNNING; 583 splx(s); 584 break; 585 } 586 pr = ifunit(vlr.vlr_parent); 587 if (pr == NULL) { 588 error = ENOENT; 589 break; 590 } 591 /* 592 * Don't let the caller set up a VLAN tag with 593 * anything except VLID bits. 594 */ 595 if (vlr.vlr_tag & ~EVL_VLID_MASK) { 596 error = EINVAL; 597 break; 598 } 599 error = vlan_config(ifv, pr, vlr.vlr_tag); 600 if (error) 601 break; 602 ifp->if_flags |= IFF_RUNNING; 603 604 /* Update promiscuous mode, if necessary. */ 605 vlan_set_promisc(ifp); 606 break; 607 608 case SIOCGETVLAN: 609 bzero(&vlr, sizeof vlr); 610 if (ifv->ifv_p) { 611 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 612 "%s", ifv->ifv_p->if_xname); 613 vlr.vlr_tag = ifv->ifv_tag; 614 } 615 error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 616 break; 617 case SIOCSETVLANPRIO: 618 if ((error = suser(p, 0)) != 0) 619 break; 620 if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr))) 621 break; 622 /* 623 * Don't let the caller set up a VLAN priority 624 * outside the range 0-7 625 */ 626 if (vlr.vlr_tag > EVL_PRIO_MAX) { 627 error = EINVAL; 628 break; 629 } 630 ifv->ifv_prio = vlr.vlr_tag; 631 break; 632 case SIOCGETVLANPRIO: 633 bzero(&vlr, sizeof vlr); 634 if (ifv->ifv_p) 635 strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname, 636 sizeof(vlr.vlr_parent)); 637 vlr.vlr_tag = ifv->ifv_prio; 638 error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 639 break; 640 case SIOCSIFFLAGS: 641 /* 642 * For promiscuous mode, we enable promiscuous mode on 643 * the parent if we need promiscuous on the VLAN interface. 644 */ 645 if (ifv->ifv_p != NULL) 646 error = vlan_set_promisc(ifp); 647 break; 648 649 case SIOCADDMULTI: 650 error = (ifv->ifv_p != NULL) ? 651 vlan_ether_addmulti(ifv, ifr) : EINVAL; 652 break; 653 654 case SIOCDELMULTI: 655 error = (ifv->ifv_p != NULL) ? 656 vlan_ether_delmulti(ifv, ifr) : EINVAL; 657 break; 658 default: 659 error = ENOTTY; 660 } 661 return error; 662 } 663 664 665 int 666 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr) 667 { 668 struct ifnet *ifp = ifv->ifv_p; /* Parent. */ 669 struct vlan_mc_entry *mc; 670 u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 671 int error; 672 673 /* XXX: sa_len is too small for such comparison 674 if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage)) 675 return (EINVAL); 676 */ 677 678 error = ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac); 679 if (error != ENETRESET) 680 return (error); 681 682 /* 683 * This is new multicast address. We have to tell parent 684 * about it. Also, remember this multicast address so that 685 * we can delete them on unconfigure. 686 */ 687 mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT); 688 if (mc == NULL) { 689 error = ENOMEM; 690 goto alloc_failed; 691 } 692 693 /* 694 * As ether_addmulti() returns ENETRESET, following two 695 * statement shouldn't fail. 696 */ 697 (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi); 698 ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, mc->mc_enm); 699 memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len); 700 LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries); 701 702 error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr); 703 if (error != 0) 704 goto ioctl_failed; 705 706 return (error); 707 708 ioctl_failed: 709 LIST_REMOVE(mc, mc_entries); 710 free(mc, M_DEVBUF); 711 alloc_failed: 712 (void)ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac); 713 714 return (error); 715 } 716 717 int 718 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr) 719 { 720 struct ifnet *ifp = ifv->ifv_p; /* Parent. */ 721 struct ether_multi *enm; 722 struct vlan_mc_entry *mc; 723 u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 724 int error; 725 726 /* 727 * Find a key to lookup vlan_mc_entry. We have to do this 728 * before calling ether_delmulti for obvious reason. 729 */ 730 if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0) 731 return (error); 732 ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, enm); 733 if (enm == NULL) 734 return (EINVAL); 735 736 LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) 737 if (mc->mc_enm == enm) 738 break; 739 740 /* We won't delete entries we didn't add */ 741 if (mc == NULL) 742 return (EINVAL); 743 744 error = ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac); 745 if (error != ENETRESET) 746 return (error); 747 748 /* We no longer use this multicast address. Tell parent so. */ 749 error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr); 750 if (error == 0) { 751 /* And forget about this address. */ 752 LIST_REMOVE(mc, mc_entries); 753 free(mc, M_DEVBUF); 754 } else 755 (void)ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac); 756 return (error); 757 } 758 759 /* 760 * Delete any multicast address we have asked to add from parent 761 * interface. Called when the vlan is being unconfigured. 762 */ 763 void 764 vlan_ether_purgemulti(struct ifvlan *ifv) 765 { 766 struct ifnet *ifp = ifv->ifv_p; /* Parent. */ 767 struct vlan_mc_entry *mc; 768 union { 769 struct ifreq ifreq; 770 struct { 771 char ifr_name[IFNAMSIZ]; 772 struct sockaddr_storage ifr_ss; 773 } ifreq_storage; 774 } ifreq; 775 struct ifreq *ifr = &ifreq.ifreq; 776 777 memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ); 778 while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) { 779 memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len); 780 (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr); 781 LIST_REMOVE(mc, mc_entries); 782 free(mc, M_DEVBUF); 783 } 784 } 785