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