]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/if_bond.c
xnu-4570.31.3.tar.gz
[apple/xnu.git] / bsd / net / if_bond.c
CommitLineData
91447636 1/*
5c9f4661 2 * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
91447636 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
91447636
A
27 */
28
29/*
30 * if_bond.c
31 * - bond/failover interface
32 * - implements IEEE 802.3ad Link Aggregation
33 */
34
35/*
36 * Modification History:
37 *
38 * April 29, 2004 Dieter Siegmund (dieter@apple.com)
39 * - created
40 */
41
42#include <sys/param.h>
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/mbuf.h>
46#include <sys/queue.h>
47#include <sys/socket.h>
48#include <sys/sockio.h>
49#include <sys/sysctl.h>
50#include <sys/systm.h>
51#include <sys/kern_event.h>
52
53#include <net/bpf.h>
54#include <net/ethernet.h>
55#include <net/if.h>
56#include <net/kpi_interface.h>
57#include <net/if_arp.h>
58#include <net/if_dl.h>
59#include <net/if_ether.h>
60#include <net/if_types.h>
61#include <net/if_bond_var.h>
62#include <net/ieee8023ad.h>
63#include <net/lacp.h>
64#include <net/dlil.h>
65#include <sys/time.h>
66#include <net/devtimer.h>
67#include <net/if_vlan_var.h>
2d21ac55 68#include <net/kpi_protocol.h>
91447636
A
69
70#include <kern/locks.h>
71#include <libkern/OSAtomic.h>
72
73#include <netinet/in.h>
74#include <netinet/if_ether.h>
75#include <netinet/in_systm.h>
76#include <netinet/ip.h>
77#include <netinet/ip6.h>
78
79#include <net/if_media.h>
80#include <net/multicast_list.h>
81
91447636
A
82static struct ether_addr slow_proto_multicast = {
83 IEEE8023AD_SLOW_PROTO_MULTICAST
84};
85
86#define BOND_MAXUNIT 128
87#define BONDNAME "bond"
88#define M_BOND M_DEVBUF
89
90#define EA_FORMAT "%x:%x:%x:%x:%x:%x"
91#define EA_CH(e, i) ((u_char)((u_char *)(e))[(i)])
92#define EA_LIST(ea) EA_CH(ea,0),EA_CH(ea,1),EA_CH(ea,2),EA_CH(ea,3),EA_CH(ea,4),EA_CH(ea,5)
93
94#define timestamp_printf printf
95
96/**
97 ** bond locks
98 **/
99static __inline__ lck_grp_t *
100my_lck_grp_alloc_init(const char * grp_name)
101{
102 lck_grp_t * grp;
103 lck_grp_attr_t * grp_attrs;
104
105 grp_attrs = lck_grp_attr_alloc_init();
91447636
A
106 grp = lck_grp_alloc_init(grp_name, grp_attrs);
107 lck_grp_attr_free(grp_attrs);
108 return (grp);
109}
110
111static __inline__ lck_mtx_t *
112my_lck_mtx_alloc_init(lck_grp_t * lck_grp)
113{
114 lck_attr_t * lck_attrs;
115 lck_mtx_t * lck_mtx;
116
117 lck_attrs = lck_attr_alloc_init();
91447636
A
118 lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs);
119 lck_attr_free(lck_attrs);
120 return (lck_mtx);
121}
122
123static lck_mtx_t * bond_lck_mtx;
124
125static __inline__ void
126bond_lock_init(void)
127{
128 lck_grp_t * bond_lck_grp;
129
130 bond_lck_grp = my_lck_grp_alloc_init("if_bond");
131 bond_lck_mtx = my_lck_mtx_alloc_init(bond_lck_grp);
132}
133
134static __inline__ void
135bond_assert_lock_held(void)
136{
5ba3f43e 137 LCK_MTX_ASSERT(bond_lck_mtx, LCK_MTX_ASSERT_OWNED);
91447636
A
138 return;
139}
140
141static __inline__ void
142bond_assert_lock_not_held(void)
143{
5ba3f43e 144 LCK_MTX_ASSERT(bond_lck_mtx, LCK_MTX_ASSERT_NOTOWNED);
91447636
A
145 return;
146}
147
148static __inline__ void
149bond_lock(void)
150{
151 lck_mtx_lock(bond_lck_mtx);
152 return;
153}
154
155static __inline__ void
156bond_unlock(void)
157{
158 lck_mtx_unlock(bond_lck_mtx);
159 return;
160}
161
162/**
163 ** bond structures, types
164 **/
165
166struct LAG_info_s {
167 lacp_system li_system;
168 lacp_system_priority li_system_priority;
169 lacp_key li_key;
170};
171typedef struct LAG_info_s LAG_info, * LAG_info_ref;
172
173struct bondport_s;
174TAILQ_HEAD(port_list, bondport_s);
175struct ifbond_s;
176TAILQ_HEAD(ifbond_list, ifbond_s);
177struct LAG_s;
178TAILQ_HEAD(lag_list, LAG_s);
179
180typedef struct ifbond_s ifbond, * ifbond_ref;
181typedef struct bondport_s bondport, * bondport_ref;
182
183struct LAG_s {
184 TAILQ_ENTRY(LAG_s) lag_list;
185 struct port_list lag_port_list;
186 short lag_port_count;
187 short lag_selected_port_count;
188 int lag_active_media;
189 LAG_info lag_info;
190};
191typedef struct LAG_s LAG, * LAG_ref;
192
193typedef struct partner_state_s {
194 LAG_info ps_lag_info;
195 lacp_port ps_port;
196 lacp_port_priority ps_port_priority;
197 lacp_actor_partner_state ps_state;
198} partner_state, * partner_state_ref;
199
200struct ifbond_s {
201 TAILQ_ENTRY(ifbond_s) ifb_bond_list;
202 int ifb_flags;
2d21ac55 203 SInt32 ifb_retain_count;
91447636
A
204 char ifb_name[IFNAMSIZ];
205 struct ifnet * ifb_ifp;
206 bpf_packet_func ifb_bpf_input;
207 bpf_packet_func ifb_bpf_output;
208 int ifb_altmtu;
209 struct port_list ifb_port_list;
210 short ifb_port_count;
211 struct lag_list ifb_lag_list;
212 lacp_key ifb_key;
213 short ifb_max_active; /* 0 == unlimited */
214 LAG_ref ifb_active_lag;
215 struct ifmultiaddr * ifb_ifma_slow_proto;
216 bondport_ref * ifb_distributing_array;
217 int ifb_distributing_count;
2d21ac55
A
218 int ifb_last_link_event;
219 int ifb_mode; /* LACP, STATIC */
91447636
A
220};
221
222struct media_info {
223 int mi_active;
224 int mi_status;
225};
226
227enum {
228 ReceiveState_none = 0,
229 ReceiveState_INITIALIZE = 1,
230 ReceiveState_PORT_DISABLED = 2,
231 ReceiveState_EXPIRED = 3,
232 ReceiveState_LACP_DISABLED = 4,
233 ReceiveState_DEFAULTED = 5,
234 ReceiveState_CURRENT = 6,
235};
236
237typedef u_char ReceiveState;
238
239enum {
240 SelectedState_UNSELECTED = IF_BOND_STATUS_SELECTED_STATE_UNSELECTED,
241 SelectedState_SELECTED = IF_BOND_STATUS_SELECTED_STATE_SELECTED,
242 SelectedState_STANDBY = IF_BOND_STATUS_SELECTED_STATE_STANDBY
243};
244typedef u_char SelectedState;
245
246static __inline__ const char *
247SelectedStateString(SelectedState s)
248{
249 static const char * names[] = { "UNSELECTED", "SELECTED", "STANDBY" };
250
251 if (s <= SelectedState_STANDBY) {
252 return (names[s]);
253 }
254 return ("<unknown>");
255}
256
257enum {
258 MuxState_none = 0,
259 MuxState_DETACHED = 1,
260 MuxState_WAITING = 2,
261 MuxState_ATTACHED = 3,
262 MuxState_COLLECTING_DISTRIBUTING = 4,
263};
264
265typedef u_char MuxState;
266
267struct bondport_s {
268 TAILQ_ENTRY(bondport_s) po_port_list;
269 ifbond_ref po_bond;
270 struct multicast_list po_multicast;
271 struct ifnet * po_ifp;
272 struct ether_addr po_saved_addr;
273 int po_enabled;
274 char po_name[IFNAMSIZ];
275 struct ifdevmtu po_devmtu;
276
277 /* LACP */
278 TAILQ_ENTRY(bondport_s) po_lag_port_list;
279 devtimer_ref po_current_while_timer;
280 devtimer_ref po_periodic_timer;
281 devtimer_ref po_wait_while_timer;
282 devtimer_ref po_transmit_timer;
283 partner_state po_partner_state;
284 lacp_port_priority po_priority;
285 lacp_actor_partner_state po_actor_state;
286 u_char po_flags;
287 u_char po_periodic_interval;
288 u_char po_n_transmit;
289 ReceiveState po_receive_state;
290 MuxState po_mux_state;
291 SelectedState po_selected;
292 int32_t po_last_transmit_secs;
293 struct media_info po_media_info;
294 LAG_ref po_lag;
295};
296
297#define IFBF_PROMISC 0x1 /* promiscuous mode */
298#define IFBF_IF_DETACHING 0x2 /* interface is detaching */
299#define IFBF_LLADDR 0x4 /* specific link address requested */
300#define IFBF_CHANGE_IN_PROGRESS 0x8 /* interface add/remove in progress */
301
302static int bond_get_status(ifbond_ref ifb, struct if_bond_req * ibr_p,
303 user_addr_t datap);
304
91447636
A
305static __inline__ int
306ifbond_flags_if_detaching(ifbond_ref ifb)
307{
308 return ((ifb->ifb_flags & IFBF_IF_DETACHING) != 0);
309}
310
311static __inline__ void
312ifbond_flags_set_if_detaching(ifbond_ref ifb)
313{
314 ifb->ifb_flags |= IFBF_IF_DETACHING;
315 return;
316}
317
318static __inline__ int
319ifbond_flags_lladdr(ifbond_ref ifb)
320{
321 return ((ifb->ifb_flags & IFBF_LLADDR) != 0);
322}
323
91447636
A
324static __inline__ int
325ifbond_flags_change_in_progress(ifbond_ref ifb)
326{
327 return ((ifb->ifb_flags & IFBF_CHANGE_IN_PROGRESS) != 0);
328}
329
330static __inline__ void
331ifbond_flags_set_change_in_progress(ifbond_ref ifb)
332{
333 ifb->ifb_flags |= IFBF_CHANGE_IN_PROGRESS;
334 return;
335}
336
337static __inline__ void
338ifbond_flags_clear_change_in_progress(ifbond_ref ifb)
339{
340 ifb->ifb_flags &= ~IFBF_CHANGE_IN_PROGRESS;
341 return;
342}
343
344/*
345 * bondport_ref->po_flags bits
346 */
347#define BONDPORT_FLAGS_NTT 0x01
348#define BONDPORT_FLAGS_READY 0x02
349#define BONDPORT_FLAGS_SELECTED_CHANGED 0x04
350#define BONDPORT_FLAGS_MUX_ATTACHED 0x08
351#define BONDPORT_FLAGS_DISTRIBUTING 0x10
352#define BONDPORT_FLAGS_UNUSED2 0x20
353#define BONDPORT_FLAGS_UNUSED3 0x40
354#define BONDPORT_FLAGS_UNUSED4 0x80
355
356static __inline__ void
357bondport_flags_set_ntt(bondport_ref p)
358{
359 p->po_flags |= BONDPORT_FLAGS_NTT;
360 return;
361}
362
363static __inline__ void
364bondport_flags_clear_ntt(bondport_ref p)
365{
366 p->po_flags &= ~BONDPORT_FLAGS_NTT;
367 return;
368}
369
370static __inline__ int
371bondport_flags_ntt(bondport_ref p)
372{
373 return ((p->po_flags & BONDPORT_FLAGS_NTT) != 0);
374}
375
376static __inline__ void
377bondport_flags_set_ready(bondport_ref p)
378{
379 p->po_flags |= BONDPORT_FLAGS_READY;
380 return;
381}
382
383static __inline__ void
384bondport_flags_clear_ready(bondport_ref p)
385{
386 p->po_flags &= ~BONDPORT_FLAGS_READY;
387 return;
388}
389
390static __inline__ int
391bondport_flags_ready(bondport_ref p)
392{
393 return ((p->po_flags & BONDPORT_FLAGS_READY) != 0);
394}
395
396static __inline__ void
397bondport_flags_set_selected_changed(bondport_ref p)
398{
399 p->po_flags |= BONDPORT_FLAGS_SELECTED_CHANGED;
400 return;
401}
402
403static __inline__ void
404bondport_flags_clear_selected_changed(bondport_ref p)
405{
406 p->po_flags &= ~BONDPORT_FLAGS_SELECTED_CHANGED;
407 return;
408}
409
410static __inline__ int
411bondport_flags_selected_changed(bondport_ref p)
412{
413 return ((p->po_flags & BONDPORT_FLAGS_SELECTED_CHANGED) != 0);
414}
415
416static __inline__ void
417bondport_flags_set_mux_attached(bondport_ref p)
418{
419 p->po_flags |= BONDPORT_FLAGS_MUX_ATTACHED;
420 return;
421}
422
423static __inline__ void
424bondport_flags_clear_mux_attached(bondport_ref p)
425{
426 p->po_flags &= ~BONDPORT_FLAGS_MUX_ATTACHED;
427 return;
428}
429
430static __inline__ int
431bondport_flags_mux_attached(bondport_ref p)
432{
433 return ((p->po_flags & BONDPORT_FLAGS_MUX_ATTACHED) != 0);
434}
435
436static __inline__ void
437bondport_flags_set_distributing(bondport_ref p)
438{
439 p->po_flags |= BONDPORT_FLAGS_DISTRIBUTING;
440 return;
441}
442
443static __inline__ void
444bondport_flags_clear_distributing(bondport_ref p)
445{
446 p->po_flags &= ~BONDPORT_FLAGS_DISTRIBUTING;
447 return;
448}
449
450static __inline__ int
451bondport_flags_distributing(bondport_ref p)
452{
453 return ((p->po_flags & BONDPORT_FLAGS_DISTRIBUTING) != 0);
454}
455
456typedef struct bond_globals_s {
457 struct ifbond_list ifbond_list;
458 lacp_system system;
459 lacp_system_priority system_priority;
460 int verbose;
461} * bond_globals_ref;
462
463static bond_globals_ref g_bond;
464
465/**
466 ** packet_buffer routines
467 ** - thin wrapper for mbuf
468 **/
469
470typedef struct mbuf * packet_buffer_ref;
471
472static packet_buffer_ref
473packet_buffer_allocate(int length)
474{
475 packet_buffer_ref m;
476 int size;
477
478 /* leave room for ethernet header */
479 size = length + sizeof(struct ether_header);
480 if (size > (int)MHLEN) {
316670eb
A
481 if (size > (int)MCLBYTES) {
482 printf("bond: packet_buffer_allocate size %d > max %u\n",
483 size, MCLBYTES);
484 return (NULL);
485 }
486 m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
487 } else {
488 m = m_gethdr(M_WAITOK, MT_DATA);
91447636 489 }
91447636
A
490 if (m == NULL) {
491 return (NULL);
492 }
493 m->m_len = size;
494 m->m_pkthdr.len = size;
495 return (m);
496}
497
498static void *
499packet_buffer_byteptr(packet_buffer_ref buf)
500{
501 return (buf->m_data + sizeof(struct ether_header));
502}
503
504typedef enum {
505 LAEventStart,
506 LAEventTimeout,
507 LAEventPacket,
508 LAEventMediaChange,
509 LAEventSelectedChange,
510 LAEventPortMoved,
511 LAEventReady
512} LAEvent;
513
514/**
515 ** Receive machine
516 **/
517static void
518bondport_receive_machine(bondport_ref p, LAEvent event,
519 void * event_data);
520/**
521 ** Periodic Transmission machine
522 **/
523static void
524bondport_periodic_transmit_machine(bondport_ref p, LAEvent event,
525 void * event_data);
526
527/**
528 ** Transmit machine
529 **/
2d21ac55
A
530#define TRANSMIT_MACHINE_TX_IMMEDIATE ((void *)1)
531
91447636
A
532static void
533bondport_transmit_machine(bondport_ref p, LAEvent event,
534 void * event_data);
535
536/**
537 ** Mux machine
538 **/
539static void
540bondport_mux_machine(bondport_ref p, LAEvent event,
541 void * event_data);
542
543/**
544 ** bond, LAG
545 **/
546static void
547ifbond_activate_LAG(ifbond_ref bond, LAG_ref lag, int active_media);
548
549static void
550ifbond_deactivate_LAG(ifbond_ref bond, LAG_ref lag);
551
552static int
553ifbond_all_ports_ready(ifbond_ref bond);
554
555static LAG_ref
556ifbond_find_best_LAG(ifbond_ref bond, int * active_media);
557
558static int
559LAG_get_aggregatable_port_count(LAG_ref lag, int * active_media);
560
561static int
562ifbond_selection(ifbond_ref bond);
563
564
565/**
566 ** bondport
567 **/
568
569static void
570bondport_receive_lacpdu(bondport_ref p, lacpdu_ref in_lacpdu_p);
571
572static void
573bondport_slow_proto_transmit(bondport_ref p, packet_buffer_ref buf);
574
575static bondport_ref
576bondport_create(struct ifnet * port_ifp, lacp_port_priority priority,
577 int active, int short_timeout, int * error);
578static void
579bondport_start(bondport_ref p);
580
581static void
582bondport_free(bondport_ref p);
583
584static int
585bondport_aggregatable(bondport_ref p);
586
587static int
588bondport_remove_from_LAG(bondport_ref p);
589
590static void
591bondport_set_selected(bondport_ref p, SelectedState s);
592
593static int
594bondport_matches_LAG(bondport_ref p, LAG_ref lag);
595
596static void
597bondport_link_status_changed(bondport_ref p);
598
599static void
600bondport_enable_distributing(bondport_ref p);
601
602static void
603bondport_disable_distributing(bondport_ref p);
604
605static __inline__ int
606bondport_collecting(bondport_ref p)
607{
2d21ac55
A
608 if (p->po_bond->ifb_mode == IF_BOND_MODE_LACP) {
609 return (lacp_actor_partner_state_collecting(p->po_actor_state));
610 }
611 return (TRUE);
91447636
A
612}
613
614/**
615 ** bond interface/dlil specific routines
616 **/
d1ecb069
A
617static int bond_clone_create(struct if_clone *, u_int32_t, void *);
618static int bond_clone_destroy(struct ifnet *);
2d21ac55
A
619static int bond_input(ifnet_t ifp, protocol_family_t protocol, mbuf_t m,
620 char *frame_header);
91447636 621static int bond_output(struct ifnet *ifp, struct mbuf *m);
b0d623f7 622static int bond_ioctl(struct ifnet *ifp, u_long cmd, void * addr);
91447636
A
623static int bond_set_bpf_tap(struct ifnet * ifp, bpf_tap_mode mode,
624 bpf_packet_func func);
625static int bond_attach_protocol(struct ifnet *ifp);
626static int bond_detach_protocol(struct ifnet *ifp);
627static int bond_setmulti(struct ifnet *ifp);
628static int bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp);
629static int bond_remove_interface(ifbond_ref ifb, struct ifnet * port_ifp);
630static void bond_if_free(struct ifnet * ifp);
631
632static struct if_clone bond_cloner = IF_CLONE_INITIALIZER(BONDNAME,
633 bond_clone_create,
634 bond_clone_destroy,
635 0,
636 BOND_MAXUNIT);
b0d623f7 637static void interface_link_event(struct ifnet * ifp, u_int32_t event_code);
91447636
A
638
639static int
640siocsifmtu(struct ifnet * ifp, int mtu)
641{
642 struct ifreq ifr;
643
644 bzero(&ifr, sizeof(ifr));
645 ifr.ifr_mtu = mtu;
2d21ac55 646 return (ifnet_ioctl(ifp, 0, SIOCSIFMTU, &ifr));
91447636
A
647}
648
649static int
650siocgifdevmtu(struct ifnet * ifp, struct ifdevmtu * ifdm_p)
651{
652 struct ifreq ifr;
653 int error;
654
655 bzero(&ifr, sizeof(ifr));
2d21ac55 656 error = ifnet_ioctl(ifp, 0, SIOCGIFDEVMTU, &ifr);
91447636
A
657 if (error == 0) {
658 *ifdm_p = ifr.ifr_devmtu;
659 }
660 return (error);
661}
662
663static __inline__ void
664ether_addr_copy(void * dest, const void * source)
665{
666 bcopy(source, dest, ETHER_ADDR_LEN);
667 return;
668}
669
670static __inline__ void
671ifbond_retain(ifbond_ref ifb)
672{
673 OSIncrementAtomic(&ifb->ifb_retain_count);
674}
675
676static __inline__ void
677ifbond_release(ifbond_ref ifb)
678{
679 UInt32 old_retain_count;
680
681 old_retain_count = OSDecrementAtomic(&ifb->ifb_retain_count);
682 switch (old_retain_count) {
683 case 0:
684 panic("ifbond_release: retain count is 0\n");
685 break;
686 case 1:
687 if (g_bond->verbose) {
688 printf("ifbond_release(%s)\n", ifb->ifb_name);
689 }
690 if (ifb->ifb_ifma_slow_proto != NULL) {
691 if (g_bond->verbose) {
692 printf("ifbond_release(%s) removing multicast\n",
693 ifb->ifb_name);
694 }
6d2010ae
A
695 (void) if_delmulti_anon(ifb->ifb_ifma_slow_proto->ifma_ifp,
696 ifb->ifb_ifma_slow_proto->ifma_addr);
697 IFMA_REMREF(ifb->ifb_ifma_slow_proto);
91447636
A
698 }
699 if (ifb->ifb_distributing_array != NULL) {
700 FREE(ifb->ifb_distributing_array, M_BOND);
701 }
702 FREE(ifb, M_BOND);
703 break;
704 default:
705 break;
706 }
707 return;
708}
709
710/*
711 * Function: ifbond_wait
712 * Purpose:
713 * Allows a single thread to gain exclusive access to the ifbond
714 * data structure. Some operations take a long time to complete,
715 * and some have side-effects that we can't predict. Holding the
716 * bond_lock() across such operations is not possible.
717 *
718 * For example:
719 * 1) The SIOCSIFLLADDR ioctl takes a long time (several seconds) to
720 * complete. Simply holding the bond_lock() would freeze all other
721 * data structure accesses during that time.
722 * 2) When we attach our protocol to the interface, a dlil event is
723 * generated and invokes our bond_event() function. bond_event()
724 * needs to take the bond_lock(), but we're already holding it, so
725 * we're deadlocked against ourselves.
726 * Notes:
727 * Before calling, you must be holding the bond_lock and have taken
728 * a reference on the ifbond_ref.
729 */
730static void
731ifbond_wait(ifbond_ref ifb, const char * msg)
732{
733 int waited = 0;
734
735 /* other add/remove in progress */
736 while (ifbond_flags_change_in_progress(ifb)) {
737 if (g_bond->verbose) {
738 printf("%s: %s msleep\n", ifb->ifb_name, msg);
739 }
740 waited = 1;
741 (void)msleep(ifb, bond_lck_mtx, PZERO, msg, 0);
742 }
743 /* prevent other bond list remove/add from taking place */
744 ifbond_flags_set_change_in_progress(ifb);
745 if (g_bond->verbose && waited) {
746 printf("%s: %s woke up\n", ifb->ifb_name, msg);
747 }
748 return;
749}
750
751/*
752 * Function: ifbond_signal
753 * Purpose:
754 * Allows the thread that previously invoked ifbond_wait() to
755 * give up exclusive access to the ifbond data structure, and wake up
756 * any other threads waiting to access
757 * Notes:
758 * Before calling, you must be holding the bond_lock and have taken
759 * a reference on the ifbond_ref.
760 */
761static void
762ifbond_signal(ifbond_ref ifb, const char * msg)
763{
764 ifbond_flags_clear_change_in_progress(ifb);
765 wakeup((caddr_t)ifb);
766 if (g_bond->verbose) {
767 printf("%s: %s wakeup\n", ifb->ifb_name, msg);
768 }
769 return;
770}
771
772/**
773 ** Media information
774 **/
775
776static int
777link_speed(int active)
778{
779 switch (IFM_SUBTYPE(active)) {
780 case IFM_10_T:
781 case IFM_10_2:
782 case IFM_10_5:
783 case IFM_10_STP:
784 case IFM_10_FL:
785 return (10);
786 case IFM_100_TX:
787 case IFM_100_FX:
788 case IFM_100_T4:
789 case IFM_100_VG:
790 case IFM_100_T2:
791 return (100);
792 case IFM_1000_SX:
793 case IFM_1000_LX:
794 case IFM_1000_CX:
795 case IFM_1000_TX:
796 return (1000);
797 case IFM_HPNA_1:
798 return (0);
799 default:
800 /* assume that new defined types are going to be at least 10GigE */
801 case IFM_10G_SR:
802 case IFM_10G_LR:
803 return (10000);
3e170ce0
A
804 case IFM_2500_T:
805 return (2500);
806 case IFM_5000_T:
807 return (5000);
91447636
A
808 }
809}
810
811static __inline__ int
812media_active(const struct media_info * mi)
813{
814 if ((mi->mi_status & IFM_AVALID) == 0) {
815 return (1);
816 }
817 return ((mi->mi_status & IFM_ACTIVE) != 0);
818}
819
820static __inline__ int
821media_full_duplex(const struct media_info * mi)
822{
823 return ((mi->mi_active & IFM_FDX) != 0);
824}
825
826static __inline__ int
827media_speed(const struct media_info * mi)
828{
829 return (link_speed(mi->mi_active));
830}
831
832static struct media_info
833interface_media_info(struct ifnet * ifp)
834{
835 struct ifmediareq ifmr;
836 struct media_info mi;
837
838 bzero(&mi, sizeof(mi));
839 bzero(&ifmr, sizeof(ifmr));
2d21ac55 840 if (ifnet_ioctl(ifp, 0, SIOCGIFMEDIA, &ifmr) == 0) {
91447636
A
841 if (ifmr.ifm_count != 0) {
842 mi.mi_status = ifmr.ifm_status;
843 mi.mi_active = ifmr.ifm_active;
844 }
845 }
846 return (mi);
847}
848
91447636
A
849static int
850if_siflladdr(struct ifnet * ifp, const struct ether_addr * ea_p)
851{
852 struct ifreq ifr;
853
854 /*
855 * XXX setting the sa_len to ETHER_ADDR_LEN is wrong, but the driver
856 * currently expects it that way
857 */
858 ifr.ifr_addr.sa_family = AF_UNSPEC;
859 ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
860 ether_addr_copy(ifr.ifr_addr.sa_data, ea_p);
2d21ac55 861 return (ifnet_ioctl(ifp, 0, SIOCSIFLLADDR, &ifr));
91447636
A
862}
863
864/**
865 ** bond_globals
866 **/
867static bond_globals_ref
868bond_globals_create(lacp_system_priority sys_pri,
869 lacp_system_ref sys)
870{
871 bond_globals_ref b;
872
3e170ce0 873 b = _MALLOC(sizeof(*b), M_BOND, M_WAITOK | M_ZERO);
91447636
A
874 if (b == NULL) {
875 return (NULL);
876 }
91447636
A
877 TAILQ_INIT(&b->ifbond_list);
878 b->system = *sys;
879 b->system_priority = sys_pri;
91447636
A
880 return (b);
881}
882
883static int
884bond_globals_init(void)
885{
886 bond_globals_ref b;
887 int i;
888 struct ifnet * ifp;
889
890 bond_assert_lock_not_held();
891
892 if (g_bond != NULL) {
893 return (0);
894 }
895
896 /*
897 * use en0's ethernet address as the system identifier, and if it's not
898 * there, use en1 .. en3
899 */
900 ifp = NULL;
901 for (i = 0; i < 4; i++) {
902 char ifname[IFNAMSIZ+1];
903 snprintf(ifname, sizeof(ifname), "en%d", i);
91447636
A
904 ifp = ifunit(ifname);
905 if (ifp != NULL) {
906 break;
907 }
908 }
909 b = NULL;
910 if (ifp != NULL) {
39236c6e 911 b = bond_globals_create(0x8000, (lacp_system_ref)IF_LLADDR(ifp));
91447636
A
912 }
913 bond_lock();
914 if (g_bond != NULL) {
915 bond_unlock();
916 _FREE(b, M_BOND);
917 return (0);
918 }
919 g_bond = b;
920 bond_unlock();
921 if (ifp == NULL) {
922 return (ENXIO);
923 }
924 if (b == NULL) {
925 return (ENOMEM);
926 }
927 return (0);
928}
929
930static void
931bond_bpf_vlan(struct ifnet * ifp, struct mbuf * m,
932 const struct ether_header * eh_p,
933 u_int16_t vlan_tag, bpf_packet_func func)
934{
935 struct ether_vlan_header * vlh_p;
936 struct mbuf * vl_m;
937
938 vl_m = m_get(M_DONTWAIT, MT_DATA);
939 if (vl_m == NULL) {
940 return;
941 }
942 /* populate a new mbuf containing the vlan ethernet header */
943 vl_m->m_len = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
944 vlh_p = mtod(vl_m, struct ether_vlan_header *);
945 bcopy(eh_p, vlh_p, offsetof(struct ether_header, ether_type));
946 vlh_p->evl_encap_proto = htons(ETHERTYPE_VLAN);
947 vlh_p->evl_tag = htons(vlan_tag);
948 vlh_p->evl_proto = eh_p->ether_type;
949 vl_m->m_next = m;
950 (*func)(ifp, vl_m);
951 vl_m->m_next = NULL;
952 m_free(vl_m);
953 return;
954}
955
956static __inline__ void
957bond_bpf_output(struct ifnet * ifp, struct mbuf * m,
958 bpf_packet_func func)
959{
960 if (func != NULL) {
961 if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
962 const struct ether_header * eh_p;
963 eh_p = mtod(m, const struct ether_header *);
964 m->m_data += ETHER_HDR_LEN;
965 m->m_len -= ETHER_HDR_LEN;
966 bond_bpf_vlan(ifp, m, eh_p, m->m_pkthdr.vlan_tag, func);
967 m->m_data -= ETHER_HDR_LEN;
968 m->m_len += ETHER_HDR_LEN;
969 } else {
970 (*func)(ifp, m);
971 }
972 }
973 return;
974}
975
976static __inline__ void
977bond_bpf_input(ifnet_t ifp, mbuf_t m, const struct ether_header * eh_p,
978 bpf_packet_func func)
979{
980 if (func != NULL) {
981 if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
982 bond_bpf_vlan(ifp, m, eh_p, m->m_pkthdr.vlan_tag, func);
983 } else {
984 /* restore the header */
985 m->m_data -= ETHER_HDR_LEN;
986 m->m_len += ETHER_HDR_LEN;
987 (*func)(ifp, m);
988 m->m_data += ETHER_HDR_LEN;
989 m->m_len -= ETHER_HDR_LEN;
990 }
991 }
992 return;
993}
994
995/*
996 * Function: bond_setmulti
997 * Purpose:
998 * Enable multicast reception on "our" interface by enabling multicasts on
999 * each of the member ports.
1000 */
1001static int
1002bond_setmulti(struct ifnet * ifp)
1003{
1004 ifbond_ref ifb;
1005 int error;
1006 int result = 0;
1007 bondport_ref p;
1008
1009 bond_lock();
2d21ac55 1010 ifb = ifnet_softc(ifp);
91447636
A
1011 if (ifb == NULL || ifbond_flags_if_detaching(ifb)
1012 || TAILQ_EMPTY(&ifb->ifb_port_list)) {
1013 bond_unlock();
1014 return (0);
1015 }
1016 ifbond_retain(ifb);
1017 ifbond_wait(ifb, "bond_setmulti");
1018
1019 if (ifbond_flags_if_detaching(ifb)) {
1020 /* someone destroyed the bond while we were waiting */
1021 result = EBUSY;
1022 goto signal_done;
1023 }
1024 bond_unlock();
1025
1026 /* ifbond_wait() let's us safely walk the list without holding the lock */
1027 TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) {
1028 struct ifnet * port_ifp = p->po_ifp;
1029
1030 error = multicast_list_program(&p->po_multicast,
1031 ifp, port_ifp);
1032 if (error != 0) {
1033 printf("bond_setmulti(%s): "
1034 "multicast_list_program(%s%d) failed, %d\n",
2d21ac55
A
1035 ifb->ifb_name, ifnet_name(port_ifp),
1036 ifnet_unit(port_ifp), error);
91447636
A
1037 result = error;
1038 }
1039 }
1040 bond_lock();
1041 signal_done:
91447636
A
1042 ifbond_signal(ifb, "bond_setmulti");
1043 bond_unlock();
2d21ac55 1044 ifbond_release(ifb);
91447636
A
1045 return (result);
1046}
1047
b0d623f7 1048static int
91447636
A
1049bond_clone_attach(void)
1050{
b0d623f7
A
1051 int error;
1052
1053 if ((error = if_clone_attach(&bond_cloner)) != 0)
1054 return error;
91447636 1055 bond_lock_init();
b0d623f7 1056 return 0;
91447636
A
1057}
1058
1059static int
1060ifbond_add_slow_proto_multicast(ifbond_ref ifb)
1061{
1062 int error;
1063 struct ifmultiaddr * ifma = NULL;
1064 struct sockaddr_dl sdl;
1065
1066 bond_assert_lock_not_held();
1067
1068 bzero(&sdl, sizeof(sdl));
1069 sdl.sdl_len = sizeof(sdl);
1070 sdl.sdl_family = AF_LINK;
1071 sdl.sdl_type = IFT_ETHER;
1072 sdl.sdl_nlen = 0;
1073 sdl.sdl_alen = sizeof(slow_proto_multicast);
1074 bcopy(&slow_proto_multicast, sdl.sdl_data, sizeof(slow_proto_multicast));
6d2010ae 1075 error = if_addmulti_anon(ifb->ifb_ifp, (struct sockaddr *)&sdl, &ifma);
91447636
A
1076 if (error == 0) {
1077 ifb->ifb_ifma_slow_proto = ifma;
1078 }
1079 return (error);
1080}
1081
1082static int
d1ecb069 1083bond_clone_create(struct if_clone * ifc, u_int32_t unit, __unused void *params)
91447636 1084{
2d21ac55
A
1085 int error;
1086 ifbond_ref ifb;
1087 ifnet_t ifp;
39236c6e 1088 struct ifnet_init_eparams bond_init;
2d21ac55
A
1089
1090 error = bond_globals_init();
1091 if (error != 0) {
1092 return (error);
1093 }
1094
3e170ce0 1095 ifb = _MALLOC(sizeof(ifbond), M_BOND, M_WAITOK | M_ZERO);
2d21ac55
A
1096 if (ifb == NULL) {
1097 return (ENOMEM);
1098 }
2d21ac55
A
1099
1100 ifbond_retain(ifb);
1101 TAILQ_INIT(&ifb->ifb_port_list);
1102 TAILQ_INIT(&ifb->ifb_lag_list);
1103 ifb->ifb_key = unit + 1;
1104
1105 /* use the interface name as the unique id for ifp recycle */
b0d623f7 1106 if ((u_int32_t)snprintf(ifb->ifb_name, sizeof(ifb->ifb_name), "%s%d",
2d21ac55
A
1107 ifc->ifc_name, unit) >= sizeof(ifb->ifb_name)) {
1108 ifbond_release(ifb);
1109 return (EINVAL);
1110 }
1111
1112 bzero(&bond_init, sizeof(bond_init));
39236c6e
A
1113 bond_init.ver = IFNET_INIT_CURRENT_VERSION;
1114 bond_init.len = sizeof (bond_init);
1115 bond_init.flags = IFNET_INIT_LEGACY;
2d21ac55
A
1116 bond_init.uniqueid = ifb->ifb_name;
1117 bond_init.uniqueid_len = strlen(ifb->ifb_name);
1118 bond_init.name = ifc->ifc_name;
1119 bond_init.unit = unit;
1120 bond_init.family = IFNET_FAMILY_BOND;
1121 bond_init.type = IFT_IEEE8023ADLAG;
1122 bond_init.output = bond_output;
1123 bond_init.demux = ether_demux;
1124 bond_init.add_proto = ether_add_proto;
1125 bond_init.del_proto = ether_del_proto;
1126 bond_init.check_multi = ether_check_multi;
39236c6e 1127 bond_init.framer_extended = ether_frameout_extended;
2d21ac55
A
1128 bond_init.ioctl = bond_ioctl;
1129 bond_init.set_bpf_tap = bond_set_bpf_tap;
1130 bond_init.detach = bond_if_free;
1131 bond_init.broadcast_addr = etherbroadcastaddr;
1132 bond_init.broadcast_len = ETHER_ADDR_LEN;
1133 bond_init.softc = ifb;
39236c6e 1134 error = ifnet_allocate_extended(&bond_init, &ifp);
2d21ac55
A
1135
1136 if (error) {
1137 ifbond_release(ifb);
1138 return (error);
1139 }
1140
1141 ifb->ifb_ifp = ifp;
1142 ifnet_set_offload(ifp, 0);
1143 ifnet_set_addrlen(ifp, ETHER_ADDR_LEN); /* XXX ethernet specific */
1144 ifnet_set_flags(ifp, IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX, 0xffff);
1145 ifnet_set_baudrate(ifp, 0);
1146 ifnet_set_mtu(ifp, 0);
1147
1148 error = ifnet_attach(ifp, NULL);
1149 if (error != 0) {
1150 ifnet_release(ifp);
1151 ifbond_release(ifb);
1152 return (error);
1153 }
1154 error = ifbond_add_slow_proto_multicast(ifb);
1155 if (error != 0) {
1156 printf("bond_clone_create(%s): "
1157 "failed to add slow_proto multicast, %d\n",
1158 ifb->ifb_name, error);
1159 }
1160
1161 /* attach as ethernet */
1162 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
1163
1164 bond_lock();
1165 TAILQ_INSERT_HEAD(&g_bond->ifbond_list, ifb, ifb_bond_list);
1166 bond_unlock();
1167
1168 return (0);
91447636
A
1169}
1170
1171static void
1172bond_remove_all_interfaces(ifbond_ref ifb)
1173{
1174 bondport_ref p;
1175
1176 bond_assert_lock_held();
1177
1178 /*
1179 * do this in reverse order to avoid re-programming the mac address
1180 * as each head interface is removed
1181 */
1182 while ((p = TAILQ_LAST(&ifb->ifb_port_list, port_list)) != NULL) {
1183 bond_remove_interface(ifb, p->po_ifp);
1184 }
1185 return;
1186}
1187
1188static void
1189bond_remove(ifbond_ref ifb)
1190{
1191 bond_assert_lock_held();
1192 ifbond_flags_set_if_detaching(ifb);
1193 TAILQ_REMOVE(&g_bond->ifbond_list, ifb, ifb_bond_list);
1194 bond_remove_all_interfaces(ifb);
1195 return;
1196}
1197
1198static void
1199bond_if_detach(struct ifnet * ifp)
1200{
1201 int error;
1202
2d21ac55 1203 error = ifnet_detach(ifp);
6d2010ae
A
1204 if (error) {
1205 printf("bond_if_detach %s%d: ifnet_detach failed, %d\n",
1206 ifnet_name(ifp), ifnet_unit(ifp), error);
1207 }
2d21ac55 1208
91447636
A
1209 return;
1210}
1211
d1ecb069 1212static int
91447636
A
1213bond_clone_destroy(struct ifnet * ifp)
1214{
1215 ifbond_ref ifb;
1216
1217 bond_lock();
2d21ac55
A
1218 ifb = ifnet_softc(ifp);
1219 if (ifb == NULL || ifnet_type(ifp) != IFT_IEEE8023ADLAG) {
91447636 1220 bond_unlock();
d1ecb069 1221 return 0;
91447636
A
1222 }
1223 if (ifbond_flags_if_detaching(ifb)) {
1224 bond_unlock();
d1ecb069 1225 return 0;
91447636
A
1226 }
1227 bond_remove(ifb);
1228 bond_unlock();
1229 bond_if_detach(ifp);
d1ecb069 1230 return 0;
91447636
A
1231}
1232
1233static int
1234bond_set_bpf_tap(struct ifnet * ifp, bpf_tap_mode mode, bpf_packet_func func)
1235{
1236 ifbond_ref ifb;
1237
1238 bond_lock();
2d21ac55 1239 ifb = ifnet_softc(ifp);
91447636
A
1240 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
1241 bond_unlock();
1242 return (ENODEV);
1243 }
1244 switch (mode) {
1245 case BPF_TAP_DISABLE:
1246 ifb->ifb_bpf_input = ifb->ifb_bpf_output = NULL;
1247 break;
1248
1249 case BPF_TAP_INPUT:
1250 ifb->ifb_bpf_input = func;
1251 break;
1252
1253 case BPF_TAP_OUTPUT:
1254 ifb->ifb_bpf_output = func;
1255 break;
1256
1257 case BPF_TAP_INPUT_OUTPUT:
1258 ifb->ifb_bpf_input = ifb->ifb_bpf_output = func;
1259 break;
1260 default:
1261 break;
1262 }
1263 bond_unlock();
1264 return 0;
1265}
1266
1267static uint32_t
1268ether_header_hash(struct ether_header * eh_p)
1269{
1270 uint32_t h;
1271
1272 /* get 32-bits from destination ether and ether type */
1273 h = (*((uint16_t *)&eh_p->ether_dhost[4]) << 16)
1274 | eh_p->ether_type;
1275 h ^= *((uint32_t *)&eh_p->ether_dhost[0]);
1276 return (h);
1277}
1278
1279static struct mbuf *
b0d623f7 1280S_mbuf_skip_to_offset(struct mbuf * m, int32_t * offset)
91447636
A
1281{
1282 int len;
1283
1284 len = m->m_len;
1285 while (*offset >= len) {
1286 *offset -= len;
1287 m = m->m_next;
1288 if (m == NULL) {
1289 break;
1290 }
1291 len = m->m_len;
1292 }
1293 return (m);
1294}
1295
1296#if BYTE_ORDER == BIG_ENDIAN
1297static __inline__ uint32_t
1298make_uint32(u_char c0, u_char c1, u_char c2, u_char c3)
1299{
1300 return (((uint32_t)c0 << 24) | ((uint32_t)c1 << 16)
1301 | ((uint32_t)c2 << 8) | (uint32_t)c3);
1302}
1303#else /* BYTE_ORDER == LITTLE_ENDIAN */
1304static __inline__ uint32_t
1305make_uint32(u_char c0, u_char c1, u_char c2, u_char c3)
1306{
1307 return (((uint32_t)c3 << 24) | ((uint32_t)c2 << 16)
1308 | ((uint32_t)c1 << 8) | (uint32_t)c0);
1309}
1310#endif /* BYTE_ORDER == LITTLE_ENDIAN */
1311
1312static int
b0d623f7 1313S_mbuf_copy_uint32(struct mbuf * m, int32_t offset, uint32_t * val)
91447636
A
1314{
1315 struct mbuf * current;
1316 u_char * current_data;
1317 struct mbuf * next;
1318 u_char * next_data;
1319 int space_current;
1320
1321 current = S_mbuf_skip_to_offset(m, &offset);
1322 if (current == NULL) {
1323 return (1);
1324 }
1325 current_data = mtod(current, u_char *) + offset;
1326 space_current = current->m_len - offset;
1327 if (space_current >= (int)sizeof(uint32_t)) {
1328 *val = *((uint32_t *)current_data);
1329 return (0);
1330 }
1331 next = current->m_next;
1332 if (next == NULL || (next->m_len + space_current) < (int)sizeof(uint32_t)) {
1333 return (1);
1334 }
1335 next_data = mtod(next, u_char *);
1336 switch (space_current) {
1337 case 1:
1338 *val = make_uint32(current_data[0], next_data[0],
1339 next_data[1], next_data[2]);
1340 break;
1341 case 2:
1342 *val = make_uint32(current_data[0], current_data[1],
1343 next_data[0], next_data[1]);
1344 break;
1345 default:
1346 *val = make_uint32(current_data[0], current_data[1],
1347 current_data[2], next_data[0]);
1348 break;
1349 }
1350 return (0);
1351}
1352
1353#define IP_SRC_OFFSET (offsetof(struct ip, ip_src) - offsetof(struct ip, ip_p))
1354#define IP_DST_OFFSET (offsetof(struct ip, ip_dst) - offsetof(struct ip, ip_p))
1355
1356static uint32_t
1357ip_header_hash(struct mbuf * m)
1358{
1359 u_char * data;
1360 struct in_addr ip_dst;
1361 struct in_addr ip_src;
1362 u_char ip_p;
b0d623f7 1363 int32_t offset;
91447636
A
1364 struct mbuf * orig_m = m;
1365
1366 /* find the IP protocol field relative to the start of the packet */
1367 offset = offsetof(struct ip, ip_p) + sizeof(struct ether_header);
1368 m = S_mbuf_skip_to_offset(m, &offset);
1369 if (m == NULL || m->m_len < 1) {
1370 goto bad_ip_packet;
1371 }
1372 data = mtod(m, u_char *) + offset;
1373 ip_p = *data;
1374
1375 /* find the IP src relative to the IP protocol */
1376 if ((m->m_len - offset)
1377 >= (int)(IP_SRC_OFFSET + sizeof(struct in_addr) * 2)) {
1378 /* this should be the normal case */
1379 ip_src = *(struct in_addr *)(data + IP_SRC_OFFSET);
1380 ip_dst = *(struct in_addr *)(data + IP_DST_OFFSET);
1381 }
1382 else {
1383 if (S_mbuf_copy_uint32(m, offset + IP_SRC_OFFSET,
1384 (uint32_t *)&ip_src.s_addr)) {
1385 goto bad_ip_packet;
1386 }
1387 if (S_mbuf_copy_uint32(m, offset + IP_DST_OFFSET,
1388 (uint32_t *)&ip_dst.s_addr)) {
1389 goto bad_ip_packet;
1390 }
1391 }
1392 return (ntohl(ip_dst.s_addr) ^ ntohl(ip_src.s_addr) ^ ((uint32_t)ip_p));
1393
1394 bad_ip_packet:
1395 return (ether_header_hash(mtod(orig_m, struct ether_header *)));
1396}
1397
1398#define IP6_ADDRS_LEN (sizeof(struct in6_addr) * 2)
1399static uint32_t
1400ipv6_header_hash(struct mbuf * m)
1401{
1402 u_char * data;
1403 int i;
b0d623f7 1404 int32_t offset;
91447636
A
1405 struct mbuf * orig_m = m;
1406 uint32_t * scan;
1407 uint32_t val;
1408
1409 /* find the IP protocol field relative to the start of the packet */
1410 offset = offsetof(struct ip6_hdr, ip6_src) + sizeof(struct ether_header);
1411 m = S_mbuf_skip_to_offset(m, &offset);
1412 if (m == NULL) {
1413 goto bad_ipv6_packet;
1414 }
1415 data = mtod(m, u_char *) + offset;
1416 val = 0;
1417 if ((m->m_len - offset) >= (int)IP6_ADDRS_LEN) {
1418 /* this should be the normal case */
1419 for (i = 0, scan = (uint32_t *)data;
1420 i < (int)(IP6_ADDRS_LEN / sizeof(uint32_t));
1421 i++, scan++) {
1422 val ^= *scan;
1423 }
1424 }
1425 else {
1426 for (i = 0; i < (int)(IP6_ADDRS_LEN / sizeof(uint32_t)); i++) {
1427 uint32_t tmp;
1428 if (S_mbuf_copy_uint32(m, offset + i * sizeof(uint32_t),
1429 (uint32_t *)&tmp)) {
1430 goto bad_ipv6_packet;
1431 }
1432 val ^= tmp;
1433 }
1434 }
1435 return (ntohl(val));
1436
1437 bad_ipv6_packet:
1438 return (ether_header_hash(mtod(orig_m, struct ether_header *)));
1439}
1440
1441static int
1442bond_output(struct ifnet * ifp, struct mbuf * m)
1443{
1444 bpf_packet_func bpf_func;
1445 uint32_t h;
1446 ifbond_ref ifb;
1447 struct ifnet * port_ifp = NULL;
316670eb
A
1448 int err;
1449 struct flowadv adv = { FADV_SUCCESS };
91447636
A
1450
1451 if (m == 0) {
1452 return (0);
1453 }
1454 if ((m->m_flags & M_PKTHDR) == 0) {
1455 m_freem(m);
1456 return (0);
1457 }
39236c6e
A
1458 if (m->m_pkthdr.pkt_flowid != 0) {
1459 h = m->m_pkthdr.pkt_flowid;
91447636
A
1460 }
1461 else {
1462 struct ether_header * eh_p;
1463
1464 eh_p = mtod(m, struct ether_header *);
1465 switch (ntohs(eh_p->ether_type)) {
1466 case ETHERTYPE_IP:
1467 h = ip_header_hash(m);
1468 break;
1469 case ETHERTYPE_IPV6:
1470 h = ipv6_header_hash(m);
1471 break;
1472 default:
1473 h = ether_header_hash(eh_p);
1474 break;
1475 }
1476 }
1477 bond_lock();
2d21ac55 1478 ifb = ifnet_softc(ifp);
91447636
A
1479 if (ifb == NULL || ifbond_flags_if_detaching(ifb)
1480 || ifb->ifb_distributing_count == 0) {
1481 goto done;
1482 }
1483 h %= ifb->ifb_distributing_count;
1484 port_ifp = ifb->ifb_distributing_array[h]->po_ifp;
1485 bpf_func = ifb->ifb_bpf_output;
1486 bond_unlock();
1487
1488 if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
1489 (void)ifnet_stat_increment_out(ifp, 1,
1490 m->m_pkthdr.len + ETHER_VLAN_ENCAP_LEN,
1491 0);
1492 } else {
1493 (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0);
1494 }
1495 bond_bpf_output(ifp, m, bpf_func);
1496
316670eb
A
1497 err = dlil_output(port_ifp, PF_BOND, m, NULL, NULL, 1, &adv);
1498
1499 if (err == 0) {
1500 if (adv.code == FADV_FLOW_CONTROLLED) {
1501 err = EQFULL;
1502 } else if (adv.code == FADV_SUSPENDED) {
1503 err = EQSUSPENDED;
1504 }
1505 }
1506
1507 return (err);
91447636
A
1508
1509 done:
1510 bond_unlock();
1511 m_freem(m);
1512 return (0);
1513}
1514
1515static bondport_ref
1516ifbond_lookup_port(ifbond_ref ifb, struct ifnet * port_ifp)
1517{
1518 bondport_ref p;
1519 TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) {
1520 if (p->po_ifp == port_ifp) {
1521 return (p);
1522 }
1523 }
1524 return (NULL);
1525}
1526
1527static bondport_ref
1528bond_lookup_port(struct ifnet * port_ifp)
1529{
1530 ifbond_ref ifb;
1531 bondport_ref port;
1532
1533 TAILQ_FOREACH(ifb, &g_bond->ifbond_list, ifb_bond_list) {
1534 port = ifbond_lookup_port(ifb, port_ifp);
1535 if (port != NULL) {
1536 return (port);
1537 }
1538 }
1539 return (NULL);
1540}
1541
1542static void
1543bond_receive_lacpdu(struct mbuf * m, struct ifnet * port_ifp)
1544{
1545 struct ifnet * bond_ifp = NULL;
2d21ac55 1546 ifbond_ref ifb;
91447636
A
1547 int event_code = 0;
1548 bondport_ref p;
1549
1550 bond_lock();
2d21ac55 1551 if ((ifnet_eflags(port_ifp) & IFEF_BOND) == 0) {
91447636
A
1552 goto done;
1553 }
1554 p = bond_lookup_port(port_ifp);
1555 if (p == NULL) {
1556 goto done;
1557 }
1558 if (p->po_enabled == 0) {
1559 goto done;
1560 }
2d21ac55
A
1561 ifb = p->po_bond;
1562 if (ifb->ifb_mode != IF_BOND_MODE_LACP) {
1563 goto done;
1564 }
91447636 1565 bondport_receive_lacpdu(p, (lacpdu_ref)m->m_data);
2d21ac55
A
1566 if (ifbond_selection(ifb)) {
1567 event_code = (ifb->ifb_active_lag == NULL)
91447636
A
1568 ? KEV_DL_LINK_OFF
1569 : KEV_DL_LINK_ON;
1570 /* XXX need to take a reference on bond_ifp */
2d21ac55
A
1571 bond_ifp = ifb->ifb_ifp;
1572 ifb->ifb_last_link_event = event_code;
1573 }
1574 else {
1575 event_code = (ifb->ifb_active_lag == NULL)
1576 ? KEV_DL_LINK_OFF
1577 : KEV_DL_LINK_ON;
1578 if (event_code != ifb->ifb_last_link_event) {
1579 if (g_bond->verbose) {
1580 timestamp_printf("%s: (receive) generating LINK event\n",
1581 ifb->ifb_name);
1582 }
1583 bond_ifp = ifb->ifb_ifp;
1584 ifb->ifb_last_link_event = event_code;
1585 }
91447636
A
1586 }
1587
1588 done:
1589 bond_unlock();
1590 if (bond_ifp != NULL) {
1591 interface_link_event(bond_ifp, event_code);
1592 }
1593 m_freem(m);
1594 return;
1595}
1596
1597static void
1598bond_receive_la_marker_pdu(struct mbuf * m, struct ifnet * port_ifp)
1599{
1600 la_marker_pdu_ref marker_p;
1601 bondport_ref p;
1602
1603 marker_p = (la_marker_pdu_ref)(m->m_data + ETHER_HDR_LEN);
1604 if (marker_p->lm_marker_tlv_type != LA_MARKER_TLV_TYPE_MARKER) {
1605 goto failed;
1606 }
1607 bond_lock();
2d21ac55 1608 if ((ifnet_eflags(port_ifp) & IFEF_BOND) == 0) {
91447636
A
1609 bond_unlock();
1610 goto failed;
1611 }
1612 p = bond_lookup_port(port_ifp);
2d21ac55
A
1613 if (p == NULL || p->po_enabled == 0
1614 || p->po_bond->ifb_mode != IF_BOND_MODE_LACP) {
91447636
A
1615 bond_unlock();
1616 goto failed;
1617 }
1618 /* echo back the same packet as a marker response */
1619 marker_p->lm_marker_tlv_type = LA_MARKER_TLV_TYPE_MARKER_RESPONSE;
1620 bondport_slow_proto_transmit(p, (packet_buffer_ref)m);
1621 bond_unlock();
1622 return;
1623
1624 failed:
1625 m_freem(m);
1626 return;
1627}
1628
1629static int
2d21ac55
A
1630bond_input(ifnet_t port_ifp, __unused protocol_family_t protocol, mbuf_t m,
1631 char * frame_header)
91447636
A
1632{
1633 bpf_packet_func bpf_func;
1634 const struct ether_header * eh_p;
1635 ifbond_ref ifb;
1636 struct ifnet * ifp;
1637 bondport_ref p;
1638
1639 eh_p = (const struct ether_header *)frame_header;
1640 if ((m->m_flags & M_MCAST) != 0
1641 && bcmp(eh_p->ether_dhost, &slow_proto_multicast,
1642 sizeof(eh_p->ether_dhost)) == 0
1643 && ntohs(eh_p->ether_type) == IEEE8023AD_SLOW_PROTO_ETHERTYPE) {
1644 u_char subtype = *mtod(m, u_char *);
1645
1646 if (subtype == IEEE8023AD_SLOW_PROTO_SUBTYPE_LACP) {
1647 if (m->m_pkthdr.len < (int)offsetof(lacpdu, la_reserved)) {
1648 m_freem(m);
1649 return (0);
1650 }
1651 /* send to lacp */
1652 if (m->m_len < (int)offsetof(lacpdu, la_reserved)) {
1653 m = m_pullup(m, offsetof(lacpdu, la_reserved));
1654 if (m == NULL) {
1655 return (0);
1656 }
1657 }
1658 bond_receive_lacpdu(m, port_ifp);
1659 return (0);
1660 }
1661 else if (subtype == IEEE8023AD_SLOW_PROTO_SUBTYPE_LA_MARKER_PROTOCOL) {
1662 int min_size;
1663
1664 /* restore the ethernet header pointer in the mbuf */
1665 m->m_pkthdr.len += ETHER_HDR_LEN;
1666 m->m_data -= ETHER_HDR_LEN;
1667 m->m_len += ETHER_HDR_LEN;
1668 min_size = ETHER_HDR_LEN + offsetof(la_marker_pdu, lm_reserved);
1669 if (m->m_pkthdr.len < min_size) {
1670 m_freem(m);
1671 return (0);
1672 }
1673 /* send to lacp */
1674 if (m->m_len < min_size) {
1675 m = m_pullup(m, min_size);
1676 if (m == NULL) {
1677 return (0);
1678 }
1679 }
1680 /* send to marker responder */
1681 bond_receive_la_marker_pdu(m, port_ifp);
1682 return (0);
1683 }
1684 else if (subtype == 0
1685 || subtype > IEEE8023AD_SLOW_PROTO_SUBTYPE_RESERVED_END) {
1686 /* invalid subtype, discard the frame */
1687 m_freem(m);
1688 return (0);
1689 }
1690 }
1691 bond_lock();
2d21ac55 1692 if ((ifnet_eflags(port_ifp) & IFEF_BOND) == 0) {
91447636
A
1693 goto done;
1694 }
1695 p = bond_lookup_port(port_ifp);
1696 if (p == NULL || bondport_collecting(p) == 0) {
1697 goto done;
1698 }
1699
1700 /* make the packet appear as if it arrived on the bonded interface */
1701 ifb = p->po_bond;
1702 ifp = ifb->ifb_ifp;
1703 bpf_func = ifb->ifb_bpf_input;
1704 bond_unlock();
1705
1706 if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
1707 (void)ifnet_stat_increment_in(ifp, 1,
1708 (m->m_pkthdr.len + ETHER_HDR_LEN
1709 + ETHER_VLAN_ENCAP_LEN), 0);
1710 }
1711 else {
1712 (void)ifnet_stat_increment_in(ifp, 1,
1713 (m->m_pkthdr.len + ETHER_HDR_LEN), 0);
1714 }
1715 m->m_pkthdr.rcvif = ifp;
1716 bond_bpf_input(ifp, m, eh_p, bpf_func);
39236c6e 1717 m->m_pkthdr.pkt_hdr = frame_header;
2d21ac55 1718 dlil_input_packet_list(ifp, m);
91447636
A
1719 return 0;
1720
1721 done:
1722 bond_unlock();
1723 m_freem(m);
1724 return (0);
1725}
1726
1727static __inline__ const char *
1728bondport_get_name(bondport_ref p)
1729{
1730 return (p->po_name);
1731}
1732
1733static __inline__ int
1734bondport_get_index(bondport_ref p)
1735{
2d21ac55 1736 return (ifnet_index(p->po_ifp));
91447636
A
1737}
1738
1739static void
1740bondport_slow_proto_transmit(bondport_ref p, packet_buffer_ref buf)
1741{
1742 struct ether_header * eh_p;
1743 int error;
1744
1745 /* packet_buffer_allocate leaves room for ethernet header */
1746 eh_p = mtod(buf, struct ether_header *);
1747 bcopy(&slow_proto_multicast, &eh_p->ether_dhost, sizeof(eh_p->ether_dhost));
1748 bcopy(&p->po_saved_addr, eh_p->ether_shost, sizeof(eh_p->ether_shost));
1749 eh_p->ether_type = htons(IEEE8023AD_SLOW_PROTO_ETHERTYPE);
2d21ac55 1750 error = ifnet_output_raw(p->po_ifp, PF_BOND, buf);
91447636
A
1751 if (error != 0) {
1752 printf("bondport_slow_proto_transmit(%s) failed %d\n",
1753 bondport_get_name(p), error);
1754 }
1755 return;
1756}
1757
1758static void
1759bondport_timer_process_func(devtimer_ref timer,
1760 devtimer_process_func_event event)
1761{
1762 bondport_ref p;
1763
1764 switch (event) {
1765 case devtimer_process_func_event_lock:
1766 bond_lock();
1767 devtimer_retain(timer);
1768 break;
1769 case devtimer_process_func_event_unlock:
1770 if (devtimer_valid(timer)) {
1771 /* as long as the devtimer is valid, we can look at arg0 */
1772 int event_code = 0;
1773 struct ifnet * bond_ifp = NULL;
1774
1775 p = (bondport_ref)devtimer_arg0(timer);
1776 if (ifbond_selection(p->po_bond)) {
1777 event_code = (p->po_bond->ifb_active_lag == NULL)
1778 ? KEV_DL_LINK_OFF
1779 : KEV_DL_LINK_ON;
1780 /* XXX need to take a reference on bond_ifp */
1781 bond_ifp = p->po_bond->ifb_ifp;
2d21ac55
A
1782 p->po_bond->ifb_last_link_event = event_code;
1783 }
1784 else {
1785 event_code = (p->po_bond->ifb_active_lag == NULL)
1786 ? KEV_DL_LINK_OFF
1787 : KEV_DL_LINK_ON;
1788 if (event_code != p->po_bond->ifb_last_link_event) {
1789 if (g_bond->verbose) {
1790 timestamp_printf("%s: (timer) generating LINK event\n",
1791 p->po_bond->ifb_name);
1792 }
1793 bond_ifp = p->po_bond->ifb_ifp;
1794 p->po_bond->ifb_last_link_event = event_code;
1795 }
91447636
A
1796 }
1797 devtimer_release(timer);
1798 bond_unlock();
1799 if (bond_ifp != NULL) {
1800 interface_link_event(bond_ifp, event_code);
1801 }
1802 }
1803 else {
1804 /* timer is going away */
1805 devtimer_release(timer);
1806 bond_unlock();
1807 }
1808 break;
1809 default:
1810 break;
1811 }
1812}
1813
1814static bondport_ref
1815bondport_create(struct ifnet * port_ifp, lacp_port_priority priority,
1816 int active, int short_timeout, int * ret_error)
1817{
1818 int error = 0;
1819 bondport_ref p = NULL;
1820 lacp_actor_partner_state s;
1821
1822 *ret_error = 0;
3e170ce0 1823 p = _MALLOC(sizeof(*p), M_BOND, M_WAITOK | M_ZERO);
91447636
A
1824 if (p == NULL) {
1825 *ret_error = ENOMEM;
1826 return (NULL);
1827 }
91447636 1828 multicast_list_init(&p->po_multicast);
b0d623f7 1829 if ((u_int32_t)snprintf(p->po_name, sizeof(p->po_name), "%s%d",
2d21ac55 1830 ifnet_name(port_ifp), ifnet_unit(port_ifp))
91447636
A
1831 >= sizeof(p->po_name)) {
1832 printf("if_bond: name too large\n");
1833 *ret_error = EINVAL;
1834 goto failed;
1835 }
1836 error = siocgifdevmtu(port_ifp, &p->po_devmtu);
1837 if (error != 0) {
1838 printf("if_bond: SIOCGIFDEVMTU %s failed, %d\n",
1839 bondport_get_name(p), error);
1840 goto failed;
1841 }
1842 /* remember the current interface MTU so it can be restored */
2d21ac55 1843 p->po_devmtu.ifdm_current = ifnet_mtu(port_ifp);
91447636
A
1844 p->po_ifp = port_ifp;
1845 p->po_media_info = interface_media_info(port_ifp);
1846 p->po_current_while_timer = devtimer_create(bondport_timer_process_func, p);
1847 if (p->po_current_while_timer == NULL) {
1848 *ret_error = ENOMEM;
1849 goto failed;
1850 }
1851 p->po_periodic_timer = devtimer_create(bondport_timer_process_func, p);
1852 if (p->po_periodic_timer == NULL) {
1853 *ret_error = ENOMEM;
1854 goto failed;
1855 }
1856 p->po_wait_while_timer = devtimer_create(bondport_timer_process_func, p);
1857 if (p->po_wait_while_timer == NULL) {
1858 *ret_error = ENOMEM;
1859 goto failed;
1860 }
1861 p->po_transmit_timer = devtimer_create(bondport_timer_process_func, p);
1862 if (p->po_transmit_timer == NULL) {
1863 *ret_error = ENOMEM;
1864 goto failed;
1865 }
1866 p->po_receive_state = ReceiveState_none;
1867 p->po_mux_state = MuxState_none;
1868 p->po_priority = priority;
1869 s = 0;
1870 s = lacp_actor_partner_state_set_aggregatable(s);
1871 if (short_timeout) {
1872 s = lacp_actor_partner_state_set_short_timeout(s);
1873 }
1874 if (active) {
1875 s = lacp_actor_partner_state_set_active_lacp(s);
1876 }
1877 p->po_actor_state = s;
1878 return (p);
1879
1880 failed:
1881 bondport_free(p);
1882 return (NULL);
1883}
1884
1885static void
1886bondport_start(bondport_ref p)
1887{
1888 bondport_receive_machine(p, LAEventStart, NULL);
1889 bondport_mux_machine(p, LAEventStart, NULL);
1890 bondport_periodic_transmit_machine(p, LAEventStart, NULL);
1891 bondport_transmit_machine(p, LAEventStart, NULL);
1892 return;
1893}
1894
1895/*
1896 * Function: bondport_invalidate_timers
1897 * Purpose:
1898 * Invalidate all of the timers for the bondport.
1899 */
1900static void
1901bondport_invalidate_timers(bondport_ref p)
1902{
1903 devtimer_invalidate(p->po_current_while_timer);
1904 devtimer_invalidate(p->po_periodic_timer);
1905 devtimer_invalidate(p->po_wait_while_timer);
1906 devtimer_invalidate(p->po_transmit_timer);
1907}
1908
2d21ac55
A
1909/*
1910 * Function: bondport_cancel_timers
1911 * Purpose:
1912 * Cancel all of the timers for the bondport.
1913 */
1914static void
1915bondport_cancel_timers(bondport_ref p)
1916{
1917 devtimer_cancel(p->po_current_while_timer);
1918 devtimer_cancel(p->po_periodic_timer);
1919 devtimer_cancel(p->po_wait_while_timer);
1920 devtimer_cancel(p->po_transmit_timer);
1921}
1922
91447636
A
1923static void
1924bondport_free(bondport_ref p)
1925{
1926 multicast_list_remove(&p->po_multicast);
1927 devtimer_release(p->po_current_while_timer);
1928 devtimer_release(p->po_periodic_timer);
1929 devtimer_release(p->po_wait_while_timer);
1930 devtimer_release(p->po_transmit_timer);
1931 FREE(p, M_BOND);
1932 return;
1933}
1934
1935#define BOND_ADD_PROGRESS_IN_LIST 0x1
1936#define BOND_ADD_PROGRESS_PROTO_ATTACHED 0x2
1937#define BOND_ADD_PROGRESS_LLADDR_SET 0x4
1938#define BOND_ADD_PROGRESS_MTU_SET 0x8
1939
1940static __inline__ int
1941bond_device_mtu(struct ifnet * ifp, ifbond_ref ifb)
1942{
2d21ac55
A
1943 return (((int)ifnet_mtu(ifp) > ifb->ifb_altmtu)
1944 ? (int)ifnet_mtu(ifp) : ifb->ifb_altmtu);
91447636
A
1945}
1946
1947static int
1948bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp)
1949{
1950 int devmtu;
1951 int error = 0;
1952 int event_code = 0;
2d21ac55 1953 int first = FALSE;
91447636 1954 ifbond_ref ifb;
91447636
A
1955 bondport_ref * new_array = NULL;
1956 bondport_ref * old_array = NULL;
1957 bondport_ref p;
91447636
A
1958 int progress = 0;
1959
5c9f4661
A
1960 if (IFNET_IS_INTCOPROC(port_ifp)) {
1961 return (EINVAL);
1962 }
1963
91447636
A
1964 /* pre-allocate space for new port */
1965 p = bondport_create(port_ifp, 0x8000, 1, 0, &error);
1966 if (p == NULL) {
1967 return (error);
1968 }
1969 bond_lock();
2d21ac55 1970 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
1971 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
1972 bond_unlock();
1973 bondport_free(p);
1974 return ((ifb == NULL ? EOPNOTSUPP : EBUSY));
1975 }
1976
1977 /* make sure this interface can handle our current MTU */
1978 devmtu = bond_device_mtu(ifp, ifb);
1979 if (devmtu != 0
1980 && (devmtu > p->po_devmtu.ifdm_max || devmtu < p->po_devmtu.ifdm_min)) {
1981 bond_unlock();
1982 printf("if_bond: interface %s doesn't support mtu %d",
1983 bondport_get_name(p), devmtu);
1984 bondport_free(p);
1985 return (EINVAL);
1986 }
1987
1988 /* make sure ifb doesn't get de-allocated while we wait */
1989 ifbond_retain(ifb);
1990
1991 /* wait for other add or remove to complete */
1992 ifbond_wait(ifb, "bond_add_interface");
1993
1994 if (ifbond_flags_if_detaching(ifb)) {
1995 /* someone destroyed the bond while we were waiting */
1996 error = EBUSY;
1997 goto signal_done;
1998 }
1999 if (bond_lookup_port(port_ifp) != NULL) {
2000 /* port is already part of a bond */
2001 error = EBUSY;
2002 goto signal_done;
2003 }
2004 ifnet_lock_exclusive(port_ifp);
2d21ac55 2005 if ((ifnet_eflags(port_ifp) & (IFEF_VLAN | IFEF_BOND)) != 0) {
91447636
A
2006 /* interface already has VLAN's, or is part of bond */
2007 ifnet_lock_done(port_ifp);
2008 error = EBUSY;
2009 goto signal_done;
2010 }
2011
2012 /* mark the interface busy */
2d21ac55 2013 /* can't use ifnet_set_eflags because that takes the lock */
91447636
A
2014 port_ifp->if_eflags |= IFEF_BOND;
2015 ifnet_lock_done(port_ifp);
2016
91447636 2017 if (TAILQ_EMPTY(&ifb->ifb_port_list)) {
2d21ac55
A
2018 ifnet_set_offload(ifp, ifnet_offload(port_ifp));
2019 ifnet_set_flags(ifp, IFF_RUNNING, IFF_RUNNING);
91447636 2020 if (ifbond_flags_lladdr(ifb) == FALSE) {
2d21ac55 2021 first = TRUE;
91447636
A
2022 }
2023 } else {
2d21ac55
A
2024 ifnet_offload_t ifp_offload;
2025 ifnet_offload_t port_ifp_offload;
2026
2027 ifp_offload = ifnet_offload(ifp);
2028 port_ifp_offload = ifnet_offload(port_ifp);
2029 if (ifp_offload != port_ifp_offload) {
2030 ifnet_offload_t offload;
2031
2032 offload = ifp_offload & port_ifp_offload;
91447636 2033 printf("bond_add_interface(%s, %s) "
2d21ac55 2034 "hwassist values don't match 0x%x != 0x%x, using 0x%x instead\n",
91447636 2035 ifb->ifb_name, bondport_get_name(p),
2d21ac55 2036 ifp_offload, port_ifp_offload, offload);
91447636
A
2037 /*
2038 * XXX
2039 * if the bond has VLAN's, we can't simply change the hwassist
2040 * field behind its back: this needs work
2041 */
2d21ac55 2042 ifnet_set_offload(ifp, offload);
91447636
A
2043 }
2044 }
2045 p->po_bond = ifb;
2046
2047 /* remember the port's ethernet address so it can be restored */
39236c6e 2048 ether_addr_copy(&p->po_saved_addr, IF_LLADDR(port_ifp));
91447636
A
2049
2050 /* add it to the list of ports */
2051 TAILQ_INSERT_TAIL(&ifb->ifb_port_list, p, po_port_list);
2052 ifb->ifb_port_count++;
2053
2054 /* set the default MTU */
2d21ac55
A
2055 if (ifnet_mtu(ifp) == 0) {
2056 ifnet_set_mtu(ifp, ETHERMTU);
91447636
A
2057 }
2058 bond_unlock();
2d21ac55
A
2059
2060
2061 /* first port added to bond determines bond's ethernet address */
2062 if (first) {
39236c6e 2063 ifnet_set_lladdr_and_type(ifp, IF_LLADDR(port_ifp), ETHER_ADDR_LEN,
2d21ac55
A
2064 IFT_ETHER);
2065 }
2066
91447636
A
2067 progress |= BOND_ADD_PROGRESS_IN_LIST;
2068
2069 /* allocate a larger distributing array */
2070 new_array = (bondport_ref *)
2071 _MALLOC(sizeof(*new_array) * ifb->ifb_port_count, M_BOND, M_WAITOK);
2072 if (new_array == NULL) {
2073 error = ENOMEM;
2074 goto failed;
2075 }
2076
2077 /* attach our BOND "protocol" to the interface */
2078 error = bond_attach_protocol(port_ifp);
2079 if (error) {
2080 goto failed;
2081 }
2082 progress |= BOND_ADD_PROGRESS_PROTO_ATTACHED;
2083
2084 /* set the interface MTU */
2085 devmtu = bond_device_mtu(ifp, ifb);
2086 error = siocsifmtu(port_ifp, devmtu);
2087 if (error != 0) {
2088 printf("bond_add_interface(%s, %s):"
2089 " SIOCSIFMTU %d failed %d\n",
2090 ifb->ifb_name, bondport_get_name(p), devmtu, error);
2091 goto failed;
2092 }
2093 progress |= BOND_ADD_PROGRESS_MTU_SET;
2094
2095 /* program the port with our multicast addresses */
2096 error = multicast_list_program(&p->po_multicast, ifp, port_ifp);
2097 if (error) {
2098 printf("bond_add_interface(%s, %s):"
2099 " multicast_list_program failed %d\n",
2100 ifb->ifb_name, bondport_get_name(p), error);
2101 goto failed;
2102 }
2103
2104 /* mark the interface up */
2d21ac55 2105 ifnet_set_flags(port_ifp, IFF_UP, IFF_UP);
91447636 2106
2d21ac55 2107 error = ifnet_ioctl(port_ifp, 0, SIOCSIFFLAGS, NULL);
91447636
A
2108 if (error != 0) {
2109 printf("bond_add_interface(%s, %s): SIOCSIFFLAGS failed %d\n",
2110 ifb->ifb_name, bondport_get_name(p), error);
2111 goto failed;
2112 }
2113
2114 /* re-program the port's ethernet address */
2115 error = if_siflladdr(port_ifp,
39236c6e 2116 (const struct ether_addr *)IF_LLADDR(ifp));
91447636
A
2117 if (error != 0) {
2118 /* port doesn't support setting the link address */
2119 printf("bond_add_interface(%s, %s): if_siflladdr failed %d\n",
2120 ifb->ifb_name, bondport_get_name(p), error);
2121 goto failed;
2122 }
2123 progress |= BOND_ADD_PROGRESS_LLADDR_SET;
2124
2125 bond_lock();
2126
2127 /* no failures past this point */
2128 p->po_enabled = 1;
2129
2130 /* copy the contents of the existing distributing array */
2131 if (ifb->ifb_distributing_count) {
2132 bcopy(ifb->ifb_distributing_array, new_array,
2133 sizeof(*new_array) * ifb->ifb_distributing_count);
2134 }
2135 old_array = ifb->ifb_distributing_array;
2136 ifb->ifb_distributing_array = new_array;
2137
2d21ac55
A
2138 if (ifb->ifb_mode == IF_BOND_MODE_LACP) {
2139 bondport_start(p);
91447636 2140
2d21ac55
A
2141 /* check if we need to generate a link status event */
2142 if (ifbond_selection(ifb)) {
2143 event_code = (ifb->ifb_active_lag == NULL)
2144 ? KEV_DL_LINK_OFF
2145 : KEV_DL_LINK_ON;
2146 ifb->ifb_last_link_event = event_code;
2147 }
2148 }
2149 else {
2150 /* are we adding the first distributing interface? */
2151 if (media_active(&p->po_media_info)) {
2152 if (ifb->ifb_distributing_count == 0) {
2153 ifb->ifb_last_link_event = event_code = KEV_DL_LINK_ON;
2154 }
2155 bondport_enable_distributing(p);
2156 }
2157 else {
2158 bondport_disable_distributing(p);
2159 }
91447636 2160 }
2d21ac55
A
2161 /* clear the busy state, and wakeup anyone waiting */
2162 ifbond_signal(ifb, "bond_add_interface");
91447636
A
2163 bond_unlock();
2164 if (event_code != 0) {
2165 interface_link_event(ifp, event_code);
2166 }
2167 if (old_array != NULL) {
2168 FREE(old_array, M_BOND);
2169 }
2170 return 0;
2171
2172 failed:
2173 bond_assert_lock_not_held();
2174
2d21ac55
A
2175 /* if this was the first port to be added, clear our address */
2176 if (first) {
2177 ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_IEEE8023ADLAG);
2178 }
2179
91447636
A
2180 if (new_array != NULL) {
2181 FREE(new_array, M_BOND);
2182 }
2183 if ((progress & BOND_ADD_PROGRESS_LLADDR_SET) != 0) {
2184 int error1;
2185
2186 error1 = if_siflladdr(port_ifp, &p->po_saved_addr);
2187 if (error1 != 0) {
2188 printf("bond_add_interface(%s, %s): if_siflladdr failed %d\n",
2189 ifb->ifb_name, bondport_get_name(p), error1);
2190 }
2191 }
2192 if ((progress & BOND_ADD_PROGRESS_PROTO_ATTACHED) != 0) {
2193 (void)bond_detach_protocol(port_ifp);
2194 }
2195 if ((progress & BOND_ADD_PROGRESS_MTU_SET) != 0) {
2196 int error1;
2197
2198 error1 = siocsifmtu(port_ifp, p->po_devmtu.ifdm_current);
2199 if (error1 != 0) {
2200 printf("bond_add_interface(%s, %s): SIOCSIFMTU %d failed %d\n",
2d21ac55
A
2201 ifb->ifb_name, bondport_get_name(p),
2202 p->po_devmtu.ifdm_current, error1);
91447636
A
2203 }
2204 }
2205 bond_lock();
2206 if ((progress & BOND_ADD_PROGRESS_IN_LIST) != 0) {
2207 TAILQ_REMOVE(&ifb->ifb_port_list, p, po_port_list);
2208 ifb->ifb_port_count--;
2209 }
2d21ac55 2210 ifnet_set_eflags(ifp, 0, IFEF_BOND);
91447636
A
2211 if (TAILQ_EMPTY(&ifb->ifb_port_list)) {
2212 ifb->ifb_altmtu = 0;
2d21ac55
A
2213 ifnet_set_mtu(ifp, 0);
2214 ifnet_set_offload(ifp, 0);
91447636
A
2215 }
2216
2217 signal_done:
91447636
A
2218 ifbond_signal(ifb, "bond_add_interface");
2219 bond_unlock();
2d21ac55 2220 ifbond_release(ifb);
91447636
A
2221 bondport_free(p);
2222 return (error);
2223}
2224
2225static int
2226bond_remove_interface(ifbond_ref ifb, struct ifnet * port_ifp)
2227{
2228 int active_lag = 0;
2229 int error = 0;
2230 int event_code = 0;
2231 bondport_ref head_port;
91447636 2232 struct ifnet * ifp;
2d21ac55
A
2233 int last = FALSE;
2234 int new_link_address = FALSE;
91447636
A
2235 bondport_ref p;
2236 lacp_actor_partner_state s;
2d21ac55 2237 int was_distributing;
91447636
A
2238
2239 bond_assert_lock_held();
2240
2241 ifbond_retain(ifb);
2242 ifbond_wait(ifb, "bond_remove_interface");
2243
2244 p = ifbond_lookup_port(ifb, port_ifp);
2245 if (p == NULL) {
2246 error = ENXIO;
2247 /* it got removed by another thread */
2248 goto signal_done;
2249 }
2250
2251 /* de-select it and remove it from the lists */
2d21ac55 2252 was_distributing = bondport_flags_distributing(p);
91447636 2253 bondport_disable_distributing(p);
2d21ac55
A
2254 if (ifb->ifb_mode == IF_BOND_MODE_LACP) {
2255 bondport_set_selected(p, SelectedState_UNSELECTED);
2256 active_lag = bondport_remove_from_LAG(p);
2257 /* invalidate timers here while holding the bond_lock */
2258 bondport_invalidate_timers(p);
2259
2260 /* announce that we're Individual now */
2261 s = p->po_actor_state;
2262 s = lacp_actor_partner_state_set_individual(s);
2263 s = lacp_actor_partner_state_set_not_collecting(s);
2264 s = lacp_actor_partner_state_set_not_distributing(s);
2265 s = lacp_actor_partner_state_set_out_of_sync(s);
2266 p->po_actor_state = s;
2267 bondport_flags_set_ntt(p);
2268 }
2269
91447636
A
2270 TAILQ_REMOVE(&ifb->ifb_port_list, p, po_port_list);
2271 ifb->ifb_port_count--;
2272
91447636 2273 ifp = ifb->ifb_ifp;
91447636
A
2274 head_port = TAILQ_FIRST(&ifb->ifb_port_list);
2275 if (head_port == NULL) {
2d21ac55 2276 ifnet_set_flags(ifp, 0, IFF_RUNNING);
91447636 2277 if (ifbond_flags_lladdr(ifb) == FALSE) {
2d21ac55 2278 last = TRUE;
91447636 2279 }
2d21ac55
A
2280 ifnet_set_offload(ifp, 0);
2281 ifnet_set_mtu(ifp, 0);
91447636
A
2282 ifb->ifb_altmtu = 0;
2283 } else if (ifbond_flags_lladdr(ifb) == FALSE
39236c6e 2284 && bcmp(&p->po_saved_addr, IF_LLADDR(ifp),
91447636 2285 ETHER_ADDR_LEN) == 0) {
2d21ac55 2286 new_link_address = TRUE;
91447636
A
2287 }
2288 /* check if we need to generate a link status event */
2d21ac55
A
2289 if (ifb->ifb_mode == IF_BOND_MODE_LACP ) {
2290 if (ifbond_selection(ifb) || active_lag) {
2291 event_code = (ifb->ifb_active_lag == NULL)
2292 ? KEV_DL_LINK_OFF
2293 : KEV_DL_LINK_ON;
2294 ifb->ifb_last_link_event = event_code;
2295 }
2296 bondport_transmit_machine(p, LAEventStart,
2297 TRANSMIT_MACHINE_TX_IMMEDIATE);
2298 }
2299 else {
2300 /* are we removing the last distributing interface? */
2301 if (was_distributing && ifb->ifb_distributing_count == 0) {
2302 ifb->ifb_last_link_event = event_code = KEV_DL_LINK_OFF;
2303 }
91447636 2304 }
91447636 2305
2d21ac55 2306 bond_unlock();
91447636 2307
2d21ac55
A
2308 if (last) {
2309 ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_IEEE8023ADLAG);
2310 }
2311 else if (new_link_address) {
91447636
A
2312 struct ifnet * scan_ifp;
2313 bondport_ref scan_port;
2314
2315 /* ifbond_wait() allows port list traversal without holding the lock */
2316
2d21ac55
A
2317 /* this port gave the bond its ethernet address, switch to new one */
2318 ifnet_set_lladdr_and_type(ifp,
2319 &head_port->po_saved_addr, ETHER_ADDR_LEN,
2320 IFT_ETHER);
2321
91447636
A
2322 /* re-program each port with the new link address */
2323 TAILQ_FOREACH(scan_port, &ifb->ifb_port_list, po_port_list) {
2324 scan_ifp = scan_port->po_ifp;
2325
2326 error = if_siflladdr(scan_ifp,
39236c6e 2327 (const struct ether_addr *) IF_LLADDR(ifp));
91447636
A
2328 if (error != 0) {
2329 printf("bond_remove_interface(%s, %s): "
2330 "if_siflladdr (%s) failed %d\n",
2331 ifb->ifb_name, bondport_get_name(p),
2332 bondport_get_name(scan_port), error);
2333 }
2334 }
2335 }
2336
2337 /* restore the port's ethernet address */
2338 error = if_siflladdr(port_ifp, &p->po_saved_addr);
2339 if (error != 0) {
2340 printf("bond_remove_interface(%s, %s): if_siflladdr failed %d\n",
2341 ifb->ifb_name, bondport_get_name(p), error);
2342 }
2343
2344 /* restore the port's MTU */
2345 error = siocsifmtu(port_ifp, p->po_devmtu.ifdm_current);
2346 if (error != 0) {
2347 printf("bond_remove_interface(%s, %s): SIOCSIFMTU %d failed %d\n",
2348 ifb->ifb_name, bondport_get_name(p),
2349 p->po_devmtu.ifdm_current, error);
2350 }
2351
2352 /* remove the bond "protocol" */
2353 bond_detach_protocol(port_ifp);
2354
2355 /* generate link event */
2356 if (event_code != 0) {
2357 interface_link_event(ifp, event_code);
2358 }
2359
2360 bond_lock();
91447636 2361 bondport_free(p);
2d21ac55
A
2362 ifnet_set_eflags(port_ifp, 0, IFEF_BOND);
2363 /* release this bondport's reference to the ifbond */
2364 ifbond_release(ifb);
91447636
A
2365
2366 signal_done:
2367 ifbond_signal(ifb, "bond_remove_interface");
2d21ac55
A
2368 ifbond_release(ifb);
2369 return (error);
2370}
2371
2372static void
2373bond_set_lacp_mode(ifbond_ref ifb)
2374{
2375 bondport_ref p;
2376
2377 TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) {
2378 bondport_disable_distributing(p);
2379 bondport_start(p);
2380 }
2381 return;
2382}
2383
2384static void
2385bond_set_static_mode(ifbond_ref ifb)
2386{
2387 bondport_ref p;
2388 lacp_actor_partner_state s;
2389
2390 TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) {
2391 bondport_disable_distributing(p);
2392 bondport_set_selected(p, SelectedState_UNSELECTED);
2393 (void)bondport_remove_from_LAG(p);
2394 bondport_cancel_timers(p);
2395
2396 /* announce that we're Individual now */
2397 s = p->po_actor_state;
2398 s = lacp_actor_partner_state_set_individual(s);
2399 s = lacp_actor_partner_state_set_not_collecting(s);
2400 s = lacp_actor_partner_state_set_not_distributing(s);
2401 s = lacp_actor_partner_state_set_out_of_sync(s);
2402 p->po_actor_state = s;
2403 bondport_flags_set_ntt(p);
2404 bondport_transmit_machine(p, LAEventStart,
2405 TRANSMIT_MACHINE_TX_IMMEDIATE);
2406 /* clear state */
2407 p->po_actor_state = 0;
2408 bzero(&p->po_partner_state, sizeof(p->po_partner_state));
2409
2410 if (media_active(&p->po_media_info)) {
2411 bondport_enable_distributing(p);
2412 }
2413 else {
2414 bondport_disable_distributing(p);
2415 }
2416 }
2417 return;
2418}
2419
2420static int
2421bond_set_mode(struct ifnet * ifp, int mode)
2422{
2423 int error = 0;
2424 int event_code = 0;
2425 ifbond_ref ifb;
2426
2427 bond_lock();
2428 ifb = (ifbond_ref)ifnet_softc(ifp);
2429 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2430 bond_unlock();
2431 return ((ifb == NULL) ? EOPNOTSUPP : EBUSY);
2432 }
2433 if (ifb->ifb_mode == mode) {
2434 bond_unlock();
2435 return (0);
2436 }
2437
2438 ifbond_retain(ifb);
2439 ifbond_wait(ifb, "bond_set_mode");
2440
2441 /* verify (again) that the mode is actually different */
2442 if (ifb->ifb_mode == mode) {
2443 /* nothing to do */
2444 goto signal_done;
2445 }
2446
2447 ifb->ifb_mode = mode;
2448 if (mode == IF_BOND_MODE_LACP) {
2449 bond_set_lacp_mode(ifb);
2450
2451 /* check if we need to generate a link status event */
2452 if (ifbond_selection(ifb)) {
2453 event_code = (ifb->ifb_active_lag == NULL)
2454 ? KEV_DL_LINK_OFF
2455 : KEV_DL_LINK_ON;
2456 }
2457 } else {
2458 bond_set_static_mode(ifb);
2459 event_code = (ifb->ifb_distributing_count == 0)
2460 ? KEV_DL_LINK_OFF
2461 : KEV_DL_LINK_ON;
2462 }
2463 ifb->ifb_last_link_event = event_code;
2464
2465 signal_done:
2466 ifbond_signal(ifb, "bond_set_mode");
2467 bond_unlock();
2468 ifbond_release(ifb);
2469
2470 if (event_code != 0) {
2471 interface_link_event(ifp, event_code);
2472 }
91447636
A
2473 return (error);
2474}
2475
2476static int
2477bond_get_status(ifbond_ref ifb, struct if_bond_req * ibr_p, user_addr_t datap)
2478{
2479 int count;
2480 user_addr_t dst;
2481 int error = 0;
2482 struct if_bond_status_req * ibsr;
2483 struct if_bond_status ibs;
2484 bondport_ref port;
2485
2486 ibsr = &(ibr_p->ibr_ibru.ibru_status);
2487 if (ibsr->ibsr_version != IF_BOND_STATUS_REQ_VERSION) {
2488 return (EINVAL);
2489 }
2490 ibsr->ibsr_key = ifb->ifb_key;
2d21ac55 2491 ibsr->ibsr_mode = ifb->ifb_mode;
91447636
A
2492 ibsr->ibsr_total = ifb->ifb_port_count;
2493 dst = proc_is64bit(current_proc())
2494 ? ibsr->ibsr_ibsru.ibsru_buffer64
2d21ac55 2495 : CAST_USER_ADDR_T(ibsr->ibsr_ibsru.ibsru_buffer);
91447636
A
2496 if (dst == USER_ADDR_NULL) {
2497 /* just want to know how many there are */
2498 goto done;
2499 }
2500 if (ibsr->ibsr_count < 0) {
2501 return (EINVAL);
2502 }
2503 count = (ifb->ifb_port_count < ibsr->ibsr_count)
2504 ? ifb->ifb_port_count : ibsr->ibsr_count;
2505 TAILQ_FOREACH(port, &ifb->ifb_port_list, po_port_list) {
2506 struct if_bond_partner_state * ibps_p;
2507 partner_state_ref ps;
2508
2509 if (count == 0) {
2510 break;
2511 }
2512 bzero(&ibs, sizeof(ibs));
fe8ab488 2513 strlcpy(ibs.ibs_if_name, port->po_name, sizeof(ibs.ibs_if_name));
91447636 2514 ibs.ibs_port_priority = port->po_priority;
2d21ac55
A
2515 if (ifb->ifb_mode == IF_BOND_MODE_LACP) {
2516 ibs.ibs_state = port->po_actor_state;
2517 ibs.ibs_selected_state = port->po_selected;
2518 ps = &port->po_partner_state;
2519 ibps_p = &ibs.ibs_partner_state;
2520 ibps_p->ibps_system = ps->ps_lag_info.li_system;
2521 ibps_p->ibps_system_priority = ps->ps_lag_info.li_system_priority;
2522 ibps_p->ibps_key = ps->ps_lag_info.li_key;
2523 ibps_p->ibps_port = ps->ps_port;
2524 ibps_p->ibps_port_priority = ps->ps_port_priority;
2525 ibps_p->ibps_state = ps->ps_state;
2526 }
2527 else {
2528 /* fake the selected information */
2529 ibs.ibs_selected_state = bondport_flags_distributing(port)
2530 ? SelectedState_SELECTED : SelectedState_UNSELECTED;
2531 }
91447636
A
2532 error = copyout(&ibs, dst, sizeof(ibs));
2533 if (error != 0) {
2534 break;
2535 }
2536 dst += sizeof(ibs);
2537 count--;
2538 }
2539
2540 done:
2541 if (error == 0) {
2542 error = copyout(ibr_p, datap, sizeof(*ibr_p));
2543 }
2544 else {
2545 (void)copyout(ibr_p, datap, sizeof(*ibr_p));
2546 }
2547 return (error);
2548}
2549
2550static int
2551bond_set_promisc(__unused struct ifnet *ifp)
2552{
2553 int error = 0;
91447636
A
2554 return (error);
2555}
2556
2557static void
2558bond_get_mtu_values(ifbond_ref ifb, int * ret_min, int * ret_max)
2559{
2560 int mtu_min = 0;
2561 int mtu_max = 0;
2562 bondport_ref p;
2563
2564 if (TAILQ_FIRST(&ifb->ifb_port_list) != NULL) {
2565 mtu_min = IF_MINMTU;
2566 }
2567 TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) {
2568 struct ifdevmtu * devmtu_p = &p->po_devmtu;
2569
2570 if (devmtu_p->ifdm_min > mtu_min) {
2571 mtu_min = devmtu_p->ifdm_min;
2572 }
2573 if (mtu_max == 0 || devmtu_p->ifdm_max < mtu_max) {
2574 mtu_max = devmtu_p->ifdm_max;
2575 }
2576 }
2577 *ret_min = mtu_min;
2578 *ret_max = mtu_max;
2579 return;
2580}
2581
2582static int
2583bond_set_mtu_on_ports(ifbond_ref ifb, int mtu)
2584{
2585 int error = 0;
2586 bondport_ref p;
2587
2588 TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) {
2589 error = siocsifmtu(p->po_ifp, mtu);
2590 if (error != 0) {
2591 printf("if_bond(%s): SIOCSIFMTU %s failed, %d\n",
2592 ifb->ifb_name, bondport_get_name(p), error);
2593 break;
2594 }
2595 }
2596 return (error);
2597}
2598
2599static int
2600bond_set_mtu(struct ifnet * ifp, int mtu, int isdevmtu)
2601{
2602 int error = 0;
2603 ifbond_ref ifb;
2604 int mtu_min;
2605 int mtu_max;
2606 int new_max;
2607 int old_max;
2608
2609 bond_lock();
2d21ac55 2610 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2611 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2612 error = (ifb == NULL) ? EOPNOTSUPP : EBUSY;
2613 goto done;
2614 }
2615 ifbond_retain(ifb);
2616 ifbond_wait(ifb, "bond_set_mtu");
2617
2618 /* check again */
2d21ac55 2619 if (ifnet_softc(ifp) == NULL || ifbond_flags_if_detaching(ifb)) {
91447636
A
2620 error = EBUSY;
2621 goto signal_done;
2622 }
2623 bond_get_mtu_values(ifb, &mtu_min, &mtu_max);
2624 if (mtu > mtu_max) {
2625 error = EINVAL;
2626 goto signal_done;
2627 }
2628 if (mtu < mtu_min && (isdevmtu == 0 || mtu != 0)) {
2629 /* allow SIOCSIFALTMTU to set the mtu to 0 */
2630 error = EINVAL;
2631 goto signal_done;
2632 }
2633 if (isdevmtu) {
2d21ac55 2634 new_max = (mtu > (int)ifnet_mtu(ifp)) ? mtu : (int)ifnet_mtu(ifp);
91447636
A
2635 }
2636 else {
2637 new_max = (mtu > ifb->ifb_altmtu) ? mtu : ifb->ifb_altmtu;
2638 }
2d21ac55
A
2639 old_max = ((int)ifnet_mtu(ifp) > ifb->ifb_altmtu)
2640 ? (int)ifnet_mtu(ifp) : ifb->ifb_altmtu;
91447636
A
2641 if (new_max != old_max) {
2642 /* we can safely walk the list of port without the lock held */
2643 bond_unlock();
2644 error = bond_set_mtu_on_ports(ifb, new_max);
2645 if (error != 0) {
2646 /* try our best to back out of it */
2647 (void)bond_set_mtu_on_ports(ifb, old_max);
2648 }
2649 bond_lock();
2650 }
2651 if (error == 0) {
2652 if (isdevmtu) {
2653 ifb->ifb_altmtu = mtu;
2654 }
2655 else {
2d21ac55 2656 ifnet_set_mtu(ifp, mtu);
91447636
A
2657 }
2658 }
2659
2660 signal_done:
2661 ifbond_signal(ifb, "bond_set_mtu");
2662 ifbond_release(ifb);
2663
2664 done:
2665 bond_unlock();
2666 return (error);
2667}
2668
2669static int
b0d623f7 2670bond_ioctl(struct ifnet *ifp, u_long cmd, void * data)
91447636
A
2671{
2672 int error = 0;
2673 struct if_bond_req ibr;
2674 struct ifaddr * ifa;
2675 ifbond_ref ifb;
2676 struct ifreq * ifr;
b0d623f7 2677 struct ifmediareq *ifmr;
91447636
A
2678 struct ifnet * port_ifp = NULL;
2679 user_addr_t user_addr;
2680
2d21ac55 2681 if (ifnet_type(ifp) != IFT_IEEE8023ADLAG) {
91447636
A
2682 return (EOPNOTSUPP);
2683 }
2684 ifr = (struct ifreq *)data;
2685 ifa = (struct ifaddr *)data;
2686
2687 switch (cmd) {
2688 case SIOCSIFADDR:
2689 ifnet_set_flags(ifp, IFF_UP, IFF_UP);
2690 break;
2691
b0d623f7 2692 case SIOCGIFMEDIA32:
91447636 2693 case SIOCGIFMEDIA64:
91447636 2694 bond_lock();
2d21ac55 2695 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2696 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2697 bond_unlock();
2698 return (ifb == NULL ? EOPNOTSUPP : EBUSY);
2699 }
b0d623f7 2700 ifmr = (struct ifmediareq *)data;
91447636
A
2701 ifmr->ifm_current = IFM_ETHER;
2702 ifmr->ifm_mask = 0;
2703 ifmr->ifm_status = IFM_AVALID;
2704 ifmr->ifm_active = IFM_ETHER;
2705 ifmr->ifm_count = 1;
2d21ac55
A
2706 if (ifb->ifb_mode == IF_BOND_MODE_LACP) {
2707 if (ifb->ifb_active_lag != NULL) {
2708 ifmr->ifm_active = ifb->ifb_active_lag->lag_active_media;
2709 ifmr->ifm_status |= IFM_ACTIVE;
2710 }
2711 }
2712 else if (ifb->ifb_distributing_count > 0) {
2713 ifmr->ifm_active
2714 = ifb->ifb_distributing_array[0]->po_media_info.mi_active;
91447636
A
2715 ifmr->ifm_status |= IFM_ACTIVE;
2716 }
2717 bond_unlock();
b0d623f7
A
2718 user_addr = (cmd == SIOCGIFMEDIA64) ?
2719 ((struct ifmediareq64 *)ifmr)->ifmu_ulist :
2720 CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
91447636
A
2721 if (user_addr != USER_ADDR_NULL) {
2722 error = copyout(&ifmr->ifm_current,
2723 user_addr,
2724 sizeof(int));
2725 }
2726 break;
2727
2728 case SIOCSIFMEDIA:
2729 /* XXX send the SIFMEDIA to all children? Or force autoselect? */
2730 error = EINVAL;
2731 break;
2732
2733 case SIOCGIFDEVMTU:
2734 bond_lock();
2d21ac55 2735 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2736 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2737 bond_unlock();
2738 error = (ifb == NULL) ? EOPNOTSUPP : EBUSY;
2739 break;
2740 }
2741 ifr->ifr_devmtu.ifdm_current = bond_device_mtu(ifp, ifb);
2742 bond_get_mtu_values(ifb, &ifr->ifr_devmtu.ifdm_min,
2743 &ifr->ifr_devmtu.ifdm_max);
2744 bond_unlock();
2745 break;
2746
2747 case SIOCGIFALTMTU:
2748 bond_lock();
2d21ac55 2749 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2750 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2751 bond_unlock();
2752 error = (ifb == NULL) ? EOPNOTSUPP : EBUSY;
2753 break;
2754 }
2755 ifr->ifr_mtu = ifb->ifb_altmtu;
2756 bond_unlock();
2757 break;
2758
2759 case SIOCSIFALTMTU:
2760 error = bond_set_mtu(ifp, ifr->ifr_mtu, 1);
2761 break;
2762
2763 case SIOCSIFMTU:
2764 error = bond_set_mtu(ifp, ifr->ifr_mtu, 0);
2765 break;
2766
2767 case SIOCSIFBOND:
2768 user_addr = proc_is64bit(current_proc())
2769 ? ifr->ifr_data64 : CAST_USER_ADDR_T(ifr->ifr_data);
2770 error = copyin(user_addr, &ibr, sizeof(ibr));
2771 if (error) {
2772 break;
2773 }
2774 switch (ibr.ibr_op) {
2775 case IF_BOND_OP_ADD_INTERFACE:
2776 case IF_BOND_OP_REMOVE_INTERFACE:
91447636
A
2777 port_ifp = ifunit(ibr.ibr_ibru.ibru_if_name);
2778 if (port_ifp == NULL) {
2779 error = ENXIO;
2780 break;
2781 }
2d21ac55 2782 if (ifnet_type(port_ifp) != IFT_ETHER) {
91447636
A
2783 error = EPROTONOSUPPORT;
2784 break;
2785 }
2786 break;
2787 case IF_BOND_OP_SET_VERBOSE:
2d21ac55 2788 case IF_BOND_OP_SET_MODE:
91447636
A
2789 break;
2790 default:
2791 error = EOPNOTSUPP;
2792 break;
2793 }
2794 if (error != 0) {
2795 break;
2796 }
2797 switch (ibr.ibr_op) {
2798 case IF_BOND_OP_ADD_INTERFACE:
2799 error = bond_add_interface(ifp, port_ifp);
2800 break;
2801 case IF_BOND_OP_REMOVE_INTERFACE:
2802 bond_lock();
2d21ac55 2803 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2804 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2805 bond_unlock();
2806 return (ifb == NULL ? EOPNOTSUPP : EBUSY);
2807 }
2808 error = bond_remove_interface(ifb, port_ifp);
2809 bond_unlock();
2810 break;
2811 case IF_BOND_OP_SET_VERBOSE:
2812 bond_lock();
2813 if (g_bond == NULL) {
2814 bond_unlock();
2815 error = ENXIO;
2816 break;
2817 }
2818 g_bond->verbose = ibr.ibr_ibru.ibru_int_val;
2819 bond_unlock();
2820 break;
2d21ac55
A
2821 case IF_BOND_OP_SET_MODE:
2822 switch (ibr.ibr_ibru.ibru_int_val) {
2823 case IF_BOND_MODE_LACP:
2824 case IF_BOND_MODE_STATIC:
2825 break;
2826 default:
2827 error = EINVAL;
2828 break;
2829 }
2830 if (error != 0) {
2831 break;
2832 }
2833 error = bond_set_mode(ifp, ibr.ibr_ibru.ibru_int_val);
2834 break;
91447636 2835 }
2d21ac55 2836 break; /* SIOCSIFBOND */
91447636
A
2837
2838 case SIOCGIFBOND:
2839 user_addr = proc_is64bit(current_proc())
2840 ? ifr->ifr_data64 : CAST_USER_ADDR_T(ifr->ifr_data);
2841 error = copyin(user_addr, &ibr, sizeof(ibr));
2842 if (error) {
2843 break;
2844 }
2845 switch (ibr.ibr_op) {
2846 case IF_BOND_OP_GET_STATUS:
2847 break;
2848 default:
2849 error = EOPNOTSUPP;
2850 break;
2851 }
2852 if (error != 0) {
2853 break;
2854 }
2855 bond_lock();
2d21ac55 2856 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2857 if (ifb == NULL || ifbond_flags_if_detaching(ifb)) {
2858 bond_unlock();
2859 return (ifb == NULL ? EOPNOTSUPP : EBUSY);
2860 }
2861 switch (ibr.ibr_op) {
2862 case IF_BOND_OP_GET_STATUS:
2863 error = bond_get_status(ifb, &ibr, user_addr);
2864 break;
2865 }
2866 bond_unlock();
2d21ac55 2867 break; /* SIOCGIFBOND */
91447636
A
2868
2869 case SIOCSIFLLADDR:
2870 error = EOPNOTSUPP;
2871 break;
2872
2873 case SIOCSIFFLAGS:
2874 /* enable/disable promiscuous mode */
2875 bond_lock();
2876 error = bond_set_promisc(ifp);
2877 bond_unlock();
2878 break;
2879
2880 case SIOCADDMULTI:
2881 case SIOCDELMULTI:
2882 error = bond_setmulti(ifp);
2883 break;
2884 default:
2885 error = EOPNOTSUPP;
2886 }
2887 return error;
2888}
2889
2890static void
2891bond_if_free(struct ifnet * ifp)
2892{
2893 ifbond_ref ifb;
2894
2895 if (ifp == NULL) {
2896 return;
2897 }
2898 bond_lock();
2d21ac55 2899 ifb = (ifbond_ref)ifnet_softc(ifp);
91447636
A
2900 if (ifb == NULL) {
2901 bond_unlock();
2902 return;
2903 }
91447636
A
2904 ifbond_release(ifb);
2905 bond_unlock();
2d21ac55 2906 ifnet_release(ifp);
91447636
A
2907 return;
2908}
2909
2910static void
6d2010ae 2911bond_handle_event(struct ifnet * port_ifp, int event_code)
91447636
A
2912{
2913 struct ifnet * bond_ifp = NULL;
2d21ac55
A
2914 ifbond_ref ifb;
2915 int old_distributing_count;
91447636 2916 bondport_ref p;
2d21ac55 2917 struct media_info media_info = { 0, 0};
91447636 2918
6d2010ae
A
2919 switch (event_code) {
2920 case KEV_DL_IF_DETACHED:
91447636
A
2921 break;
2922 case KEV_DL_LINK_OFF:
2923 case KEV_DL_LINK_ON:
2924 media_info = interface_media_info(port_ifp);
2925 break;
2926 default:
2927 return;
2928 }
2929 bond_lock();
2930 p = bond_lookup_port(port_ifp);
2931 if (p == NULL) {
2932 bond_unlock();
2933 return;
2934 }
2d21ac55
A
2935 ifb = p->po_bond;
2936 old_distributing_count = ifb->ifb_distributing_count;
6d2010ae
A
2937 switch (event_code) {
2938 case KEV_DL_IF_DETACHED:
2d21ac55 2939 bond_remove_interface(ifb, p->po_ifp);
91447636
A
2940 break;
2941 case KEV_DL_LINK_OFF:
2942 case KEV_DL_LINK_ON:
2943 p->po_media_info = media_info;
2944 if (p->po_enabled) {
2945 bondport_link_status_changed(p);
2946 }
2947 break;
2948 }
2949 /* generate a link-event */
2d21ac55
A
2950 if (ifb->ifb_mode == IF_BOND_MODE_LACP) {
2951 if (ifbond_selection(ifb)) {
2952 event_code = (ifb->ifb_active_lag == NULL)
2953 ? KEV_DL_LINK_OFF
2954 : KEV_DL_LINK_ON;
2955 /* XXX need to take a reference on bond_ifp */
2956 bond_ifp = ifb->ifb_ifp;
2957 ifb->ifb_last_link_event = event_code;
2958 }
2959 else {
2960 event_code = (ifb->ifb_active_lag == NULL)
2961 ? KEV_DL_LINK_OFF
2962 : KEV_DL_LINK_ON;
2963 if (event_code != ifb->ifb_last_link_event) {
2964 if (g_bond->verbose) {
2965 timestamp_printf("%s: (event) generating LINK event\n",
2966 ifb->ifb_name);
2967 }
2968 bond_ifp = ifb->ifb_ifp;
2969 ifb->ifb_last_link_event = event_code;
2970 }
2971 }
91447636 2972 }
2d21ac55
A
2973 else {
2974 /*
2975 * if the distributing array membership changed from 0 <-> !0
2976 * generate a link event
2977 */
2978 if (old_distributing_count == 0
2979 && ifb->ifb_distributing_count != 0) {
2980 event_code = KEV_DL_LINK_ON;
2981 }
2982 else if (old_distributing_count != 0
2983 && ifb->ifb_distributing_count == 0) {
2984 event_code = KEV_DL_LINK_OFF;
2985 }
2986 if (event_code != 0 && event_code != ifb->ifb_last_link_event) {
2987 bond_ifp = ifb->ifb_ifp;
2988 ifb->ifb_last_link_event = event_code;
2989 }
2990 }
2991
91447636
A
2992 bond_unlock();
2993 if (bond_ifp != NULL) {
2994 interface_link_event(bond_ifp, event_code);
2995 }
2996 return;
2997}
2998
6d2010ae
A
2999static void
3000bond_event(struct ifnet * port_ifp, __unused protocol_family_t protocol,
3001 const struct kev_msg * event)
3002{
3003 int event_code;
3004
3005 if (event->vendor_code != KEV_VENDOR_APPLE
3006 || event->kev_class != KEV_NETWORK_CLASS
3007 || event->kev_subclass != KEV_DL_SUBCLASS) {
3008 return;
3009 }
3010 event_code = event->event_code;
3011 switch (event_code) {
3012 case KEV_DL_LINK_OFF:
3013 case KEV_DL_LINK_ON:
3014 /* we only care about link status changes */
3015 bond_handle_event(port_ifp, event_code);
3016 break;
3017 default:
3018 break;
3019 }
3020 return;
3021}
3022
3023static errno_t
3024bond_detached(ifnet_t port_ifp, __unused protocol_family_t protocol)
3025{
3026 bond_handle_event(port_ifp, KEV_DL_IF_DETACHED);
3027 return (0);
3028}
3029
91447636 3030static void
b0d623f7 3031interface_link_event(struct ifnet * ifp, u_int32_t event_code)
91447636
A
3032{
3033 struct {
3034 struct kern_event_msg header;
b0d623f7 3035 u_int32_t unit;
91447636
A
3036 char if_name[IFNAMSIZ];
3037 } event;
3038
6d2010ae 3039 bzero(&event, sizeof(event));
91447636
A
3040 event.header.total_size = sizeof(event);
3041 event.header.vendor_code = KEV_VENDOR_APPLE;
3042 event.header.kev_class = KEV_NETWORK_CLASS;
3043 event.header.kev_subclass = KEV_DL_SUBCLASS;
3044 event.header.event_code = event_code;
2d21ac55 3045 event.header.event_data[0] = ifnet_family(ifp);
b0d623f7 3046 event.unit = (u_int32_t) ifnet_unit(ifp);
fe8ab488 3047 strlcpy(event.if_name, ifnet_name(ifp), IFNAMSIZ);
2d21ac55 3048 ifnet_event(ifp, &event.header);
91447636
A
3049 return;
3050}
3051
3052/*
3053 * Function: bond_attach_protocol
3054 * Purpose:
3055 * Attach a DLIL protocol to the interface.
3056 *
3057 * The ethernet demux special cases to always return PF_BOND if the
3058 * interface is bonded. That means we receive all traffic from that
3059 * interface without passing any of the traffic to any other attached
3060 * protocol.
3061 */
3062static int
3063bond_attach_protocol(struct ifnet *ifp)
3064{
2d21ac55
A
3065 int error;
3066 struct ifnet_attach_proto_param reg;
91447636
A
3067
3068 bzero(&reg, sizeof(reg));
91447636
A
3069 reg.input = bond_input;
3070 reg.event = bond_event;
6d2010ae 3071 reg.detached = bond_detached;
91447636 3072
2d21ac55 3073 error = ifnet_attach_protocol(ifp, PF_BOND, &reg);
91447636 3074 if (error) {
2d21ac55
A
3075 printf("bond over %s%d: ifnet_attach_protocol failed, %d\n",
3076 ifnet_name(ifp), ifnet_unit(ifp), error);
91447636
A
3077 }
3078 return (error);
3079}
3080
3081/*
3082 * Function: bond_detach_protocol
3083 * Purpose:
3084 * Detach our DLIL protocol from an interface
3085 */
3086static int
3087bond_detach_protocol(struct ifnet *ifp)
3088{
3089 int error;
3090
2d21ac55 3091 error = ifnet_detach_protocol(ifp, PF_BOND);
91447636 3092 if (error) {
2d21ac55
A
3093 printf("bond over %s%d: ifnet_detach_protocol failed, %d\n",
3094 ifnet_name(ifp), ifnet_unit(ifp), error);
91447636
A
3095 }
3096 return (error);
3097}
3098
3099/*
3100 * DLIL interface family functions
3101 */
2d21ac55
A
3102extern int ether_attach_inet(ifnet_t ifp, protocol_family_t protocol_family);
3103extern void ether_detach_inet(ifnet_t ifp, protocol_family_t protocol_family);
3104extern int ether_attach_inet6(ifnet_t ifp, protocol_family_t protocol_family);
3105extern void ether_detach_inet6(ifnet_t ifp, protocol_family_t protocol_family);
3106extern int ether_attach_at(ifnet_t ifp, protocol_family_t protocol_family);
3107extern void ether_detach_at(ifnet_t ifp, protocol_family_t protocol_family);
91447636
A
3108
3109__private_extern__ int
3110bond_family_init(void)
3111{
3112 int error=0;
91447636 3113
2d21ac55 3114 error = proto_register_plumber(PF_INET, APPLE_IF_FAM_BOND,
91447636
A
3115 ether_attach_inet,
3116 ether_detach_inet);
3117 if (error != 0) {
2d21ac55 3118 printf("bond: proto_register_plumber failed for AF_INET error=%d\n",
91447636
A
3119 error);
3120 goto done;
3121 }
2d21ac55
A
3122#if INET6
3123 error = proto_register_plumber(PF_INET6, APPLE_IF_FAM_BOND,
91447636
A
3124 ether_attach_inet6,
3125 ether_detach_inet6);
3126 if (error != 0) {
2d21ac55
A
3127 printf("bond: proto_register_plumber failed for AF_INET6 error=%d\n",
3128 error);
3129 goto done;
3130 }
b0d623f7
A
3131#endif
3132 error = bond_clone_attach();
3133 if (error != 0) {
3134 printf("bond: proto_register_plumber failed bond_clone_attach error=%d\n",
3135 error);
3136 goto done;
3137 }
91447636
A
3138
3139 done:
3140 return (error);
3141}
3142/**
3143 **
3144 ** LACP routines:
3145 **
3146 **/
3147
3148/**
3149 ** LACP ifbond_list routines
3150 **/
3151static bondport_ref
3152ifbond_list_find_moved_port(bondport_ref rx_port,
3153 const lacp_actor_partner_tlv_ref atlv)
3154{
3155 ifbond_ref bond;
3156 bondport_ref p;
3157 partner_state_ref ps;
3158 LAG_info_ref ps_li;
3159
3160 TAILQ_FOREACH(bond, &g_bond->ifbond_list, ifb_bond_list) {
3161 TAILQ_FOREACH(p, &bond->ifb_port_list, po_port_list) {
3162
3163 if (rx_port == p) {
3164 /* no point in comparing against ourselves */
3165 continue;
3166 }
3167 if (p->po_receive_state != ReceiveState_PORT_DISABLED) {
3168 /* it's not clear that we should be checking this */
3169 continue;
3170 }
3171 ps = &p->po_partner_state;
3172 if (lacp_actor_partner_state_defaulted(ps->ps_state)) {
3173 continue;
3174 }
3175 ps_li = &ps->ps_lag_info;
3176 if (ps->ps_port == lacp_actor_partner_tlv_get_port(atlv)
3177 && bcmp(&ps_li->li_system, atlv->lap_system,
3178 sizeof(ps_li->li_system)) == 0) {
3179 if (g_bond->verbose) {
3180 timestamp_printf("System " EA_FORMAT
3181 " Port 0x%x moved from %s to %s\n",
3182 EA_LIST(&ps_li->li_system), ps->ps_port,
3183 bondport_get_name(p),
3184 bondport_get_name(rx_port));
3185 }
3186 return (p);
3187 }
3188 }
3189 }
3190 return (NULL);
3191}
3192
3193/**
3194 ** LACP ifbond, LAG routines
3195 **/
3196
3197static int
3198ifbond_selection(ifbond_ref bond)
3199{
3200 int all_ports_ready = 0;
3201 int active_media = 0;
3202 LAG_ref lag = NULL;
3203 int lag_changed = 0;
3204 bondport_ref p;
3205 int port_speed = 0;
3206
3207 lag = ifbond_find_best_LAG(bond, &active_media);
3208 if (lag != bond->ifb_active_lag) {
3209 if (bond->ifb_active_lag != NULL) {
3210 ifbond_deactivate_LAG(bond, bond->ifb_active_lag);
3211 bond->ifb_active_lag = NULL;
3212 }
3213 bond->ifb_active_lag = lag;
3214 if (lag != NULL) {
3215 ifbond_activate_LAG(bond, lag, active_media);
3216 }
3217 lag_changed = 1;
3218 }
3219 else if (lag != NULL) {
3220 if (lag->lag_active_media != active_media) {
3221 if (g_bond->verbose) {
3222 timestamp_printf("LAG PORT SPEED CHANGED from %d to %d\n",
3223 link_speed(lag->lag_active_media),
3224 link_speed(active_media));
3225 }
3226 ifbond_deactivate_LAG(bond, lag);
3227 ifbond_activate_LAG(bond, lag, active_media);
3228 lag_changed = 1;
3229 }
3230 }
3231 if (lag != NULL) {
3232 port_speed = link_speed(active_media);
3233 all_ports_ready = ifbond_all_ports_ready(bond);
3234 }
3235 TAILQ_FOREACH(p, &bond->ifb_port_list, po_port_list) {
3236 if (lag != NULL && p->po_lag == lag
3237 && media_speed(&p->po_media_info) == port_speed
3238 && (p->po_mux_state == MuxState_DETACHED
3239 || p->po_selected == SelectedState_SELECTED
3240 || p->po_selected == SelectedState_STANDBY)
3241 && bondport_aggregatable(p)) {
3242 if (bond->ifb_max_active > 0) {
3243 if (lag->lag_selected_port_count < bond->ifb_max_active) {
3244 if (p->po_selected == SelectedState_STANDBY
3245 || p->po_selected == SelectedState_UNSELECTED) {
3246 bondport_set_selected(p, SelectedState_SELECTED);
3247 }
3248 }
3249 else if (p->po_selected == SelectedState_UNSELECTED) {
3250 bondport_set_selected(p, SelectedState_STANDBY);
3251 }
3252 }
3253 else {
3254 bondport_set_selected(p, SelectedState_SELECTED);
3255 }
3256 }
3257 if (bondport_flags_selected_changed(p)) {
3258 bondport_flags_clear_selected_changed(p);
3259 bondport_mux_machine(p, LAEventSelectedChange, NULL);
3260 }
3261 if (all_ports_ready
3262 && bondport_flags_ready(p)
3263 && p->po_mux_state == MuxState_WAITING) {
3264 bondport_mux_machine(p, LAEventReady, NULL);
3265 }
3266 bondport_transmit_machine(p, LAEventStart, NULL);
3267 }
3268 return (lag_changed);
3269}
3270
3271static LAG_ref
3272ifbond_find_best_LAG(ifbond_ref bond, int * active_media)
3273{
3274 int best_active = 0;
3275 LAG_ref best_lag = NULL;
3276 int best_count = 0;
3277 int best_speed = 0;
3278 LAG_ref lag;
3279
3280 if (bond->ifb_active_lag != NULL) {
3281 best_lag = bond->ifb_active_lag;
3282 best_count = LAG_get_aggregatable_port_count(best_lag, &best_active);
3283 if (bond->ifb_max_active > 0
3284 && best_count > bond->ifb_max_active) {
3285 best_count = bond->ifb_max_active;
3286 }
3287 best_speed = link_speed(best_active);
3288 }
3289 TAILQ_FOREACH(lag, &bond->ifb_lag_list, lag_list) {
3290 int active;
3291 int count;
3292 int speed;
3293
3294 if (lag == bond->ifb_active_lag) {
3295 /* we've already computed it */
3296 continue;
3297 }
3298 count = LAG_get_aggregatable_port_count(lag, &active);
3299 if (count == 0) {
3300 continue;
3301 }
3302 if (bond->ifb_max_active > 0
3303 && count > bond->ifb_max_active) {
3304 /* if there's a limit, don't count extra links */
3305 count = bond->ifb_max_active;
3306 }
3307 speed = link_speed(active);
3308 if ((count * speed) > (best_count * best_speed)) {
3309 best_count = count;
3310 best_speed = speed;
3311 best_active = active;
3312 best_lag = lag;
3313 }
3314 }
3315 if (best_count == 0) {
3316 return (NULL);
3317 }
3318 *active_media = best_active;
3319 return (best_lag);
3320}
3321
3322static void
3323ifbond_deactivate_LAG(__unused ifbond_ref bond, LAG_ref lag)
3324{
3325 bondport_ref p;
3326
3327 TAILQ_FOREACH(p, &lag->lag_port_list, po_lag_port_list) {
3328 bondport_set_selected(p, SelectedState_UNSELECTED);
3329 }
3330 return;
3331}
3332
3333static void
3334ifbond_activate_LAG(ifbond_ref bond, LAG_ref lag, int active_media)
3335{
3336 int need = 0;
3337 bondport_ref p;
3338
3339 if (bond->ifb_max_active > 0) {
3340 need = bond->ifb_max_active;
3341 }
3342 lag->lag_active_media = active_media;
3343 TAILQ_FOREACH(p, &lag->lag_port_list, po_lag_port_list) {
3344 if (bondport_aggregatable(p) == 0) {
3345 bondport_set_selected(p, SelectedState_UNSELECTED);
3346 }
3347 else if (media_speed(&p->po_media_info) != link_speed(active_media)) {
3348 bondport_set_selected(p, SelectedState_UNSELECTED);
3349 }
3350 else if (p->po_mux_state == MuxState_DETACHED) {
3351 if (bond->ifb_max_active > 0) {
3352 if (need > 0) {
3353 bondport_set_selected(p, SelectedState_SELECTED);
3354 need--;
3355 }
3356 else {
3357 bondport_set_selected(p, SelectedState_STANDBY);
3358 }
3359 }
3360 else {
3361 bondport_set_selected(p, SelectedState_SELECTED);
3362 }
3363 }
3364 else {
3365 bondport_set_selected(p, SelectedState_UNSELECTED);
3366 }
3367 }
3368 return;
3369}
3370
3371#if 0
3372static void
3373ifbond_set_max_active(ifbond_ref bond, int max_active)
3374{
3375 LAG_ref lag = bond->ifb_active_lag;
3376
3377 bond->ifb_max_active = max_active;
3378 if (bond->ifb_max_active <= 0 || lag == NULL) {
3379 return;
3380 }
3381 if (lag->lag_selected_port_count > bond->ifb_max_active) {
3382 bondport_ref p;
3383 int remove_count;
3384
3385 remove_count = lag->lag_selected_port_count - bond->ifb_max_active;
3386 TAILQ_FOREACH(p, &lag->lag_port_list, po_lag_port_list) {
3387 if (p->po_selected == SelectedState_SELECTED) {
3388 bondport_set_selected(p, SelectedState_UNSELECTED);
3389 remove_count--;
3390 if (remove_count == 0) {
3391 break;
3392 }
3393 }
3394 }
3395 }
3396 return;
3397}
b0d623f7 3398#endif
91447636
A
3399
3400static int
3401ifbond_all_ports_ready(ifbond_ref bond)
3402{
3403 int ready = 0;
3404 bondport_ref p;
3405
3406 if (bond->ifb_active_lag == NULL) {
3407 return (0);
3408 }
3409 TAILQ_FOREACH(p, &bond->ifb_active_lag->lag_port_list, po_lag_port_list) {
3410 if (p->po_mux_state == MuxState_WAITING
3411 && p->po_selected == SelectedState_SELECTED) {
3412 if (bondport_flags_ready(p) == 0) {
3413 return (0);
3414 }
3415 }
3416 /* note that there was at least one ready port */
3417 ready = 1;
3418 }
3419 return (ready);
3420}
3421
3422static int
3423ifbond_all_ports_attached(ifbond_ref bond, bondport_ref this_port)
3424{
3425 bondport_ref p;
3426
3427 TAILQ_FOREACH(p, &bond->ifb_port_list, po_port_list) {
3428 if (this_port == p) {
3429 continue;
3430 }
3431 if (bondport_flags_mux_attached(p) == 0) {
3432 return (0);
3433 }
3434 }
3435 return (1);
3436}
3437
3438static LAG_ref
3439ifbond_get_LAG_matching_port(ifbond_ref bond, bondport_ref p)
3440{
3441 LAG_ref lag;
3442
3443 TAILQ_FOREACH(lag, &bond->ifb_lag_list, lag_list) {
3444 if (bcmp(&lag->lag_info, &p->po_partner_state.ps_lag_info,
3445 sizeof(lag->lag_info)) == 0) {
3446 return (lag);
3447 }
3448 }
3449 return (NULL);
3450}
3451
3452static int
3453LAG_get_aggregatable_port_count(LAG_ref lag, int * active_media)
3454{
3455 int active;
3456 int count;
3457 bondport_ref p;
3458 int speed;
3459
3460 active = 0;
3461 count = 0;
3462 speed = 0;
3463 TAILQ_FOREACH(p, &lag->lag_port_list, po_lag_port_list) {
3464 if (bondport_aggregatable(p)) {
3465 int this_speed;
3466
3467 this_speed = media_speed(&p->po_media_info);
3468 if (this_speed == 0) {
3469 continue;
3470 }
3471 if (this_speed > speed) {
3472 active = p->po_media_info.mi_active;
3473 speed = this_speed;
3474 count = 1;
3475 }
3476 else if (this_speed == speed) {
3477 count++;
3478 }
3479 }
3480 }
3481 *active_media = active;
3482 return (count);
3483}
3484
3485
3486/**
3487 ** LACP bondport routines
3488 **/
3489static void
3490bondport_link_status_changed(bondport_ref p)
3491{
3492 ifbond_ref bond = p->po_bond;
3493
3494 if (g_bond->verbose) {
3495 if (media_active(&p->po_media_info)) {
3496 timestamp_printf("[%s] Link UP %d Mbit/s %s duplex\n",
3497 bondport_get_name(p),
3498 media_speed(&p->po_media_info),
3499 media_full_duplex(&p->po_media_info)
3500 ? "full" : "half");
3501 }
3502 else {
3503 timestamp_printf("[%s] Link DOWN\n", bondport_get_name(p));
3504 }
3505 }
2d21ac55
A
3506 if (bond->ifb_mode == IF_BOND_MODE_LACP) {
3507 if (media_active(&p->po_media_info)
3508 && bond->ifb_active_lag != NULL
3509 && p->po_lag == bond->ifb_active_lag
3510 && p->po_selected != SelectedState_UNSELECTED) {
3511 if (media_speed(&p->po_media_info) != p->po_lag->lag_active_media) {
3512 if (g_bond->verbose) {
3513 timestamp_printf("[%s] Port speed %d differs from LAG %d\n",
3514 bondport_get_name(p),
3515 media_speed(&p->po_media_info),
3516 link_speed(p->po_lag->lag_active_media));
3517 }
3518 bondport_set_selected(p, SelectedState_UNSELECTED);
91447636 3519 }
2d21ac55
A
3520 }
3521 bondport_receive_machine(p, LAEventMediaChange, NULL);
3522 bondport_mux_machine(p, LAEventMediaChange, NULL);
3523 bondport_periodic_transmit_machine(p, LAEventMediaChange, NULL);
3524 }
3525 else {
3526 if (media_active(&p->po_media_info)) {
3527 bondport_enable_distributing(p);
3528 }
3529 else {
3530 bondport_disable_distributing(p);
91447636
A
3531 }
3532 }
91447636
A
3533 return;
3534}
3535
3536static int
3537bondport_aggregatable(bondport_ref p)
3538{
3539 partner_state_ref ps = &p->po_partner_state;
3540
3541 if (lacp_actor_partner_state_aggregatable(p->po_actor_state) == 0
3542 || lacp_actor_partner_state_aggregatable(ps->ps_state) == 0) {
3543 /* we and/or our partner are individual */
3544 return (0);
3545 }
3546 if (p->po_lag == NULL) {
3547 return (0);
3548 }
3549 switch (p->po_receive_state) {
3550 default:
3551 if (g_bond->verbose) {
3552 timestamp_printf("[%s] Port is not selectable\n",
3553 bondport_get_name(p));
3554 }
3555 return (0);
3556 case ReceiveState_CURRENT:
3557 case ReceiveState_EXPIRED:
3558 break;
3559 }
3560 return (1);
3561}
3562
3563static int
3564bondport_matches_LAG(bondport_ref p, LAG_ref lag)
3565{
3566 LAG_info_ref lag_li;
3567 partner_state_ref ps;
3568 LAG_info_ref ps_li;
3569
3570 ps = &p->po_partner_state;
3571 ps_li = &ps->ps_lag_info;
3572 lag_li = &lag->lag_info;
3573 if (ps_li->li_system_priority == lag_li->li_system_priority
3574 && ps_li->li_key == lag_li->li_key
3575 && (bcmp(&ps_li->li_system, &lag_li->li_system,
3576 sizeof(lag_li->li_system))
3577 == 0)) {
3578 return (1);
3579 }
3580 return (0);
3581}
3582
3583static int
3584bondport_remove_from_LAG(bondport_ref p)
3585{
3586 int active_lag = 0;
3587 ifbond_ref bond = p->po_bond;
3588 LAG_ref lag = p->po_lag;
3589
3590 if (lag == NULL) {
3591 return (0);
3592 }
3593 TAILQ_REMOVE(&lag->lag_port_list, p, po_lag_port_list);
3594 if (g_bond->verbose) {
3595 timestamp_printf("[%s] Removed from LAG (0x%04x," EA_FORMAT
3596 ",0x%04x)\n",
3597 bondport_get_name(p),
3598 lag->lag_info.li_system_priority,
3599 EA_LIST(&lag->lag_info.li_system),
3600 lag->lag_info.li_key);
3601 }
3602 p->po_lag = NULL;
3603 lag->lag_port_count--;
3604 if (lag->lag_port_count > 0) {
3605 return (bond->ifb_active_lag == lag);
3606 }
3607 if (g_bond->verbose) {
3608 timestamp_printf("Key 0x%04x: LAG Released (%04x," EA_FORMAT
3609 ",0x%04x)\n",
3610 bond->ifb_key,
3611 lag->lag_info.li_system_priority,
3612 EA_LIST(&lag->lag_info.li_system),
3613 lag->lag_info.li_key);
3614 }
3615 TAILQ_REMOVE(&bond->ifb_lag_list, lag, lag_list);
3616 if (bond->ifb_active_lag == lag) {
3617 bond->ifb_active_lag = NULL;
3618 active_lag = 1;
3619 }
3620 FREE(lag, M_BOND);
3621 return (active_lag);
3622}
3623
3624static void
3625bondport_add_to_LAG(bondport_ref p, LAG_ref lag)
3626{
3627 TAILQ_INSERT_TAIL(&lag->lag_port_list, p, po_lag_port_list);
3628 p->po_lag = lag;
3629 lag->lag_port_count++;
3630 if (g_bond->verbose) {
3631 timestamp_printf("[%s] Added to LAG (0x%04x," EA_FORMAT "0x%04x)\n",
3632 bondport_get_name(p),
3633 lag->lag_info.li_system_priority,
3634 EA_LIST(&lag->lag_info.li_system),
3635 lag->lag_info.li_key);
3636 }
3637 return;
3638}
3639
3640static void
3641bondport_assign_to_LAG(bondport_ref p)
3642{
3643 ifbond_ref bond = p->po_bond;
3644 LAG_ref lag;
3645
3646 if (lacp_actor_partner_state_defaulted(p->po_actor_state)) {
3647 bondport_remove_from_LAG(p);
3648 return;
3649 }
3650 lag = p->po_lag;
3651 if (lag != NULL) {
3652 if (bondport_matches_LAG(p, lag)) {
3653 /* still OK */
3654 return;
3655 }
3656 bondport_remove_from_LAG(p);
3657 }
3658 lag = ifbond_get_LAG_matching_port(bond, p);
3659 if (lag != NULL) {
3660 bondport_add_to_LAG(p, lag);
3661 return;
3662 }
3663 lag = (LAG_ref)_MALLOC(sizeof(*lag), M_BOND, M_WAITOK);
3664 TAILQ_INIT(&lag->lag_port_list);
3665 lag->lag_port_count = 0;
3666 lag->lag_selected_port_count = 0;
3667 lag->lag_info = p->po_partner_state.ps_lag_info;
3668 TAILQ_INSERT_TAIL(&bond->ifb_lag_list, lag, lag_list);
3669 if (g_bond->verbose) {
3670 timestamp_printf("Key 0x%04x: LAG Created (0x%04x," EA_FORMAT
3671 ",0x%04x)\n",
3672 bond->ifb_key,
3673 lag->lag_info.li_system_priority,
3674 EA_LIST(&lag->lag_info.li_system),
3675 lag->lag_info.li_key);
3676 }
3677 bondport_add_to_LAG(p, lag);
3678 return;
3679}
3680
3681static void
3682bondport_receive_lacpdu(bondport_ref p, lacpdu_ref in_lacpdu_p)
3683{
3684 bondport_ref moved_port;
3685
3686 moved_port
3687 = ifbond_list_find_moved_port(p, (const lacp_actor_partner_tlv_ref)
3688 &in_lacpdu_p->la_actor_tlv);
3689 if (moved_port != NULL) {
3690 bondport_receive_machine(moved_port, LAEventPortMoved, NULL);
3691 }
3692 bondport_receive_machine(p, LAEventPacket, in_lacpdu_p);
3693 bondport_mux_machine(p, LAEventPacket, in_lacpdu_p);
3694 bondport_periodic_transmit_machine(p, LAEventPacket, in_lacpdu_p);
3695 return;
3696}
3697
3698static void
3699bondport_set_selected(bondport_ref p, SelectedState s)
3700{
3701 if (s != p->po_selected) {
3702 ifbond_ref bond = p->po_bond;
3703 LAG_ref lag = p->po_lag;
3704
3705 bondport_flags_set_selected_changed(p);
3706 if (lag != NULL && bond->ifb_active_lag == lag) {
3707 if (p->po_selected == SelectedState_SELECTED) {
3708 lag->lag_selected_port_count--;
3709 }
3710 else if (s == SelectedState_SELECTED) {
3711 lag->lag_selected_port_count++;
3712 }
3713 if (g_bond->verbose) {
3714 timestamp_printf("[%s] SetSelected: %s (was %s)\n",
3715 bondport_get_name(p),
3716 SelectedStateString(s),
3717 SelectedStateString(p->po_selected));
3718 }
3719 }
3720 }
3721 p->po_selected = s;
3722 return;
3723}
3724
3725/**
3726 ** Receive machine
3727 **/
3728
3729static void
3730bondport_UpdateDefaultSelected(bondport_ref p)
3731{
3732 bondport_set_selected(p, SelectedState_UNSELECTED);
3733 return;
3734}
3735
3736static void
3737bondport_RecordDefault(bondport_ref p)
3738{
3739 bzero(&p->po_partner_state, sizeof(p->po_partner_state));
3740 p->po_actor_state
3741 = lacp_actor_partner_state_set_defaulted(p->po_actor_state);
3742 bondport_assign_to_LAG(p);
3743 return;
3744}
3745
3746static void
3747bondport_UpdateSelected(bondport_ref p, lacpdu_ref lacpdu_p)
3748{
3749 lacp_actor_partner_tlv_ref actor;
3750 partner_state_ref ps;
3751 LAG_info_ref ps_li;
3752
3753 /* compare the PDU's Actor information to our Partner state */
3754 actor = (lacp_actor_partner_tlv_ref)lacpdu_p->la_actor_tlv;
3755 ps = &p->po_partner_state;
3756 ps_li = &ps->ps_lag_info;
3757 if (lacp_actor_partner_tlv_get_port(actor) != ps->ps_port
3758 || (lacp_actor_partner_tlv_get_port_priority(actor)
3759 != ps->ps_port_priority)
3760 || bcmp(actor->lap_system, &ps_li->li_system, sizeof(ps_li->li_system))
3761 || (lacp_actor_partner_tlv_get_system_priority(actor)
3762 != ps_li->li_system_priority)
3763 || (lacp_actor_partner_tlv_get_key(actor) != ps_li->li_key)
3764 || (lacp_actor_partner_state_aggregatable(actor->lap_state)
3765 != lacp_actor_partner_state_aggregatable(ps->ps_state))) {
3766 bondport_set_selected(p, SelectedState_UNSELECTED);
3767 if (g_bond->verbose) {
3768 timestamp_printf("[%s] updateSelected UNSELECTED\n",
3769 bondport_get_name(p));
3770 }
3771 }
3772 return;
3773}
3774
3775static void
3776bondport_RecordPDU(bondport_ref p, lacpdu_ref lacpdu_p)
3777{
3778 lacp_actor_partner_tlv_ref actor;
3779 ifbond_ref bond = p->po_bond;
3780 int lacp_maintain = 0;
3781 partner_state_ref ps;
3782 lacp_actor_partner_tlv_ref partner;
3783 LAG_info_ref ps_li;
3784
3785 /* copy the PDU's Actor information into our Partner state */
3786 actor = (lacp_actor_partner_tlv_ref)lacpdu_p->la_actor_tlv;
3787 ps = &p->po_partner_state;
3788 ps_li = &ps->ps_lag_info;
3789 ps->ps_port = lacp_actor_partner_tlv_get_port(actor);
3790 ps->ps_port_priority = lacp_actor_partner_tlv_get_port_priority(actor);
3791 ps_li->li_system = *((lacp_system_ref)actor->lap_system);
3792 ps_li->li_system_priority
3793 = lacp_actor_partner_tlv_get_system_priority(actor);
3794 ps_li->li_key = lacp_actor_partner_tlv_get_key(actor);
3795 ps->ps_state = lacp_actor_partner_state_set_out_of_sync(actor->lap_state);
3796 p->po_actor_state
3797 = lacp_actor_partner_state_set_not_defaulted(p->po_actor_state);
3798
3799 /* compare the PDU's Partner information to our own information */
3800 partner = (lacp_actor_partner_tlv_ref)lacpdu_p->la_partner_tlv;
3801
3802 if (lacp_actor_partner_state_active_lacp(ps->ps_state)
3803 || (lacp_actor_partner_state_active_lacp(p->po_actor_state)
3804 && lacp_actor_partner_state_active_lacp(partner->lap_state))) {
3805 if (g_bond->verbose) {
3806 timestamp_printf("[%s] recordPDU: LACP will maintain\n",
3807 bondport_get_name(p));
3808 }
3809 lacp_maintain = 1;
3810 }
3811 if ((lacp_actor_partner_tlv_get_port(partner)
3812 == bondport_get_index(p))
3813 && lacp_actor_partner_tlv_get_port_priority(partner) == p->po_priority
3814 && bcmp(partner->lap_system, &g_bond->system,
3815 sizeof(g_bond->system)) == 0
3816 && (lacp_actor_partner_tlv_get_system_priority(partner)
3817 == g_bond->system_priority)
3818 && lacp_actor_partner_tlv_get_key(partner) == bond->ifb_key
3819 && (lacp_actor_partner_state_aggregatable(partner->lap_state)
3820 == lacp_actor_partner_state_aggregatable(p->po_actor_state))
3821 && lacp_actor_partner_state_in_sync(actor->lap_state)
3822 && lacp_maintain) {
3823 ps->ps_state = lacp_actor_partner_state_set_in_sync(ps->ps_state);
3824 if (g_bond->verbose) {
3825 timestamp_printf("[%s] recordPDU: LACP partner in sync\n",
3826 bondport_get_name(p));
3827 }
3828 }
3829 else if (lacp_actor_partner_state_aggregatable(actor->lap_state) == 0
3830 && lacp_actor_partner_state_in_sync(actor->lap_state)
3831 && lacp_maintain) {
3832 ps->ps_state = lacp_actor_partner_state_set_in_sync(ps->ps_state);
3833 if (g_bond->verbose) {
3834 timestamp_printf("[%s] recordPDU: LACP partner in sync (ind)\n",
3835 bondport_get_name(p));
3836 }
3837 }
3838 bondport_assign_to_LAG(p);
3839 return;
3840}
3841
3842static __inline__ lacp_actor_partner_state
3843updateNTTBits(lacp_actor_partner_state s)
3844{
3845 return (s & (LACP_ACTOR_PARTNER_STATE_LACP_ACTIVITY
3846 | LACP_ACTOR_PARTNER_STATE_LACP_TIMEOUT
3847 | LACP_ACTOR_PARTNER_STATE_AGGREGATION
3848 | LACP_ACTOR_PARTNER_STATE_SYNCHRONIZATION));
3849}
3850
3851static void
3852bondport_UpdateNTT(bondport_ref p, lacpdu_ref lacpdu_p)
3853{
3854 ifbond_ref bond = p->po_bond;
3855 lacp_actor_partner_tlv_ref partner;
3856
3857 /* compare the PDU's Actor information to our Partner state */
3858 partner = (lacp_actor_partner_tlv_ref)lacpdu_p->la_partner_tlv;
3859 if ((lacp_actor_partner_tlv_get_port(partner) != bondport_get_index(p))
3860 || lacp_actor_partner_tlv_get_port_priority(partner) != p->po_priority
3861 || bcmp(partner->lap_system, &g_bond->system, sizeof(g_bond->system))
3862 || (lacp_actor_partner_tlv_get_system_priority(partner)
3863 != g_bond->system_priority)
3864 || lacp_actor_partner_tlv_get_key(partner) != bond->ifb_key
3865 || (updateNTTBits(partner->lap_state)
3866 != updateNTTBits(p->po_actor_state))) {
3867 bondport_flags_set_ntt(p);
3868 if (g_bond->verbose) {
3869 timestamp_printf("[%s] updateNTT: Need To Transmit\n",
3870 bondport_get_name(p));
3871 }
3872 }
3873 return;
3874}
3875
3876static void
3877bondport_AttachMuxToAggregator(bondport_ref p)
3878{
3879 if (bondport_flags_mux_attached(p) == 0) {
3880 if (g_bond->verbose) {
3881 timestamp_printf("[%s] Attached Mux To Aggregator\n",
3882 bondport_get_name(p));
3883 }
3884 bondport_flags_set_mux_attached(p);
3885 }
3886 return;
3887}
3888
3889static void
3890bondport_DetachMuxFromAggregator(bondport_ref p)
3891{
3892 if (bondport_flags_mux_attached(p)) {
3893 if (g_bond->verbose) {
3894 timestamp_printf("[%s] Detached Mux From Aggregator\n",
3895 bondport_get_name(p));
3896 }
3897 bondport_flags_clear_mux_attached(p);
3898 }
3899 return;
3900}
3901
3902static void
3903bondport_enable_distributing(bondport_ref p)
3904{
3905 if (bondport_flags_distributing(p) == 0) {
3906 ifbond_ref bond = p->po_bond;
3907
3908 bond->ifb_distributing_array[bond->ifb_distributing_count++] = p;
3909 if (g_bond->verbose) {
3910 timestamp_printf("[%s] Distribution Enabled\n",
3911 bondport_get_name(p));
3912 }
3913 bondport_flags_set_distributing(p);
3914 }
3915 return;
3916}
3917
3918static void
3919bondport_disable_distributing(bondport_ref p)
3920{
3921 if (bondport_flags_distributing(p)) {
3922 bondport_ref * array;
3923 ifbond_ref bond;
3924 int count;
3925 int i;
3926
3927 bond = p->po_bond;
3928 array = bond->ifb_distributing_array;
3929 count = bond->ifb_distributing_count;
3930 for (i = 0; i < count; i++) {
3931 if (array[i] == p) {
3932 int j;
3933
3934 for (j = i; j < (count - 1); j++) {
3935 array[j] = array[j + 1];
3936 }
3937 break;
3938 }
3939 }
3940 bond->ifb_distributing_count--;
3941 if (g_bond->verbose) {
3942 timestamp_printf("[%s] Distribution Disabled\n",
3943 bondport_get_name(p));
3944 }
3945 bondport_flags_clear_distributing(p);
3946 }
3947 return;
3948}
3949
3950/**
3951 ** Receive machine functions
3952 **/
3953static void
3954bondport_receive_machine_initialize(bondport_ref p, LAEvent event,
3955 void * event_data);
3956static void
3957bondport_receive_machine_port_disabled(bondport_ref p, LAEvent event,
3958 void * event_data);
3959static void
3960bondport_receive_machine_expired(bondport_ref p, LAEvent event,
3961 void * event_data);
3962static void
3963bondport_receive_machine_lacp_disabled(bondport_ref p, LAEvent event,
3964 void * event_data);
3965static void
3966bondport_receive_machine_defaulted(bondport_ref p, LAEvent event,
3967 void * event_data);
3968static void
3969bondport_receive_machine_current(bondport_ref p, LAEvent event,
3970 void * event_data);
3971
3972static void
3973bondport_receive_machine_event(bondport_ref p, LAEvent event,
3974 void * event_data)
3975{
3976 switch (p->po_receive_state) {
3977 case ReceiveState_none:
3978 bondport_receive_machine_initialize(p, LAEventStart, NULL);
3979 break;
3980 case ReceiveState_INITIALIZE:
3981 bondport_receive_machine_initialize(p, event, event_data);
3982 break;
3983 case ReceiveState_PORT_DISABLED:
3984 bondport_receive_machine_port_disabled(p, event, event_data);
3985 break;
3986 case ReceiveState_EXPIRED:
3987 bondport_receive_machine_expired(p, event, event_data);
3988 break;
3989 case ReceiveState_LACP_DISABLED:
3990 bondport_receive_machine_lacp_disabled(p, event, event_data);
3991 break;
3992 case ReceiveState_DEFAULTED:
3993 bondport_receive_machine_defaulted(p, event, event_data);
3994 break;
3995 case ReceiveState_CURRENT:
3996 bondport_receive_machine_current(p, event, event_data);
3997 break;
3998 default:
3999 break;
4000 }
4001 return;
4002}
4003
4004static void
4005bondport_receive_machine(bondport_ref p, LAEvent event,
4006 void * event_data)
4007{
4008 switch (event) {
4009 case LAEventPacket:
4010 if (p->po_receive_state != ReceiveState_LACP_DISABLED) {
4011 bondport_receive_machine_current(p, event, event_data);
4012 }
4013 break;
4014 case LAEventMediaChange:
4015 if (media_active(&p->po_media_info)) {
4016 switch (p->po_receive_state) {
4017 case ReceiveState_PORT_DISABLED:
4018 case ReceiveState_LACP_DISABLED:
4019 bondport_receive_machine_port_disabled(p, LAEventMediaChange, NULL);
4020 break;
4021 default:
4022 break;
4023 }
4024 }
4025 else {
4026 bondport_receive_machine_port_disabled(p, LAEventStart, NULL);
4027 }
4028 break;
4029 default:
4030 bondport_receive_machine_event(p, event, event_data);
4031 break;
4032 }
4033 return;
4034}
4035
4036static void
4037bondport_receive_machine_initialize(bondport_ref p, LAEvent event,
4038 __unused void * event_data)
4039{
4040 switch (event) {
4041 case LAEventStart:
4042 devtimer_cancel(p->po_current_while_timer);
4043 if (g_bond->verbose) {
4044 timestamp_printf("[%s] Receive INITIALIZE\n",
4045 bondport_get_name(p));
4046 }
4047 p->po_receive_state = ReceiveState_INITIALIZE;
4048 bondport_set_selected(p, SelectedState_UNSELECTED);
4049 bondport_RecordDefault(p);
4050 p->po_actor_state
4051 = lacp_actor_partner_state_set_not_expired(p->po_actor_state);
4052 bondport_receive_machine_port_disabled(p, LAEventStart, NULL);
4053 break;
4054 default:
4055 break;
4056 }
4057 return;
4058}
4059
4060static void
4061bondport_receive_machine_port_disabled(bondport_ref p, LAEvent event,
4062 __unused void * event_data)
4063{
4064 partner_state_ref ps;
4065
4066 switch (event) {
4067 case LAEventStart:
4068 devtimer_cancel(p->po_current_while_timer);
4069 if (g_bond->verbose) {
4070 timestamp_printf("[%s] Receive PORT_DISABLED\n",
4071 bondport_get_name(p));
4072 }
4073 p->po_receive_state = ReceiveState_PORT_DISABLED;
4074 ps = &p->po_partner_state;
4075 ps->ps_state = lacp_actor_partner_state_set_out_of_sync(ps->ps_state);
4076 /* FALL THROUGH */
4077 case LAEventMediaChange:
4078 if (media_active(&p->po_media_info)) {
4079 if (media_full_duplex(&p->po_media_info)) {
4080 bondport_receive_machine_expired(p, LAEventStart, NULL);
4081 }
4082 else {
4083 bondport_receive_machine_lacp_disabled(p, LAEventStart, NULL);
4084 }
4085 }
4086 else if (p->po_selected == SelectedState_SELECTED) {
4087 struct timeval tv;
4088
4089 if (g_bond->verbose) {
4090 timestamp_printf("[%s] Receive PORT_DISABLED: "
4091 "link timer started\n",
4092 bondport_get_name(p));
4093 }
4094 tv.tv_sec = 1;
4095 tv.tv_usec = 0;
4096 devtimer_set_relative(p->po_current_while_timer, tv,
4097 (devtimer_timeout_func)
4098 bondport_receive_machine_port_disabled,
4099 (void *)LAEventTimeout, NULL);
4100 }
4101 else if (p->po_selected == SelectedState_STANDBY) {
4102 bondport_set_selected(p, SelectedState_UNSELECTED);
4103 }
4104 break;
4105 case LAEventTimeout:
4106 if (p->po_selected == SelectedState_SELECTED) {
4107 if (g_bond->verbose) {
4108 timestamp_printf("[%s] Receive PORT_DISABLED: "
4109 "link timer completed, marking UNSELECTED\n",
4110 bondport_get_name(p));
4111 }
4112 bondport_set_selected(p, SelectedState_UNSELECTED);
4113 }
4114 break;
4115 case LAEventPortMoved:
4116 bondport_receive_machine_initialize(p, LAEventStart, NULL);
4117 break;
4118 default:
4119 break;
4120 }
4121 return;
4122}
4123
4124static void
4125bondport_receive_machine_expired(bondport_ref p, LAEvent event,
4126 __unused void * event_data)
4127{
4128 lacp_actor_partner_state s;
4129 struct timeval tv;
4130
4131 switch (event) {
4132 case LAEventStart:
4133 devtimer_cancel(p->po_current_while_timer);
4134 if (g_bond->verbose) {
4135 timestamp_printf("[%s] Receive EXPIRED\n",
4136 bondport_get_name(p));
4137 }
4138 p->po_receive_state = ReceiveState_EXPIRED;
4139 s = p->po_partner_state.ps_state;
4140 s = lacp_actor_partner_state_set_out_of_sync(s);
4141 s = lacp_actor_partner_state_set_short_timeout(s);
4142 p->po_partner_state.ps_state = s;
4143 p->po_actor_state
4144 = lacp_actor_partner_state_set_expired(p->po_actor_state);
4145 /* start current_while timer */
4146 tv.tv_sec = LACP_SHORT_TIMEOUT_TIME;
4147 tv.tv_usec = 0;
4148 devtimer_set_relative(p->po_current_while_timer, tv,
4149 (devtimer_timeout_func)
4150 bondport_receive_machine_expired,
4151 (void *)LAEventTimeout, NULL);
4152
4153 break;
4154 case LAEventTimeout:
4155 bondport_receive_machine_defaulted(p, LAEventStart, NULL);
4156 break;
4157 default:
4158 break;
4159 }
4160 return;
4161}
4162
4163static void
4164bondport_receive_machine_lacp_disabled(bondport_ref p, LAEvent event,
4165 __unused void * event_data)
4166{
4167 partner_state_ref ps;
4168 switch (event) {
4169 case LAEventStart:
4170 devtimer_cancel(p->po_current_while_timer);
4171 if (g_bond->verbose) {
4172 timestamp_printf("[%s] Receive LACP_DISABLED\n",
4173 bondport_get_name(p));
4174 }
4175 p->po_receive_state = ReceiveState_LACP_DISABLED;
4176 bondport_set_selected(p, SelectedState_UNSELECTED);
4177 bondport_RecordDefault(p);
4178 ps = &p->po_partner_state;
4179 ps->ps_state = lacp_actor_partner_state_set_individual(ps->ps_state);
4180 p->po_actor_state
4181 = lacp_actor_partner_state_set_not_expired(p->po_actor_state);
4182 break;
4183 default:
4184 break;
4185 }
4186 return;
4187}
4188
4189static void
4190bondport_receive_machine_defaulted(bondport_ref p, LAEvent event,
4191 __unused void * event_data)
4192{
4193 switch (event) {
4194 case LAEventStart:
4195 devtimer_cancel(p->po_current_while_timer);
4196 if (g_bond->verbose) {
4197 timestamp_printf("[%s] Receive DEFAULTED\n",
4198 bondport_get_name(p));
4199 }
4200 p->po_receive_state = ReceiveState_DEFAULTED;
4201 bondport_UpdateDefaultSelected(p);
4202 bondport_RecordDefault(p);
4203 p->po_actor_state
4204 = lacp_actor_partner_state_set_not_expired(p->po_actor_state);
4205 break;
4206 default:
4207 break;
4208 }
4209 return;
4210}
4211
4212static void
4213bondport_receive_machine_current(bondport_ref p, LAEvent event,
4214 void * event_data)
4215{
4216 partner_state_ref ps;
4217 struct timeval tv;
4218
4219 switch (event) {
4220 case LAEventPacket:
4221 devtimer_cancel(p->po_current_while_timer);
4222 if (g_bond->verbose) {
4223 timestamp_printf("[%s] Receive CURRENT\n",
4224 bondport_get_name(p));
4225 }
4226 p->po_receive_state = ReceiveState_CURRENT;
4227 bondport_UpdateSelected(p, event_data);
4228 bondport_UpdateNTT(p, event_data);
4229 bondport_RecordPDU(p, event_data);
4230 p->po_actor_state
4231 = lacp_actor_partner_state_set_not_expired(p->po_actor_state);
4232 bondport_assign_to_LAG(p);
4233 /* start current_while timer */
4234 ps = &p->po_partner_state;
4235 if (lacp_actor_partner_state_short_timeout(ps->ps_state)) {
4236 tv.tv_sec = LACP_SHORT_TIMEOUT_TIME;
4237 }
4238 else {
4239 tv.tv_sec = LACP_LONG_TIMEOUT_TIME;
4240 }
4241 tv.tv_usec = 0;
4242 devtimer_set_relative(p->po_current_while_timer, tv,
4243 (devtimer_timeout_func)
4244 bondport_receive_machine_current,
4245 (void *)LAEventTimeout, NULL);
4246 break;
4247 case LAEventTimeout:
4248 bondport_receive_machine_expired(p, LAEventStart, NULL);
4249 break;
4250 default:
4251 break;
4252 }
4253 return;
4254}
4255
4256/**
4257 ** Periodic Transmission machine
4258 **/
4259
4260static void
4261bondport_periodic_transmit_machine(bondport_ref p, LAEvent event,
4262 __unused void * event_data)
4263{
4264 int interval;
4265 partner_state_ref ps;
4266 struct timeval tv;
4267
4268 switch (event) {
4269 case LAEventStart:
4270 if (g_bond->verbose) {
4271 timestamp_printf("[%s] periodic_transmit Start\n",
4272 bondport_get_name(p));
4273 }
4274 /* FALL THROUGH */
4275 case LAEventMediaChange:
4276 devtimer_cancel(p->po_periodic_timer);
4277 p->po_periodic_interval = 0;
4278 if (media_active(&p->po_media_info) == 0
4279 || media_full_duplex(&p->po_media_info) == 0) {
4280 break;
4281 }
4282 case LAEventPacket:
4283 /* Neither Partner nor Actor are LACP Active, no periodic tx */
4284 ps = &p->po_partner_state;
4285 if (lacp_actor_partner_state_active_lacp(p->po_actor_state) == 0
4286 && (lacp_actor_partner_state_active_lacp(ps->ps_state)
4287 == 0)) {
4288 devtimer_cancel(p->po_periodic_timer);
4289 p->po_periodic_interval = 0;
4290 break;
4291 }
4292 if (lacp_actor_partner_state_short_timeout(ps->ps_state)) {
4293 interval = LACP_FAST_PERIODIC_TIME;
4294 }
4295 else {
4296 interval = LACP_SLOW_PERIODIC_TIME;
4297 }
4298 if (p->po_periodic_interval != interval) {
4299 if (interval == LACP_FAST_PERIODIC_TIME
4300 && p->po_periodic_interval == LACP_SLOW_PERIODIC_TIME) {
4301 if (g_bond->verbose) {
4302 timestamp_printf("[%s] periodic_transmit:"
4303 " Need To Transmit\n",
4304 bondport_get_name(p));
4305 }
4306 bondport_flags_set_ntt(p);
4307 }
4308 p->po_periodic_interval = interval;
4309 tv.tv_usec = 0;
4310 tv.tv_sec = interval;
4311 devtimer_set_relative(p->po_periodic_timer, tv,
4312 (devtimer_timeout_func)
4313 bondport_periodic_transmit_machine,
4314 (void *)LAEventTimeout, NULL);
4315 if (g_bond->verbose) {
4316 timestamp_printf("[%s] Periodic Transmission Timer: %d secs\n",
4317 bondport_get_name(p),
4318 p->po_periodic_interval);
4319 }
4320 }
4321 break;
4322 case LAEventTimeout:
4323 bondport_flags_set_ntt(p);
4324 tv.tv_sec = p->po_periodic_interval;
4325 tv.tv_usec = 0;
4326 devtimer_set_relative(p->po_periodic_timer, tv, (devtimer_timeout_func)
4327 bondport_periodic_transmit_machine,
4328 (void *)LAEventTimeout, NULL);
4329 if (g_bond->verbose > 1) {
4330 timestamp_printf("[%s] Periodic Transmission Timer: %d secs\n",
4331 bondport_get_name(p), p->po_periodic_interval);
4332 }
4333 break;
4334 default:
4335 break;
4336 }
4337 return;
4338}
4339
4340/**
4341 ** Transmit machine
4342 **/
4343static int
4344bondport_can_transmit(bondport_ref p, int32_t current_secs,
b0d623f7 4345 __darwin_time_t * next_secs)
91447636
A
4346{
4347 if (p->po_last_transmit_secs != current_secs) {
4348 p->po_last_transmit_secs = current_secs;
4349 p->po_n_transmit = 0;
4350 }
4351 if (p->po_n_transmit < LACP_PACKET_RATE) {
4352 p->po_n_transmit++;
4353 return (1);
4354 }
4355 if (next_secs != NULL) {
4356 *next_secs = current_secs + 1;
4357 }
4358 return (0);
4359}
4360
4361static void
4362bondport_transmit_machine(bondport_ref p, LAEvent event,
4363 void * event_data)
4364{
4365 lacp_actor_partner_tlv_ref aptlv;
4366 lacp_collector_tlv_ref ctlv;
4367 struct timeval next_tick_time = {0, 0};
4368 lacpdu_ref out_lacpdu_p;
4369 packet_buffer_ref pkt;
4370 partner_state_ref ps;
4371 LAG_info_ref ps_li;
4372
4373 switch (event) {
4374 case LAEventTimeout:
4375 case LAEventStart:
4376 if (p->po_periodic_interval == 0 || bondport_flags_ntt(p) == 0) {
4377 break;
4378 }
2d21ac55 4379 if (event_data == TRANSMIT_MACHINE_TX_IMMEDIATE) {
91447636
A
4380 /* we're going away, transmit the packet no matter what */
4381 }
4382 else if (bondport_can_transmit(p, devtimer_current_secs(),
4383 &next_tick_time.tv_sec) == 0) {
4384 if (devtimer_enabled(p->po_transmit_timer)) {
4385 if (g_bond->verbose > 0) {
4386 timestamp_printf("[%s] Transmit Timer Already Set\n",
4387 bondport_get_name(p));
4388 }
4389 }
4390 else {
4391 devtimer_set_absolute(p->po_transmit_timer, next_tick_time,
4392 (devtimer_timeout_func)
4393 bondport_transmit_machine,
4394 (void *)LAEventTimeout, NULL);
4395 if (g_bond->verbose > 0) {
4396 timestamp_printf("[%s] Transmit Timer Deadline %d secs\n",
4397 bondport_get_name(p),
2d21ac55 4398 (int)next_tick_time.tv_sec);
91447636
A
4399 }
4400 }
4401 break;
4402 }
4403 if (g_bond->verbose > 0) {
4404 if (event == LAEventTimeout) {
4405 timestamp_printf("[%s] Transmit Timer Complete\n",
4406 bondport_get_name(p));
4407 }
4408 }
4409 pkt = packet_buffer_allocate(sizeof(*out_lacpdu_p));
4410 if (pkt == NULL) {
4411 printf("[%s] Transmit: failed to allocate packet buffer\n",
4412 bondport_get_name(p));
4413 break;
4414 }
4415 out_lacpdu_p = (lacpdu_ref)packet_buffer_byteptr(pkt);
4416 bzero(out_lacpdu_p, sizeof(*out_lacpdu_p));
4417 out_lacpdu_p->la_subtype = IEEE8023AD_SLOW_PROTO_SUBTYPE_LACP;
4418 out_lacpdu_p->la_version = LACPDU_VERSION_1;
4419
4420 /* Actor */
4421 aptlv = (lacp_actor_partner_tlv_ref)out_lacpdu_p->la_actor_tlv;
4422 aptlv->lap_tlv_type = LACPDU_TLV_TYPE_ACTOR;
4423 aptlv->lap_length = LACPDU_ACTOR_TLV_LENGTH;
4424 *((lacp_system_ref)aptlv->lap_system) = g_bond->system;
4425 lacp_actor_partner_tlv_set_system_priority(aptlv,
4426 g_bond->system_priority);
4427 lacp_actor_partner_tlv_set_port_priority(aptlv, p->po_priority);
4428 lacp_actor_partner_tlv_set_port(aptlv, bondport_get_index(p));
4429 lacp_actor_partner_tlv_set_key(aptlv, p->po_bond->ifb_key);
4430 aptlv->lap_state = p->po_actor_state;
4431
4432 /* Partner */
4433 aptlv = (lacp_actor_partner_tlv_ref)out_lacpdu_p->la_partner_tlv;
4434 aptlv->lap_tlv_type = LACPDU_TLV_TYPE_PARTNER;
4435 aptlv->lap_length = LACPDU_PARTNER_TLV_LENGTH;
4436 ps = &p->po_partner_state;
4437 ps_li = &ps->ps_lag_info;
4438 lacp_actor_partner_tlv_set_port(aptlv, ps->ps_port);
4439 lacp_actor_partner_tlv_set_port_priority(aptlv, ps->ps_port_priority);
4440 *((lacp_system_ref)aptlv->lap_system) = ps_li->li_system;
4441 lacp_actor_partner_tlv_set_system_priority(aptlv,
4442 ps_li->li_system_priority);
4443 lacp_actor_partner_tlv_set_key(aptlv, ps_li->li_key);
4444 aptlv->lap_state = ps->ps_state;
4445
4446 /* Collector */
4447 ctlv = (lacp_collector_tlv_ref)out_lacpdu_p->la_collector_tlv;
4448 ctlv->lac_tlv_type = LACPDU_TLV_TYPE_COLLECTOR;
4449 ctlv->lac_length = LACPDU_COLLECTOR_TLV_LENGTH;
4450
4451 bondport_slow_proto_transmit(p, pkt);
4452 bondport_flags_clear_ntt(p);
4453 if (g_bond->verbose > 0) {
4454 timestamp_printf("[%s] Transmit Packet %d\n",
4455 bondport_get_name(p), p->po_n_transmit);
4456 }
4457 break;
4458 default:
4459 break;
4460 }
4461 return;
4462}
4463
4464/**
4465 ** Mux machine functions
4466 **/
4467
4468static void
4469bondport_mux_machine_detached(bondport_ref p, LAEvent event,
4470 void * event_data);
4471static void
4472bondport_mux_machine_waiting(bondport_ref p, LAEvent event,
4473 void * event_data);
4474static void
4475bondport_mux_machine_attached(bondport_ref p, LAEvent event,
4476 void * event_data);
4477
4478static void
4479bondport_mux_machine_collecting_distributing(bondport_ref p, LAEvent event,
4480 void * event_data);
4481
4482static void
4483bondport_mux_machine(bondport_ref p, LAEvent event, void * event_data)
4484{
4485 switch (p->po_mux_state) {
4486 case MuxState_none:
4487 bondport_mux_machine_detached(p, LAEventStart, NULL);
4488 break;
4489 case MuxState_DETACHED:
4490 bondport_mux_machine_detached(p, event, event_data);
4491 break;
4492 case MuxState_WAITING:
4493 bondport_mux_machine_waiting(p, event, event_data);
4494 break;
4495 case MuxState_ATTACHED:
4496 bondport_mux_machine_attached(p, event, event_data);
4497 break;
4498 case MuxState_COLLECTING_DISTRIBUTING:
4499 bondport_mux_machine_collecting_distributing(p, event, event_data);
4500 break;
4501 default:
4502 break;
4503 }
4504 return;
4505}
4506
4507static void
4508bondport_mux_machine_detached(bondport_ref p, LAEvent event,
4509 __unused void * event_data)
4510{
4511 lacp_actor_partner_state s;
4512
4513 switch (event) {
4514 case LAEventStart:
4515 devtimer_cancel(p->po_wait_while_timer);
4516 if (g_bond->verbose) {
4517 timestamp_printf("[%s] Mux DETACHED\n",
4518 bondport_get_name(p));
4519 }
4520 p->po_mux_state = MuxState_DETACHED;
4521 bondport_flags_clear_ready(p);
4522 bondport_DetachMuxFromAggregator(p);
4523 bondport_disable_distributing(p);
4524 s = p->po_actor_state;
4525 s = lacp_actor_partner_state_set_out_of_sync(s);
4526 s = lacp_actor_partner_state_set_not_collecting(s);
4527 s = lacp_actor_partner_state_set_not_distributing(s);
4528 p->po_actor_state = s;
4529 bondport_flags_set_ntt(p);
4530 break;
4531 case LAEventSelectedChange:
4532 case LAEventPacket:
4533 case LAEventMediaChange:
4534 if (p->po_selected == SelectedState_SELECTED
4535 || p->po_selected == SelectedState_STANDBY) {
4536 bondport_mux_machine_waiting(p, LAEventStart, NULL);
4537 }
4538 break;
4539 default:
4540 break;
4541 }
4542 return;
4543}
4544
4545static void
4546bondport_mux_machine_waiting(bondport_ref p, LAEvent event,
4547 __unused void * event_data)
4548{
4549 struct timeval tv;
4550
4551 switch (event) {
4552 case LAEventStart:
4553 devtimer_cancel(p->po_wait_while_timer);
4554 if (g_bond->verbose) {
4555 timestamp_printf("[%s] Mux WAITING\n",
4556 bondport_get_name(p));
4557 }
4558 p->po_mux_state = MuxState_WAITING;
4559 /* FALL THROUGH */
4560 default:
4561 case LAEventSelectedChange:
4562 if (p->po_selected == SelectedState_UNSELECTED) {
4563 bondport_mux_machine_detached(p, LAEventStart, NULL);
4564 break;
4565 }
4566 if (p->po_selected == SelectedState_STANDBY) {
4567 devtimer_cancel(p->po_wait_while_timer);
4568 /* wait until state changes to SELECTED */
4569 if (g_bond->verbose) {
4570 timestamp_printf("[%s] Mux WAITING: Standby\n",
4571 bondport_get_name(p));
4572 }
4573 break;
4574 }
4575 if (bondport_flags_ready(p)) {
4576 if (g_bond->verbose) {
4577 timestamp_printf("[%s] Mux WAITING: Port is already ready\n",
4578 bondport_get_name(p));
4579 }
4580 break;
4581 }
4582 if (devtimer_enabled(p->po_wait_while_timer)) {
4583 if (g_bond->verbose) {
4584 timestamp_printf("[%s] Mux WAITING: Timer already set\n",
4585 bondport_get_name(p));
4586 }
4587 break;
4588 }
4589 if (ifbond_all_ports_attached(p->po_bond, p)) {
4590 devtimer_cancel(p->po_wait_while_timer);
4591 if (g_bond->verbose) {
4592 timestamp_printf("[%s] Mux WAITING: No waiting\n",
4593 bondport_get_name(p));
4594 }
4595 bondport_flags_set_ready(p);
4596 goto no_waiting;
4597 }
4598 if (g_bond->verbose) {
4599 timestamp_printf("[%s] Mux WAITING: 2 seconds\n",
4600 bondport_get_name(p));
4601 }
4602 tv.tv_sec = LACP_AGGREGATE_WAIT_TIME;
4603 tv.tv_usec = 0;
4604 devtimer_set_relative(p->po_wait_while_timer, tv,
4605 (devtimer_timeout_func)
4606 bondport_mux_machine_waiting,
4607 (void *)LAEventTimeout, NULL);
4608 break;
4609 case LAEventTimeout:
4610 if (g_bond->verbose) {
4611 timestamp_printf("[%s] Mux WAITING: Ready\n",
4612 bondport_get_name(p));
4613 }
4614 bondport_flags_set_ready(p);
4615 break;
4616 case LAEventReady:
4617 no_waiting:
4618 if (bondport_flags_ready(p)){
4619 if (g_bond->verbose) {
4620 timestamp_printf("[%s] Mux WAITING: All Ports Ready\n",
4621 bondport_get_name(p));
4622 }
4623 bondport_mux_machine_attached(p, LAEventStart, NULL);
4624 break;
4625 }
4626 break;
4627 }
4628 return;
4629}
4630
4631static void
4632bondport_mux_machine_attached(bondport_ref p, LAEvent event,
4633 __unused void * event_data)
4634{
4635 lacp_actor_partner_state s;
4636
4637 switch (event) {
4638 case LAEventStart:
4639 devtimer_cancel(p->po_wait_while_timer);
4640 if (g_bond->verbose) {
4641 timestamp_printf("[%s] Mux ATTACHED\n",
4642 bondport_get_name(p));
4643 }
4644 p->po_mux_state = MuxState_ATTACHED;
4645 bondport_AttachMuxToAggregator(p);
4646 s = p->po_actor_state;
4647 s = lacp_actor_partner_state_set_in_sync(s);
4648 s = lacp_actor_partner_state_set_not_collecting(s);
4649 s = lacp_actor_partner_state_set_not_distributing(s);
4650 bondport_disable_distributing(p);
4651 p->po_actor_state = s;
4652 bondport_flags_set_ntt(p);
4653 /* FALL THROUGH */
4654 default:
4655 switch (p->po_selected) {
4656 case SelectedState_SELECTED:
4657 s = p->po_partner_state.ps_state;
4658 if (lacp_actor_partner_state_in_sync(s)) {
4659 bondport_mux_machine_collecting_distributing(p, LAEventStart,
4660 NULL);
4661 }
4662 break;
4663 default:
4664 bondport_mux_machine_detached(p, LAEventStart, NULL);
4665 break;
4666 }
4667 break;
4668 }
4669 return;
4670}
4671
4672static void
4673bondport_mux_machine_collecting_distributing(bondport_ref p,
4674 LAEvent event,
4675 __unused void * event_data)
4676{
4677 lacp_actor_partner_state s;
4678
4679 switch (event) {
4680 case LAEventStart:
4681 devtimer_cancel(p->po_wait_while_timer);
4682 if (g_bond->verbose) {
4683 timestamp_printf("[%s] Mux COLLECTING_DISTRIBUTING\n",
4684 bondport_get_name(p));
4685 }
4686 p->po_mux_state = MuxState_COLLECTING_DISTRIBUTING;
4687 bondport_enable_distributing(p);
4688 s = p->po_actor_state;
4689 s = lacp_actor_partner_state_set_collecting(s);
4690 s = lacp_actor_partner_state_set_distributing(s);
4691 p->po_actor_state = s;
4692 bondport_flags_set_ntt(p);
4693 /* FALL THROUGH */
4694 default:
4695 s = p->po_partner_state.ps_state;
4696 if (lacp_actor_partner_state_in_sync(s) == 0) {
4697 bondport_mux_machine_attached(p, LAEventStart, NULL);
4698 break;
4699 }
4700 switch (p->po_selected) {
4701 case SelectedState_UNSELECTED:
4702 case SelectedState_STANDBY:
4703 bondport_mux_machine_attached(p, LAEventStart, NULL);
4704 break;
4705 default:
4706 break;
4707 }
4708 break;
4709 }
4710 return;
4711}