LCOV - code coverage report
Current view: top level - vnet/bfd - bfd_main.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 895 1015 88.2 %
Date: 2023-10-26 01:39:38 Functions: 77 82 93.9 %

          Line data    Source code
       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 <vlibmemory/api.h>
      21             : #include <vppinfra/random.h>
      22             : #include <vppinfra/error.h>
      23             : #include <vppinfra/hash.h>
      24             : #include <vppinfra/xxhash.h>
      25             : #include <vnet/ethernet/ethernet.h>
      26             : #include <vnet/ethernet/packet.h>
      27             : #include <vnet/bfd/bfd_debug.h>
      28             : #include <vnet/bfd/bfd_protocol.h>
      29             : #include <vnet/bfd/bfd_main.h>
      30             : #include <vlib/log.h>
      31             : #include <vnet/crypto/crypto.h>
      32             : 
      33             : static void
      34          84 : bfd_validate_counters (bfd_main_t *bm)
      35             : {
      36          84 :   vlib_validate_combined_counter (&bm->rx_counter, pool_elts (bm->sessions));
      37          84 :   vlib_validate_combined_counter (&bm->rx_echo_counter,
      38          84 :                                   pool_elts (bm->sessions));
      39          84 :   vlib_validate_combined_counter (&bm->tx_counter, pool_elts (bm->sessions));
      40          84 :   vlib_validate_combined_counter (&bm->tx_echo_counter,
      41          84 :                                   pool_elts (bm->sessions));
      42          84 : }
      43             : 
      44             : static u64
      45          61 : bfd_calc_echo_checksum (u32 discriminator, u64 expire_time, u32 secret)
      46             : {
      47          61 :   u64 checksum = 0;
      48             : #if defined(clib_crc32c_uses_intrinsics) && !defined (__i386__)
      49             :   checksum = clib_crc32c_u64 (0, discriminator);
      50             :   checksum = clib_crc32c_u64 (checksum, expire_time);
      51             :   checksum = clib_crc32c_u64 (checksum, secret);
      52             : #else
      53          61 :   checksum = clib_xxhash (discriminator ^ expire_time ^ secret);
      54             : #endif
      55          61 :   return checksum;
      56             : }
      57             : 
      58             : static u64
      59        1853 : bfd_usec_to_nsec (u64 us)
      60             : {
      61        1853 :   return us * NSEC_PER_USEC;
      62             : }
      63             : 
      64             : u32
      65          42 : bfd_nsec_to_usec (u64 nsec)
      66             : {
      67          42 :   return nsec / NSEC_PER_USEC;
      68             : }
      69             : 
      70             : always_inline u64
      71       10914 : bfd_time_now_nsec (vlib_main_t * vm, f64 * vm_time)
      72             : {
      73       10914 :   f64 _vm_time = vlib_time_now (vm);
      74       10914 :   if (vm_time)
      75       10255 :     *vm_time = _vm_time;
      76       10914 :   return _vm_time * NSEC_PER_SEC;
      77             : }
      78             : 
      79             : static vlib_node_registration_t bfd_process_node;
      80             : 
      81             : u8 *
      82         154 : format_bfd_auth_key (u8 * s, va_list * args)
      83             : {
      84         154 :   const bfd_auth_key_t *key = va_arg (*args, bfd_auth_key_t *);
      85         154 :   if (key)
      86             :     {
      87           0 :       s = format (s, "{auth-type=%u:%s, conf-key-id=%u, use-count=%u}, ",
      88           0 :                   key->auth_type, bfd_auth_type_str (key->auth_type),
      89             :                   key->conf_key_id, key->use_count);
      90             :     }
      91             :   else
      92             :     {
      93         154 :       s = format (s, "{none}");
      94             :     }
      95         154 :   return s;
      96             : }
      97             : 
      98             : /*
      99             :  * We actually send all bfd pkts to the "error" node after scanning
     100             :  * them, so the graph node has only one next-index. The "error-drop"
     101             :  * node automatically bumps our per-node packet counters for us.
     102             :  */
     103             : typedef enum
     104             : {
     105             :   BFD_INPUT_NEXT_NORMAL,
     106             :   BFD_INPUT_N_NEXT,
     107             : } bfd_input_next_t;
     108             : 
     109             : static void bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
     110             :                                  int handling_wakeup);
     111             : 
     112             : static void
     113          84 : bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
     114             : {
     115          84 :   bs->local_state = BFD_STATE_down;
     116          84 :   bs->local_diag = BFD_DIAG_CODE_no_diag;
     117          84 :   bs->remote_state = BFD_STATE_down;
     118          84 :   bs->remote_discr = 0;
     119          84 :   bs->hop_type = BFD_HOP_TYPE_SINGLE;
     120          84 :   bs->config_desired_min_tx_usec = BFD_DEFAULT_DESIRED_MIN_TX_USEC;
     121          84 :   bs->config_desired_min_tx_nsec = bm->default_desired_min_tx_nsec;
     122          84 :   bs->effective_desired_min_tx_nsec = bm->default_desired_min_tx_nsec;
     123          84 :   bs->remote_min_rx_usec = 1;
     124          84 :   bs->remote_min_rx_nsec = bfd_usec_to_nsec (bs->remote_min_rx_usec);
     125          84 :   bs->remote_min_echo_rx_usec = 0;
     126          84 :   bs->remote_min_echo_rx_nsec = 0;
     127          84 :   bs->remote_demand = 0;
     128          84 :   bs->auth.remote_seq_number = 0;
     129          84 :   bs->auth.remote_seq_number_known = 0;
     130          84 :   bs->auth.local_seq_number = random_u32 (&bm->random_seed);
     131          84 :   bs->echo_secret = random_u32 (&bm->random_seed);
     132          84 : }
     133             : 
     134             : static void
     135         123 : bfd_set_diag (bfd_session_t * bs, bfd_diag_code_e code)
     136             : {
     137         123 :   if (bs->local_diag != code)
     138             :     {
     139             :       BFD_DBG ("set local_diag, bs_idx=%d: '%d:%s'", bs->bs_idx, code,
     140             :                bfd_diag_code_string (code));
     141          35 :       bs->local_diag = code;
     142             :     }
     143         123 : }
     144             : 
     145             : static void
     146         123 : bfd_set_state (vlib_main_t * vm, bfd_main_t * bm, bfd_session_t * bs,
     147             :                bfd_state_e new_state, int handling_wakeup)
     148             : {
     149         123 :   if (bs->local_state != new_state)
     150             :     {
     151             :       BFD_DBG ("Change state, bs_idx=%d: %s->%s", bs->bs_idx,
     152             :                bfd_state_string (bs->local_state),
     153             :                bfd_state_string (new_state));
     154          84 :       bs->local_state = new_state;
     155          84 :       bfd_on_state_change (bm, bs, bfd_time_now_nsec (vm, NULL),
     156             :                            handling_wakeup);
     157             :     }
     158         123 : }
     159             : 
     160             : const char *
     161          79 : bfd_poll_state_string (bfd_poll_state_e state)
     162             : {
     163          79 :   switch (state)
     164             :     {
     165             : #define F(x)         \
     166             :   case BFD_POLL_##x: \
     167             :     return "BFD_POLL_" #x;
     168          79 :       foreach_bfd_poll_state (F)
     169             : #undef F
     170             :     }
     171           0 :   return "UNKNOWN";
     172             : }
     173             : 
     174             : static void
     175          34 : bfd_set_poll_state (bfd_session_t * bs, bfd_poll_state_e state)
     176             : {
     177          34 :   if (bs->poll_state != state)
     178             :     {
     179             :       BFD_DBG ("Setting poll state=%s, bs_idx=%u",
     180             :                bfd_poll_state_string (state), bs->bs_idx);
     181          34 :       bs->poll_state = state;
     182             :     }
     183          34 : }
     184             : 
     185             : static void
     186         238 : bfd_recalc_tx_interval (bfd_session_t *bs)
     187             : {
     188         238 :   bs->transmit_interval_nsec =
     189         238 :     clib_max (bs->effective_desired_min_tx_nsec, bs->remote_min_rx_nsec);
     190             :   BFD_DBG ("Recalculated transmit interval " BFD_CLK_FMT,
     191             :            BFD_CLK_PRN (bs->transmit_interval_nsec));
     192         238 : }
     193             : 
     194             : static void
     195         101 : bfd_recalc_echo_tx_interval (bfd_session_t *bs)
     196             : {
     197         101 :   bs->echo_transmit_interval_nsec =
     198         101 :     clib_max (bs->effective_desired_min_tx_nsec, bs->remote_min_echo_rx_nsec);
     199             :   BFD_DBG ("Recalculated echo transmit interval " BFD_CLK_FMT,
     200             :            BFD_CLK_PRN (bs->echo_transmit_interval_nsec));
     201         101 : }
     202             : 
     203             : static void
     204         949 : bfd_calc_next_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now)
     205             : {
     206         949 :   if (bs->local_detect_mult > 1)
     207             :     {
     208             :       /* common case - 75-100% of transmit interval */
     209        1892 :       bs->tx_timeout_nsec = bs->last_tx_nsec +
     210         946 :         (1 - .25 * (random_f64 (&bm->random_seed))) *
     211         946 :         bs->transmit_interval_nsec;
     212         946 :       if (bs->tx_timeout_nsec < now)
     213             :         {
     214             :           /*
     215             :            * the timeout is in the past, which means that either remote
     216             :            * demand mode was set or performance/clock issues ...
     217             :            */
     218             :           BFD_DBG ("Missed %lu transmit events (now is %lu, calc "
     219             :                    "tx_timeout is %lu)",
     220             :                    (now - bs->tx_timeout_nsec) /
     221             :                    bs->transmit_interval_nsec, now, bs->tx_timeout_nsec);
     222          18 :           bs->tx_timeout_nsec = now;
     223             :         }
     224             :     }
     225             :   else
     226             :     {
     227             :       /* special case - 75-90% of transmit interval */
     228           6 :       bs->tx_timeout_nsec = bs->last_tx_nsec +
     229           3 :         (.9 - .15 * (random_f64 (&bm->random_seed))) *
     230           3 :         bs->transmit_interval_nsec;
     231           3 :       if (bs->tx_timeout_nsec < now)
     232             :         {
     233             :           /*
     234             :            * the timeout is in the past, which means that either remote
     235             :            * demand mode was set or performance/clock issues ...
     236             :            */
     237             :           BFD_DBG ("Missed %lu transmit events (now is %lu, calc "
     238             :                    "tx_timeout is %lu)",
     239             :                    (now - bs->tx_timeout_nsec) /
     240             :                    bs->transmit_interval_nsec, now, bs->tx_timeout_nsec);
     241           0 :           bs->tx_timeout_nsec = now;
     242             :         }
     243             :     }
     244         949 :   if (bs->tx_timeout_nsec)
     245             :     {
     246             :       BFD_DBG ("Next transmit in %lu nsec/%.02fs@%lu",
     247             :                bs->tx_timeout_nsec - now,
     248             :                (bs->tx_timeout_nsec - now) * SEC_PER_NSEC,
     249             :                bs->tx_timeout_nsec);
     250             :     }
     251         949 : }
     252             : 
     253             : static void
     254          34 : bfd_calc_next_echo_tx (bfd_session_t *bs, u64 now)
     255             : {
     256          34 :   bs->echo_tx_timeout_nsec =
     257          34 :     bs->echo_last_tx_nsec + bs->echo_transmit_interval_nsec;
     258          34 :   if (bs->echo_tx_timeout_nsec < now)
     259             :     {
     260             :       /* huh, we've missed it already, transmit now */
     261             :       BFD_DBG ("Missed %lu echo transmit events (now is %lu, calc tx_timeout "
     262             :                "is %lu)",
     263             :                (now - bs->echo_tx_timeout_nsec) /
     264             :                bs->echo_transmit_interval_nsec,
     265             :                now, bs->echo_tx_timeout_nsec);
     266           0 :       bs->echo_tx_timeout_nsec = now;
     267             :     }
     268             :   BFD_DBG ("Next echo transmit in %lu nsec/%.02fs@%lu",
     269             :            bs->echo_tx_timeout_nsec - now,
     270             :            (bs->echo_tx_timeout_nsec - now) * SEC_PER_NSEC,
     271             :            bs->echo_tx_timeout_nsec);
     272          34 : }
     273             : 
     274             : static void
     275         426 : bfd_recalc_detection_time (bfd_session_t *bs)
     276             : {
     277         426 :   if (bs->local_state == BFD_STATE_init || bs->local_state == BFD_STATE_up)
     278             :     {
     279         141 :       bs->detection_time_nsec =
     280         141 :         bs->remote_detect_mult *
     281         141 :         clib_max (bs->effective_required_min_rx_nsec,
     282             :                   bs->remote_desired_min_tx_nsec);
     283             :       BFD_DBG ("Recalculated detection time %lu nsec/%.3fs",
     284             :                bs->detection_time_nsec,
     285             :                bs->detection_time_nsec * SEC_PER_NSEC);
     286             :     }
     287         426 : }
     288             : 
     289             : static void
     290        2568 : bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now,
     291             :                int handling_wakeup)
     292             : {
     293        2568 :   u64 next = 0;
     294        2568 :   u64 rx_timeout = 0;
     295        2568 :   u64 tx_timeout = 0;
     296        2568 :   if (BFD_STATE_up == bs->local_state)
     297             :     {
     298        1824 :       rx_timeout = bs->last_rx_nsec + bs->detection_time_nsec;
     299             :     }
     300        2568 :   if (BFD_STATE_up != bs->local_state ||
     301        1824 :       (!bs->remote_demand && bs->remote_min_rx_usec) ||
     302          61 :       BFD_POLL_NOT_NEEDED != bs->poll_state)
     303             :     {
     304        2509 :       tx_timeout = bs->tx_timeout_nsec;
     305             :     }
     306        2568 :   if (tx_timeout && rx_timeout)
     307             :     {
     308        1765 :       next = clib_min (tx_timeout, rx_timeout);
     309             :     }
     310         803 :   else if (tx_timeout)
     311             :     {
     312         667 :       next = tx_timeout;
     313             :     }
     314         136 :   else if (rx_timeout)
     315             :     {
     316          59 :       next = rx_timeout;
     317             :     }
     318        2568 :   if (bs->echo && next > bs->echo_tx_timeout_nsec)
     319             :     {
     320         140 :       next = bs->echo_tx_timeout_nsec;
     321             :     }
     322             :   BFD_DBG ("bs_idx=%u, tx_timeout=%lu, echo_tx_timeout=%lu, rx_timeout=%lu, "
     323             :            "next=%s",
     324             :            bs->bs_idx, tx_timeout, bs->echo_tx_timeout_nsec, rx_timeout,
     325             :            next == tx_timeout
     326             :            ? "tx" : (next == bs->echo_tx_timeout_nsec ? "echo tx" : "rx"));
     327        2568 :   if (next)
     328             :     {
     329        2491 :       int send_signal = 0;
     330        2491 :       bs->event_time_nsec = next;
     331             :       /* add extra tick if it's not even */
     332        2491 :       u32 wheel_time_ticks =
     333        2491 :         (bs->event_time_nsec - now) / bm->nsec_per_tw_tick +
     334        2491 :         ((bs->event_time_nsec - now) % bm->nsec_per_tw_tick != 0);
     335             :       BFD_DBG ("event_time_nsec %lu (%lu nsec/%.3fs in future) -> "
     336             :                "wheel_time_ticks %u", bs->event_time_nsec,
     337             :                bs->event_time_nsec - now,
     338             :                (bs->event_time_nsec - now) * SEC_PER_NSEC, wheel_time_ticks);
     339        2491 :       wheel_time_ticks = wheel_time_ticks ? wheel_time_ticks : 1;
     340        2491 :       bfd_lock (bm);
     341        2491 :       if (bs->tw_id)
     342             :         {
     343         530 :           TW (tw_timer_update) (&bm->wheel, bs->tw_id, wheel_time_ticks);
     344             :           BFD_DBG ("tw_timer_update(%p, %u, %lu);", &bm->wheel, bs->tw_id,
     345             :                    wheel_time_ticks);
     346             :         }
     347             :       else
     348             :         {
     349        1961 :           bs->tw_id =
     350        1961 :             TW (tw_timer_start) (&bm->wheel, bs->bs_idx, 0, wheel_time_ticks);
     351             :           BFD_DBG ("tw_timer_start(%p, %u, 0, %lu) == %u;", &bm->wheel,
     352             :                    bs->bs_idx, wheel_time_ticks);
     353             :         }
     354             : 
     355        2491 :       if (!handling_wakeup)
     356             :         {
     357             : 
     358             :           /* Send only if it is earlier than current awaited wakeup time */
     359         483 :           send_signal =
     360         618 :             (bs->event_time_nsec < bm->bfd_process_next_wakeup_nsec) &&
     361             :             /*
     362             :              * If the wake-up time is within 2x the delay of the event propagation delay,
     363             :              * avoid the expense of sending the event. The 2x multiplier is to workaround the race whereby
     364             :              * simultaneous event + expired timer create one recurring bogus wakeup/suspend instance,
     365             :              * due to double scheduling of the node on the pending list.
     366             :              */
     367         135 :             (bm->bfd_process_next_wakeup_nsec - bs->event_time_nsec >
     368         617 :              2 * bm->bfd_process_wakeup_event_delay_nsec) &&
     369             :             /* Must be no events in flight to send an event */
     370         134 :             (!bm->bfd_process_wakeup_events_in_flight);
     371             : 
     372             :           /* If we do send the signal, note this down along with the start timestamp */
     373         483 :           if (send_signal)
     374             :             {
     375         131 :               bm->bfd_process_wakeup_events_in_flight++;
     376         131 :               bm->bfd_process_wakeup_event_start_nsec = now;
     377             :             }
     378             :         }
     379        2491 :       bfd_unlock (bm);
     380             : 
     381             :       /* Use the multithreaded event sending so the workers can send events too */
     382        2491 :       if (send_signal)
     383             :         {
     384         131 :           vlib_process_signal_event_mt (bm->vlib_main,
     385         131 :                                         bm->bfd_process_node_index,
     386             :                                         BFD_EVENT_RESCHEDULE, ~0);
     387             :         }
     388             :     }
     389        2568 : }
     390             : 
     391             : static void
     392          93 : bfd_set_effective_desired_min_tx (bfd_main_t * bm,
     393             :                                   bfd_session_t * bs, u64 now,
     394             :                                   u64 desired_min_tx_nsec)
     395             : {
     396          93 :   bs->effective_desired_min_tx_nsec = desired_min_tx_nsec;
     397             :   BFD_DBG ("Set effective desired min tx to " BFD_CLK_FMT,
     398             :            BFD_CLK_PRN (bs->effective_desired_min_tx_nsec));
     399          93 :   bfd_recalc_detection_time (bs);
     400          93 :   bfd_recalc_tx_interval (bs);
     401          93 :   bfd_recalc_echo_tx_interval (bs);
     402          93 :   bfd_calc_next_tx (bm, bs, now);
     403          93 : }
     404             : 
     405             : static void
     406         174 : bfd_set_effective_required_min_rx (bfd_session_t *bs, u64 required_min_rx_nsec)
     407             : {
     408         174 :   bs->effective_required_min_rx_nsec = required_min_rx_nsec;
     409             :   BFD_DBG ("Set effective required min rx to " BFD_CLK_FMT,
     410             :            BFD_CLK_PRN (bs->effective_required_min_rx_nsec));
     411         174 :   bfd_recalc_detection_time (bs);
     412         174 : }
     413             : 
     414             : static void
     415         375 : bfd_set_remote_required_min_rx (bfd_session_t *bs,
     416             :                                 u32 remote_required_min_rx_usec)
     417             : {
     418         375 :   if (bs->remote_min_rx_usec != remote_required_min_rx_usec)
     419             :     {
     420          68 :       bs->remote_min_rx_usec = remote_required_min_rx_usec;
     421          68 :       bs->remote_min_rx_nsec = bfd_usec_to_nsec (remote_required_min_rx_usec);
     422             :       BFD_DBG ("Set remote min rx to " BFD_CLK_FMT,
     423             :                BFD_CLK_PRN (bs->remote_min_rx_nsec));
     424          68 :       bfd_recalc_detection_time (bs);
     425          68 :       bfd_recalc_tx_interval (bs);
     426             :     }
     427         375 : }
     428             : 
     429             : static void
     430         359 : bfd_set_remote_required_min_echo_rx (bfd_session_t *bs,
     431             :                                      u32 remote_required_min_echo_rx_usec)
     432             : {
     433         359 :   if (bs->remote_min_echo_rx_usec != remote_required_min_echo_rx_usec)
     434             :     {
     435           8 :       bs->remote_min_echo_rx_usec = remote_required_min_echo_rx_usec;
     436           8 :       bs->remote_min_echo_rx_nsec =
     437           8 :         bfd_usec_to_nsec (bs->remote_min_echo_rx_usec);
     438             :       BFD_DBG ("Set remote min echo rx to " BFD_CLK_FMT,
     439             :                BFD_CLK_PRN (bs->remote_min_echo_rx_nsec));
     440           8 :       bfd_recalc_echo_tx_interval (bs);
     441             :     }
     442         359 : }
     443             : 
     444             : static void
     445         239 : bfd_notify_listeners (bfd_main_t * bm,
     446             :                       bfd_listen_event_e event, const bfd_session_t * bs)
     447             : {
     448             :   bfd_notify_fn_t *fn;
     449         717 :   vec_foreach (fn, bm->listeners)
     450             :   {
     451         478 :     (*fn) (event, bs);
     452             :   }
     453         239 : }
     454             : 
     455             : void
     456          77 : bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
     457             : {
     458             :   BFD_DBG ("\nStarting session: %U", format_bfd_session, bs);
     459          77 :   vlib_log_info (bm->log_class, "start BFD session: %U",
     460             :                  format_bfd_session_brief, bs);
     461          77 :   bfd_set_effective_required_min_rx (bs, bs->config_required_min_rx_nsec);
     462          77 :   bfd_recalc_tx_interval (bs);
     463          77 :   vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
     464          77 :                              BFD_EVENT_NEW_SESSION, bs->bs_idx);
     465          77 :   bfd_notify_listeners (bm, BFD_LISTEN_EVENT_CREATE, bs);
     466          77 : }
     467             : 
     468             : void
     469          78 : bfd_session_stop (bfd_main_t *bm, bfd_session_t *bs)
     470             : {
     471             :   BFD_DBG ("\nStopping session: %U", format_bfd_session, bs);
     472          78 :   bfd_notify_listeners (bm, BFD_LISTEN_EVENT_DELETE, bs);
     473          78 : }
     474             : 
     475             : void
     476          45 : bfd_session_set_flags (vlib_main_t * vm, bfd_session_t * bs, u8 admin_up_down)
     477             : {
     478          45 :   bfd_main_t *bm = &bfd_main;
     479          45 :   u64 now = bfd_time_now_nsec (vm, NULL);
     480          45 :   if (admin_up_down)
     481             :     {
     482             :       BFD_DBG ("Session set admin-up, bs-idx=%u", bs->bs_idx);
     483          41 :       vlib_log_info (bm->log_class, "set session admin-up: %U",
     484             :                      format_bfd_session_brief, bs);
     485          41 :       bfd_set_state (vm, bm, bs, BFD_STATE_down, 0);
     486          41 :       bfd_set_diag (bs, BFD_DIAG_CODE_no_diag);
     487          41 :       bfd_calc_next_tx (bm, bs, now);
     488          41 :       bfd_set_timer (bm, bs, now, 0);
     489             :     }
     490             :   else
     491             :     {
     492             :       BFD_DBG ("Session set admin-down, bs-idx=%u", bs->bs_idx);
     493           4 :       vlib_log_info (bm->log_class, "set session admin-down: %U",
     494             :                      format_bfd_session_brief, bs);
     495           4 :       bfd_set_diag (bs, BFD_DIAG_CODE_admin_down);
     496           4 :       bfd_set_state (vm, bm, bs, BFD_STATE_admin_down, 0);
     497           4 :       bfd_calc_next_tx (bm, bs, now);
     498           4 :       bfd_set_timer (bm, bs, now, 0);
     499             :     }
     500          45 : }
     501             : 
     502             : u8 *
     503         271 : format_bfd_pkt (u8 *s, va_list *args)
     504             : {
     505         271 :   u32 len = va_arg (*args, u32);
     506         271 :   u8 *data = va_arg (*args, u8 *);
     507             : 
     508         271 :   const bfd_pkt_t *pkt = (bfd_pkt_t *) data;
     509         271 :   if (len > STRUCT_SIZE_OF (bfd_pkt_t, head))
     510             :     {
     511         813 :       s = format (
     512             :         s,
     513             :         "BFD v%u, diag=%u(%s), state=%u(%s),\n"
     514             :         "    flags=(P:%u, F:%u, C:%u, A:%u, D:%u, M:%u), "
     515             :         "detect_mult=%u, length=%u",
     516         271 :         bfd_pkt_get_version (pkt), bfd_pkt_get_diag_code (pkt),
     517         271 :         bfd_diag_code_string (bfd_pkt_get_diag_code (pkt)),
     518         542 :         bfd_pkt_get_state (pkt), bfd_state_string (bfd_pkt_get_state (pkt)),
     519         271 :         bfd_pkt_get_poll (pkt), bfd_pkt_get_final (pkt),
     520         271 :         bfd_pkt_get_control_plane_independent (pkt),
     521         271 :         bfd_pkt_get_auth_present (pkt), bfd_pkt_get_demand (pkt),
     522         271 :         bfd_pkt_get_multipoint (pkt), pkt->head.detect_mult, pkt->head.length);
     523         271 :       if (len >= sizeof (bfd_pkt_t) && pkt->head.length >= sizeof (bfd_pkt_t))
     524             :         {
     525         271 :           s = format (s, "\n    my discriminator: %u\n",
     526             :                       clib_net_to_host_u32 (pkt->my_disc));
     527         271 :           s = format (s, "    your discriminator: %u\n",
     528             :                       clib_net_to_host_u32 (pkt->your_disc));
     529         271 :           s = format (s, "    desired min tx interval: %u\n",
     530             :                       clib_net_to_host_u32 (pkt->des_min_tx));
     531         271 :           s = format (s, "    required min rx interval: %u\n",
     532             :                       clib_net_to_host_u32 (pkt->req_min_rx));
     533         271 :           s = format (s, "    required min echo rx interval: %u",
     534             :                       clib_net_to_host_u32 (pkt->req_min_echo_rx));
     535             :         }
     536         271 :       if (len >= sizeof (bfd_pkt_with_common_auth_t) &&
     537         228 :           pkt->head.length >= sizeof (bfd_pkt_with_common_auth_t) &&
     538         114 :           bfd_pkt_get_auth_present (pkt))
     539             :         {
     540         114 :           const bfd_pkt_with_common_auth_t *with_auth = (void *) pkt;
     541         114 :           const bfd_auth_common_t *common = &with_auth->common_auth;
     542         114 :           s = format (s, "\n    auth len: %u\n", common->len);
     543         114 :           s = format (s, "    auth type: %u:%s", common->type,
     544         114 :                       bfd_auth_type_str (common->type));
     545         114 :           if (len >= sizeof (bfd_pkt_with_sha1_auth_t) &&
     546         114 :               pkt->head.length >= sizeof (bfd_pkt_with_sha1_auth_t) &&
     547         114 :               (BFD_AUTH_TYPE_keyed_sha1 == common->type ||
     548          38 :                BFD_AUTH_TYPE_meticulous_keyed_sha1 == common->type))
     549             :             {
     550         112 :               const bfd_pkt_with_sha1_auth_t *with_sha1 = (void *) pkt;
     551         112 :               const bfd_auth_sha1_t *sha1 = &with_sha1->sha1_auth;
     552         112 :               s = format (s, "    seq num: %u\n",
     553             :                           clib_net_to_host_u32 (sha1->seq_num));
     554         112 :               s = format (s, "    key id: %u\n", sha1->key_id);
     555         112 :               s = format (s, "    hash: %U", format_hex_bytes, sha1->hash,
     556             :                           sizeof (sha1->hash));
     557             :             }
     558             :         }
     559             :     }
     560             : 
     561         271 :   return s;
     562             : }
     563             : 
     564             : u8 *
     565         232 : bfd_input_format_trace (u8 *s, va_list *args)
     566             : {
     567         232 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     568         232 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     569         232 :   const bfd_input_trace_t *t = va_arg (*args, bfd_input_trace_t *);
     570             : 
     571         232 :   s = format (s, "%U", format_bfd_pkt, t->len, t->data);
     572             : 
     573         232 :   return s;
     574             : }
     575             : 
     576             : typedef struct
     577             : {
     578             :   u32 bs_idx;
     579             : } bfd_rpc_event_t;
     580             : 
     581             : static void
     582           0 : bfd_rpc_event_cb (const bfd_rpc_event_t * a)
     583             : {
     584           0 :   bfd_main_t *bm = &bfd_main;
     585           0 :   u32 bs_idx = a->bs_idx;
     586           0 :   u32 valid_bs = 0;
     587             :   bfd_session_t session_data;
     588             : 
     589           0 :   bfd_lock (bm);
     590           0 :   if (!pool_is_free_index (bm->sessions, bs_idx))
     591             :     {
     592           0 :       bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
     593           0 :       clib_memcpy (&session_data, bs, sizeof (bfd_session_t));
     594           0 :       valid_bs = 1;
     595             :     }
     596             :   else
     597             :     {
     598             :       BFD_DBG ("Ignoring event RPC for non-existent session index %u",
     599             :                bs_idx);
     600             :     }
     601           0 :   bfd_unlock (bm);
     602             : 
     603           0 :   if (valid_bs)
     604           0 :     bfd_event (bm, &session_data);
     605           0 : }
     606             : 
     607             : static void
     608           0 : bfd_event_rpc (u32 bs_idx)
     609           0 : {
     610           0 :   const u32 data_size = sizeof (bfd_rpc_event_t);
     611           0 :   u8 data[data_size];
     612           0 :   bfd_rpc_event_t *event = (bfd_rpc_event_t *) data;
     613             : 
     614           0 :   event->bs_idx = bs_idx;
     615           0 :   vl_api_rpc_call_main_thread (bfd_rpc_event_cb, data, data_size);
     616           0 : }
     617             : 
     618             : typedef struct
     619             : {
     620             :   u32 bs_idx;
     621             : } bfd_rpc_notify_listeners_t;
     622             : 
     623             : static void
     624           0 : bfd_rpc_notify_listeners_cb (const bfd_rpc_notify_listeners_t * a)
     625             : {
     626           0 :   bfd_main_t *bm = &bfd_main;
     627           0 :   u32 bs_idx = a->bs_idx;
     628           0 :   bfd_lock (bm);
     629           0 :   if (!pool_is_free_index (bm->sessions, bs_idx))
     630             :     {
     631           0 :       bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
     632           0 :       bfd_notify_listeners (bm, BFD_LISTEN_EVENT_UPDATE, bs);
     633             :     }
     634             :   else
     635             :     {
     636             :       BFD_DBG ("Ignoring notify RPC for non-existent session index %u",
     637             :                bs_idx);
     638             :     }
     639           0 :   bfd_unlock (bm);
     640           0 : }
     641             : 
     642             : static void
     643           0 : bfd_notify_listeners_rpc (u32 bs_idx)
     644           0 : {
     645           0 :   const u32 data_size = sizeof (bfd_rpc_notify_listeners_t);
     646           0 :   u8 data[data_size];
     647           0 :   bfd_rpc_notify_listeners_t *notify = (bfd_rpc_notify_listeners_t *) data;
     648           0 :   notify->bs_idx = bs_idx;
     649           0 :   vl_api_rpc_call_main_thread (bfd_rpc_notify_listeners_cb, data, data_size);
     650           0 : }
     651             : 
     652             : static void
     653          84 : bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
     654             :                      int handling_wakeup)
     655             : {
     656             :   BFD_DBG ("\nState changed: %U", format_bfd_session, bs);
     657             : 
     658          84 :   if (vlib_get_thread_index () == 0)
     659             :     {
     660          84 :       bfd_event (bm, bs);
     661             :     }
     662             :   else
     663             :     {
     664             :       /* without RPC - a REGRESSION: BFD event are not propagated */
     665           0 :       bfd_event_rpc (bs->bs_idx);
     666             :     }
     667             : 
     668          84 :   switch (bs->local_state)
     669             :     {
     670           4 :     case BFD_STATE_admin_down:
     671           4 :       bs->echo = 0;
     672           4 :       bfd_set_effective_desired_min_tx (bm, bs, now,
     673           4 :                                         clib_max
     674             :                                         (bs->config_desired_min_tx_nsec,
     675             :                                          bm->default_desired_min_tx_nsec));
     676           4 :       bfd_set_effective_required_min_rx (bs, bs->config_required_min_rx_nsec);
     677           4 :       bfd_set_timer (bm, bs, now, handling_wakeup);
     678           4 :       break;
     679          27 :     case BFD_STATE_down:
     680          27 :       bs->echo = 0;
     681          27 :       bfd_set_effective_desired_min_tx (bm, bs, now,
     682          27 :                                         clib_max
     683             :                                         (bs->config_desired_min_tx_nsec,
     684             :                                          bm->default_desired_min_tx_nsec));
     685          27 :       bfd_set_effective_required_min_rx (bs, bs->config_required_min_rx_nsec);
     686          27 :       bfd_set_timer (bm, bs, now, handling_wakeup);
     687          27 :       break;
     688           3 :     case BFD_STATE_init:
     689           3 :       bs->echo = 0;
     690           3 :       bfd_set_effective_desired_min_tx (bm, bs, now,
     691             :                                         bs->config_desired_min_tx_nsec);
     692           3 :       bfd_set_timer (bm, bs, now, handling_wakeup);
     693           3 :       break;
     694          50 :     case BFD_STATE_up:
     695          50 :       bfd_set_effective_desired_min_tx (bm, bs, now,
     696             :                                         bs->config_desired_min_tx_nsec);
     697          50 :       if (BFD_POLL_NOT_NEEDED == bs->poll_state)
     698             :         {
     699          50 :           bfd_set_effective_required_min_rx (bs,
     700             :                                              bs->config_required_min_rx_nsec);
     701             :         }
     702          50 :       bfd_set_timer (bm, bs, now, handling_wakeup);
     703          50 :       break;
     704             :     }
     705          84 :   if (vlib_get_thread_index () == 0)
     706             :     {
     707          84 :       bfd_notify_listeners (bm, BFD_LISTEN_EVENT_UPDATE, bs);
     708             :     }
     709             :   else
     710             :     {
     711             :       /* without RPC - a REGRESSION: state changes are not propagated */
     712           0 :       bfd_notify_listeners_rpc (bs->bs_idx);
     713             :     }
     714          84 : }
     715             : 
     716             : static void
     717          91 : bfd_on_config_change (bfd_main_t *bm, bfd_session_t *bs, u64 now)
     718             : {
     719             :   /*
     720             :    * if remote demand mode is set and we need to do a poll, set the next
     721             :    * timeout so that the session wakes up immediately
     722             :    */
     723          91 :   if (bs->remote_demand && BFD_POLL_NEEDED == bs->poll_state &&
     724           1 :       bs->poll_state_start_or_timeout_nsec < now)
     725             :     {
     726           1 :       bs->tx_timeout_nsec = now;
     727             :     }
     728          91 :   bfd_recalc_detection_time (bs);
     729          91 :   bfd_set_timer (bm, bs, now, 0);
     730          91 : }
     731             : 
     732             : static void
     733         452 : bfd_add_transport_layer (vlib_main_t * vm, u32 bi, bfd_session_t * bs)
     734             : {
     735         452 :   switch (bs->transport)
     736             :     {
     737         386 :     case BFD_TRANSPORT_UDP4:
     738             :       BFD_DBG ("Transport bfd via udp4, bs_idx=%u", bs->bs_idx);
     739         386 :       bfd_add_udp4_transport (vm, bi, bs, 0 /* is_echo */ );
     740         386 :       break;
     741          66 :     case BFD_TRANSPORT_UDP6:
     742             :       BFD_DBG ("Transport bfd via udp6, bs_idx=%u", bs->bs_idx);
     743          66 :       bfd_add_udp6_transport (vm, bi, bs, 0 /* is_echo */ );
     744          66 :       break;
     745             :     }
     746         452 : }
     747             : 
     748             : static int
     749         452 : bfd_transport_control_frame (vlib_main_t *vm, vlib_node_runtime_t *rt, u32 bi,
     750             :                              bfd_session_t *bs)
     751             : {
     752         452 :   switch (bs->transport)
     753             :     {
     754         386 :     case BFD_TRANSPORT_UDP4:
     755             :       BFD_DBG ("Transport bfd via udp4, bs_idx=%u", bs->bs_idx);
     756         386 :       return bfd_transport_udp4 (vm, rt, bi, bs, 0 /* is_echo */);
     757             :       break;
     758          66 :     case BFD_TRANSPORT_UDP6:
     759             :       BFD_DBG ("Transport bfd via udp6, bs_idx=%u", bs->bs_idx);
     760          66 :       return bfd_transport_udp6 (vm, rt, bi, bs, 0 /* is_echo */);
     761             :       break;
     762             :     }
     763           0 :   return 0;
     764             : }
     765             : 
     766             : static int
     767          34 : bfd_echo_add_transport_layer (vlib_main_t * vm, u32 bi, bfd_session_t * bs)
     768             : {
     769          34 :   switch (bs->transport)
     770             :     {
     771          24 :     case BFD_TRANSPORT_UDP4:
     772             :       BFD_DBG ("Transport bfd echo via udp4, bs_idx=%u", bs->bs_idx);
     773          24 :       return bfd_add_udp4_transport (vm, bi, bs, 1 /* is_echo */ );
     774             :       break;
     775          10 :     case BFD_TRANSPORT_UDP6:
     776             :       BFD_DBG ("Transport bfd echo via udp6, bs_idx=%u", bs->bs_idx);
     777          10 :       return bfd_add_udp6_transport (vm, bi, bs, 1 /* is_echo */ );
     778             :       break;
     779             :     }
     780           0 :   return 0;
     781             : }
     782             : 
     783             : static int
     784          34 : bfd_transport_echo (vlib_main_t *vm, vlib_node_runtime_t *rt, u32 bi,
     785             :                     bfd_session_t *bs)
     786             : {
     787          34 :   switch (bs->transport)
     788             :     {
     789          24 :     case BFD_TRANSPORT_UDP4:
     790             :       BFD_DBG ("Transport bfd echo via udp4, bs_idx=%u", bs->bs_idx);
     791          24 :       return bfd_transport_udp4 (vm, rt, bi, bs, 1 /* is_echo */);
     792             :       break;
     793          10 :     case BFD_TRANSPORT_UDP6:
     794             :       BFD_DBG ("Transport bfd echo via udp6, bs_idx=%u", bs->bs_idx);
     795          10 :       return bfd_transport_udp6 (vm, rt, bi, bs, 1 /* is_echo */);
     796             :       break;
     797             :     }
     798           0 :   return 0;
     799             : }
     800             : 
     801             : static void
     802         167 : bfd_add_sha1_auth_section (vlib_main_t *vm, vlib_buffer_t *b,
     803             :                            bfd_session_t *bs)
     804             : {
     805         167 :   bfd_pkt_with_sha1_auth_t *pkt = vlib_buffer_get_current (b);
     806         167 :   bfd_auth_sha1_t *auth = &pkt->sha1_auth;
     807         167 :   b->current_length += sizeof (*auth);
     808         167 :   pkt->pkt.head.length += sizeof (*auth);
     809         167 :   bfd_pkt_set_auth_present (&pkt->pkt);
     810         167 :   clib_memset (auth, 0, sizeof (*auth));
     811         167 :   auth->type_len.type = bs->auth.curr_key->auth_type;
     812             :   /*
     813             :    * only meticulous authentication types require incrementing seq number
     814             :    * for every message, but doing so doesn't violate the RFC
     815             :    */
     816         167 :   ++bs->auth.local_seq_number;
     817         167 :   auth->type_len.len = sizeof (bfd_auth_sha1_t);
     818         167 :   auth->key_id = bs->auth.curr_bfd_key_id;
     819         167 :   auth->seq_num = clib_host_to_net_u32 (bs->auth.local_seq_number);
     820             :   /*
     821             :    * first copy the password into the packet, then calculate the hash
     822             :    * and finally replace the password with the calculated hash
     823             :    */
     824         167 :   clib_memcpy (auth->hash, bs->auth.curr_key->key,
     825             :                sizeof (bs->auth.curr_key->key));
     826             :   unsigned char hash[sizeof (auth->hash)];
     827             : 
     828             :   vnet_crypto_op_t op;
     829         167 :   vnet_crypto_op_init (&op, VNET_CRYPTO_OP_SHA1_HASH);
     830         167 :   op.src = (u8 *) pkt;
     831         167 :   op.len = sizeof (*pkt);
     832         167 :   op.digest = hash;
     833         167 :   vnet_crypto_process_ops (vm, &op, 1);
     834             :   BFD_DBG ("hashing: %U", format_hex_bytes, pkt, sizeof (*pkt));
     835         167 :   clib_memcpy (auth->hash, hash, sizeof (hash));
     836         167 : }
     837             : 
     838             : static void
     839         452 : bfd_add_auth_section (vlib_main_t *vm, vlib_buffer_t *b, bfd_session_t *bs)
     840             : {
     841         452 :   bfd_main_t *bm = &bfd_main;
     842         452 :   if (bs->auth.curr_key)
     843             :     {
     844         167 :       const bfd_auth_type_e auth_type = bs->auth.curr_key->auth_type;
     845         167 :       switch (auth_type)
     846             :         {
     847           0 :         case BFD_AUTH_TYPE_reserved:
     848             :           /* fallthrough */
     849             :         case BFD_AUTH_TYPE_simple_password:
     850             :           /* fallthrough */
     851             :         case BFD_AUTH_TYPE_keyed_md5:
     852             :           /* fallthrough */
     853             :         case BFD_AUTH_TYPE_meticulous_keyed_md5:
     854           0 :           vlib_log_crit (bm->log_class,
     855             :                          "internal error, unexpected BFD auth type '%d'",
     856             :                          auth_type);
     857           0 :           break;
     858         167 :         case BFD_AUTH_TYPE_keyed_sha1:
     859             :           /* fallthrough */
     860             :         case BFD_AUTH_TYPE_meticulous_keyed_sha1:
     861         167 :           bfd_add_sha1_auth_section (vm, b, bs);
     862         167 :           break;
     863             :         }
     864         285 :     }
     865         452 : }
     866             : 
     867             : static int
     868        1279 : bfd_is_echo_possible (bfd_session_t * bs)
     869             : {
     870        1279 :   if (BFD_STATE_up == bs->local_state && BFD_STATE_up == bs->remote_state &&
     871        1256 :       bs->remote_min_echo_rx_usec > 0)
     872             :     {
     873         423 :       switch (bs->transport)
     874             :         {
     875         373 :         case BFD_TRANSPORT_UDP4:
     876         373 :           return bfd_udp_is_echo_available (BFD_TRANSPORT_UDP4);
     877          50 :         case BFD_TRANSPORT_UDP6:
     878          50 :           return bfd_udp_is_echo_available (BFD_TRANSPORT_UDP6);
     879             :         }
     880             :     }
     881         856 :   return 0;
     882             : }
     883             : 
     884             : static void
     885         452 : bfd_init_control_frame (bfd_session_t *bs, vlib_buffer_t *b)
     886             : {
     887         452 :   bfd_pkt_t *pkt = vlib_buffer_get_current (b);
     888         452 :   u32 bfd_length = 0;
     889         452 :   bfd_length = sizeof (bfd_pkt_t);
     890         452 :   clib_memset (pkt, 0, sizeof (*pkt));
     891         452 :   bfd_pkt_set_version (pkt, 1);
     892         452 :   bfd_pkt_set_diag_code (pkt, bs->local_diag);
     893         452 :   bfd_pkt_set_state (pkt, bs->local_state);
     894         452 :   pkt->head.detect_mult = bs->local_detect_mult;
     895         452 :   pkt->head.length = bfd_length;
     896         452 :   pkt->my_disc = bs->local_discr;
     897         452 :   pkt->your_disc = bs->remote_discr;
     898         452 :   pkt->des_min_tx = clib_host_to_net_u32 (bs->config_desired_min_tx_usec);
     899         452 :   if (bs->echo)
     900             :     {
     901          38 :       pkt->req_min_rx =
     902          38 :         clib_host_to_net_u32 (bfd_nsec_to_usec
     903             :                               (bs->effective_required_min_rx_nsec));
     904             :     }
     905             :   else
     906             :     {
     907         414 :       pkt->req_min_rx =
     908         414 :         clib_host_to_net_u32 (bs->config_required_min_rx_usec);
     909             :     }
     910         452 :   pkt->req_min_echo_rx = clib_host_to_net_u32 (1);
     911         452 :   b->current_length = bfd_length;
     912         452 : }
     913             : 
     914             : typedef struct
     915             : {
     916             :   u32 bs_idx;
     917             :   u32 len;
     918             :   u8 data[400];
     919             : } bfd_process_trace_t;
     920             : 
     921             : static void
     922         486 : bfd_process_trace_buf (vlib_main_t *vm, vlib_node_runtime_t *rt,
     923             :                        vlib_buffer_t *b, bfd_session_t *bs)
     924             : {
     925         486 :   u32 n_trace = vlib_get_trace_count (vm, rt);
     926         486 :   if (n_trace > 0)
     927             :     {
     928             :       bfd_process_trace_t *tr;
     929          32 :       if (vlib_trace_buffer (vm, rt, 0, b, 0))
     930             :         {
     931          32 :           tr = vlib_add_trace (vm, rt, b, sizeof (*tr));
     932          32 :           tr->bs_idx = bs->bs_idx;
     933          32 :           u64 len = (b->current_length < sizeof (tr->data)) ?
     934          32 :                             b->current_length :
     935             :                             sizeof (tr->data);
     936          32 :           tr->len = len;
     937          32 :           clib_memcpy_fast (tr->data, vlib_buffer_get_current (b), len);
     938          32 :           --n_trace;
     939          32 :           vlib_set_trace_count (vm, rt, n_trace);
     940             :         }
     941             :     }
     942         486 : }
     943             : 
     944             : static void
     945         312 : bfd_send_echo (vlib_main_t *vm, vlib_node_runtime_t *rt, bfd_main_t *bm,
     946             :                bfd_session_t *bs, u64 now)
     947             : {
     948         312 :   if (!bfd_is_echo_possible (bs))
     949             :     {
     950             :       BFD_DBG ("\nSwitching off echo function: %U", format_bfd_session, bs);
     951           2 :       bs->echo = 0;
     952           2 :       return;
     953             :     }
     954         310 :   if (now >= bs->echo_tx_timeout_nsec)
     955             :     {
     956             :       BFD_DBG ("\nSending echo packet: %U", format_bfd_session, bs);
     957             :       u32 bi;
     958          34 :       if (vlib_buffer_alloc (vm, &bi, 1) != 1)
     959             :         {
     960           0 :           vlib_log_crit (bm->log_class, "buffer allocation failure");
     961           0 :           return;
     962             :         }
     963          34 :       vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     964          34 :       ASSERT (b->current_data == 0);
     965          34 :       bfd_echo_pkt_t *pkt = vlib_buffer_get_current (b);
     966          34 :       clib_memset (pkt, 0, sizeof (*pkt));
     967          34 :       pkt->discriminator = bs->local_discr;
     968          34 :       pkt->expire_time_nsec =
     969          34 :         now + bs->echo_transmit_interval_nsec * bs->local_detect_mult;
     970          34 :       pkt->checksum =
     971          34 :         bfd_calc_echo_checksum (bs->local_discr, pkt->expire_time_nsec,
     972             :                                 bs->echo_secret);
     973          34 :       b->current_length = sizeof (*pkt);
     974          34 :       bfd_process_trace_buf (vm, rt, b, bs);
     975          34 :       if (!bfd_echo_add_transport_layer (vm, bi, bs))
     976             :         {
     977             :           BFD_ERR ("cannot send echo packet out, turning echo off");
     978           0 :           bs->echo = 0;
     979           0 :           vlib_buffer_free_one (vm, bi);
     980           0 :           return;
     981             :         }
     982          34 :       if (!bfd_transport_echo (vm, rt, bi, bs))
     983             :         {
     984             :           BFD_ERR ("cannot send echo packet out, turning echo off");
     985           0 :           bs->echo = 0;
     986           0 :           vlib_buffer_free_one (vm, bi);
     987           0 :           return;
     988             :         }
     989          34 :       bs->echo_last_tx_nsec = now;
     990          34 :       bfd_calc_next_echo_tx (bs, now);
     991             :     }
     992             :   else
     993             :     {
     994             :       BFD_DBG
     995             :         ("No need to send echo packet now, now is %lu, tx_timeout is %lu",
     996             :          now, bs->echo_tx_timeout_nsec);
     997             :     }
     998             : }
     999             : 
    1000             : static void
    1001        1989 : bfd_send_periodic (vlib_main_t *vm, vlib_node_runtime_t *rt, bfd_main_t *bm,
    1002             :                    bfd_session_t *bs, u64 now)
    1003             : {
    1004        1989 :   if (!bs->remote_min_rx_usec && BFD_POLL_NOT_NEEDED == bs->poll_state)
    1005             :     {
    1006             :       BFD_DBG ("Remote min rx interval is zero, not sending periodic control "
    1007             :                "frame");
    1008          11 :       return;
    1009             :     }
    1010        1978 :   if (BFD_POLL_NOT_NEEDED == bs->poll_state && bs->remote_demand &&
    1011          29 :       BFD_STATE_up == bs->local_state && BFD_STATE_up == bs->remote_state)
    1012             :     {
    1013             :       /*
    1014             :        * A system MUST NOT periodically transmit BFD Control packets if Demand
    1015             :        * mode is active on the remote system (bfd.RemoteDemandMode is 1,
    1016             :        * bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll
    1017             :        * Sequence is not being transmitted.
    1018             :        */
    1019             :       BFD_DBG ("Remote demand is set, not sending periodic control frame");
    1020          29 :       return;
    1021             :     }
    1022        1949 :   if (now >= bs->tx_timeout_nsec)
    1023             :     {
    1024             :       BFD_DBG ("\nSending periodic control frame: %U", format_bfd_session,
    1025             :                bs);
    1026             :       u32 bi;
    1027         452 :       if (vlib_buffer_alloc (vm, &bi, 1) != 1)
    1028             :         {
    1029           0 :           vlib_log_crit (bm->log_class, "buffer allocation failure");
    1030           0 :           return;
    1031             :         }
    1032         452 :       vlib_buffer_t *b = vlib_get_buffer (vm, bi);
    1033         452 :       ASSERT (b->current_data == 0);
    1034         452 :       bfd_init_control_frame (bs, b);
    1035         452 :       switch (bs->poll_state)
    1036             :         {
    1037          14 :         case BFD_POLL_NEEDED:
    1038          14 :           if (now < bs->poll_state_start_or_timeout_nsec)
    1039             :             {
    1040             :               BFD_DBG ("Cannot start a poll sequence yet, need to wait for "
    1041             :                        BFD_CLK_FMT,
    1042             :                        BFD_CLK_PRN (bs->poll_state_start_or_timeout_nsec -
    1043             :                                     now));
    1044           2 :               break;
    1045             :             }
    1046          12 :           bs->poll_state_start_or_timeout_nsec = now;
    1047          12 :           bfd_set_poll_state (bs, BFD_POLL_IN_PROGRESS);
    1048             :           /* fallthrough */
    1049          30 :         case BFD_POLL_IN_PROGRESS:
    1050             :         case BFD_POLL_IN_PROGRESS_AND_QUEUED:
    1051          30 :           bfd_pkt_set_poll (vlib_buffer_get_current (b));
    1052             :           BFD_DBG ("Setting poll bit in packet, bs_idx=%u", bs->bs_idx);
    1053          30 :           break;
    1054         420 :         case BFD_POLL_NOT_NEEDED:
    1055             :           /* fallthrough */
    1056         420 :           break;
    1057             :         }
    1058         452 :       bfd_add_auth_section (vm, b, bs);
    1059         452 :       bfd_process_trace_buf (vm, rt, b, bs);
    1060         452 :       bfd_add_transport_layer (vm, bi, bs);
    1061         452 :       if (!bfd_transport_control_frame (vm, rt, bi, bs))
    1062             :         {
    1063           0 :           vlib_buffer_free_one (vm, bi);
    1064             :         }
    1065         452 :       bs->last_tx_nsec = now;
    1066         452 :       bfd_calc_next_tx (bm, bs, now);
    1067             :     }
    1068             :   else
    1069             :     {
    1070             :       BFD_DBG
    1071             :         ("No need to send control frame now, now is %lu, tx_timeout is %lu",
    1072             :          now, bs->tx_timeout_nsec);
    1073             :     }
    1074             : }
    1075             : 
    1076             : void
    1077           0 : bfd_init_final_control_frame (vlib_main_t *vm, vlib_buffer_t *b,
    1078             :                               bfd_session_t *bs)
    1079             : {
    1080             :   BFD_DBG ("Send final control frame for bs_idx=%lu", bs->bs_idx);
    1081           0 :   bfd_init_control_frame (bs, b);
    1082           0 :   bfd_pkt_set_final (vlib_buffer_get_current (b));
    1083           0 :   bfd_add_auth_section (vm, b, bs);
    1084           0 :   u32 bi = vlib_get_buffer_index (vm, b);
    1085           0 :   bfd_add_transport_layer (vm, bi, bs);
    1086           0 :   bs->last_tx_nsec = bfd_time_now_nsec (vm, NULL);
    1087             :   /*
    1088             :    * RFC allows to include changes in final frame, so if there were any
    1089             :    * pending, we already did that, thus we can clear any pending poll needs
    1090             :    */
    1091           0 :   bfd_set_poll_state (bs, BFD_POLL_NOT_NEEDED);
    1092           0 : }
    1093             : 
    1094             : static void
    1095        1489 : bfd_check_rx_timeout (vlib_main_t * vm, bfd_main_t * bm, bfd_session_t * bs,
    1096             :                       u64 now, int handling_wakeup)
    1097             : {
    1098        1489 :   if (bs->last_rx_nsec + bs->detection_time_nsec <= now)
    1099             :     {
    1100             :       BFD_DBG ("Rx timeout, session goes down");
    1101             :       /*
    1102             :        * RFC 5880 6.8.1. State Variables
    1103             : 
    1104             :        * bfd.RemoteDiscr
    1105             : 
    1106             :        * The remote discriminator for this BFD session.  This is the
    1107             :        * discriminator chosen by the remote system, and is totally opaque
    1108             :        * to the local system.  This MUST be initialized to zero.  If a
    1109             :        * period of a Detection Time passes without the receipt of a valid,
    1110             :        * authenticated BFD packet from the remote system, this variable
    1111             :        * MUST be set to zero.
    1112             :        */
    1113          16 :       bs->remote_discr = 0;
    1114          16 :       bfd_set_diag (bs, BFD_DIAG_CODE_det_time_exp);
    1115          16 :       bfd_set_state (vm, bm, bs, BFD_STATE_down, handling_wakeup);
    1116             :       /*
    1117             :        * If the remote system does not receive any
    1118             :        * BFD Control packets for a Detection Time, it SHOULD reset
    1119             :        * bfd.RemoteMinRxInterval to its initial value of 1 (per section 6.8.1,
    1120             :        * since it is no longer required to maintain previous session state)
    1121             :        * and then can transmit at its own rate.
    1122             :        */
    1123          16 :       bfd_set_remote_required_min_rx (bs, 1);
    1124             :     }
    1125        1473 :   else if (bs->echo
    1126         308 :            && bs->echo_last_rx_nsec +
    1127         308 :            bs->echo_transmit_interval_nsec * bs->local_detect_mult <= now)
    1128             :     {
    1129             :       BFD_DBG ("Echo rx timeout, session goes down");
    1130           3 :       bfd_set_diag (bs, BFD_DIAG_CODE_echo_failed);
    1131           3 :       bfd_set_state (vm, bm, bs, BFD_STATE_down, handling_wakeup);
    1132             :     }
    1133        1489 : }
    1134             : 
    1135             : void
    1136        1912 : bfd_on_timeout (vlib_main_t *vm, vlib_node_runtime_t *rt, bfd_main_t *bm,
    1137             :                 bfd_session_t *bs, u64 now)
    1138             : {
    1139             :   BFD_DBG ("Timeout for bs_idx=%lu", bs->bs_idx);
    1140        1912 :   switch (bs->local_state)
    1141             :     {
    1142         423 :     case BFD_STATE_admin_down:
    1143             :       /* fallthrough */
    1144             :     case BFD_STATE_down:
    1145         423 :       bfd_send_periodic (vm, rt, bm, bs, now);
    1146         423 :       break;
    1147           6 :     case BFD_STATE_init:
    1148           6 :       bfd_check_rx_timeout (vm, bm, bs, now, 1);
    1149           6 :       bfd_send_periodic (vm, rt, bm, bs, now);
    1150           6 :       break;
    1151        1483 :     case BFD_STATE_up:
    1152        1483 :       bfd_check_rx_timeout (vm, bm, bs, now, 1);
    1153        2450 :       if (BFD_POLL_NOT_NEEDED == bs->poll_state && !bs->echo &&
    1154         967 :           bfd_is_echo_possible (bs))
    1155             :         {
    1156             :           /* switch on echo function as main detection method now */
    1157             :           BFD_DBG ("Switching on echo function, bs_idx=%u", bs->bs_idx);
    1158           7 :           bs->echo = 1;
    1159           7 :           bs->echo_last_rx_nsec = now;
    1160           7 :           bs->echo_tx_timeout_nsec = now;
    1161           7 :           bfd_set_effective_required_min_rx (
    1162           7 :             bs, clib_max (bm->min_required_min_rx_while_echo_nsec,
    1163             :                           bs->config_required_min_rx_nsec));
    1164           7 :           bfd_set_poll_state (bs, BFD_POLL_NEEDED);
    1165             :         }
    1166        1483 :       bfd_send_periodic (vm, rt, bm, bs, now);
    1167        1483 :       if (bs->echo)
    1168             :         {
    1169         312 :           bfd_send_echo (vm, rt, bm, bs, now);
    1170             :         }
    1171        1483 :       break;
    1172             :     }
    1173        1912 : }
    1174             : 
    1175             : u8 *
    1176          39 : format_bfd_process_trace (u8 *s, va_list *args)
    1177             : {
    1178          39 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
    1179          39 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
    1180          39 :   bfd_process_trace_t *t = va_arg (*args, bfd_process_trace_t *);
    1181             : 
    1182             :   s =
    1183          39 :     format (s, "bs_idx=%u => %U", t->bs_idx, format_bfd_pkt, t->len, t->data);
    1184             : 
    1185          39 :   return s;
    1186             : }
    1187             : 
    1188             : /*
    1189             :  * bfd process node function
    1190             :  */
    1191             : static uword
    1192         575 : bfd_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
    1193             :              CLIB_UNUSED (vlib_frame_t *f))
    1194             : {
    1195         575 :   bfd_main_t *bm = &bfd_main;
    1196         575 :   u32 *expired = 0;
    1197         575 :   uword event_type, *event_data = 0;
    1198             : 
    1199             :   /* So we can send events to the bfd process */
    1200         575 :   bm->bfd_process_node_index = bfd_process_node.index;
    1201             : 
    1202             :   while (1)
    1203        4840 :     {
    1204             :       f64 vm_time;
    1205        5415 :       u64 now = bfd_time_now_nsec (vm, &vm_time);
    1206             :       BFD_DBG ("wakeup, now is %llunsec, vlib_time_now() is %.9f", now,
    1207             :                vm_time);
    1208        5415 :       bfd_lock (bm);
    1209             :       f64 timeout;
    1210        5415 :       if (pool_elts (bm->sessions))
    1211             :         {
    1212             :           u32 first_expires_in_ticks =
    1213        4803 :             TW (tw_timer_first_expires_in_ticks) (&bm->wheel);
    1214        4803 :           if (!first_expires_in_ticks)
    1215             :             {
    1216             :               BFD_DBG
    1217             :                 ("tw_timer_first_expires_in_ticks(%p) returns 0ticks",
    1218             :                  &bm->wheel);
    1219        3247 :               timeout = bm->wheel.next_run_time - vm_time;
    1220             :               BFD_DBG ("wheel.next_run_time is %.9f",
    1221             :                        bm->wheel.next_run_time);
    1222        3247 :               u64 next_expire_nsec = now + timeout * SEC_PER_NSEC;
    1223        3247 :               bm->bfd_process_next_wakeup_nsec = next_expire_nsec;
    1224        3247 :               bfd_unlock (bm);
    1225             :             }
    1226             :           else
    1227             :             {
    1228             :               BFD_DBG ("tw_timer_first_expires_in_ticks(%p) returns %luticks",
    1229             :                        &bm->wheel, first_expires_in_ticks);
    1230        1556 :               u64 next_expire_nsec =
    1231        1556 :                 now + first_expires_in_ticks * bm->nsec_per_tw_tick;
    1232        1556 :               bm->bfd_process_next_wakeup_nsec = next_expire_nsec;
    1233        1556 :               bfd_unlock (bm);
    1234        1556 :               ASSERT (next_expire_nsec - now <= UINT32_MAX);
    1235             :               // cast to u32 to avoid warning
    1236        1556 :               timeout = (u32) (next_expire_nsec - now) * SEC_PER_NSEC;
    1237             :             }
    1238             :           BFD_DBG ("vlib_process_wait_for_event_or_clock(vm, %.09f)",
    1239             :                    timeout);
    1240        4803 :           (void) vlib_process_wait_for_event_or_clock (vm, timeout);
    1241             :         }
    1242             :       else
    1243             :         {
    1244         612 :           bfd_unlock (bm);
    1245         612 :           (void) vlib_process_wait_for_event (vm);
    1246             :         }
    1247        4840 :       event_type = vlib_process_get_events (vm, &event_data);
    1248        4840 :       now = bfd_time_now_nsec (vm, &vm_time);
    1249             :       uword *session_index;
    1250        4840 :       switch (event_type)
    1251             :         {
    1252        4540 :         case ~0:                /* no events => timeout */
    1253             :           /* nothing to do here */
    1254        4540 :           break;
    1255         131 :         case BFD_EVENT_RESCHEDULE:
    1256             :           BFD_DBG ("reschedule event");
    1257         131 :           bfd_lock (bm);
    1258         131 :           bm->bfd_process_wakeup_event_delay_nsec =
    1259         131 :             now - bm->bfd_process_wakeup_event_start_nsec;
    1260         131 :           bm->bfd_process_wakeup_events_in_flight--;
    1261         131 :           bfd_unlock (bm);
    1262             :           /* nothing to do here - reschedule is done automatically after
    1263             :            * each event or timeout */
    1264         131 :           break;
    1265          77 :         case BFD_EVENT_NEW_SESSION:
    1266         154 :           vec_foreach (session_index, event_data)
    1267             :           {
    1268          77 :             bfd_lock (bm);
    1269          77 :             if (!pool_is_free_index (bm->sessions, *session_index))
    1270             :               {
    1271          77 :                 bfd_session_t *bs =
    1272          77 :                   pool_elt_at_index (bm->sessions, *session_index);
    1273          77 :                 bfd_send_periodic (vm, rt, bm, bs, now);
    1274          77 :                 bfd_set_timer (bm, bs, now, 1);
    1275             :               }
    1276             :             else
    1277             :               {
    1278             :                 BFD_DBG ("Ignoring event for non-existent session index %u",
    1279             :                          (u32) * session_index);
    1280             :               }
    1281          77 :             bfd_unlock (bm);
    1282             :           }
    1283          77 :           break;
    1284          92 :         case BFD_EVENT_CONFIG_CHANGED:
    1285         184 :           vec_foreach (session_index, event_data)
    1286             :           {
    1287          92 :             bfd_lock (bm);
    1288          92 :             if (!pool_is_free_index (bm->sessions, *session_index))
    1289             :               {
    1290          91 :                 bfd_session_t *bs =
    1291          91 :                   pool_elt_at_index (bm->sessions, *session_index);
    1292          91 :                 bfd_on_config_change (bm, bs, now);
    1293             :               }
    1294             :             else
    1295             :               {
    1296             :                 BFD_DBG ("Ignoring event for non-existent session index %u",
    1297             :                          (u32) * session_index);
    1298             :               }
    1299          92 :             bfd_unlock (bm);
    1300             :           }
    1301          92 :           break;
    1302           0 :         default:
    1303           0 :           vlib_log_err (bm->log_class, "BUG: event type 0x%wx", event_type);
    1304           0 :           break;
    1305             :         }
    1306             :       BFD_DBG ("tw_timer_expire_timers_vec(%p, %.04f);", &bm->wheel, vm_time);
    1307        4840 :       bfd_lock (bm);
    1308             :       expired =
    1309        4840 :         TW (tw_timer_expire_timers_vec) (&bm->wheel, vm_time, expired);
    1310             :       BFD_DBG ("Expired %d elements", vec_len (expired));
    1311        4840 :       u32 *p = NULL;
    1312        6770 :       vec_foreach (p, expired)
    1313             :       {
    1314        1930 :         const u32 bs_idx = *p;
    1315        1930 :         if (!pool_is_free_index (bm->sessions, bs_idx))
    1316             :           {
    1317        1912 :             bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
    1318        1912 :             bs->tw_id = 0;   /* timer is gone because it expired */
    1319        1912 :             bfd_on_timeout (vm, rt, bm, bs, now);
    1320        1912 :             bfd_set_timer (bm, bs, now, 1);
    1321             :           }
    1322             :       }
    1323        4840 :       bfd_unlock (bm);
    1324        4840 :       if (expired)
    1325             :         {
    1326        4826 :           vec_set_len (expired, 0);
    1327             :         }
    1328        4840 :       if (event_data)
    1329             :         {
    1330        4840 :           vec_set_len (event_data, 0);
    1331             :         }
    1332             :     }
    1333             : 
    1334             :   return 0;
    1335             : }
    1336             : 
    1337             : /*
    1338             :  * bfd process node declaration
    1339             :  */
    1340             : // clang-format off
    1341      183788 : VLIB_REGISTER_NODE (bfd_process_node, static) =
    1342             : {
    1343             :   .function = bfd_process,
    1344             :   .type = VLIB_NODE_TYPE_PROCESS,
    1345             :   .name = "bfd-process",
    1346             :   .flags = (VLIB_NODE_FLAG_TRACE_SUPPORTED),
    1347             :   .format_trace = format_bfd_process_trace,
    1348             :   .n_next_nodes = BFD_TX_N_NEXT,
    1349             :   .next_nodes = {
    1350             :     [BFD_TX_IP4_ARP] = "ip4-arp",
    1351             :     [BFD_TX_IP6_NDP] = "ip6-discover-neighbor",
    1352             :     [BFD_TX_IP4_REWRITE] = "ip4-rewrite",
    1353             :     [BFD_TX_IP6_REWRITE] = "ip6-rewrite",
    1354             :     [BFD_TX_IP4_MIDCHAIN] = "ip4-midchain",
    1355             :     [BFD_TX_IP6_MIDCHAIN] = "ip6-midchain",
    1356             :   }
    1357             : };
    1358             : // clang-format on
    1359             : 
    1360             : static clib_error_t *
    1361       13514 : bfd_sw_interface_up_down (CLIB_UNUSED (vnet_main_t *vnm),
    1362             :                           CLIB_UNUSED (u32 sw_if_index), u32 flags)
    1363             : {
    1364             :   // bfd_main_t *bm = &bfd_main;
    1365             :   // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
    1366       13514 :   if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
    1367             :     {
    1368             :       /* TODO */
    1369             :     }
    1370       13514 :   return 0;
    1371             : }
    1372             : 
    1373        2881 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
    1374             : 
    1375             : static clib_error_t *
    1376       13336 : bfd_hw_interface_up_down (CLIB_UNUSED (vnet_main_t *vnm),
    1377             :                           CLIB_UNUSED (u32 hw_if_index), u32 flags)
    1378             : {
    1379             :   // bfd_main_t *bm = &bfd_main;
    1380       13336 :   if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
    1381             :     {
    1382             :       /* TODO */
    1383             :     }
    1384       13336 :   return 0;
    1385             : }
    1386             : 
    1387        2881 : VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
    1388             : 
    1389             : void
    1390        1150 : bfd_register_listener (bfd_notify_fn_t fn)
    1391             : {
    1392        1150 :   bfd_main_t *bm = &bfd_main;
    1393             : 
    1394        1150 :   vec_add1 (bm->listeners, fn);
    1395        1150 : }
    1396             : 
    1397             : /*
    1398             :  * setup function
    1399             :  */
    1400             : static clib_error_t *
    1401         575 : bfd_main_init (vlib_main_t * vm)
    1402             : {
    1403         575 :   vlib_thread_main_t *tm = &vlib_thread_main;
    1404         575 :   u32 n_vlib_mains = tm->n_vlib_mains;
    1405             : #if BFD_DEBUG
    1406             :   setbuf (stdout, NULL);
    1407             : #endif
    1408         575 :   bfd_main_t *bm = &bfd_main;
    1409         575 :   bm->random_seed = random_default_seed ();
    1410         575 :   bm->vlib_main = vm;
    1411         575 :   bm->vnet_main = vnet_get_main ();
    1412         575 :   clib_memset (&bm->wheel, 0, sizeof (bm->wheel));
    1413         575 :   bm->nsec_per_tw_tick = (f64) NSEC_PER_SEC / BFD_TW_TPS;
    1414         575 :   bm->default_desired_min_tx_nsec =
    1415         575 :     bfd_usec_to_nsec (BFD_DEFAULT_DESIRED_MIN_TX_USEC);
    1416         575 :   bm->min_required_min_rx_while_echo_nsec =
    1417         575 :     bfd_usec_to_nsec (BFD_REQUIRED_MIN_RX_USEC_WHILE_ECHO);
    1418             :   BFD_DBG ("tw_timer_wheel_init(%p, %p, %.04f, %u)", &bm->wheel, NULL,
    1419             :            1.00 / BFD_TW_TPS, ~0);
    1420         575 :   TW (tw_timer_wheel_init) (&bm->wheel, NULL, 1.00 / BFD_TW_TPS, ~0);
    1421         575 :   bm->log_class = vlib_log_register_class ("bfd", 0);
    1422         575 :   vlib_log_debug (bm->log_class, "initialized");
    1423         575 :   bm->owner_thread_index = ~0;
    1424         575 :   if (n_vlib_mains > 1)
    1425          37 :     clib_spinlock_init (&bm->lock);
    1426         575 :   bm->rx_counter.name = "bfd rx session counters";
    1427         575 :   bm->rx_counter.stat_segment_name = "/bfd/rx-session-counters";
    1428         575 :   bm->rx_echo_counter.name = "bfd rx session echo counters";
    1429         575 :   bm->rx_echo_counter.stat_segment_name = "/bfd/rx-session-echo-counters";
    1430         575 :   bm->tx_counter.name = "bfd tx session counters";
    1431         575 :   bm->tx_counter.stat_segment_name = "/bfd/tx-session-counters";
    1432         575 :   bm->tx_echo_counter.name = "bfd tx session echo counters";
    1433         575 :   bm->tx_echo_counter.stat_segment_name = "/bfd/tx-session-echo-counters";
    1434         575 :   return 0;
    1435             : }
    1436             : 
    1437       50687 : VLIB_INIT_FUNCTION (bfd_main_init);
    1438             : 
    1439             : bfd_session_t *
    1440          84 : bfd_get_session (bfd_main_t * bm, bfd_transport_e t)
    1441             : {
    1442             :   bfd_session_t *result;
    1443             : 
    1444          84 :   bfd_lock (bm);
    1445             : 
    1446          84 :   pool_get (bm->sessions, result);
    1447          84 :   clib_memset (result, 0, sizeof (*result));
    1448          84 :   result->bs_idx = result - bm->sessions;
    1449          84 :   result->transport = t;
    1450          84 :   const unsigned limit = 1000;
    1451          84 :   unsigned counter = 0;
    1452             :   do
    1453             :     {
    1454          84 :       result->local_discr = random_u32 (&bm->random_seed);
    1455          84 :       if (counter > limit)
    1456             :         {
    1457           0 :           vlib_log_crit (bm->log_class,
    1458             :                          "couldn't allocate unused session discriminator even "
    1459             :                          "after %u tries!", limit);
    1460           0 :           pool_put (bm->sessions, result);
    1461           0 :           bfd_unlock (bm);
    1462           0 :           return NULL;
    1463             :         }
    1464          84 :       ++counter;
    1465             :     }
    1466          84 :   while (hash_get (bm->session_by_disc, result->local_discr));
    1467          84 :   bfd_set_defaults (bm, result);
    1468          84 :   hash_set (bm->session_by_disc, result->local_discr, result->bs_idx);
    1469          84 :   bfd_validate_counters (bm);
    1470          84 :   vlib_zero_combined_counter (&bm->rx_counter, result->bs_idx);
    1471          84 :   vlib_zero_combined_counter (&bm->rx_echo_counter, result->bs_idx);
    1472          84 :   vlib_zero_combined_counter (&bm->tx_counter, result->bs_idx);
    1473          84 :   vlib_zero_combined_counter (&bm->tx_echo_counter, result->bs_idx);
    1474          84 :   bfd_unlock (bm);
    1475          84 :   return result;
    1476             : }
    1477             : 
    1478             : void
    1479          84 : bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
    1480             : {
    1481          84 :   bfd_lock (bm);
    1482             : 
    1483          84 :   vlib_log_info (bm->log_class, "delete session: %U",
    1484             :                  format_bfd_session_brief, bs);
    1485          84 :   if (bs->auth.curr_key)
    1486             :     {
    1487          26 :       --bs->auth.curr_key->use_count;
    1488             :     }
    1489          84 :   if (bs->auth.next_key)
    1490             :     {
    1491           0 :       --bs->auth.next_key->use_count;
    1492             :     }
    1493          84 :   hash_unset (bm->session_by_disc, bs->local_discr);
    1494          84 :   vlib_zero_combined_counter (&bm->rx_counter, bs->bs_idx);
    1495          84 :   vlib_zero_combined_counter (&bm->rx_echo_counter, bs->bs_idx);
    1496          84 :   vlib_zero_combined_counter (&bm->tx_counter, bs->bs_idx);
    1497          84 :   vlib_zero_combined_counter (&bm->tx_echo_counter, bs->bs_idx);
    1498          84 :   pool_put (bm->sessions, bs);
    1499          84 :   bfd_unlock (bm);
    1500          84 : }
    1501             : 
    1502             : bfd_session_t *
    1503         517 : bfd_find_session_by_idx (bfd_main_t * bm, uword bs_idx)
    1504             : {
    1505         517 :   bfd_lock_check (bm);
    1506         517 :   if (!pool_is_free_index (bm->sessions, bs_idx))
    1507             :     {
    1508         517 :       return pool_elt_at_index (bm->sessions, bs_idx);
    1509             :     }
    1510           0 :   return NULL;
    1511             : }
    1512             : 
    1513             : bfd_session_t *
    1514         395 : bfd_find_session_by_disc (bfd_main_t * bm, u32 disc)
    1515             : {
    1516         395 :   bfd_lock_check (bm);
    1517         395 :   uword *p = hash_get (bfd_main.session_by_disc, disc);
    1518         395 :   if (p)
    1519             :     {
    1520         395 :       return pool_elt_at_index (bfd_main.sessions, *p);
    1521             :     }
    1522           0 :   return NULL;
    1523             : }
    1524             : 
    1525             : /**
    1526             :  * @brief verify bfd packet - common checks
    1527             :  *
    1528             :  * @param pkt
    1529             :  *
    1530             :  * @return 1 if bfd packet is valid
    1531             :  */
    1532             : bfd_error_t
    1533         370 : bfd_verify_pkt_common (const bfd_pkt_t *pkt)
    1534             : {
    1535         370 :   if (1 != bfd_pkt_get_version (pkt))
    1536             :     {
    1537             :       BFD_ERR ("BFD verification failed - unexpected version: '%d'",
    1538             :                bfd_pkt_get_version (pkt));
    1539           0 :       return BFD_ERROR_VERSION;
    1540             :     }
    1541         740 :   if (pkt->head.length < sizeof (bfd_pkt_t) ||
    1542         370 :       (bfd_pkt_get_auth_present (pkt) &&
    1543         146 :        pkt->head.length < sizeof (bfd_pkt_with_common_auth_t)))
    1544             :     {
    1545             :       BFD_ERR ("BFD verification failed - unexpected length: '%d' (auth "
    1546             :                "present: %d)",
    1547             :                pkt->head.length, bfd_pkt_get_auth_present (pkt));
    1548           0 :       return BFD_ERROR_LENGTH;
    1549             :     }
    1550         370 :   if (!pkt->head.detect_mult)
    1551             :     {
    1552             :       BFD_ERR ("BFD verification failed - unexpected detect-mult: '%d'",
    1553             :                pkt->head.detect_mult);
    1554           0 :       return BFD_ERROR_DETECT_MULTI;
    1555             :     }
    1556         370 :   if (bfd_pkt_get_multipoint (pkt))
    1557             :     {
    1558             :       BFD_ERR ("BFD verification failed - unexpected multipoint: '%d'",
    1559             :                bfd_pkt_get_multipoint (pkt));
    1560           0 :       return BFD_ERROR_MULTI_POINT;
    1561             :     }
    1562         370 :   if (!pkt->my_disc)
    1563             :     {
    1564             :       BFD_ERR ("BFD verification failed - unexpected my-disc: '%d'",
    1565             :                pkt->my_disc);
    1566           0 :       return BFD_ERROR_MY_DISC;
    1567             :     }
    1568         370 :   if (!pkt->your_disc)
    1569             :     {
    1570           2 :       const u8 pkt_state = bfd_pkt_get_state (pkt);
    1571           2 :       if (pkt_state != BFD_STATE_down && pkt_state != BFD_STATE_admin_down)
    1572             :         {
    1573             :           BFD_ERR ("BFD verification failed - unexpected state: '%s' "
    1574             :                    "(your-disc is zero)", bfd_state_string (pkt_state));
    1575           0 :           return BFD_ERROR_YOUR_DISC;
    1576             :         }
    1577             :     }
    1578         370 :   return BFD_ERROR_NONE;
    1579             : }
    1580             : 
    1581             : static void
    1582           3 : bfd_session_switch_auth_to_next (bfd_session_t * bs)
    1583             : {
    1584             :   BFD_DBG ("Switching authentication key from %U to %U for bs_idx=%u",
    1585             :            format_bfd_auth_key, bs->auth.curr_key, format_bfd_auth_key,
    1586             :            bs->auth.next_key, bs->bs_idx);
    1587           3 :   bs->auth.is_delayed = 0;
    1588           3 :   if (bs->auth.curr_key)
    1589             :     {
    1590           2 :       --bs->auth.curr_key->use_count;
    1591             :     }
    1592           3 :   bs->auth.curr_key = bs->auth.next_key;
    1593           3 :   bs->auth.next_key = NULL;
    1594           3 :   bs->auth.curr_bfd_key_id = bs->auth.next_bfd_key_id;
    1595           3 : }
    1596             : 
    1597             : static int
    1598         147 : bfd_auth_type_is_meticulous (bfd_auth_type_e auth_type)
    1599             : {
    1600         147 :   if (BFD_AUTH_TYPE_meticulous_keyed_md5 == auth_type ||
    1601             :       BFD_AUTH_TYPE_meticulous_keyed_sha1 == auth_type)
    1602             :     {
    1603          52 :       return 1;
    1604             :     }
    1605          95 :   return 0;
    1606             : }
    1607             : 
    1608             : static int
    1609         147 : bfd_verify_pkt_auth_seq_num (vlib_main_t * vm, bfd_session_t * bs,
    1610             :                              u32 received_seq_num, int is_meticulous)
    1611             : {
    1612             :   /*
    1613             :    * RFC 5880 6.8.1:
    1614             :    *
    1615             :    * This variable MUST be set to zero after no packets have been
    1616             :    * received on this session for at least twice the Detection Time.
    1617             :    */
    1618         147 :   u64 now = bfd_time_now_nsec (vm, NULL);
    1619         147 :   if (now - bs->last_rx_nsec > bs->detection_time_nsec * 2)
    1620             :     {
    1621             :       BFD_DBG ("BFD peer unresponsive for %lu nsec, which is > 2 * "
    1622             :                "detection_time=%u nsec, resetting remote_seq_number_known "
    1623             :                "flag", now - bs->last_rx_nsec, bs->detection_time_nsec * 2);
    1624          15 :       bs->auth.remote_seq_number_known = 0;
    1625             :     }
    1626         147 :   if (bs->auth.remote_seq_number_known)
    1627             :     {
    1628             :       /* remote sequence number is known, verify its validity */
    1629         130 :       const u32 max_u32 = 0xffffffff;
    1630             :       /* the calculation might wrap, account for the special case... */
    1631         130 :       if (bs->auth.remote_seq_number > max_u32 - 3 * bs->local_detect_mult)
    1632             :         {
    1633             :           /*
    1634             :            * special case
    1635             :            *
    1636             :            *        x                   y                   z
    1637             :            *  |----------+----------------------------+-----------|
    1638             :            *  0          ^                            ^ 0xffffffff
    1639             :            *             |        remote_seq_num------+
    1640             :            *             |
    1641             :            *             +-----(remote_seq_num + 3*detect_mult) % * 0xffffffff
    1642             :            *
    1643             :            *    x + y + z = 0xffffffff
    1644             :            *    x + z = 3 * detect_mult
    1645             :            */
    1646           4 :           const u32 z = max_u32 - bs->auth.remote_seq_number;
    1647           4 :           const u32 x = 3 * bs->local_detect_mult - z;
    1648           4 :           if (received_seq_num > x &&
    1649           3 :               received_seq_num < bs->auth.remote_seq_number + is_meticulous)
    1650             :             {
    1651             :               BFD_ERR
    1652             :                 ("Recvd sequence number=%u out of ranges <0, %u>, <%u, %u>",
    1653             :                  received_seq_num, x,
    1654             :                  bs->auth.remote_seq_number + is_meticulous, max_u32);
    1655           0 :               return 0;
    1656             :             }
    1657             :         }
    1658             :       else
    1659             :         {
    1660             :           /* regular case */
    1661         126 :           const u32 min = bs->auth.remote_seq_number + is_meticulous;
    1662         126 :           const u32 max =
    1663         126 :             bs->auth.remote_seq_number + 3 * bs->local_detect_mult;
    1664         126 :           if (received_seq_num < min || received_seq_num > max)
    1665             :             {
    1666             :               BFD_ERR ("Recvd sequence number=%u out of range <%u, %u>",
    1667             :                        received_seq_num, min, max);
    1668          10 :               return 0;
    1669             :             }
    1670             :         }
    1671             :     }
    1672         137 :   return 1;
    1673             : }
    1674             : 
    1675             : static int
    1676         137 : bfd_verify_pkt_auth_key_sha1 (vlib_main_t *vm, const bfd_pkt_t *pkt,
    1677             :                               u32 pkt_size, CLIB_UNUSED (bfd_session_t *bs),
    1678             :                               u8 bfd_key_id, bfd_auth_key_t *auth_key)
    1679             : {
    1680         137 :   ASSERT (auth_key->auth_type == BFD_AUTH_TYPE_keyed_sha1 ||
    1681             :           auth_key->auth_type == BFD_AUTH_TYPE_meticulous_keyed_sha1);
    1682             : 
    1683         137 :   bfd_pkt_with_common_auth_t *with_common = (void *) pkt;
    1684         137 :   if (pkt_size < sizeof (*with_common))
    1685             :     {
    1686             :       BFD_ERR ("Packet size too small to hold authentication common header");
    1687           0 :       return 0;
    1688             :     }
    1689         137 :   if (with_common->common_auth.type != auth_key->auth_type)
    1690             :     {
    1691             :       BFD_ERR ("BFD auth type mismatch, packet auth=%d:%s doesn't match "
    1692             :                "in-use auth=%d:%s",
    1693             :                with_common->common_auth.type,
    1694             :                bfd_auth_type_str (with_common->common_auth.type),
    1695             :                auth_key->auth_type, bfd_auth_type_str (auth_key->auth_type));
    1696           0 :       return 0;
    1697             :     }
    1698         137 :   bfd_pkt_with_sha1_auth_t *with_sha1 = (void *) pkt;
    1699         137 :   if (pkt_size < sizeof (*with_sha1) ||
    1700         137 :       with_sha1->sha1_auth.type_len.len < sizeof (with_sha1->sha1_auth))
    1701             :     {
    1702             :       BFD_ERR
    1703             :         ("BFD size mismatch, payload size=%u, expected=%u, auth_len=%u, "
    1704             :          "expected=%u", pkt_size, sizeof (*with_sha1),
    1705             :          with_sha1->sha1_auth.type_len.len, sizeof (with_sha1->sha1_auth));
    1706           0 :       return 0;
    1707             :     }
    1708         137 :   if (with_sha1->sha1_auth.key_id != bfd_key_id)
    1709             :     {
    1710             :       BFD_ERR
    1711             :         ("BFD key ID mismatch, packet key ID=%u doesn't match key ID=%u%s",
    1712             :          with_sha1->sha1_auth.key_id, bfd_key_id,
    1713             :          bs->
    1714             :          auth.is_delayed ? " (but a delayed auth change is scheduled)" : "");
    1715           1 :       return 0;
    1716             :     }
    1717             : 
    1718             :   u8 hash_from_packet[STRUCT_SIZE_OF (bfd_auth_sha1_t, hash)];
    1719             :   u8 calculated_hash[STRUCT_SIZE_OF (bfd_auth_sha1_t, hash)];
    1720         136 :   clib_memcpy (hash_from_packet, with_sha1->sha1_auth.hash,
    1721             :                sizeof (with_sha1->sha1_auth.hash));
    1722         136 :   clib_memcpy (with_sha1->sha1_auth.hash, auth_key->key,
    1723             :                sizeof (auth_key->key));
    1724             :   vnet_crypto_op_t op;
    1725         136 :   vnet_crypto_op_init (&op, VNET_CRYPTO_OP_SHA1_HASH);
    1726         136 :   op.src = (u8 *) with_sha1;
    1727         136 :   op.len = sizeof (*with_sha1);
    1728         136 :   op.digest = calculated_hash;
    1729         136 :   vnet_crypto_process_ops (vm, &op, 1);
    1730             : 
    1731             :   /* Restore the modified data within the packet */
    1732         136 :   clib_memcpy (with_sha1->sha1_auth.hash, hash_from_packet,
    1733             :                sizeof (with_sha1->sha1_auth.hash));
    1734             : 
    1735         136 :   if (0 ==
    1736         136 :       memcmp (calculated_hash, hash_from_packet, sizeof (calculated_hash)))
    1737             :     {
    1738         136 :       clib_memcpy (with_sha1->sha1_auth.hash, hash_from_packet,
    1739             :                    sizeof (hash_from_packet));
    1740         136 :       return 1;
    1741             :     }
    1742             :   BFD_ERR ("SHA1 hash: %U doesn't match the expected value: %U",
    1743             :            format_hex_bytes, hash_from_packet, sizeof (hash_from_packet),
    1744             :            format_hex_bytes, calculated_hash, sizeof (calculated_hash));
    1745           0 :   return 0;
    1746             : }
    1747             : 
    1748             : static int
    1749         147 : bfd_verify_pkt_auth_key (vlib_main_t * vm, const bfd_pkt_t * pkt,
    1750             :                          u32 pkt_size, bfd_session_t * bs, u8 bfd_key_id,
    1751             :                          bfd_auth_key_t * auth_key)
    1752             : {
    1753         147 :   bfd_main_t *bm = &bfd_main;
    1754         147 :   switch (auth_key->auth_type)
    1755             :     {
    1756           0 :     case BFD_AUTH_TYPE_reserved:
    1757           0 :       vlib_log_err (bm->log_class,
    1758             :                     "internal error, unexpected auth_type=%d:%s",
    1759             :                     auth_key->auth_type,
    1760             :                     bfd_auth_type_str (auth_key->auth_type));
    1761           0 :       return 0;
    1762           0 :     case BFD_AUTH_TYPE_simple_password:
    1763             :       /* fallthrough */
    1764             :     case BFD_AUTH_TYPE_keyed_md5:
    1765             :       /* fallthrough */
    1766             :     case BFD_AUTH_TYPE_meticulous_keyed_md5:
    1767           0 :       vlib_log_err (
    1768             :         bm->log_class,
    1769             :         "internal error, not implemented, unexpected auth_type=%d:%s",
    1770             :         auth_key->auth_type, bfd_auth_type_str (auth_key->auth_type));
    1771           0 :       return 0;
    1772         147 :     case BFD_AUTH_TYPE_keyed_sha1:
    1773             :       /* fallthrough */
    1774             :     case BFD_AUTH_TYPE_meticulous_keyed_sha1:
    1775             :       do
    1776             :         {
    1777         147 :           const u32 seq_num = clib_net_to_host_u32 (((bfd_pkt_with_sha1_auth_t
    1778             :                                                       *) pkt)->
    1779             :                                                     sha1_auth.seq_num);
    1780         147 :           return bfd_verify_pkt_auth_seq_num (
    1781             :                    vm, bs, seq_num,
    1782         284 :                    bfd_auth_type_is_meticulous (auth_key->auth_type)) &&
    1783         137 :                  bfd_verify_pkt_auth_key_sha1 (vm, pkt, pkt_size, bs,
    1784             :                                                bfd_key_id, auth_key);
    1785             :         }
    1786             :       while (0);
    1787             :     }
    1788           0 :   return 0;
    1789             : }
    1790             : 
    1791             : /**
    1792             :  * @brief verify bfd packet - authentication
    1793             :  *
    1794             :  * @param pkt
    1795             :  *
    1796             :  * @return 1 if bfd packet is valid
    1797             :  */
    1798             : int
    1799         370 : bfd_verify_pkt_auth (vlib_main_t * vm, const bfd_pkt_t * pkt, u16 pkt_size,
    1800             :                      bfd_session_t * bs)
    1801             : {
    1802         370 :   if (bfd_pkt_get_auth_present (pkt))
    1803             :     {
    1804             :       /* authentication present in packet */
    1805         146 :       if (!bs->auth.curr_key)
    1806             :         {
    1807             :           /* currently not using authentication - can we turn it on? */
    1808           1 :           if (bs->auth.is_delayed && bs->auth.next_key)
    1809             :             {
    1810             :               /* yes, switch is scheduled - make sure the auth is valid */
    1811           1 :               if (bfd_verify_pkt_auth_key (vm, pkt, pkt_size, bs,
    1812           1 :                                            bs->auth.next_bfd_key_id,
    1813             :                                            bs->auth.next_key))
    1814             :                 {
    1815             :                   /* auth matches next key, do the switch, packet is valid */
    1816           1 :                   bfd_session_switch_auth_to_next (bs);
    1817           1 :                   return 1;
    1818             :                 }
    1819             :             }
    1820             :         }
    1821             :       else
    1822             :         {
    1823             :           /* yes, using authentication, verify the key */
    1824         145 :           if (bfd_verify_pkt_auth_key (vm, pkt, pkt_size, bs,
    1825         145 :                                        bs->auth.curr_bfd_key_id,
    1826             :                                        bs->auth.curr_key))
    1827             :             {
    1828             :               /* verification passed, packet is valid */
    1829         134 :               return 1;
    1830             :             }
    1831             :           else
    1832             :             {
    1833             :               /* verification failed - but maybe we need to switch key */
    1834          11 :               if (bs->auth.is_delayed && bs->auth.next_key)
    1835             :                 {
    1836             :                   /* delayed switch present, verify if that key works */
    1837           1 :                   if (bfd_verify_pkt_auth_key (vm, pkt, pkt_size, bs,
    1838           1 :                                                bs->auth.next_bfd_key_id,
    1839             :                                                bs->auth.next_key))
    1840             :                     {
    1841             :                       /* auth matches next key, switch key, packet is valid */
    1842           1 :                       bfd_session_switch_auth_to_next (bs);
    1843           1 :                       return 1;
    1844             :                     }
    1845             :                 }
    1846             :             }
    1847             :         }
    1848             :     }
    1849             :   else
    1850             :     {
    1851             :       /* authentication in packet not present */
    1852         224 :       if (pkt_size > sizeof (*pkt))
    1853             :         {
    1854             :           BFD_ERR ("BFD verification failed - unexpected packet size '%d' "
    1855             :                    "(auth not present)", pkt_size);
    1856           0 :           return 0;
    1857             :         }
    1858         224 :       if (bs->auth.curr_key)
    1859             :         {
    1860             :           /* currently authenticating - could we turn it off? */
    1861           2 :           if (bs->auth.is_delayed && !bs->auth.next_key)
    1862             :             {
    1863             :               /* yes, delayed switch to NULL key is scheduled */
    1864           1 :               bfd_session_switch_auth_to_next (bs);
    1865           1 :               return 1;
    1866             :             }
    1867             :         }
    1868             :       else
    1869             :         {
    1870             :           /* no auth in packet, no auth in use - packet is valid */
    1871         222 :           return 1;
    1872             :         }
    1873             :     }
    1874          11 :   return 0;
    1875             : }
    1876             : 
    1877             : bfd_error_t
    1878         359 : bfd_consume_pkt (vlib_main_t *vm, bfd_main_t *bm, const bfd_pkt_t *pkt,
    1879             :                  u32 bs_idx)
    1880             : {
    1881         359 :   bfd_lock_check (bm);
    1882             : 
    1883         359 :   bfd_session_t *bs = bfd_find_session_by_idx (bm, bs_idx);
    1884         359 :   if (!bs || (pkt->your_disc && pkt->your_disc != bs->local_discr))
    1885             :     {
    1886           0 :       return BFD_ERROR_YOUR_DISC;
    1887             :     }
    1888             :   BFD_DBG ("Scanning bfd packet, bs_idx=%d", bs->bs_idx);
    1889         359 :   bs->remote_discr = pkt->my_disc;
    1890         359 :   bs->remote_state = bfd_pkt_get_state (pkt);
    1891         359 :   bs->remote_demand = bfd_pkt_get_demand (pkt);
    1892         359 :   bs->remote_diag = bfd_pkt_get_diag_code (pkt);
    1893         359 :   u64 now = bfd_time_now_nsec (vm, NULL);
    1894         359 :   bs->last_rx_nsec = now;
    1895         359 :   if (bfd_pkt_get_auth_present (pkt))
    1896             :     {
    1897         136 :       bfd_auth_type_e auth_type =
    1898         136 :         ((bfd_pkt_with_common_auth_t *) (pkt))->common_auth.type;
    1899         136 :       switch (auth_type)
    1900             :         {
    1901           0 :         case BFD_AUTH_TYPE_reserved:
    1902             :           /* fallthrough */
    1903             :         case BFD_AUTH_TYPE_simple_password:
    1904             :           /* fallthrough */
    1905             :         case BFD_AUTH_TYPE_keyed_md5:
    1906             :           /* fallthrough */
    1907             :         case BFD_AUTH_TYPE_meticulous_keyed_md5:
    1908           0 :           vlib_log_crit (bm->log_class,
    1909             :                          "internal error, unexpected auth_type=%d:%s",
    1910             :                          auth_type, bfd_auth_type_str (auth_type));
    1911           0 :           break;
    1912         136 :         case BFD_AUTH_TYPE_keyed_sha1:
    1913             :           /* fallthrough */
    1914             :         case BFD_AUTH_TYPE_meticulous_keyed_sha1:
    1915             :           do
    1916             :             {
    1917         136 :               bfd_pkt_with_sha1_auth_t *with_sha1 =
    1918             :                 (bfd_pkt_with_sha1_auth_t *) pkt;
    1919         136 :               bs->auth.remote_seq_number =
    1920         136 :                 clib_net_to_host_u32 (with_sha1->sha1_auth.seq_num);
    1921         136 :               bs->auth.remote_seq_number_known = 1;
    1922             :               BFD_DBG ("Received sequence number %u",
    1923             :                        bs->auth.remote_seq_number);
    1924             :             }
    1925             :           while (0);
    1926             :         }
    1927         223 :     }
    1928         359 :   bs->remote_desired_min_tx_nsec =
    1929         359 :     bfd_usec_to_nsec (clib_net_to_host_u32 (pkt->des_min_tx));
    1930         359 :   bs->remote_detect_mult = pkt->head.detect_mult;
    1931         359 :   bfd_set_remote_required_min_rx (bs, clib_net_to_host_u32 (pkt->req_min_rx));
    1932         359 :   bfd_set_remote_required_min_echo_rx (
    1933             :     bs, clib_net_to_host_u32 (pkt->req_min_echo_rx));
    1934         359 :   if (bfd_pkt_get_final (pkt))
    1935             :     {
    1936          11 :       if (BFD_POLL_IN_PROGRESS == bs->poll_state)
    1937             :         {
    1938             :           BFD_DBG ("Poll sequence terminated, bs_idx=%u", bs->bs_idx);
    1939           9 :           bfd_set_poll_state (bs, BFD_POLL_NOT_NEEDED);
    1940           9 :           if (BFD_STATE_up == bs->local_state)
    1941             :             {
    1942           9 :               bfd_set_effective_desired_min_tx (
    1943             :                 bm, bs, now, bs->config_desired_min_tx_nsec);
    1944           9 :               bfd_set_effective_required_min_rx (
    1945             :                 bs,
    1946           9 :                 clib_max (bs->echo * bm->min_required_min_rx_while_echo_nsec,
    1947             :                           bs->config_required_min_rx_nsec));
    1948             :             }
    1949             :         }
    1950           2 :       else if (BFD_POLL_IN_PROGRESS_AND_QUEUED == bs->poll_state)
    1951             :         {
    1952             :           /*
    1953             :            * next poll sequence must be delayed by at least the round trip
    1954             :            * time, so calculate that here
    1955             :            */
    1956             :           BFD_DBG ("Next poll sequence can commence in " BFD_CLK_FMT,
    1957             :                    BFD_CLK_PRN (now - bs->poll_state_start_or_timeout_nsec));
    1958           1 :           bs->poll_state_start_or_timeout_nsec =
    1959           1 :             now + (now - bs->poll_state_start_or_timeout_nsec);
    1960             :           BFD_DBG
    1961             :             ("Poll sequence terminated, but another is needed, bs_idx=%u",
    1962             :              bs->bs_idx);
    1963           1 :           bfd_set_poll_state (bs, BFD_POLL_NEEDED);
    1964             :         }
    1965             :     }
    1966         359 :   bfd_calc_next_tx (bm, bs, now);
    1967         359 :   bfd_set_timer (bm, bs, now, 0);
    1968         359 :   if (BFD_STATE_admin_down == bs->local_state)
    1969             :     {
    1970             :       BFD_DBG ("Session is admin-down, ignoring packet, bs_idx=%u",
    1971             :                bs->bs_idx);
    1972           1 :       return BFD_ERROR_ADMIN_DOWN;
    1973             :     }
    1974         358 :   if (BFD_STATE_admin_down == bs->remote_state)
    1975             :     {
    1976           0 :       bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
    1977           0 :       bfd_set_state (vm, bm, bs, BFD_STATE_down, 0);
    1978             :     }
    1979         358 :   else if (BFD_STATE_down == bs->local_state)
    1980             :     {
    1981          52 :       if (BFD_STATE_down == bs->remote_state)
    1982             :         {
    1983           3 :           bfd_set_diag (bs, BFD_DIAG_CODE_no_diag);
    1984           3 :           bfd_set_state (vm, bm, bs, BFD_STATE_init, 0);
    1985             :         }
    1986          49 :       else if (BFD_STATE_init == bs->remote_state)
    1987             :         {
    1988          47 :           bfd_set_diag (bs, BFD_DIAG_CODE_no_diag);
    1989          47 :           bfd_set_state (vm, bm, bs, BFD_STATE_up, 0);
    1990             :         }
    1991             :     }
    1992         306 :   else if (BFD_STATE_init == bs->local_state)
    1993             :     {
    1994           3 :       if (BFD_STATE_up == bs->remote_state ||
    1995           0 :           BFD_STATE_init == bs->remote_state)
    1996             :         {
    1997           3 :           bfd_set_diag (bs, BFD_DIAG_CODE_no_diag);
    1998           3 :           bfd_set_state (vm, bm, bs, BFD_STATE_up, 0);
    1999             :         }
    2000             :     }
    2001             :   else                          /* BFD_STATE_up == bs->local_state */
    2002             :     {
    2003         303 :       if (BFD_STATE_down == bs->remote_state)
    2004             :         {
    2005           6 :           bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
    2006           6 :           bfd_set_state (vm, bm, bs, BFD_STATE_down, 0);
    2007             :         }
    2008             :     }
    2009         358 :   return BFD_ERROR_NONE;
    2010             : }
    2011             : 
    2012             : bfd_session_t *
    2013          27 : bfd_consume_echo_pkt (vlib_main_t *vm, bfd_main_t *bm, vlib_buffer_t *b)
    2014             : {
    2015          27 :   bfd_echo_pkt_t *pkt = NULL;
    2016          27 :   if (b->current_length != sizeof (*pkt))
    2017             :     {
    2018           0 :       return 0;
    2019             :     }
    2020          27 :   pkt = vlib_buffer_get_current (b);
    2021          27 :   bfd_session_t *bs = bfd_find_session_by_disc (bm, pkt->discriminator);
    2022          27 :   if (!bs)
    2023             :     {
    2024           0 :       return 0;
    2025             :     }
    2026             :   BFD_DBG ("Scanning bfd echo packet, bs_idx=%d", bs->bs_idx);
    2027             :   u64 checksum =
    2028          27 :     bfd_calc_echo_checksum (bs->local_discr, pkt->expire_time_nsec,
    2029             :                             bs->echo_secret);
    2030          27 :   if (checksum != pkt->checksum)
    2031             :     {
    2032             :       BFD_DBG ("Invalid echo packet, checksum mismatch");
    2033           3 :       return 0;
    2034             :     }
    2035          24 :   u64 now = bfd_time_now_nsec (vm, NULL);
    2036          24 :   if (pkt->expire_time_nsec < now)
    2037             :     {
    2038             :       BFD_DBG ("Stale packet received, expire time %lu < now %lu",
    2039             :                pkt->expire_time_nsec, now);
    2040             :     }
    2041             :   else
    2042             :     {
    2043          21 :       bs->echo_last_rx_nsec = now;
    2044             :     }
    2045          24 :   return bs;
    2046             : }
    2047             : 
    2048             : u8 *
    2049          77 : format_bfd_session (u8 * s, va_list * args)
    2050             : {
    2051          77 :   const bfd_session_t *bs = va_arg (*args, bfd_session_t *);
    2052         231 :   s = format (s, "bs_idx=%u local-state=%s remote-state=%s\n"
    2053             :               "local-discriminator=%u remote-discriminator=%u\n"
    2054             :               "local-diag=%s echo-active=%s\n"
    2055             :               "desired-min-tx=%u required-min-rx=%u\n"
    2056             :               "required-min-echo-rx=%u detect-mult=%u\n"
    2057             :               "remote-min-rx=%u remote-min-echo-rx=%u\n"
    2058             :               "remote-demand=%s poll-state=%s\n"
    2059             :               "auth: local-seq-num=%u remote-seq-num=%u\n"
    2060             :               "      is-delayed=%s\n"
    2061             :               "      curr-key=%U\n"
    2062             :               "      next-key=%U",
    2063             :               bs->bs_idx, bfd_state_string (bs->local_state),
    2064             :               bfd_state_string (bs->remote_state), bs->local_discr,
    2065             :               bs->remote_discr, bfd_diag_code_string (bs->local_diag),
    2066          77 :               (bs->echo ? "yes" : "no"), bs->config_desired_min_tx_usec,
    2067          77 :               bs->config_required_min_rx_usec, 1, bs->local_detect_mult,
    2068             :               bs->remote_min_rx_usec, bs->remote_min_echo_rx_usec,
    2069          77 :               (bs->remote_demand ? "yes" : "no"),
    2070             :               bfd_poll_state_string (bs->poll_state),
    2071             :               bs->auth.local_seq_number, bs->auth.remote_seq_number,
    2072          77 :               (bs->auth.is_delayed ? "yes" : "no"),
    2073             :               format_bfd_auth_key, bs->auth.curr_key, format_bfd_auth_key,
    2074             :               bs->auth.next_key);
    2075          77 :   return s;
    2076             : }
    2077             : 
    2078             : u8 *
    2079         335 : format_bfd_session_brief (u8 * s, va_list * args)
    2080             : {
    2081         335 :   const bfd_session_t *bs = va_arg (*args, bfd_session_t *);
    2082             :   s =
    2083         335 :     format (s, "bs_idx=%u local-state=%s remote-state=%s", bs->bs_idx,
    2084             :             bfd_state_string (bs->local_state),
    2085             :             bfd_state_string (bs->remote_state));
    2086         335 :   return s;
    2087             : }
    2088             : 
    2089             : unsigned
    2090          53 : bfd_auth_type_supported (bfd_auth_type_e auth_type)
    2091             : {
    2092          53 :   if (auth_type == BFD_AUTH_TYPE_keyed_sha1 ||
    2093             :       auth_type == BFD_AUTH_TYPE_meticulous_keyed_sha1)
    2094             :     {
    2095          53 :       return 1;
    2096             :     }
    2097           0 :   return 0;
    2098             : }
    2099             : 
    2100             : vnet_api_error_t
    2101          37 : bfd_auth_activate (bfd_session_t * bs, u32 conf_key_id,
    2102             :                    u8 bfd_key_id, u8 is_delayed)
    2103             : {
    2104          37 :   bfd_main_t *bm = &bfd_main;
    2105             :   const uword *key_idx_p =
    2106          37 :     hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
    2107          37 :   if (!key_idx_p)
    2108             :     {
    2109           1 :       vlib_log_err (bm->log_class,
    2110             :                     "authentication key with config ID %u doesn't exist)",
    2111             :                     conf_key_id);
    2112           1 :       return VNET_API_ERROR_BFD_ENOENT;
    2113             :     }
    2114          36 :   const uword key_idx = *key_idx_p;
    2115          36 :   bfd_auth_key_t *key = pool_elt_at_index (bm->auth_keys, key_idx);
    2116          36 :   if (is_delayed)
    2117             :     {
    2118           4 :       if (bs->auth.next_key == key && bs->auth.next_bfd_key_id == bfd_key_id)
    2119             :         {
    2120             :           /* already using this key, no changes required */
    2121           1 :           return 0;
    2122             :         }
    2123           3 :       if (bs->auth.next_key != key)
    2124             :         {
    2125           3 :           ++key->use_count;
    2126           3 :           bs->auth.next_key = key;
    2127             :         }
    2128           3 :       bs->auth.next_bfd_key_id = bfd_key_id;
    2129           3 :       bs->auth.is_delayed = 1;
    2130             :     }
    2131             :   else
    2132             :     {
    2133          32 :       if (bs->auth.curr_key == key && bs->auth.curr_bfd_key_id == bfd_key_id)
    2134             :         {
    2135             :           /* already using this key, no changes required */
    2136           1 :           return 0;
    2137             :         }
    2138          31 :       ++key->use_count;
    2139          31 :       if (bs->auth.curr_key)
    2140             :         {
    2141           2 :           --bs->auth.curr_key->use_count;
    2142             :         }
    2143          31 :       bs->auth.curr_key = key;
    2144          31 :       bs->auth.curr_bfd_key_id = bfd_key_id;
    2145          31 :       bs->auth.is_delayed = 0;
    2146             :     }
    2147             :   BFD_DBG ("\nSession auth modified: %U", format_bfd_session, bs);
    2148          34 :   vlib_log_info (bm->log_class, "session auth modified: %U",
    2149             :                  format_bfd_session_brief, bs);
    2150          34 :   return 0;
    2151             : }
    2152             : 
    2153             : vnet_api_error_t
    2154           7 : bfd_auth_deactivate (bfd_session_t * bs, u8 is_delayed)
    2155             : {
    2156           7 :   bfd_main_t *bm = &bfd_main;
    2157           7 :   if (!is_delayed)
    2158             :     {
    2159             :       /* not delayed - deactivate the current key right now */
    2160           4 :       if (bs->auth.curr_key)
    2161             :         {
    2162           3 :           --bs->auth.curr_key->use_count;
    2163           3 :           bs->auth.curr_key = NULL;
    2164             :         }
    2165           4 :       bs->auth.is_delayed = 0;
    2166             :     }
    2167             :   else
    2168             :     {
    2169             :       /* delayed - mark as so */
    2170           3 :       bs->auth.is_delayed = 1;
    2171             :     }
    2172             :   /*
    2173             :    * clear the next key unconditionally - either the auth change is not delayed
    2174             :    * in which case the caller expects the session to not use authentication
    2175             :    * from this point forward, or it is delayed, in which case the next_key
    2176             :    * needs to be set to NULL to make it so in the future
    2177             :    */
    2178           7 :   if (bs->auth.next_key)
    2179             :     {
    2180           1 :       --bs->auth.next_key->use_count;
    2181           1 :       bs->auth.next_key = NULL;
    2182             :     }
    2183             :   BFD_DBG ("\nSession auth modified: %U", format_bfd_session, bs);
    2184           7 :   vlib_log_info (bm->log_class, "session auth modified: %U",
    2185             :                  format_bfd_session_brief, bs);
    2186           7 :   return 0;
    2187             : }
    2188             : 
    2189             : vnet_api_error_t
    2190          92 : bfd_session_set_params (bfd_main_t * bm, bfd_session_t * bs,
    2191             :                         u32 desired_min_tx_usec,
    2192             :                         u32 required_min_rx_usec, u8 detect_mult)
    2193             : {
    2194          92 :   if (bs->local_detect_mult != detect_mult ||
    2195           6 :       bs->config_desired_min_tx_usec != desired_min_tx_usec ||
    2196           6 :       bs->config_required_min_rx_usec != required_min_rx_usec)
    2197             :     {
    2198             :       BFD_DBG ("\nChanging session params: %U", format_bfd_session, bs);
    2199          92 :       switch (bs->poll_state)
    2200             :         {
    2201          91 :         case BFD_POLL_NOT_NEEDED:
    2202          91 :           if (BFD_STATE_up == bs->local_state ||
    2203          85 :               BFD_STATE_init == bs->local_state)
    2204             :             {
    2205             :               /* poll sequence is not needed for detect multiplier change */
    2206           6 :               if (bs->config_desired_min_tx_usec != desired_min_tx_usec ||
    2207           6 :                   bs->config_required_min_rx_usec != required_min_rx_usec)
    2208             :                 {
    2209           4 :                   bfd_set_poll_state (bs, BFD_POLL_NEEDED);
    2210             :                 }
    2211             :             }
    2212          91 :           break;
    2213           0 :         case BFD_POLL_NEEDED:
    2214             :         case BFD_POLL_IN_PROGRESS_AND_QUEUED:
    2215             :           /*
    2216             :            * nothing to do - will be handled in the future poll which is
    2217             :            * already scheduled for execution
    2218             :            */
    2219           0 :           break;
    2220           1 :         case BFD_POLL_IN_PROGRESS:
    2221             :           /* poll sequence is not needed for detect multiplier change */
    2222           1 :           if (bs->config_desired_min_tx_usec != desired_min_tx_usec ||
    2223           1 :               bs->config_required_min_rx_usec != required_min_rx_usec)
    2224             :             {
    2225             :               BFD_DBG ("Poll in progress, queueing extra poll, bs_idx=%u",
    2226             :                        bs->bs_idx);
    2227           1 :               bfd_set_poll_state (bs, BFD_POLL_IN_PROGRESS_AND_QUEUED);
    2228             :             }
    2229             :         }
    2230             : 
    2231          92 :       bs->local_detect_mult = detect_mult;
    2232          92 :       bs->config_desired_min_tx_usec = desired_min_tx_usec;
    2233          92 :       bs->config_desired_min_tx_nsec = bfd_usec_to_nsec (desired_min_tx_usec);
    2234          92 :       bs->config_required_min_rx_usec = required_min_rx_usec;
    2235          92 :       bs->config_required_min_rx_nsec =
    2236          92 :         bfd_usec_to_nsec (required_min_rx_usec);
    2237             :       BFD_DBG ("\nChanged session params: %U", format_bfd_session, bs);
    2238             : 
    2239          92 :       vlib_log_info (bm->log_class, "changed session params: %U",
    2240             :                      format_bfd_session_brief, bs);
    2241          92 :       vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
    2242          92 :                                  BFD_EVENT_CONFIG_CHANGED, bs->bs_idx);
    2243             :     }
    2244             :   else
    2245             :     {
    2246             :       BFD_DBG ("Ignore parameter change - no change, bs_idx=%u", bs->bs_idx);
    2247             :     }
    2248          92 :   return 0;
    2249             : }
    2250             : 
    2251             : vnet_api_error_t
    2252          53 : bfd_auth_set_key (u32 conf_key_id, u8 auth_type, u8 key_len,
    2253             :                   const u8 * key_data)
    2254             : {
    2255          53 :   bfd_main_t *bm = &bfd_main;
    2256          53 :   bfd_auth_key_t *auth_key = NULL;
    2257          53 :   if (!key_len || key_len > bfd_max_key_len_for_auth_type (auth_type))
    2258             :     {
    2259           0 :       vlib_log_err (bm->log_class,
    2260             :                     "invalid authentication key length for auth_type=%d:%s "
    2261             :                     "(key_len=%u, must be non-zero, expected max=%u)",
    2262             :                     auth_type, bfd_auth_type_str (auth_type), key_len,
    2263             :                     (u32) bfd_max_key_len_for_auth_type (auth_type));
    2264           0 :       return VNET_API_ERROR_INVALID_VALUE;
    2265             :     }
    2266          53 :   if (!bfd_auth_type_supported (auth_type))
    2267             :     {
    2268           0 :       vlib_log_err (bm->log_class, "unsupported auth type=%d:%s", auth_type,
    2269             :                     bfd_auth_type_str (auth_type));
    2270           0 :       return VNET_API_ERROR_BFD_NOTSUPP;
    2271             :     }
    2272          53 :   uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
    2273          53 :   if (key_idx_p)
    2274             :     {
    2275             :       /* modifying existing key - must not be used */
    2276           2 :       const uword key_idx = *key_idx_p;
    2277           2 :       auth_key = pool_elt_at_index (bm->auth_keys, key_idx);
    2278           2 :       if (auth_key->use_count > 0)
    2279             :         {
    2280           2 :           vlib_log_err (bm->log_class,
    2281             :                         "authentication key with conf ID %u in use by %u BFD "
    2282             :                         "session(s) - cannot modify", conf_key_id,
    2283             :                         auth_key->use_count);
    2284           2 :           return VNET_API_ERROR_BFD_EINUSE;
    2285             :         }
    2286             :     }
    2287             :   else
    2288             :     {
    2289             :       /* adding new key */
    2290          51 :       pool_get (bm->auth_keys, auth_key);
    2291          51 :       auth_key->conf_key_id = conf_key_id;
    2292          51 :       hash_set (bm->auth_key_by_conf_key_id, conf_key_id,
    2293             :                 auth_key - bm->auth_keys);
    2294             :     }
    2295          51 :   auth_key->auth_type = auth_type;
    2296          51 :   clib_memset (auth_key->key, 0, sizeof (auth_key->key));
    2297          51 :   clib_memcpy (auth_key->key, key_data, key_len);
    2298          51 :   return 0;
    2299             : }
    2300             : 
    2301             : vnet_api_error_t
    2302          51 : bfd_auth_del_key (u32 conf_key_id)
    2303             : {
    2304          51 :   bfd_auth_key_t *auth_key = NULL;
    2305          51 :   bfd_main_t *bm = &bfd_main;
    2306          51 :   uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
    2307          51 :   if (key_idx_p)
    2308             :     {
    2309             :       /* deleting existing key - must not be used */
    2310          51 :       const uword key_idx = *key_idx_p;
    2311          51 :       auth_key = pool_elt_at_index (bm->auth_keys, key_idx);
    2312          51 :       if (auth_key->use_count > 0)
    2313             :         {
    2314           0 :           vlib_log_err (bm->log_class,
    2315             :                         "authentication key with conf ID %u in use by %u BFD "
    2316             :                         "session(s) - cannot delete", conf_key_id,
    2317             :                         auth_key->use_count);
    2318           0 :           return VNET_API_ERROR_BFD_EINUSE;
    2319             :         }
    2320          51 :       hash_unset (bm->auth_key_by_conf_key_id, conf_key_id);
    2321          51 :       clib_memset (auth_key, 0, sizeof (*auth_key));
    2322          51 :       pool_put (bm->auth_keys, auth_key);
    2323             :     }
    2324             :   else
    2325             :     {
    2326             :       /* no such key */
    2327           0 :       vlib_log_err (bm->log_class,
    2328             :                     "authentication key with conf ID %u does not exist",
    2329             :                     conf_key_id);
    2330           0 :       return VNET_API_ERROR_BFD_ENOENT;
    2331             :     }
    2332          51 :   return 0;
    2333             : }
    2334             : 
    2335             : bfd_main_t bfd_main;
    2336             : 
    2337             : /*
    2338             :  * fd.io coding-style-patch-verification: ON
    2339             :  *
    2340             :  * Local Variables:
    2341             :  * eval: (c-set-style "gnu")
    2342             :  * End:
    2343             :  */

Generated by: LCOV version 1.14