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