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