1 /* $NetBSD: if_agr.c,v 1.19 2007/12/08 15:04:29 ad 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.19 2007/12/08 15:04:29 ad Exp $"); 31 32 #include "bpfilter.h" 33 #include "opt_inet.h" 34 35 #include <sys/param.h> 36 #include <sys/callout.h> 37 #include <sys/malloc.h> 38 #include <sys/mbuf.h> 39 #include <sys/systm.h> 40 #include <sys/types.h> 41 #include <sys/queue.h> 42 #include <sys/sockio.h> 43 #include <sys/proc.h> /* XXX for curproc */ 44 #include <sys/kauth.h> 45 46 #if NBPFILTER > 0 47 #include <net/bpf.h> 48 #endif 49 #include <net/if.h> 50 #include <net/if_dl.h> 51 #include <net/if_types.h> 52 53 #if defined(INET) 54 #include <netinet/in.h> 55 #include <netinet/if_inarp.h> 56 #endif 57 58 #include <net/agr/if_agrvar.h> 59 #include <net/agr/if_agrvar_impl.h> 60 #include <net/agr/if_agrioctl.h> 61 #include <net/agr/if_agrsubr.h> 62 #include <net/agr/if_agrethervar.h> 63 64 void agrattach(int); 65 66 static int agr_clone_create(struct if_clone *, int); 67 static int agr_clone_destroy(struct ifnet *); 68 static void agr_start(struct ifnet *); 69 static int agr_setconfig(struct ifnet *, const struct agrreq *); 70 static int agr_getconfig(struct ifnet *, struct agrreq *); 71 static int agr_getportlist(struct ifnet *, struct agrreq *); 72 static int agr_addport(struct ifnet *, struct ifnet *); 73 static int agr_remport(struct ifnet *, struct ifnet *); 74 static int agrreq_copyin(const void *, struct agrreq *); 75 static int agrreq_copyout(void *, struct agrreq *); 76 static int agr_ioctl(struct ifnet *, u_long, void *); 77 static struct agr_port *agr_select_tx_port(struct agr_softc *, struct mbuf *); 78 static int agr_ioctl_filter(struct ifnet *, u_long, void *); 79 static void agr_reset_iftype(struct ifnet *); 80 static int agr_config_promisc(struct agr_softc *); 81 static int agrport_config_promisc_callback(struct agr_port *, void *); 82 static int agrport_config_promisc(struct agr_port *, bool); 83 static int agrport_cleanup(struct agr_softc *, struct agr_port *); 84 85 static struct if_clone agr_cloner = 86 IF_CLONE_INITIALIZER("agr", agr_clone_create, agr_clone_destroy); 87 88 /* 89 * EXPORTED FUNCTIONS 90 */ 91 92 /* 93 * agrattch: device attach routine. 94 */ 95 96 void 97 agrattach(int count) 98 { 99 100 if_clone_attach(&agr_cloner); 101 } 102 103 /* 104 * agr_input: frame collector. 105 */ 106 107 void 108 agr_input(struct ifnet *ifp_port, struct mbuf *m) 109 { 110 struct agr_port *port; 111 struct ifnet *ifp; 112 113 port = ifp_port->if_agrprivate; 114 KASSERT(port); 115 ifp = port->port_agrifp; 116 if ((port->port_flags & AGRPORT_COLLECTING) == 0) { 117 m_freem(m); 118 ifp->if_ierrors++; 119 return; 120 } 121 122 ifp->if_ipackets++; 123 m->m_pkthdr.rcvif = ifp; 124 125 #if NBPFILTER > 0 126 if (ifp->if_bpf) { 127 bpf_mtap(ifp->if_bpf, m); 128 } 129 #endif 130 131 (*ifp->if_input)(ifp, m); 132 } 133 134 /* 135 * EXPORTED AGR-INTERNAL FUNCTIONS 136 */ 137 138 void 139 agr_lock(struct agr_softc *sc) 140 { 141 142 mutex_enter(&sc->sc_lock); 143 } 144 145 void 146 agr_unlock(struct agr_softc *sc) 147 { 148 149 mutex_exit(&sc->sc_lock); 150 } 151 152 void 153 agr_ioctl_lock(struct agr_softc *sc) 154 { 155 156 mutex_enter(&sc->sc_ioctl_lock); 157 } 158 159 void 160 agr_ioctl_unlock(struct agr_softc *sc) 161 { 162 163 mutex_exit(&sc->sc_ioctl_lock); 164 } 165 166 /* 167 * agr_xmit_frame: transmit a pre-built frame. 168 */ 169 170 int 171 agr_xmit_frame(struct ifnet *ifp_port, struct mbuf *m) 172 { 173 int error; 174 175 struct sockaddr_storage dst0; 176 struct sockaddr *dst; 177 int hdrlen; 178 179 /* 180 * trim off link level header and let if_output re-add it. 181 * XXX better to introduce an API to transmit pre-built frames. 182 */ 183 184 hdrlen = ifp_port->if_hdrlen; 185 if (m->m_pkthdr.len < hdrlen) { 186 m_freem(m); 187 return EINVAL; 188 } 189 memset(&dst0, 0, sizeof(dst0)); 190 dst = (struct sockaddr *)&dst0; 191 dst->sa_family = pseudo_AF_HDRCMPLT; 192 dst->sa_len = hdrlen; 193 m_copydata(m, 0, hdrlen, &dst->sa_data); 194 m_adj(m, hdrlen); 195 196 error = (*ifp_port->if_output)(ifp_port, m, dst, NULL); 197 198 return error; 199 } 200 201 int 202 agrport_ioctl(struct agr_port *port, u_long cmd, void *arg) 203 { 204 struct ifnet *ifp = port->port_ifp; 205 206 KASSERT(ifp->if_agrprivate == (void *)port); 207 KASSERT(ifp->if_ioctl == agr_ioctl_filter); 208 209 return (*port->port_ioctl)(ifp, cmd, arg); 210 } 211 212 /* 213 * INTERNAL FUNCTIONS 214 */ 215 216 static int 217 agr_clone_create(struct if_clone *ifc, int unit) 218 { 219 struct agr_softc *sc; 220 struct ifnet *ifp; 221 222 sc = agr_alloc_softc(); 223 TAILQ_INIT(&sc->sc_ports); 224 mutex_init(&sc->sc_ioctl_lock, MUTEX_DRIVER, IPL_NONE); 225 mutex_init(&sc->sc_lock, MUTEX_DRIVER, IPL_NET); 226 agrtimer_init(sc); 227 ifp = &sc->sc_if; 228 snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", 229 ifc->ifc_name, unit); 230 231 ifp->if_softc = sc; 232 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 233 ifp->if_start = agr_start; 234 ifp->if_ioctl = agr_ioctl; 235 IFQ_SET_READY(&ifp->if_snd); 236 237 if_attach(ifp); 238 239 agr_reset_iftype(ifp); 240 241 return 0; 242 } 243 244 static void 245 agr_reset_iftype(struct ifnet *ifp) 246 { 247 248 ifp->if_type = IFT_OTHER; 249 ifp->if_dlt = DLT_NULL; 250 ifp->if_addrlen = 0; 251 if_alloc_sadl(ifp); 252 } 253 254 static int 255 agr_clone_destroy(struct ifnet *ifp) 256 { 257 struct agr_softc *sc = ifp->if_softc; 258 int error; 259 260 agr_ioctl_lock(sc); 261 262 AGR_LOCK(sc); 263 if (sc->sc_nports > 0) { 264 error = EBUSY; 265 } else { 266 error = 0; 267 } 268 AGR_UNLOCK(sc); 269 270 agr_ioctl_unlock(sc); 271 272 if (error == 0) { 273 if_detach(ifp); 274 mutex_destroy(&sc->sc_ioctl_lock); 275 mutex_destroy(&sc->sc_lock); 276 agr_free_softc(sc); 277 } 278 279 return error; 280 } 281 282 static struct agr_port * 283 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m) 284 { 285 286 return (*sc->sc_iftop->iftop_select_tx_port)(sc, m); 287 } 288 289 #if 0 /* "generic" version */ 290 static struct agr_port * 291 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m) 292 { 293 struct agr_port *port; 294 uint32_t hash; 295 296 hash = (*sc->sc_iftop->iftop_hashmbuf)(sc, m); 297 if (sc->sc_nports == 0) 298 return NULL; 299 hash %= sc->sc_nports; 300 port = TAILQ_FIRST(&sc->sc_ports); 301 KASSERT(port != NULL); 302 while (hash--) { 303 port = TAILQ_NEXT(port, port_q); 304 KASSERT(port != NULL); 305 } 306 307 return port; 308 } 309 #endif /* 0 */ 310 311 static void 312 agr_start(struct ifnet *ifp) 313 { 314 struct agr_softc *sc = ifp->if_softc; 315 struct mbuf *m; 316 317 AGR_LOCK(sc); 318 319 while (/* CONSTCOND */ 1) { 320 struct agr_port *port; 321 322 IFQ_DEQUEUE(&ifp->if_snd, m); 323 if (m == NULL) { 324 break; 325 } 326 #if NBPFILTER > 0 327 if (ifp->if_bpf) { 328 bpf_mtap(ifp->if_bpf, m); 329 } 330 #endif 331 port = agr_select_tx_port(sc, m); 332 if (port) { 333 int error; 334 335 error = agr_xmit_frame(port->port_ifp, m); 336 if (error) { 337 ifp->if_oerrors++; 338 } else { 339 ifp->if_opackets++; 340 } 341 } else { 342 m_freem(m); 343 ifp->if_oerrors++; 344 } 345 } 346 347 AGR_UNLOCK(sc); 348 349 ifp->if_flags &= ~IFF_OACTIVE; 350 } 351 352 static int 353 agr_setconfig(struct ifnet *ifp, const struct agrreq *ar) 354 { 355 int cmd = ar->ar_cmd; 356 struct ifnet *ifp_port; 357 int error = 0; 358 char ifname[IFNAMSIZ]; 359 360 memset(ifname, 0, sizeof(ifname)); 361 error = copyin(ar->ar_buf, ifname, 362 MIN(ar->ar_buflen, sizeof(ifname) - 1)); 363 if (error) { 364 return error; 365 } 366 ifp_port = ifunit(ifname); 367 if (ifp_port == NULL) { 368 return ENOENT; 369 } 370 371 switch (cmd) { 372 case AGRCMD_ADDPORT: 373 error = agr_addport(ifp, ifp_port); 374 break; 375 376 case AGRCMD_REMPORT: 377 error = agr_remport(ifp, ifp_port); 378 break; 379 380 default: 381 error = EINVAL; 382 break; 383 } 384 385 return error; 386 } 387 388 static int 389 agr_getportlist(struct ifnet *ifp, struct agrreq *ar) 390 { 391 struct agr_softc *sc = ifp->if_softc; 392 struct agr_port *port; 393 struct agrportlist apl; 394 struct agrportinfo api; 395 char *cp = ar->ar_buf; 396 size_t bufleft = (cp == NULL) ? 0 : ar->ar_buflen; 397 int error; 398 399 if (cp != NULL) { 400 memset(&apl, 0, sizeof(apl)); 401 memset(&api, 0, sizeof(api)); 402 403 if (bufleft < sizeof(apl)) { 404 return E2BIG; 405 } 406 apl.apl_nports = sc->sc_nports; 407 error = copyout(&apl, cp, sizeof(apl)); 408 if (error) { 409 return error; 410 } 411 cp += sizeof(apl); 412 } 413 bufleft -= sizeof(apl); 414 415 TAILQ_FOREACH(port, &sc->sc_ports, port_q) { 416 if (cp != NULL) { 417 if (bufleft < sizeof(api)) { 418 return E2BIG; 419 } 420 memcpy(api.api_ifname, port->port_ifp->if_xname, 421 sizeof(api.api_ifname)); 422 api.api_flags = 0; 423 if (port->port_flags & AGRPORT_COLLECTING) { 424 api.api_flags |= AGRPORTINFO_COLLECTING; 425 } 426 if (port->port_flags & AGRPORT_DISTRIBUTING) { 427 api.api_flags |= AGRPORTINFO_DISTRIBUTING; 428 } 429 error = copyout(&api, cp, sizeof(api)); 430 if (error) { 431 return error; 432 } 433 cp += sizeof(api); 434 } 435 bufleft -= sizeof(api); 436 } 437 438 if (cp == NULL) { 439 ar->ar_buflen = -bufleft; /* necessary buffer size */ 440 } 441 442 return 0; 443 } 444 445 static int 446 agr_getconfig(struct ifnet *ifp, struct agrreq *ar) 447 { 448 int cmd = ar->ar_cmd; 449 int error; 450 451 switch (cmd) { 452 case AGRCMD_PORTLIST: 453 error = agr_getportlist(ifp, ar); 454 break; 455 456 default: 457 error = EINVAL; 458 break; 459 } 460 461 return error; 462 } 463 464 static int 465 agr_addport(struct ifnet *ifp, struct ifnet *ifp_port) 466 { 467 struct agr_softc *sc = ifp->if_softc; 468 struct agr_port *port = NULL; 469 int error = 0; 470 471 if (ifp_port->if_ioctl == NULL) { 472 error = EOPNOTSUPP; 473 goto out; 474 } 475 476 if (ifp_port->if_agrprivate) { 477 error = EBUSY; 478 goto out; 479 } 480 481 if (ifp_port->if_start == agr_start) { 482 error = EINVAL; 483 goto out; 484 } 485 486 port = malloc(sizeof(*port) + ifp_port->if_addrlen, M_DEVBUF, 487 M_WAITOK | M_ZERO); 488 if (port == NULL) { 489 error = ENOMEM; 490 goto out; 491 } 492 port->port_flags = AGRPORT_LARVAL; 493 494 if (IFADDR_NEXT(IFADDR_FIRST(ifp_port)) != NULL) { 495 error = EBUSY; 496 goto out; 497 } 498 499 if (sc->sc_nports == 0) { 500 switch (ifp_port->if_type) { 501 case IFT_ETHER: 502 sc->sc_iftop = &agrether_ops; 503 break; 504 505 default: 506 error = EPROTONOSUPPORT; /* XXX */ 507 goto out; 508 } 509 510 error = (*sc->sc_iftop->iftop_ctor)(sc, ifp_port); 511 if (error) 512 goto out; 513 agrtimer_start(sc); 514 } else { 515 if (ifp->if_type != ifp_port->if_type) { 516 error = EINVAL; 517 goto out; 518 } 519 if (ifp->if_addrlen != ifp_port->if_addrlen) { 520 error = EINVAL; 521 goto out; 522 } 523 } 524 525 memcpy(port->port_origlladdr, CLLADDR(ifp_port->if_sadl), 526 ifp_port->if_addrlen); 527 528 /* 529 * start to modify ifp_port. 530 */ 531 532 error = (*ifp_port->if_ioctl)(ifp_port, SIOCSIFADDR, 533 (void *)IFADDR_FIRST(ifp)); 534 535 if (error) { 536 printf("%s: SIOCSIFADDR error %d\n", __func__, error); 537 goto cleanup; 538 } 539 port->port_flags |= AGRPORT_LADDRCHANGED; 540 541 ifp->if_type = ifp_port->if_type; 542 AGR_LOCK(sc); 543 544 port->port_ifp = ifp_port; 545 ifp_port->if_agrprivate = port; 546 port->port_agrifp = ifp; 547 TAILQ_INSERT_TAIL(&sc->sc_ports, port, port_q); 548 sc->sc_nports++; 549 550 port->port_ioctl = ifp_port->if_ioctl; 551 ifp_port->if_ioctl = agr_ioctl_filter; 552 553 port->port_flags |= AGRPORT_ATTACHED; 554 555 AGR_UNLOCK(sc); 556 557 error = (*sc->sc_iftop->iftop_portinit)(sc, port); 558 if (error) { 559 printf("%s: portinit error %d\n", __func__, error); 560 goto cleanup; 561 } 562 563 ifp->if_flags |= IFF_RUNNING; 564 565 agrport_config_promisc(port, (ifp->if_flags & IFF_PROMISC) != 0); 566 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, true); 567 if (error) { 568 printf("%s: configmulti error %d\n", __func__, error); 569 goto cleanup; 570 } 571 572 AGR_LOCK(sc); 573 port->port_flags &= ~AGRPORT_LARVAL; 574 AGR_UNLOCK(sc); 575 out: 576 if (error && port) { 577 free(port, M_DEVBUF); 578 } 579 return error; 580 581 cleanup: 582 if (agrport_cleanup(sc, port)) { 583 printf("%s: error on cleanup\n", __func__); 584 585 port = NULL; /* XXX */ 586 } 587 588 if (sc->sc_nports == 0) { 589 KASSERT(TAILQ_EMPTY(&sc->sc_ports)); 590 agrtimer_stop(sc); 591 (*sc->sc_iftop->iftop_dtor)(sc); 592 sc->sc_iftop = NULL; 593 agr_reset_iftype(ifp); 594 } else { 595 KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); 596 } 597 598 goto out; 599 } 600 601 static int 602 agr_remport(struct ifnet *ifp, struct ifnet *ifp_port) 603 { 604 struct agr_softc *sc = ifp->if_softc; 605 struct agr_port *port; 606 int error = 0; 607 608 if (ifp_port->if_agrprivate == NULL) { 609 error = ENOENT; 610 return error; 611 } 612 613 port = ifp_port->if_agrprivate; 614 if (port->port_agrifp != ifp) { 615 error = EINVAL; 616 return error; 617 } 618 619 KASSERT(sc->sc_nports > 0); 620 621 #if 0 622 if (sc->sc_nports == 1 && 623 IFADDR_NEXT(IFADDR_FIRST(ifp)) != NULL) { 624 error = EBUSY; 625 return error; 626 } 627 #endif 628 629 AGR_LOCK(sc); 630 port->port_flags |= AGRPORT_DETACHING; 631 AGR_UNLOCK(sc); 632 633 error = (*sc->sc_iftop->iftop_portfini)(sc, port); 634 if (error) { 635 /* XXX XXX */ 636 printf("%s: portfini error %d\n", __func__, error); 637 goto out; 638 } 639 640 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, false); 641 if (error) { 642 /* XXX XXX */ 643 printf("%s: configmulti_port error %d\n", __func__, error); 644 goto out; 645 } 646 647 error = agrport_cleanup(sc, port); 648 if (error) { 649 /* XXX XXX */ 650 printf("%s: agrport_cleanup error %d\n", __func__, error); 651 goto out; 652 } 653 654 free(port, M_DEVBUF); 655 656 out: 657 if (sc->sc_nports == 0) { 658 KASSERT(TAILQ_EMPTY(&sc->sc_ports)); 659 agrtimer_stop(sc); 660 (*sc->sc_iftop->iftop_dtor)(sc); 661 sc->sc_iftop = NULL; 662 /* XXX should purge all addresses? */ 663 agr_reset_iftype(ifp); 664 } else { 665 KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); 666 } 667 668 return error; 669 } 670 671 static int 672 agrport_cleanup(struct agr_softc *sc, struct agr_port *port) 673 { 674 struct ifnet *ifp_port = port->port_ifp; 675 int error; 676 int result = 0; 677 678 error = agrport_config_promisc(port, false); 679 if (error) { 680 printf("%s: config_promisc error %d\n", __func__, error); 681 result = error; 682 } 683 684 if ((port->port_flags & AGRPORT_LADDRCHANGED)) { 685 #if 0 686 memcpy(LLADDR(ifp_port->if_sadl), port->port_origlladdr, 687 ifp_port->if_addrlen); 688 if (ifp_port->if_init != NULL) { 689 error = (*ifp_port->if_init)(ifp_port); 690 } 691 #else 692 union { 693 struct sockaddr sa; 694 struct sockaddr_dl sdl; 695 struct sockaddr_storage ss; 696 } u; 697 struct ifaddr ifa; 698 699 sockaddr_dl_init(&u.sdl, sizeof(u.ss), 700 0, ifp_port->if_type, NULL, 0, 701 port->port_origlladdr, ifp_port->if_addrlen); 702 memset(&ifa, 0, sizeof(ifa)); 703 ifa.ifa_addr = &u.sa; 704 error = agrport_ioctl(port, SIOCSIFADDR, &ifa); 705 #endif 706 if (error) { 707 printf("%s: if_init error %d\n", __func__, error); 708 result = error; 709 } else { 710 port->port_flags &= ~AGRPORT_LADDRCHANGED; 711 } 712 } 713 714 AGR_LOCK(sc); 715 if ((port->port_flags & AGRPORT_ATTACHED)) { 716 ifp_port->if_agrprivate = NULL; 717 718 TAILQ_REMOVE(&sc->sc_ports, port, port_q); 719 sc->sc_nports--; 720 721 KASSERT(ifp_port->if_ioctl == agr_ioctl_filter); 722 ifp_port->if_ioctl = port->port_ioctl; 723 724 port->port_flags &= ~AGRPORT_ATTACHED; 725 } 726 AGR_UNLOCK(sc); 727 728 return result; 729 } 730 731 static int 732 agr_ioctl_multi(struct ifnet *ifp, u_long cmd, struct ifreq *ifr) 733 { 734 struct agr_softc *sc = ifp->if_softc; 735 int error; 736 737 error = (*sc->sc_iftop->iftop_configmulti_ifreq)(sc, ifr, 738 (cmd == SIOCADDMULTI)); 739 740 return error; 741 } 742 743 /* XXX an incomplete hack; can't filter ioctls handled ifioctl(). */ 744 static int 745 agr_ioctl_filter(struct ifnet *ifp, u_long cmd, void *arg) 746 { 747 struct agr_port *port = ifp->if_agrprivate; 748 int error; 749 750 KASSERT(port); 751 752 switch (cmd) { 753 case SIOCGIFADDR: 754 case SIOCGIFMEDIA: 755 case SIOCSIFFLAGS: /* XXX */ 756 error = agrport_ioctl(port, cmd, arg); 757 break; 758 default: 759 error = EBUSY; 760 break; 761 } 762 return error; 763 } 764 765 static int 766 agrreq_copyin(const void *ubuf, struct agrreq *ar) 767 { 768 int error; 769 770 error = copyin(ubuf, ar, sizeof(*ar)); 771 if (error) { 772 return error; 773 } 774 775 if (ar->ar_version != AGRREQ_VERSION) { 776 return EINVAL; 777 } 778 779 return 0; 780 } 781 782 static int 783 agrreq_copyout(void *ubuf, struct agrreq *ar) 784 { 785 int error; 786 787 KASSERT(ar->ar_version == AGRREQ_VERSION); 788 789 error = copyout(ar, ubuf, sizeof(*ar)); 790 if (error) { 791 return error; 792 } 793 794 return 0; 795 } 796 797 static int 798 agr_ioctl(struct ifnet *ifp, u_long cmd, void *data) 799 { 800 struct agr_softc *sc = ifp->if_softc; 801 struct ifreq *ifr = (struct ifreq *)data; 802 struct ifaddr *ifa = (struct ifaddr *)data; 803 struct sockaddr *sa; 804 struct agrreq ar; 805 int error = 0; 806 int s; 807 808 agr_ioctl_lock(sc); 809 810 s = splnet(); 811 812 switch (cmd) { 813 case SIOCSIFADDR: 814 if (sc->sc_nports == 0) { 815 error = EINVAL; 816 break; 817 } 818 ifp->if_flags |= IFF_UP; 819 switch (ifa->ifa_addr->sa_family) { 820 #if defined(INET) 821 case AF_INET: 822 arp_ifinit(ifp, ifa); 823 break; 824 #endif 825 default: 826 break; 827 } 828 break; 829 830 case SIOCGIFADDR: 831 sa = (struct sockaddr *)&ifr->ifr_data; 832 memcpy(sa->sa_data, CLLADDR(ifp->if_sadl), ifp->if_addrlen); 833 break; 834 835 #if 0 /* notyet */ 836 case SIOCSIFMTU: 837 #endif 838 839 case SIOCSIFFLAGS: 840 agr_config_promisc(sc); 841 break; 842 843 case SIOCSETAGR: 844 splx(s); 845 error = kauth_authorize_network(kauth_cred_get(), 846 KAUTH_NETWORK_INTERFACE, 847 KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, 848 NULL); 849 if (!error) { 850 error = agrreq_copyin(ifr->ifr_data, &ar); 851 } 852 if (!error) { 853 error = agr_setconfig(ifp, &ar); 854 } 855 s = splnet(); 856 break; 857 858 case SIOCGETAGR: 859 splx(s); 860 error = agrreq_copyin(ifr->ifr_data, &ar); 861 if (!error) { 862 error = agr_getconfig(ifp, &ar); 863 } 864 if (!error) { 865 error = agrreq_copyout(ifr->ifr_data, &ar); 866 } 867 s = splnet(); 868 break; 869 870 case SIOCADDMULTI: 871 case SIOCDELMULTI: 872 if (sc->sc_nports == 0) { 873 error = EINVAL; 874 break; 875 } 876 error = agr_ioctl_multi(ifp, cmd, ifr); 877 break; 878 879 default: 880 error = EINVAL; 881 break; 882 } 883 884 splx(s); 885 886 agr_ioctl_unlock(sc); 887 888 return error; 889 } 890 891 static int 892 agr_config_promisc(struct agr_softc *sc) 893 { 894 int error; 895 896 agr_port_foreach(sc, agrport_config_promisc_callback, &error); 897 898 return error; 899 } 900 901 static int 902 agrport_config_promisc_callback(struct agr_port *port, void *arg) 903 { 904 struct agr_softc *sc = AGR_SC_FROM_PORT(port); 905 int *errorp = arg; 906 int error; 907 bool promisc; 908 909 promisc = (sc->sc_if.if_flags & IFF_PROMISC) != 0; 910 911 error = agrport_config_promisc(port, promisc); 912 if (error) { 913 *errorp = error; 914 } 915 916 return 0; 917 } 918 919 static int 920 agrport_config_promisc(struct agr_port *port, bool promisc) 921 { 922 int error; 923 924 if (( promisc && (port->port_flags & AGRPORT_PROMISC) != 0) || 925 (!promisc && (port->port_flags & AGRPORT_PROMISC) == 0)) { 926 return 0; 927 } 928 929 error = ifpromisc(port->port_ifp, promisc); 930 if (error == 0) { 931 if (promisc) { 932 port->port_flags |= AGRPORT_PROMISC; 933 } else { 934 port->port_flags &= ~AGRPORT_PROMISC; 935 } 936 } 937 938 return error; 939 } 940