This source file includes following definitions.
- trunkattach
- trunk_clone_create
- trunk_clone_destroy
- trunk_lladdr
- trunk_capabilities
- trunk_port_lladdr
- trunk_port_create
- trunk_port_checkstacking
- trunk_port_destroy
- trunk_port_watchdog
- trunk_port_ioctl
- trunk_port_ifdetach
- trunk_port_get
- trunk_port2req
- trunk_ioctl
- trunk_ether_addmulti
- trunk_ether_delmulti
- trunk_ether_purgemulti
- trunk_ether_cmdmulti
- trunk_ioctl_allports
- trunk_start
- trunk_enqueue
- trunk_hashmbuf
- trunk_init
- trunk_stop
- trunk_watchdog
- trunk_input
- trunk_media_change
- trunk_media_status
- trunk_port_state
- trunk_link_active
- trunk_rr_attach
- trunk_rr_detach
- trunk_rr_port_destroy
- trunk_rr_start
- trunk_rr_input
- trunk_fail_attach
- trunk_fail_detach
- trunk_fail_start
- trunk_fail_input
- trunk_lb_attach
- trunk_lb_detach
- trunk_lb_porttable
- trunk_lb_port_create
- trunk_lb_port_destroy
- trunk_lb_gethdr
- trunk_lb_start
- trunk_lb_input
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
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
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
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
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
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
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
192
193
194 if_attach(ifp);
195 ether_ifattach(ifp);
196
197
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
211 trunk_ether_purgemulti(tr);
212
213 s = splnet();
214
215
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
257 priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
258
259
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
279 trunk_lladdr((struct arpcom *)ifp, lladdr);
280
281
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
305 if (tr->tr_count >= TRUNK_MAX_PORTS)
306 return (ENOSPC);
307
308
309 if (ifp->if_flags & IFF_OACTIVE)
310 return (EBUSY);
311
312
313 if (trunk_port_get(NULL, ifp) != NULL)
314 return (EBUSY);
315
316
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
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
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
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
363 trunk_port_lladdr(tp, tr->tr_primary->tp_lladdr);
364
365
366 SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
367 tr->tr_count++;
368
369
370 tr->tr_capabilities = trunk_capabilities(tr);
371
372
373 trunk_ether_cmdmulti(tp, SIOCADDMULTI);
374
375
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
414 trunk_ether_cmdmulti(tp, SIOCDELMULTI);
415
416
417 if (ifp->if_flags & IFF_UP)
418 if_down(ifp);
419
420 ifpromisc(ifp, 0);
421
422
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
432 SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
433 tr->tr_count--;
434
435
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
450 SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
451 trunk_port_lladdr(tp_ptr, lladdr);
452 }
453
454
455 trunk_port_lladdr(tp, tp->tp_lladdr);
456
457 free(tp, M_DEVBUF);
458
459
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
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
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
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
551 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
552 if (tp->tp_if == ifp)
553 return (tp);
554 }
555 } else {
556
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1159
1160
1161
1162
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
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
1229 if ((error = trunk_enqueue(tp->tp_if, m)) != 0)
1230 return (error);
1231
1232
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
1246 m->m_pkthdr.rcvif = ifp;
1247
1248 return (0);
1249 }
1250
1251
1252
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
1282 if ((tp = trunk_link_active(tr, tr->tr_primary)) == NULL)
1283 return (ENOENT);
1284
1285
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
1305
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
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
1416
1417
1418 if ((tp = trunk_link_active(tr, tp)) == NULL)
1419 return (ENOENT);
1420
1421
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
1432 m->m_pkthdr.rcvif = ifp;
1433
1434 return (0);
1435 }