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