2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
33 * - bond/failover interface
34 * - implements IEEE 802.3ad Link Aggregation
38 * Modification History:
40 * April 29, 2004 Dieter Siegmund (dieter@apple.com)
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46 #include <sys/malloc.h>
48 #include <sys/queue.h>
49 #include <sys/socket.h>
50 #include <sys/sockio.h>
51 #include <sys/sysctl.h>
52 #include <sys/systm.h>
53 #include <sys/kern_event.h>
56 #include <net/ethernet.h>
58 #include <net/kpi_interface.h>
59 #include <net/if_arp.h>
60 #include <net/if_dl.h>
61 #include <net/if_ether.h>
62 #include <net/if_types.h>
63 #include <net/if_bond_var.h>
64 #include <net/ieee8023ad.h>
68 #include <net/devtimer.h>
69 #include <net/if_vlan_var.h>
71 #include <kern/locks.h>
72 #include <libkern/OSAtomic.h>
74 #include <netinet/in.h>
75 #include <netinet/if_ether.h>
76 #include <netinet/in_systm.h>
77 #include <netinet/ip.h>
78 #include <netinet/ip6.h>
80 #include <net/if_media.h>
81 #include <net/multicast_list.h>
83 extern int dlil_input_packet(struct ifnet
*, struct mbuf
*, char *);
85 static struct ether_addr slow_proto_multicast
= {
86 IEEE8023AD_SLOW_PROTO_MULTICAST
89 #define BOND_MAXUNIT 128
90 #define BONDNAME "bond"
91 #define M_BOND M_DEVBUF
93 #define EA_FORMAT "%x:%x:%x:%x:%x:%x"
94 #define EA_CH(e, i) ((u_char)((u_char *)(e))[(i)])
95 #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)
97 #define timestamp_printf printf
102 static __inline__ lck_grp_t
*
103 my_lck_grp_alloc_init(const char * grp_name
)
106 lck_grp_attr_t
* grp_attrs
;
108 grp_attrs
= lck_grp_attr_alloc_init();
109 lck_grp_attr_setdefault(grp_attrs
);
110 lck_grp_attr_setdefault(grp_attrs
);
111 grp
= lck_grp_alloc_init(grp_name
, grp_attrs
);
112 lck_grp_attr_free(grp_attrs
);
116 static __inline__ lck_mtx_t
*
117 my_lck_mtx_alloc_init(lck_grp_t
* lck_grp
)
119 lck_attr_t
* lck_attrs
;
122 lck_attrs
= lck_attr_alloc_init();
123 lck_attr_setdefault(lck_attrs
);
124 lck_mtx
= lck_mtx_alloc_init(lck_grp
, lck_attrs
);
125 lck_attr_free(lck_attrs
);
129 static lck_mtx_t
* bond_lck_mtx
;
131 static __inline__
void
134 lck_grp_t
* bond_lck_grp
;
136 bond_lck_grp
= my_lck_grp_alloc_init("if_bond");
137 bond_lck_mtx
= my_lck_mtx_alloc_init(bond_lck_grp
);
140 static __inline__
void
141 bond_assert_lock_held(void)
143 lck_mtx_assert(bond_lck_mtx
, LCK_MTX_ASSERT_OWNED
);
147 static __inline__
void
148 bond_assert_lock_not_held(void)
150 lck_mtx_assert(bond_lck_mtx
, LCK_MTX_ASSERT_NOTOWNED
);
154 static __inline__
void
157 lck_mtx_lock(bond_lck_mtx
);
161 static __inline__
void
164 lck_mtx_unlock(bond_lck_mtx
);
169 ** bond structures, types
173 lacp_system li_system
;
174 lacp_system_priority li_system_priority
;
177 typedef struct LAG_info_s LAG_info
, * LAG_info_ref
;
180 TAILQ_HEAD(port_list
, bondport_s
);
182 TAILQ_HEAD(ifbond_list
, ifbond_s
);
184 TAILQ_HEAD(lag_list
, LAG_s
);
186 typedef struct ifbond_s ifbond
, * ifbond_ref
;
187 typedef struct bondport_s bondport
, * bondport_ref
;
190 TAILQ_ENTRY(LAG_s
) lag_list
;
191 struct port_list lag_port_list
;
192 short lag_port_count
;
193 short lag_selected_port_count
;
194 int lag_active_media
;
197 typedef struct LAG_s LAG
, * LAG_ref
;
199 typedef struct partner_state_s
{
200 LAG_info ps_lag_info
;
202 lacp_port_priority ps_port_priority
;
203 lacp_actor_partner_state ps_state
;
204 } partner_state
, * partner_state_ref
;
207 TAILQ_ENTRY(ifbond_s
) ifb_bond_list
;
209 UInt32 ifb_retain_count
;
210 char ifb_name
[IFNAMSIZ
];
211 struct ifnet
* ifb_ifp
;
212 bpf_packet_func ifb_bpf_input
;
213 bpf_packet_func ifb_bpf_output
;
215 struct port_list ifb_port_list
;
216 short ifb_port_count
;
217 struct lag_list ifb_lag_list
;
219 short ifb_max_active
; /* 0 == unlimited */
220 LAG_ref ifb_active_lag
;
221 struct ifmultiaddr
* ifb_ifma_slow_proto
;
222 bondport_ref
* ifb_distributing_array
;
223 int ifb_distributing_count
;
232 ReceiveState_none
= 0,
233 ReceiveState_INITIALIZE
= 1,
234 ReceiveState_PORT_DISABLED
= 2,
235 ReceiveState_EXPIRED
= 3,
236 ReceiveState_LACP_DISABLED
= 4,
237 ReceiveState_DEFAULTED
= 5,
238 ReceiveState_CURRENT
= 6,
241 typedef u_char ReceiveState
;
244 SelectedState_UNSELECTED
= IF_BOND_STATUS_SELECTED_STATE_UNSELECTED
,
245 SelectedState_SELECTED
= IF_BOND_STATUS_SELECTED_STATE_SELECTED
,
246 SelectedState_STANDBY
= IF_BOND_STATUS_SELECTED_STATE_STANDBY
248 typedef u_char SelectedState
;
250 static __inline__
const char *
251 SelectedStateString(SelectedState s
)
253 static const char * names
[] = { "UNSELECTED", "SELECTED", "STANDBY" };
255 if (s
<= SelectedState_STANDBY
) {
258 return ("<unknown>");
263 MuxState_DETACHED
= 1,
264 MuxState_WAITING
= 2,
265 MuxState_ATTACHED
= 3,
266 MuxState_COLLECTING_DISTRIBUTING
= 4,
269 typedef u_char MuxState
;
272 TAILQ_ENTRY(bondport_s
) po_port_list
;
274 struct multicast_list po_multicast
;
275 struct ifnet
* po_ifp
;
276 struct ether_addr po_saved_addr
;
278 char po_name
[IFNAMSIZ
];
279 struct ifdevmtu po_devmtu
;
282 TAILQ_ENTRY(bondport_s
) po_lag_port_list
;
283 devtimer_ref po_current_while_timer
;
284 devtimer_ref po_periodic_timer
;
285 devtimer_ref po_wait_while_timer
;
286 devtimer_ref po_transmit_timer
;
287 partner_state po_partner_state
;
288 lacp_port_priority po_priority
;
289 lacp_actor_partner_state po_actor_state
;
291 u_char po_periodic_interval
;
292 u_char po_n_transmit
;
293 ReceiveState po_receive_state
;
294 MuxState po_mux_state
;
295 SelectedState po_selected
;
296 int32_t po_last_transmit_secs
;
297 struct media_info po_media_info
;
301 #define IFBF_PROMISC 0x1 /* promiscuous mode */
302 #define IFBF_IF_DETACHING 0x2 /* interface is detaching */
303 #define IFBF_LLADDR 0x4 /* specific link address requested */
304 #define IFBF_CHANGE_IN_PROGRESS 0x8 /* interface add/remove in progress */
306 static int bond_get_status(ifbond_ref ifb
, struct if_bond_req
* ibr_p
,
309 static __inline__
int
310 ifbond_flags_promisc(ifbond_ref ifb
)
312 return ((ifb
->ifb_flags
& IFBF_PROMISC
) != 0);
315 static __inline__
void
316 ifbond_flags_set_promisc(ifbond_ref ifb
)
318 ifb
->ifb_flags
|= IFBF_PROMISC
;
322 static __inline__
void
323 ifbond_flags_clear_promisc(ifbond_ref ifb
)
325 ifb
->ifb_flags
&= ~IFBF_PROMISC
;
329 static __inline__
int
330 ifbond_flags_if_detaching(ifbond_ref ifb
)
332 return ((ifb
->ifb_flags
& IFBF_IF_DETACHING
) != 0);
335 static __inline__
void
336 ifbond_flags_set_if_detaching(ifbond_ref ifb
)
338 ifb
->ifb_flags
|= IFBF_IF_DETACHING
;
342 static __inline__
int
343 ifbond_flags_lladdr(ifbond_ref ifb
)
345 return ((ifb
->ifb_flags
& IFBF_LLADDR
) != 0);
348 static __inline__
void
349 ifbond_flags_set_lladdr(ifbond_ref ifb
)
351 ifb
->ifb_flags
|= IFBF_LLADDR
;
355 static __inline__
void
356 ifbond_flags_clear_lladdr(ifbond_ref ifb
)
358 ifb
->ifb_flags
&= ~IFBF_LLADDR
;
362 static __inline__
int
363 ifbond_flags_change_in_progress(ifbond_ref ifb
)
365 return ((ifb
->ifb_flags
& IFBF_CHANGE_IN_PROGRESS
) != 0);
368 static __inline__
void
369 ifbond_flags_set_change_in_progress(ifbond_ref ifb
)
371 ifb
->ifb_flags
|= IFBF_CHANGE_IN_PROGRESS
;
375 static __inline__
void
376 ifbond_flags_clear_change_in_progress(ifbond_ref ifb
)
378 ifb
->ifb_flags
&= ~IFBF_CHANGE_IN_PROGRESS
;
383 * bondport_ref->po_flags bits
385 #define BONDPORT_FLAGS_NTT 0x01
386 #define BONDPORT_FLAGS_READY 0x02
387 #define BONDPORT_FLAGS_SELECTED_CHANGED 0x04
388 #define BONDPORT_FLAGS_MUX_ATTACHED 0x08
389 #define BONDPORT_FLAGS_DISTRIBUTING 0x10
390 #define BONDPORT_FLAGS_UNUSED2 0x20
391 #define BONDPORT_FLAGS_UNUSED3 0x40
392 #define BONDPORT_FLAGS_UNUSED4 0x80
394 static __inline__
void
395 bondport_flags_set_ntt(bondport_ref p
)
397 p
->po_flags
|= BONDPORT_FLAGS_NTT
;
401 static __inline__
void
402 bondport_flags_clear_ntt(bondport_ref p
)
404 p
->po_flags
&= ~BONDPORT_FLAGS_NTT
;
408 static __inline__
int
409 bondport_flags_ntt(bondport_ref p
)
411 return ((p
->po_flags
& BONDPORT_FLAGS_NTT
) != 0);
414 static __inline__
void
415 bondport_flags_set_ready(bondport_ref p
)
417 p
->po_flags
|= BONDPORT_FLAGS_READY
;
421 static __inline__
void
422 bondport_flags_clear_ready(bondport_ref p
)
424 p
->po_flags
&= ~BONDPORT_FLAGS_READY
;
428 static __inline__
int
429 bondport_flags_ready(bondport_ref p
)
431 return ((p
->po_flags
& BONDPORT_FLAGS_READY
) != 0);
434 static __inline__
void
435 bondport_flags_set_selected_changed(bondport_ref p
)
437 p
->po_flags
|= BONDPORT_FLAGS_SELECTED_CHANGED
;
441 static __inline__
void
442 bondport_flags_clear_selected_changed(bondport_ref p
)
444 p
->po_flags
&= ~BONDPORT_FLAGS_SELECTED_CHANGED
;
448 static __inline__
int
449 bondport_flags_selected_changed(bondport_ref p
)
451 return ((p
->po_flags
& BONDPORT_FLAGS_SELECTED_CHANGED
) != 0);
454 static __inline__
void
455 bondport_flags_set_mux_attached(bondport_ref p
)
457 p
->po_flags
|= BONDPORT_FLAGS_MUX_ATTACHED
;
461 static __inline__
void
462 bondport_flags_clear_mux_attached(bondport_ref p
)
464 p
->po_flags
&= ~BONDPORT_FLAGS_MUX_ATTACHED
;
468 static __inline__
int
469 bondport_flags_mux_attached(bondport_ref p
)
471 return ((p
->po_flags
& BONDPORT_FLAGS_MUX_ATTACHED
) != 0);
474 static __inline__
void
475 bondport_flags_set_distributing(bondport_ref p
)
477 p
->po_flags
|= BONDPORT_FLAGS_DISTRIBUTING
;
481 static __inline__
void
482 bondport_flags_clear_distributing(bondport_ref p
)
484 p
->po_flags
&= ~BONDPORT_FLAGS_DISTRIBUTING
;
488 static __inline__
int
489 bondport_flags_distributing(bondport_ref p
)
491 return ((p
->po_flags
& BONDPORT_FLAGS_DISTRIBUTING
) != 0);
494 typedef struct bond_globals_s
{
495 struct ifbond_list ifbond_list
;
497 lacp_system_priority system_priority
;
499 } * bond_globals_ref
;
501 static bond_globals_ref g_bond
;
504 ** packet_buffer routines
505 ** - thin wrapper for mbuf
508 typedef struct mbuf
* packet_buffer_ref
;
510 static packet_buffer_ref
511 packet_buffer_allocate(int length
)
516 /* leave room for ethernet header */
517 size
= length
+ sizeof(struct ether_header
);
518 if (size
> (int)MHLEN
) {
519 /* XXX doesn't handle large payloads */
520 printf("bond: packet_buffer_allocate size %d > max %d\n", size
, MHLEN
);
523 m
= m_gethdr(M_WAITOK
, MT_DATA
);
528 m
->m_pkthdr
.len
= size
;
533 packet_buffer_byteptr(packet_buffer_ref buf
)
535 return (buf
->m_data
+ sizeof(struct ether_header
));
543 LAEventSelectedChange
,
552 bondport_receive_machine(bondport_ref p
, LAEvent event
,
555 ** Periodic Transmission machine
558 bondport_periodic_transmit_machine(bondport_ref p
, LAEvent event
,
565 bondport_transmit_machine(bondport_ref p
, LAEvent event
,
572 bondport_mux_machine(bondport_ref p
, LAEvent event
,
579 ifbond_activate_LAG(ifbond_ref bond
, LAG_ref lag
, int active_media
);
582 ifbond_deactivate_LAG(ifbond_ref bond
, LAG_ref lag
);
585 ifbond_all_ports_ready(ifbond_ref bond
);
588 ifbond_find_best_LAG(ifbond_ref bond
, int * active_media
);
591 LAG_get_aggregatable_port_count(LAG_ref lag
, int * active_media
);
594 ifbond_selection(ifbond_ref bond
);
602 bondport_receive_lacpdu(bondport_ref p
, lacpdu_ref in_lacpdu_p
);
605 bondport_slow_proto_transmit(bondport_ref p
, packet_buffer_ref buf
);
608 bondport_create(struct ifnet
* port_ifp
, lacp_port_priority priority
,
609 int active
, int short_timeout
, int * error
);
611 bondport_start(bondport_ref p
);
614 bondport_free(bondport_ref p
);
617 bondport_aggregatable(bondport_ref p
);
620 bondport_remove_from_LAG(bondport_ref p
);
623 bondport_set_selected(bondport_ref p
, SelectedState s
);
626 bondport_matches_LAG(bondport_ref p
, LAG_ref lag
);
629 bondport_link_status_changed(bondport_ref p
);
632 bondport_enable_distributing(bondport_ref p
);
635 bondport_disable_distributing(bondport_ref p
);
637 static __inline__
int
638 bondport_collecting(bondport_ref p
)
640 return (lacp_actor_partner_state_collecting(p
->po_actor_state
));
644 ** bond interface/dlil specific routines
646 static int bond_clone_create(struct if_clone
*, int);
647 static void bond_clone_destroy(struct ifnet
*);
648 static int bond_input(struct mbuf
*m
, char *frame_header
, struct ifnet
*ifp
,
649 u_long protocol_family
, int sync_ok
);
650 static int bond_output(struct ifnet
*ifp
, struct mbuf
*m
);
651 static int bond_ioctl(struct ifnet
*ifp
, u_int32_t cmd
, void * addr
);
652 static int bond_set_bpf_tap(struct ifnet
* ifp
, bpf_tap_mode mode
,
653 bpf_packet_func func
);
654 static int bond_attach_protocol(struct ifnet
*ifp
);
655 static int bond_detach_protocol(struct ifnet
*ifp
);
656 static int bond_setmulti(struct ifnet
*ifp
);
657 static int bond_add_interface(struct ifnet
* ifp
, struct ifnet
* port_ifp
);
658 static int bond_remove_interface(ifbond_ref ifb
, struct ifnet
* port_ifp
);
659 static void bond_if_free(struct ifnet
* ifp
);
661 static struct if_clone bond_cloner
= IF_CLONE_INITIALIZER(BONDNAME
,
666 static void interface_link_event(struct ifnet
* ifp
, u_long event_code
);
669 siocsifmtu(struct ifnet
* ifp
, int mtu
)
673 bzero(&ifr
, sizeof(ifr
));
675 return (dlil_ioctl(0, ifp
, SIOCSIFMTU
, (caddr_t
)&ifr
));
679 siocgifdevmtu(struct ifnet
* ifp
, struct ifdevmtu
* ifdm_p
)
684 bzero(&ifr
, sizeof(ifr
));
685 error
= dlil_ioctl(0, ifp
, SIOCGIFDEVMTU
, (caddr_t
)&ifr
);
687 *ifdm_p
= ifr
.ifr_devmtu
;
692 static __inline__
void
693 ether_addr_copy(void * dest
, const void * source
)
695 bcopy(source
, dest
, ETHER_ADDR_LEN
);
699 static __inline__
void
700 ifbond_retain(ifbond_ref ifb
)
702 OSIncrementAtomic(&ifb
->ifb_retain_count
);
705 static __inline__
void
706 ifbond_release(ifbond_ref ifb
)
708 UInt32 old_retain_count
;
710 old_retain_count
= OSDecrementAtomic(&ifb
->ifb_retain_count
);
711 switch (old_retain_count
) {
713 panic("ifbond_release: retain count is 0\n");
716 if (g_bond
->verbose
) {
717 printf("ifbond_release(%s)\n", ifb
->ifb_name
);
719 if (ifb
->ifb_ifma_slow_proto
!= NULL
) {
720 if (g_bond
->verbose
) {
721 printf("ifbond_release(%s) removing multicast\n",
724 (void)if_delmultiaddr(ifb
->ifb_ifma_slow_proto
, 0);
725 ifma_release(ifb
->ifb_ifma_slow_proto
);
727 if (ifb
->ifb_distributing_array
!= NULL
) {
728 FREE(ifb
->ifb_distributing_array
, M_BOND
);
739 * Function: ifbond_wait
741 * Allows a single thread to gain exclusive access to the ifbond
742 * data structure. Some operations take a long time to complete,
743 * and some have side-effects that we can't predict. Holding the
744 * bond_lock() across such operations is not possible.
747 * 1) The SIOCSIFLLADDR ioctl takes a long time (several seconds) to
748 * complete. Simply holding the bond_lock() would freeze all other
749 * data structure accesses during that time.
750 * 2) When we attach our protocol to the interface, a dlil event is
751 * generated and invokes our bond_event() function. bond_event()
752 * needs to take the bond_lock(), but we're already holding it, so
753 * we're deadlocked against ourselves.
755 * Before calling, you must be holding the bond_lock and have taken
756 * a reference on the ifbond_ref.
759 ifbond_wait(ifbond_ref ifb
, const char * msg
)
763 /* other add/remove in progress */
764 while (ifbond_flags_change_in_progress(ifb
)) {
765 if (g_bond
->verbose
) {
766 printf("%s: %s msleep\n", ifb
->ifb_name
, msg
);
769 (void)msleep(ifb
, bond_lck_mtx
, PZERO
, msg
, 0);
771 /* prevent other bond list remove/add from taking place */
772 ifbond_flags_set_change_in_progress(ifb
);
773 if (g_bond
->verbose
&& waited
) {
774 printf("%s: %s woke up\n", ifb
->ifb_name
, msg
);
780 * Function: ifbond_signal
782 * Allows the thread that previously invoked ifbond_wait() to
783 * give up exclusive access to the ifbond data structure, and wake up
784 * any other threads waiting to access
786 * Before calling, you must be holding the bond_lock and have taken
787 * a reference on the ifbond_ref.
790 ifbond_signal(ifbond_ref ifb
, const char * msg
)
792 ifbond_flags_clear_change_in_progress(ifb
);
793 wakeup((caddr_t
)ifb
);
794 if (g_bond
->verbose
) {
795 printf("%s: %s wakeup\n", ifb
->ifb_name
, msg
);
805 link_speed(int active
)
807 switch (IFM_SUBTYPE(active
)) {
828 /* assume that new defined types are going to be at least 10GigE */
835 static __inline__
int
836 media_active(const struct media_info
* mi
)
838 if ((mi
->mi_status
& IFM_AVALID
) == 0) {
841 return ((mi
->mi_status
& IFM_ACTIVE
) != 0);
844 static __inline__
int
845 media_full_duplex(const struct media_info
* mi
)
847 return ((mi
->mi_active
& IFM_FDX
) != 0);
850 static __inline__
int
851 media_speed(const struct media_info
* mi
)
853 return (link_speed(mi
->mi_active
));
856 static struct media_info
857 interface_media_info(struct ifnet
* ifp
)
859 struct ifmediareq ifmr
;
860 struct media_info mi
;
862 bzero(&mi
, sizeof(mi
));
863 bzero(&ifmr
, sizeof(ifmr
));
864 if (dlil_ioctl(0, ifp
, SIOCGIFMEDIA
, (caddr_t
)&ifmr
) == 0) {
865 if (ifmr
.ifm_count
!= 0) {
866 mi
.mi_status
= ifmr
.ifm_status
;
867 mi
.mi_active
= ifmr
.ifm_active
;
874 ** interface utility functions
876 static __inline__
struct ifaddr
*
877 ifindex_get_ifaddr(int i
)
879 if (i
> if_index
|| i
== 0) {
882 return (ifnet_addrs
[i
- 1]);
885 static __inline__
struct ifaddr
*
886 ifp_get_ifaddr(struct ifnet
* ifp
)
888 return (ifindex_get_ifaddr(ifp
->if_index
));
891 static __inline__
struct sockaddr_dl
*
892 ifp_get_sdl(struct ifnet
* ifp
)
896 ifa
= ifp_get_ifaddr(ifp
);
897 return ((struct sockaddr_dl
*)(ifa
->ifa_addr
));
901 if_siflladdr(struct ifnet
* ifp
, const struct ether_addr
* ea_p
)
906 * XXX setting the sa_len to ETHER_ADDR_LEN is wrong, but the driver
907 * currently expects it that way
909 ifr
.ifr_addr
.sa_family
= AF_UNSPEC
;
910 ifr
.ifr_addr
.sa_len
= ETHER_ADDR_LEN
;
911 ether_addr_copy(ifr
.ifr_addr
.sa_data
, ea_p
);
913 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s%d", ifp
->if_name
,
916 return (dlil_ioctl(0, ifp
, SIOCSIFLLADDR
, (caddr_t
)&ifr
));
922 static bond_globals_ref
923 bond_globals_create(lacp_system_priority sys_pri
,
928 b
= _MALLOC(sizeof(*b
), M_BOND
, M_WAITOK
);
932 bzero(b
, sizeof(*b
));
933 TAILQ_INIT(&b
->ifbond_list
);
935 b
->system_priority
= sys_pri
;
943 bond_globals_init(void)
949 bond_assert_lock_not_held();
951 if (g_bond
!= NULL
) {
956 * use en0's ethernet address as the system identifier, and if it's not
957 * there, use en1 .. en3
960 for (i
= 0; i
< 4; i
++) {
961 char ifname
[IFNAMSIZ
+1];
962 snprintf(ifname
, sizeof(ifname
), "en%d", i
);
963 /* XXX ifunit() needs to return a reference on the ifp */
964 ifp
= ifunit(ifname
);
971 b
= bond_globals_create(0x8000,
972 (lacp_system_ref
)LLADDR(ifp_get_sdl(ifp
)));
975 if (g_bond
!= NULL
) {
992 bond_bpf_vlan(struct ifnet
* ifp
, struct mbuf
* m
,
993 const struct ether_header
* eh_p
,
994 u_int16_t vlan_tag
, bpf_packet_func func
)
996 struct ether_vlan_header
* vlh_p
;
999 vl_m
= m_get(M_DONTWAIT
, MT_DATA
);
1003 /* populate a new mbuf containing the vlan ethernet header */
1004 vl_m
->m_len
= ETHER_HDR_LEN
+ ETHER_VLAN_ENCAP_LEN
;
1005 vlh_p
= mtod(vl_m
, struct ether_vlan_header
*);
1006 bcopy(eh_p
, vlh_p
, offsetof(struct ether_header
, ether_type
));
1007 vlh_p
->evl_encap_proto
= htons(ETHERTYPE_VLAN
);
1008 vlh_p
->evl_tag
= htons(vlan_tag
);
1009 vlh_p
->evl_proto
= eh_p
->ether_type
;
1012 vl_m
->m_next
= NULL
;
1017 static __inline__
void
1018 bond_bpf_output(struct ifnet
* ifp
, struct mbuf
* m
,
1019 bpf_packet_func func
)
1022 if (m
->m_pkthdr
.csum_flags
& CSUM_VLAN_TAG_VALID
) {
1023 const struct ether_header
* eh_p
;
1024 eh_p
= mtod(m
, const struct ether_header
*);
1025 m
->m_data
+= ETHER_HDR_LEN
;
1026 m
->m_len
-= ETHER_HDR_LEN
;
1027 bond_bpf_vlan(ifp
, m
, eh_p
, m
->m_pkthdr
.vlan_tag
, func
);
1028 m
->m_data
-= ETHER_HDR_LEN
;
1029 m
->m_len
+= ETHER_HDR_LEN
;
1037 static __inline__
void
1038 bond_bpf_input(ifnet_t ifp
, mbuf_t m
, const struct ether_header
* eh_p
,
1039 bpf_packet_func func
)
1042 if (m
->m_pkthdr
.csum_flags
& CSUM_VLAN_TAG_VALID
) {
1043 bond_bpf_vlan(ifp
, m
, eh_p
, m
->m_pkthdr
.vlan_tag
, func
);
1045 /* restore the header */
1046 m
->m_data
-= ETHER_HDR_LEN
;
1047 m
->m_len
+= ETHER_HDR_LEN
;
1049 m
->m_data
+= ETHER_HDR_LEN
;
1050 m
->m_len
-= ETHER_HDR_LEN
;
1057 * Function: bond_setmulti
1059 * Enable multicast reception on "our" interface by enabling multicasts on
1060 * each of the member ports.
1063 bond_setmulti(struct ifnet
* ifp
)
1071 ifb
= ifp
->if_private
;
1072 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)
1073 || TAILQ_EMPTY(&ifb
->ifb_port_list
)) {
1078 ifbond_wait(ifb
, "bond_setmulti");
1080 if (ifbond_flags_if_detaching(ifb
)) {
1081 /* someone destroyed the bond while we were waiting */
1087 /* ifbond_wait() let's us safely walk the list without holding the lock */
1088 TAILQ_FOREACH(p
, &ifb
->ifb_port_list
, po_port_list
) {
1089 struct ifnet
* port_ifp
= p
->po_ifp
;
1091 error
= multicast_list_program(&p
->po_multicast
,
1094 printf("bond_setmulti(%s): "
1095 "multicast_list_program(%s%d) failed, %d\n",
1096 ifb
->ifb_name
, port_ifp
->if_name
,
1097 port_ifp
->if_unit
, error
);
1103 ifbond_release(ifb
);
1104 ifbond_signal(ifb
, "bond_setmulti");
1110 bond_clone_attach(void)
1112 if_clone_attach(&bond_cloner
);
1118 ifbond_add_slow_proto_multicast(ifbond_ref ifb
)
1121 struct ifmultiaddr
* ifma
= NULL
;
1122 struct sockaddr_dl sdl
;
1124 bond_assert_lock_not_held();
1126 bzero(&sdl
, sizeof(sdl
));
1127 sdl
.sdl_len
= sizeof(sdl
);
1128 sdl
.sdl_family
= AF_LINK
;
1129 sdl
.sdl_type
= IFT_ETHER
;
1131 sdl
.sdl_alen
= sizeof(slow_proto_multicast
);
1132 bcopy(&slow_proto_multicast
, sdl
.sdl_data
, sizeof(slow_proto_multicast
));
1133 error
= if_addmulti(ifb
->ifb_ifp
, (struct sockaddr
*)&sdl
,
1136 ifb
->ifb_ifma_slow_proto
= ifma
;
1142 bond_clone_create(struct if_clone
* ifc
, int unit
)
1148 error
= bond_globals_init();
1153 ifb
= _MALLOC(sizeof(ifbond
), M_BOND
, M_WAITOK
);
1157 bzero(ifb
, sizeof(*ifb
));
1160 TAILQ_INIT(&ifb
->ifb_port_list
);
1161 TAILQ_INIT(&ifb
->ifb_lag_list
);
1162 ifb
->ifb_key
= unit
+ 1;
1164 /* use the interface name as the unique id for ifp recycle */
1165 if ((u_long
)snprintf(ifb
->ifb_name
, sizeof(ifb
->ifb_name
), "%s%d",
1166 ifc
->ifc_name
, unit
) >= sizeof(ifb
->ifb_name
)) {
1167 ifbond_release(ifb
);
1170 error
= dlil_if_acquire(APPLE_IF_FAM_BOND
,
1172 strlen(ifb
->ifb_name
),
1175 ifbond_release(ifb
);
1179 ifp
->if_name
= ifc
->ifc_name
;
1180 ifp
->if_unit
= unit
;
1181 ifp
->if_family
= APPLE_IF_FAM_BOND
;
1182 ifp
->if_private
= NULL
;
1183 ifp
->if_ioctl
= bond_ioctl
;
1184 ifp
->if_set_bpf_tap
= bond_set_bpf_tap
;
1185 ifp
->if_free
= bond_if_free
;
1186 ifp
->if_output
= bond_output
;
1187 ifp
->if_hwassist
= 0;
1188 ifp
->if_addrlen
= ETHER_ADDR_LEN
;
1189 ifp
->if_baudrate
= 0;
1190 ifp
->if_type
= IFT_IEEE8023ADLAG
;
1191 ifp
->if_flags
= IFF_BROADCAST
| IFF_MULTICAST
| IFF_SIMPLEX
;
1194 /* XXX ethernet specific */
1195 ifp
->if_broadcast
.length
= ETHER_ADDR_LEN
;
1196 bcopy(etherbroadcastaddr
, ifp
->if_broadcast
.u
.buffer
, ETHER_ADDR_LEN
);
1198 error
= dlil_if_attach(ifp
);
1200 dlil_if_release(ifp
);
1201 ifbond_release(ifb
);
1204 error
= ifbond_add_slow_proto_multicast(ifb
);
1206 printf("bond_clone_create(%s): "
1207 "failed to add slow_proto multicast, %d\n",
1208 ifb
->ifb_name
, error
);
1211 /* attach as ethernet */
1212 bpfattach(ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
1215 ifp
->if_private
= ifb
;
1216 TAILQ_INSERT_HEAD(&g_bond
->ifbond_list
, ifb
, ifb_bond_list
);
1223 bond_remove_all_interfaces(ifbond_ref ifb
)
1227 bond_assert_lock_held();
1230 * do this in reverse order to avoid re-programming the mac address
1231 * as each head interface is removed
1233 while ((p
= TAILQ_LAST(&ifb
->ifb_port_list
, port_list
)) != NULL
) {
1234 bond_remove_interface(ifb
, p
->po_ifp
);
1240 bond_remove(ifbond_ref ifb
)
1242 bond_assert_lock_held();
1243 ifbond_flags_set_if_detaching(ifb
);
1244 TAILQ_REMOVE(&g_bond
->ifbond_list
, ifb
, ifb_bond_list
);
1245 bond_remove_all_interfaces(ifb
);
1250 bond_if_detach(struct ifnet
* ifp
)
1254 error
= dlil_if_detach(ifp
);
1255 if (error
!= DLIL_WAIT_FOR_FREE
) {
1257 printf("bond_if_detach %s%d: dlil_if_detach failed, %d\n",
1258 ifp
->if_name
, ifp
->if_unit
, error
);
1266 bond_clone_destroy(struct ifnet
* ifp
)
1271 ifb
= ifp
->if_private
;
1272 if (ifb
== NULL
|| ifp
->if_type
!= IFT_IEEE8023ADLAG
) {
1276 if (ifbond_flags_if_detaching(ifb
)) {
1282 bond_if_detach(ifp
);
1287 bond_set_bpf_tap(struct ifnet
* ifp
, bpf_tap_mode mode
, bpf_packet_func func
)
1292 ifb
= ifp
->if_private
;
1293 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
1298 case BPF_TAP_DISABLE
:
1299 ifb
->ifb_bpf_input
= ifb
->ifb_bpf_output
= NULL
;
1303 ifb
->ifb_bpf_input
= func
;
1306 case BPF_TAP_OUTPUT
:
1307 ifb
->ifb_bpf_output
= func
;
1310 case BPF_TAP_INPUT_OUTPUT
:
1311 ifb
->ifb_bpf_input
= ifb
->ifb_bpf_output
= func
;
1321 ether_header_hash(struct ether_header
* eh_p
)
1325 /* get 32-bits from destination ether and ether type */
1326 h
= (*((uint16_t *)&eh_p
->ether_dhost
[4]) << 16)
1328 h
^= *((uint32_t *)&eh_p
->ether_dhost
[0]);
1332 static struct mbuf
*
1333 S_mbuf_skip_to_offset(struct mbuf
* m
, long * offset
)
1338 while (*offset
>= len
) {
1349 #if BYTE_ORDER == BIG_ENDIAN
1350 static __inline__
uint32_t
1351 make_uint32(u_char c0
, u_char c1
, u_char c2
, u_char c3
)
1353 return (((uint32_t)c0
<< 24) | ((uint32_t)c1
<< 16)
1354 | ((uint32_t)c2
<< 8) | (uint32_t)c3
);
1356 #else /* BYTE_ORDER == LITTLE_ENDIAN */
1357 static __inline__
uint32_t
1358 make_uint32(u_char c0
, u_char c1
, u_char c2
, u_char c3
)
1360 return (((uint32_t)c3
<< 24) | ((uint32_t)c2
<< 16)
1361 | ((uint32_t)c1
<< 8) | (uint32_t)c0
);
1363 #endif /* BYTE_ORDER == LITTLE_ENDIAN */
1366 S_mbuf_copy_uint32(struct mbuf
* m
, long offset
, uint32_t * val
)
1368 struct mbuf
* current
;
1369 u_char
* current_data
;
1374 current
= S_mbuf_skip_to_offset(m
, &offset
);
1375 if (current
== NULL
) {
1378 current_data
= mtod(current
, u_char
*) + offset
;
1379 space_current
= current
->m_len
- offset
;
1380 if (space_current
>= (int)sizeof(uint32_t)) {
1381 *val
= *((uint32_t *)current_data
);
1384 next
= current
->m_next
;
1385 if (next
== NULL
|| (next
->m_len
+ space_current
) < (int)sizeof(uint32_t)) {
1388 next_data
= mtod(next
, u_char
*);
1389 switch (space_current
) {
1391 *val
= make_uint32(current_data
[0], next_data
[0],
1392 next_data
[1], next_data
[2]);
1395 *val
= make_uint32(current_data
[0], current_data
[1],
1396 next_data
[0], next_data
[1]);
1399 *val
= make_uint32(current_data
[0], current_data
[1],
1400 current_data
[2], next_data
[0]);
1406 #define IP_SRC_OFFSET (offsetof(struct ip, ip_src) - offsetof(struct ip, ip_p))
1407 #define IP_DST_OFFSET (offsetof(struct ip, ip_dst) - offsetof(struct ip, ip_p))
1410 ip_header_hash(struct mbuf
* m
)
1413 struct in_addr ip_dst
;
1414 struct in_addr ip_src
;
1417 struct mbuf
* orig_m
= m
;
1419 /* find the IP protocol field relative to the start of the packet */
1420 offset
= offsetof(struct ip
, ip_p
) + sizeof(struct ether_header
);
1421 m
= S_mbuf_skip_to_offset(m
, &offset
);
1422 if (m
== NULL
|| m
->m_len
< 1) {
1425 data
= mtod(m
, u_char
*) + offset
;
1428 /* find the IP src relative to the IP protocol */
1429 if ((m
->m_len
- offset
)
1430 >= (int)(IP_SRC_OFFSET
+ sizeof(struct in_addr
) * 2)) {
1431 /* this should be the normal case */
1432 ip_src
= *(struct in_addr
*)(data
+ IP_SRC_OFFSET
);
1433 ip_dst
= *(struct in_addr
*)(data
+ IP_DST_OFFSET
);
1436 if (S_mbuf_copy_uint32(m
, offset
+ IP_SRC_OFFSET
,
1437 (uint32_t *)&ip_src
.s_addr
)) {
1440 if (S_mbuf_copy_uint32(m
, offset
+ IP_DST_OFFSET
,
1441 (uint32_t *)&ip_dst
.s_addr
)) {
1445 return (ntohl(ip_dst
.s_addr
) ^ ntohl(ip_src
.s_addr
) ^ ((uint32_t)ip_p
));
1448 return (ether_header_hash(mtod(orig_m
, struct ether_header
*)));
1451 #define IP6_ADDRS_LEN (sizeof(struct in6_addr) * 2)
1453 ipv6_header_hash(struct mbuf
* m
)
1458 struct mbuf
* orig_m
= m
;
1462 /* find the IP protocol field relative to the start of the packet */
1463 offset
= offsetof(struct ip6_hdr
, ip6_src
) + sizeof(struct ether_header
);
1464 m
= S_mbuf_skip_to_offset(m
, &offset
);
1466 goto bad_ipv6_packet
;
1468 data
= mtod(m
, u_char
*) + offset
;
1470 if ((m
->m_len
- offset
) >= (int)IP6_ADDRS_LEN
) {
1471 /* this should be the normal case */
1472 for (i
= 0, scan
= (uint32_t *)data
;
1473 i
< (int)(IP6_ADDRS_LEN
/ sizeof(uint32_t));
1479 for (i
= 0; i
< (int)(IP6_ADDRS_LEN
/ sizeof(uint32_t)); i
++) {
1481 if (S_mbuf_copy_uint32(m
, offset
+ i
* sizeof(uint32_t),
1482 (uint32_t *)&tmp
)) {
1483 goto bad_ipv6_packet
;
1488 return (ntohl(val
));
1491 return (ether_header_hash(mtod(orig_m
, struct ether_header
*)));
1495 bond_output(struct ifnet
* ifp
, struct mbuf
* m
)
1497 bpf_packet_func bpf_func
;
1500 struct ifnet
* port_ifp
= NULL
;
1505 if ((m
->m_flags
& M_PKTHDR
) == 0) {
1509 if (m
->m_pkthdr
.socket_id
!= 0) {
1510 h
= m
->m_pkthdr
.socket_id
;
1513 struct ether_header
* eh_p
;
1515 eh_p
= mtod(m
, struct ether_header
*);
1516 switch (ntohs(eh_p
->ether_type
)) {
1518 h
= ip_header_hash(m
);
1520 case ETHERTYPE_IPV6
:
1521 h
= ipv6_header_hash(m
);
1524 h
= ether_header_hash(eh_p
);
1529 ifb
= ifp
->if_private
;
1530 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)
1531 || ifb
->ifb_distributing_count
== 0) {
1534 h
%= ifb
->ifb_distributing_count
;
1535 port_ifp
= ifb
->ifb_distributing_array
[h
]->po_ifp
;
1536 bpf_func
= ifb
->ifb_bpf_output
;
1539 if (m
->m_pkthdr
.csum_flags
& CSUM_VLAN_TAG_VALID
) {
1540 (void)ifnet_stat_increment_out(ifp
, 1,
1541 m
->m_pkthdr
.len
+ ETHER_VLAN_ENCAP_LEN
,
1544 (void)ifnet_stat_increment_out(ifp
, 1, m
->m_pkthdr
.len
, 0);
1546 bond_bpf_output(ifp
, m
, bpf_func
);
1548 return (dlil_output(port_ifp
, 0, m
, NULL
, NULL
, 1));
1557 ifbond_lookup_port(ifbond_ref ifb
, struct ifnet
* port_ifp
)
1560 TAILQ_FOREACH(p
, &ifb
->ifb_port_list
, po_port_list
) {
1561 if (p
->po_ifp
== port_ifp
) {
1569 bond_lookup_port(struct ifnet
* port_ifp
)
1574 TAILQ_FOREACH(ifb
, &g_bond
->ifbond_list
, ifb_bond_list
) {
1575 port
= ifbond_lookup_port(ifb
, port_ifp
);
1584 bond_receive_lacpdu(struct mbuf
* m
, struct ifnet
* port_ifp
)
1586 struct ifnet
* bond_ifp
= NULL
;
1591 if ((port_ifp
->if_eflags
& IFEF_BOND
) == 0) {
1594 p
= bond_lookup_port(port_ifp
);
1598 if (p
->po_enabled
== 0) {
1601 bondport_receive_lacpdu(p
, (lacpdu_ref
)m
->m_data
);
1602 if (ifbond_selection(p
->po_bond
)) {
1603 event_code
= (p
->po_bond
->ifb_active_lag
== NULL
)
1606 /* XXX need to take a reference on bond_ifp */
1607 bond_ifp
= p
->po_bond
->ifb_ifp
;
1612 if (bond_ifp
!= NULL
) {
1613 interface_link_event(bond_ifp
, event_code
);
1620 bond_receive_la_marker_pdu(struct mbuf
* m
, struct ifnet
* port_ifp
)
1622 la_marker_pdu_ref marker_p
;
1625 marker_p
= (la_marker_pdu_ref
)(m
->m_data
+ ETHER_HDR_LEN
);
1626 if (marker_p
->lm_marker_tlv_type
!= LA_MARKER_TLV_TYPE_MARKER
) {
1630 if ((port_ifp
->if_eflags
& IFEF_BOND
) == 0) {
1634 p
= bond_lookup_port(port_ifp
);
1635 if (p
== NULL
|| p
->po_enabled
== 0) {
1639 /* echo back the same packet as a marker response */
1640 marker_p
->lm_marker_tlv_type
= LA_MARKER_TLV_TYPE_MARKER_RESPONSE
;
1641 bondport_slow_proto_transmit(p
, (packet_buffer_ref
)m
);
1651 bond_input(struct mbuf
* m
, char * frame_header
, struct ifnet
* port_ifp
,
1652 __unused u_long protocol_family
, __unused
int sync_ok
)
1654 bpf_packet_func bpf_func
;
1655 const struct ether_header
* eh_p
;
1660 eh_p
= (const struct ether_header
*)frame_header
;
1661 if ((m
->m_flags
& M_MCAST
) != 0
1662 && bcmp(eh_p
->ether_dhost
, &slow_proto_multicast
,
1663 sizeof(eh_p
->ether_dhost
)) == 0
1664 && ntohs(eh_p
->ether_type
) == IEEE8023AD_SLOW_PROTO_ETHERTYPE
) {
1665 u_char subtype
= *mtod(m
, u_char
*);
1667 if (subtype
== IEEE8023AD_SLOW_PROTO_SUBTYPE_LACP
) {
1668 if (m
->m_pkthdr
.len
< (int)offsetof(lacpdu
, la_reserved
)) {
1673 if (m
->m_len
< (int)offsetof(lacpdu
, la_reserved
)) {
1674 m
= m_pullup(m
, offsetof(lacpdu
, la_reserved
));
1679 bond_receive_lacpdu(m
, port_ifp
);
1682 else if (subtype
== IEEE8023AD_SLOW_PROTO_SUBTYPE_LA_MARKER_PROTOCOL
) {
1685 /* restore the ethernet header pointer in the mbuf */
1686 m
->m_pkthdr
.len
+= ETHER_HDR_LEN
;
1687 m
->m_data
-= ETHER_HDR_LEN
;
1688 m
->m_len
+= ETHER_HDR_LEN
;
1689 min_size
= ETHER_HDR_LEN
+ offsetof(la_marker_pdu
, lm_reserved
);
1690 if (m
->m_pkthdr
.len
< min_size
) {
1695 if (m
->m_len
< min_size
) {
1696 m
= m_pullup(m
, min_size
);
1701 /* send to marker responder */
1702 bond_receive_la_marker_pdu(m
, port_ifp
);
1705 else if (subtype
== 0
1706 || subtype
> IEEE8023AD_SLOW_PROTO_SUBTYPE_RESERVED_END
) {
1707 /* invalid subtype, discard the frame */
1713 if ((port_ifp
->if_eflags
& IFEF_BOND
) == 0) {
1716 p
= bond_lookup_port(port_ifp
);
1717 if (p
== NULL
|| bondport_collecting(p
) == 0) {
1721 /* make the packet appear as if it arrived on the bonded interface */
1724 bpf_func
= ifb
->ifb_bpf_input
;
1727 if (m
->m_pkthdr
.csum_flags
& CSUM_VLAN_TAG_VALID
) {
1728 (void)ifnet_stat_increment_in(ifp
, 1,
1729 (m
->m_pkthdr
.len
+ ETHER_HDR_LEN
1730 + ETHER_VLAN_ENCAP_LEN
), 0);
1733 (void)ifnet_stat_increment_in(ifp
, 1,
1734 (m
->m_pkthdr
.len
+ ETHER_HDR_LEN
), 0);
1736 m
->m_pkthdr
.rcvif
= ifp
;
1737 bond_bpf_input(ifp
, m
, eh_p
, bpf_func
);
1738 dlil_input_packet(ifp
, m
, frame_header
);
1747 static __inline__
const char *
1748 bondport_get_name(bondport_ref p
)
1750 return (p
->po_name
);
1753 static __inline__
int
1754 bondport_get_index(bondport_ref p
)
1756 return (p
->po_ifp
->if_index
);
1760 bondport_slow_proto_transmit(bondport_ref p
, packet_buffer_ref buf
)
1762 struct ether_header
* eh_p
;
1765 /* packet_buffer_allocate leaves room for ethernet header */
1766 eh_p
= mtod(buf
, struct ether_header
*);
1767 bcopy(&slow_proto_multicast
, &eh_p
->ether_dhost
, sizeof(eh_p
->ether_dhost
));
1768 bcopy(&p
->po_saved_addr
, eh_p
->ether_shost
, sizeof(eh_p
->ether_shost
));
1769 eh_p
->ether_type
= htons(IEEE8023AD_SLOW_PROTO_ETHERTYPE
);
1770 error
= dlil_output(p
->po_ifp
, 0, buf
, NULL
, NULL
, 1);
1772 printf("bondport_slow_proto_transmit(%s) failed %d\n",
1773 bondport_get_name(p
), error
);
1779 bondport_timer_process_func(devtimer_ref timer
,
1780 devtimer_process_func_event event
)
1785 case devtimer_process_func_event_lock
:
1787 devtimer_retain(timer
);
1789 case devtimer_process_func_event_unlock
:
1790 if (devtimer_valid(timer
)) {
1791 /* as long as the devtimer is valid, we can look at arg0 */
1793 struct ifnet
* bond_ifp
= NULL
;
1795 p
= (bondport_ref
)devtimer_arg0(timer
);
1796 if (ifbond_selection(p
->po_bond
)) {
1797 event_code
= (p
->po_bond
->ifb_active_lag
== NULL
)
1800 /* XXX need to take a reference on bond_ifp */
1801 bond_ifp
= p
->po_bond
->ifb_ifp
;
1803 devtimer_release(timer
);
1805 if (bond_ifp
!= NULL
) {
1806 interface_link_event(bond_ifp
, event_code
);
1810 /* timer is going away */
1811 devtimer_release(timer
);
1821 bondport_create(struct ifnet
* port_ifp
, lacp_port_priority priority
,
1822 int active
, int short_timeout
, int * ret_error
)
1825 bondport_ref p
= NULL
;
1826 lacp_actor_partner_state s
;
1829 p
= _MALLOC(sizeof(*p
), M_BOND
, M_WAITOK
);
1831 *ret_error
= ENOMEM
;
1834 bzero(p
, sizeof(*p
));
1835 multicast_list_init(&p
->po_multicast
);
1836 if ((u_long
)snprintf(p
->po_name
, sizeof(p
->po_name
), "%s%d",
1837 port_ifp
->if_name
, port_ifp
->if_unit
)
1838 >= sizeof(p
->po_name
)) {
1839 printf("if_bond: name too large\n");
1840 *ret_error
= EINVAL
;
1843 error
= siocgifdevmtu(port_ifp
, &p
->po_devmtu
);
1845 printf("if_bond: SIOCGIFDEVMTU %s failed, %d\n",
1846 bondport_get_name(p
), error
);
1849 /* remember the current interface MTU so it can be restored */
1850 p
->po_devmtu
.ifdm_current
= port_ifp
->if_mtu
;
1851 p
->po_ifp
= port_ifp
;
1852 p
->po_media_info
= interface_media_info(port_ifp
);
1853 p
->po_current_while_timer
= devtimer_create(bondport_timer_process_func
, p
);
1854 if (p
->po_current_while_timer
== NULL
) {
1855 *ret_error
= ENOMEM
;
1858 p
->po_periodic_timer
= devtimer_create(bondport_timer_process_func
, p
);
1859 if (p
->po_periodic_timer
== NULL
) {
1860 *ret_error
= ENOMEM
;
1863 p
->po_wait_while_timer
= devtimer_create(bondport_timer_process_func
, p
);
1864 if (p
->po_wait_while_timer
== NULL
) {
1865 *ret_error
= ENOMEM
;
1868 p
->po_transmit_timer
= devtimer_create(bondport_timer_process_func
, p
);
1869 if (p
->po_transmit_timer
== NULL
) {
1870 *ret_error
= ENOMEM
;
1873 p
->po_receive_state
= ReceiveState_none
;
1874 p
->po_mux_state
= MuxState_none
;
1875 p
->po_priority
= priority
;
1877 s
= lacp_actor_partner_state_set_aggregatable(s
);
1878 if (short_timeout
) {
1879 s
= lacp_actor_partner_state_set_short_timeout(s
);
1882 s
= lacp_actor_partner_state_set_active_lacp(s
);
1884 p
->po_actor_state
= s
;
1893 bondport_start(bondport_ref p
)
1895 bondport_receive_machine(p
, LAEventStart
, NULL
);
1896 bondport_mux_machine(p
, LAEventStart
, NULL
);
1897 bondport_periodic_transmit_machine(p
, LAEventStart
, NULL
);
1898 bondport_transmit_machine(p
, LAEventStart
, NULL
);
1903 * Function: bondport_invalidate_timers
1905 * Invalidate all of the timers for the bondport.
1908 bondport_invalidate_timers(bondport_ref p
)
1910 devtimer_invalidate(p
->po_current_while_timer
);
1911 devtimer_invalidate(p
->po_periodic_timer
);
1912 devtimer_invalidate(p
->po_wait_while_timer
);
1913 devtimer_invalidate(p
->po_transmit_timer
);
1917 bondport_free(bondport_ref p
)
1919 multicast_list_remove(&p
->po_multicast
);
1920 devtimer_release(p
->po_current_while_timer
);
1921 devtimer_release(p
->po_periodic_timer
);
1922 devtimer_release(p
->po_wait_while_timer
);
1923 devtimer_release(p
->po_transmit_timer
);
1928 #define BOND_ADD_PROGRESS_IN_LIST 0x1
1929 #define BOND_ADD_PROGRESS_PROTO_ATTACHED 0x2
1930 #define BOND_ADD_PROGRESS_LLADDR_SET 0x4
1931 #define BOND_ADD_PROGRESS_MTU_SET 0x8
1933 static __inline__
int
1934 bond_device_mtu(struct ifnet
* ifp
, ifbond_ref ifb
)
1936 return (((int)ifp
->if_mtu
> ifb
->ifb_altmtu
)
1937 ? (int)ifp
->if_mtu
: ifb
->ifb_altmtu
);
1941 bond_add_interface(struct ifnet
* ifp
, struct ifnet
* port_ifp
)
1947 struct sockaddr_dl
* ifb_sdl
;
1948 bondport_ref
* new_array
= NULL
;
1949 bondport_ref
* old_array
= NULL
;
1951 struct sockaddr_dl
* port_sdl
;
1954 /* pre-allocate space for new port */
1955 p
= bondport_create(port_ifp
, 0x8000, 1, 0, &error
);
1960 ifb
= (ifbond_ref
)ifp
->if_private
;
1961 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
1964 return ((ifb
== NULL
? EOPNOTSUPP
: EBUSY
));
1967 /* make sure this interface can handle our current MTU */
1968 devmtu
= bond_device_mtu(ifp
, ifb
);
1970 && (devmtu
> p
->po_devmtu
.ifdm_max
|| devmtu
< p
->po_devmtu
.ifdm_min
)) {
1972 printf("if_bond: interface %s doesn't support mtu %d",
1973 bondport_get_name(p
), devmtu
);
1978 /* make sure ifb doesn't get de-allocated while we wait */
1981 /* wait for other add or remove to complete */
1982 ifbond_wait(ifb
, "bond_add_interface");
1984 if (ifbond_flags_if_detaching(ifb
)) {
1985 /* someone destroyed the bond while we were waiting */
1989 if (bond_lookup_port(port_ifp
) != NULL
) {
1990 /* port is already part of a bond */
1994 ifnet_lock_exclusive(port_ifp
);
1995 if ((port_ifp
->if_eflags
& (IFEF_VLAN
| IFEF_BOND
)) != 0) {
1996 /* interface already has VLAN's, or is part of bond */
1997 ifnet_lock_done(port_ifp
);
2002 /* mark the interface busy */
2003 port_ifp
->if_eflags
|= IFEF_BOND
;
2004 ifnet_lock_done(port_ifp
);
2006 port_sdl
= ifp_get_sdl(port_ifp
);
2007 ifb_sdl
= ifp_get_sdl(ifp
);
2009 if (TAILQ_EMPTY(&ifb
->ifb_port_list
)) {
2010 ifp
->if_hwassist
= port_ifp
->if_hwassist
;
2011 ifp
->if_flags
|= IFF_RUNNING
;
2012 if (ifbond_flags_lladdr(ifb
) == FALSE
) {
2013 /* first port added to bond determines bond's ethernet address */
2014 ether_addr_copy(LLADDR(ifb_sdl
), LLADDR(port_sdl
));
2015 ifb_sdl
->sdl_type
= IFT_ETHER
;
2016 ifb_sdl
->sdl_alen
= ETHER_ADDR_LEN
;
2019 if (ifp
->if_hwassist
!= port_ifp
->if_hwassist
) {
2020 printf("bond_add_interface(%s, %s) "
2021 "hwassist values don't match 0x%x != 0x%x\n",
2022 ifb
->ifb_name
, bondport_get_name(p
),
2023 ifp
->if_hwassist
, port_ifp
->if_hwassist
);
2026 * if the bond has VLAN's, we can't simply change the hwassist
2027 * field behind its back: this needs work
2029 ifp
->if_hwassist
= 0;
2034 /* remember the port's ethernet address so it can be restored */
2035 ether_addr_copy(&p
->po_saved_addr
, LLADDR(port_sdl
));
2037 /* add it to the list of ports */
2038 TAILQ_INSERT_TAIL(&ifb
->ifb_port_list
, p
, po_port_list
);
2039 ifb
->ifb_port_count
++;
2041 /* set the default MTU */
2042 if (ifp
->if_mtu
== 0) {
2043 ifp
->if_mtu
= ETHERMTU
;
2046 progress
|= BOND_ADD_PROGRESS_IN_LIST
;
2048 /* allocate a larger distributing array */
2049 new_array
= (bondport_ref
*)
2050 _MALLOC(sizeof(*new_array
) * ifb
->ifb_port_count
, M_BOND
, M_WAITOK
);
2051 if (new_array
== NULL
) {
2056 /* attach our BOND "protocol" to the interface */
2057 error
= bond_attach_protocol(port_ifp
);
2061 progress
|= BOND_ADD_PROGRESS_PROTO_ATTACHED
;
2063 /* set the interface MTU */
2064 devmtu
= bond_device_mtu(ifp
, ifb
);
2065 error
= siocsifmtu(port_ifp
, devmtu
);
2067 printf("bond_add_interface(%s, %s):"
2068 " SIOCSIFMTU %d failed %d\n",
2069 ifb
->ifb_name
, bondport_get_name(p
), devmtu
, error
);
2072 progress
|= BOND_ADD_PROGRESS_MTU_SET
;
2074 /* program the port with our multicast addresses */
2075 error
= multicast_list_program(&p
->po_multicast
, ifp
, port_ifp
);
2077 printf("bond_add_interface(%s, %s):"
2078 " multicast_list_program failed %d\n",
2079 ifb
->ifb_name
, bondport_get_name(p
), error
);
2083 /* mark the interface up */
2084 ifnet_set_flags(port_ifp
, IFF_UP
, IFF_UP
);
2086 error
= dlil_ioctl(0, port_ifp
, SIOCSIFFLAGS
, (caddr_t
)NULL
);
2088 printf("bond_add_interface(%s, %s): SIOCSIFFLAGS failed %d\n",
2089 ifb
->ifb_name
, bondport_get_name(p
), error
);
2093 /* re-program the port's ethernet address */
2094 error
= if_siflladdr(port_ifp
,
2095 (const struct ether_addr
*)LLADDR(ifb_sdl
));
2097 /* port doesn't support setting the link address */
2098 printf("bond_add_interface(%s, %s): if_siflladdr failed %d\n",
2099 ifb
->ifb_name
, bondport_get_name(p
), error
);
2102 progress
|= BOND_ADD_PROGRESS_LLADDR_SET
;
2106 /* no failures past this point */
2109 /* copy the contents of the existing distributing array */
2110 if (ifb
->ifb_distributing_count
) {
2111 bcopy(ifb
->ifb_distributing_array
, new_array
,
2112 sizeof(*new_array
) * ifb
->ifb_distributing_count
);
2114 old_array
= ifb
->ifb_distributing_array
;
2115 ifb
->ifb_distributing_array
= new_array
;
2117 /* clear the busy state, and wakeup anyone waiting */
2118 ifbond_signal(ifb
, "bond_add_interface");
2121 /* check if we need to generate a link status event */
2122 if (ifbond_selection(ifb
)) {
2123 event_code
= (ifb
->ifb_active_lag
== NULL
)
2128 if (event_code
!= 0) {
2129 interface_link_event(ifp
, event_code
);
2131 if (old_array
!= NULL
) {
2132 FREE(old_array
, M_BOND
);
2137 bond_assert_lock_not_held();
2139 if (new_array
!= NULL
) {
2140 FREE(new_array
, M_BOND
);
2142 if ((progress
& BOND_ADD_PROGRESS_LLADDR_SET
) != 0) {
2145 error1
= if_siflladdr(port_ifp
, &p
->po_saved_addr
);
2147 printf("bond_add_interface(%s, %s): if_siflladdr failed %d\n",
2148 ifb
->ifb_name
, bondport_get_name(p
), error1
);
2151 if ((progress
& BOND_ADD_PROGRESS_PROTO_ATTACHED
) != 0) {
2152 (void)bond_detach_protocol(port_ifp
);
2154 if ((progress
& BOND_ADD_PROGRESS_MTU_SET
) != 0) {
2157 error1
= siocsifmtu(port_ifp
, p
->po_devmtu
.ifdm_current
);
2159 printf("bond_add_interface(%s, %s): SIOCSIFMTU %d failed %d\n",
2160 ifb
->ifb_name
, bondport_get_name(p
), p
->po_devmtu
.ifdm_current
,
2165 if ((progress
& BOND_ADD_PROGRESS_IN_LIST
) != 0) {
2166 TAILQ_REMOVE(&ifb
->ifb_port_list
, p
, po_port_list
);
2167 ifb
->ifb_port_count
--;
2169 ifnet_lock_exclusive(port_ifp
);
2170 port_ifp
->if_eflags
&= ~IFEF_BOND
;
2171 ifnet_lock_done(port_ifp
);
2172 if (TAILQ_EMPTY(&ifb
->ifb_port_list
)) {
2173 ifb
->ifb_altmtu
= 0;
2175 ifp
->if_hwassist
= 0;
2176 if (ifbond_flags_lladdr(ifb
) == FALSE
) {
2177 bzero(LLADDR(ifb_sdl
), ETHER_ADDR_LEN
);
2178 ifb_sdl
->sdl_type
= IFT_IEEE8023ADLAG
;
2179 ifb_sdl
->sdl_alen
= 0;
2184 ifbond_release(ifb
);
2185 ifbond_signal(ifb
, "bond_add_interface");
2192 bond_remove_interface(ifbond_ref ifb
, struct ifnet
* port_ifp
)
2197 bondport_ref head_port
;
2198 struct sockaddr_dl
* ifb_sdl
;
2200 int new_link_address
= 0;
2202 lacp_actor_partner_state s
;
2204 bond_assert_lock_held();
2207 ifbond_wait(ifb
, "bond_remove_interface");
2209 p
= ifbond_lookup_port(ifb
, port_ifp
);
2212 /* it got removed by another thread */
2216 /* de-select it and remove it from the lists */
2217 bondport_disable_distributing(p
);
2218 bondport_set_selected(p
, SelectedState_UNSELECTED
);
2219 active_lag
= bondport_remove_from_LAG(p
);
2220 TAILQ_REMOVE(&ifb
->ifb_port_list
, p
, po_port_list
);
2221 ifb
->ifb_port_count
--;
2223 /* invalidate timers here while holding the bond_lock */
2224 bondport_invalidate_timers(p
);
2226 /* announce that we're Individual now */
2227 s
= p
->po_actor_state
;
2228 s
= lacp_actor_partner_state_set_individual(s
);
2229 s
= lacp_actor_partner_state_set_not_collecting(s
);
2230 s
= lacp_actor_partner_state_set_not_distributing(s
);
2231 s
= lacp_actor_partner_state_set_out_of_sync(s
);
2232 p
->po_actor_state
= s
;
2233 bondport_flags_set_ntt(p
);
2236 ifb_sdl
= ifp_get_sdl(ifp
);
2237 head_port
= TAILQ_FIRST(&ifb
->ifb_port_list
);
2238 if (head_port
== NULL
) {
2239 ifp
->if_flags
&= ~IFF_RUNNING
;
2240 if (ifbond_flags_lladdr(ifb
) == FALSE
) {
2241 ifb_sdl
->sdl_type
= IFT_IEEE8023ADLAG
;
2242 ifb_sdl
->sdl_alen
= 0;
2243 bzero(LLADDR(ifb_sdl
), ETHER_ADDR_LEN
);
2245 ifp
->if_hwassist
= 0;
2247 ifb
->ifb_altmtu
= 0;
2248 } else if (ifbond_flags_lladdr(ifb
) == FALSE
2249 && bcmp(&p
->po_saved_addr
, LLADDR(ifb_sdl
),
2250 ETHER_ADDR_LEN
) == 0) {
2251 /* this port gave the bond its ethernet address, switch to new one */
2252 ether_addr_copy(LLADDR(ifb_sdl
), &head_port
->po_saved_addr
);
2253 ifb_sdl
->sdl_type
= IFT_ETHER
;
2254 ifb_sdl
->sdl_alen
= ETHER_ADDR_LEN
;
2255 new_link_address
= 1;
2257 /* check if we need to generate a link status event */
2258 if (ifbond_selection(ifb
) || active_lag
) {
2259 event_code
= (ifb
->ifb_active_lag
== NULL
)
2265 bondport_transmit_machine(p
, LAEventStart
, (void *)1);
2267 if (new_link_address
) {
2268 struct ifnet
* scan_ifp
;
2269 bondport_ref scan_port
;
2271 /* ifbond_wait() allows port list traversal without holding the lock */
2273 /* re-program each port with the new link address */
2274 TAILQ_FOREACH(scan_port
, &ifb
->ifb_port_list
, po_port_list
) {
2275 scan_ifp
= scan_port
->po_ifp
;
2277 error
= if_siflladdr(scan_ifp
,
2278 (const struct ether_addr
*) LLADDR(ifb_sdl
));
2280 printf("bond_remove_interface(%s, %s): "
2281 "if_siflladdr (%s) failed %d\n",
2282 ifb
->ifb_name
, bondport_get_name(p
),
2283 bondport_get_name(scan_port
), error
);
2288 /* restore the port's ethernet address */
2289 error
= if_siflladdr(port_ifp
, &p
->po_saved_addr
);
2291 printf("bond_remove_interface(%s, %s): if_siflladdr failed %d\n",
2292 ifb
->ifb_name
, bondport_get_name(p
), error
);
2295 /* restore the port's MTU */
2296 error
= siocsifmtu(port_ifp
, p
->po_devmtu
.ifdm_current
);
2298 printf("bond_remove_interface(%s, %s): SIOCSIFMTU %d failed %d\n",
2299 ifb
->ifb_name
, bondport_get_name(p
),
2300 p
->po_devmtu
.ifdm_current
, error
);
2303 /* remove the bond "protocol" */
2304 bond_detach_protocol(port_ifp
);
2306 /* generate link event */
2307 if (event_code
!= 0) {
2308 interface_link_event(ifp
, event_code
);
2312 ifbond_release(ifb
);
2314 ifnet_lock_exclusive(port_ifp
);
2315 port_ifp
->if_eflags
&= ~IFEF_BOND
;
2316 ifnet_lock_done(port_ifp
);
2319 ifbond_signal(ifb
, "bond_remove_interface");
2320 ifbond_release(ifb
); /* a second release for the second reference */
2325 bond_get_status(ifbond_ref ifb
, struct if_bond_req
* ibr_p
, user_addr_t datap
)
2330 struct if_bond_status_req
* ibsr
;
2331 struct if_bond_status ibs
;
2334 ibsr
= &(ibr_p
->ibr_ibru
.ibru_status
);
2335 if (ibsr
->ibsr_version
!= IF_BOND_STATUS_REQ_VERSION
) {
2338 ibsr
->ibsr_key
= ifb
->ifb_key
;
2339 ibsr
->ibsr_total
= ifb
->ifb_port_count
;
2340 dst
= proc_is64bit(current_proc())
2341 ? ibsr
->ibsr_ibsru
.ibsru_buffer64
2342 : CAST_USER_ADDR_T(ibsr
->ibsr_ibsru
.ibsru_buffer32
);
2343 if (dst
== USER_ADDR_NULL
) {
2344 /* just want to know how many there are */
2347 if (ibsr
->ibsr_count
< 0) {
2350 count
= (ifb
->ifb_port_count
< ibsr
->ibsr_count
)
2351 ? ifb
->ifb_port_count
: ibsr
->ibsr_count
;
2352 TAILQ_FOREACH(port
, &ifb
->ifb_port_list
, po_port_list
) {
2353 struct if_bond_partner_state
* ibps_p
;
2354 partner_state_ref ps
;
2359 bzero(&ibs
, sizeof(ibs
));
2360 strncpy(ibs
.ibs_if_name
, port
->po_name
, sizeof(ibs
.ibs_if_name
));
2361 ibs
.ibs_port_priority
= port
->po_priority
;
2362 ibs
.ibs_state
= port
->po_actor_state
;
2363 ibs
.ibs_selected_state
= port
->po_selected
;
2364 ps
= &port
->po_partner_state
;
2365 ibps_p
= &ibs
.ibs_partner_state
;
2366 ibps_p
->ibps_system
= ps
->ps_lag_info
.li_system
;
2367 ibps_p
->ibps_system_priority
= ps
->ps_lag_info
.li_system_priority
;
2368 ibps_p
->ibps_key
= ps
->ps_lag_info
.li_key
;
2369 ibps_p
->ibps_port
= ps
->ps_port
;
2370 ibps_p
->ibps_port_priority
= ps
->ps_port_priority
;
2371 ibps_p
->ibps_state
= ps
->ps_state
;
2372 error
= copyout(&ibs
, dst
, sizeof(ibs
));
2382 error
= copyout(ibr_p
, datap
, sizeof(*ibr_p
));
2385 (void)copyout(ibr_p
, datap
, sizeof(*ibr_p
));
2391 bond_set_promisc(__unused
struct ifnet
*ifp
)
2395 ifbond_ref ifb
= ifp
->if_private
;
2398 if ((ifp
->if_flags
& IFF_PROMISC
) != 0) {
2399 if ((ifb
->ifb_flags
& IFBF_PROMISC
) == 0) {
2400 error
= ifnet_set_promiscuous(ifb
->ifb_p
, 1);
2402 ifb
->ifb_flags
|= IFBF_PROMISC
;
2405 if ((ifb
->ifb_flags
& IFBF_PROMISC
) != 0) {
2406 error
= ifnet_set_promiscuous(ifb
->ifb_p
, 0);
2408 ifb
->ifb_flags
&= ~IFBF_PROMISC
;
2416 bond_get_mtu_values(ifbond_ref ifb
, int * ret_min
, int * ret_max
)
2422 if (TAILQ_FIRST(&ifb
->ifb_port_list
) != NULL
) {
2423 mtu_min
= IF_MINMTU
;
2425 TAILQ_FOREACH(p
, &ifb
->ifb_port_list
, po_port_list
) {
2426 struct ifdevmtu
* devmtu_p
= &p
->po_devmtu
;
2428 if (devmtu_p
->ifdm_min
> mtu_min
) {
2429 mtu_min
= devmtu_p
->ifdm_min
;
2431 if (mtu_max
== 0 || devmtu_p
->ifdm_max
< mtu_max
) {
2432 mtu_max
= devmtu_p
->ifdm_max
;
2441 bond_set_mtu_on_ports(ifbond_ref ifb
, int mtu
)
2446 TAILQ_FOREACH(p
, &ifb
->ifb_port_list
, po_port_list
) {
2447 error
= siocsifmtu(p
->po_ifp
, mtu
);
2449 printf("if_bond(%s): SIOCSIFMTU %s failed, %d\n",
2450 ifb
->ifb_name
, bondport_get_name(p
), error
);
2458 bond_set_mtu(struct ifnet
* ifp
, int mtu
, int isdevmtu
)
2468 ifb
= (ifbond_ref
)ifp
->if_private
;
2469 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2470 error
= (ifb
== NULL
) ? EOPNOTSUPP
: EBUSY
;
2474 ifbond_wait(ifb
, "bond_set_mtu");
2477 if (ifp
->if_private
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2481 bond_get_mtu_values(ifb
, &mtu_min
, &mtu_max
);
2482 if (mtu
> mtu_max
) {
2486 if (mtu
< mtu_min
&& (isdevmtu
== 0 || mtu
!= 0)) {
2487 /* allow SIOCSIFALTMTU to set the mtu to 0 */
2492 new_max
= (mtu
> (int)ifp
->if_mtu
) ? mtu
: (int)ifp
->if_mtu
;
2495 new_max
= (mtu
> ifb
->ifb_altmtu
) ? mtu
: ifb
->ifb_altmtu
;
2497 old_max
= ((int)ifp
->if_mtu
> ifb
->ifb_altmtu
)
2498 ? (int)ifp
->if_mtu
: ifb
->ifb_altmtu
;
2499 if (new_max
!= old_max
) {
2500 /* we can safely walk the list of port without the lock held */
2502 error
= bond_set_mtu_on_ports(ifb
, new_max
);
2504 /* try our best to back out of it */
2505 (void)bond_set_mtu_on_ports(ifb
, old_max
);
2511 ifb
->ifb_altmtu
= mtu
;
2519 ifbond_signal(ifb
, "bond_set_mtu");
2520 ifbond_release(ifb
);
2528 bond_ioctl(struct ifnet
*ifp
, u_int32_t cmd
, void * data
)
2531 struct if_bond_req ibr
;
2532 struct ifaddr
* ifa
;
2535 struct ifmediareq64
*ifmr
;
2536 struct ifnet
* port_ifp
= NULL
;
2537 user_addr_t user_addr
;
2539 if (ifp
->if_type
!= IFT_IEEE8023ADLAG
) {
2540 return (EOPNOTSUPP
);
2542 ifr
= (struct ifreq
*)data
;
2543 ifa
= (struct ifaddr
*)data
;
2547 ifnet_set_flags(ifp
, IFF_UP
, IFF_UP
);
2550 case SIOCGIFMEDIA64
:
2553 ifb
= (ifbond_ref
)ifp
->if_private
;
2554 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2556 return (ifb
== NULL
? EOPNOTSUPP
: EBUSY
);
2558 ifmr
= (struct ifmediareq64
*)data
;
2559 ifmr
->ifm_current
= IFM_ETHER
;
2561 ifmr
->ifm_status
= IFM_AVALID
;
2562 ifmr
->ifm_active
= IFM_ETHER
;
2563 ifmr
->ifm_count
= 1;
2564 if (ifb
->ifb_active_lag
!= NULL
) {
2565 ifmr
->ifm_active
= ifb
->ifb_active_lag
->lag_active_media
;
2566 ifmr
->ifm_status
|= IFM_ACTIVE
;
2569 user_addr
= (cmd
== SIOCGIFMEDIA64
)
2570 ? ifmr
->ifm_ifmu
.ifmu_ulist64
2571 : CAST_USER_ADDR_T(ifmr
->ifm_ifmu
.ifmu_ulist32
);
2572 if (user_addr
!= USER_ADDR_NULL
) {
2573 error
= copyout(&ifmr
->ifm_current
,
2580 /* XXX send the SIFMEDIA to all children? Or force autoselect? */
2586 ifb
= (ifbond_ref
)ifp
->if_private
;
2587 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2589 error
= (ifb
== NULL
) ? EOPNOTSUPP
: EBUSY
;
2592 ifr
->ifr_devmtu
.ifdm_current
= bond_device_mtu(ifp
, ifb
);
2593 bond_get_mtu_values(ifb
, &ifr
->ifr_devmtu
.ifdm_min
,
2594 &ifr
->ifr_devmtu
.ifdm_max
);
2600 ifb
= (ifbond_ref
)ifp
->if_private
;
2601 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2603 error
= (ifb
== NULL
) ? EOPNOTSUPP
: EBUSY
;
2606 ifr
->ifr_mtu
= ifb
->ifb_altmtu
;
2611 error
= bond_set_mtu(ifp
, ifr
->ifr_mtu
, 1);
2615 error
= bond_set_mtu(ifp
, ifr
->ifr_mtu
, 0);
2619 user_addr
= proc_is64bit(current_proc())
2620 ? ifr
->ifr_data64
: CAST_USER_ADDR_T(ifr
->ifr_data
);
2621 error
= copyin(user_addr
, &ibr
, sizeof(ibr
));
2625 switch (ibr
.ibr_op
) {
2626 case IF_BOND_OP_ADD_INTERFACE
:
2627 case IF_BOND_OP_REMOVE_INTERFACE
:
2628 /* XXX ifunit() needs to return a reference on the ifp */
2629 port_ifp
= ifunit(ibr
.ibr_ibru
.ibru_if_name
);
2630 if (port_ifp
== NULL
) {
2634 if (port_ifp
->if_type
!= IFT_ETHER
) {
2635 error
= EPROTONOSUPPORT
;
2639 case IF_BOND_OP_SET_VERBOSE
:
2648 switch (ibr
.ibr_op
) {
2649 case IF_BOND_OP_ADD_INTERFACE
:
2650 error
= bond_add_interface(ifp
, port_ifp
);
2652 case IF_BOND_OP_REMOVE_INTERFACE
:
2654 ifb
= (ifbond_ref
)ifp
->if_private
;
2655 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2657 return (ifb
== NULL
? EOPNOTSUPP
: EBUSY
);
2659 error
= bond_remove_interface(ifb
, port_ifp
);
2662 case IF_BOND_OP_SET_VERBOSE
:
2664 if (g_bond
== NULL
) {
2669 g_bond
->verbose
= ibr
.ibr_ibru
.ibru_int_val
;
2676 user_addr
= proc_is64bit(current_proc())
2677 ? ifr
->ifr_data64
: CAST_USER_ADDR_T(ifr
->ifr_data
);
2678 error
= copyin(user_addr
, &ibr
, sizeof(ibr
));
2682 switch (ibr
.ibr_op
) {
2683 case IF_BOND_OP_GET_STATUS
:
2693 ifb
= (ifbond_ref
)ifp
->if_private
;
2694 if (ifb
== NULL
|| ifbond_flags_if_detaching(ifb
)) {
2696 return (ifb
== NULL
? EOPNOTSUPP
: EBUSY
);
2698 switch (ibr
.ibr_op
) {
2699 case IF_BOND_OP_GET_STATUS
:
2700 error
= bond_get_status(ifb
, &ibr
, user_addr
);
2711 /* enable/disable promiscuous mode */
2713 error
= bond_set_promisc(ifp
);
2719 error
= bond_setmulti(ifp
);
2728 bond_if_free(struct ifnet
* ifp
)
2736 ifb
= (ifbond_ref
)ifp
->if_private
;
2741 ifp
->if_private
= NULL
;
2742 ifbond_release(ifb
);
2744 dlil_if_release(ifp
);
2749 bond_event(struct ifnet
* port_ifp
, struct kev_msg
* event
)
2751 struct ifnet
* bond_ifp
= NULL
;
2754 struct media_info media_info
;
2756 if (event
->vendor_code
!= KEV_VENDOR_APPLE
2757 || event
->kev_class
!= KEV_NETWORK_CLASS
2758 || event
->kev_subclass
!= KEV_DL_SUBCLASS
) {
2761 switch (event
->event_code
) {
2762 case KEV_DL_IF_DETACHING
:
2764 case KEV_DL_LINK_OFF
:
2765 case KEV_DL_LINK_ON
:
2766 media_info
= interface_media_info(port_ifp
);
2772 p
= bond_lookup_port(port_ifp
);
2777 switch (event
->event_code
) {
2778 case KEV_DL_IF_DETACHING
:
2779 bond_remove_interface(p
->po_bond
, p
->po_ifp
);
2781 case KEV_DL_LINK_OFF
:
2782 case KEV_DL_LINK_ON
:
2783 p
->po_media_info
= media_info
;
2784 if (p
->po_enabled
) {
2785 bondport_link_status_changed(p
);
2789 /* generate a link-event */
2790 if (ifbond_selection(p
->po_bond
)) {
2791 event_code
= (p
->po_bond
->ifb_active_lag
== NULL
)
2794 /* XXX need to take a reference on bond_ifp */
2795 bond_ifp
= p
->po_bond
->ifb_ifp
;
2798 if (bond_ifp
!= NULL
) {
2799 interface_link_event(bond_ifp
, event_code
);
2805 interface_link_event(struct ifnet
* ifp
, u_long event_code
)
2808 struct kern_event_msg header
;
2810 char if_name
[IFNAMSIZ
];
2813 event
.header
.total_size
= sizeof(event
);
2814 event
.header
.vendor_code
= KEV_VENDOR_APPLE
;
2815 event
.header
.kev_class
= KEV_NETWORK_CLASS
;
2816 event
.header
.kev_subclass
= KEV_DL_SUBCLASS
;
2817 event
.header
.event_code
= event_code
;
2818 event
.header
.event_data
[0] = ifp
->if_family
;
2819 event
.unit
= (u_long
) ifp
->if_unit
;
2820 strncpy(event
.if_name
, ifp
->if_name
, IFNAMSIZ
);
2821 dlil_event(ifp
, &event
.header
);
2826 * Function: bond_attach_protocol
2828 * Attach a DLIL protocol to the interface.
2830 * The ethernet demux special cases to always return PF_BOND if the
2831 * interface is bonded. That means we receive all traffic from that
2832 * interface without passing any of the traffic to any other attached
2836 bond_attach_protocol(struct ifnet
*ifp
)
2839 struct dlil_proto_reg_str reg
;
2841 bzero(®
, sizeof(reg
));
2842 TAILQ_INIT(®
.demux_desc_head
);
2843 reg
.interface_family
= ifp
->if_family
;
2844 reg
.unit_number
= ifp
->if_unit
;
2845 reg
.input
= bond_input
;
2846 reg
.event
= bond_event
;
2847 reg
.protocol_family
= PF_BOND
;
2849 error
= dlil_attach_protocol(®
);
2851 printf("bond over %s%d: dlil_attach_protocol failed, %d\n",
2852 ifp
->if_name
, ifp
->if_unit
, error
);
2858 * Function: bond_detach_protocol
2860 * Detach our DLIL protocol from an interface
2863 bond_detach_protocol(struct ifnet
*ifp
)
2867 error
= dlil_detach_protocol(ifp
, PF_BOND
);
2869 printf("bond over %s%d: dlil_detach_protocol failed, %d\n",
2870 ifp
->if_name
, ifp
->if_unit
, error
);
2876 * DLIL interface family functions
2878 extern int ether_add_if(struct ifnet
*ifp
);
2879 extern int ether_del_if(struct ifnet
*ifp
);
2880 extern int ether_init_if(struct ifnet
*ifp
);
2881 extern int ether_add_proto_old(struct ifnet
*ifp
, u_long protocol_family
,
2882 struct ddesc_head_str
*desc_head
);
2884 extern int ether_attach_inet(struct ifnet
*ifp
, u_long protocol_family
);
2885 extern int ether_detach_inet(struct ifnet
*ifp
, u_long protocol_family
);
2886 extern int ether_attach_inet6(struct ifnet
*ifp
, u_long protocol_family
);
2887 extern int ether_detach_inet6(struct ifnet
*ifp
, u_long protocol_family
);
2889 __private_extern__
int
2890 bond_family_init(void)
2893 struct dlil_ifmod_reg_str ifmod_reg
;
2895 bzero(&ifmod_reg
, sizeof(ifmod_reg
));
2896 ifmod_reg
.add_if
= ether_add_if
;
2897 ifmod_reg
.del_if
= ether_del_if
;
2898 ifmod_reg
.init_if
= NULL
;
2899 ifmod_reg
.add_proto
= ether_add_proto_old
;
2900 ifmod_reg
.del_proto
= ether_del_proto
;
2901 ifmod_reg
.ifmod_ioctl
= ether_ioctl
;
2902 ifmod_reg
.shutdown
= NULL
;
2904 if (dlil_reg_if_modules(APPLE_IF_FAM_BOND
, &ifmod_reg
)) {
2905 printf("WARNING: bond_family_init -- "
2906 "Can't register if family modules\n");
2911 error
= dlil_reg_proto_module(PF_INET
, APPLE_IF_FAM_BOND
,
2915 printf("bond: dlil_reg_proto_module failed for AF_INET6 error=%d\n",
2920 error
= dlil_reg_proto_module(PF_INET6
, APPLE_IF_FAM_BOND
,
2922 ether_detach_inet6
);
2924 printf("bond: dlil_reg_proto_module failed for AF_INET6 error=%d\n",
2928 bond_clone_attach();
2940 ** LACP ifbond_list routines
2943 ifbond_list_find_moved_port(bondport_ref rx_port
,
2944 const lacp_actor_partner_tlv_ref atlv
)
2948 partner_state_ref ps
;
2951 TAILQ_FOREACH(bond
, &g_bond
->ifbond_list
, ifb_bond_list
) {
2952 TAILQ_FOREACH(p
, &bond
->ifb_port_list
, po_port_list
) {
2955 /* no point in comparing against ourselves */
2958 if (p
->po_receive_state
!= ReceiveState_PORT_DISABLED
) {
2959 /* it's not clear that we should be checking this */
2962 ps
= &p
->po_partner_state
;
2963 if (lacp_actor_partner_state_defaulted(ps
->ps_state
)) {
2966 ps_li
= &ps
->ps_lag_info
;
2967 if (ps
->ps_port
== lacp_actor_partner_tlv_get_port(atlv
)
2968 && bcmp(&ps_li
->li_system
, atlv
->lap_system
,
2969 sizeof(ps_li
->li_system
)) == 0) {
2970 if (g_bond
->verbose
) {
2971 timestamp_printf("System " EA_FORMAT
2972 " Port 0x%x moved from %s to %s\n",
2973 EA_LIST(&ps_li
->li_system
), ps
->ps_port
,
2974 bondport_get_name(p
),
2975 bondport_get_name(rx_port
));
2985 ** LACP ifbond, LAG routines
2989 ifbond_selection(ifbond_ref bond
)
2991 int all_ports_ready
= 0;
2992 int active_media
= 0;
2994 int lag_changed
= 0;
2998 lag
= ifbond_find_best_LAG(bond
, &active_media
);
2999 if (lag
!= bond
->ifb_active_lag
) {
3000 if (bond
->ifb_active_lag
!= NULL
) {
3001 ifbond_deactivate_LAG(bond
, bond
->ifb_active_lag
);
3002 bond
->ifb_active_lag
= NULL
;
3004 bond
->ifb_active_lag
= lag
;
3006 ifbond_activate_LAG(bond
, lag
, active_media
);
3010 else if (lag
!= NULL
) {
3011 if (lag
->lag_active_media
!= active_media
) {
3012 if (g_bond
->verbose
) {
3013 timestamp_printf("LAG PORT SPEED CHANGED from %d to %d\n",
3014 link_speed(lag
->lag_active_media
),
3015 link_speed(active_media
));
3017 ifbond_deactivate_LAG(bond
, lag
);
3018 ifbond_activate_LAG(bond
, lag
, active_media
);
3023 port_speed
= link_speed(active_media
);
3024 all_ports_ready
= ifbond_all_ports_ready(bond
);
3026 TAILQ_FOREACH(p
, &bond
->ifb_port_list
, po_port_list
) {
3027 if (lag
!= NULL
&& p
->po_lag
== lag
3028 && media_speed(&p
->po_media_info
) == port_speed
3029 && (p
->po_mux_state
== MuxState_DETACHED
3030 || p
->po_selected
== SelectedState_SELECTED
3031 || p
->po_selected
== SelectedState_STANDBY
)
3032 && bondport_aggregatable(p
)) {
3033 if (bond
->ifb_max_active
> 0) {
3034 if (lag
->lag_selected_port_count
< bond
->ifb_max_active
) {
3035 if (p
->po_selected
== SelectedState_STANDBY
3036 || p
->po_selected
== SelectedState_UNSELECTED
) {
3037 bondport_set_selected(p
, SelectedState_SELECTED
);
3040 else if (p
->po_selected
== SelectedState_UNSELECTED
) {
3041 bondport_set_selected(p
, SelectedState_STANDBY
);
3045 bondport_set_selected(p
, SelectedState_SELECTED
);
3048 if (bondport_flags_selected_changed(p
)) {
3049 bondport_flags_clear_selected_changed(p
);
3050 bondport_mux_machine(p
, LAEventSelectedChange
, NULL
);
3053 && bondport_flags_ready(p
)
3054 && p
->po_mux_state
== MuxState_WAITING
) {
3055 bondport_mux_machine(p
, LAEventReady
, NULL
);
3057 bondport_transmit_machine(p
, LAEventStart
, NULL
);
3059 return (lag_changed
);
3063 ifbond_find_best_LAG(ifbond_ref bond
, int * active_media
)
3065 int best_active
= 0;
3066 LAG_ref best_lag
= NULL
;
3071 if (bond
->ifb_active_lag
!= NULL
) {
3072 best_lag
= bond
->ifb_active_lag
;
3073 best_count
= LAG_get_aggregatable_port_count(best_lag
, &best_active
);
3074 if (bond
->ifb_max_active
> 0
3075 && best_count
> bond
->ifb_max_active
) {
3076 best_count
= bond
->ifb_max_active
;
3078 best_speed
= link_speed(best_active
);
3080 TAILQ_FOREACH(lag
, &bond
->ifb_lag_list
, lag_list
) {
3085 if (lag
== bond
->ifb_active_lag
) {
3086 /* we've already computed it */
3089 count
= LAG_get_aggregatable_port_count(lag
, &active
);
3093 if (bond
->ifb_max_active
> 0
3094 && count
> bond
->ifb_max_active
) {
3095 /* if there's a limit, don't count extra links */
3096 count
= bond
->ifb_max_active
;
3098 speed
= link_speed(active
);
3099 if ((count
* speed
) > (best_count
* best_speed
)) {
3102 best_active
= active
;
3106 if (best_count
== 0) {
3109 *active_media
= best_active
;
3114 ifbond_deactivate_LAG(__unused ifbond_ref bond
, LAG_ref lag
)
3118 TAILQ_FOREACH(p
, &lag
->lag_port_list
, po_lag_port_list
) {
3119 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3125 ifbond_activate_LAG(ifbond_ref bond
, LAG_ref lag
, int active_media
)
3130 if (bond
->ifb_max_active
> 0) {
3131 need
= bond
->ifb_max_active
;
3133 lag
->lag_active_media
= active_media
;
3134 TAILQ_FOREACH(p
, &lag
->lag_port_list
, po_lag_port_list
) {
3135 if (bondport_aggregatable(p
) == 0) {
3136 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3138 else if (media_speed(&p
->po_media_info
) != link_speed(active_media
)) {
3139 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3141 else if (p
->po_mux_state
== MuxState_DETACHED
) {
3142 if (bond
->ifb_max_active
> 0) {
3144 bondport_set_selected(p
, SelectedState_SELECTED
);
3148 bondport_set_selected(p
, SelectedState_STANDBY
);
3152 bondport_set_selected(p
, SelectedState_SELECTED
);
3156 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3164 ifbond_set_max_active(ifbond_ref bond
, int max_active
)
3166 LAG_ref lag
= bond
->ifb_active_lag
;
3168 bond
->ifb_max_active
= max_active
;
3169 if (bond
->ifb_max_active
<= 0 || lag
== NULL
) {
3172 if (lag
->lag_selected_port_count
> bond
->ifb_max_active
) {
3176 remove_count
= lag
->lag_selected_port_count
- bond
->ifb_max_active
;
3177 TAILQ_FOREACH(p
, &lag
->lag_port_list
, po_lag_port_list
) {
3178 if (p
->po_selected
== SelectedState_SELECTED
) {
3179 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3181 if (remove_count
== 0) {
3192 ifbond_all_ports_ready(ifbond_ref bond
)
3197 if (bond
->ifb_active_lag
== NULL
) {
3200 TAILQ_FOREACH(p
, &bond
->ifb_active_lag
->lag_port_list
, po_lag_port_list
) {
3201 if (p
->po_mux_state
== MuxState_WAITING
3202 && p
->po_selected
== SelectedState_SELECTED
) {
3203 if (bondport_flags_ready(p
) == 0) {
3207 /* note that there was at least one ready port */
3214 ifbond_all_ports_attached(ifbond_ref bond
, bondport_ref this_port
)
3218 TAILQ_FOREACH(p
, &bond
->ifb_port_list
, po_port_list
) {
3219 if (this_port
== p
) {
3222 if (bondport_flags_mux_attached(p
) == 0) {
3230 ifbond_get_LAG_matching_port(ifbond_ref bond
, bondport_ref p
)
3234 TAILQ_FOREACH(lag
, &bond
->ifb_lag_list
, lag_list
) {
3235 if (bcmp(&lag
->lag_info
, &p
->po_partner_state
.ps_lag_info
,
3236 sizeof(lag
->lag_info
)) == 0) {
3244 LAG_get_aggregatable_port_count(LAG_ref lag
, int * active_media
)
3254 TAILQ_FOREACH(p
, &lag
->lag_port_list
, po_lag_port_list
) {
3255 if (bondport_aggregatable(p
)) {
3258 this_speed
= media_speed(&p
->po_media_info
);
3259 if (this_speed
== 0) {
3262 if (this_speed
> speed
) {
3263 active
= p
->po_media_info
.mi_active
;
3267 else if (this_speed
== speed
) {
3272 *active_media
= active
;
3278 ** LACP bondport routines
3281 bondport_link_status_changed(bondport_ref p
)
3283 ifbond_ref bond
= p
->po_bond
;
3285 if (g_bond
->verbose
) {
3286 if (media_active(&p
->po_media_info
)) {
3287 timestamp_printf("[%s] Link UP %d Mbit/s %s duplex\n",
3288 bondport_get_name(p
),
3289 media_speed(&p
->po_media_info
),
3290 media_full_duplex(&p
->po_media_info
)
3294 timestamp_printf("[%s] Link DOWN\n", bondport_get_name(p
));
3297 if (media_active(&p
->po_media_info
)
3298 && bond
->ifb_active_lag
!= NULL
3299 && p
->po_lag
== bond
->ifb_active_lag
3300 && p
->po_selected
!= SelectedState_UNSELECTED
) {
3301 if (media_speed(&p
->po_media_info
) != p
->po_lag
->lag_active_media
) {
3302 if (g_bond
->verbose
) {
3303 timestamp_printf("[%s] Port speed %d differs from LAG %d\n",
3304 bondport_get_name(p
),
3305 media_speed(&p
->po_media_info
),
3306 link_speed(p
->po_lag
->lag_active_media
));
3308 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3311 bondport_receive_machine(p
, LAEventMediaChange
, NULL
);
3312 bondport_mux_machine(p
, LAEventMediaChange
, NULL
);
3313 bondport_periodic_transmit_machine(p
, LAEventMediaChange
, NULL
);
3319 bondport_aggregatable(bondport_ref p
)
3321 partner_state_ref ps
= &p
->po_partner_state
;
3323 if (lacp_actor_partner_state_aggregatable(p
->po_actor_state
) == 0
3324 || lacp_actor_partner_state_aggregatable(ps
->ps_state
) == 0) {
3325 /* we and/or our partner are individual */
3328 if (p
->po_lag
== NULL
) {
3331 switch (p
->po_receive_state
) {
3333 if (g_bond
->verbose
) {
3334 timestamp_printf("[%s] Port is not selectable\n",
3335 bondport_get_name(p
));
3338 case ReceiveState_CURRENT
:
3339 case ReceiveState_EXPIRED
:
3346 bondport_matches_LAG(bondport_ref p
, LAG_ref lag
)
3348 LAG_info_ref lag_li
;
3349 partner_state_ref ps
;
3352 ps
= &p
->po_partner_state
;
3353 ps_li
= &ps
->ps_lag_info
;
3354 lag_li
= &lag
->lag_info
;
3355 if (ps_li
->li_system_priority
== lag_li
->li_system_priority
3356 && ps_li
->li_key
== lag_li
->li_key
3357 && (bcmp(&ps_li
->li_system
, &lag_li
->li_system
,
3358 sizeof(lag_li
->li_system
))
3366 bondport_remove_from_LAG(bondport_ref p
)
3369 ifbond_ref bond
= p
->po_bond
;
3370 LAG_ref lag
= p
->po_lag
;
3375 TAILQ_REMOVE(&lag
->lag_port_list
, p
, po_lag_port_list
);
3376 if (g_bond
->verbose
) {
3377 timestamp_printf("[%s] Removed from LAG (0x%04x," EA_FORMAT
3379 bondport_get_name(p
),
3380 lag
->lag_info
.li_system_priority
,
3381 EA_LIST(&lag
->lag_info
.li_system
),
3382 lag
->lag_info
.li_key
);
3385 lag
->lag_port_count
--;
3386 if (lag
->lag_port_count
> 0) {
3387 return (bond
->ifb_active_lag
== lag
);
3389 if (g_bond
->verbose
) {
3390 timestamp_printf("Key 0x%04x: LAG Released (%04x," EA_FORMAT
3393 lag
->lag_info
.li_system_priority
,
3394 EA_LIST(&lag
->lag_info
.li_system
),
3395 lag
->lag_info
.li_key
);
3397 TAILQ_REMOVE(&bond
->ifb_lag_list
, lag
, lag_list
);
3398 if (bond
->ifb_active_lag
== lag
) {
3399 bond
->ifb_active_lag
= NULL
;
3403 return (active_lag
);
3407 bondport_add_to_LAG(bondport_ref p
, LAG_ref lag
)
3409 TAILQ_INSERT_TAIL(&lag
->lag_port_list
, p
, po_lag_port_list
);
3411 lag
->lag_port_count
++;
3412 if (g_bond
->verbose
) {
3413 timestamp_printf("[%s] Added to LAG (0x%04x," EA_FORMAT
"0x%04x)\n",
3414 bondport_get_name(p
),
3415 lag
->lag_info
.li_system_priority
,
3416 EA_LIST(&lag
->lag_info
.li_system
),
3417 lag
->lag_info
.li_key
);
3423 bondport_assign_to_LAG(bondport_ref p
)
3425 ifbond_ref bond
= p
->po_bond
;
3428 if (lacp_actor_partner_state_defaulted(p
->po_actor_state
)) {
3429 bondport_remove_from_LAG(p
);
3434 if (bondport_matches_LAG(p
, lag
)) {
3438 bondport_remove_from_LAG(p
);
3440 lag
= ifbond_get_LAG_matching_port(bond
, p
);
3442 bondport_add_to_LAG(p
, lag
);
3445 lag
= (LAG_ref
)_MALLOC(sizeof(*lag
), M_BOND
, M_WAITOK
);
3446 TAILQ_INIT(&lag
->lag_port_list
);
3447 lag
->lag_port_count
= 0;
3448 lag
->lag_selected_port_count
= 0;
3449 lag
->lag_info
= p
->po_partner_state
.ps_lag_info
;
3450 TAILQ_INSERT_TAIL(&bond
->ifb_lag_list
, lag
, lag_list
);
3451 if (g_bond
->verbose
) {
3452 timestamp_printf("Key 0x%04x: LAG Created (0x%04x," EA_FORMAT
3455 lag
->lag_info
.li_system_priority
,
3456 EA_LIST(&lag
->lag_info
.li_system
),
3457 lag
->lag_info
.li_key
);
3459 bondport_add_to_LAG(p
, lag
);
3464 bondport_receive_lacpdu(bondport_ref p
, lacpdu_ref in_lacpdu_p
)
3466 bondport_ref moved_port
;
3469 = ifbond_list_find_moved_port(p
, (const lacp_actor_partner_tlv_ref
)
3470 &in_lacpdu_p
->la_actor_tlv
);
3471 if (moved_port
!= NULL
) {
3472 bondport_receive_machine(moved_port
, LAEventPortMoved
, NULL
);
3474 bondport_receive_machine(p
, LAEventPacket
, in_lacpdu_p
);
3475 bondport_mux_machine(p
, LAEventPacket
, in_lacpdu_p
);
3476 bondport_periodic_transmit_machine(p
, LAEventPacket
, in_lacpdu_p
);
3481 bondport_set_selected(bondport_ref p
, SelectedState s
)
3483 if (s
!= p
->po_selected
) {
3484 ifbond_ref bond
= p
->po_bond
;
3485 LAG_ref lag
= p
->po_lag
;
3487 bondport_flags_set_selected_changed(p
);
3488 if (lag
!= NULL
&& bond
->ifb_active_lag
== lag
) {
3489 if (p
->po_selected
== SelectedState_SELECTED
) {
3490 lag
->lag_selected_port_count
--;
3492 else if (s
== SelectedState_SELECTED
) {
3493 lag
->lag_selected_port_count
++;
3495 if (g_bond
->verbose
) {
3496 timestamp_printf("[%s] SetSelected: %s (was %s)\n",
3497 bondport_get_name(p
),
3498 SelectedStateString(s
),
3499 SelectedStateString(p
->po_selected
));
3512 bondport_UpdateDefaultSelected(bondport_ref p
)
3514 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3519 bondport_RecordDefault(bondport_ref p
)
3521 bzero(&p
->po_partner_state
, sizeof(p
->po_partner_state
));
3523 = lacp_actor_partner_state_set_defaulted(p
->po_actor_state
);
3524 bondport_assign_to_LAG(p
);
3529 bondport_UpdateSelected(bondport_ref p
, lacpdu_ref lacpdu_p
)
3531 lacp_actor_partner_tlv_ref actor
;
3532 partner_state_ref ps
;
3535 /* compare the PDU's Actor information to our Partner state */
3536 actor
= (lacp_actor_partner_tlv_ref
)lacpdu_p
->la_actor_tlv
;
3537 ps
= &p
->po_partner_state
;
3538 ps_li
= &ps
->ps_lag_info
;
3539 if (lacp_actor_partner_tlv_get_port(actor
) != ps
->ps_port
3540 || (lacp_actor_partner_tlv_get_port_priority(actor
)
3541 != ps
->ps_port_priority
)
3542 || bcmp(actor
->lap_system
, &ps_li
->li_system
, sizeof(ps_li
->li_system
))
3543 || (lacp_actor_partner_tlv_get_system_priority(actor
)
3544 != ps_li
->li_system_priority
)
3545 || (lacp_actor_partner_tlv_get_key(actor
) != ps_li
->li_key
)
3546 || (lacp_actor_partner_state_aggregatable(actor
->lap_state
)
3547 != lacp_actor_partner_state_aggregatable(ps
->ps_state
))) {
3548 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3549 if (g_bond
->verbose
) {
3550 timestamp_printf("[%s] updateSelected UNSELECTED\n",
3551 bondport_get_name(p
));
3558 bondport_RecordPDU(bondport_ref p
, lacpdu_ref lacpdu_p
)
3560 lacp_actor_partner_tlv_ref actor
;
3561 ifbond_ref bond
= p
->po_bond
;
3562 int lacp_maintain
= 0;
3563 partner_state_ref ps
;
3564 lacp_actor_partner_tlv_ref partner
;
3567 /* copy the PDU's Actor information into our Partner state */
3568 actor
= (lacp_actor_partner_tlv_ref
)lacpdu_p
->la_actor_tlv
;
3569 ps
= &p
->po_partner_state
;
3570 ps_li
= &ps
->ps_lag_info
;
3571 ps
->ps_port
= lacp_actor_partner_tlv_get_port(actor
);
3572 ps
->ps_port_priority
= lacp_actor_partner_tlv_get_port_priority(actor
);
3573 ps_li
->li_system
= *((lacp_system_ref
)actor
->lap_system
);
3574 ps_li
->li_system_priority
3575 = lacp_actor_partner_tlv_get_system_priority(actor
);
3576 ps_li
->li_key
= lacp_actor_partner_tlv_get_key(actor
);
3577 ps
->ps_state
= lacp_actor_partner_state_set_out_of_sync(actor
->lap_state
);
3579 = lacp_actor_partner_state_set_not_defaulted(p
->po_actor_state
);
3581 /* compare the PDU's Partner information to our own information */
3582 partner
= (lacp_actor_partner_tlv_ref
)lacpdu_p
->la_partner_tlv
;
3584 if (lacp_actor_partner_state_active_lacp(ps
->ps_state
)
3585 || (lacp_actor_partner_state_active_lacp(p
->po_actor_state
)
3586 && lacp_actor_partner_state_active_lacp(partner
->lap_state
))) {
3587 if (g_bond
->verbose
) {
3588 timestamp_printf("[%s] recordPDU: LACP will maintain\n",
3589 bondport_get_name(p
));
3593 if ((lacp_actor_partner_tlv_get_port(partner
)
3594 == bondport_get_index(p
))
3595 && lacp_actor_partner_tlv_get_port_priority(partner
) == p
->po_priority
3596 && bcmp(partner
->lap_system
, &g_bond
->system
,
3597 sizeof(g_bond
->system
)) == 0
3598 && (lacp_actor_partner_tlv_get_system_priority(partner
)
3599 == g_bond
->system_priority
)
3600 && lacp_actor_partner_tlv_get_key(partner
) == bond
->ifb_key
3601 && (lacp_actor_partner_state_aggregatable(partner
->lap_state
)
3602 == lacp_actor_partner_state_aggregatable(p
->po_actor_state
))
3603 && lacp_actor_partner_state_in_sync(actor
->lap_state
)
3605 ps
->ps_state
= lacp_actor_partner_state_set_in_sync(ps
->ps_state
);
3606 if (g_bond
->verbose
) {
3607 timestamp_printf("[%s] recordPDU: LACP partner in sync\n",
3608 bondport_get_name(p
));
3611 else if (lacp_actor_partner_state_aggregatable(actor
->lap_state
) == 0
3612 && lacp_actor_partner_state_in_sync(actor
->lap_state
)
3614 ps
->ps_state
= lacp_actor_partner_state_set_in_sync(ps
->ps_state
);
3615 if (g_bond
->verbose
) {
3616 timestamp_printf("[%s] recordPDU: LACP partner in sync (ind)\n",
3617 bondport_get_name(p
));
3620 bondport_assign_to_LAG(p
);
3624 static __inline__ lacp_actor_partner_state
3625 updateNTTBits(lacp_actor_partner_state s
)
3627 return (s
& (LACP_ACTOR_PARTNER_STATE_LACP_ACTIVITY
3628 | LACP_ACTOR_PARTNER_STATE_LACP_TIMEOUT
3629 | LACP_ACTOR_PARTNER_STATE_AGGREGATION
3630 | LACP_ACTOR_PARTNER_STATE_SYNCHRONIZATION
));
3634 bondport_UpdateNTT(bondport_ref p
, lacpdu_ref lacpdu_p
)
3636 ifbond_ref bond
= p
->po_bond
;
3637 lacp_actor_partner_tlv_ref partner
;
3639 /* compare the PDU's Actor information to our Partner state */
3640 partner
= (lacp_actor_partner_tlv_ref
)lacpdu_p
->la_partner_tlv
;
3641 if ((lacp_actor_partner_tlv_get_port(partner
) != bondport_get_index(p
))
3642 || lacp_actor_partner_tlv_get_port_priority(partner
) != p
->po_priority
3643 || bcmp(partner
->lap_system
, &g_bond
->system
, sizeof(g_bond
->system
))
3644 || (lacp_actor_partner_tlv_get_system_priority(partner
)
3645 != g_bond
->system_priority
)
3646 || lacp_actor_partner_tlv_get_key(partner
) != bond
->ifb_key
3647 || (updateNTTBits(partner
->lap_state
)
3648 != updateNTTBits(p
->po_actor_state
))) {
3649 bondport_flags_set_ntt(p
);
3650 if (g_bond
->verbose
) {
3651 timestamp_printf("[%s] updateNTT: Need To Transmit\n",
3652 bondport_get_name(p
));
3659 bondport_AttachMuxToAggregator(bondport_ref p
)
3661 if (bondport_flags_mux_attached(p
) == 0) {
3662 if (g_bond
->verbose
) {
3663 timestamp_printf("[%s] Attached Mux To Aggregator\n",
3664 bondport_get_name(p
));
3666 bondport_flags_set_mux_attached(p
);
3672 bondport_DetachMuxFromAggregator(bondport_ref p
)
3674 if (bondport_flags_mux_attached(p
)) {
3675 if (g_bond
->verbose
) {
3676 timestamp_printf("[%s] Detached Mux From Aggregator\n",
3677 bondport_get_name(p
));
3679 bondport_flags_clear_mux_attached(p
);
3685 bondport_enable_distributing(bondport_ref p
)
3687 if (bondport_flags_distributing(p
) == 0) {
3688 ifbond_ref bond
= p
->po_bond
;
3690 bond
->ifb_distributing_array
[bond
->ifb_distributing_count
++] = p
;
3691 if (g_bond
->verbose
) {
3692 timestamp_printf("[%s] Distribution Enabled\n",
3693 bondport_get_name(p
));
3695 bondport_flags_set_distributing(p
);
3701 bondport_disable_distributing(bondport_ref p
)
3703 if (bondport_flags_distributing(p
)) {
3704 bondport_ref
* array
;
3710 array
= bond
->ifb_distributing_array
;
3711 count
= bond
->ifb_distributing_count
;
3712 for (i
= 0; i
< count
; i
++) {
3713 if (array
[i
] == p
) {
3716 for (j
= i
; j
< (count
- 1); j
++) {
3717 array
[j
] = array
[j
+ 1];
3722 bond
->ifb_distributing_count
--;
3723 if (g_bond
->verbose
) {
3724 timestamp_printf("[%s] Distribution Disabled\n",
3725 bondport_get_name(p
));
3727 bondport_flags_clear_distributing(p
);
3733 ** Receive machine functions
3736 bondport_receive_machine_initialize(bondport_ref p
, LAEvent event
,
3739 bondport_receive_machine_port_disabled(bondport_ref p
, LAEvent event
,
3742 bondport_receive_machine_expired(bondport_ref p
, LAEvent event
,
3745 bondport_receive_machine_lacp_disabled(bondport_ref p
, LAEvent event
,
3748 bondport_receive_machine_defaulted(bondport_ref p
, LAEvent event
,
3751 bondport_receive_machine_current(bondport_ref p
, LAEvent event
,
3755 bondport_receive_machine_event(bondport_ref p
, LAEvent event
,
3758 switch (p
->po_receive_state
) {
3759 case ReceiveState_none
:
3760 bondport_receive_machine_initialize(p
, LAEventStart
, NULL
);
3762 case ReceiveState_INITIALIZE
:
3763 bondport_receive_machine_initialize(p
, event
, event_data
);
3765 case ReceiveState_PORT_DISABLED
:
3766 bondport_receive_machine_port_disabled(p
, event
, event_data
);
3768 case ReceiveState_EXPIRED
:
3769 bondport_receive_machine_expired(p
, event
, event_data
);
3771 case ReceiveState_LACP_DISABLED
:
3772 bondport_receive_machine_lacp_disabled(p
, event
, event_data
);
3774 case ReceiveState_DEFAULTED
:
3775 bondport_receive_machine_defaulted(p
, event
, event_data
);
3777 case ReceiveState_CURRENT
:
3778 bondport_receive_machine_current(p
, event
, event_data
);
3787 bondport_receive_machine(bondport_ref p
, LAEvent event
,
3792 if (p
->po_receive_state
!= ReceiveState_LACP_DISABLED
) {
3793 bondport_receive_machine_current(p
, event
, event_data
);
3796 case LAEventMediaChange
:
3797 if (media_active(&p
->po_media_info
)) {
3798 switch (p
->po_receive_state
) {
3799 case ReceiveState_PORT_DISABLED
:
3800 case ReceiveState_LACP_DISABLED
:
3801 bondport_receive_machine_port_disabled(p
, LAEventMediaChange
, NULL
);
3808 bondport_receive_machine_port_disabled(p
, LAEventStart
, NULL
);
3812 bondport_receive_machine_event(p
, event
, event_data
);
3819 bondport_receive_machine_initialize(bondport_ref p
, LAEvent event
,
3820 __unused
void * event_data
)
3824 devtimer_cancel(p
->po_current_while_timer
);
3825 if (g_bond
->verbose
) {
3826 timestamp_printf("[%s] Receive INITIALIZE\n",
3827 bondport_get_name(p
));
3829 p
->po_receive_state
= ReceiveState_INITIALIZE
;
3830 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3831 bondport_RecordDefault(p
);
3833 = lacp_actor_partner_state_set_not_expired(p
->po_actor_state
);
3834 bondport_receive_machine_port_disabled(p
, LAEventStart
, NULL
);
3843 bondport_receive_machine_port_disabled(bondport_ref p
, LAEvent event
,
3844 __unused
void * event_data
)
3846 partner_state_ref ps
;
3850 devtimer_cancel(p
->po_current_while_timer
);
3851 if (g_bond
->verbose
) {
3852 timestamp_printf("[%s] Receive PORT_DISABLED\n",
3853 bondport_get_name(p
));
3855 p
->po_receive_state
= ReceiveState_PORT_DISABLED
;
3856 ps
= &p
->po_partner_state
;
3857 ps
->ps_state
= lacp_actor_partner_state_set_out_of_sync(ps
->ps_state
);
3859 case LAEventMediaChange
:
3860 if (media_active(&p
->po_media_info
)) {
3861 if (media_full_duplex(&p
->po_media_info
)) {
3862 bondport_receive_machine_expired(p
, LAEventStart
, NULL
);
3865 bondport_receive_machine_lacp_disabled(p
, LAEventStart
, NULL
);
3868 else if (p
->po_selected
== SelectedState_SELECTED
) {
3871 if (g_bond
->verbose
) {
3872 timestamp_printf("[%s] Receive PORT_DISABLED: "
3873 "link timer started\n",
3874 bondport_get_name(p
));
3878 devtimer_set_relative(p
->po_current_while_timer
, tv
,
3879 (devtimer_timeout_func
)
3880 bondport_receive_machine_port_disabled
,
3881 (void *)LAEventTimeout
, NULL
);
3883 else if (p
->po_selected
== SelectedState_STANDBY
) {
3884 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3887 case LAEventTimeout
:
3888 if (p
->po_selected
== SelectedState_SELECTED
) {
3889 if (g_bond
->verbose
) {
3890 timestamp_printf("[%s] Receive PORT_DISABLED: "
3891 "link timer completed, marking UNSELECTED\n",
3892 bondport_get_name(p
));
3894 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3897 case LAEventPortMoved
:
3898 bondport_receive_machine_initialize(p
, LAEventStart
, NULL
);
3907 bondport_receive_machine_expired(bondport_ref p
, LAEvent event
,
3908 __unused
void * event_data
)
3910 lacp_actor_partner_state s
;
3915 devtimer_cancel(p
->po_current_while_timer
);
3916 if (g_bond
->verbose
) {
3917 timestamp_printf("[%s] Receive EXPIRED\n",
3918 bondport_get_name(p
));
3920 p
->po_receive_state
= ReceiveState_EXPIRED
;
3921 s
= p
->po_partner_state
.ps_state
;
3922 s
= lacp_actor_partner_state_set_out_of_sync(s
);
3923 s
= lacp_actor_partner_state_set_short_timeout(s
);
3924 p
->po_partner_state
.ps_state
= s
;
3926 = lacp_actor_partner_state_set_expired(p
->po_actor_state
);
3927 /* start current_while timer */
3928 tv
.tv_sec
= LACP_SHORT_TIMEOUT_TIME
;
3930 devtimer_set_relative(p
->po_current_while_timer
, tv
,
3931 (devtimer_timeout_func
)
3932 bondport_receive_machine_expired
,
3933 (void *)LAEventTimeout
, NULL
);
3936 case LAEventTimeout
:
3937 bondport_receive_machine_defaulted(p
, LAEventStart
, NULL
);
3946 bondport_receive_machine_lacp_disabled(bondport_ref p
, LAEvent event
,
3947 __unused
void * event_data
)
3949 partner_state_ref ps
;
3952 devtimer_cancel(p
->po_current_while_timer
);
3953 if (g_bond
->verbose
) {
3954 timestamp_printf("[%s] Receive LACP_DISABLED\n",
3955 bondport_get_name(p
));
3957 p
->po_receive_state
= ReceiveState_LACP_DISABLED
;
3958 bondport_set_selected(p
, SelectedState_UNSELECTED
);
3959 bondport_RecordDefault(p
);
3960 ps
= &p
->po_partner_state
;
3961 ps
->ps_state
= lacp_actor_partner_state_set_individual(ps
->ps_state
);
3963 = lacp_actor_partner_state_set_not_expired(p
->po_actor_state
);
3972 bondport_receive_machine_defaulted(bondport_ref p
, LAEvent event
,
3973 __unused
void * event_data
)
3977 devtimer_cancel(p
->po_current_while_timer
);
3978 if (g_bond
->verbose
) {
3979 timestamp_printf("[%s] Receive DEFAULTED\n",
3980 bondport_get_name(p
));
3982 p
->po_receive_state
= ReceiveState_DEFAULTED
;
3983 bondport_UpdateDefaultSelected(p
);
3984 bondport_RecordDefault(p
);
3986 = lacp_actor_partner_state_set_not_expired(p
->po_actor_state
);
3995 bondport_receive_machine_current(bondport_ref p
, LAEvent event
,
3998 partner_state_ref ps
;
4003 devtimer_cancel(p
->po_current_while_timer
);
4004 if (g_bond
->verbose
) {
4005 timestamp_printf("[%s] Receive CURRENT\n",
4006 bondport_get_name(p
));
4008 p
->po_receive_state
= ReceiveState_CURRENT
;
4009 bondport_UpdateSelected(p
, event_data
);
4010 bondport_UpdateNTT(p
, event_data
);
4011 bondport_RecordPDU(p
, event_data
);
4013 = lacp_actor_partner_state_set_not_expired(p
->po_actor_state
);
4014 bondport_assign_to_LAG(p
);
4015 /* start current_while timer */
4016 ps
= &p
->po_partner_state
;
4017 if (lacp_actor_partner_state_short_timeout(ps
->ps_state
)) {
4018 tv
.tv_sec
= LACP_SHORT_TIMEOUT_TIME
;
4021 tv
.tv_sec
= LACP_LONG_TIMEOUT_TIME
;
4024 devtimer_set_relative(p
->po_current_while_timer
, tv
,
4025 (devtimer_timeout_func
)
4026 bondport_receive_machine_current
,
4027 (void *)LAEventTimeout
, NULL
);
4029 case LAEventTimeout
:
4030 bondport_receive_machine_expired(p
, LAEventStart
, NULL
);
4039 ** Periodic Transmission machine
4043 bondport_periodic_transmit_machine(bondport_ref p
, LAEvent event
,
4044 __unused
void * event_data
)
4047 partner_state_ref ps
;
4052 if (g_bond
->verbose
) {
4053 timestamp_printf("[%s] periodic_transmit Start\n",
4054 bondport_get_name(p
));
4057 case LAEventMediaChange
:
4058 devtimer_cancel(p
->po_periodic_timer
);
4059 p
->po_periodic_interval
= 0;
4060 if (media_active(&p
->po_media_info
) == 0
4061 || media_full_duplex(&p
->po_media_info
) == 0) {
4065 /* Neither Partner nor Actor are LACP Active, no periodic tx */
4066 ps
= &p
->po_partner_state
;
4067 if (lacp_actor_partner_state_active_lacp(p
->po_actor_state
) == 0
4068 && (lacp_actor_partner_state_active_lacp(ps
->ps_state
)
4070 devtimer_cancel(p
->po_periodic_timer
);
4071 p
->po_periodic_interval
= 0;
4074 if (lacp_actor_partner_state_short_timeout(ps
->ps_state
)) {
4075 interval
= LACP_FAST_PERIODIC_TIME
;
4078 interval
= LACP_SLOW_PERIODIC_TIME
;
4080 if (p
->po_periodic_interval
!= interval
) {
4081 if (interval
== LACP_FAST_PERIODIC_TIME
4082 && p
->po_periodic_interval
== LACP_SLOW_PERIODIC_TIME
) {
4083 if (g_bond
->verbose
) {
4084 timestamp_printf("[%s] periodic_transmit:"
4085 " Need To Transmit\n",
4086 bondport_get_name(p
));
4088 bondport_flags_set_ntt(p
);
4090 p
->po_periodic_interval
= interval
;
4092 tv
.tv_sec
= interval
;
4093 devtimer_set_relative(p
->po_periodic_timer
, tv
,
4094 (devtimer_timeout_func
)
4095 bondport_periodic_transmit_machine
,
4096 (void *)LAEventTimeout
, NULL
);
4097 if (g_bond
->verbose
) {
4098 timestamp_printf("[%s] Periodic Transmission Timer: %d secs\n",
4099 bondport_get_name(p
),
4100 p
->po_periodic_interval
);
4104 case LAEventTimeout
:
4105 bondport_flags_set_ntt(p
);
4106 tv
.tv_sec
= p
->po_periodic_interval
;
4108 devtimer_set_relative(p
->po_periodic_timer
, tv
, (devtimer_timeout_func
)
4109 bondport_periodic_transmit_machine
,
4110 (void *)LAEventTimeout
, NULL
);
4111 if (g_bond
->verbose
> 1) {
4112 timestamp_printf("[%s] Periodic Transmission Timer: %d secs\n",
4113 bondport_get_name(p
), p
->po_periodic_interval
);
4126 bondport_can_transmit(bondport_ref p
, int32_t current_secs
,
4129 if (p
->po_last_transmit_secs
!= current_secs
) {
4130 p
->po_last_transmit_secs
= current_secs
;
4131 p
->po_n_transmit
= 0;
4133 if (p
->po_n_transmit
< LACP_PACKET_RATE
) {
4137 if (next_secs
!= NULL
) {
4138 *next_secs
= current_secs
+ 1;
4144 bondport_transmit_machine(bondport_ref p
, LAEvent event
,
4147 lacp_actor_partner_tlv_ref aptlv
;
4148 lacp_collector_tlv_ref ctlv
;
4149 struct timeval next_tick_time
= {0, 0};
4150 lacpdu_ref out_lacpdu_p
;
4151 packet_buffer_ref pkt
;
4152 partner_state_ref ps
;
4156 case LAEventTimeout
:
4158 if (p
->po_periodic_interval
== 0 || bondport_flags_ntt(p
) == 0) {
4161 if (event_data
!= NULL
) {
4162 /* we're going away, transmit the packet no matter what */
4164 else if (bondport_can_transmit(p
, devtimer_current_secs(),
4165 &next_tick_time
.tv_sec
) == 0) {
4166 if (devtimer_enabled(p
->po_transmit_timer
)) {
4167 if (g_bond
->verbose
> 0) {
4168 timestamp_printf("[%s] Transmit Timer Already Set\n",
4169 bondport_get_name(p
));
4173 devtimer_set_absolute(p
->po_transmit_timer
, next_tick_time
,
4174 (devtimer_timeout_func
)
4175 bondport_transmit_machine
,
4176 (void *)LAEventTimeout
, NULL
);
4177 if (g_bond
->verbose
> 0) {
4178 timestamp_printf("[%s] Transmit Timer Deadline %d secs\n",
4179 bondport_get_name(p
),
4180 next_tick_time
.tv_sec
);
4185 if (g_bond
->verbose
> 0) {
4186 if (event
== LAEventTimeout
) {
4187 timestamp_printf("[%s] Transmit Timer Complete\n",
4188 bondport_get_name(p
));
4191 pkt
= packet_buffer_allocate(sizeof(*out_lacpdu_p
));
4193 printf("[%s] Transmit: failed to allocate packet buffer\n",
4194 bondport_get_name(p
));
4197 out_lacpdu_p
= (lacpdu_ref
)packet_buffer_byteptr(pkt
);
4198 bzero(out_lacpdu_p
, sizeof(*out_lacpdu_p
));
4199 out_lacpdu_p
->la_subtype
= IEEE8023AD_SLOW_PROTO_SUBTYPE_LACP
;
4200 out_lacpdu_p
->la_version
= LACPDU_VERSION_1
;
4203 aptlv
= (lacp_actor_partner_tlv_ref
)out_lacpdu_p
->la_actor_tlv
;
4204 aptlv
->lap_tlv_type
= LACPDU_TLV_TYPE_ACTOR
;
4205 aptlv
->lap_length
= LACPDU_ACTOR_TLV_LENGTH
;
4206 *((lacp_system_ref
)aptlv
->lap_system
) = g_bond
->system
;
4207 lacp_actor_partner_tlv_set_system_priority(aptlv
,
4208 g_bond
->system_priority
);
4209 lacp_actor_partner_tlv_set_port_priority(aptlv
, p
->po_priority
);
4210 lacp_actor_partner_tlv_set_port(aptlv
, bondport_get_index(p
));
4211 lacp_actor_partner_tlv_set_key(aptlv
, p
->po_bond
->ifb_key
);
4212 aptlv
->lap_state
= p
->po_actor_state
;
4215 aptlv
= (lacp_actor_partner_tlv_ref
)out_lacpdu_p
->la_partner_tlv
;
4216 aptlv
->lap_tlv_type
= LACPDU_TLV_TYPE_PARTNER
;
4217 aptlv
->lap_length
= LACPDU_PARTNER_TLV_LENGTH
;
4218 ps
= &p
->po_partner_state
;
4219 ps_li
= &ps
->ps_lag_info
;
4220 lacp_actor_partner_tlv_set_port(aptlv
, ps
->ps_port
);
4221 lacp_actor_partner_tlv_set_port_priority(aptlv
, ps
->ps_port_priority
);
4222 *((lacp_system_ref
)aptlv
->lap_system
) = ps_li
->li_system
;
4223 lacp_actor_partner_tlv_set_system_priority(aptlv
,
4224 ps_li
->li_system_priority
);
4225 lacp_actor_partner_tlv_set_key(aptlv
, ps_li
->li_key
);
4226 aptlv
->lap_state
= ps
->ps_state
;
4229 ctlv
= (lacp_collector_tlv_ref
)out_lacpdu_p
->la_collector_tlv
;
4230 ctlv
->lac_tlv_type
= LACPDU_TLV_TYPE_COLLECTOR
;
4231 ctlv
->lac_length
= LACPDU_COLLECTOR_TLV_LENGTH
;
4233 bondport_slow_proto_transmit(p
, pkt
);
4234 bondport_flags_clear_ntt(p
);
4235 if (g_bond
->verbose
> 0) {
4236 timestamp_printf("[%s] Transmit Packet %d\n",
4237 bondport_get_name(p
), p
->po_n_transmit
);
4247 ** Mux machine functions
4251 bondport_mux_machine_detached(bondport_ref p
, LAEvent event
,
4254 bondport_mux_machine_waiting(bondport_ref p
, LAEvent event
,
4257 bondport_mux_machine_attached(bondport_ref p
, LAEvent event
,
4261 bondport_mux_machine_collecting_distributing(bondport_ref p
, LAEvent event
,
4265 bondport_mux_machine(bondport_ref p
, LAEvent event
, void * event_data
)
4267 switch (p
->po_mux_state
) {
4269 bondport_mux_machine_detached(p
, LAEventStart
, NULL
);
4271 case MuxState_DETACHED
:
4272 bondport_mux_machine_detached(p
, event
, event_data
);
4274 case MuxState_WAITING
:
4275 bondport_mux_machine_waiting(p
, event
, event_data
);
4277 case MuxState_ATTACHED
:
4278 bondport_mux_machine_attached(p
, event
, event_data
);
4280 case MuxState_COLLECTING_DISTRIBUTING
:
4281 bondport_mux_machine_collecting_distributing(p
, event
, event_data
);
4290 bondport_mux_machine_detached(bondport_ref p
, LAEvent event
,
4291 __unused
void * event_data
)
4293 lacp_actor_partner_state s
;
4297 devtimer_cancel(p
->po_wait_while_timer
);
4298 if (g_bond
->verbose
) {
4299 timestamp_printf("[%s] Mux DETACHED\n",
4300 bondport_get_name(p
));
4302 p
->po_mux_state
= MuxState_DETACHED
;
4303 bondport_flags_clear_ready(p
);
4304 bondport_DetachMuxFromAggregator(p
);
4305 bondport_disable_distributing(p
);
4306 s
= p
->po_actor_state
;
4307 s
= lacp_actor_partner_state_set_out_of_sync(s
);
4308 s
= lacp_actor_partner_state_set_not_collecting(s
);
4309 s
= lacp_actor_partner_state_set_not_distributing(s
);
4310 p
->po_actor_state
= s
;
4311 bondport_flags_set_ntt(p
);
4313 case LAEventSelectedChange
:
4315 case LAEventMediaChange
:
4316 if (p
->po_selected
== SelectedState_SELECTED
4317 || p
->po_selected
== SelectedState_STANDBY
) {
4318 bondport_mux_machine_waiting(p
, LAEventStart
, NULL
);
4328 bondport_mux_machine_waiting(bondport_ref p
, LAEvent event
,
4329 __unused
void * event_data
)
4335 devtimer_cancel(p
->po_wait_while_timer
);
4336 if (g_bond
->verbose
) {
4337 timestamp_printf("[%s] Mux WAITING\n",
4338 bondport_get_name(p
));
4340 p
->po_mux_state
= MuxState_WAITING
;
4343 case LAEventSelectedChange
:
4344 if (p
->po_selected
== SelectedState_UNSELECTED
) {
4345 bondport_mux_machine_detached(p
, LAEventStart
, NULL
);
4348 if (p
->po_selected
== SelectedState_STANDBY
) {
4349 devtimer_cancel(p
->po_wait_while_timer
);
4350 /* wait until state changes to SELECTED */
4351 if (g_bond
->verbose
) {
4352 timestamp_printf("[%s] Mux WAITING: Standby\n",
4353 bondport_get_name(p
));
4357 if (bondport_flags_ready(p
)) {
4358 if (g_bond
->verbose
) {
4359 timestamp_printf("[%s] Mux WAITING: Port is already ready\n",
4360 bondport_get_name(p
));
4364 if (devtimer_enabled(p
->po_wait_while_timer
)) {
4365 if (g_bond
->verbose
) {
4366 timestamp_printf("[%s] Mux WAITING: Timer already set\n",
4367 bondport_get_name(p
));
4371 if (ifbond_all_ports_attached(p
->po_bond
, p
)) {
4372 devtimer_cancel(p
->po_wait_while_timer
);
4373 if (g_bond
->verbose
) {
4374 timestamp_printf("[%s] Mux WAITING: No waiting\n",
4375 bondport_get_name(p
));
4377 bondport_flags_set_ready(p
);
4380 if (g_bond
->verbose
) {
4381 timestamp_printf("[%s] Mux WAITING: 2 seconds\n",
4382 bondport_get_name(p
));
4384 tv
.tv_sec
= LACP_AGGREGATE_WAIT_TIME
;
4386 devtimer_set_relative(p
->po_wait_while_timer
, tv
,
4387 (devtimer_timeout_func
)
4388 bondport_mux_machine_waiting
,
4389 (void *)LAEventTimeout
, NULL
);
4391 case LAEventTimeout
:
4392 if (g_bond
->verbose
) {
4393 timestamp_printf("[%s] Mux WAITING: Ready\n",
4394 bondport_get_name(p
));
4396 bondport_flags_set_ready(p
);
4400 if (bondport_flags_ready(p
)){
4401 if (g_bond
->verbose
) {
4402 timestamp_printf("[%s] Mux WAITING: All Ports Ready\n",
4403 bondport_get_name(p
));
4405 bondport_mux_machine_attached(p
, LAEventStart
, NULL
);
4414 bondport_mux_machine_attached(bondport_ref p
, LAEvent event
,
4415 __unused
void * event_data
)
4417 lacp_actor_partner_state s
;
4421 devtimer_cancel(p
->po_wait_while_timer
);
4422 if (g_bond
->verbose
) {
4423 timestamp_printf("[%s] Mux ATTACHED\n",
4424 bondport_get_name(p
));
4426 p
->po_mux_state
= MuxState_ATTACHED
;
4427 bondport_AttachMuxToAggregator(p
);
4428 s
= p
->po_actor_state
;
4429 s
= lacp_actor_partner_state_set_in_sync(s
);
4430 s
= lacp_actor_partner_state_set_not_collecting(s
);
4431 s
= lacp_actor_partner_state_set_not_distributing(s
);
4432 bondport_disable_distributing(p
);
4433 p
->po_actor_state
= s
;
4434 bondport_flags_set_ntt(p
);
4437 switch (p
->po_selected
) {
4438 case SelectedState_SELECTED
:
4439 s
= p
->po_partner_state
.ps_state
;
4440 if (lacp_actor_partner_state_in_sync(s
)) {
4441 bondport_mux_machine_collecting_distributing(p
, LAEventStart
,
4446 bondport_mux_machine_detached(p
, LAEventStart
, NULL
);
4455 bondport_mux_machine_collecting_distributing(bondport_ref p
,
4457 __unused
void * event_data
)
4459 lacp_actor_partner_state s
;
4463 devtimer_cancel(p
->po_wait_while_timer
);
4464 if (g_bond
->verbose
) {
4465 timestamp_printf("[%s] Mux COLLECTING_DISTRIBUTING\n",
4466 bondport_get_name(p
));
4468 p
->po_mux_state
= MuxState_COLLECTING_DISTRIBUTING
;
4469 bondport_enable_distributing(p
);
4470 s
= p
->po_actor_state
;
4471 s
= lacp_actor_partner_state_set_collecting(s
);
4472 s
= lacp_actor_partner_state_set_distributing(s
);
4473 p
->po_actor_state
= s
;
4474 bondport_flags_set_ntt(p
);
4477 s
= p
->po_partner_state
.ps_state
;
4478 if (lacp_actor_partner_state_in_sync(s
) == 0) {
4479 bondport_mux_machine_attached(p
, LAEventStart
, NULL
);
4482 switch (p
->po_selected
) {
4483 case SelectedState_UNSELECTED
:
4484 case SelectedState_STANDBY
:
4485 bondport_mux_machine_attached(p
, LAEventStart
, NULL
);