dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / vnet / vnet / bfd / bfd_main.c
1 /*
2  * Copyright (c) 2011-2016 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  * @file
17  * @brief BFD nodes implementation
18  */
19
20 #include <vppinfra/random.h>
21 #include <vppinfra/error.h>
22 #include <vppinfra/hash.h>
23 #include <vnet/ethernet/ethernet.h>
24 #include <vnet/ethernet/packet.h>
25 #include <vnet/bfd/bfd_debug.h>
26 #include <vnet/bfd/bfd_protocol.h>
27 #include <vnet/bfd/bfd_main.h>
28
29 static u64
30 bfd_us_to_clocks (bfd_main_t * bm, u64 us)
31 {
32   return bm->cpu_cps * ((f64) us / USEC_PER_SECOND);
33 }
34
35 static vlib_node_registration_t bfd_process_node;
36
37 typedef enum
38 {
39 #define F(t, n) BFD_OUTPUT_##t,
40   foreach_bfd_transport (F)
41 #undef F
42     BFD_OUTPUT_N_NEXT,
43 } bfd_output_next_t;
44
45 static u32 bfd_next_index_by_transport[] = {
46 #define F(t, n) [BFD_TRANSPORT_##t] = BFD_OUTPUT_##t,
47   foreach_bfd_transport (F)
48 #undef F
49 };
50
51 /*
52  * We actually send all bfd pkts to the "error" node after scanning
53  * them, so the graph node has only one next-index. The "error-drop"
54  * node automatically bumps our per-node packet counters for us.
55  */
56 typedef enum
57 {
58   BFD_INPUT_NEXT_NORMAL,
59   BFD_INPUT_N_NEXT,
60 } bfd_input_next_t;
61
62 static void bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
63                                  int handling_wakeup);
64
65 static void
66 bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
67 {
68   bs->local_state = BFD_STATE_down;
69   bs->local_diag = BFD_DIAG_CODE_no_diag;
70   bs->remote_state = BFD_STATE_down;
71   bs->local_demand = 0;
72   bs->remote_discr = 0;
73   bs->desired_min_tx_us = BFD_DEFAULT_DESIRED_MIN_TX_US;
74   bs->desired_min_tx_clocks = bfd_us_to_clocks (bm, bs->desired_min_tx_us);
75   bs->remote_min_rx_us = 1;
76   bs->remote_demand = 0;
77 }
78
79 static void
80 bfd_set_diag (bfd_session_t * bs, bfd_diag_code_e code)
81 {
82   if (bs->local_diag != code)
83     {
84       BFD_DBG ("set local_diag, bs_idx=%d: '%d:%s'", bs->bs_idx, code,
85                bfd_diag_code_string (code));
86       bs->local_diag = code;
87     }
88 }
89
90 static void
91 bfd_set_state (bfd_main_t * bm, bfd_session_t * bs,
92                bfd_state_e new_state, int handling_wakeup)
93 {
94   if (bs->local_state != new_state)
95     {
96       BFD_DBG ("Change state, bs_idx=%d: %s->%s", bs->bs_idx,
97                bfd_state_string (bs->local_state),
98                bfd_state_string (new_state));
99       bs->local_state = new_state;
100       bfd_on_state_change (bm, bs, clib_cpu_time_now (), handling_wakeup);
101     }
102 }
103
104 static void
105 bfd_recalc_tx_interval (bfd_main_t * bm, bfd_session_t * bs)
106 {
107   if (!bs->local_demand)
108     {
109       bs->transmit_interval_clocks =
110         clib_max (bs->desired_min_tx_clocks, bs->remote_min_rx_clocks);
111     }
112   else
113     {
114       /* TODO */
115     }
116   BFD_DBG ("Recalculated transmit interval %lu clocks/%.2fs",
117            bs->transmit_interval_clocks,
118            bs->transmit_interval_clocks / bm->cpu_cps);
119 }
120
121 static void
122 bfd_calc_next_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now)
123 {
124   if (!bs->local_demand)
125     {
126       if (bs->local_detect_mult > 1)
127         {
128           /* common case - 75-100% of transmit interval */
129           bs->tx_timeout_clocks = now +
130             (1 - .25 * (random_f64 (&bm->random_seed))) *
131             bs->transmit_interval_clocks;
132           if (bs->tx_timeout_clocks < now)
133             {
134               /* huh, we've missed it already, skip the missed events */
135               const u64 missed =
136                 (now - bs->tx_timeout_clocks) / bs->transmit_interval_clocks;
137               BFD_ERR ("Missed %lu transmit events (now is %lu, calc "
138                        "tx_timeout is %lu)!",
139                        missed, now, bs->tx_timeout_clocks);
140               bs->tx_timeout_clocks +=
141                 (missed + 1) * bs->transmit_interval_clocks;
142             }
143         }
144       else
145         {
146           /* special case - 75-90% of transmit interval */
147           bs->tx_timeout_clocks =
148             now +
149             (.9 - .15 * (random_f64 (&bm->random_seed))) *
150             bs->transmit_interval_clocks;
151           if (bs->tx_timeout_clocks < now)
152             {
153               /* huh, we've missed it already, skip the missed events */
154               const u64 missed =
155                 (now - bs->tx_timeout_clocks) / bs->transmit_interval_clocks;
156               BFD_ERR ("Missed %lu transmit events (now is %lu, calc "
157                        "tx_timeout is %lu)!",
158                        missed, now, bs->tx_timeout_clocks);
159               bs->tx_timeout_clocks +=
160                 (missed + 1) * bs->transmit_interval_clocks;
161             }
162         }
163     }
164   else
165     {
166       /* TODO */
167     }
168   if (bs->tx_timeout_clocks)
169     {
170       BFD_DBG ("Next transmit in %lu clocks/%.02fs@%lu",
171                bs->tx_timeout_clocks - now,
172                (bs->tx_timeout_clocks - now) / bm->cpu_cps,
173                bs->tx_timeout_clocks);
174     }
175 }
176
177 static void
178 bfd_recalc_detection_time (bfd_main_t * bm, bfd_session_t * bs)
179 {
180   if (!bs->local_demand)
181     {
182       bs->detection_time_clocks =
183         bs->remote_detect_mult *
184         bfd_us_to_clocks (bm, clib_max (bs->required_min_rx_us,
185                                         bs->remote_desired_min_tx_us));
186     }
187   else
188     {
189       bs->detection_time_clocks =
190         bs->local_detect_mult *
191         bfd_us_to_clocks (bm,
192                           clib_max (bs->desired_min_tx_us,
193                                     bs->remote_min_rx_us));
194     }
195   BFD_DBG ("Recalculated detection time %lu clocks/%.2fs",
196            bs->detection_time_clocks,
197            bs->detection_time_clocks / bm->cpu_cps);
198 }
199
200 static void
201 bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now,
202                int handling_wakeup)
203 {
204   u64 next = 0;
205   u64 rx_timeout = 0;
206   if (BFD_STATE_up == bs->local_state)
207     {
208       rx_timeout = bs->last_rx_clocks + bs->detection_time_clocks;
209     }
210   if (bs->tx_timeout_clocks && rx_timeout)
211     {
212       next = clib_min (bs->tx_timeout_clocks, rx_timeout);
213     }
214   else if (bs->tx_timeout_clocks)
215     {
216       next = bs->tx_timeout_clocks;
217     }
218   else if (rx_timeout)
219     {
220       next = rx_timeout;
221     }
222   BFD_DBG ("bs_idx=%u, tx_timeout=%lu, rx_timeout=%lu, next=%s", bs->bs_idx,
223            bs->tx_timeout_clocks, rx_timeout,
224            next == bs->tx_timeout_clocks ? "tx" : "rx");
225   /* sometimes the wheel expires an event a bit sooner than requested, account
226      for that here */
227   if (next && (now + bm->wheel_inaccuracy > bs->wheel_time_clocks ||
228                next < bs->wheel_time_clocks || !bs->wheel_time_clocks))
229     {
230       bs->wheel_time_clocks = next;
231       BFD_DBG ("timing_wheel_insert(%p, %lu (%ld clocks/%.2fs in the "
232                "future), %u);",
233                &bm->wheel, bs->wheel_time_clocks,
234                (i64) bs->wheel_time_clocks - clib_cpu_time_now (),
235                (i64) (bs->wheel_time_clocks - clib_cpu_time_now ()) /
236                bm->cpu_cps, bs->bs_idx);
237       timing_wheel_insert (&bm->wheel, bs->wheel_time_clocks, bs->bs_idx);
238       if (!handling_wakeup)
239         {
240           vlib_process_signal_event (bm->vlib_main,
241                                      bm->bfd_process_node_index,
242                                      BFD_EVENT_RESCHEDULE, bs->bs_idx);
243         }
244     }
245 }
246
247 static void
248 bfd_set_desired_min_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now,
249                         u32 desired_min_tx_us, int handling_wakeup)
250 {
251   bs->desired_min_tx_us = desired_min_tx_us;
252   bs->desired_min_tx_clocks = bfd_us_to_clocks (bm, bs->desired_min_tx_us);
253   BFD_DBG ("Set desired min tx to %uus/%lu clocks/%.2fs",
254            bs->desired_min_tx_us, bs->desired_min_tx_clocks,
255            bs->desired_min_tx_clocks / bm->cpu_cps);
256   bfd_recalc_detection_time (bm, bs);
257   bfd_recalc_tx_interval (bm, bs);
258   bfd_calc_next_tx (bm, bs, now);
259   bfd_set_timer (bm, bs, now, handling_wakeup);
260 }
261
262 static void
263 bfd_set_remote_required_min_rx (bfd_main_t * bm, bfd_session_t * bs,
264                                 u64 now,
265                                 u32 remote_required_min_rx_us,
266                                 int handling_wakeup)
267 {
268   bs->remote_min_rx_us = remote_required_min_rx_us;
269   bs->remote_min_rx_clocks = bfd_us_to_clocks (bm, bs->remote_min_rx_us);
270   BFD_DBG ("Set remote min rx to %uus/%lu clocks/%.2fs", bs->remote_min_rx_us,
271            bs->remote_min_rx_clocks, bs->remote_min_rx_clocks / bm->cpu_cps);
272   bfd_recalc_detection_time (bm, bs);
273   bfd_recalc_tx_interval (bm, bs);
274   bfd_calc_next_tx (bm, bs, now);
275   bfd_set_timer (bm, bs, now, handling_wakeup);
276 }
277
278 void
279 bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
280 {
281   BFD_DBG ("%U", format_bfd_session, bs);
282   bfd_recalc_tx_interval (bm, bs);
283   vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
284                              BFD_EVENT_NEW_SESSION, bs->bs_idx);
285 }
286
287 vnet_api_error_t
288 bfd_del_session (uword bs_idx)
289 {
290   const bfd_main_t *bm = &bfd_main;
291   if (!pool_is_free_index (bm->sessions, bs_idx))
292     {
293       bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
294       pool_put (bm->sessions, bs);
295       return 0;
296     }
297   else
298     {
299       BFD_ERR ("no such session");
300       return VNET_API_ERROR_BFD_NOENT;
301     }
302   return 0;
303 }
304
305 const char *
306 bfd_diag_code_string (bfd_diag_code_e diag)
307 {
308 #define F(n, t, s)             \
309   case BFD_DIAG_CODE_NAME (t): \
310     return s;
311   switch (diag)
312     {
313     foreach_bfd_diag_code (F)}
314   return "UNKNOWN";
315 #undef F
316 }
317
318 const char *
319 bfd_state_string (bfd_state_e state)
320 {
321 #define F(n, t, s)         \
322   case BFD_STATE_NAME (t): \
323     return s;
324   switch (state)
325     {
326     foreach_bfd_state (F)}
327   return "UNKNOWN";
328 #undef F
329 }
330
331 vnet_api_error_t
332 bfd_session_set_flags (u32 bs_idx, u8 admin_up_down)
333 {
334   bfd_main_t *bm = &bfd_main;
335   if (pool_is_free_index (bm->sessions, bs_idx))
336     {
337       BFD_ERR ("invalid bs_idx=%u", bs_idx);
338       return VNET_API_ERROR_BFD_NOENT;
339     }
340   bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
341   if (admin_up_down)
342     {
343       bfd_set_state (bm, bs, BFD_STATE_down, 0);
344     }
345   else
346     {
347       bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
348       bfd_set_state (bm, bs, BFD_STATE_admin_down, 0);
349     }
350   return 0;
351 }
352
353 u8 *
354 bfd_input_format_trace (u8 * s, va_list * args)
355 {
356   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
357   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
358   const bfd_input_trace_t *t = va_arg (*args, bfd_input_trace_t *);
359   const bfd_pkt_t *pkt = (bfd_pkt_t *) t->data;
360   if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
361     {
362       s = format (s, "BFD v%u, diag=%u(%s), state=%u(%s),\n"
363                   "    flags=(P:%u, F:%u, C:%u, A:%u, D:%u, M:%u), detect_mult=%u, "
364                   "length=%u\n",
365                   bfd_pkt_get_version (pkt), bfd_pkt_get_diag_code (pkt),
366                   bfd_diag_code_string (bfd_pkt_get_diag_code (pkt)),
367                   bfd_pkt_get_state (pkt),
368                   bfd_state_string (bfd_pkt_get_state (pkt)),
369                   bfd_pkt_get_poll (pkt), bfd_pkt_get_final (pkt),
370                   bfd_pkt_get_control_plane_independent (pkt),
371                   bfd_pkt_get_auth_present (pkt), bfd_pkt_get_demand (pkt),
372                   bfd_pkt_get_multipoint (pkt), pkt->head.detect_mult,
373                   pkt->head.length);
374       if (t->len >= sizeof (bfd_pkt_t)
375           && pkt->head.length >= sizeof (bfd_pkt_t))
376         {
377           s = format (s, "    my discriminator: %u\n", pkt->my_disc);
378           s = format (s, "    your discriminator: %u\n", pkt->your_disc);
379           s = format (s, "    desired min tx interval: %u\n",
380                       clib_net_to_host_u32 (pkt->des_min_tx));
381           s = format (s, "    required min rx interval: %u\n",
382                       clib_net_to_host_u32 (pkt->req_min_rx));
383           s = format (s, "    required min echo rx interval: %u\n",
384                       clib_net_to_host_u32 (pkt->req_min_echo_rx));
385         }
386     }
387
388   return s;
389 }
390
391 static void
392 bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
393                      int handling_wakeup)
394 {
395   BFD_DBG ("State changed: %U", format_bfd_session, bs);
396   bfd_event (bm, bs);
397   switch (bs->local_state)
398     {
399     case BFD_STATE_admin_down:
400       bfd_set_desired_min_tx (bm, bs, now,
401                               clib_max (bs->config_desired_min_tx_us,
402                                         BFD_DEFAULT_DESIRED_MIN_TX_US),
403                               handling_wakeup);
404       break;
405     case BFD_STATE_down:
406       bfd_set_desired_min_tx (bm, bs, now,
407                               clib_max (bs->config_desired_min_tx_us,
408                                         BFD_DEFAULT_DESIRED_MIN_TX_US),
409                               handling_wakeup);
410       break;
411     case BFD_STATE_init:
412       bfd_set_desired_min_tx (bm, bs, now,
413                               clib_max (bs->config_desired_min_tx_us,
414                                         BFD_DEFAULT_DESIRED_MIN_TX_US),
415                               handling_wakeup);
416       break;
417     case BFD_STATE_up:
418       bfd_set_desired_min_tx (bm, bs, now, bs->config_desired_min_tx_us,
419                               handling_wakeup);
420       break;
421     }
422 }
423
424 static void
425 bfd_add_transport_layer (vlib_main_t * vm, vlib_buffer_t * b,
426                          bfd_session_t * bs)
427 {
428   switch (bs->transport)
429     {
430     case BFD_TRANSPORT_UDP4:
431       /* fallthrough */
432     case BFD_TRANSPORT_UDP6:
433       BFD_DBG ("Transport bfd via udp, bs_idx=%u", bs->bs_idx);
434       bfd_add_udp_transport (vm, b, &bs->udp);
435       break;
436     }
437 }
438
439 static vlib_buffer_t *
440 bfd_create_frame (vlib_main_t * vm, vlib_node_runtime_t * rt,
441                   bfd_session_t * bs)
442 {
443   u32 bi;
444   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
445     {
446       clib_warning ("buffer allocation failure");
447       return NULL;
448     }
449
450   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
451   ASSERT (b->current_data == 0);
452
453   u32 *to_next;
454   u32 n_left_to_next;
455
456   vlib_get_next_frame (vm, rt, bfd_next_index_by_transport[bs->transport],
457                        to_next, n_left_to_next);
458
459   to_next[0] = bi;
460   n_left_to_next -= 1;
461
462   vlib_put_next_frame (vm, rt, bfd_next_index_by_transport[bs->transport],
463                        n_left_to_next);
464   return b;
465 }
466
467 static void
468 bfd_init_control_frame (vlib_buffer_t * b, bfd_session_t * bs)
469 {
470   bfd_pkt_t *pkt = vlib_buffer_get_current (b);
471   const u32 bfd_length = 24;
472   memset (pkt, 0, sizeof (*pkt));
473
474   bfd_pkt_set_version (pkt, 1);
475   bfd_pkt_set_diag_code (pkt, bs->local_diag);
476   bfd_pkt_set_state (pkt, bs->local_state);
477   if (bs->local_demand && BFD_STATE_up == bs->local_state &&
478       BFD_STATE_up == bs->remote_state)
479     {
480       bfd_pkt_set_demand (pkt);
481     }
482   pkt->head.detect_mult = bs->local_detect_mult;
483   pkt->head.length = clib_host_to_net_u32 (bfd_length);
484   pkt->my_disc = bs->local_discr;
485   pkt->your_disc = bs->remote_discr;
486   pkt->des_min_tx = clib_host_to_net_u32 (bs->desired_min_tx_us);
487   pkt->req_min_rx = clib_host_to_net_u32 (bs->required_min_rx_us);
488   pkt->req_min_echo_rx = clib_host_to_net_u32 (0);      /* FIXME */
489   b->current_length = bfd_length;
490 }
491
492 static void
493 bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
494                    bfd_main_t * bm, bfd_session_t * bs, u64 now,
495                    int handling_wakeup)
496 {
497   if (!bs->remote_min_rx_us)
498     {
499       BFD_DBG
500         ("bfd.RemoteMinRxInterval is zero, not sending periodic control "
501          "frame");
502       return;
503     }
504   /* FIXME
505      A system MUST NOT periodically transmit BFD Control packets if Demand
506      mode is active on the remote system (bfd.RemoteDemandMode is 1,
507      bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll
508      Sequence is not being transmitted.
509    */
510   /* sometimes the wheel expires an event a bit sooner than requested, account
511      for that here */
512   if (now + bm->wheel_inaccuracy >= bs->tx_timeout_clocks)
513     {
514       BFD_DBG ("Send periodic control frame for bs_idx=%lu", bs->bs_idx);
515       vlib_buffer_t *b = bfd_create_frame (vm, rt, bs);
516       if (!b)
517         {
518           return;
519         }
520       bfd_init_control_frame (b, bs);
521       bfd_add_transport_layer (vm, b, bs);
522       bfd_calc_next_tx (bm, bs, now);
523     }
524   else
525     {
526       BFD_DBG
527         ("No need to send control frame now, now is %lu, tx_timeout is %lu",
528          now, bs->tx_timeout_clocks);
529     }
530   bfd_set_timer (bm, bs, now, handling_wakeup);
531 }
532
533 void
534 bfd_send_final (vlib_main_t * vm, vlib_buffer_t * b, bfd_session_t * bs)
535 {
536   BFD_DBG ("Send final control frame for bs_idx=%lu", bs->bs_idx);
537   bfd_init_control_frame (b, bs);
538   bfd_pkt_set_final (vlib_buffer_get_current (b));
539   bfd_add_transport_layer (vm, b, bs);
540 }
541
542 static void
543 bfd_check_rx_timeout (bfd_main_t * bm, bfd_session_t * bs, u64 now,
544                       int handling_wakeup)
545 {
546   /* sometimes the wheel expires an event a bit sooner than requested, account
547      for that here */
548   if (bs->last_rx_clocks + bs->detection_time_clocks <=
549       now + bm->wheel_inaccuracy)
550     {
551       BFD_DBG ("Rx timeout, session goes down");
552       bfd_set_diag (bs, BFD_DIAG_CODE_det_time_exp);
553       bfd_set_state (bm, bs, BFD_STATE_down, handling_wakeup);
554     }
555 }
556
557 void
558 bfd_on_timeout (vlib_main_t * vm, vlib_node_runtime_t * rt, bfd_main_t * bm,
559                 bfd_session_t * bs, u64 now)
560 {
561   BFD_DBG ("Timeout for bs_idx=%lu", bs->bs_idx);
562   switch (bs->local_state)
563     {
564     case BFD_STATE_admin_down:
565       BFD_ERR ("Unexpected timeout when in %s state",
566                bfd_state_string (bs->local_state));
567       abort ();
568       break;
569     case BFD_STATE_down:
570       bfd_send_periodic (vm, rt, bm, bs, now, 1);
571       break;
572     case BFD_STATE_init:
573       BFD_ERR ("Unexpected timeout when in %s state",
574                bfd_state_string (bs->local_state));
575       abort ();
576       break;
577     case BFD_STATE_up:
578       bfd_check_rx_timeout (bm, bs, now, 1);
579       bfd_send_periodic (vm, rt, bm, bs, now, 1);
580       break;
581     }
582 }
583
584 /*
585  * bfd process node function
586  */
587 static uword
588 bfd_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
589 {
590   bfd_main_t *bm = &bfd_main;
591   u32 *expired = 0;
592   uword event_type, *event_data = 0;
593
594   /* So we can send events to the bfd process */
595   bm->bfd_process_node_index = bfd_process_node.index;
596
597   while (1)
598     {
599       u64 now = clib_cpu_time_now ();
600       u64 next_expire = timing_wheel_next_expiring_elt_time (&bm->wheel);
601       BFD_DBG ("timing_wheel_next_expiring_elt_time(%p) returns %lu",
602                &bm->wheel, next_expire);
603       if ((i64) next_expire < 0)
604         {
605           BFD_DBG ("wait for event without timeout");
606           (void) vlib_process_wait_for_event (vm);
607           event_type = vlib_process_get_events (vm, &event_data);
608         }
609       else
610         {
611           f64 timeout = ((i64) next_expire - (i64) now) / bm->cpu_cps;
612           BFD_DBG ("wait for event with timeout %.02f", timeout);
613           if (timeout < 0)
614             {
615               BFD_DBG ("negative timeout, already expired, skipping wait");
616               event_type = ~0;
617             }
618           else
619             {
620               (void) vlib_process_wait_for_event_or_clock (vm, timeout);
621               event_type = vlib_process_get_events (vm, &event_data);
622             }
623         }
624       now = clib_cpu_time_now ();
625       switch (event_type)
626         {
627         case ~0:                /* no events => timeout */
628           /* nothing to do here */
629           break;
630         case BFD_EVENT_RESCHEDULE:
631           /* nothing to do here - reschedule is done automatically after
632            * each event or timeout */
633           break;
634         case BFD_EVENT_NEW_SESSION:
635           do
636             {
637               bfd_session_t *bs =
638                 pool_elt_at_index (bm->sessions, *event_data);
639               bfd_send_periodic (vm, rt, bm, bs, now, 1);
640             }
641           while (0);
642           break;
643         default:
644           clib_warning ("BUG: event type 0x%wx", event_type);
645           break;
646         }
647       BFD_DBG ("advancing wheel, now is %lu", now);
648       BFD_DBG ("timing_wheel_advance (%p, %lu, %p, 0);", &bm->wheel, now,
649                expired);
650       expired = timing_wheel_advance (&bm->wheel, now, expired, 0);
651       BFD_DBG ("Expired %d elements", vec_len (expired));
652       u32 *p = NULL;
653       vec_foreach (p, expired)
654       {
655         const u32 bs_idx = *p;
656         if (!pool_is_free_index (bm->sessions, bs_idx))
657           {
658             bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
659             bfd_on_timeout (vm, rt, bm, bs, now);
660           }
661       }
662       if (expired)
663         {
664           _vec_len (expired) = 0;
665         }
666       if (event_data)
667         {
668           _vec_len (event_data) = 0;
669         }
670     }
671
672   return 0;
673 }
674
675 /*
676  * bfd process node declaration
677  */
678 /* *INDENT-OFF* */
679 VLIB_REGISTER_NODE (bfd_process_node, static) = {
680   .function = bfd_process,
681   .type = VLIB_NODE_TYPE_PROCESS,
682   .name = "bfd-process",
683   .n_next_nodes = BFD_OUTPUT_N_NEXT,
684   .next_nodes =
685       {
686 #define F(t, n) [BFD_OUTPUT_##t] = n,
687           foreach_bfd_transport (F)
688 #undef F
689       },
690 };
691 /* *INDENT-ON* */
692
693 static clib_error_t *
694 bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
695 {
696   // bfd_main_t *bm = &bfd_main;
697   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
698   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
699     {
700       /* TODO */
701     }
702   return 0;
703 }
704
705 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
706
707 static clib_error_t *
708 bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
709 {
710   // bfd_main_t *bm = &bfd_main;
711   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
712     {
713       /* TODO */
714     }
715   return 0;
716 }
717
718 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
719
720 /*
721  * setup function
722  */
723 static clib_error_t *
724 bfd_main_init (vlib_main_t * vm)
725 {
726   bfd_main_t *bm = &bfd_main;
727   bm->random_seed = random_default_seed ();
728   bm->vlib_main = vm;
729   bm->vnet_main = vnet_get_main ();
730   memset (&bm->wheel, 0, sizeof (bm->wheel));
731   bm->cpu_cps = 2590000000;     // vm->clib_time.clocks_per_second;
732   BFD_DBG ("cps is %.2f", bm->cpu_cps);
733   const u64 now = clib_cpu_time_now ();
734   timing_wheel_init (&bm->wheel, now, bm->cpu_cps);
735   bm->wheel_inaccuracy = 2 << bm->wheel.log2_clocks_per_bin;
736
737   return 0;
738 }
739
740 VLIB_INIT_FUNCTION (bfd_main_init);
741
742 bfd_session_t *
743 bfd_get_session (bfd_main_t * bm, bfd_transport_t t)
744 {
745   bfd_session_t *result;
746   pool_get (bm->sessions, result);
747   memset (result, 0, sizeof (*result));
748   result->bs_idx = result - bm->sessions;
749   result->transport = t;
750   result->local_discr = random_u32 (&bm->random_seed);
751   bfd_set_defaults (bm, result);
752   hash_set (bm->session_by_disc, result->local_discr, result->bs_idx);
753   return result;
754 }
755
756 void
757 bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
758 {
759   hash_unset (bm->session_by_disc, bs->local_discr);
760   pool_put (bm->sessions, bs);
761 }
762
763 bfd_session_t *
764 bfd_find_session_by_idx (bfd_main_t * bm, uword bs_idx)
765 {
766   if (!pool_is_free_index (bm->sessions, bs_idx))
767     {
768       return pool_elt_at_index (bm->sessions, bs_idx);
769     }
770   return NULL;
771 }
772
773 bfd_session_t *
774 bfd_find_session_by_disc (bfd_main_t * bm, u32 disc)
775 {
776   uword *p = hash_get (bfd_main.session_by_disc, disc);
777   if (p)
778     {
779       return pool_elt_at_index (bfd_main.sessions, *p);
780     }
781   return NULL;
782 }
783
784 /**
785  * @brief verify bfd packet - common checks
786  *
787  * @param pkt
788  *
789  * @return 1 if bfd packet is valid
790  */
791 int
792 bfd_verify_pkt_common (const bfd_pkt_t * pkt)
793 {
794   if (1 != bfd_pkt_get_version (pkt))
795     {
796       BFD_ERR ("BFD verification failed - unexpected version: '%d'",
797                bfd_pkt_get_version (pkt));
798       return 0;
799     }
800   if (pkt->head.length < sizeof (bfd_pkt_t) ||
801       (bfd_pkt_get_auth_present (pkt) &&
802        pkt->head.length < sizeof (bfd_pkt_with_auth_t)))
803     {
804       BFD_ERR ("BFD verification failed - unexpected length: '%d' (auth "
805                "present: %d)",
806                pkt->head.length, bfd_pkt_get_auth_present (pkt));
807       return 0;
808     }
809   if (!pkt->head.detect_mult)
810     {
811       BFD_ERR ("BFD verification failed - unexpected detect-mult: '%d'",
812                pkt->head.detect_mult);
813       return 0;
814     }
815   if (bfd_pkt_get_multipoint (pkt))
816     {
817       BFD_ERR ("BFD verification failed - unexpected multipoint: '%d'",
818                bfd_pkt_get_multipoint (pkt));
819       return 0;
820     }
821   if (!pkt->my_disc)
822     {
823       BFD_ERR ("BFD verification failed - unexpected my-disc: '%d'",
824                pkt->my_disc);
825       return 0;
826     }
827   if (!pkt->your_disc)
828     {
829       const u8 pkt_state = bfd_pkt_get_state (pkt);
830       if (pkt_state != BFD_STATE_down && pkt_state != BFD_STATE_admin_down)
831         {
832           BFD_ERR ("BFD verification failed - unexpected state: '%s' "
833                    "(your-disc is zero)", bfd_state_string (pkt_state));
834           return 0;
835         }
836     }
837   return 1;
838 }
839
840 /**
841  * @brief verify bfd packet - authentication
842  *
843  * @param pkt
844  *
845  * @return 1 if bfd packet is valid
846  */
847 int
848 bfd_verify_pkt_session (const bfd_pkt_t * pkt, u16 pkt_size,
849                         const bfd_session_t * bs)
850 {
851   const bfd_pkt_with_auth_t *with_auth = (bfd_pkt_with_auth_t *) pkt;
852   if (!bfd_pkt_get_auth_present (pkt))
853     {
854       if (pkt_size > sizeof (*pkt))
855         {
856           BFD_ERR ("BFD verification failed - unexpected packet size '%d' "
857                    "(auth not present)", pkt_size);
858           return 0;
859         }
860     }
861   else
862     {
863       if (!with_auth->auth.type)
864         {
865           BFD_ERR ("BFD verification failed - unexpected auth type: '%d'",
866                    with_auth->auth.type);
867           return 0;
868         }
869       /* TODO FIXME - implement the actual verification */
870     }
871   return 1;
872 }
873
874 void
875 bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
876 {
877   bfd_session_t *bs = bfd_find_session_by_idx (bm, bs_idx);
878   if (!bs)
879     {
880       return;
881     }
882   BFD_DBG ("Scanning bfd packet, bs_idx=%d", bs->bs_idx);
883   bs->remote_discr = pkt->my_disc;
884   bs->remote_state = bfd_pkt_get_state (pkt);
885   bs->remote_demand = bfd_pkt_get_demand (pkt);
886   u64 now = clib_cpu_time_now ();
887   bs->last_rx_clocks = now;
888   bs->remote_desired_min_tx_us = clib_net_to_host_u32 (pkt->des_min_tx);
889   bs->remote_detect_mult = pkt->head.detect_mult;
890   bfd_set_remote_required_min_rx (bm, bs, now,
891                                   clib_net_to_host_u32 (pkt->req_min_rx), 0);
892   /* FIXME
893      If the Required Min Echo RX Interval field is zero, the
894      transmission of Echo packets, if any, MUST cease.
895
896      If a Poll Sequence is being transmitted by the local system and
897      the Final (F) bit in the received packet is set, the Poll Sequence
898      MUST be terminated.
899    */
900   /* FIXME 6.8.2 */
901   /* FIXME 6.8.4 */
902   if (BFD_STATE_admin_down == bs->local_state)
903     return;
904   if (BFD_STATE_admin_down == bs->remote_state)
905     {
906       bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
907       bfd_set_state (bm, bs, BFD_STATE_down, 0);
908     }
909   else if (BFD_STATE_down == bs->local_state)
910     {
911       if (BFD_STATE_down == bs->remote_state)
912         {
913           bfd_set_state (bm, bs, BFD_STATE_init, 0);
914         }
915       else if (BFD_STATE_init == bs->remote_state)
916         {
917           bfd_set_state (bm, bs, BFD_STATE_up, 0);
918         }
919     }
920   else if (BFD_STATE_init == bs->local_state)
921     {
922       if (BFD_STATE_up == bs->remote_state ||
923           BFD_STATE_init == bs->remote_state)
924         {
925           bfd_set_state (bm, bs, BFD_STATE_up, 0);
926         }
927     }
928   else                          /* BFD_STATE_up == bs->local_state */
929     {
930       if (BFD_STATE_down == bs->remote_state)
931         {
932           bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
933           bfd_set_state (bm, bs, BFD_STATE_down, 0);
934         }
935     }
936 }
937
938 u8 *
939 format_bfd_session (u8 * s, va_list * args)
940 {
941   const bfd_session_t *bs = va_arg (*args, bfd_session_t *);
942   return format (s, "BFD(%u): bfd.SessionState=%s, "
943                  "bfd.RemoteSessionState=%s, "
944                  "bfd.LocalDiscr=%u, "
945                  "bfd.RemoteDiscr=%u, "
946                  "bfd.LocalDiag=%s, "
947                  "bfd.DesiredMinTxInterval=%u, "
948                  "bfd.RequiredMinRxInterval=%u, "
949                  "bfd.RemoteMinRxInterval=%u, "
950                  "bfd.DemandMode=%s, "
951                  "bfd.RemoteDemandMode=%s, "
952                  "bfd.DetectMult=%u, ",
953                  bs->bs_idx, bfd_state_string (bs->local_state),
954                  bfd_state_string (bs->remote_state), bs->local_discr,
955                  bs->remote_discr, bfd_diag_code_string (bs->local_diag),
956                  bs->desired_min_tx_us, bs->required_min_rx_us,
957                  bs->remote_min_rx_us, (bs->local_demand ? "yes" : "no"),
958                  (bs->remote_demand ? "yes" : "no"), bs->local_detect_mult);
959 }
960
961 bfd_main_t bfd_main;
962
963 /*
964  * fd.io coding-style-patch-verification: ON
965  *
966  * Local Variables:
967  * eval: (c-set-style "gnu")
968  * End:
969  */