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