1 /* $OpenBSD: if_trunk.c,v 1.89 2014/07/12 18:44:22 tedu Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "bpfilter.h" 20 #include "trunk.h" 21 22 #include <sys/param.h> 23 #include <sys/kernel.h> 24 #include <sys/malloc.h> 25 #include <sys/mbuf.h> 26 #include <sys/queue.h> 27 #include <sys/socket.h> 28 #include <sys/sockio.h> 29 #include <sys/systm.h> 30 #include <sys/timeout.h> 31 #include <sys/hash.h> 32 33 #include <dev/rndvar.h> 34 35 #include <net/if.h> 36 #include <net/if_arp.h> 37 #include <net/if_dl.h> 38 #include <net/if_media.h> 39 #include <net/if_types.h> 40 #if NBPFILTER > 0 41 #include <net/bpf.h> 42 #endif 43 44 #ifdef INET 45 #include <netinet/in.h> 46 #include <netinet/in_systm.h> 47 #include <netinet/if_ether.h> 48 #include <netinet/ip.h> 49 #endif 50 51 #ifdef INET6 52 #include <netinet/ip6.h> 53 #endif 54 55 #include <net/if_vlan_var.h> 56 #include <net/if_trunk.h> 57 #include <net/trunklacp.h> 58 59 60 SLIST_HEAD(__trhead, trunk_softc) trunk_list; /* list of trunks */ 61 62 void trunkattach(int); 63 int trunk_clone_create(struct if_clone *, int); 64 int trunk_clone_destroy(struct ifnet *); 65 void trunk_lladdr(struct arpcom *, u_int8_t *); 66 int trunk_capabilities(struct trunk_softc *); 67 void trunk_port_lladdr(struct trunk_port *, u_int8_t *); 68 int trunk_port_create(struct trunk_softc *, struct ifnet *); 69 int trunk_port_destroy(struct trunk_port *); 70 void trunk_port_watchdog(struct ifnet *); 71 void trunk_port_state(void *); 72 void trunk_port_ifdetach(void *); 73 int trunk_port_ioctl(struct ifnet *, u_long, caddr_t); 74 struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *); 75 int trunk_port_checkstacking(struct trunk_softc *); 76 void trunk_port2req(struct trunk_port *, struct trunk_reqport *); 77 int trunk_ioctl(struct ifnet *, u_long, caddr_t); 78 int trunk_ether_addmulti(struct trunk_softc *, struct ifreq *); 79 int trunk_ether_delmulti(struct trunk_softc *, struct ifreq *); 80 void trunk_ether_purgemulti(struct trunk_softc *); 81 int trunk_ether_cmdmulti(struct trunk_port *, u_long); 82 int trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t); 83 void trunk_start(struct ifnet *); 84 void trunk_init(struct ifnet *); 85 void trunk_stop(struct ifnet *); 86 void trunk_watchdog(struct ifnet *); 87 int trunk_media_change(struct ifnet *); 88 void trunk_media_status(struct ifnet *, struct ifmediareq *); 89 struct trunk_port *trunk_link_active(struct trunk_softc *, 90 struct trunk_port *); 91 const void *trunk_gethdr(struct mbuf *, u_int, u_int, void *); 92 93 struct if_clone trunk_cloner = 94 IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy); 95 96 /* Simple round robin */ 97 int trunk_rr_attach(struct trunk_softc *); 98 int trunk_rr_detach(struct trunk_softc *); 99 void trunk_rr_port_destroy(struct trunk_port *); 100 int trunk_rr_start(struct trunk_softc *, struct mbuf *); 101 int trunk_rr_input(struct trunk_softc *, struct trunk_port *, 102 struct ether_header *, struct mbuf *); 103 104 /* Active failover */ 105 int trunk_fail_attach(struct trunk_softc *); 106 int trunk_fail_detach(struct trunk_softc *); 107 int trunk_fail_start(struct trunk_softc *, struct mbuf *); 108 int trunk_fail_input(struct trunk_softc *, struct trunk_port *, 109 struct ether_header *, struct mbuf *); 110 111 /* Loadbalancing */ 112 int trunk_lb_attach(struct trunk_softc *); 113 int trunk_lb_detach(struct trunk_softc *); 114 int trunk_lb_port_create(struct trunk_port *); 115 void trunk_lb_port_destroy(struct trunk_port *); 116 int trunk_lb_start(struct trunk_softc *, struct mbuf *); 117 int trunk_lb_input(struct trunk_softc *, struct trunk_port *, 118 struct ether_header *, struct mbuf *); 119 int trunk_lb_porttable(struct trunk_softc *, struct trunk_port *); 120 121 /* Broadcast mode */ 122 int trunk_bcast_attach(struct trunk_softc *); 123 int trunk_bcast_detach(struct trunk_softc *); 124 int trunk_bcast_start(struct trunk_softc *, struct mbuf *); 125 int trunk_bcast_input(struct trunk_softc *, struct trunk_port *, 126 struct ether_header *, struct mbuf *); 127 128 /* 802.3ad LACP */ 129 int trunk_lacp_attach(struct trunk_softc *); 130 int trunk_lacp_detach(struct trunk_softc *); 131 int trunk_lacp_start(struct trunk_softc *, struct mbuf *); 132 int trunk_lacp_input(struct trunk_softc *, struct trunk_port *, 133 struct ether_header *, struct mbuf *); 134 135 /* Trunk protocol table */ 136 static const struct { 137 enum trunk_proto ti_proto; 138 int (*ti_attach)(struct trunk_softc *); 139 } trunk_protos[] = { 140 { TRUNK_PROTO_ROUNDROBIN, trunk_rr_attach }, 141 { TRUNK_PROTO_FAILOVER, trunk_fail_attach }, 142 { TRUNK_PROTO_LOADBALANCE, trunk_lb_attach }, 143 { TRUNK_PROTO_BROADCAST, trunk_bcast_attach }, 144 { TRUNK_PROTO_LACP, trunk_lacp_attach }, 145 { TRUNK_PROTO_NONE, NULL } 146 }; 147 148 void 149 trunkattach(int count) 150 { 151 SLIST_INIT(&trunk_list); 152 if_clone_attach(&trunk_cloner); 153 } 154 155 int 156 trunk_clone_create(struct if_clone *ifc, int unit) 157 { 158 struct trunk_softc *tr; 159 struct ifnet *ifp; 160 int i, error = 0; 161 162 if ((tr = malloc(sizeof(struct trunk_softc), 163 M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 164 return (ENOMEM); 165 166 tr->tr_unit = unit; 167 tr->tr_proto = TRUNK_PROTO_NONE; 168 for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) { 169 if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) { 170 tr->tr_proto = trunk_protos[i].ti_proto; 171 if ((error = trunk_protos[i].ti_attach(tr)) != 0) { 172 free(tr, M_DEVBUF, 0); 173 return (error); 174 } 175 break; 176 } 177 } 178 SLIST_INIT(&tr->tr_ports); 179 180 /* Initialise pseudo media types */ 181 ifmedia_init(&tr->tr_media, 0, trunk_media_change, 182 trunk_media_status); 183 ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL); 184 ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO); 185 186 ifp = &tr->tr_ac.ac_if; 187 ifp->if_softc = tr; 188 ifp->if_start = trunk_start; 189 ifp->if_watchdog = trunk_watchdog; 190 ifp->if_ioctl = trunk_ioctl; 191 ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 192 ifp->if_capabilities = trunk_capabilities(tr); 193 194 IFQ_SET_MAXLEN(&ifp->if_snd, 1); 195 IFQ_SET_READY(&ifp->if_snd); 196 197 snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", 198 ifc->ifc_name, unit); 199 200 /* 201 * Attach as an ordinary ethernet device, children will be attached 202 * as special device IFT_IEEE8023ADLAG. 203 */ 204 if_attach(ifp); 205 ether_ifattach(ifp); 206 207 /* Insert into the global list of trunks */ 208 SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries); 209 210 return (0); 211 } 212 213 int 214 trunk_clone_destroy(struct ifnet *ifp) 215 { 216 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 217 struct trunk_port *tp; 218 int error, s; 219 220 /* Remove any multicast groups that we may have joined. */ 221 trunk_ether_purgemulti(tr); 222 223 s = splnet(); 224 225 /* Shutdown and remove trunk ports, return on error */ 226 while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) { 227 if ((error = trunk_port_destroy(tp)) != 0) { 228 splx(s); 229 return (error); 230 } 231 } 232 233 ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY); 234 ether_ifdetach(ifp); 235 if_detach(ifp); 236 237 SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries); 238 free(tr, M_DEVBUF, 0); 239 240 splx(s); 241 242 return (0); 243 } 244 245 void 246 trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr) 247 { 248 struct ifnet *ifp = &ac->ac_if; 249 struct sockaddr_dl *sdl; 250 251 sdl = ifp->if_sadl; 252 sdl->sdl_type = IFT_ETHER; 253 sdl->sdl_alen = ETHER_ADDR_LEN; 254 bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN); 255 bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN); 256 } 257 258 int 259 trunk_capabilities(struct trunk_softc *tr) 260 { 261 struct trunk_port *tp; 262 int cap = ~0, priv; 263 264 /* Preserve private capabilities */ 265 priv = tr->tr_capabilities & IFCAP_TRUNK_MASK; 266 267 /* Get capabilities from the trunk ports */ 268 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) 269 cap &= tp->tp_capabilities; 270 271 if (tr->tr_ifflags & IFF_DEBUG) { 272 printf("%s: capabilities 0x%08x\n", 273 tr->tr_ifname, cap == ~0 ? priv : (cap | priv)); 274 } 275 276 return (cap == ~0 ? priv : (cap | priv)); 277 } 278 279 void 280 trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr) 281 { 282 struct ifnet *ifp = tp->tp_if; 283 284 /* Set the link layer address */ 285 trunk_lladdr((struct arpcom *)ifp, lladdr); 286 287 /* Reset the port to update the lladdr */ 288 ifnewlladdr(ifp); 289 } 290 291 int 292 trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp) 293 { 294 struct trunk_softc *tr_ptr; 295 struct trunk_port *tp; 296 int error = 0; 297 298 /* Limit the maximal number of trunk ports */ 299 if (tr->tr_count >= TRUNK_MAX_PORTS) 300 return (ENOSPC); 301 302 /* New trunk port has to be in an idle state */ 303 if (ifp->if_flags & IFF_OACTIVE) 304 return (EBUSY); 305 306 /* Check if port has already been associated to a trunk */ 307 if (trunk_port_get(NULL, ifp) != NULL) 308 return (EBUSY); 309 310 /* XXX Disallow non-ethernet interfaces (this should be any of 802) */ 311 if (ifp->if_type != IFT_ETHER) 312 return (EPROTONOSUPPORT); 313 314 /* Take MTU from the first member port */ 315 if (SLIST_EMPTY(&tr->tr_ports)) { 316 if (tr->tr_ifflags & IFF_DEBUG) 317 printf("%s: first port, setting trunk mtu %u\n", 318 tr->tr_ifname, ifp->if_mtu); 319 tr->tr_ac.ac_if.if_mtu = ifp->if_mtu; 320 tr->tr_ac.ac_if.if_hardmtu = ifp->if_mtu; 321 } else if (tr->tr_ac.ac_if.if_mtu != ifp->if_mtu) { 322 printf("%s: adding %s failed, MTU %u != %u\n", tr->tr_ifname, 323 ifp->if_xname, ifp->if_mtu, tr->tr_ac.ac_if.if_mtu); 324 return (EINVAL); 325 } 326 327 if ((error = ifpromisc(ifp, 1)) != 0) 328 return (error); 329 330 if ((tp = malloc(sizeof(struct trunk_port), 331 M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 332 return (ENOMEM); 333 334 /* Check if port is a stacked trunk */ 335 SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) { 336 if (ifp == &tr_ptr->tr_ac.ac_if) { 337 tp->tp_flags |= TRUNK_PORT_STACK; 338 if (trunk_port_checkstacking(tr_ptr) >= 339 TRUNK_MAX_STACKING) { 340 free(tp, M_DEVBUF, 0); 341 return (E2BIG); 342 } 343 } 344 } 345 346 /* Change the interface type */ 347 tp->tp_iftype = ifp->if_type; 348 ifp->if_type = IFT_IEEE8023ADLAG; 349 ifp->if_tp = (caddr_t)tp; 350 tp->tp_watchdog = ifp->if_watchdog; 351 ifp->if_watchdog = trunk_port_watchdog; 352 tp->tp_ioctl = ifp->if_ioctl; 353 ifp->if_ioctl = trunk_port_ioctl; 354 355 tp->tp_if = ifp; 356 tp->tp_trunk = tr; 357 358 /* Save port link layer address */ 359 bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN); 360 361 if (SLIST_EMPTY(&tr->tr_ports)) { 362 tr->tr_primary = tp; 363 tp->tp_flags |= TRUNK_PORT_MASTER; 364 trunk_lladdr(&tr->tr_ac, tp->tp_lladdr); 365 } 366 367 /* Update link layer address for this port */ 368 trunk_port_lladdr(tp, 369 ((struct arpcom *)(tr->tr_primary->tp_if))->ac_enaddr); 370 371 /* Insert into the list of ports */ 372 SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries); 373 tr->tr_count++; 374 375 /* Update trunk capabilities */ 376 tr->tr_capabilities = trunk_capabilities(tr); 377 378 /* Add multicast addresses to this port */ 379 trunk_ether_cmdmulti(tp, SIOCADDMULTI); 380 381 /* Register callback for physical link state changes */ 382 tp->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1, 383 trunk_port_state, tp); 384 385 /* Register callback if parent wants to unregister */ 386 tp->dh_cookie = hook_establish(ifp->if_detachhooks, 0, 387 trunk_port_ifdetach, tp); 388 389 if (tr->tr_port_create != NULL) 390 error = (*tr->tr_port_create)(tp); 391 392 return (error); 393 } 394 395 int 396 trunk_port_checkstacking(struct trunk_softc *tr) 397 { 398 struct trunk_softc *tr_ptr; 399 struct trunk_port *tp; 400 int m = 0; 401 402 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { 403 if (tp->tp_flags & TRUNK_PORT_STACK) { 404 tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc; 405 m = MAX(m, trunk_port_checkstacking(tr_ptr)); 406 } 407 } 408 409 return (m + 1); 410 } 411 412 int 413 trunk_port_destroy(struct trunk_port *tp) 414 { 415 struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; 416 struct trunk_port *tp_ptr; 417 struct ifnet *ifp = tp->tp_if; 418 419 if (tr->tr_port_destroy != NULL) 420 (*tr->tr_port_destroy)(tp); 421 422 /* Remove multicast addresses from this port */ 423 trunk_ether_cmdmulti(tp, SIOCDELMULTI); 424 425 /* Port has to be down */ 426 if (ifp->if_flags & IFF_UP) 427 if_down(ifp); 428 429 ifpromisc(ifp, 0); 430 431 /* Restore interface */ 432 ifp->if_type = tp->tp_iftype; 433 ifp->if_watchdog = tp->tp_watchdog; 434 ifp->if_ioctl = tp->tp_ioctl; 435 ifp->if_tp = NULL; 436 437 hook_disestablish(ifp->if_linkstatehooks, tp->lh_cookie); 438 hook_disestablish(ifp->if_detachhooks, tp->dh_cookie); 439 440 /* Finally, remove the port from the trunk */ 441 SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries); 442 tr->tr_count--; 443 444 /* Update the primary interface */ 445 if (tp == tr->tr_primary) { 446 u_int8_t lladdr[ETHER_ADDR_LEN]; 447 448 if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) { 449 bzero(&lladdr, ETHER_ADDR_LEN); 450 } else { 451 bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr, 452 lladdr, ETHER_ADDR_LEN); 453 tp_ptr->tp_flags = TRUNK_PORT_MASTER; 454 } 455 trunk_lladdr(&tr->tr_ac, lladdr); 456 tr->tr_primary = tp_ptr; 457 458 /* Update link layer address for each port */ 459 SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries) 460 trunk_port_lladdr(tp_ptr, lladdr); 461 } 462 463 /* Reset the port lladdr */ 464 trunk_port_lladdr(tp, tp->tp_lladdr); 465 466 free(tp, M_DEVBUF, 0); 467 468 /* Update trunk capabilities */ 469 tr->tr_capabilities = trunk_capabilities(tr); 470 471 return (0); 472 } 473 474 void 475 trunk_port_watchdog(struct ifnet *ifp) 476 { 477 struct trunk_port *tp; 478 479 /* Should be checked by the caller */ 480 if (ifp->if_type != IFT_IEEE8023ADLAG) 481 return; 482 if ((tp = (struct trunk_port *)ifp->if_tp) == NULL || 483 tp->tp_trunk == NULL) 484 return; 485 486 if (tp->tp_watchdog != NULL) 487 (*tp->tp_watchdog)(ifp); 488 } 489 490 491 int 492 trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 493 { 494 struct trunk_reqport *rp = (struct trunk_reqport *)data; 495 struct trunk_softc *tr; 496 struct trunk_port *tp = NULL; 497 int s, error = 0; 498 499 s = splnet(); 500 501 /* Should be checked by the caller */ 502 if (ifp->if_type != IFT_IEEE8023ADLAG || 503 (tp = (struct trunk_port *)ifp->if_tp) == NULL || 504 (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) { 505 error = EINVAL; 506 goto fallback; 507 } 508 509 switch (cmd) { 510 case SIOCGTRUNKPORT: 511 if (rp->rp_portname[0] == '\0' || 512 ifunit(rp->rp_portname) != ifp) { 513 error = EINVAL; 514 break; 515 } 516 517 /* Search in all trunks if the global flag is set */ 518 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ? 519 NULL : tr, ifp)) == NULL) { 520 error = ENOENT; 521 break; 522 } 523 524 trunk_port2req(tp, rp); 525 break; 526 case SIOCSIFMTU: 527 /* Do not allow the MTU to be changed once joined */ 528 error = EINVAL; 529 break; 530 default: 531 error = ENOTTY; 532 goto fallback; 533 } 534 535 splx(s); 536 return (error); 537 538 fallback: 539 splx(s); 540 541 if (tp != NULL) 542 error = (*tp->tp_ioctl)(ifp, cmd, data); 543 544 return (error); 545 } 546 547 void 548 trunk_port_ifdetach(void *arg) 549 { 550 struct trunk_port *tp = (struct trunk_port *)arg; 551 552 trunk_port_destroy(tp); 553 } 554 555 struct trunk_port * 556 trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp) 557 { 558 struct trunk_port *tp; 559 struct trunk_softc *tr_ptr; 560 561 if (tr != NULL) { 562 /* Search port in specified trunk */ 563 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { 564 if (tp->tp_if == ifp) 565 return (tp); 566 } 567 } else { 568 /* Search all trunks for the selected port */ 569 SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) { 570 SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) { 571 if (tp->tp_if == ifp) 572 return (tp); 573 } 574 } 575 } 576 577 return (NULL); 578 } 579 580 void 581 trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp) 582 { 583 struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; 584 585 strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname)); 586 strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname)); 587 rp->rp_prio = tp->tp_prio; 588 if (tr->tr_portreq != NULL) 589 (*tr->tr_portreq)(tp, (caddr_t)&rp->rp_psc); 590 591 /* Add protocol specific flags */ 592 switch (tr->tr_proto) { 593 case TRUNK_PROTO_FAILOVER: 594 rp->rp_flags = tp->tp_flags; 595 if (tp == trunk_link_active(tr, tr->tr_primary)) 596 rp->rp_flags |= TRUNK_PORT_ACTIVE; 597 break; 598 599 case TRUNK_PROTO_ROUNDROBIN: 600 case TRUNK_PROTO_LOADBALANCE: 601 case TRUNK_PROTO_BROADCAST: 602 rp->rp_flags = tp->tp_flags; 603 if (TRUNK_PORTACTIVE(tp)) 604 rp->rp_flags |= TRUNK_PORT_ACTIVE; 605 break; 606 607 case TRUNK_PROTO_LACP: 608 /* LACP has a different definition of active */ 609 rp->rp_flags = lacp_port_status(tp); 610 break; 611 default: 612 break; 613 } 614 } 615 616 int 617 trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 618 { 619 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 620 struct trunk_reqall *ra = (struct trunk_reqall *)data; 621 struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf; 622 struct ifreq *ifr = (struct ifreq *)data; 623 struct ifaddr *ifa = (struct ifaddr *)data; 624 struct trunk_port *tp; 625 struct ifnet *tpif; 626 int s, i, error = 0; 627 628 s = splnet(); 629 630 bzero(&rpbuf, sizeof(rpbuf)); 631 632 switch (cmd) { 633 case SIOCGTRUNK: 634 ra->ra_proto = tr->tr_proto; 635 if (tr->tr_req != NULL) 636 (*tr->tr_req)(tr, (caddr_t)&ra->ra_psc); 637 ra->ra_ports = i = 0; 638 tp = SLIST_FIRST(&tr->tr_ports); 639 while (tp && ra->ra_size >= 640 i + sizeof(struct trunk_reqport)) { 641 trunk_port2req(tp, &rpbuf); 642 error = copyout(&rpbuf, (caddr_t)ra->ra_port + i, 643 sizeof(struct trunk_reqport)); 644 if (error) 645 break; 646 i += sizeof(struct trunk_reqport); 647 ra->ra_ports++; 648 tp = SLIST_NEXT(tp, tp_entries); 649 } 650 break; 651 case SIOCSTRUNK: 652 if ((error = suser(curproc, 0)) != 0) { 653 error = EPERM; 654 break; 655 } 656 if (ra->ra_proto >= TRUNK_PROTO_MAX) { 657 error = EPROTONOSUPPORT; 658 break; 659 } 660 if (tr->tr_proto != TRUNK_PROTO_NONE) 661 error = tr->tr_detach(tr); 662 if (error != 0) 663 break; 664 for (i = 0; i < (sizeof(trunk_protos) / 665 sizeof(trunk_protos[0])); i++) { 666 if (trunk_protos[i].ti_proto == ra->ra_proto) { 667 if (tr->tr_ifflags & IFF_DEBUG) 668 printf("%s: using proto %u\n", 669 tr->tr_ifname, 670 trunk_protos[i].ti_proto); 671 tr->tr_proto = trunk_protos[i].ti_proto; 672 if (tr->tr_proto != TRUNK_PROTO_NONE) 673 error = trunk_protos[i].ti_attach(tr); 674 goto out; 675 } 676 } 677 error = EPROTONOSUPPORT; 678 break; 679 case SIOCGTRUNKPORT: 680 if (rp->rp_portname[0] == '\0' || 681 (tpif = ifunit(rp->rp_portname)) == NULL) { 682 error = EINVAL; 683 break; 684 } 685 686 /* Search in all trunks if the global flag is set */ 687 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ? 688 NULL : tr, tpif)) == NULL) { 689 error = ENOENT; 690 break; 691 } 692 693 trunk_port2req(tp, rp); 694 break; 695 case SIOCSTRUNKPORT: 696 if ((error = suser(curproc, 0)) != 0) { 697 error = EPERM; 698 break; 699 } 700 if (rp->rp_portname[0] == '\0' || 701 (tpif = ifunit(rp->rp_portname)) == NULL) { 702 error = EINVAL; 703 break; 704 } 705 error = trunk_port_create(tr, tpif); 706 break; 707 case SIOCSTRUNKDELPORT: 708 if ((error = suser(curproc, 0)) != 0) { 709 error = EPERM; 710 break; 711 } 712 if (rp->rp_portname[0] == '\0' || 713 (tpif = ifunit(rp->rp_portname)) == NULL) { 714 error = EINVAL; 715 break; 716 } 717 718 /* Search in all trunks if the global flag is set */ 719 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ? 720 NULL : tr, tpif)) == NULL) { 721 error = ENOENT; 722 break; 723 } 724 725 error = trunk_port_destroy(tp); 726 break; 727 case SIOCSIFADDR: 728 ifp->if_flags |= IFF_UP; 729 #ifdef INET 730 if (ifa->ifa_addr->sa_family == AF_INET) 731 arp_ifinit(&tr->tr_ac, ifa); 732 #endif /* INET */ 733 error = ENETRESET; 734 break; 735 case SIOCSIFFLAGS: 736 error = ENETRESET; 737 break; 738 case SIOCADDMULTI: 739 error = trunk_ether_addmulti(tr, ifr); 740 break; 741 case SIOCDELMULTI: 742 error = trunk_ether_delmulti(tr, ifr); 743 break; 744 case SIOCSIFMEDIA: 745 case SIOCGIFMEDIA: 746 error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd); 747 break; 748 case SIOCSIFLLADDR: 749 /* Update the port lladdrs as well */ 750 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) 751 trunk_port_lladdr(tp, ifr->ifr_addr.sa_data); 752 error = ENETRESET; 753 break; 754 default: 755 error = ether_ioctl(ifp, &tr->tr_ac, cmd, data); 756 } 757 758 if (error == ENETRESET) { 759 if (ifp->if_flags & IFF_UP) { 760 if ((ifp->if_flags & IFF_RUNNING) == 0) 761 trunk_init(ifp); 762 } else { 763 if (ifp->if_flags & IFF_RUNNING) 764 trunk_stop(ifp); 765 } 766 error = 0; 767 } 768 769 out: 770 splx(s); 771 return (error); 772 } 773 774 int 775 trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr) 776 { 777 struct trunk_mc *mc; 778 u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 779 int error; 780 781 /* Ignore ENETRESET error code */ 782 if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET) 783 return (error); 784 785 if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) { 786 error = ENOMEM; 787 goto failed; 788 } 789 790 ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi); 791 ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm); 792 bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len); 793 SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries); 794 795 if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI, 796 (caddr_t)ifr)) != 0) { 797 trunk_ether_delmulti(tr, ifr); 798 return (error); 799 } 800 801 return (error); 802 803 failed: 804 ether_delmulti(ifr, &tr->tr_ac); 805 806 return (error); 807 } 808 809 int 810 trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr) 811 { 812 struct ether_multi *enm; 813 struct trunk_mc *mc; 814 u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; 815 int error; 816 817 if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0) 818 return (error); 819 ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm); 820 if (enm == NULL) 821 return (EINVAL); 822 823 SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) 824 if (mc->mc_enm == enm) 825 break; 826 827 /* We won't delete entries we didn't add */ 828 if (mc == NULL) 829 return (EINVAL); 830 831 if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET) 832 return (error); 833 834 if ((error = trunk_ioctl_allports(tr, SIOCDELMULTI, 835 (caddr_t)ifr)) != 0) { 836 /* XXX At least one port failed to remove the address */ 837 if (tr->tr_ifflags & IFF_DEBUG) { 838 printf("%s: failed to remove multicast address " 839 "on all ports\n", tr->tr_ifname); 840 } 841 } 842 843 SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries); 844 free(mc, M_DEVBUF, 0); 845 846 return (0); 847 } 848 849 void 850 trunk_ether_purgemulti(struct trunk_softc *tr) 851 { 852 struct trunk_mc *mc; 853 struct trunk_ifreq ifs; 854 struct ifreq *ifr = &ifs.ifreq.ifreq; 855 856 while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) { 857 bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len); 858 859 /* Try to remove multicast address on all ports */ 860 trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr); 861 862 SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries); 863 free(mc, M_DEVBUF, 0); 864 } 865 } 866 867 int 868 trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd) 869 { 870 struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; 871 struct trunk_mc *mc; 872 struct trunk_ifreq ifs; 873 struct ifreq *ifr = &ifs.ifreq.ifreq; 874 int ret, error = 0; 875 876 bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ); 877 SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) { 878 bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len); 879 880 if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) { 881 if (tr->tr_ifflags & IFF_DEBUG) { 882 printf("%s: ioctl %lu failed on %s: %d\n", 883 tr->tr_ifname, cmd, tp->tp_ifname, ret); 884 } 885 /* Store last known error and continue */ 886 error = ret; 887 } 888 } 889 890 return (error); 891 } 892 893 int 894 trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data) 895 { 896 struct ifreq *ifr = (struct ifreq *)data; 897 struct trunk_port *tp; 898 int ret, error = 0; 899 900 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { 901 bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ); 902 if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) { 903 if (tr->tr_ifflags & IFF_DEBUG) { 904 printf("%s: ioctl %lu failed on %s: %d\n", 905 tr->tr_ifname, cmd, tp->tp_ifname, ret); 906 } 907 /* Store last known error and continue */ 908 error = ret; 909 } 910 } 911 912 return (error); 913 } 914 915 void 916 trunk_start(struct ifnet *ifp) 917 { 918 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 919 struct mbuf *m; 920 int error; 921 922 for (;;) { 923 IFQ_DEQUEUE(&ifp->if_snd, m); 924 if (m == NULL) 925 break; 926 927 #if NBPFILTER > 0 928 if (ifp->if_bpf) 929 bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT); 930 #endif 931 932 if (tr->tr_proto != TRUNK_PROTO_NONE && tr->tr_count) { 933 error = (*tr->tr_start)(tr, m); 934 if (error == 0) 935 ifp->if_opackets++; 936 else 937 ifp->if_oerrors++; 938 } else { 939 m_freem(m); 940 if (tr->tr_proto != TRUNK_PROTO_NONE) 941 ifp->if_oerrors++; 942 } 943 } 944 } 945 946 int 947 trunk_enqueue(struct ifnet *ifp, struct mbuf *m) 948 { 949 int len, error = 0; 950 u_short mflags; 951 952 splassert(IPL_NET); 953 954 /* Send mbuf */ 955 mflags = m->m_flags; 956 len = m->m_pkthdr.len; 957 IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); 958 if (error) 959 return (error); 960 if_start(ifp); 961 962 ifp->if_obytes += len; 963 if (mflags & M_MCAST) 964 ifp->if_omcasts++; 965 966 return (error); 967 } 968 969 u_int32_t 970 trunk_hashmbuf(struct mbuf *m, u_int32_t key) 971 { 972 u_int16_t etype, ether_vtag; 973 u_int32_t p = 0; 974 u_int16_t *vlan, vlanbuf[2]; 975 int off; 976 struct ether_header *eh; 977 #ifdef INET 978 struct ip *ip, ipbuf; 979 #endif 980 #ifdef INET6 981 u_int32_t flow; 982 struct ip6_hdr *ip6, ip6buf; 983 #endif 984 985 off = sizeof(*eh); 986 if (m->m_len < off) 987 return (p); 988 eh = mtod(m, struct ether_header *); 989 etype = ntohs(eh->ether_type); 990 p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, key); 991 p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 992 993 /* Special handling for encapsulating VLAN frames */ 994 if (m->m_flags & M_VLANTAG) { 995 ether_vtag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 996 p = hash32_buf(ðer_vtag, sizeof(ether_vtag), p); 997 } else if (etype == ETHERTYPE_VLAN) { 998 if ((vlan = (u_int16_t *) 999 trunk_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL) 1000 return (p); 1001 ether_vtag = EVL_VLANOFTAG(*vlan); 1002 p = hash32_buf(ðer_vtag, sizeof(ether_vtag), p); 1003 etype = ntohs(vlan[1]); 1004 off += EVL_ENCAPLEN; 1005 } 1006 1007 switch (etype) { 1008 #ifdef INET 1009 case ETHERTYPE_IP: 1010 if ((ip = (struct ip *) 1011 trunk_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL) 1012 return (p); 1013 p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p); 1014 p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 1015 break; 1016 #endif 1017 #ifdef INET6 1018 case ETHERTYPE_IPV6: 1019 if ((ip6 = (struct ip6_hdr *) 1020 trunk_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL) 1021 return (p); 1022 p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 1023 p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 1024 flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 1025 p = hash32_buf(&flow, sizeof(flow), p); /* IPv6 flow label */ 1026 break; 1027 #endif 1028 } 1029 1030 return (p); 1031 } 1032 1033 void 1034 trunk_init(struct ifnet *ifp) 1035 { 1036 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 1037 int s; 1038 1039 s = splnet(); 1040 1041 ifp->if_flags |= IFF_RUNNING; 1042 ifp->if_flags &= ~IFF_OACTIVE; 1043 1044 if (tr->tr_init != NULL) 1045 (*tr->tr_init)(tr); 1046 1047 splx(s); 1048 } 1049 1050 void 1051 trunk_stop(struct ifnet *ifp) 1052 { 1053 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 1054 int s; 1055 1056 s = splnet(); 1057 1058 ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 1059 1060 if (tr->tr_stop != NULL) 1061 (*tr->tr_stop)(tr); 1062 1063 splx(s); 1064 } 1065 1066 void 1067 trunk_watchdog(struct ifnet *ifp) 1068 { 1069 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 1070 1071 if (tr->tr_proto != TRUNK_PROTO_NONE && 1072 (*tr->tr_watchdog)(tr) != 0) { 1073 ifp->if_oerrors++; 1074 } 1075 1076 } 1077 1078 int 1079 trunk_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m) 1080 { 1081 struct trunk_softc *tr; 1082 struct trunk_port *tp; 1083 struct ifnet *trifp = NULL; 1084 int error = 0; 1085 1086 /* Should be checked by the caller */ 1087 if (ifp->if_type != IFT_IEEE8023ADLAG) { 1088 error = EPROTONOSUPPORT; 1089 goto bad; 1090 } 1091 if ((tp = (struct trunk_port *)ifp->if_tp) == NULL || 1092 (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) { 1093 error = ENOENT; 1094 goto bad; 1095 } 1096 trifp = &tr->tr_ac.ac_if; 1097 if (tr->tr_proto == TRUNK_PROTO_NONE) { 1098 error = ENOENT; 1099 goto bad; 1100 } 1101 1102 #if NBPFILTER > 0 1103 if (trifp->if_bpf && tr->tr_proto != TRUNK_PROTO_FAILOVER) 1104 bpf_mtap_hdr(trifp->if_bpf, (char *)eh, ETHER_HDR_LEN, m, 1105 BPF_DIRECTION_IN, NULL); 1106 #endif 1107 1108 error = (*tr->tr_input)(tr, tp, eh, m); 1109 if (error != 0) 1110 return (error); 1111 1112 trifp->if_ipackets++; 1113 return (0); 1114 1115 bad: 1116 if (error > 0 && trifp != NULL) 1117 trifp->if_ierrors++; 1118 m_freem(m); 1119 return (error); 1120 } 1121 1122 int 1123 trunk_media_change(struct ifnet *ifp) 1124 { 1125 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 1126 1127 if (tr->tr_ifflags & IFF_DEBUG) 1128 printf("%s\n", __func__); 1129 1130 /* Ignore */ 1131 return (0); 1132 } 1133 1134 void 1135 trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1136 { 1137 struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc; 1138 struct trunk_port *tp; 1139 1140 imr->ifm_status = IFM_AVALID; 1141 imr->ifm_active = IFM_ETHER | IFM_AUTO; 1142 1143 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { 1144 if (TRUNK_PORTACTIVE(tp)) 1145 imr->ifm_status |= IFM_ACTIVE; 1146 } 1147 } 1148 1149 void 1150 trunk_port_state(void *arg) 1151 { 1152 struct trunk_port *tp = (struct trunk_port *)arg; 1153 struct trunk_softc *tr = NULL; 1154 1155 if (tp != NULL) 1156 tr = (struct trunk_softc *)tp->tp_trunk; 1157 if (tr == NULL) 1158 return; 1159 if (tr->tr_linkstate != NULL) 1160 (*tr->tr_linkstate)(tp); 1161 trunk_link_active(tr, tp); 1162 } 1163 1164 struct trunk_port * 1165 trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp) 1166 { 1167 struct trunk_port *tp_next, *rval = NULL; 1168 int new_link = LINK_STATE_DOWN; 1169 1170 /* 1171 * Search a port which reports an active link state. 1172 */ 1173 1174 if (tp == NULL) 1175 goto search; 1176 if (TRUNK_PORTACTIVE(tp)) { 1177 rval = tp; 1178 goto found; 1179 } 1180 if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL && 1181 TRUNK_PORTACTIVE(tp_next)) { 1182 rval = tp_next; 1183 goto found; 1184 } 1185 1186 search: 1187 SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) { 1188 if (TRUNK_PORTACTIVE(tp_next)) { 1189 rval = tp_next; 1190 goto found; 1191 } 1192 } 1193 1194 found: 1195 if (rval != NULL) { 1196 /* 1197 * The IEEE 802.1D standard assumes that a trunk with 1198 * multiple ports is always full duplex. This is valid 1199 * for load sharing trunks and if at least two links 1200 * are active. Unfortunately, checking the latter would 1201 * be too expensive at this point. 1202 */ 1203 if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) && 1204 (tr->tr_count > 1)) 1205 new_link = LINK_STATE_FULL_DUPLEX; 1206 else 1207 new_link = rval->tp_link_state; 1208 } 1209 1210 if (tr->tr_ac.ac_if.if_link_state != new_link) { 1211 tr->tr_ac.ac_if.if_link_state = new_link; 1212 if_link_state_change(&tr->tr_ac.ac_if); 1213 } 1214 1215 return (rval); 1216 } 1217 1218 const void * 1219 trunk_gethdr(struct mbuf *m, u_int off, u_int len, void *buf) 1220 { 1221 if (m->m_pkthdr.len < (off + len)) 1222 return (NULL); 1223 else if (m->m_len < (off + len)) { 1224 m_copydata(m, off, len, buf); 1225 return (buf); 1226 } 1227 return (mtod(m, caddr_t) + off); 1228 } 1229 1230 /* 1231 * Simple round robin trunking 1232 */ 1233 1234 int 1235 trunk_rr_attach(struct trunk_softc *tr) 1236 { 1237 struct trunk_port *tp; 1238 1239 tr->tr_detach = trunk_rr_detach; 1240 tr->tr_start = trunk_rr_start; 1241 tr->tr_input = trunk_rr_input; 1242 tr->tr_init = NULL; 1243 tr->tr_stop = NULL; 1244 tr->tr_linkstate = NULL; 1245 tr->tr_port_create = NULL; 1246 tr->tr_port_destroy = trunk_rr_port_destroy; 1247 tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX; 1248 tr->tr_req = NULL; 1249 tr->tr_portreq = NULL; 1250 1251 tp = SLIST_FIRST(&tr->tr_ports); 1252 tr->tr_psc = (caddr_t)tp; 1253 1254 return (0); 1255 } 1256 1257 int 1258 trunk_rr_detach(struct trunk_softc *tr) 1259 { 1260 tr->tr_psc = NULL; 1261 return (0); 1262 } 1263 1264 void 1265 trunk_rr_port_destroy(struct trunk_port *tp) 1266 { 1267 struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; 1268 1269 if (tp == (struct trunk_port *)tr->tr_psc) 1270 tr->tr_psc = NULL; 1271 } 1272 1273 int 1274 trunk_rr_start(struct trunk_softc *tr, struct mbuf *m) 1275 { 1276 struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next; 1277 int error = 0; 1278 1279 if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL) { 1280 m_freem(m); 1281 return (ENOENT); 1282 } 1283 1284 /* Send mbuf */ 1285 if ((error = trunk_enqueue(tp->tp_if, m)) != 0) 1286 return (error); 1287 1288 /* Get next active port */ 1289 tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries)); 1290 tr->tr_psc = (caddr_t)tp_next; 1291 1292 return (0); 1293 } 1294 1295 int 1296 trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp, 1297 struct ether_header *eh, struct mbuf *m) 1298 { 1299 struct ifnet *ifp = &tr->tr_ac.ac_if; 1300 1301 /* Just pass in the packet to our trunk device */ 1302 m->m_pkthdr.rcvif = ifp; 1303 1304 return (0); 1305 } 1306 1307 /* 1308 * Active failover 1309 */ 1310 1311 int 1312 trunk_fail_attach(struct trunk_softc *tr) 1313 { 1314 tr->tr_detach = trunk_fail_detach; 1315 tr->tr_start = trunk_fail_start; 1316 tr->tr_input = trunk_fail_input; 1317 tr->tr_init = NULL; 1318 tr->tr_stop = NULL; 1319 tr->tr_port_create = NULL; 1320 tr->tr_port_destroy = NULL; 1321 tr->tr_linkstate = NULL; 1322 tr->tr_req = NULL; 1323 tr->tr_portreq = NULL; 1324 1325 return (0); 1326 } 1327 1328 int 1329 trunk_fail_detach(struct trunk_softc *tr) 1330 { 1331 return (0); 1332 } 1333 1334 int 1335 trunk_fail_start(struct trunk_softc *tr, struct mbuf *m) 1336 { 1337 struct trunk_port *tp; 1338 1339 /* Use the master port if active or the next available port */ 1340 if ((tp = trunk_link_active(tr, tr->tr_primary)) == NULL) { 1341 m_freem(m); 1342 return (ENOENT); 1343 } 1344 1345 /* Send mbuf */ 1346 return (trunk_enqueue(tp->tp_if, m)); 1347 } 1348 1349 int 1350 trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp, 1351 struct ether_header *eh, struct mbuf *m) 1352 { 1353 struct ifnet *ifp = &tr->tr_ac.ac_if; 1354 struct trunk_port *tmp_tp; 1355 int accept = 0; 1356 1357 if (tp == tr->tr_primary) { 1358 accept = 1; 1359 } else if (tr->tr_primary->tp_link_state == LINK_STATE_DOWN) { 1360 tmp_tp = trunk_link_active(tr, NULL); 1361 /* 1362 * If tmp_tp is null, we've received a packet when all 1363 * our links are down. Weird, but process it anyways. 1364 */ 1365 if ((tmp_tp == NULL || tmp_tp == tp)) 1366 accept = 1; 1367 } 1368 if (!accept) { 1369 m_freem(m); 1370 return (-1); 1371 } 1372 #if NBPFILTER > 0 1373 if (ifp->if_bpf) 1374 bpf_mtap_hdr(ifp->if_bpf, (char *)eh, ETHER_HDR_LEN, m, 1375 BPF_DIRECTION_IN, NULL); 1376 #endif 1377 1378 m->m_pkthdr.rcvif = ifp; 1379 return (0); 1380 } 1381 1382 /* 1383 * Loadbalancing 1384 */ 1385 1386 int 1387 trunk_lb_attach(struct trunk_softc *tr) 1388 { 1389 struct trunk_lb *lb; 1390 1391 if ((lb = malloc(sizeof(*lb), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 1392 return (ENOMEM); 1393 1394 tr->tr_detach = trunk_lb_detach; 1395 tr->tr_start = trunk_lb_start; 1396 tr->tr_input = trunk_lb_input; 1397 tr->tr_port_create = trunk_lb_port_create; 1398 tr->tr_port_destroy = trunk_lb_port_destroy; 1399 tr->tr_linkstate = NULL; 1400 tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX; 1401 tr->tr_req = NULL; 1402 tr->tr_portreq = NULL; 1403 tr->tr_init = NULL; 1404 tr->tr_stop = NULL; 1405 1406 lb->lb_key = arc4random(); 1407 tr->tr_psc = (caddr_t)lb; 1408 1409 return (0); 1410 } 1411 1412 int 1413 trunk_lb_detach(struct trunk_softc *tr) 1414 { 1415 struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc; 1416 if (lb != NULL) 1417 free(lb, M_DEVBUF, 0); 1418 return (0); 1419 } 1420 1421 int 1422 trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp) 1423 { 1424 struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc; 1425 struct trunk_port *tp_next; 1426 int i = 0; 1427 1428 bzero(&lb->lb_ports, sizeof(lb->lb_ports)); 1429 SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) { 1430 if (tp_next == tp) 1431 continue; 1432 if (i >= TRUNK_MAX_PORTS) 1433 return (EINVAL); 1434 if (tr->tr_ifflags & IFF_DEBUG) 1435 printf("%s: port %s at index %d\n", 1436 tr->tr_ifname, tp_next->tp_ifname, i); 1437 lb->lb_ports[i++] = tp_next; 1438 } 1439 1440 return (0); 1441 } 1442 1443 int 1444 trunk_lb_port_create(struct trunk_port *tp) 1445 { 1446 struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; 1447 return (trunk_lb_porttable(tr, NULL)); 1448 } 1449 1450 void 1451 trunk_lb_port_destroy(struct trunk_port *tp) 1452 { 1453 struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; 1454 trunk_lb_porttable(tr, tp); 1455 } 1456 1457 int 1458 trunk_lb_start(struct trunk_softc *tr, struct mbuf *m) 1459 { 1460 struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc; 1461 struct trunk_port *tp = NULL; 1462 u_int32_t p = 0; 1463 1464 p = trunk_hashmbuf(m, lb->lb_key); 1465 p %= tr->tr_count; 1466 tp = lb->lb_ports[p]; 1467 1468 /* 1469 * Check the port's link state. This will return the next active 1470 * port if the link is down or the port is NULL. 1471 */ 1472 if ((tp = trunk_link_active(tr, tp)) == NULL) { 1473 m_freem(m); 1474 return (ENOENT); 1475 } 1476 1477 /* Send mbuf */ 1478 return (trunk_enqueue(tp->tp_if, m)); 1479 } 1480 1481 int 1482 trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp, 1483 struct ether_header *eh, struct mbuf *m) 1484 { 1485 struct ifnet *ifp = &tr->tr_ac.ac_if; 1486 1487 /* Just pass in the packet to our trunk device */ 1488 m->m_pkthdr.rcvif = ifp; 1489 1490 return (0); 1491 } 1492 1493 /* 1494 * Broadcast mode 1495 */ 1496 1497 int 1498 trunk_bcast_attach(struct trunk_softc *tr) 1499 { 1500 tr->tr_detach = trunk_bcast_detach; 1501 tr->tr_start = trunk_bcast_start; 1502 tr->tr_input = trunk_bcast_input; 1503 tr->tr_init = NULL; 1504 tr->tr_stop = NULL; 1505 tr->tr_port_create = NULL; 1506 tr->tr_port_destroy = NULL; 1507 tr->tr_linkstate = NULL; 1508 tr->tr_req = NULL; 1509 tr->tr_portreq = NULL; 1510 1511 return (0); 1512 } 1513 1514 int 1515 trunk_bcast_detach(struct trunk_softc *tr) 1516 { 1517 return (0); 1518 } 1519 1520 int 1521 trunk_bcast_start(struct trunk_softc *tr, struct mbuf *m0) 1522 { 1523 int active_ports = 0; 1524 int errors = 0; 1525 int ret; 1526 struct trunk_port *tp, *last = NULL; 1527 struct mbuf *m; 1528 1529 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { 1530 if (!TRUNK_PORTACTIVE(tp)) 1531 continue; 1532 1533 active_ports++; 1534 1535 if (last != NULL) { 1536 m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT); 1537 if (m == NULL) { 1538 ret = ENOBUFS; 1539 errors++; 1540 break; 1541 } 1542 1543 ret = trunk_enqueue(last->tp_if, m); 1544 if (ret != 0) 1545 errors++; 1546 } 1547 last = tp; 1548 } 1549 if (last == NULL) { 1550 m_freem(m0); 1551 return (ENOENT); 1552 } 1553 1554 ret = trunk_enqueue(last->tp_if, m0); 1555 if (ret != 0) 1556 errors++; 1557 1558 if (errors == active_ports) 1559 return (ret); 1560 1561 return (0); 1562 } 1563 1564 int 1565 trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp, 1566 struct ether_header *eh, struct mbuf *m) 1567 { 1568 struct ifnet *ifp = &tr->tr_ac.ac_if; 1569 1570 m->m_pkthdr.rcvif = ifp; 1571 return (0); 1572 } 1573 1574 /* 1575 * 802.3ad LACP 1576 */ 1577 1578 int 1579 trunk_lacp_attach(struct trunk_softc *tr) 1580 { 1581 struct trunk_port *tp; 1582 int error; 1583 1584 tr->tr_detach = trunk_lacp_detach; 1585 tr->tr_port_create = lacp_port_create; 1586 tr->tr_port_destroy = lacp_port_destroy; 1587 tr->tr_linkstate = lacp_linkstate; 1588 tr->tr_start = trunk_lacp_start; 1589 tr->tr_input = trunk_lacp_input; 1590 tr->tr_init = lacp_init; 1591 tr->tr_stop = lacp_stop; 1592 tr->tr_req = lacp_req; 1593 tr->tr_portreq = lacp_portreq; 1594 1595 error = lacp_attach(tr); 1596 if (error) 1597 return (error); 1598 1599 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) 1600 lacp_port_create(tp); 1601 1602 return (error); 1603 } 1604 1605 int 1606 trunk_lacp_detach(struct trunk_softc *tr) 1607 { 1608 struct trunk_port *tp; 1609 int error; 1610 1611 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) 1612 lacp_port_destroy(tp); 1613 1614 /* unlocking is safe here */ 1615 error = lacp_detach(tr); 1616 1617 return (error); 1618 } 1619 1620 int 1621 trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m) 1622 { 1623 struct trunk_port *tp; 1624 1625 tp = lacp_select_tx_port(tr, m); 1626 if (tp == NULL) { 1627 m_freem(m); 1628 return (EBUSY); 1629 } 1630 1631 /* Send mbuf */ 1632 return (trunk_enqueue(tp->tp_if, m)); 1633 } 1634 1635 int 1636 trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp, 1637 struct ether_header *eh, struct mbuf *m) 1638 { 1639 struct ifnet *ifp = &tr->tr_ac.ac_if; 1640 1641 m = lacp_input(tp, eh, m); 1642 if (m == NULL) 1643 return (-1); 1644 1645 m->m_pkthdr.rcvif = ifp; 1646 return (0); 1647 } 1648