1/* Copyright 2004 Linux Networx */
2#ifdef PROTO_LACP
3#if 0
4#include "nic.h"
5#include "timer.h"
6#endif
7
8#define LACP_DEBUG 0
9
10/* Structure definitions originally taken from the linux bond_3ad driver */
11
12#define SLOW_DST_MAC "\x01\x80\xc2\x00\x00\x02"
13static const char slow_dest[] = SLOW_DST_MAC;
14
15
16#define SLOW_SUBTYPE_LACP 1
17#define SLOW_SUBTYPE_MARKER 2
18
19struct slow_header {
20	uint8_t subtype;
21};
22
23struct lacp_info {
24	uint16_t system_priority;
25	uint8_t  system[ETH_ALEN];
26	uint16_t key;
27	uint16_t port_priority;
28	uint16_t port;
29	uint8_t  state;
30	uint8_t  reserved[3];
31} PACKED;
32
33#define LACP_CMP_LEN (2 + 6 + 2 + 2 + 2)
34#define LACP_CP_LEN  (2 + 6 + 2 + 2 + 2 + 1)
35
36/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */
37struct slow_lacp {
38	uint8_t  subtype;		       /* = LACP(= 0x01) */
39	uint8_t  version_number;
40	uint8_t  tlv_type_actor_info;	       /* = actor information(type/length/value) */
41#define LACP_TLV_TERMINATOR 0
42#define LACP_TLV_ACTOR      1
43#define LACP_TLV_PARTNER    2
44#define LACP_TLV_COLLECTOR  3
45	uint8_t  actor_information_length;     /* = 20 */
46	struct lacp_info actor;
47	uint8_t  tlv_type_partner_info;        /* = partner information */
48	uint8_t  partner_information_length;   /* = 20 */
49	struct lacp_info partner;
50	uint8_t  tlv_type_collector_info;      /* = collector information */
51	uint8_t  collector_information_length; /* = 16 */
52	uint16_t collector_max_delay;
53	uint8_t  reserved_12[12];
54	uint8_t  tlv_type_terminator;	       /* = terminator */
55	uint8_t  terminator_length;	       /* = 0 */
56	uint8_t  reserved_50[50];	       /* = 0 */
57} PACKED;
58
59/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */
60struct slow_marker {
61	uint8_t  subtype;                      /* = 0x02  (marker PDU) */
62	uint8_t  version_number;	       /* = 0x01 */
63	uint8_t  tlv_type;
64#define MARKER_TLV_TERMINATOR 0                /* marker terminator */
65#define MARKER_TLV_INFO       1	               /* marker information */
66#define MARKER_TLV_RESPONSE   2	               /* marker response information */
67	uint8_t  marker_length;                /* = 0x16 */
68	uint16_t requester_port;	       /* The number assigned to the port by the requester */
69	uint8_t  requester_system[ETH_ALEN];   /* The requester's system id */
70	uint32_t requester_transaction_id;     /* The transaction id allocated by the requester, */
71	uint16_t pad;		               /* = 0 */
72	uint8_t  tlv_type_terminator;	       /* = 0x00 */
73	uint8_t  terminator_length;	       /* = 0x00 */
74	uint8_t  reserved_90[90];	       /* = 0 */
75} PACKED;
76
77union slow_union {
78	struct slow_header header;
79	struct slow_lacp lacp;
80	struct slow_marker marker;
81};
82
83#define FAST_PERIODIC_TIME   (1*TICKS_PER_SEC)
84#define SLOW_PERIODIC_TIME   (30*TICKS_PER_SEC)
85#define SHORT_TIMEOUT_TIME   (3*FAST_PERIODIC_TIME)
86#define LONG_TIMEOUT_TIME    (3*SLOW_PERIODIC_TIME)
87#define CHURN_DETECTION_TIME (60*TICKS_PER_SEC)
88#define AGGREGATE_WAIT_TIME  (2*TICKS_PER_SEC)
89
90#define LACP_ACTIVITY        (1 << 0)
91#define LACP_TIMEOUT         (1 << 1)
92#define LACP_AGGREGATION     (1 << 2)
93#define LACP_SYNCHRONIZATION (1 << 3)
94#define LACP_COLLECTING      (1 << 4)
95#define LACP_DISTRIBUTING    (1 << 5)
96#define LACP_DEFAULTED       (1 << 6)
97#define LACP_EXPIRED         (1 << 7)
98
99#define UNSELECTED 0
100#define STANDBY    1
101#define SELECTED   2
102
103
104struct lacp_state {
105	struct slow_lacp pkt;
106	unsigned long current_while_timer; /* Time when the LACP information expires */
107	unsigned long periodic_timer; /* Time when I need to send my partner an update */
108};
109
110static struct lacp_state lacp;
111
112
113#if LACP_DEBUG > 0
114static void print_lacp_state(uint8_t state)
115{
116	printf("%hhx", state);
117	if (state & LACP_ACTIVITY) {
118		printf(" Activity");
119	}
120	if (state & LACP_TIMEOUT) {
121		printf(" Timeout");
122	}
123	if (state & LACP_AGGREGATION) {
124		printf(" Aggregation");
125	}
126	if (state & LACP_SYNCHRONIZATION) {
127		printf(" Syncronization");
128	}
129	if (state & LACP_COLLECTING) {
130		printf(" Collecting");
131	}
132	if (state & LACP_DISTRIBUTING) {
133		printf(" Distributing");
134	}
135	if (state & LACP_DEFAULTED) {
136		printf(" Defaulted");
137	}
138	if (state & LACP_EXPIRED) {
139		printf(" Expired");
140	}
141	printf("\n");
142}
143
144static inline void print_lacpdu(struct slow_lacp *pkt)
145{
146	printf("subtype version:  %hhx %hhx\n",
147		pkt->subtype, pkt->version_number);
148	printf("actor_tlv %hhx", pkt->tlv_type_actor_info);
149	printf(" len: %hhx (\n", pkt->actor_information_length);
150	printf(" sys_pri: %hx", ntohs(pkt->actor.system_priority));
151	printf(" mac: %!", pkt->actor.system);
152	printf(" key: %hx", ntohs(pkt->actor.key));
153	printf(" port_pri: %hx", ntohs(pkt->actor.port_priority));
154	printf(" port: %hx\n", ntohs(pkt->actor.port));
155	printf(" state: ");
156	print_lacp_state(pkt->actor.state);
157#if LACP_DEBUG > 1
158	printf(" reserved:     %hhx %hhx %hhx\n",
159		pkt->actor.reserved[0], pkt->actor.reserved[1], pkt->actor.reserved[2]);
160#endif
161	printf(")\n");
162	printf("partner_tlv: %hhx", pkt->tlv_type_partner_info);
163	printf(" len: %hhx (\n", pkt->partner_information_length);
164	printf(" sys_pri: %hx", ntohs(pkt->partner.system_priority));
165	printf(" mac: %!", pkt->partner.system);
166	printf(" key: %hx", ntohs(pkt->partner.key));
167	printf(" port_pri: %hx", ntohs(pkt->partner.port_priority));
168	printf(" port: %hx\n", ntohs(pkt->partner.port));
169	printf(" state: ");
170	print_lacp_state(pkt->partner.state);
171#if LACP_DEBUG > 1
172	printf(" reserved:     %hhx %hhx %hhx\n",
173		pkt->partner.reserved[0], pkt->partner.reserved[1], pkt->partner.reserved[2]);
174#endif
175	printf(")\n");
176	printf("collector_tlv: %hhx ", pkt->tlv_type_collector_info);
177	printf(" len: %hhx (", pkt->collector_information_length);
178	printf(" max_delay: %hx", ntohs(pkt->collector_max_delay));
179#if LACP_DEBUG > 1
180	printf("reserved_12:      %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
181		pkt->reserved_12[0], pkt->reserved_12[1], pkt->reserved_12[2],
182		pkt->reserved_12[3], pkt->reserved_12[4], pkt->reserved_12[5],
183		pkt->reserved_12[6], pkt->reserved_12[7], pkt->reserved_12[8],
184		pkt->reserved_12[9], pkt->reserved_12[10], pkt->reserved_12[11]);
185#endif
186	printf(" )\n");
187	printf("terminator_tlv: %hhx", pkt->tlv_type_terminator);
188	printf(" len: %hhx ()\n", pkt->terminator_length);
189}
190
191static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when)
192{
193	return when?(when - now)/TICKS_PER_SEC : 0;
194}
195static void print_lacp(const char *which, struct slow_lacp *pkt, unsigned long now)
196{
197	printf("%s\n", which);
198	print_lacpdu(pkt);
199	printf("timers: c %ds p %ds\n",
200		lacp_timer_val(now, lacp.current_while_timer),
201		lacp_timer_val(now, lacp.periodic_timer)
202		);
203	printf("\n");
204}
205#else /* LACP_DEBUG */
206#define print_lacp(which, pkt, now) do {} while(0)
207#endif /* LACP_DEBUG */
208
209static void lacp_init_state(const uint8_t *mac)
210{
211	memset(&lacp, 0, sizeof(lacp));
212
213	/* Initialize the packet constants */
214	lacp.pkt.subtype               = 1;
215	lacp.pkt.version_number        = 1;
216
217
218	/* The default state of my interface */
219	lacp.pkt.tlv_type_actor_info      = LACP_TLV_ACTOR;
220	lacp.pkt.actor_information_length = 0x14;
221	lacp.pkt.actor.system_priority    = htons(1);
222	memcpy(lacp.pkt.actor.system, mac, ETH_ALEN);
223	lacp.pkt.actor.key                = htons(1);
224	lacp.pkt.actor.port               = htons(1);
225	lacp.pkt.actor.port_priority      = htons(1);
226	lacp.pkt.actor.state =
227		LACP_SYNCHRONIZATION |
228		LACP_COLLECTING      |
229		LACP_DISTRIBUTING    |
230		LACP_DEFAULTED;
231
232	/* Set my partner defaults */
233	lacp.pkt.tlv_type_partner_info      = LACP_TLV_PARTNER;
234	lacp.pkt.partner_information_length = 0x14;
235	lacp.pkt.partner.system_priority    = htons(1);
236	/* memset(lacp.pkt.parnter_system, 0, ETH_ALEN); */
237	lacp.pkt.partner.key                = htons(1);
238	lacp.pkt.partner.port               = htons(1);
239	lacp.pkt.partner.port_priority      = htons(1);
240	lacp.pkt.partner.state =
241		LACP_ACTIVITY        |
242		LACP_SYNCHRONIZATION |
243		LACP_COLLECTING      |
244		LACP_DISTRIBUTING    |
245		LACP_DEFAULTED;
246
247	lacp.pkt.tlv_type_collector_info      = LACP_TLV_COLLECTOR;
248	lacp.pkt.collector_information_length = 0x10;
249	lacp.pkt.collector_max_delay          = htons(0x8000); /* ???? */
250
251	lacp.pkt.tlv_type_terminator          = LACP_TLV_TERMINATOR;
252	lacp.pkt.terminator_length            = 0;
253}
254
255#define LACP_NTT_MASK (LACP_ACTIVITY | LACP_TIMEOUT | \
256	LACP_SYNCHRONIZATION | LACP_AGGREGATION)
257
258static inline int lacp_update_ntt(struct slow_lacp *pkt)
259{
260	int ntt = 0;
261	if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) != 0) ||
262		((pkt->partner.state & LACP_NTT_MASK) !=
263			(lacp.pkt.actor.state & LACP_NTT_MASK)))
264	{
265		ntt = 1;
266	}
267	return ntt;
268}
269
270static inline void lacp_record_pdu(struct slow_lacp *pkt)
271{
272	memcpy(&lacp.pkt.partner, &pkt->actor, LACP_CP_LEN);
273
274	lacp.pkt.actor.state &= ~LACP_DEFAULTED;
275	lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
276	if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) == 0) &&
277		((pkt->partner.state & LACP_AGGREGATION) ==
278			(lacp.pkt.actor.state & LACP_AGGREGATION)))
279	{
280		lacp.pkt.partner.state  |= LACP_SYNCHRONIZATION;
281	}
282	if (!(pkt->actor.state & LACP_AGGREGATION)) {
283		lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
284	}
285
286	/* ACTIVITY? */
287}
288
289static inline int lacp_timer_expired(unsigned long now, unsigned long when)
290{
291	return when && (now > when);
292}
293
294static inline void lacp_start_periodic_timer(unsigned long now)
295{
296	if ((lacp.pkt.partner.state & LACP_ACTIVITY) ||
297		(lacp.pkt.actor.state & LACP_ACTIVITY)) {
298		lacp.periodic_timer = now +
299			(((lacp.pkt.partner.state & LACP_TIMEOUT)?
300				FAST_PERIODIC_TIME : SLOW_PERIODIC_TIME));
301	}
302}
303
304static inline void lacp_start_current_while_timer(unsigned long now)
305{
306	lacp.current_while_timer = now +
307		((lacp.pkt.actor.state & LACP_TIMEOUT) ?
308		SHORT_TIMEOUT_TIME : LONG_TIMEOUT_TIME);
309
310	lacp.pkt.actor.state &= ~LACP_EXPIRED;
311}
312
313static void send_lacp_reports(unsigned long now, int ntt)
314{
315	if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
316		lacp_init_state(nic.node_addr);
317	}
318	/* If the remote information has expired I need to take action */
319	if (lacp_timer_expired(now, lacp.current_while_timer)) {
320		if (!(lacp.pkt.actor.state & LACP_EXPIRED)) {
321			lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
322			lacp.pkt.partner.state |= LACP_TIMEOUT;
323			lacp.pkt.actor.state |= LACP_EXPIRED;
324			lacp.current_while_timer = now + SHORT_TIMEOUT_TIME;
325			ntt = 1;
326		}
327		else {
328			lacp_init_state(nic.node_addr);
329		}
330	}
331	/* If the periodic timer has expired I need to transmit */
332	if (lacp_timer_expired(now, lacp.periodic_timer)) {
333		ntt = 1;
334		/* Reset by lacp_start_periodic_timer */
335	}
336	if (ntt) {
337		eth_transmit(slow_dest, ETH_P_SLOW, sizeof(lacp.pkt), &lacp.pkt);
338
339		/* Restart the periodic timer */
340		lacp_start_periodic_timer(now);
341
342		print_lacp("Trasmitted", &lacp.pkt, now);
343	}
344}
345
346static inline void send_eth_slow_reports(unsigned long now)
347{
348	send_lacp_reports(now, 0);
349}
350
351static inline void process_eth_slow(unsigned short ptype, unsigned long now)
352{
353	union slow_union *pkt;
354	if ((ptype != ETH_P_SLOW) ||
355		(nic.packetlen < (ETH_HLEN + sizeof(pkt->header)))) {
356		return;
357	}
358	pkt = (union slow_union *)&nic.packet[ETH_HLEN];
359	if ((pkt->header.subtype == SLOW_SUBTYPE_LACP) &&
360		(nic.packetlen >= ETH_HLEN + sizeof(pkt->lacp))) {
361		int ntt;
362		if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
363			lacp_init_state(nic.node_addr);
364		}
365		/* As long as nic.packet is 2 byte aligned all is good */
366		print_lacp("Received", &pkt->lacp, now);
367		/* I don't actually implement the MUX or SELECT
368		 * machines.
369		 *
370		 * What logically happens when the client and I
371		 * disagree about an aggregator is the current
372		 * aggregtator is unselected.  The MUX machine places
373		 * me in DETACHED.  The SELECT machine runs and
374		 * reslects the same aggregator.  If I go through
375		 * these steps fast enough an outside observer can not
376		 * notice this.
377		 *
378		 * Since the process will not generate any noticeable
379		 * effect it does not need an implmenetation.  This
380		 * keeps the code simple and the code and binary
381		 * size down.
382		 */
383		/* lacp_update_selected(&pkt->lacp); */
384		ntt = lacp_update_ntt(&pkt->lacp);
385		lacp_record_pdu(&pkt->lacp);
386		lacp_start_current_while_timer(now);
387		send_lacp_reports(now, ntt);
388	}
389	/* If we receive a marker information packet return it */
390	else if ((pkt->header.subtype == SLOW_SUBTYPE_MARKER) &&
391		(nic.packetlen >= ETH_HLEN + sizeof(pkt->marker)) &&
392		(pkt->marker.tlv_type == MARKER_TLV_INFO) &&
393		(pkt->marker.marker_length == 0x16))
394	{
395		pkt->marker.tlv_type = MARKER_TLV_RESPONSE;
396		eth_transmit(slow_dest, ETH_P_SLOW,
397			sizeof(pkt->marker), &pkt->marker);
398	}
399
400 }
401#else
402
403#define send_eth_slow_reports(now)    do {} while(0)
404#define process_eth_slow(ptype, now)  do {} while(0)
405
406#endif
407