BFD: respect remote demand mode
[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 #if WITH_LIBSSL > 0
29 #include <openssl/sha.h>
30 #endif
31
32 static u64
33 bfd_usec_to_clocks (const bfd_main_t * bm, u64 us)
34 {
35   return bm->cpu_cps * ((f64) us / USEC_PER_SECOND);
36 }
37
38 static vlib_node_registration_t bfd_process_node;
39
40 /* set to 0 here, real values filled at startup */
41 static u32 bfd_node_index_by_transport[] = {
42 #define F(t, n) [BFD_TRANSPORT_##t] = 0,
43   foreach_bfd_transport (F)
44 #undef F
45 };
46
47 static u8 *
48 format_bfd_auth_key (u8 * s, va_list * args)
49 {
50   const bfd_auth_key_t *key = va_arg (*args, bfd_auth_key_t *);
51   if (key)
52     {
53       s = format (s, "{auth-type=%u:%s, conf-key-id=%u, use-count=%u}, ",
54                   key->auth_type, bfd_auth_type_str (key->auth_type),
55                   key->conf_key_id, key->use_count);
56     }
57   else
58     {
59       s = format (s, "{none}");
60     }
61   return s;
62 }
63
64 /*
65  * We actually send all bfd pkts to the "error" node after scanning
66  * them, so the graph node has only one next-index. The "error-drop"
67  * node automatically bumps our per-node packet counters for us.
68  */
69 typedef enum
70 {
71   BFD_INPUT_NEXT_NORMAL,
72   BFD_INPUT_N_NEXT,
73 } bfd_input_next_t;
74
75 static void bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
76                                  int handling_wakeup);
77
78 static void
79 bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
80 {
81   bs->local_state = BFD_STATE_down;
82   bs->local_diag = BFD_DIAG_CODE_no_diag;
83   bs->remote_state = BFD_STATE_down;
84   bs->local_demand = 0;
85   bs->remote_discr = 0;
86   bs->config_desired_min_tx_usec = BFD_DEFAULT_DESIRED_MIN_TX_US;
87   bs->config_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
88   bs->effective_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
89   bs->remote_min_rx_usec = 1;
90   bs->remote_min_rx_clocks = bfd_usec_to_clocks (bm, bs->remote_min_rx_usec);
91   bs->remote_demand = 0;
92   bs->auth.remote_seq_number = 0;
93   bs->auth.remote_seq_number_known = 0;
94   bs->auth.local_seq_number = random_u32 (&bm->random_seed);
95 }
96
97 static void
98 bfd_set_diag (bfd_session_t * bs, bfd_diag_code_e code)
99 {
100   if (bs->local_diag != code)
101     {
102       BFD_DBG ("set local_diag, bs_idx=%d: '%d:%s'", bs->bs_idx, code,
103                bfd_diag_code_string (code));
104       bs->local_diag = code;
105     }
106 }
107
108 static void
109 bfd_set_state (bfd_main_t * bm, bfd_session_t * bs,
110                bfd_state_e new_state, int handling_wakeup)
111 {
112   if (bs->local_state != new_state)
113     {
114       BFD_DBG ("Change state, bs_idx=%d: %s->%s", bs->bs_idx,
115                bfd_state_string (bs->local_state),
116                bfd_state_string (new_state));
117       bs->local_state = new_state;
118       bfd_on_state_change (bm, bs, clib_cpu_time_now (), handling_wakeup);
119     }
120 }
121
122 static void
123 bfd_recalc_tx_interval (bfd_main_t * bm, bfd_session_t * bs)
124 {
125   if (!bs->local_demand)
126     {
127       bs->transmit_interval_clocks =
128         clib_max (bs->effective_desired_min_tx_clocks,
129                   bs->remote_min_rx_clocks);
130     }
131   else
132     {
133       /* TODO */
134     }
135   BFD_DBG ("Recalculated transmit interval %lu clocks/%.2fs",
136            bs->transmit_interval_clocks,
137            bs->transmit_interval_clocks / bm->cpu_cps);
138 }
139
140 static void
141 bfd_calc_next_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now)
142 {
143   if (!bs->local_demand)
144     {
145       if (bs->local_detect_mult > 1)
146         {
147           /* common case - 75-100% of transmit interval */
148           bs->tx_timeout_clocks = bs->last_tx_clocks +
149             (1 - .25 * (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, transmit now */
154               BFD_DBG ("Missed %lu transmit events (now is %lu, calc "
155                        "tx_timeout is %lu)",
156                        (now - bs->tx_timeout_clocks) /
157                        bs->transmit_interval_clocks,
158                        now, bs->tx_timeout_clocks);
159               bs->tx_timeout_clocks = now;
160             }
161         }
162       else
163         {
164           /* special case - 75-90% of transmit interval */
165           bs->tx_timeout_clocks =
166             bs->last_tx_clocks +
167             (.9 - .15 * (random_f64 (&bm->random_seed))) *
168             bs->transmit_interval_clocks;
169           if (bs->tx_timeout_clocks < now)
170             {
171               /* huh, we've missed it already, transmit now */
172               BFD_DBG ("Missed %lu transmit events (now is %lu, calc "
173                        "tx_timeout is %lu)",
174                        (now - bs->tx_timeout_clocks) /
175                        bs->transmit_interval_clocks,
176                        now, bs->tx_timeout_clocks);
177               bs->tx_timeout_clocks = now;
178             }
179         }
180     }
181   else
182     {
183       /* TODO */
184     }
185   if (bs->tx_timeout_clocks)
186     {
187       BFD_DBG ("Next transmit in %lu clocks/%.02fs@%lu",
188                bs->tx_timeout_clocks - now,
189                (bs->tx_timeout_clocks - now) / bm->cpu_cps,
190                bs->tx_timeout_clocks);
191     }
192 }
193
194 static void
195 bfd_recalc_detection_time (bfd_main_t * bm, bfd_session_t * bs)
196 {
197   if (!bs->local_demand)
198     {
199       /* asynchronous mode */
200       bs->detection_time_clocks =
201         bs->remote_detect_mult *
202         clib_max (bs->effective_required_min_rx_clocks,
203                   bs->remote_desired_min_tx_clocks);
204     }
205   else
206     {
207       /* demand mode */
208       bs->detection_time_clocks =
209         bs->local_detect_mult * clib_max (bs->config_desired_min_tx_clocks,
210                                           bs->remote_min_rx_clocks);
211     }
212   BFD_DBG ("Recalculated detection time %lu clocks/%.2fs",
213            bs->detection_time_clocks,
214            bs->detection_time_clocks / bm->cpu_cps);
215 }
216
217 static void
218 bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now,
219                int handling_wakeup)
220 {
221   u64 next = 0;
222   u64 rx_timeout = 0;
223   if (BFD_STATE_up == bs->local_state)
224     {
225       rx_timeout = bs->last_rx_clocks + bs->detection_time_clocks;
226     }
227   if (bs->tx_timeout_clocks && rx_timeout)
228     {
229       next = clib_min (bs->tx_timeout_clocks, rx_timeout);
230     }
231   else if (bs->tx_timeout_clocks)
232     {
233       next = bs->tx_timeout_clocks;
234     }
235   else if (rx_timeout)
236     {
237       next = rx_timeout;
238     }
239   BFD_DBG ("bs_idx=%u, tx_timeout=%lu, rx_timeout=%lu, next=%s", bs->bs_idx,
240            bs->tx_timeout_clocks, rx_timeout,
241            next == bs->tx_timeout_clocks ? "tx" : "rx");
242   /* sometimes the wheel expires an event a bit sooner than requested, account
243      for that here */
244   if (next && (now + bm->wheel_inaccuracy > bs->wheel_time_clocks ||
245                next < bs->wheel_time_clocks || !bs->wheel_time_clocks))
246     {
247       bs->wheel_time_clocks = next;
248       BFD_DBG ("timing_wheel_insert(%p, %lu (%ld clocks/%.2fs in the "
249                "future), %u);",
250                &bm->wheel, bs->wheel_time_clocks,
251                (i64) bs->wheel_time_clocks - clib_cpu_time_now (),
252                (i64) (bs->wheel_time_clocks - clib_cpu_time_now ()) /
253                bm->cpu_cps, bs->bs_idx);
254       timing_wheel_insert (&bm->wheel, bs->wheel_time_clocks, bs->bs_idx);
255       if (!handling_wakeup)
256         {
257           vlib_process_signal_event (bm->vlib_main,
258                                      bm->bfd_process_node_index,
259                                      BFD_EVENT_RESCHEDULE, bs->bs_idx);
260         }
261     }
262 }
263
264 static void
265 bfd_set_effective_desired_min_tx (bfd_main_t * bm,
266                                   bfd_session_t * bs, u64 now,
267                                   u64 desired_min_tx_clocks,
268                                   int handling_wakeup)
269 {
270   bs->effective_desired_min_tx_clocks = desired_min_tx_clocks;
271   BFD_DBG ("Set effective desired min tx to " BFD_CLK_FMT,
272            BFD_CLK_PRN (bs->effective_desired_min_tx_clocks));
273   bfd_recalc_detection_time (bm, bs);
274   bfd_recalc_tx_interval (bm, bs);
275   bfd_calc_next_tx (bm, bs, now);
276   bfd_set_timer (bm, bs, now, handling_wakeup);
277 }
278
279 static void
280 bfd_set_effective_required_min_rx (bfd_main_t * bm,
281                                    bfd_session_t * bs, u64 now,
282                                    u64 required_min_rx_clocks,
283                                    int handling_wakeup)
284 {
285   bs->effective_required_min_rx_clocks = required_min_rx_clocks;
286   BFD_DBG ("Set effective required min rx to " BFD_CLK_FMT,
287            BFD_CLK_PRN (bs->effective_required_min_rx_clocks));
288   bfd_recalc_detection_time (bm, bs);
289   bfd_set_timer (bm, bs, now, handling_wakeup);
290 }
291
292 static void
293 bfd_set_remote_required_min_rx (bfd_main_t * bm, bfd_session_t * bs,
294                                 u64 now,
295                                 u32 remote_required_min_rx_usec,
296                                 int handling_wakeup)
297 {
298   bs->remote_min_rx_usec = remote_required_min_rx_usec;
299   bs->remote_min_rx_clocks =
300     bfd_usec_to_clocks (bm, remote_required_min_rx_usec);
301   BFD_DBG ("Set remote min rx to " BFD_CLK_FMT,
302            BFD_CLK_PRN (bs->remote_min_rx_clocks));
303   bfd_recalc_detection_time (bm, bs);
304   bfd_recalc_tx_interval (bm, bs);
305   bfd_calc_next_tx (bm, bs, now);
306   bfd_set_timer (bm, bs, now, handling_wakeup);
307 }
308
309 void
310 bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
311 {
312   BFD_DBG ("%U", format_bfd_session, bs);
313   bfd_recalc_tx_interval (bm, bs);
314   vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
315                              BFD_EVENT_NEW_SESSION, bs->bs_idx);
316 }
317
318 vnet_api_error_t
319 bfd_del_session (uword bs_idx)
320 {
321   const bfd_main_t *bm = &bfd_main;
322   if (!pool_is_free_index (bm->sessions, bs_idx))
323     {
324       bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
325       pool_put (bm->sessions, bs);
326       return 0;
327     }
328   else
329     {
330       BFD_ERR ("no such session");
331       return VNET_API_ERROR_BFD_ENOENT;
332     }
333   return 0;
334 }
335
336 void
337 bfd_session_set_flags (bfd_session_t * bs, u8 admin_up_down)
338 {
339   bfd_main_t *bm = &bfd_main;
340   if (admin_up_down)
341     {
342       bfd_set_state (bm, bs, BFD_STATE_down, 0);
343     }
344   else
345     {
346       bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
347       bfd_set_state (bm, bs, BFD_STATE_admin_down, 0);
348     }
349 }
350
351 u8 *
352 bfd_input_format_trace (u8 * s, va_list * args)
353 {
354   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
355   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
356   const bfd_input_trace_t *t = va_arg (*args, bfd_input_trace_t *);
357   const bfd_pkt_t *pkt = (bfd_pkt_t *) t->data;
358   if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
359     {
360       s = format (s, "BFD v%u, diag=%u(%s), state=%u(%s),\n"
361                   "    flags=(P:%u, F:%u, C:%u, A:%u, D:%u, M:%u), "
362                   "detect_mult=%u, length=%u\n",
363                   bfd_pkt_get_version (pkt), bfd_pkt_get_diag_code (pkt),
364                   bfd_diag_code_string (bfd_pkt_get_diag_code (pkt)),
365                   bfd_pkt_get_state (pkt),
366                   bfd_state_string (bfd_pkt_get_state (pkt)),
367                   bfd_pkt_get_poll (pkt), bfd_pkt_get_final (pkt),
368                   bfd_pkt_get_control_plane_independent (pkt),
369                   bfd_pkt_get_auth_present (pkt), bfd_pkt_get_demand (pkt),
370                   bfd_pkt_get_multipoint (pkt), pkt->head.detect_mult,
371                   pkt->head.length);
372       if (t->len >= sizeof (bfd_pkt_t) &&
373           pkt->head.length >= sizeof (bfd_pkt_t))
374         {
375           s = format (s, "    my discriminator: %u\n",
376                       clib_net_to_host_u32 (pkt->my_disc));
377           s = format (s, "    your discriminator: %u\n",
378                       clib_net_to_host_u32 (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",
384                       clib_net_to_host_u32 (pkt->req_min_echo_rx));
385         }
386       if (t->len >= sizeof (bfd_pkt_with_common_auth_t) &&
387           pkt->head.length >= sizeof (bfd_pkt_with_common_auth_t) &&
388           bfd_pkt_get_auth_present (pkt))
389         {
390           const bfd_pkt_with_common_auth_t *with_auth = (void *) pkt;
391           const bfd_auth_common_t *common = &with_auth->common_auth;
392           s = format (s, "\n    auth len: %u\n", common->len);
393           s = format (s, "    auth type: %u:%s\n", common->type,
394                       bfd_auth_type_str (common->type));
395           if (t->len >= sizeof (bfd_pkt_with_sha1_auth_t) &&
396               pkt->head.length >= sizeof (bfd_pkt_with_sha1_auth_t) &&
397               (BFD_AUTH_TYPE_keyed_sha1 == common->type ||
398                BFD_AUTH_TYPE_meticulous_keyed_sha1 == common->type))
399             {
400               const bfd_pkt_with_sha1_auth_t *with_sha1 = (void *) pkt;
401               const bfd_auth_sha1_t *sha1 = &with_sha1->sha1_auth;
402               s = format (s, "    seq num: %u\n",
403                           clib_net_to_host_u32 (sha1->seq_num));
404               s = format (s, "    key id: %u\n", sha1->key_id);
405               s = format (s, "    hash: %U", format_hex_bytes, sha1->hash,
406                           sizeof (sha1->hash));
407             }
408         }
409       else
410         {
411           s = format (s, "\n");
412         }
413     }
414
415   return s;
416 }
417
418 static void
419 bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
420                      int handling_wakeup)
421 {
422   BFD_DBG ("State changed: %U", format_bfd_session, bs);
423   bfd_event (bm, bs);
424   switch (bs->local_state)
425     {
426     case BFD_STATE_admin_down:
427       bfd_set_effective_required_min_rx (bm, bs, now,
428                                          bs->config_required_min_rx_clocks,
429                                          handling_wakeup);
430       bfd_set_effective_desired_min_tx (bm, bs, now,
431                                         clib_max
432                                         (bs->config_desired_min_tx_clocks,
433                                          bm->default_desired_min_tx_clocks),
434                                         handling_wakeup);
435       break;
436     case BFD_STATE_down:
437       bfd_set_effective_required_min_rx (bm, bs, now,
438                                          bs->config_required_min_rx_clocks,
439                                          handling_wakeup);
440       bfd_set_effective_desired_min_tx (bm, bs, now,
441                                         clib_max
442                                         (bs->config_desired_min_tx_clocks,
443                                          bm->default_desired_min_tx_clocks),
444                                         handling_wakeup);
445       break;
446     case BFD_STATE_init:
447       bfd_set_effective_desired_min_tx (bm, bs, now,
448                                         clib_max
449                                         (bs->config_desired_min_tx_clocks,
450                                          bm->default_desired_min_tx_clocks),
451                                         handling_wakeup);
452       break;
453     case BFD_STATE_up:
454       if (POLL_NOT_NEEDED == bs->poll_state)
455         {
456           bfd_set_effective_required_min_rx (bm, bs, now,
457                                              bs->config_required_min_rx_clocks,
458                                              handling_wakeup);
459         }
460       bfd_set_effective_desired_min_tx (bm, bs, now,
461                                         bs->config_desired_min_tx_clocks,
462                                         handling_wakeup);
463       break;
464     }
465 }
466
467 static void
468 bfd_on_config_change (vlib_main_t * vm, vlib_node_runtime_t * rt,
469                       bfd_main_t * bm, bfd_session_t * bs, u64 now)
470 {
471   if (bs->remote_demand)
472     {
473       /* TODO - initiate poll sequence here */
474     }
475   else
476     {
477       /* asynchronous - poll is part of periodic - nothing to do here */
478     }
479   bfd_recalc_detection_time (bm, bs);
480   bfd_set_timer (bm, bs, now, 0);
481 }
482
483 static void
484 bfd_add_transport_layer (vlib_main_t * vm, vlib_buffer_t * b,
485                          bfd_session_t * bs)
486 {
487   switch (bs->transport)
488     {
489     case BFD_TRANSPORT_UDP4:
490       BFD_DBG ("Transport bfd via udp4, bs_idx=%u", bs->bs_idx);
491       bfd_add_udp4_transport (vm, b, bs);
492       break;
493     case BFD_TRANSPORT_UDP6:
494       BFD_DBG ("Transport bfd via udp6, bs_idx=%u", bs->bs_idx);
495       bfd_add_udp6_transport (vm, b, bs);
496       break;
497     }
498 }
499
500 static vlib_buffer_t *
501 bfd_create_frame_to_next_node (vlib_main_t * vm, bfd_session_t * bs)
502 {
503   u32 bi;
504   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
505     {
506       clib_warning ("buffer allocation failure");
507       return NULL;
508     }
509
510   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
511   ASSERT (b->current_data == 0);
512
513   vlib_frame_t *f =
514     vlib_get_frame_to_node (vm, bfd_node_index_by_transport[bs->transport]);
515
516   u32 *to_next = vlib_frame_vector_args (f);
517   to_next[0] = bi;
518   f->n_vectors = 1;
519
520   vlib_put_frame_to_node (vm, bfd_node_index_by_transport[bs->transport], f);
521   return b;
522 }
523
524 #if WITH_LIBSSL > 0
525 static void
526 bfd_add_sha1_auth_section (vlib_buffer_t * b, bfd_session_t * bs)
527 {
528   bfd_pkt_with_sha1_auth_t *pkt = vlib_buffer_get_current (b);
529   bfd_auth_sha1_t *auth = &pkt->sha1_auth;
530   b->current_length += sizeof (*auth);
531   pkt->pkt.head.length += sizeof (*auth);
532   bfd_pkt_set_auth_present (&pkt->pkt);
533   memset (auth, 0, sizeof (*auth));
534   auth->type_len.type = bs->auth.curr_key->auth_type;
535   /*
536    * only meticulous authentication types require incrementing seq number
537    * for every message, but doing so doesn't violate the RFC
538    */
539   ++bs->auth.local_seq_number;
540   auth->type_len.len = sizeof (bfd_auth_sha1_t);
541   auth->key_id = bs->auth.curr_bfd_key_id;
542   auth->seq_num = clib_host_to_net_u32 (bs->auth.local_seq_number);
543   /*
544    * first copy the password into the packet, then calculate the hash
545    * and finally replace the password with the calculated hash
546    */
547   clib_memcpy (auth->hash, bs->auth.curr_key->key,
548                sizeof (bs->auth.curr_key->key));
549   unsigned char hash[sizeof (auth->hash)];
550   SHA1 ((unsigned char *) pkt, sizeof (*pkt), hash);
551   BFD_DBG ("hashing: %U", format_hex_bytes, pkt, sizeof (*pkt));
552   clib_memcpy (auth->hash, hash, sizeof (hash));
553 }
554 #endif
555
556 static void
557 bfd_add_auth_section (vlib_buffer_t * b, bfd_session_t * bs)
558 {
559   if (bs->auth.curr_key)
560     {
561       const bfd_auth_type_e auth_type = bs->auth.curr_key->auth_type;
562       switch (auth_type)
563         {
564         case BFD_AUTH_TYPE_reserved:
565           /* fallthrough */
566         case BFD_AUTH_TYPE_simple_password:
567           /* fallthrough */
568         case BFD_AUTH_TYPE_keyed_md5:
569           /* fallthrough */
570         case BFD_AUTH_TYPE_meticulous_keyed_md5:
571           clib_warning ("Internal error, unexpected BFD auth type '%d'",
572                         auth_type);
573           break;
574 #if WITH_LIBSSL > 0
575         case BFD_AUTH_TYPE_keyed_sha1:
576           /* fallthrough */
577         case BFD_AUTH_TYPE_meticulous_keyed_sha1:
578           bfd_add_sha1_auth_section (b, bs);
579           break;
580 #else
581         case BFD_AUTH_TYPE_keyed_sha1:
582           /* fallthrough */
583         case BFD_AUTH_TYPE_meticulous_keyed_sha1:
584           clib_warning ("Internal error, unexpected BFD auth type '%d'",
585                         auth_type);
586           break;
587 #endif
588         }
589     }
590 }
591
592 static void
593 bfd_init_control_frame (vlib_buffer_t * b, bfd_session_t * bs)
594 {
595   bfd_pkt_t *pkt = vlib_buffer_get_current (b);
596
597   u32 bfd_length = 0;
598   bfd_length = sizeof (bfd_pkt_t);
599   memset (pkt, 0, sizeof (*pkt));
600   bfd_pkt_set_version (pkt, 1);
601   bfd_pkt_set_diag_code (pkt, bs->local_diag);
602   bfd_pkt_set_state (pkt, bs->local_state);
603   if (bs->local_demand && BFD_STATE_up == bs->local_state &&
604       BFD_STATE_up == bs->remote_state)
605     {
606       bfd_pkt_set_demand (pkt);
607     }
608   pkt->head.detect_mult = bs->local_detect_mult;
609   pkt->head.length = clib_host_to_net_u32 (bfd_length);
610   pkt->my_disc = bs->local_discr;
611   pkt->your_disc = bs->remote_discr;
612   pkt->des_min_tx = clib_host_to_net_u32 (bs->config_desired_min_tx_usec);
613   pkt->req_min_rx = clib_host_to_net_u32 (bs->config_required_min_rx_usec);
614   pkt->req_min_echo_rx = clib_host_to_net_u32 (1);
615   b->current_length = bfd_length;
616 }
617
618 static void
619 bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
620                    bfd_main_t * bm, bfd_session_t * bs, u64 now,
621                    int handling_wakeup)
622 {
623   if (!bs->remote_min_rx_usec)
624     {
625       BFD_DBG
626         ("bfd.RemoteMinRxInterval is zero, not sending periodic control "
627          "frame");
628       return;
629     }
630   if (POLL_NOT_NEEDED == bs->poll_state && bs->remote_demand &&
631       BFD_STATE_up == bs->local_state && BFD_STATE_up == bs->remote_state)
632     {
633       /*
634        * A system MUST NOT periodically transmit BFD Control packets if Demand
635        * mode is active on the remote system (bfd.RemoteDemandMode is 1,
636        * bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll
637        * Sequence is not being transmitted.
638        */
639       BFD_DBG ("bfd.RemoteDemand is non-zero, not sending periodic control "
640                "frame");
641       return;
642     }
643   /* sometimes the wheel expires an event a bit sooner than requested, account
644      for that here */
645   if (now + bm->wheel_inaccuracy >= bs->tx_timeout_clocks)
646     {
647       BFD_DBG ("Send periodic control frame for bs_idx=%lu: %U", bs->bs_idx,
648                format_bfd_session, bs);
649       vlib_buffer_t *b = bfd_create_frame_to_next_node (vm, bs);
650       if (!b)
651         {
652           return;
653         }
654       bfd_init_control_frame (b, bs);
655       if (POLL_NOT_NEEDED != bs->poll_state)
656         {
657           /* here we are either beginning a new poll sequence or retrying .. */
658           bfd_pkt_set_poll (vlib_buffer_get_current (b));
659           bs->poll_state = POLL_IN_PROGRESS;
660           BFD_DBG ("Setting poll bit in packet, bs_idx=%u", bs->bs_idx);
661         }
662       bfd_add_auth_section (b, bs);
663       bfd_add_transport_layer (vm, b, bs);
664       bs->last_tx_clocks = now;
665       bfd_calc_next_tx (bm, bs, now);
666     }
667   else
668     {
669       BFD_DBG
670         ("No need to send control frame now, now is %lu, tx_timeout is %lu",
671          now, bs->tx_timeout_clocks);
672     }
673   bfd_set_timer (bm, bs, now, handling_wakeup);
674 }
675
676 void
677 bfd_init_final_control_frame (vlib_main_t * vm, vlib_buffer_t * b,
678                               bfd_session_t * bs)
679 {
680   BFD_DBG ("Send final control frame for bs_idx=%lu", bs->bs_idx);
681   bfd_init_control_frame (b, bs);
682   bfd_pkt_set_final (vlib_buffer_get_current (b));
683   bfd_add_auth_section (b, bs);
684   bfd_add_transport_layer (vm, b, bs);
685   bs->last_tx_clocks = clib_cpu_time_now ();
686   /*
687    * RFC allows to include changes in final frame, so if there were any
688    * pending, we already did that, thus we can clear any pending poll needs
689    */
690   bs->poll_state = POLL_NOT_NEEDED;
691 }
692
693 static void
694 bfd_check_rx_timeout (bfd_main_t * bm, bfd_session_t * bs, u64 now,
695                       int handling_wakeup)
696 {
697   /* sometimes the wheel expires an event a bit sooner than requested, account
698      for that here */
699   if (bs->last_rx_clocks + bs->detection_time_clocks <=
700       now + bm->wheel_inaccuracy)
701     {
702       BFD_DBG ("Rx timeout, session goes down");
703       bfd_set_diag (bs, BFD_DIAG_CODE_det_time_exp);
704       bfd_set_state (bm, bs, BFD_STATE_down, handling_wakeup);
705       /*
706        * If the remote system does not receive any
707        * BFD Control packets for a Detection Time, it SHOULD reset
708        * bfd.RemoteMinRxInterval to its initial value of 1 (per section 6.8.1,
709        * since it is no longer required to maintain previous session state)
710        * and then can transmit at its own rate.
711        */
712       bfd_set_remote_required_min_rx (bm, bs, now, 1, handling_wakeup);
713     }
714 }
715
716 void
717 bfd_on_timeout (vlib_main_t * vm, vlib_node_runtime_t * rt, bfd_main_t * bm,
718                 bfd_session_t * bs, u64 now)
719 {
720   BFD_DBG ("Timeout for bs_idx=%lu", bs->bs_idx);
721   switch (bs->local_state)
722     {
723     case BFD_STATE_admin_down:
724       BFD_ERR ("Unexpected timeout when in %s state",
725                bfd_state_string (bs->local_state));
726       abort ();
727       break;
728     case BFD_STATE_down:
729       bfd_send_periodic (vm, rt, bm, bs, now, 1);
730       break;
731     case BFD_STATE_init:
732       BFD_ERR ("Unexpected timeout when in %s state",
733                bfd_state_string (bs->local_state));
734       abort ();
735       break;
736     case BFD_STATE_up:
737       bfd_check_rx_timeout (bm, bs, now, 1);
738       bfd_send_periodic (vm, rt, bm, bs, now, 1);
739       break;
740     }
741 }
742
743 /*
744  * bfd process node function
745  */
746 static uword
747 bfd_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
748 {
749   bfd_main_t *bm = &bfd_main;
750   u32 *expired = 0;
751   uword event_type, *event_data = 0;
752
753   /* So we can send events to the bfd process */
754   bm->bfd_process_node_index = bfd_process_node.index;
755
756   while (1)
757     {
758       u64 now = clib_cpu_time_now ();
759       u64 next_expire = timing_wheel_next_expiring_elt_time (&bm->wheel);
760       BFD_DBG ("timing_wheel_next_expiring_elt_time(%p) returns %lu",
761                &bm->wheel, next_expire);
762       if ((i64) next_expire < 0)
763         {
764           BFD_DBG ("wait for event without timeout");
765           (void) vlib_process_wait_for_event (vm);
766           event_type = vlib_process_get_events (vm, &event_data);
767         }
768       else
769         {
770           f64 timeout = ((i64) next_expire - (i64) now) / bm->cpu_cps;
771           BFD_DBG ("wait for event with timeout %.02f", timeout);
772           if (timeout < 0)
773             {
774               BFD_DBG ("negative timeout, already expired, skipping wait");
775               event_type = ~0;
776             }
777           else
778             {
779               (void) vlib_process_wait_for_event_or_clock (vm, timeout);
780               event_type = vlib_process_get_events (vm, &event_data);
781             }
782         }
783       now = clib_cpu_time_now ();
784       switch (event_type)
785         {
786         case ~0:                /* no events => timeout */
787           /* nothing to do here */
788           break;
789         case BFD_EVENT_RESCHEDULE:
790           /* nothing to do here - reschedule is done automatically after
791            * each event or timeout */
792           break;
793         case BFD_EVENT_NEW_SESSION:
794           if (!pool_is_free_index (bm->sessions, *event_data))
795             {
796               bfd_session_t *bs =
797                 pool_elt_at_index (bm->sessions, *event_data);
798               bfd_send_periodic (vm, rt, bm, bs, now, 1);
799             }
800           else
801             {
802               BFD_DBG ("Ignoring event for non-existent session index %u",
803                        (u32) * event_data);
804             }
805           break;
806         case BFD_EVENT_CONFIG_CHANGED:
807           if (!pool_is_free_index (bm->sessions, *event_data))
808             {
809               bfd_session_t *bs =
810                 pool_elt_at_index (bm->sessions, *event_data);
811               bfd_on_config_change (vm, rt, bm, bs, now);
812             }
813           else
814             {
815               BFD_DBG ("Ignoring event for non-existent session index %u",
816                        (u32) * event_data);
817             }
818           break;
819         default:
820           clib_warning ("BUG: event type 0x%wx", event_type);
821           break;
822         }
823       BFD_DBG ("advancing wheel, now is %lu", now);
824       BFD_DBG ("timing_wheel_advance (%p, %lu, %p, 0);", &bm->wheel, now,
825                expired);
826       expired = timing_wheel_advance (&bm->wheel, now, expired, 0);
827       BFD_DBG ("Expired %d elements", vec_len (expired));
828       u32 *p = NULL;
829       vec_foreach (p, expired)
830       {
831         const u32 bs_idx = *p;
832         if (!pool_is_free_index (bm->sessions, bs_idx))
833           {
834             bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
835             bfd_on_timeout (vm, rt, bm, bs, now);
836           }
837       }
838       if (expired)
839         {
840           _vec_len (expired) = 0;
841         }
842       if (event_data)
843         {
844           _vec_len (event_data) = 0;
845         }
846     }
847
848   return 0;
849 }
850
851 /*
852  * bfd process node declaration
853  */
854 /* *INDENT-OFF* */
855 VLIB_REGISTER_NODE (bfd_process_node, static) = {
856   .function = bfd_process,
857   .type = VLIB_NODE_TYPE_PROCESS,
858   .name = "bfd-process",
859   .n_next_nodes = 0,
860   .next_nodes = {},
861 };
862 /* *INDENT-ON* */
863
864 static clib_error_t *
865 bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
866 {
867   // bfd_main_t *bm = &bfd_main;
868   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
869   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
870     {
871       /* TODO */
872     }
873   return 0;
874 }
875
876 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
877
878 static clib_error_t *
879 bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
880 {
881   // bfd_main_t *bm = &bfd_main;
882   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
883     {
884       /* TODO */
885     }
886   return 0;
887 }
888
889 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
890
891 /*
892  * setup function
893  */
894 static clib_error_t *
895 bfd_main_init (vlib_main_t * vm)
896 {
897 #if BFD_DEBUG
898   setbuf (stdout, NULL);
899 #endif
900   bfd_main_t *bm = &bfd_main;
901   bm->random_seed = random_default_seed ();
902   bm->vlib_main = vm;
903   bm->vnet_main = vnet_get_main ();
904   memset (&bm->wheel, 0, sizeof (bm->wheel));
905   bm->cpu_cps = vm->clib_time.clocks_per_second;
906   BFD_DBG ("cps is %.2f", bm->cpu_cps);
907   bm->default_desired_min_tx_clocks =
908     bfd_usec_to_clocks (bm, BFD_DEFAULT_DESIRED_MIN_TX_US);
909   const u64 now = clib_cpu_time_now ();
910   timing_wheel_init (&bm->wheel, now, bm->cpu_cps);
911   bm->wheel_inaccuracy = 2 << bm->wheel.log2_clocks_per_bin;
912
913   vlib_node_t *node = NULL;
914 #define F(t, n)                                                 \
915   node = vlib_get_node_by_name (vm, (u8 *)n);                   \
916   bfd_node_index_by_transport[BFD_TRANSPORT_##t] = node->index; \
917   BFD_DBG ("node '%s' has index %u", n, node->index);
918   foreach_bfd_transport (F);
919 #undef F
920   return 0;
921 }
922
923 VLIB_INIT_FUNCTION (bfd_main_init);
924
925 bfd_session_t *
926 bfd_get_session (bfd_main_t * bm, bfd_transport_t t)
927 {
928   bfd_session_t *result;
929   pool_get (bm->sessions, result);
930   memset (result, 0, sizeof (*result));
931   result->bs_idx = result - bm->sessions;
932   result->transport = t;
933   result->local_discr = random_u32 (&bm->random_seed);
934   bfd_set_defaults (bm, result);
935   hash_set (bm->session_by_disc, result->local_discr, result->bs_idx);
936   return result;
937 }
938
939 void
940 bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
941 {
942   if (bs->auth.curr_key)
943     {
944       --bs->auth.curr_key->use_count;
945     }
946   if (bs->auth.next_key)
947     {
948       --bs->auth.next_key->use_count;
949     }
950   hash_unset (bm->session_by_disc, bs->local_discr);
951   pool_put (bm->sessions, bs);
952 }
953
954 bfd_session_t *
955 bfd_find_session_by_idx (bfd_main_t * bm, uword bs_idx)
956 {
957   if (!pool_is_free_index (bm->sessions, bs_idx))
958     {
959       return pool_elt_at_index (bm->sessions, bs_idx);
960     }
961   return NULL;
962 }
963
964 bfd_session_t *
965 bfd_find_session_by_disc (bfd_main_t * bm, u32 disc)
966 {
967   uword *p = hash_get (bfd_main.session_by_disc, disc);
968   if (p)
969     {
970       return pool_elt_at_index (bfd_main.sessions, *p);
971     }
972   return NULL;
973 }
974
975 /**
976  * @brief verify bfd packet - common checks
977  *
978  * @param pkt
979  *
980  * @return 1 if bfd packet is valid
981  */
982 int
983 bfd_verify_pkt_common (const bfd_pkt_t * pkt)
984 {
985   if (1 != bfd_pkt_get_version (pkt))
986     {
987       BFD_ERR ("BFD verification failed - unexpected version: '%d'",
988                bfd_pkt_get_version (pkt));
989       return 0;
990     }
991   if (pkt->head.length < sizeof (bfd_pkt_t) ||
992       (bfd_pkt_get_auth_present (pkt) &&
993        pkt->head.length < sizeof (bfd_pkt_with_common_auth_t)))
994     {
995       BFD_ERR ("BFD verification failed - unexpected length: '%d' (auth "
996                "present: %d)",
997                pkt->head.length, bfd_pkt_get_auth_present (pkt));
998       return 0;
999     }
1000   if (!pkt->head.detect_mult)
1001     {
1002       BFD_ERR ("BFD verification failed - unexpected detect-mult: '%d'",
1003                pkt->head.detect_mult);
1004       return 0;
1005     }
1006   if (bfd_pkt_get_multipoint (pkt))
1007     {
1008       BFD_ERR ("BFD verification failed - unexpected multipoint: '%d'",
1009                bfd_pkt_get_multipoint (pkt));
1010       return 0;
1011     }
1012   if (!pkt->my_disc)
1013     {
1014       BFD_ERR ("BFD verification failed - unexpected my-disc: '%d'",
1015                pkt->my_disc);
1016       return 0;
1017     }
1018   if (!pkt->your_disc)
1019     {
1020       const u8 pkt_state = bfd_pkt_get_state (pkt);
1021       if (pkt_state != BFD_STATE_down && pkt_state != BFD_STATE_admin_down)
1022         {
1023           BFD_ERR ("BFD verification failed - unexpected state: '%s' "
1024                    "(your-disc is zero)", bfd_state_string (pkt_state));
1025           return 0;
1026         }
1027     }
1028   return 1;
1029 }
1030
1031 static void
1032 bfd_session_switch_auth_to_next (bfd_session_t * bs)
1033 {
1034   BFD_DBG ("Switching authentication key from %U to %U for bs_idx=%u",
1035            format_bfd_auth_key, bs->auth.curr_key, format_bfd_auth_key,
1036            bs->auth.next_key, bs->bs_idx);
1037   bs->auth.is_delayed = 0;
1038   if (bs->auth.curr_key)
1039     {
1040       --bs->auth.curr_key->use_count;
1041     }
1042   bs->auth.curr_key = bs->auth.next_key;
1043   bs->auth.next_key = NULL;
1044   bs->auth.curr_bfd_key_id = bs->auth.next_bfd_key_id;
1045 }
1046
1047 static int
1048 bfd_auth_type_is_meticulous (bfd_auth_type_e auth_type)
1049 {
1050   if (BFD_AUTH_TYPE_meticulous_keyed_md5 == auth_type ||
1051       BFD_AUTH_TYPE_meticulous_keyed_sha1 == auth_type)
1052     {
1053       return 1;
1054     }
1055   return 0;
1056 }
1057
1058 static int
1059 bfd_verify_pkt_auth_seq_num (bfd_session_t * bs,
1060                              u32 received_seq_num, int is_meticulous)
1061 {
1062   /*
1063    * RFC 5880 6.8.1:
1064    *
1065    * This variable MUST be set to zero after no packets have been
1066    * received on this session for at least twice the Detection Time.
1067    */
1068   u64 now = clib_cpu_time_now ();
1069   if (now - bs->last_rx_clocks > bs->detection_time_clocks * 2)
1070     {
1071       BFD_DBG ("BFD peer unresponsive for %lu clocks, which is > 2 * "
1072                "detection_time=%u clocks, resetting remote_seq_number_known "
1073                "flag",
1074                now - bs->last_rx_clocks, bs->detection_time_clocks * 2);
1075       bs->auth.remote_seq_number_known = 0;
1076     }
1077   if (bs->auth.remote_seq_number_known)
1078     {
1079       /* remote sequence number is known, verify its validity */
1080       const u32 max_u32 = 0xffffffff;
1081       /* the calculation might wrap, account for the special case... */
1082       if (bs->auth.remote_seq_number > max_u32 - 3 * bs->local_detect_mult)
1083         {
1084           /*
1085            * special case
1086            *
1087            *        x                   y                   z
1088            *  |----------+----------------------------+-----------|
1089            *  0          ^                            ^ 0xffffffff
1090            *             |        remote_seq_num------+
1091            *             |
1092            *             +-----(remote_seq_num + 3*detect_mult) % * 0xffffffff
1093            *
1094            *    x + y + z = 0xffffffff
1095            *    x + z = 3 * detect_mult
1096            */
1097           const u32 z = max_u32 - bs->auth.remote_seq_number;
1098           const u32 x = 3 * bs->local_detect_mult - z;
1099           if (received_seq_num > x &&
1100               received_seq_num < bs->auth.remote_seq_number + is_meticulous)
1101             {
1102               BFD_ERR
1103                 ("Recvd sequence number=%u out of ranges <0, %u>, <%u, %u>",
1104                  received_seq_num, x,
1105                  bs->auth.remote_seq_number + is_meticulous, max_u32);
1106               return 0;
1107             }
1108         }
1109       else
1110         {
1111           /* regular case */
1112           const u32 min = bs->auth.remote_seq_number + is_meticulous;
1113           const u32 max =
1114             bs->auth.remote_seq_number + 3 * bs->local_detect_mult;
1115           if (received_seq_num < min || received_seq_num > max)
1116             {
1117               BFD_ERR ("Recvd sequence number=%u out of range <%u, %u>",
1118                        received_seq_num, min, max);
1119               return 0;
1120             }
1121         }
1122     }
1123   return 1;
1124 }
1125
1126 static int
1127 bfd_verify_pkt_auth_key_sha1 (const bfd_pkt_t * pkt, u32 pkt_size,
1128                               bfd_session_t * bs, u8 bfd_key_id,
1129                               bfd_auth_key_t * auth_key)
1130 {
1131   ASSERT (auth_key->auth_type == BFD_AUTH_TYPE_keyed_sha1 ||
1132           auth_key->auth_type == BFD_AUTH_TYPE_meticulous_keyed_sha1);
1133
1134   u8 result[SHA_DIGEST_LENGTH];
1135   bfd_pkt_with_common_auth_t *with_common = (void *) pkt;
1136   if (pkt_size < sizeof (*with_common))
1137     {
1138       BFD_ERR ("Packet size too small to hold authentication common header");
1139       return 0;
1140     }
1141   if (with_common->common_auth.type != auth_key->auth_type)
1142     {
1143       BFD_ERR ("BFD auth type mismatch, packet auth=%d:%s doesn't match "
1144                "in-use auth=%d:%s",
1145                with_common->common_auth.type,
1146                bfd_auth_type_str (with_common->common_auth.type),
1147                auth_key->auth_type, bfd_auth_type_str (auth_key->auth_type));
1148       return 0;
1149     }
1150   bfd_pkt_with_sha1_auth_t *with_sha1 = (void *) pkt;
1151   if (pkt_size < sizeof (*with_sha1) ||
1152       with_sha1->sha1_auth.type_len.len < sizeof (with_sha1->sha1_auth))
1153     {
1154       BFD_ERR
1155         ("BFD size mismatch, payload size=%u, expected=%u, auth_len=%u, "
1156          "expected=%u", pkt_size, sizeof (*with_sha1),
1157          with_sha1->sha1_auth.type_len.len, sizeof (with_sha1->sha1_auth));
1158       return 0;
1159     }
1160   if (with_sha1->sha1_auth.key_id != bfd_key_id)
1161     {
1162       BFD_ERR
1163         ("BFD key ID mismatch, packet key ID=%u doesn't match key ID=%u%s",
1164          with_sha1->sha1_auth.key_id, bfd_key_id,
1165          bs->
1166          auth.is_delayed ? " (but a delayed auth change is scheduled)" : "");
1167       return 0;
1168     }
1169   SHA_CTX ctx;
1170   if (!SHA1_Init (&ctx))
1171     {
1172       BFD_ERR ("SHA1_Init failed");
1173       return 0;
1174     }
1175   /* ignore last 20 bytes - use the actual key data instead pkt data */
1176   if (!SHA1_Update (&ctx, with_sha1,
1177                     sizeof (*with_sha1) - sizeof (with_sha1->sha1_auth.hash)))
1178     {
1179       BFD_ERR ("SHA1_Update failed");
1180       return 0;
1181     }
1182   if (!SHA1_Update (&ctx, auth_key->key, sizeof (auth_key->key)))
1183     {
1184       BFD_ERR ("SHA1_Update failed");
1185       return 0;
1186     }
1187   if (!SHA1_Final (result, &ctx))
1188     {
1189       BFD_ERR ("SHA1_Final failed");
1190       return 0;
1191     }
1192   if (0 == memcmp (result, with_sha1->sha1_auth.hash, SHA_DIGEST_LENGTH))
1193     {
1194       return 1;
1195     }
1196   BFD_ERR ("SHA1 hash: %U doesn't match the expected value: %U",
1197            format_hex_bytes, with_sha1->sha1_auth.hash, SHA_DIGEST_LENGTH,
1198            format_hex_bytes, result, SHA_DIGEST_LENGTH);
1199   return 0;
1200 }
1201
1202 static int
1203 bfd_verify_pkt_auth_key (const bfd_pkt_t * pkt, u32 pkt_size,
1204                          bfd_session_t * bs, u8 bfd_key_id,
1205                          bfd_auth_key_t * auth_key)
1206 {
1207   switch (auth_key->auth_type)
1208     {
1209     case BFD_AUTH_TYPE_reserved:
1210       clib_warning ("Internal error, unexpected auth_type=%d:%s",
1211                     auth_key->auth_type,
1212                     bfd_auth_type_str (auth_key->auth_type));
1213       return 0;
1214     case BFD_AUTH_TYPE_simple_password:
1215       clib_warning
1216         ("Internal error, not implemented, unexpected auth_type=%d:%s",
1217          auth_key->auth_type, bfd_auth_type_str (auth_key->auth_type));
1218       return 0;
1219     case BFD_AUTH_TYPE_keyed_md5:
1220       /* fallthrough */
1221     case BFD_AUTH_TYPE_meticulous_keyed_md5:
1222       clib_warning
1223         ("Internal error, not implemented, unexpected auth_type=%d:%s",
1224          auth_key->auth_type, bfd_auth_type_str (auth_key->auth_type));
1225       return 0;
1226     case BFD_AUTH_TYPE_keyed_sha1:
1227       /* fallthrough */
1228     case BFD_AUTH_TYPE_meticulous_keyed_sha1:
1229 #if WITH_LIBSSL > 0
1230       do
1231         {
1232           const u32 seq_num = clib_net_to_host_u32 (((bfd_pkt_with_sha1_auth_t
1233                                                       *) pkt)->
1234                                                     sha1_auth.seq_num);
1235           return bfd_verify_pkt_auth_seq_num (bs, seq_num,
1236                                               bfd_auth_type_is_meticulous
1237                                               (auth_key->auth_type))
1238             && bfd_verify_pkt_auth_key_sha1 (pkt, pkt_size, bs, bfd_key_id,
1239                                              auth_key);
1240         }
1241       while (0);
1242 #else
1243       clib_warning
1244         ("Internal error, attempt to use SHA1 without SSL support");
1245       return 0;
1246 #endif
1247     }
1248   return 0;
1249 }
1250
1251 /**
1252  * @brief verify bfd packet - authentication
1253  *
1254  * @param pkt
1255  *
1256  * @return 1 if bfd packet is valid
1257  */
1258 int
1259 bfd_verify_pkt_auth (const bfd_pkt_t * pkt, u16 pkt_size, bfd_session_t * bs)
1260 {
1261   if (bfd_pkt_get_auth_present (pkt))
1262     {
1263       /* authentication present in packet */
1264       if (!bs->auth.curr_key)
1265         {
1266           /* currently not using authentication - can we turn it on? */
1267           if (bs->auth.is_delayed && bs->auth.next_key)
1268             {
1269               /* yes, switch is scheduled - make sure the auth is valid */
1270               if (bfd_verify_pkt_auth_key (pkt, pkt_size, bs,
1271                                            bs->auth.next_bfd_key_id,
1272                                            bs->auth.next_key))
1273                 {
1274                   /* auth matches next key, do the switch, packet is valid */
1275                   bfd_session_switch_auth_to_next (bs);
1276                   return 1;
1277                 }
1278             }
1279         }
1280       else
1281         {
1282           /* yes, using authentication, verify the key */
1283           if (bfd_verify_pkt_auth_key (pkt, pkt_size, bs,
1284                                        bs->auth.curr_bfd_key_id,
1285                                        bs->auth.curr_key))
1286             {
1287               /* verification passed, packet is valid */
1288               return 1;
1289             }
1290           else
1291             {
1292               /* verification failed - but maybe we need to switch key */
1293               if (bs->auth.is_delayed && bs->auth.next_key)
1294                 {
1295                   /* delayed switch present, verify if that key works */
1296                   if (bfd_verify_pkt_auth_key (pkt, pkt_size, bs,
1297                                                bs->auth.next_bfd_key_id,
1298                                                bs->auth.next_key))
1299                     {
1300                       /* auth matches next key, switch key, packet is valid */
1301                       bfd_session_switch_auth_to_next (bs);
1302                       return 1;
1303                     }
1304                 }
1305             }
1306         }
1307     }
1308   else
1309     {
1310       /* authentication in packet not present */
1311       if (pkt_size > sizeof (*pkt))
1312         {
1313           BFD_ERR ("BFD verification failed - unexpected packet size '%d' "
1314                    "(auth not present)", pkt_size);
1315           return 0;
1316         }
1317       if (bs->auth.curr_key)
1318         {
1319           /* currently authenticating - could we turn it off? */
1320           if (bs->auth.is_delayed && !bs->auth.next_key)
1321             {
1322               /* yes, delayed switch to NULL key is scheduled */
1323               bfd_session_switch_auth_to_next (bs);
1324               return 1;
1325             }
1326         }
1327       else
1328         {
1329           /* no auth in packet, no auth in use - packet is valid */
1330           return 1;
1331         }
1332     }
1333   return 0;
1334 }
1335
1336 void
1337 bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
1338 {
1339   bfd_session_t *bs = bfd_find_session_by_idx (bm, bs_idx);
1340   if (!bs)
1341     {
1342       return;
1343     }
1344   BFD_DBG ("Scanning bfd packet, bs_idx=%d", bs->bs_idx);
1345   bs->remote_discr = pkt->my_disc;
1346   bs->remote_state = bfd_pkt_get_state (pkt);
1347   bs->remote_demand = bfd_pkt_get_demand (pkt);
1348   u64 now = clib_cpu_time_now ();
1349   bs->last_rx_clocks = now;
1350   if (bfd_pkt_get_auth_present (pkt))
1351     {
1352       bfd_auth_type_e auth_type =
1353         ((bfd_pkt_with_common_auth_t *) (pkt))->common_auth.type;
1354       switch (auth_type)
1355         {
1356         case BFD_AUTH_TYPE_reserved:
1357           /* fallthrough */
1358         case BFD_AUTH_TYPE_simple_password:
1359           /* fallthrough */
1360         case BFD_AUTH_TYPE_keyed_md5:
1361           /* fallthrough */
1362         case BFD_AUTH_TYPE_meticulous_keyed_md5:
1363           clib_warning ("Internal error, unexpected auth_type=%d:%s",
1364                         auth_type, bfd_auth_type_str (auth_type));
1365           break;
1366         case BFD_AUTH_TYPE_keyed_sha1:
1367           /* fallthrough */
1368         case BFD_AUTH_TYPE_meticulous_keyed_sha1:
1369           do
1370             {
1371               bfd_pkt_with_sha1_auth_t *with_sha1 =
1372                 (bfd_pkt_with_sha1_auth_t *) pkt;
1373               bs->auth.remote_seq_number =
1374                 clib_net_to_host_u32 (with_sha1->sha1_auth.seq_num);
1375               bs->auth.remote_seq_number_known = 1;
1376               BFD_DBG ("Received sequence number %u",
1377                        bs->auth.remote_seq_number);
1378             }
1379           while (0);
1380         }
1381     }
1382   bs->remote_desired_min_tx_clocks =
1383     bfd_usec_to_clocks (bm, clib_net_to_host_u32 (pkt->des_min_tx));
1384   bs->remote_detect_mult = pkt->head.detect_mult;
1385   bfd_set_remote_required_min_rx (bm, bs, now,
1386                                   clib_net_to_host_u32 (pkt->req_min_rx), 0);
1387   /* FIXME
1388      If the Required Min Echo RX Interval field is zero, the
1389      transmission of Echo packets, if any, MUST cease.
1390
1391      If a Poll Sequence is being transmitted by the local system and
1392      the Final (F) bit in the received packet is set, the Poll Sequence
1393      MUST be terminated.
1394    */
1395   /* FIXME 6.8.2 */
1396   /* FIXME 6.8.4 */
1397   if (bs->poll_state == POLL_IN_PROGRESS && bfd_pkt_get_final (pkt))
1398     {
1399       bs->poll_state = POLL_NOT_NEEDED;
1400       BFD_DBG ("Poll sequence terminated, bs_idx=%u", bs->bs_idx);
1401       if (BFD_STATE_up == bs->local_state)
1402         {
1403           bfd_set_effective_required_min_rx (bm, bs, now,
1404                                              bs->config_required_min_rx_clocks,
1405                                              0);
1406           bfd_recalc_detection_time (bm, bs);
1407         }
1408     }
1409   if (BFD_STATE_admin_down == bs->local_state)
1410     return;
1411   if (BFD_STATE_admin_down == bs->remote_state)
1412     {
1413       bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
1414       bfd_set_state (bm, bs, BFD_STATE_down, 0);
1415     }
1416   else if (BFD_STATE_down == bs->local_state)
1417     {
1418       if (BFD_STATE_down == bs->remote_state)
1419         {
1420           bfd_set_state (bm, bs, BFD_STATE_init, 0);
1421         }
1422       else if (BFD_STATE_init == bs->remote_state)
1423         {
1424           bfd_set_state (bm, bs, BFD_STATE_up, 0);
1425         }
1426     }
1427   else if (BFD_STATE_init == bs->local_state)
1428     {
1429       if (BFD_STATE_up == bs->remote_state ||
1430           BFD_STATE_init == bs->remote_state)
1431         {
1432           bfd_set_state (bm, bs, BFD_STATE_up, 0);
1433         }
1434     }
1435   else                          /* BFD_STATE_up == bs->local_state */
1436     {
1437       if (BFD_STATE_down == bs->remote_state)
1438         {
1439           bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
1440           bfd_set_state (bm, bs, BFD_STATE_down, 0);
1441         }
1442     }
1443 }
1444
1445 static const char *
1446 bfd_poll_state_string (bfd_poll_state_e state)
1447 {
1448   switch (state)
1449     {
1450 #define F(x)     \
1451   case POLL_##x: \
1452     return "POLL_" #x;
1453       foreach_bfd_poll_state (F)
1454 #undef F
1455     }
1456   return "UNKNOWN";
1457 }
1458
1459 u8 *
1460 format_bfd_session (u8 * s, va_list * args)
1461 {
1462   const bfd_session_t *bs = va_arg (*args, bfd_session_t *);
1463   s = format (s, "BFD(%u): bfd.SessionState=%s, "
1464               "bfd.RemoteSessionState=%s, "
1465               "bfd.LocalDiscr=%u, "
1466               "bfd.RemoteDiscr=%u, "
1467               "bfd.LocalDiag=%s, "
1468               "bfd.DesiredMinTxInterval=%u, "
1469               "bfd.RequiredMinRxInterval=%u, "
1470               "bfd.RequiredMinEchoRxInterval=%u, "
1471               "bfd.RemoteMinRxInterval=%u, "
1472               "bfd.DemandMode=%s, "
1473               "bfd.RemoteDemandMode=%s, "
1474               "bfd.DetectMult=%u, "
1475               "Auth: {local-seq-num=%u, "
1476               "remote-seq-num=%u, "
1477               "is-delayed=%s, "
1478               "curr-key=%U, "
1479               "next-key=%U},"
1480               "poll-state: %s",
1481               bs->bs_idx, bfd_state_string (bs->local_state),
1482               bfd_state_string (bs->remote_state), bs->local_discr,
1483               bs->remote_discr, bfd_diag_code_string (bs->local_diag),
1484               bs->config_desired_min_tx_usec, bs->config_required_min_rx_usec,
1485               1, bs->remote_min_rx_usec, (bs->local_demand ? "yes" : "no"),
1486               (bs->remote_demand ? "yes" : "no"), bs->local_detect_mult,
1487               bs->auth.local_seq_number, bs->auth.remote_seq_number,
1488               (bs->auth.is_delayed ? "yes" : "no"), format_bfd_auth_key,
1489               bs->auth.curr_key, format_bfd_auth_key, bs->auth.next_key,
1490               bfd_poll_state_string (bs->poll_state));
1491   return s;
1492 }
1493
1494 unsigned
1495 bfd_auth_type_supported (bfd_auth_type_e auth_type)
1496 {
1497   if (auth_type == BFD_AUTH_TYPE_keyed_sha1 ||
1498       auth_type == BFD_AUTH_TYPE_meticulous_keyed_sha1)
1499     {
1500       return 1;
1501     }
1502   return 0;
1503 }
1504
1505 vnet_api_error_t
1506 bfd_auth_activate (bfd_session_t * bs, u32 conf_key_id,
1507                    u8 bfd_key_id, u8 is_delayed)
1508 {
1509   bfd_main_t *bm = &bfd_main;
1510   const uword *key_idx_p =
1511     hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
1512   if (!key_idx_p)
1513     {
1514       clib_warning ("Authentication key with config ID %u doesn't exist)",
1515                     conf_key_id);
1516       return VNET_API_ERROR_BFD_ENOENT;
1517     }
1518   const uword key_idx = *key_idx_p;
1519   bfd_auth_key_t *key = pool_elt_at_index (bm->auth_keys, key_idx);
1520   if (is_delayed)
1521     {
1522       if (bs->auth.next_key == key)
1523         {
1524           /* already using this key, no changes required */
1525           return 0;
1526         }
1527       bs->auth.next_key = key;
1528       bs->auth.next_bfd_key_id = bfd_key_id;
1529       bs->auth.is_delayed = 1;
1530     }
1531   else
1532     {
1533       if (bs->auth.curr_key == key)
1534         {
1535           /* already using this key, no changes required */
1536           return 0;
1537         }
1538       if (bs->auth.curr_key)
1539         {
1540           --bs->auth.curr_key->use_count;
1541         }
1542       bs->auth.curr_key = key;
1543       bs->auth.curr_bfd_key_id = bfd_key_id;
1544       bs->auth.is_delayed = 0;
1545     }
1546   ++key->use_count;
1547   BFD_DBG ("Session auth modified: %U", format_bfd_session, bs);
1548   return 0;
1549 }
1550
1551 vnet_api_error_t
1552 bfd_auth_deactivate (bfd_session_t * bs, u8 is_delayed)
1553 {
1554 #if WITH_LIBSSL > 0
1555   if (!is_delayed)
1556     {
1557       /* not delayed - deactivate the current key right now */
1558       if (bs->auth.curr_key)
1559         {
1560           --bs->auth.curr_key->use_count;
1561           bs->auth.curr_key = NULL;
1562         }
1563       bs->auth.is_delayed = 0;
1564     }
1565   else
1566     {
1567       /* delayed - mark as so */
1568       bs->auth.is_delayed = 1;
1569     }
1570   /*
1571    * clear the next key unconditionally - either the auth change is not delayed
1572    * in which case the caller expects the session to not use authentication
1573    * from this point forward, or it is delayed, in which case the next_key
1574    * needs to be set to NULL to make it so in the future
1575    */
1576   if (bs->auth.next_key)
1577     {
1578       --bs->auth.next_key->use_count;
1579       bs->auth.next_key = NULL;
1580     }
1581   BFD_DBG ("Session auth modified: %U", format_bfd_session, bs);
1582   return 0;
1583 #else
1584   clib_warning ("SSL missing, cannot deactivate BFD authentication");
1585   return VNET_API_ERROR_BFD_NOTSUPP;
1586 #endif
1587 }
1588
1589 vnet_api_error_t
1590 bfd_session_set_params (bfd_main_t * bm, bfd_session_t * bs,
1591                         u32 desired_min_tx_usec,
1592                         u32 required_min_rx_usec, u8 detect_mult)
1593 {
1594   if (bs->local_detect_mult != detect_mult ||
1595       bs->config_desired_min_tx_usec != desired_min_tx_usec ||
1596       bs->config_required_min_rx_usec != required_min_rx_usec)
1597     {
1598       BFD_DBG ("Changing session params: %U", format_bfd_session, bs);
1599       switch (bs->poll_state)
1600         {
1601         case POLL_NOT_NEEDED:
1602           if (BFD_STATE_up == bs->local_state ||
1603               BFD_STATE_init == bs->local_state)
1604             {
1605               /* poll sequence is not needed for detect multiplier change */
1606               if (bs->config_desired_min_tx_usec != desired_min_tx_usec ||
1607                   bs->config_required_min_rx_usec != required_min_rx_usec)
1608                 {
1609                   bs->poll_state = POLL_NEEDED;
1610                   BFD_DBG ("Set poll state=%s, bs_idx=%u",
1611                            bfd_poll_state_string (bs->poll_state),
1612                            bs->bs_idx);
1613                 }
1614             }
1615           break;
1616         case POLL_NEEDED:
1617           /* nothing to do */
1618           break;
1619         case POLL_IN_PROGRESS:
1620           /* can't change params now ... */
1621           BFD_ERR ("Poll in progress, cannot change params for session with "
1622                    "bs_idx=%u", bs->bs_idx);
1623           return VNET_API_ERROR_BFD_EAGAIN;
1624         }
1625
1626       bs->local_detect_mult = detect_mult;
1627       bs->config_desired_min_tx_usec = desired_min_tx_usec;
1628       bs->config_desired_min_tx_clocks =
1629         bfd_usec_to_clocks (bm, desired_min_tx_usec);
1630       bs->config_required_min_rx_usec = required_min_rx_usec;
1631       bs->config_required_min_rx_clocks =
1632         bfd_usec_to_clocks (bm, required_min_rx_usec);
1633       BFD_DBG ("Changed session params: %U", format_bfd_session, bs);
1634
1635       vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
1636                                  BFD_EVENT_CONFIG_CHANGED, bs->bs_idx);
1637     }
1638   else
1639     {
1640       BFD_DBG ("Ignore parameter change - no change, bs_idx=%u", bs->bs_idx);
1641     }
1642   return 0;
1643 }
1644
1645 bfd_main_t bfd_main;
1646
1647 /*
1648  * fd.io coding-style-patch-verification: ON
1649  *
1650  * Local Variables:
1651  * eval: (c-set-style "gnu")
1652  * End:
1653  */