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