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