1 /* $NetBSD: if_agr.c,v 1.22 2008/11/07 00:20:18 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.22 2008/11/07 00:20:18 dyoung 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 const struct ifaddr *ifa; 468 struct agr_softc *sc = ifp->if_softc; 469 struct agr_port *port = NULL; 470 int error = 0; 471 472 if (ifp_port->if_ioctl == NULL) { 473 error = EOPNOTSUPP; 474 goto out; 475 } 476 477 if (ifp_port->if_agrprivate) { 478 error = EBUSY; 479 goto out; 480 } 481 482 if (ifp_port->if_start == agr_start) { 483 error = EINVAL; 484 goto out; 485 } 486 487 port = malloc(sizeof(*port) + ifp_port->if_addrlen, M_DEVBUF, 488 M_WAITOK | M_ZERO); 489 if (port == NULL) { 490 error = ENOMEM; 491 goto out; 492 } 493 port->port_flags = AGRPORT_LARVAL; 494 495 IFADDR_FOREACH(ifa, ifp_port) { 496 if (ifa->ifa_addr->sa_family != AF_LINK) { 497 error = EBUSY; 498 goto out; 499 } 500 } 501 502 if (sc->sc_nports == 0) { 503 switch (ifp_port->if_type) { 504 case IFT_ETHER: 505 sc->sc_iftop = &agrether_ops; 506 break; 507 508 default: 509 error = EPROTONOSUPPORT; /* XXX */ 510 goto out; 511 } 512 513 error = (*sc->sc_iftop->iftop_ctor)(sc, ifp_port); 514 if (error) 515 goto out; 516 agrtimer_start(sc); 517 } else { 518 if (ifp->if_type != ifp_port->if_type) { 519 error = EINVAL; 520 goto out; 521 } 522 if (ifp->if_addrlen != ifp_port->if_addrlen) { 523 error = EINVAL; 524 goto out; 525 } 526 } 527 528 memcpy(port->port_origlladdr, CLLADDR(ifp_port->if_sadl), 529 ifp_port->if_addrlen); 530 531 /* 532 * start to modify ifp_port. 533 */ 534 535 error = (*ifp_port->if_ioctl)(ifp_port, SIOCINITIFADDR, ifp->if_dl); 536 537 if (error) { 538 printf("%s: SIOCINITIFADDR error %d\n", __func__, error); 539 goto cleanup; 540 } 541 port->port_flags |= AGRPORT_LADDRCHANGED; 542 543 ifp->if_type = ifp_port->if_type; 544 AGR_LOCK(sc); 545 546 port->port_ifp = ifp_port; 547 ifp_port->if_agrprivate = port; 548 port->port_agrifp = ifp; 549 TAILQ_INSERT_TAIL(&sc->sc_ports, port, port_q); 550 sc->sc_nports++; 551 552 port->port_ioctl = ifp_port->if_ioctl; 553 ifp_port->if_ioctl = agr_ioctl_filter; 554 555 port->port_flags |= AGRPORT_ATTACHED; 556 557 AGR_UNLOCK(sc); 558 559 error = (*sc->sc_iftop->iftop_portinit)(sc, port); 560 if (error) { 561 printf("%s: portinit error %d\n", __func__, error); 562 goto cleanup; 563 } 564 565 ifp->if_flags |= IFF_RUNNING; 566 567 agrport_config_promisc(port, (ifp->if_flags & IFF_PROMISC) != 0); 568 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, true); 569 if (error) { 570 printf("%s: configmulti error %d\n", __func__, error); 571 goto cleanup; 572 } 573 574 AGR_LOCK(sc); 575 port->port_flags &= ~AGRPORT_LARVAL; 576 AGR_UNLOCK(sc); 577 out: 578 if (error && port) { 579 free(port, M_DEVBUF); 580 } 581 return error; 582 583 cleanup: 584 if (agrport_cleanup(sc, port)) { 585 printf("%s: error on cleanup\n", __func__); 586 587 port = NULL; /* XXX */ 588 } 589 590 if (sc->sc_nports == 0) { 591 KASSERT(TAILQ_EMPTY(&sc->sc_ports)); 592 agrtimer_stop(sc); 593 (*sc->sc_iftop->iftop_dtor)(sc); 594 sc->sc_iftop = NULL; 595 agr_reset_iftype(ifp); 596 } else { 597 KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); 598 } 599 600 goto out; 601 } 602 603 static int 604 agr_remport(struct ifnet *ifp, struct ifnet *ifp_port) 605 { 606 struct agr_softc *sc = ifp->if_softc; 607 struct agr_port *port; 608 int error = 0; 609 610 if (ifp_port->if_agrprivate == NULL) { 611 error = ENOENT; 612 return error; 613 } 614 615 port = ifp_port->if_agrprivate; 616 if (port->port_agrifp != ifp) { 617 error = EINVAL; 618 return error; 619 } 620 621 KASSERT(sc->sc_nports > 0); 622 623 AGR_LOCK(sc); 624 port->port_flags |= AGRPORT_DETACHING; 625 AGR_UNLOCK(sc); 626 627 error = (*sc->sc_iftop->iftop_portfini)(sc, port); 628 if (error) { 629 /* XXX XXX */ 630 printf("%s: portfini error %d\n", __func__, error); 631 goto out; 632 } 633 634 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, false); 635 if (error) { 636 /* XXX XXX */ 637 printf("%s: configmulti_port error %d\n", __func__, error); 638 goto out; 639 } 640 641 error = agrport_cleanup(sc, port); 642 if (error) { 643 /* XXX XXX */ 644 printf("%s: agrport_cleanup error %d\n", __func__, error); 645 goto out; 646 } 647 648 free(port, M_DEVBUF); 649 650 out: 651 if (sc->sc_nports == 0) { 652 KASSERT(TAILQ_EMPTY(&sc->sc_ports)); 653 agrtimer_stop(sc); 654 (*sc->sc_iftop->iftop_dtor)(sc); 655 sc->sc_iftop = NULL; 656 /* XXX should purge all addresses? */ 657 agr_reset_iftype(ifp); 658 } else { 659 KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); 660 } 661 662 return error; 663 } 664 665 static int 666 agrport_cleanup(struct agr_softc *sc, struct agr_port *port) 667 { 668 struct ifnet *ifp_port = port->port_ifp; 669 int error; 670 int result = 0; 671 672 error = agrport_config_promisc(port, false); 673 if (error) { 674 printf("%s: config_promisc error %d\n", __func__, error); 675 result = error; 676 } 677 678 if ((port->port_flags & AGRPORT_LADDRCHANGED)) { 679 #if 0 680 memcpy(LLADDR(ifp_port->if_sadl), port->port_origlladdr, 681 ifp_port->if_addrlen); 682 if (ifp_port->if_init != NULL) { 683 error = (*ifp_port->if_init)(ifp_port); 684 } 685 #else 686 union { 687 struct sockaddr sa; 688 struct sockaddr_dl sdl; 689 struct sockaddr_storage ss; 690 } u; 691 struct ifaddr ifa; 692 693 sockaddr_dl_init(&u.sdl, sizeof(u.ss), 694 0, ifp_port->if_type, NULL, 0, 695 port->port_origlladdr, ifp_port->if_addrlen); 696 memset(&ifa, 0, sizeof(ifa)); 697 ifa.ifa_addr = &u.sa; 698 error = agrport_ioctl(port, SIOCINITIFADDR, &ifa); 699 #endif 700 if (error) { 701 printf("%s: if_init error %d\n", __func__, error); 702 result = error; 703 } else { 704 port->port_flags &= ~AGRPORT_LADDRCHANGED; 705 } 706 } 707 708 AGR_LOCK(sc); 709 if ((port->port_flags & AGRPORT_ATTACHED)) { 710 ifp_port->if_agrprivate = NULL; 711 712 TAILQ_REMOVE(&sc->sc_ports, port, port_q); 713 sc->sc_nports--; 714 715 KASSERT(ifp_port->if_ioctl == agr_ioctl_filter); 716 ifp_port->if_ioctl = port->port_ioctl; 717 718 port->port_flags &= ~AGRPORT_ATTACHED; 719 } 720 AGR_UNLOCK(sc); 721 722 return result; 723 } 724 725 static int 726 agr_ioctl_multi(struct ifnet *ifp, u_long cmd, struct ifreq *ifr) 727 { 728 struct agr_softc *sc = ifp->if_softc; 729 int error; 730 731 error = (*sc->sc_iftop->iftop_configmulti_ifreq)(sc, ifr, 732 (cmd == SIOCADDMULTI)); 733 734 return error; 735 } 736 737 /* 738 * XXX an incomplete hack; can't filter ioctls handled ifioctl(). 739 * 740 * the intention here is to prevent operations on underlying interfaces 741 * so that their states are not changed in the way that agr(4) doesn't 742 * expect. cf. the BUGS section in the agr(4) manual page. 743 */ 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 SIOCADDMULTI: /* add m'cast addr */ 754 case SIOCAIFADDR: /* add/chg IF alias */ 755 case SIOCALIFADDR: /* add IF addr */ 756 case SIOCDELMULTI: /* del m'cast addr */ 757 case SIOCDIFADDR: /* delete IF addr */ 758 case SIOCDIFPHYADDR: /* delete gif addrs */ 759 case SIOCDLIFADDR: /* delete IF addr */ 760 case SIOCINITIFADDR: 761 case SIOCSDRVSPEC: /* set driver-specific parameters */ 762 case SIOCSIFADDR: /* set ifnet address */ 763 case SIOCSIFBRDADDR: /* set broadcast addr */ 764 case SIOCSIFDSTADDR: /* set p-p address */ 765 case SIOCSIFGENERIC: /* generic IF set op */ 766 case SIOCSIFMEDIA: /* set net media */ 767 case SIOCSIFMETRIC: /* set IF metric */ 768 case SIOCSIFMTU: /* set ifnet mtu */ 769 case SIOCSIFNETMASK: /* set net addr mask */ 770 case SIOCSIFPHYADDR: /* set gif addres */ 771 case SIOCSLIFPHYADDR: /* set gif addrs */ 772 case SIOCSVH: /* set carp param */ 773 error = EBUSY; 774 break; 775 case SIOCSIFCAP: /* XXX */ 776 case SIOCSIFFLAGS: /* XXX */ 777 default: 778 error = agrport_ioctl(port, cmd, arg); 779 break; 780 } 781 return error; 782 } 783 784 static int 785 agrreq_copyin(const void *ubuf, struct agrreq *ar) 786 { 787 int error; 788 789 error = copyin(ubuf, ar, sizeof(*ar)); 790 if (error) { 791 return error; 792 } 793 794 if (ar->ar_version != AGRREQ_VERSION) { 795 return EINVAL; 796 } 797 798 return 0; 799 } 800 801 static int 802 agrreq_copyout(void *ubuf, struct agrreq *ar) 803 { 804 int error; 805 806 KASSERT(ar->ar_version == AGRREQ_VERSION); 807 808 error = copyout(ar, ubuf, sizeof(*ar)); 809 if (error) { 810 return error; 811 } 812 813 return 0; 814 } 815 816 static int 817 agr_ioctl(struct ifnet *ifp, u_long cmd, void *data) 818 { 819 struct agr_softc *sc = ifp->if_softc; 820 struct ifreq *ifr = (struct ifreq *)data; 821 struct ifaddr *ifa = (struct ifaddr *)data; 822 struct agrreq ar; 823 int error = 0; 824 int s; 825 826 agr_ioctl_lock(sc); 827 828 s = splnet(); 829 830 switch (cmd) { 831 case SIOCINITIFADDR: 832 if (sc->sc_nports == 0) { 833 error = EINVAL; 834 break; 835 } 836 ifp->if_flags |= IFF_UP; 837 switch (ifa->ifa_addr->sa_family) { 838 #if defined(INET) 839 case AF_INET: 840 arp_ifinit(ifp, ifa); 841 break; 842 #endif 843 default: 844 break; 845 } 846 break; 847 848 #if 0 /* notyet */ 849 case SIOCSIFMTU: 850 #endif 851 852 case SIOCSIFFLAGS: 853 if ((error = ifioctl_common(ifp, cmd, data)) != 0) 854 break; 855 agr_config_promisc(sc); 856 break; 857 858 case SIOCSETAGR: 859 splx(s); 860 error = kauth_authorize_network(kauth_cred_get(), 861 KAUTH_NETWORK_INTERFACE, 862 KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, 863 NULL); 864 if (!error) { 865 error = agrreq_copyin(ifr->ifr_data, &ar); 866 } 867 if (!error) { 868 error = agr_setconfig(ifp, &ar); 869 } 870 s = splnet(); 871 break; 872 873 case SIOCGETAGR: 874 splx(s); 875 error = agrreq_copyin(ifr->ifr_data, &ar); 876 if (!error) { 877 error = agr_getconfig(ifp, &ar); 878 } 879 if (!error) { 880 error = agrreq_copyout(ifr->ifr_data, &ar); 881 } 882 s = splnet(); 883 break; 884 885 case SIOCADDMULTI: 886 case SIOCDELMULTI: 887 if (sc->sc_nports == 0) { 888 error = EINVAL; 889 break; 890 } 891 error = agr_ioctl_multi(ifp, cmd, ifr); 892 break; 893 894 default: 895 error = ifioctl_common(ifp, cmd, data); 896 break; 897 } 898 899 splx(s); 900 901 agr_ioctl_unlock(sc); 902 903 return error; 904 } 905 906 static int 907 agr_config_promisc(struct agr_softc *sc) 908 { 909 int error; 910 911 agr_port_foreach(sc, agrport_config_promisc_callback, &error); 912 913 return error; 914 } 915 916 static int 917 agrport_config_promisc_callback(struct agr_port *port, void *arg) 918 { 919 struct agr_softc *sc = AGR_SC_FROM_PORT(port); 920 int *errorp = arg; 921 int error; 922 bool promisc; 923 924 promisc = (sc->sc_if.if_flags & IFF_PROMISC) != 0; 925 926 error = agrport_config_promisc(port, promisc); 927 if (error) { 928 *errorp = error; 929 } 930 931 return 0; 932 } 933 934 static int 935 agrport_config_promisc(struct agr_port *port, bool promisc) 936 { 937 int error; 938 939 if (( promisc && (port->port_flags & AGRPORT_PROMISC) != 0) || 940 (!promisc && (port->port_flags & AGRPORT_PROMISC) == 0)) { 941 return 0; 942 } 943 944 error = ifpromisc(port->port_ifp, promisc); 945 if (error == 0) { 946 if (promisc) { 947 port->port_flags |= AGRPORT_PROMISC; 948 } else { 949 port->port_flags &= ~AGRPORT_PROMISC; 950 } 951 } 952 953 return error; 954 } 955