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