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