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