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#define _GNU_SOURCE
17
18#include <vnet/bonding/node.h>
19#include <lacp/node.h>
20
21/*
22 *  LACP State = INITIALIZE
23 */
24static lacp_fsm_state_t lacp_rx_state_initialize[] = {
25  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED},	// event 0 BEGIN
26  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED},	// event 1 PORT_DISABLED
27  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED},	// event 2 PORT_MOVED
28  {LACP_NOACTION, LACP_RX_STATE_INITIALIZE},	// event 3 LACP_ENABLED
29  {LACP_NOACTION, LACP_RX_STATE_INITIALIZE},	// event 4 LACP_DISABLED
30  {LACP_NOACTION, LACP_RX_STATE_INITIALIZE},	// event 5 PDU_RECEIVED
31  {LACP_NOACTION, LACP_RX_STATE_INITIALIZE},	// event 6 TIMER_EXPIRED
32};
33
34/*
35 *  LACP State = PORT_DISABLED
36 */
37static lacp_fsm_state_t lacp_rx_state_port_disabled[] = {
38  {LACP_ACTION_PORT_DISABLED, LACP_RX_STATE_PORT_DISABLED},	// event 0 BEGIN
39  {LACP_ACTION_PORT_DISABLED, LACP_RX_STATE_PORT_DISABLED},	// event 1 PORT_DISABLED
40  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE},	// event 2 PORT_MOVED
41  {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED},	// event 3 LACP_ENABLED
42  {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED},	// event 4 LACP_DISABLED
43  {LACP_NOACTION, LACP_RX_STATE_PORT_DISABLED},	// event 5 PDU_RECEIVED
44  {LACP_NOACTION, LACP_RX_STATE_PORT_DISABLED},	// event 6 TIMER_EXPIRED
45};
46
47/*
48 *  LACP State = EXPIRED
49 */
50static lacp_fsm_state_t lacp_rx_state_expired[] = {
51  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE},	// event 0 BEGIN
52  {LACP_NOACTION, LACP_RX_STATE_EXPIRED},	// event 1 PORT_DISABLED
53  {LACP_NOACTION, LACP_RX_STATE_EXPIRED},	// event 2 PORT_MOVED
54  {LACP_NOACTION, LACP_RX_STATE_EXPIRED},	// event 3 LACP_ENABLED
55  {LACP_NOACTION, LACP_RX_STATE_EXPIRED},	// event 4 LACP_DISABLED
56  {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT},	// event 5 PDU_RECEIVED
57  {LACP_ACTION_DEFAULTED, LACP_RX_STATE_DEFAULTED},	// event 6 TIMER_EXPIRED
58};
59
60/*
61 *  LACP State = LACP_DISABLED
62 */
63static lacp_fsm_state_t lacp_rx_state_lacp_disabled[] = {
64  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE},	// event 0 BEGIN
65  {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED},	// event 1 PORT_DISABLED
66  {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED},	// event 2 PORT_MOVED
67  {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED},	// event 3 LACP_ENABLED XXX
68  {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED},	// event 4 LACP_DISABLED
69  {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED},	// event 5 PDU_RECEIVED
70  {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED},	// event 6 TIMER_EXPIRED
71};
72
73/*
74 *  LACP State = DEFAULTED
75 */
76static lacp_fsm_state_t lacp_rx_state_defaulted[] = {
77  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE},	// event 0 BEGIN
78  {LACP_NOACTION, LACP_RX_STATE_DEFAULTED},	// event 1 PORT_DISABLED
79  {LACP_NOACTION, LACP_RX_STATE_DEFAULTED},	// event 2 PORT_MOVED
80  {LACP_NOACTION, LACP_RX_STATE_DEFAULTED},	// event 3 LACP_ENABLED
81  {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED},	// event 4 LACP_DISABLED
82  {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT},	// event 5 PDU_RECEIVED
83  {LACP_ACTION_DEFAULTED, LACP_RX_STATE_DEFAULTED},	// event 6 TIMER_EXPIRED
84};
85
86/*
87 *  LACP State = CURRENT
88 */
89static lacp_fsm_state_t lacp_rx_state_current[] = {
90  {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE},	// event 0 BEGIN
91  {LACP_NOACTION, LACP_RX_STATE_CURRENT},	// event 1 PORT_DISABLED
92  {LACP_NOACTION, LACP_RX_STATE_CURRENT},	// event 1 PORT_MOVED
93  {LACP_NOACTION, LACP_RX_STATE_CURRENT},	// event 2 LACP_ENABLED
94  {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED},	// event 3 LACP_DISABLED
95  {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT},	// event 4 PDU_RECEIVED
96  {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED},	// event 5 TIMER_EXPIRED
97};
98
99static lacp_fsm_machine_t lacp_rx_fsm_table[] = {
100  {lacp_rx_state_initialize},
101  {lacp_rx_state_port_disabled},
102  {lacp_rx_state_expired},
103  {lacp_rx_state_lacp_disabled},
104  {lacp_rx_state_defaulted},
105  {lacp_rx_state_current},
106};
107
108lacp_machine_t lacp_rx_machine = {
109  lacp_rx_fsm_table,
110  lacp_rx_debug_func,
111};
112
113static void
114lacp_set_port_unselected (vlib_main_t * vm, slave_if_t * sif)
115{
116  sif->selected = LACP_PORT_UNSELECTED;
117
118  switch (sif->mux_state)
119    {
120    case LACP_MUX_STATE_DETACHED:
121      break;
122    case LACP_MUX_STATE_WAITING:
123      break;
124    case LACP_MUX_STATE_ATTACHED:
125      return;
126      break;
127    case LACP_MUX_STATE_COLLECTING_DISTRIBUTING:
128      if (sif->partner.state & LACP_STATE_SYNCHRONIZATION)
129	return;
130      break;
131    default:
132      break;
133    }
134  lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
135			 LACP_MUX_EVENT_UNSELECTED, &sif->mux_state);
136}
137
138static void
139lacp_update_default_selected (vlib_main_t * vm, slave_if_t * sif)
140{
141  if ((sif->partner_admin.state & LACP_STATE_AGGREGATION) !=
142      (sif->partner.state & LACP_STATE_AGGREGATION) ||
143      memcmp (&sif->partner, &sif->partner_admin,
144	      sizeof (sif->partner) - sizeof (sif->partner.state)))
145    {
146      lacp_set_port_unselected (vm, sif);
147    }
148}
149
150static void
151lacp_record_default (slave_if_t * sif)
152{
153  sif->partner = sif->partner_admin;
154  sif->actor.state |= LACP_STATE_DEFAULTED;
155}
156
157static void
158lacp_update_selected (vlib_main_t * vm, slave_if_t * sif)
159{
160  lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
161
162  if ((lacpdu->actor.port_info.state & LACP_STATE_AGGREGATION) !=
163      (sif->partner.state & LACP_STATE_AGGREGATION) ||
164      memcmp (&sif->partner, &lacpdu->actor.port_info,
165	      sizeof (sif->partner) - sizeof (sif->partner.state)))
166    {
167      lacp_set_port_unselected (vm, sif);
168    }
169}
170
171static void
172lacp_update_ntt (vlib_main_t * vm, slave_if_t * sif)
173{
174  lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
175  u8 states = LACP_STATE_LACP_ACTIVITY | LACP_STATE_LACP_TIMEOUT |
176    LACP_STATE_SYNCHRONIZATION | LACP_STATE_AGGREGATION;
177
178  if ((states & lacpdu->partner.port_info.state) !=
179      (states & sif->actor.state)
180      || memcmp (&sif->actor, &lacpdu->partner.port_info,
181		 sizeof (sif->actor) - sizeof (sif->actor.state)))
182    {
183      sif->ntt = 1;
184      lacp_start_periodic_timer (vm, sif, 0);
185    }
186}
187
188/*
189 * compare lacpdu partner info against sif->partner. Return 1 if they match, 0
190 * otherwise.
191 */
192static u8
193lacp_compare_partner (slave_if_t * sif)
194{
195  lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
196
197  if ((!memcmp (&sif->partner, &lacpdu->actor.port_info,
198		sizeof (sif->partner) - sizeof (sif->partner.state)) &&
199       ((sif->actor.state & LACP_STATE_AGGREGATION) ==
200	(lacpdu->partner.port_info.state & LACP_STATE_AGGREGATION))) ||
201      ((lacpdu->actor.port_info.state & LACP_STATE_AGGREGATION) == 0))
202    return 1;
203
204  return 0;
205}
206
207static void
208lacp_record_pdu (vlib_main_t * vm, slave_if_t * sif)
209{
210  lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
211  u8 match;
212
213  /* Transition PTX out of NO_PERIODIC if needed */
214  if (!(sif->partner.state & LACP_STATE_LACP_ACTIVITY) &&
215      (lacpdu->actor.port_info.state & LACP_STATE_LACP_ACTIVITY))
216    lacp_ptx_post_short_timeout_event (vm, sif);
217  match = lacp_compare_partner (sif);
218  sif->partner = lacpdu->actor.port_info;
219  sif->actor.state &= ~LACP_STATE_DEFAULTED;
220  if (match && (lacpdu->actor.port_info.state & LACP_STATE_SYNCHRONIZATION))
221    sif->partner.state |= LACP_STATE_SYNCHRONIZATION;
222  else
223    sif->partner.state &= ~LACP_STATE_SYNCHRONIZATION;
224}
225
226static void
227lacp_set_port_moved (vlib_main_t * vm, slave_if_t * sif, u8 val)
228{
229  sif->port_moved = val;
230
231  if (sif->port_moved)
232    lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
233			   LACP_RX_EVENT_PORT_MOVED, &sif->rx_state);
234  else if (!sif->port_enabled)
235    lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
236			   LACP_RX_EVENT_PORT_DISABLED, &sif->rx_state);
237}
238
239int
240lacp_rx_action_initialize (void *p1, void *p2)
241{
242  vlib_main_t *vm = p1;
243  slave_if_t *sif = p2;
244
245  lacp_set_port_unselected (vm, sif);
246  lacp_record_default (sif);
247  sif->actor.state &= ~LACP_STATE_EXPIRED;
248  lacp_set_port_moved (vm, sif, 0);
249  /* UCT */
250  lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
251			 LACP_RX_EVENT_BEGIN, &sif->rx_state);
252
253  return 0;
254}
255
256int
257lacp_rx_action_port_disabled (void *p1, void *p2)
258{
259  vlib_main_t *vm = p1;
260  slave_if_t *sif = p2;
261
262  sif->partner.state &= ~LACP_STATE_SYNCHRONIZATION;
263  if (sif->port_moved)
264    {
265      lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
266			     LACP_RX_EVENT_PORT_MOVED, &sif->rx_state);
267    }
268  if (sif->port_enabled)
269    {
270      if (sif->lacp_enabled)
271	lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
272			       LACP_RX_EVENT_LACP_ENABLED, &sif->rx_state);
273      else
274	lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
275			       LACP_RX_EVENT_LACP_DISABLED, &sif->rx_state);
276    }
277
278  return 0;
279}
280
281int
282lacp_rx_action_expired (void *p1, void *p2)
283{
284  vlib_main_t *vm = p1;
285  slave_if_t *sif = p2;
286  u8 timer_expired;
287
288  sif->partner.state &= ~LACP_STATE_SYNCHRONIZATION;
289  sif->partner.state |= LACP_STATE_LACP_TIMEOUT;
290  lacp_ptx_post_short_timeout_event (vm, sif);
291  if (lacp_timer_is_running (sif->current_while_timer) &&
292      lacp_timer_is_expired (vm, sif->current_while_timer))
293    timer_expired = 1;
294  else
295    timer_expired = 0;
296  lacp_start_current_while_timer (vm, sif, sif->ttl_in_seconds);
297  sif->actor.state |= LACP_STATE_EXPIRED;
298  if (timer_expired)
299    lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
300			   LACP_RX_EVENT_TIMER_EXPIRED, &sif->rx_state);
301  if (sif->last_rx_pkt && vec_len (sif->last_rx_pkt))
302    lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
303			   LACP_RX_EVENT_PDU_RECEIVED, &sif->rx_state);
304
305  return 0;
306}
307
308int
309lacp_rx_action_lacp_disabled (void *p1, void *p2)
310{
311  vlib_main_t *vm = p1;
312  slave_if_t *sif = p2;
313
314  lacp_set_port_unselected (vm, sif);
315  lacp_record_default (sif);
316  sif->partner.state &= ~LACP_STATE_AGGREGATION;
317  sif->actor.state &= ~LACP_STATE_EXPIRED;
318
319  return 0;
320}
321
322int
323lacp_rx_action_defaulted (void *p1, void *p2)
324{
325  vlib_main_t *vm = p1;
326  slave_if_t *sif = p2;
327
328  lacp_stop_timer (&sif->current_while_timer);
329  lacp_update_default_selected (vm, sif);
330  lacp_record_default (sif);
331  sif->actor.state &= ~LACP_STATE_EXPIRED;
332  if (sif->last_rx_pkt && vec_len (sif->last_rx_pkt))
333    lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
334			   LACP_RX_EVENT_PDU_RECEIVED, &sif->rx_state);
335
336  return 0;
337}
338
339static int
340lacp_port_is_moved (vlib_main_t * vm, slave_if_t * sif)
341{
342  bond_main_t *bm = &bond_main;
343  slave_if_t *sif2;
344  lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
345
346  /* *INDENT-OFF* */
347  pool_foreach (sif2, bm->neighbors, {
348      {
349	if ((sif != sif2) && (sif2->rx_state == LACP_RX_STATE_PORT_DISABLED) &&
350	    !memcmp (sif2->partner.system,
351		     lacpdu->partner.port_info.system, 6) &&
352	    (sif2->partner.port_number == lacpdu->partner.port_info.port_number))
353	  return 1;
354      }
355  });
356  /* *INDENT-ON* */
357
358  return 0;
359}
360
361int
362lacp_rx_action_current (void *p1, void *p2)
363{
364  vlib_main_t *vm = p1;
365  slave_if_t *sif = p2;
366
367  lacp_update_selected (vm, sif);
368  lacp_update_ntt (vm, sif);
369  lacp_record_pdu (vm, sif);
370  lacp_start_current_while_timer (vm, sif, sif->ttl_in_seconds);
371  sif->actor.state &= ~LACP_STATE_EXPIRED;
372  if (lacp_port_is_moved (vm, sif))
373    lacp_set_port_moved (vm, sif, 1);
374  lacp_selection_logic (vm, sif);
375
376  return 0;
377}
378
379static u8 *
380format_rx_event (u8 * s, va_list * args)
381{
382  static lacp_event_struct lacp_rx_event_array[] = {
383#define _(b, s, n) {.bit = b, .str = #s, },
384    foreach_lacp_rx_event
385#undef _
386    {.str = NULL}
387  };
388  int e = va_arg (*args, int);
389  lacp_event_struct *event_entry = lacp_rx_event_array;
390
391  if (e >= (sizeof (lacp_rx_event_array) / sizeof (*event_entry)))
392    s = format (s, "Bad event %d", e);
393  else
394    s = format (s, "%s", event_entry[e].str);
395
396  return s;
397}
398
399void
400lacp_rx_debug_func (slave_if_t * sif, int event, int state,
401		    lacp_fsm_state_t * transition)
402{
403  vlib_worker_thread_t *w = vlib_worker_threads + os_get_thread_index ();
404  /* *INDENT-OFF* */
405  ELOG_TYPE_DECLARE (e) =
406    {
407      .format = "%s",
408      .format_args = "T4",
409    };
410  /* *INDENT-ON* */
411  struct
412  {
413    u32 event;
414  } *ed = 0;
415
416  ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track);
417  ed->event = elog_string (&vlib_global_main.elog_main, "%U-RX: %U, %U->%U%c",
418			   format_vnet_sw_if_index_name, vnet_get_main (),
419			   sif->sw_if_index, format_rx_event, event,
420			   format_rx_sm_state, state, format_rx_sm_state,
421			   transition->next_state, 0);
422}
423
424void
425lacp_init_rx_machine (vlib_main_t * vm, slave_if_t * sif)
426{
427  lacp_machine_dispatch (&lacp_rx_machine, vm, sif, LACP_RX_EVENT_BEGIN,
428			 &sif->rx_state);
429  lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
430			 LACP_RX_EVENT_LACP_ENABLED, &sif->rx_state);
431}
432
433/*
434 * fd.io coding-style-patch-verification: ON
435 *
436 * Local Variables:
437 * eval: (c-set-style "gnu")
438 * End:
439 */
440