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