1 /* $NetBSD: if_agr.c,v 1.30 2011/10/19 01:49:50 dyoung Exp $ */ 2 3 /*- 4 * Copyright (c)2005 YAMAMOTO Takashi, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: if_agr.c,v 1.30 2011/10/19 01:49:50 dyoung Exp $"); 31 32 #include "opt_inet.h" 33 34 #include <sys/param.h> 35 #include <sys/callout.h> 36 #include <sys/malloc.h> 37 #include <sys/mbuf.h> 38 #include <sys/systm.h> 39 #include <sys/types.h> 40 #include <sys/queue.h> 41 #include <sys/sockio.h> 42 #include <sys/proc.h> /* XXX for curproc */ 43 #include <sys/kauth.h> 44 #include <sys/xcall.h> 45 46 #include <net/bpf.h> 47 #include <net/if.h> 48 #include <net/if_dl.h> 49 #include <net/if_types.h> 50 #include <net/if_ether.h> 51 52 #if defined(INET) 53 #include <netinet/in.h> 54 #include <netinet/if_inarp.h> 55 #endif 56 57 #include <net/agr/if_agrvar.h> 58 #include <net/agr/if_agrvar_impl.h> 59 #include <net/agr/if_agrioctl.h> 60 #include <net/agr/if_agrsubr.h> 61 #include <net/agr/if_agrethervar.h> 62 63 void agrattach(int); 64 65 static int agr_clone_create(struct if_clone *, int); 66 static int agr_clone_destroy(struct ifnet *); 67 static void agr_start(struct ifnet *); 68 static int agr_setconfig(struct agr_softc *, const struct agrreq *); 69 static int agr_getconfig(struct agr_softc *, struct agrreq *); 70 static int agr_getportlist(struct agr_softc *, struct agrreq *); 71 static int agr_addport(struct ifnet *, struct ifnet *); 72 static int agr_remport(struct ifnet *, struct ifnet *); 73 static int agrreq_copyin(const void *, struct agrreq *); 74 static int agrreq_copyout(void *, struct agrreq *); 75 static int agr_ioctl(struct ifnet *, u_long, void *); 76 static struct agr_port *agr_select_tx_port(struct agr_softc *, struct mbuf *); 77 static int agr_ioctl_filter(struct ifnet *, u_long, void *); 78 static void agr_reset_iftype(struct ifnet *); 79 static int agr_config_promisc(struct agr_softc *); 80 static int agrport_config_promisc_callback(struct agr_port *, void *); 81 static int agrport_config_promisc(struct agr_port *, bool); 82 static int agrport_cleanup(struct agr_softc *, struct agr_port *); 83 84 static int agr_enter(struct agr_softc *); 85 static void agr_exit(struct agr_softc *); 86 static int agr_pause(struct agr_softc *); 87 static void agr_evacuate(struct agr_softc *); 88 static void agr_sync(void); 89 static void agr_ports_lock(struct agr_softc *); 90 static void agr_ports_unlock(struct agr_softc *); 91 static bool agr_ports_enter(struct agr_softc *); 92 static void agr_ports_exit(struct agr_softc *); 93 94 static struct if_clone agr_cloner = 95 IF_CLONE_INITIALIZER("agr", agr_clone_create, agr_clone_destroy); 96 97 /* 98 * EXPORTED FUNCTIONS 99 */ 100 101 /* 102 * agrattch: device attach routine. 103 */ 104 105 void 106 agrattach(int count) 107 { 108 109 if_clone_attach(&agr_cloner); 110 } 111 112 /* 113 * agr_input: frame collector. 114 */ 115 116 void 117 agr_input(struct ifnet *ifp_port, struct mbuf *m) 118 { 119 struct agr_port *port; 120 struct ifnet *ifp; 121 #if NVLAN > 0 122 struct m_tag *mtag; 123 #endif 124 125 port = ifp_port->if_agrprivate; 126 KASSERT(port); 127 ifp = port->port_agrifp; 128 if ((port->port_flags & AGRPORT_COLLECTING) == 0) { 129 m_freem(m); 130 ifp->if_ierrors++; 131 return; 132 } 133 134 ifp->if_ipackets++; 135 m->m_pkthdr.rcvif = ifp; 136 137 #define DNH_DEBUG 138 #if NVLAN > 0 139 /* got a vlan packet? */ 140 if ((mtag = m_tag_find(m, PACKET_TAG_VLAN, NULL)) != NULL) { 141 #ifdef DNH_DEBUG 142 printf("%s: vlan tag %d attached\n", 143 ifp->if_xname, 144 htole16((*(u_int *)(mtag + 1)) & 0xffff)); 145 printf("%s: vlan input\n", ifp->if_xname); 146 #endif 147 vlan_input(ifp, m); 148 return; 149 #ifdef DNH_DEBUG 150 } else { 151 struct ethercom *ec = (void *)ifp; 152 printf("%s: no vlan tag attached, ec_nvlans=%d\n", 153 ifp->if_xname, ec->ec_nvlans); 154 #endif 155 } 156 #endif 157 158 bpf_mtap(ifp, m); 159 (*ifp->if_input)(ifp, m); 160 } 161 162 /* 163 * EXPORTED AGR-INTERNAL FUNCTIONS 164 */ 165 166 void 167 agr_lock(struct agr_softc *sc) 168 { 169 170 mutex_enter(&sc->sc_lock); 171 } 172 173 void 174 agr_unlock(struct agr_softc *sc) 175 { 176 177 mutex_exit(&sc->sc_lock); 178 } 179 180 /* 181 * agr_xmit_frame: transmit a pre-built frame. 182 */ 183 184 int 185 agr_xmit_frame(struct ifnet *ifp_port, struct mbuf *m) 186 { 187 int error; 188 189 struct sockaddr_storage dst0; 190 struct sockaddr *dst; 191 int hdrlen; 192 193 /* 194 * trim off link level header and let if_output re-add it. 195 * XXX better to introduce an API to transmit pre-built frames. 196 */ 197 198 hdrlen = ifp_port->if_hdrlen; 199 if (m->m_pkthdr.len < hdrlen) { 200 m_freem(m); 201 return EINVAL; 202 } 203 memset(&dst0, 0, sizeof(dst0)); 204 dst = (struct sockaddr *)&dst0; 205 dst->sa_family = pseudo_AF_HDRCMPLT; 206 dst->sa_len = hdrlen; 207 m_copydata(m, 0, hdrlen, &dst->sa_data); 208 m_adj(m, hdrlen); 209 210 error = (*ifp_port->if_output)(ifp_port, m, dst, NULL); 211 212 return error; 213 } 214 215 int 216 agrport_ioctl(struct agr_port *port, u_long cmd, void *arg) 217 { 218 struct ifnet *ifp = port->port_ifp; 219 220 KASSERT(ifp->if_agrprivate == (void *)port); 221 KASSERT(ifp->if_ioctl == agr_ioctl_filter); 222 223 return (*port->port_ioctl)(ifp, cmd, arg); 224 } 225 226 /* 227 * INTERNAL FUNCTIONS 228 */ 229 230 /* 231 * Enable vlan hardware assist for the specified port. 232 */ 233 static int 234 agr_vlan_add(struct agr_port *port, void *arg) 235 { 236 struct ifnet *ifp = port->port_ifp; 237 struct ethercom *ec_port = (void *)ifp; 238 int error=0; 239 240 if (ec_port->ec_nvlans++ == 0 && 241 (ec_port->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) { 242 struct ifnet *p = port->port_ifp; 243 /* 244 * Enable Tx/Rx of VLAN-sized frames. 245 */ 246 ec_port->ec_capenable |= ETHERCAP_VLAN_MTU; 247 if (p->if_flags & IFF_UP) { 248 error = if_flags_set(p, p->if_flags); 249 if (error) { 250 if (ec_port->ec_nvlans-- == 1) 251 ec_port->ec_capenable &= 252 ~ETHERCAP_VLAN_MTU; 253 return (error); 254 } 255 } 256 } 257 258 return error; 259 } 260 261 /* 262 * Disable vlan hardware assist for the specified port. 263 */ 264 static int 265 agr_vlan_del(struct agr_port *port, void *arg) 266 { 267 struct ethercom *ec_port = (void *)port->port_ifp; 268 269 /* Disable vlan support */ 270 if (ec_port->ec_nvlans-- == 1) { 271 /* 272 * Disable Tx/Rx of VLAN-sized frames. 273 */ 274 ec_port->ec_capenable &= ~ETHERCAP_VLAN_MTU; 275 if (port->port_ifp->if_flags & IFF_UP) { 276 (void)if_flags_set(port->port_ifp, 277 port->port_ifp->if_flags); 278 } 279 } 280 281 return 0; 282 } 283 284 285 /* 286 * Check for vlan attach/detach. 287 * ec->ec_nvlans is directly modified by the vlan driver. 288 * We keep a local count in sc (sc->sc_nvlans) to detect 289 * when the vlan driver attaches or detaches. 290 * Note the agr interface must be up for this to work. 291 */ 292 static void 293 agr_vlan_check(struct ifnet *ifp, struct agr_softc *sc) 294 { 295 struct ethercom *ec = (void *)ifp; 296 int error; 297 298 /* vlans in sync? */ 299 if (sc->sc_nvlans == ec->ec_nvlans) { 300 return; 301 } 302 303 if (sc->sc_nvlans == 0) { 304 /* vlan added */ 305 error = agr_port_foreach(sc, agr_vlan_add, NULL); 306 sc->sc_nvlans = ec->ec_nvlans; 307 } else if (ec->ec_nvlans == 0) { 308 /* vlan removed */ 309 error = agr_port_foreach(sc, agr_vlan_del, NULL); 310 sc->sc_nvlans = 0; 311 } 312 } 313 314 static int 315 agr_clone_create(struct if_clone *ifc, int unit) 316 { 317 struct agr_softc *sc; 318 struct ifnet *ifp; 319 320 sc = agr_alloc_softc(); 321 TAILQ_INIT(&sc->sc_ports); 322 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NET); 323 mutex_init(&sc->sc_entry_mtx, MUTEX_DEFAULT, IPL_NONE); 324 cv_init(&sc->sc_insc_cv, "agrsoftc"); 325 cv_init(&sc->sc_ports_cv, "agrports"); 326 agrtimer_init(sc); 327 ifp = &sc->sc_if; 328 snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", 329 ifc->ifc_name, unit); 330 331 ifp->if_softc = sc; 332 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 333 ifp->if_start = agr_start; 334 ifp->if_ioctl = agr_ioctl; 335 IFQ_SET_READY(&ifp->if_snd); 336 337 if_attach(ifp); 338 339 agr_reset_iftype(ifp); 340 341 return 0; 342 } 343 344 static void 345 agr_reset_iftype(struct ifnet *ifp) 346 { 347 348 ifp->if_type = IFT_OTHER; 349 ifp->if_dlt = DLT_NULL; 350 ifp->if_addrlen = 0; 351 if_alloc_sadl(ifp); 352 } 353 354 static int 355 agr_clone_destroy(struct ifnet *ifp) 356 { 357 struct agr_softc *sc = ifp->if_softc; 358 int error; 359 360 if ((error = agr_pause(sc)) != 0) 361 return error; 362 363 if_detach(ifp); 364 agrtimer_destroy(sc); 365 /* Now that the ifnet has been detached, and our 366 * component ifnets are disconnected, there can be 367 * no new threads in the softc. Wait for every 368 * thread to get out of the softc. 369 */ 370 agr_evacuate(sc); 371 mutex_destroy(&sc->sc_lock); 372 mutex_destroy(&sc->sc_entry_mtx); 373 cv_destroy(&sc->sc_insc_cv); 374 cv_destroy(&sc->sc_ports_cv); 375 agr_free_softc(sc); 376 377 return 0; 378 } 379 380 static struct agr_port * 381 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m) 382 { 383 384 return (*sc->sc_iftop->iftop_select_tx_port)(sc, m); 385 } 386 387 #if 0 /* "generic" version */ 388 static struct agr_port * 389 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m) 390 { 391 struct agr_port *port; 392 uint32_t hash; 393 394 hash = (*sc->sc_iftop->iftop_hashmbuf)(sc, m); 395 if (sc->sc_nports == 0) 396 return NULL; 397 hash %= sc->sc_nports; 398 port = TAILQ_FIRST(&sc->sc_ports); 399 KASSERT(port != NULL); 400 while (hash--) { 401 port = TAILQ_NEXT(port, port_q); 402 KASSERT(port != NULL); 403 } 404 405 return port; 406 } 407 #endif /* 0 */ 408 409 static void 410 agr_start(struct ifnet *ifp) 411 { 412 struct agr_softc *sc = ifp->if_softc; 413 struct mbuf *m; 414 415 AGR_LOCK(sc); 416 417 while (/* CONSTCOND */ 1) { 418 struct agr_port *port; 419 420 IFQ_DEQUEUE(&ifp->if_snd, m); 421 if (m == NULL) { 422 break; 423 } 424 bpf_mtap(ifp, m); 425 port = agr_select_tx_port(sc, m); 426 if (port) { 427 int error; 428 429 error = agr_xmit_frame(port->port_ifp, m); 430 if (error) { 431 ifp->if_oerrors++; 432 } else { 433 ifp->if_opackets++; 434 } 435 } else { 436 m_freem(m); 437 ifp->if_oerrors++; 438 } 439 } 440 441 AGR_UNLOCK(sc); 442 443 ifp->if_flags &= ~IFF_OACTIVE; 444 } 445 446 static int 447 agr_setconfig(struct agr_softc *sc, const struct agrreq *ar) 448 { 449 struct ifnet *ifp = &sc->sc_if; 450 int cmd = ar->ar_cmd; 451 struct ifnet *ifp_port; 452 int error = 0; 453 char ifname[IFNAMSIZ]; 454 455 memset(ifname, 0, sizeof(ifname)); 456 error = copyin(ar->ar_buf, ifname, 457 MIN(ar->ar_buflen, sizeof(ifname) - 1)); 458 if (error) { 459 return error; 460 } 461 ifp_port = ifunit(ifname); 462 if (ifp_port == NULL) { 463 return ENOENT; 464 } 465 466 agr_ports_lock(sc); 467 switch (cmd) { 468 case AGRCMD_ADDPORT: 469 error = agr_addport(ifp, ifp_port); 470 break; 471 472 case AGRCMD_REMPORT: 473 error = agr_remport(ifp, ifp_port); 474 break; 475 476 default: 477 error = EINVAL; 478 break; 479 } 480 agr_ports_unlock(sc); 481 482 return error; 483 } 484 485 static int 486 agr_getportlist(struct agr_softc *sc, struct agrreq *ar) 487 { 488 struct agr_port *port; 489 struct agrportlist apl; 490 struct agrportinfo api; 491 char *cp = ar->ar_buf; 492 size_t bufleft = (cp == NULL) ? 0 : ar->ar_buflen; 493 int error; 494 495 if (cp != NULL) { 496 memset(&apl, 0, sizeof(apl)); 497 memset(&api, 0, sizeof(api)); 498 499 if (bufleft < sizeof(apl)) { 500 return E2BIG; 501 } 502 apl.apl_nports = sc->sc_nports; 503 error = copyout(&apl, cp, sizeof(apl)); 504 if (error) { 505 return error; 506 } 507 cp += sizeof(apl); 508 } 509 bufleft -= sizeof(apl); 510 511 TAILQ_FOREACH(port, &sc->sc_ports, port_q) { 512 if (cp != NULL) { 513 if (bufleft < sizeof(api)) { 514 return E2BIG; 515 } 516 memcpy(api.api_ifname, port->port_ifp->if_xname, 517 sizeof(api.api_ifname)); 518 api.api_flags = 0; 519 if (port->port_flags & AGRPORT_COLLECTING) { 520 api.api_flags |= AGRPORTINFO_COLLECTING; 521 } 522 if (port->port_flags & AGRPORT_DISTRIBUTING) { 523 api.api_flags |= AGRPORTINFO_DISTRIBUTING; 524 } 525 error = copyout(&api, cp, sizeof(api)); 526 if (error) { 527 return error; 528 } 529 cp += sizeof(api); 530 } 531 bufleft -= sizeof(api); 532 } 533 534 if (cp == NULL) { 535 ar->ar_buflen = -bufleft; /* necessary buffer size */ 536 } 537 538 return 0; 539 } 540 541 static int 542 agr_getconfig(struct agr_softc *sc, struct agrreq *ar) 543 { 544 int cmd = ar->ar_cmd; 545 int error; 546 547 (void)agr_ports_enter(sc); 548 switch (cmd) { 549 case AGRCMD_PORTLIST: 550 error = agr_getportlist(sc, ar); 551 break; 552 553 default: 554 error = EINVAL; 555 break; 556 } 557 agr_ports_exit(sc); 558 559 return error; 560 } 561 562 static int 563 agr_addport(struct ifnet *ifp, struct ifnet *ifp_port) 564 { 565 const struct ifaddr *ifa; 566 struct agr_softc *sc = ifp->if_softc; 567 struct agr_port *port = NULL; 568 int error = 0; 569 570 if (ifp_port->if_ioctl == NULL) { 571 error = EOPNOTSUPP; 572 goto out; 573 } 574 575 if (ifp_port->if_agrprivate) { 576 error = EBUSY; 577 goto out; 578 } 579 580 if (ifp_port->if_start == agr_start) { 581 error = EINVAL; 582 goto out; 583 } 584 585 port = malloc(sizeof(*port) + ifp_port->if_addrlen, M_DEVBUF, 586 M_WAITOK | M_ZERO); 587 if (port == NULL) { 588 error = ENOMEM; 589 goto out; 590 } 591 port->port_flags = AGRPORT_LARVAL; 592 593 IFADDR_FOREACH(ifa, ifp_port) { 594 if (ifa->ifa_addr->sa_family != AF_LINK) { 595 error = EBUSY; 596 goto out; 597 } 598 } 599 600 if (sc->sc_nports == 0) { 601 switch (ifp_port->if_type) { 602 case IFT_ETHER: 603 sc->sc_iftop = &agrether_ops; 604 break; 605 606 default: 607 error = EPROTONOSUPPORT; /* XXX */ 608 goto out; 609 } 610 611 error = (*sc->sc_iftop->iftop_ctor)(sc, ifp_port); 612 if (error) 613 goto out; 614 agrtimer_start(sc); 615 } else { 616 if (ifp->if_type != ifp_port->if_type) { 617 error = EINVAL; 618 goto out; 619 } 620 if (ifp->if_addrlen != ifp_port->if_addrlen) { 621 error = EINVAL; 622 goto out; 623 } 624 } 625 626 memcpy(port->port_origlladdr, CLLADDR(ifp_port->if_sadl), 627 ifp_port->if_addrlen); 628 629 /* 630 * start to modify ifp_port. 631 */ 632 633 /* 634 * XXX this should probably be SIOCALIFADDR but that doesn't 635 * appear to work (ENOTTY). We want to change the mac address 636 * of each port to that of the first port. No need for arps 637 * since there are no inet addresses assigned to the ports. 638 */ 639 error = if_addr_init(ifp_port, ifp->if_dl, true); 640 641 if (error) { 642 printf("%s: if_addr_init error %d\n", __func__, error); 643 goto cleanup; 644 } 645 port->port_flags |= AGRPORT_LADDRCHANGED; 646 647 ifp->if_type = ifp_port->if_type; 648 AGR_LOCK(sc); 649 650 port->port_ifp = ifp_port; 651 ifp_port->if_agrprivate = port; 652 port->port_agrifp = ifp; 653 TAILQ_INSERT_TAIL(&sc->sc_ports, port, port_q); 654 sc->sc_nports++; 655 656 port->port_ioctl = ifp_port->if_ioctl; 657 ifp_port->if_ioctl = agr_ioctl_filter; 658 659 port->port_flags |= AGRPORT_ATTACHED; 660 661 AGR_UNLOCK(sc); 662 663 error = (*sc->sc_iftop->iftop_portinit)(sc, port); 664 if (error) { 665 printf("%s: portinit error %d\n", __func__, error); 666 goto cleanup; 667 } 668 669 ifp->if_flags |= IFF_RUNNING; 670 671 agrport_config_promisc(port, (ifp->if_flags & IFF_PROMISC) != 0); 672 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, true); 673 if (error) { 674 printf("%s: configmulti error %d\n", __func__, error); 675 goto cleanup; 676 } 677 678 AGR_LOCK(sc); 679 port->port_flags &= ~AGRPORT_LARVAL; 680 AGR_UNLOCK(sc); 681 out: 682 if (error && port) { 683 free(port, M_DEVBUF); 684 } 685 return error; 686 687 cleanup: 688 if (agrport_cleanup(sc, port)) { 689 printf("%s: error on cleanup\n", __func__); 690 691 port = NULL; /* XXX */ 692 } 693 694 if (sc->sc_nports == 0) { 695 KASSERT(TAILQ_EMPTY(&sc->sc_ports)); 696 agrtimer_stop(sc); 697 (*sc->sc_iftop->iftop_dtor)(sc); 698 sc->sc_iftop = NULL; 699 agr_reset_iftype(ifp); 700 } else { 701 KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); 702 } 703 704 goto out; 705 } 706 707 static int 708 agr_remport(struct ifnet *ifp, struct ifnet *ifp_port) 709 { 710 struct agr_softc *sc = ifp->if_softc; 711 struct agr_port *port; 712 int error = 0; 713 714 if (ifp_port->if_agrprivate == NULL) { 715 error = ENOENT; 716 return error; 717 } 718 719 port = ifp_port->if_agrprivate; 720 if (port->port_agrifp != ifp) { 721 error = EINVAL; 722 return error; 723 } 724 725 KASSERT(sc->sc_nports > 0); 726 727 AGR_LOCK(sc); 728 port->port_flags |= AGRPORT_DETACHING; 729 AGR_UNLOCK(sc); 730 731 error = (*sc->sc_iftop->iftop_portfini)(sc, port); 732 if (error) { 733 /* XXX XXX */ 734 printf("%s: portfini error %d\n", __func__, error); 735 goto out; 736 } 737 738 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, false); 739 if (error) { 740 /* XXX XXX */ 741 printf("%s: configmulti_port error %d\n", __func__, error); 742 goto out; 743 } 744 745 error = agrport_cleanup(sc, port); 746 if (error) { 747 /* XXX XXX */ 748 printf("%s: agrport_cleanup error %d\n", __func__, error); 749 goto out; 750 } 751 752 free(port, M_DEVBUF); 753 754 out: 755 if (sc->sc_nports == 0) { 756 KASSERT(TAILQ_EMPTY(&sc->sc_ports)); 757 agrtimer_stop(sc); 758 (*sc->sc_iftop->iftop_dtor)(sc); 759 sc->sc_iftop = NULL; 760 /* XXX should purge all addresses? */ 761 agr_reset_iftype(ifp); 762 } else { 763 KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); 764 } 765 766 return error; 767 } 768 769 static int 770 agrport_cleanup(struct agr_softc *sc, struct agr_port *port) 771 { 772 struct ifnet *ifp_port = port->port_ifp; 773 int error; 774 int result = 0; 775 776 error = agrport_config_promisc(port, false); 777 if (error) { 778 printf("%s: config_promisc error %d\n", __func__, error); 779 result = error; 780 } 781 782 if ((port->port_flags & AGRPORT_LADDRCHANGED)) { 783 #if 0 784 memcpy(LLADDR(ifp_port->if_sadl), port->port_origlladdr, 785 ifp_port->if_addrlen); 786 if (ifp_port->if_init != NULL) { 787 error = (*ifp_port->if_init)(ifp_port); 788 } 789 #else 790 union { 791 struct sockaddr sa; 792 struct sockaddr_dl sdl; 793 struct sockaddr_storage ss; 794 } u; 795 struct ifaddr ifa; 796 797 sockaddr_dl_init(&u.sdl, sizeof(u.ss), 798 0, ifp_port->if_type, NULL, 0, 799 port->port_origlladdr, ifp_port->if_addrlen); 800 memset(&ifa, 0, sizeof(ifa)); 801 ifa.ifa_addr = &u.sa; 802 error = agrport_ioctl(port, SIOCINITIFADDR, &ifa); 803 #endif 804 if (error) { 805 printf("%s: if_init error %d\n", __func__, error); 806 result = error; 807 } else { 808 port->port_flags &= ~AGRPORT_LADDRCHANGED; 809 } 810 } 811 812 AGR_LOCK(sc); 813 if ((port->port_flags & AGRPORT_ATTACHED)) { 814 ifp_port->if_agrprivate = NULL; 815 816 TAILQ_REMOVE(&sc->sc_ports, port, port_q); 817 sc->sc_nports--; 818 819 KASSERT(ifp_port->if_ioctl == agr_ioctl_filter); 820 ifp_port->if_ioctl = port->port_ioctl; 821 822 port->port_flags &= ~AGRPORT_ATTACHED; 823 } 824 AGR_UNLOCK(sc); 825 826 return result; 827 } 828 829 static int 830 agr_ioctl_multi(struct ifnet *ifp, u_long cmd, struct ifreq *ifr) 831 { 832 struct agr_softc *sc = ifp->if_softc; 833 int error; 834 835 error = (*sc->sc_iftop->iftop_configmulti_ifreq)(sc, ifr, 836 (cmd == SIOCADDMULTI)); 837 838 return error; 839 } 840 841 /* 842 * XXX an incomplete hack; can't filter ioctls handled ifioctl(). 843 * 844 * the intention here is to prevent operations on underlying interfaces 845 * so that their states are not changed in the way that agr(4) doesn't 846 * expect. cf. the BUGS section in the agr(4) manual page. 847 */ 848 static int 849 agr_ioctl_filter(struct ifnet *ifp, u_long cmd, void *arg) 850 { 851 struct agr_port *port = ifp->if_agrprivate; 852 int error; 853 854 KASSERT(port); 855 856 switch (cmd) { 857 case SIOCADDMULTI: /* add m'cast addr */ 858 case SIOCAIFADDR: /* add/chg IF alias */ 859 case SIOCALIFADDR: /* add IF addr */ 860 case SIOCDELMULTI: /* del m'cast addr */ 861 case SIOCDIFADDR: /* delete IF addr */ 862 case SIOCDIFPHYADDR: /* delete gif addrs */ 863 case SIOCDLIFADDR: /* delete IF addr */ 864 case SIOCINITIFADDR: 865 case SIOCSDRVSPEC: /* set driver-specific parameters */ 866 case SIOCSIFADDR: /* set ifnet address */ 867 case SIOCSIFBRDADDR: /* set broadcast addr */ 868 case SIOCSIFDSTADDR: /* set p-p address */ 869 case SIOCSIFGENERIC: /* generic IF set op */ 870 case SIOCSIFMEDIA: /* set net media */ 871 case SIOCSIFMETRIC: /* set IF metric */ 872 case SIOCSIFMTU: /* set ifnet mtu */ 873 case SIOCSIFNETMASK: /* set net addr mask */ 874 case SIOCSIFPHYADDR: /* set gif addres */ 875 case SIOCSLIFPHYADDR: /* set gif addrs */ 876 case SIOCSVH: /* set carp param */ 877 error = EBUSY; 878 break; 879 case SIOCSIFCAP: /* XXX */ 880 case SIOCSIFFLAGS: /* XXX */ 881 default: 882 error = agrport_ioctl(port, cmd, arg); 883 break; 884 } 885 return error; 886 } 887 888 static int 889 agrreq_copyin(const void *ubuf, struct agrreq *ar) 890 { 891 int error; 892 893 error = copyin(ubuf, ar, sizeof(*ar)); 894 if (error) { 895 return error; 896 } 897 898 if (ar->ar_version != AGRREQ_VERSION) { 899 return EINVAL; 900 } 901 902 return 0; 903 } 904 905 static int 906 agrreq_copyout(void *ubuf, struct agrreq *ar) 907 { 908 int error; 909 910 KASSERT(ar->ar_version == AGRREQ_VERSION); 911 912 error = copyout(ar, ubuf, sizeof(*ar)); 913 if (error) { 914 return error; 915 } 916 917 return 0; 918 } 919 920 /* Make sure that if any interrupt handlers are out of the softc. */ 921 static void 922 agr_sync(void) 923 { 924 uint64_t h; 925 926 if (!mp_online) 927 return; 928 929 h = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); 930 xc_wait(h); 931 } 932 933 static int 934 agr_pause(struct agr_softc *sc) 935 { 936 int error; 937 938 mutex_enter(&sc->sc_entry_mtx); 939 if ((error = sc->sc_noentry) != 0) 940 goto out; 941 942 sc->sc_noentry = EBUSY; 943 944 while (sc->sc_insc != 0) 945 cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx); 946 947 if (sc->sc_nports == 0) { 948 sc->sc_noentry = ENXIO; 949 } else { 950 sc->sc_noentry = 0; 951 error = EBUSY; 952 } 953 cv_broadcast(&sc->sc_insc_cv); 954 out: 955 mutex_exit(&sc->sc_entry_mtx); 956 return error; 957 } 958 959 static void 960 agr_evacuate(struct agr_softc *sc) 961 { 962 mutex_enter(&sc->sc_entry_mtx); 963 cv_broadcast(&sc->sc_insc_cv); 964 while (sc->sc_insc != 0 || sc->sc_paused != 0) 965 cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx); 966 mutex_exit(&sc->sc_entry_mtx); 967 968 agr_sync(); 969 } 970 971 static int 972 agr_enter(struct agr_softc *sc) 973 { 974 int error; 975 976 mutex_enter(&sc->sc_entry_mtx); 977 sc->sc_paused++; 978 while ((error = sc->sc_noentry) == EBUSY) 979 cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx); 980 sc->sc_paused--; 981 if (error == 0) 982 sc->sc_insc++; 983 mutex_exit(&sc->sc_entry_mtx); 984 985 return error; 986 } 987 988 static void 989 agr_exit(struct agr_softc *sc) 990 { 991 mutex_enter(&sc->sc_entry_mtx); 992 if (--sc->sc_insc == 0) 993 cv_signal(&sc->sc_insc_cv); 994 mutex_exit(&sc->sc_entry_mtx); 995 } 996 997 static bool 998 agr_ports_enter(struct agr_softc *sc) 999 { 1000 mutex_enter(&sc->sc_entry_mtx); 1001 while (sc->sc_wrports) 1002 cv_wait(&sc->sc_ports_cv, &sc->sc_entry_mtx); 1003 sc->sc_rdports++; 1004 mutex_exit(&sc->sc_entry_mtx); 1005 1006 return true; 1007 } 1008 1009 static void 1010 agr_ports_exit(struct agr_softc *sc) 1011 { 1012 mutex_enter(&sc->sc_entry_mtx); 1013 if (--sc->sc_rdports == 0) 1014 cv_signal(&sc->sc_ports_cv); 1015 mutex_exit(&sc->sc_entry_mtx); 1016 } 1017 1018 static void 1019 agr_ports_lock(struct agr_softc *sc) 1020 { 1021 mutex_enter(&sc->sc_entry_mtx); 1022 while (sc->sc_rdports != 0) 1023 cv_wait(&sc->sc_ports_cv, &sc->sc_entry_mtx); 1024 sc->sc_wrports = true; 1025 mutex_exit(&sc->sc_entry_mtx); 1026 } 1027 1028 static void 1029 agr_ports_unlock(struct agr_softc *sc) 1030 { 1031 mutex_enter(&sc->sc_entry_mtx); 1032 sc->sc_wrports = false; 1033 cv_signal(&sc->sc_ports_cv); 1034 mutex_exit(&sc->sc_entry_mtx); 1035 } 1036 1037 static int 1038 agr_ioctl(struct ifnet *ifp, const u_long cmd, void *data) 1039 { 1040 struct agr_softc *sc = ifp->if_softc; 1041 struct ifreq *ifr = (struct ifreq *)data; 1042 struct ifaddr *ifa = (struct ifaddr *)data; 1043 struct agrreq ar; 1044 int error; 1045 bool in_ports = false; 1046 int s; 1047 1048 if ((error = agr_enter(sc)) != 0) 1049 return error; 1050 1051 s = splnet(); 1052 1053 switch (cmd) { 1054 case SIOCINITIFADDR: 1055 in_ports = agr_ports_enter(sc); 1056 if (sc->sc_nports == 0) { 1057 error = EINVAL; 1058 break; 1059 } 1060 ifp->if_flags |= IFF_UP; 1061 switch (ifa->ifa_addr->sa_family) { 1062 #if defined(INET) 1063 case AF_INET: 1064 arp_ifinit(ifp, ifa); 1065 break; 1066 #endif 1067 default: 1068 break; 1069 } 1070 break; 1071 1072 #if 0 /* notyet */ 1073 case SIOCSIFMTU: 1074 #endif 1075 1076 case SIOCSIFFLAGS: 1077 /* 1078 * Check for a change in vlan status. This ioctl is the 1079 * only way we can tell that a vlan has attached or detached. 1080 * Note the agr interface must be up. 1081 */ 1082 agr_vlan_check(ifp, sc); 1083 1084 if ((error = ifioctl_common(ifp, cmd, data)) != 0) 1085 break; 1086 agr_config_promisc(sc); 1087 break; 1088 1089 case SIOCSETAGR: 1090 splx(s); 1091 error = kauth_authorize_network(kauth_cred_get(), 1092 KAUTH_NETWORK_INTERFACE, 1093 KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, 1094 NULL); 1095 if (!error) { 1096 error = agrreq_copyin(ifr->ifr_data, &ar); 1097 } 1098 if (!error) { 1099 error = agr_setconfig(sc, &ar); 1100 } 1101 s = splnet(); 1102 break; 1103 1104 case SIOCGETAGR: 1105 splx(s); 1106 error = agrreq_copyin(ifr->ifr_data, &ar); 1107 if (!error) { 1108 error = agr_getconfig(sc, &ar); 1109 } 1110 if (!error) { 1111 error = agrreq_copyout(ifr->ifr_data, &ar); 1112 } 1113 s = splnet(); 1114 break; 1115 1116 case SIOCADDMULTI: 1117 case SIOCDELMULTI: 1118 in_ports = agr_ports_enter(sc); 1119 if (sc->sc_nports == 0) 1120 error = EINVAL; 1121 else 1122 error = agr_ioctl_multi(ifp, cmd, ifr); 1123 break; 1124 1125 default: 1126 error = ifioctl_common(ifp, cmd, data); 1127 break; 1128 } 1129 1130 if (in_ports) 1131 agr_ports_exit(sc); 1132 1133 splx(s); 1134 1135 agr_exit(sc); 1136 1137 return error; 1138 } 1139 1140 static int 1141 agr_config_promisc(struct agr_softc *sc) 1142 { 1143 int error; 1144 1145 agr_port_foreach(sc, agrport_config_promisc_callback, &error); 1146 1147 return error; 1148 } 1149 1150 static int 1151 agrport_config_promisc_callback(struct agr_port *port, void *arg) 1152 { 1153 struct agr_softc *sc = AGR_SC_FROM_PORT(port); 1154 int *errorp = arg; 1155 int error; 1156 bool promisc; 1157 1158 promisc = (sc->sc_if.if_flags & IFF_PROMISC) != 0; 1159 1160 error = agrport_config_promisc(port, promisc); 1161 if (error) { 1162 *errorp = error; 1163 } 1164 1165 return 0; 1166 } 1167 1168 static int 1169 agrport_config_promisc(struct agr_port *port, bool promisc) 1170 { 1171 int error; 1172 1173 if (( promisc && (port->port_flags & AGRPORT_PROMISC) != 0) || 1174 (!promisc && (port->port_flags & AGRPORT_PROMISC) == 0)) { 1175 return 0; 1176 } 1177 1178 error = ifpromisc(port->port_ifp, promisc); 1179 if (error == 0) { 1180 if (promisc) { 1181 port->port_flags |= AGRPORT_PROMISC; 1182 } else { 1183 port->port_flags &= ~AGRPORT_PROMISC; 1184 } 1185 } 1186 1187 return error; 1188 } 1189