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