Annotation of sys/net/if_trunk.c, Revision 1.1.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