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