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