lacp: remove duplicated code
[vpp.git] / src / plugins / lacp / lacp.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdint.h>
17 #include <vlib/vlib.h>
18 #include <vlib/unix/unix.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vpp/app/version.h>
21 #include <vppinfra/hash.h>
22 #include <vnet/bonding/node.h>
23 #include <lacp/node.h>
24
25 lacp_main_t lacp_main;
26
27 /*
28  * Generate lacp pdu
29  */
30 static void
31 lacp_fill_pdu (lacp_pdu_t * lacpdu, slave_if_t * sif)
32 {
33   /* Actor TLV */
34   lacpdu->actor.port_info = sif->actor;
35
36   /* Partner TLV */
37   lacpdu->partner.port_info = sif->partner;
38 }
39
40 /*
41  * send a lacp pkt on an ethernet interface
42  */
43 static void
44 lacp_send_ethernet_lacp_pdu (slave_if_t * sif)
45 {
46   lacp_main_t *lm = &lacp_main;
47   u32 *to_next;
48   ethernet_lacp_pdu_t *h0;
49   vnet_hw_interface_t *hw;
50   u32 bi0;
51   vlib_buffer_t *b0;
52   vlib_frame_t *f;
53   vlib_main_t *vm = lm->vlib_main;
54   vnet_main_t *vnm = lm->vnet_main;
55
56   /*
57    * see lacp_periodic_init() to understand what's already painted
58    * into the buffer by the packet template mechanism
59    */
60   h0 = vlib_packet_template_get_packet
61     (vm, &lm->packet_templates[sif->packet_template_index], &bi0);
62
63   if (!h0)
64     return;
65
66   /* Add the interface's ethernet source address */
67   hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
68
69   clib_memcpy (h0->ethernet.src_address, hw->hw_address,
70                vec_len (hw->hw_address));
71
72   lacp_fill_pdu (&h0->lacp, sif);
73
74   /* Set the outbound packet length */
75   b0 = vlib_get_buffer (vm, bi0);
76   b0->current_length = sizeof (ethernet_lacp_pdu_t);
77   b0->current_data = 0;
78   b0->total_length_not_including_first_buffer = 0;
79
80   /* And the outbound interface */
81   vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
82
83   /* And output the packet on the correct interface */
84   f = vlib_get_frame_to_node (vm, hw->output_node_index);
85
86   to_next = vlib_frame_vector_args (f);
87   to_next[0] = bi0;
88   f->n_vectors = 1;
89
90   vlib_put_frame_to_node (vm, hw->output_node_index, f);
91
92   sif->last_lacpdu_sent_time = vlib_time_now (lm->vlib_main);
93   sif->pdu_sent++;
94 }
95
96 /*
97  * Decide which lacp packet template to use
98  */
99 static int
100 lacp_pick_packet_template (slave_if_t * sif)
101 {
102   sif->packet_template_index = LACP_PACKET_TEMPLATE_ETHERNET;
103
104   return 0;
105 }
106
107 void
108 lacp_send_lacp_pdu (vlib_main_t * vm, slave_if_t * sif)
109 {
110   if (sif->mode != BOND_MODE_LACP)
111     {
112       lacp_stop_timer (&sif->periodic_timer);
113       return;
114     }
115
116   if (sif->packet_template_index == (u8) ~ 0)
117     {
118       /* If we don't know how to talk to this peer, don't try again */
119       if (lacp_pick_packet_template (sif))
120         {
121           lacp_stop_timer (&sif->periodic_timer);
122           return;
123         }
124     }
125
126   switch (sif->packet_template_index)
127     {
128     case LACP_PACKET_TEMPLATE_ETHERNET:
129       lacp_send_ethernet_lacp_pdu (sif);
130       break;
131
132     default:
133       ASSERT (0);
134     }
135 }
136
137 void
138 lacp_periodic (vlib_main_t * vm)
139 {
140   bond_main_t *bm = &bond_main;
141   lacp_main_t *lm = &lacp_main;
142   slave_if_t *sif;
143
144   /* *INDENT-OFF* */
145   pool_foreach (sif, bm->neighbors,
146   ({
147     if (sif->port_enabled == 0)
148       continue;
149
150     if (lacp_timer_is_running (sif->current_while_timer) &&
151         lacp_timer_is_expired (lm->vlib_main, sif->current_while_timer))
152       {
153         lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
154                                LACP_RX_EVENT_TIMER_EXPIRED, &sif->rx_state);
155       }
156
157     if (lacp_timer_is_running (sif->periodic_timer) &&
158         lacp_timer_is_expired (lm->vlib_main, sif->periodic_timer))
159       {
160         lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
161                                LACP_PTX_EVENT_TIMER_EXPIRED, &sif->ptx_state);
162       }
163     if (lacp_timer_is_running (sif->wait_while_timer) &&
164         lacp_timer_is_expired (lm->vlib_main, sif->wait_while_timer))
165       {
166         sif->ready_n = 1;
167         lacp_stop_timer (&sif->wait_while_timer);
168         lacp_selection_logic (vm, sif);
169       }
170   }));
171   /* *INDENT-ON* */
172 }
173
174 static void
175 lacp_interface_enable_disable (vlib_main_t * vm, bond_if_t * bif,
176                                slave_if_t * sif, u8 enable)
177 {
178   lacp_main_t *lm = &lacp_main;
179   uword port_number;
180
181   if (enable)
182     {
183       lacp_create_periodic_process ();
184       port_number = clib_bitmap_first_clear (bif->port_number_bitmap);
185       bif->port_number_bitmap = clib_bitmap_set (bif->port_number_bitmap,
186                                                  port_number, 1);
187       // bitmap starts at 0. Our port number starts at 1.
188       lacp_init_neighbor (sif, bif->hw_address, port_number + 1, sif->group);
189       lacp_init_state_machines (vm, sif);
190       lm->lacp_int++;
191       if (lm->lacp_int == 1)
192         {
193           vlib_process_signal_event (vm, lm->lacp_process_node_index,
194                                      LACP_PROCESS_EVENT_START, 0);
195         }
196     }
197   else
198     {
199       ASSERT (lm->lacp_int >= 1);
200       if (lm->lacp_int == 0)
201         {
202           /* *INDENT-OFF* */
203           ELOG_TYPE_DECLARE (e) =
204             {
205               .format = "lacp-int-en-dis: BUG lacp_int == 0",
206             };
207           /* *INDENT-ON* */
208           ELOG_DATA (&vlib_global_main.elog_main, e);
209         }
210       else
211         {
212           lm->lacp_int--;
213           if (lm->lacp_int == 0)
214             vlib_process_signal_event (vm, lm->lacp_process_node_index,
215                                        LACP_PROCESS_EVENT_STOP, 0);
216         }
217     }
218 }
219
220 static clib_error_t *
221 lacp_periodic_init (vlib_main_t * vm)
222 {
223   lacp_main_t *lm = &lacp_main;
224   ethernet_lacp_pdu_t h;
225   ethernet_marker_pdu_t m;
226   u8 dst[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
227
228   /* initialize binary API */
229   lacp_plugin_api_hookup (vm);
230
231   /* Create the ethernet lacp packet template */
232
233   clib_memset (&h, 0, sizeof (h));
234
235   memcpy (h.ethernet.dst_address, dst, sizeof (h.ethernet.dst_address));
236
237   /* leave src address blank (fill in at send time) */
238
239   h.ethernet.type = htons (ETHERNET_TYPE_SLOW_PROTOCOLS);
240
241   h.lacp.subtype = LACP_SUBTYPE;
242   h.lacp.version_number = LACP_ACTOR_LACP_VERSION;
243
244   /* Actor TLV */
245   h.lacp.actor.tlv_type = LACP_ACTOR_INFORMATION;
246   h.lacp.actor.tlv_length = sizeof (lacp_actor_partner_t);
247
248   /* Partner TLV */
249   h.lacp.partner.tlv_type = LACP_PARTNER_INFORMATION;
250   h.lacp.partner.tlv_length = sizeof (lacp_actor_partner_t);
251
252   /* Collector TLV */
253   h.lacp.collector.tlv_type = LACP_COLLECTOR_INFORMATION;
254   h.lacp.collector.tlv_length = sizeof (lacp_collector_t);
255   h.lacp.collector.max_delay = 0;
256
257   /* Terminator TLV */
258   h.lacp.terminator.tlv_type = LACP_TERMINATOR_INFORMATION;
259   h.lacp.terminator.tlv_length = 0;
260
261   vlib_packet_template_init
262     (vm, &lm->packet_templates[LACP_PACKET_TEMPLATE_ETHERNET],
263      /* data */ &h,
264      sizeof (h),
265      /* alloc chunk size */ 8,
266      "lacp-ethernet");
267
268   /* Create the ethernet marker protocol packet template */
269
270   clib_memset (&m, 0, sizeof (m));
271
272   memcpy (m.ethernet.dst_address, dst, sizeof (m.ethernet.dst_address));
273
274   /* leave src address blank (fill in at send time) */
275
276   m.ethernet.type = htons (ETHERNET_TYPE_SLOW_PROTOCOLS);
277
278   m.marker.subtype = MARKER_SUBTYPE;
279   m.marker.version_number = MARKER_PROTOCOL_VERSION;
280
281   m.marker.marker_info.tlv_length = sizeof (marker_information_t);
282
283   /* Terminator TLV */
284   m.marker.terminator.tlv_type = MARKER_TERMINATOR_INFORMATION;
285   m.marker.terminator.tlv_length = 0;
286
287   vlib_packet_template_init
288     (vm, &lm->marker_packet_templates[MARKER_PACKET_TEMPLATE_ETHERNET],
289      /* data */ &m,
290      sizeof (m),
291      /* alloc chunk size */ 8,
292      "marker-ethernet");
293
294   bond_register_callback (lacp_interface_enable_disable);
295
296   return 0;
297 }
298
299 int
300 lacp_machine_dispatch (lacp_machine_t * machine, vlib_main_t * vm,
301                        slave_if_t * sif, int event, int *state)
302 {
303   lacp_fsm_state_t *transition;
304   int rc = 0;
305
306   transition = &machine->tables[*state].state_table[event];
307   LACP_DBG2 (sif, event, *state, machine, transition);
308   *state = transition->next_state;
309   if (transition->action)
310     rc = (*transition->action) ((void *) vm, (void *) sif);
311
312   return rc;
313 }
314
315 void
316 lacp_init_neighbor (slave_if_t * sif, u8 * hw_address, u16 port_number,
317                     u32 group)
318 {
319   lacp_stop_timer (&sif->wait_while_timer);
320   lacp_stop_timer (&sif->current_while_timer);
321   lacp_stop_timer (&sif->actor_churn_timer);
322   lacp_stop_timer (&sif->partner_churn_timer);
323   lacp_stop_timer (&sif->periodic_timer);
324   lacp_stop_timer (&sif->last_lacpdu_sent_time);
325   lacp_stop_timer (&sif->last_lacpdu_recd_time);
326   lacp_stop_timer (&sif->last_marker_pdu_sent_time);
327   lacp_stop_timer (&sif->last_marker_pdu_recd_time);
328   sif->lacp_enabled = 1;
329   sif->loopback_port = 0;
330   sif->ready = 0;
331   sif->ready_n = 0;
332   sif->port_moved = 0;
333   sif->ntt = 0;
334   sif->selected = LACP_PORT_UNSELECTED;
335   sif->actor.state = LACP_STATE_AGGREGATION;
336   if (sif->ttl_in_seconds == LACP_SHORT_TIMOUT_TIME)
337     sif->actor.state |= LACP_STATE_LACP_TIMEOUT;
338   if (sif->is_passive == 0)
339     sif->actor.state |= LACP_STATE_LACP_ACTIVITY;
340   clib_memcpy (sif->actor.system, hw_address, 6);
341   sif->actor.system_priority = htons (LACP_DEFAULT_SYSTEM_PRIORITY);
342   sif->actor.key = htons (group);
343   sif->actor.port_number = htons (port_number);
344   sif->actor.port_priority = htons (LACP_DEFAULT_PORT_PRIORITY);
345
346   sif->partner.system_priority = htons (LACP_DEFAULT_SYSTEM_PRIORITY);
347   sif->partner.key = htons (group);
348   sif->partner.port_number = htons (port_number);
349   sif->partner.port_priority = htons (LACP_DEFAULT_PORT_PRIORITY);
350   sif->partner.state = 0;
351
352   sif->actor_admin = sif->actor;
353   sif->partner_admin = sif->partner;
354 }
355
356 void
357 lacp_init_state_machines (vlib_main_t * vm, slave_if_t * sif)
358 {
359   lacp_init_tx_machine (vm, sif);
360   lacp_init_mux_machine (vm, sif);
361   lacp_init_ptx_machine (vm, sif);
362   lacp_init_rx_machine (vm, sif);
363 }
364
365 VLIB_INIT_FUNCTION (lacp_periodic_init);
366
367 static clib_error_t *
368 lacp_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
369 {
370   lacp_main_t *lm = &lacp_main;
371   slave_if_t *sif;
372   vlib_main_t *vm = lm->vlib_main;
373
374   sif = bond_get_slave_by_sw_if_index (sw_if_index);
375   if (sif)
376     {
377       sif->port_enabled = flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
378       if (sif->port_enabled == 0)
379         {
380           if (sif->lacp_enabled)
381             {
382               lacp_init_neighbor (sif, sif->actor_admin.system,
383                                   ntohs (sif->actor_admin.port_number),
384                                   ntohs (sif->actor_admin.key));
385               lacp_init_state_machines (vm, sif);
386             }
387         }
388     }
389
390   return 0;
391 }
392
393 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lacp_sw_interface_up_down);
394
395 static clib_error_t *
396 lacp_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
397 {
398   lacp_main_t *lm = &lacp_main;
399   slave_if_t *sif;
400   vnet_sw_interface_t *sw;
401   vlib_main_t *vm = lm->vlib_main;
402
403   sw = vnet_get_hw_sw_interface (vnm, hw_if_index);
404   sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
405   if (sif)
406     {
407       if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
408         {
409           if (sif->lacp_enabled)
410             {
411               lacp_init_neighbor (sif, sif->actor_admin.system,
412                                   ntohs (sif->actor_admin.port_number),
413                                   ntohs (sif->actor_admin.key));
414               lacp_init_state_machines (vm, sif);
415             }
416         }
417     }
418
419   return 0;
420 }
421
422 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (lacp_hw_interface_up_down);
423
424 /* *INDENT-OFF* */
425 VLIB_PLUGIN_REGISTER () = {
426     .version = VPP_BUILD_VER,
427     .description = "Link Aggregation Control Protocol (LACP)",
428 };
429 /* *INDENT-ON* */
430
431 /*
432  * fd.io coding-style-patch-verification: ON
433  *
434  * Local Variables:
435  * eval: (c-set-style "gnu")
436  * End:
437  */