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