b759a713d1a197483bc588867b11d801853f9c71
[gpxe.git] / src / core / proto_eth_slow.c
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"
13 static const char slow_dest[] = SLOW_DST_MAC;
14
15
16 #define SLOW_SUBTYPE_LACP 1
17 #define SLOW_SUBTYPE_MARKER 2
18
19 struct slow_header {
20         uint8_t subtype;
21 };
22
23 struct 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) */
37 struct 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) */
60 struct 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
77 union 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
104 struct 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
110 static struct lacp_state lacp;
111
112
113 #if LACP_DEBUG > 0
114 static 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
144 static 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
191 static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when)
192 {
193         return when?(when - now)/TICKS_PER_SEC : 0;
194 }
195 static 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
209 static 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
258 static 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
270 static 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
289 static inline int lacp_timer_expired(unsigned long now, unsigned long when)
290 {
291         return when && (now > when);
292 }
293
294 static 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
304 static 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
313 static 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
346 static inline void send_eth_slow_reports(unsigned long now)
347 {
348         send_lacp_reports(now, 0);
349 }
350
351 static 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