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