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