BFD: modify session parameters 54/5054/2
authorKlement Sekera <ksekera@cisco.com>
Thu, 2 Feb 2017 05:58:07 +0000 (06:58 +0100)
committerKlement Sekera <ksekera@cisco.com>
Wed, 8 Feb 2017 06:47:00 +0000 (07:47 +0100)
Change-Id: I666e5c0cc71a3693640960c93cdd1907f84fbe23
Signed-off-by: Klement Sekera <ksekera@cisco.com>
13 files changed:
src/vnet/api_errno.h
src/vnet/bfd/bfd.api
src/vnet/bfd/bfd_api.c
src/vnet/bfd/bfd_api.h
src/vnet/bfd/bfd_debug.h
src/vnet/bfd/bfd_main.c
src/vnet/bfd/bfd_main.h
src/vnet/bfd/bfd_protocol.c
src/vnet/bfd/bfd_udp.c
test/bfd.py
test/framework.py
test/test_bfd.py
test/vpp_papi_provider.py

index 3288023..0daba16 100644 (file)
@@ -95,7 +95,8 @@ _(BFD_EEXIST, -101, "Duplicate BFD object") \
 _(BFD_ENOENT, -102, "No such BFD object") \
 _(BFD_EINUSE, -103, "BFD object in use") \
 _(BFD_NOTSUPP, -104, "BFD feature not supported") \
-_(LISP_RLOC_LOCAL, -105, "RLOC address is local")
+_(LISP_RLOC_LOCAL, -105, "RLOC address is local") \
+_(BFD_EAGAIN, -106, "BFD object cannot be manipulated at this time")
 
 typedef enum
 {
index 17ca35b..f307ed2 100644 (file)
@@ -107,6 +107,40 @@ define bfd_udp_add_reply
   i32 retval;
 };
 
+/** \brief Modify UDP BFD session on interface
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - sw index of the interface
+    @param desired_min_tx - desired min transmit interval (microseconds)
+    @param required_min_rx - required min receive interval (microseconds)
+    @param local_addr - local address
+    @param peer_addr - peer address
+    @param is_ipv6 - local_addr, peer_addr are IPv6 if non-zero, otherwise IPv4
+    @param detect_mult - detect multiplier (# of packets missed before connection goes down)
+*/
+define bfd_udp_mod
+{
+  u32 client_index;
+  u32 context;
+  u32 sw_if_index;
+  u32 desired_min_tx;
+  u32 required_min_rx;
+  u8 local_addr[16];
+  u8 peer_addr[16];
+  u8 is_ipv6;
+  u8 detect_mult;
+};
+
+/** \brief Modify UDP BFD session response
+    @param context - sender context, to match reply w/ request
+    @param retval - return code for the request
+*/
+define bfd_udp_mod_reply
+{
+  u32 context;
+  i32 retval;
+};
+
 /** \brief Delete UDP BFD session on interface
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
@@ -155,6 +189,9 @@ define bfd_udp_session_dump
     @param is_authenticated - non-zero if authentication in-use, zero otherwise
     @param bfd_key_id - ID of key currently in-use if auth is on
     @param conf_key_id - configured key ID for this session
+    @param required_min_rx - required min receive interval (microseconds)
+    @param desired_min_tx - desired min transmit interval (microseconds)
+    @param detect_mult - detect multiplier (# of packets missed before connection goes down)
 */
 define bfd_udp_session_details
 {
@@ -167,6 +204,9 @@ define bfd_udp_session_details
   u8 is_authenticated;
   u8 bfd_key_id;
   u32 conf_key_id;
+  u32 required_min_rx;
+  u32 desired_min_tx;
+  u8 detect_mult;
 };
 
 /** \brief Set flags of BFD UDP session
index cfc3a38..af70f0e 100644 (file)
@@ -45,6 +45,7 @@
 
 #define foreach_vpe_api_msg                                \
   _ (BFD_UDP_ADD, bfd_udp_add)                             \
+  _ (BFD_UDP_MOD, bfd_udp_mod)                             \
   _ (BFD_UDP_DEL, bfd_udp_del)                             \
   _ (BFD_UDP_SESSION_DUMP, bfd_udp_session_dump)           \
   _ (BFD_UDP_SESSION_SET_FLAGS, bfd_udp_session_set_flags) \
@@ -97,6 +98,25 @@ vl_api_bfd_udp_add_t_handler (vl_api_bfd_udp_add_t * mp)
   REPLY_MACRO (VL_API_BFD_UDP_ADD_REPLY);
 }
 
+static void
+vl_api_bfd_udp_mod_t_handler (vl_api_bfd_udp_mod_t * mp)
+{
+  vl_api_bfd_udp_mod_reply_t *rmp;
+  int rv;
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+  BFD_UDP_API_PARAM_COMMON_CODE;
+
+  rv = bfd_udp_mod_session (BFD_UDP_API_PARAM_FROM_MP (mp),
+                           clib_net_to_host_u32 (mp->desired_min_tx),
+                           clib_net_to_host_u32 (mp->required_min_rx),
+                           mp->detect_mult);
+
+  BAD_SW_IF_INDEX_LABEL;
+  REPLY_MACRO (VL_API_BFD_UDP_MOD_REPLY);
+}
+
 static void
 vl_api_bfd_udp_del_t_handler (vl_api_bfd_udp_del_t * mp)
 {
@@ -146,6 +166,10 @@ send_bfd_udp_session_details (unix_shared_memory_queue_t * q, u32 context,
                   sizeof (key->peer_addr.ip4.data));
     }
 
+  mp->required_min_rx =
+    clib_host_to_net_u32 (bs->config_required_min_rx_usec);
+  mp->desired_min_tx = clib_host_to_net_u32 (bs->config_desired_min_tx_usec);
+  mp->detect_mult = bs->local_detect_mult;
   vl_msg_api_send_shmem (q, (u8 *) & mp);
 }
 
index 128a3dc..f4486a7 100644 (file)
 
 vnet_api_error_t
 bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
-                    const ip46_address_t * peer_addr, u32 desired_min_tx_us,
-                    u32 required_min_rx_us, u8 detect_mult,
-                    u8 is_authenticated, u32 conf_key_id, u8 bfd_key_id);
+                    const ip46_address_t * peer_addr,
+                    u32 desired_min_tx_usec, u32 required_min_rx_usec,
+                    u8 detect_mult, u8 is_authenticated, u32 conf_key_id,
+                    u8 bfd_key_id);
+
+vnet_api_error_t bfd_udp_mod_session (u32 sw_if_index,
+                                     const ip46_address_t * local_addr,
+                                     const ip46_address_t * peer_addr,
+                                     u32 desired_min_tx_usec,
+                                     u32 required_min_rx_usec,
+                                     u8 detect_mult);
 
 vnet_api_error_t bfd_udp_del_session (u32 sw_if_index,
                                      const ip46_address_t * local_addr,
index 707ebab..a06e934 100644 (file)
     }                                                                    \
   while (0);
 
+#define BFD_CLK_FMT "%luus/%lu clocks/%.2fs"
+#define BFD_CLK_PRN(clocks)                                                \
+  (u64) ((((f64)clocks) / vlib_get_main ()->clib_time.clocks_per_second) * \
+         USEC_PER_SECOND),                                                 \
+      (clocks),                                                            \
+      (((f64)clocks) / vlib_get_main ()->clib_time.clocks_per_second)
+
 #else
 #define BFD_DBG(...)
 #define BFD_ERR(...)
index 8f2fae2..798d063 100644 (file)
 #endif
 
 static u64
-bfd_us_to_clocks (bfd_main_t * bm, u64 us)
+bfd_usec_to_clocks (const bfd_main_t * bm, u64 us)
 {
   return bm->cpu_cps * ((f64) us / USEC_PER_SECOND);
 }
 
+// static u64 bfd_clocks_to_usec (const bfd_main_t *bm, u64 clocks)
+//{
+//  return (clocks / bm->cpu_cps) * USEC_PER_SECOND;
+//}
+
 static vlib_node_registration_t bfd_process_node;
 
 /* set to 0 here, real values filled at startup */
@@ -83,9 +88,11 @@ bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
   bs->remote_state = BFD_STATE_down;
   bs->local_demand = 0;
   bs->remote_discr = 0;
-  bs->desired_min_tx_us = BFD_DEFAULT_DESIRED_MIN_TX_US;
-  bs->desired_min_tx_clocks = bfd_us_to_clocks (bm, bs->desired_min_tx_us);
-  bs->remote_min_rx_us = 1;
+  bs->config_desired_min_tx_usec = BFD_DEFAULT_DESIRED_MIN_TX_US;
+  bs->config_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
+  bs->effective_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
+  bs->remote_min_rx_usec = 1;
+  bs->remote_min_rx_clocks = bfd_usec_to_clocks (bm, bs->remote_min_rx_usec);
   bs->remote_demand = 0;
   bs->auth.remote_seq_number = 0;
   bs->auth.remote_seq_number_known = 0;
@@ -123,7 +130,8 @@ bfd_recalc_tx_interval (bfd_main_t * bm, bfd_session_t * bs)
   if (!bs->local_demand)
     {
       bs->transmit_interval_clocks =
-       clib_max (bs->desired_min_tx_clocks, bs->remote_min_rx_clocks);
+       clib_max (bs->effective_desired_min_tx_clocks,
+                 bs->remote_min_rx_clocks);
     }
   else
     {
@@ -193,18 +201,18 @@ bfd_recalc_detection_time (bfd_main_t * bm, bfd_session_t * bs)
 {
   if (!bs->local_demand)
     {
+      /* asynchronous mode */
       bs->detection_time_clocks =
        bs->remote_detect_mult *
-       bfd_us_to_clocks (bm, clib_max (bs->required_min_rx_us,
-                                       bs->remote_desired_min_tx_us));
+       clib_max (bs->effective_required_min_rx_clocks,
+                 bs->remote_desired_min_tx_clocks);
     }
   else
     {
+      /* demand mode */
       bs->detection_time_clocks =
-       bs->local_detect_mult *
-       bfd_us_to_clocks (bm,
-                         clib_max (bs->desired_min_tx_us,
-                                   bs->remote_min_rx_us));
+       bs->local_detect_mult * clib_max (bs->config_desired_min_tx_clocks,
+                                         bs->remote_min_rx_clocks);
     }
   BFD_DBG ("Recalculated detection time %lu clocks/%.2fs",
           bs->detection_time_clocks,
@@ -259,30 +267,44 @@ bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now,
 }
 
 static void
-bfd_set_desired_min_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now,
-                       u32 desired_min_tx_us, int handling_wakeup)
+bfd_set_effective_desired_min_tx (bfd_main_t * bm,
+                                 bfd_session_t * bs, u64 now,
+                                 u64 desired_min_tx_clocks,
+                                 int handling_wakeup)
 {
-  bs->desired_min_tx_us = desired_min_tx_us;
-  bs->desired_min_tx_clocks = bfd_us_to_clocks (bm, bs->desired_min_tx_us);
-  BFD_DBG ("Set desired min tx to %uus/%lu clocks/%.2fs",
-          bs->desired_min_tx_us, bs->desired_min_tx_clocks,
-          bs->desired_min_tx_clocks / bm->cpu_cps);
+  bs->effective_desired_min_tx_clocks = desired_min_tx_clocks;
+  BFD_DBG ("Set effective desired min tx to " BFD_CLK_FMT,
+          BFD_CLK_PRN (bs->effective_desired_min_tx_clocks));
   bfd_recalc_detection_time (bm, bs);
   bfd_recalc_tx_interval (bm, bs);
   bfd_calc_next_tx (bm, bs, now);
   bfd_set_timer (bm, bs, now, handling_wakeup);
 }
 
+static void
+bfd_set_effective_required_min_rx (bfd_main_t * bm,
+                                  bfd_session_t * bs, u64 now,
+                                  u64 required_min_rx_clocks,
+                                  int handling_wakeup)
+{
+  bs->effective_required_min_rx_clocks = required_min_rx_clocks;
+  BFD_DBG ("Set effective required min rx to " BFD_CLK_FMT,
+          BFD_CLK_PRN (bs->effective_required_min_rx_clocks));
+  bfd_recalc_detection_time (bm, bs);
+  bfd_set_timer (bm, bs, now, handling_wakeup);
+}
+
 static void
 bfd_set_remote_required_min_rx (bfd_main_t * bm, bfd_session_t * bs,
                                u64 now,
-                               u32 remote_required_min_rx_us,
+                               u32 remote_required_min_rx_usec,
                                int handling_wakeup)
 {
-  bs->remote_min_rx_us = remote_required_min_rx_us;
-  bs->remote_min_rx_clocks = bfd_us_to_clocks (bm, bs->remote_min_rx_us);
-  BFD_DBG ("Set remote min rx to %uus/%lu clocks/%.2fs", bs->remote_min_rx_us,
-          bs->remote_min_rx_clocks, bs->remote_min_rx_clocks / bm->cpu_cps);
+  bs->remote_min_rx_usec = remote_required_min_rx_usec;
+  bs->remote_min_rx_clocks =
+    bfd_usec_to_clocks (bm, remote_required_min_rx_usec);
+  BFD_DBG ("Set remote min rx to " BFD_CLK_FMT,
+          BFD_CLK_PRN (bs->remote_min_rx_clocks));
   bfd_recalc_detection_time (bm, bs);
   bfd_recalc_tx_interval (bm, bs);
   bfd_calc_next_tx (bm, bs, now);
@@ -316,32 +338,6 @@ bfd_del_session (uword bs_idx)
   return 0;
 }
 
-const char *
-bfd_diag_code_string (bfd_diag_code_e diag)
-{
-#define F(n, t, s)             \
-  case BFD_DIAG_CODE_NAME (t): \
-    return s;
-  switch (diag)
-    {
-    foreach_bfd_diag_code (F)}
-  return "UNKNOWN";
-#undef F
-}
-
-const char *
-bfd_state_string (bfd_state_e state)
-{
-#define F(n, t, s)         \
-  case BFD_STATE_NAME (t): \
-    return s;
-  switch (state)
-    {
-    foreach_bfd_state (F)}
-  return "UNKNOWN";
-#undef F
-}
-
 void
 bfd_session_set_flags (bfd_session_t * bs, u8 admin_up_down)
 {
@@ -404,30 +400,62 @@ bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
   switch (bs->local_state)
     {
     case BFD_STATE_admin_down:
-      bfd_set_desired_min_tx (bm, bs, now,
-                             clib_max (bs->config_desired_min_tx_us,
-                                       BFD_DEFAULT_DESIRED_MIN_TX_US),
-                             handling_wakeup);
+      bfd_set_effective_required_min_rx (bm, bs, now,
+                                        bs->config_required_min_rx_clocks,
+                                        handling_wakeup);
+      bfd_set_effective_desired_min_tx (bm, bs, now,
+                                       clib_max
+                                       (bs->config_desired_min_tx_clocks,
+                                        bm->default_desired_min_tx_clocks),
+                                       handling_wakeup);
       break;
     case BFD_STATE_down:
-      bfd_set_desired_min_tx (bm, bs, now,
-                             clib_max (bs->config_desired_min_tx_us,
-                                       BFD_DEFAULT_DESIRED_MIN_TX_US),
-                             handling_wakeup);
+      bfd_set_effective_required_min_rx (bm, bs, now,
+                                        bs->config_required_min_rx_clocks,
+                                        handling_wakeup);
+      bfd_set_effective_desired_min_tx (bm, bs, now,
+                                       clib_max
+                                       (bs->config_desired_min_tx_clocks,
+                                        bm->default_desired_min_tx_clocks),
+                                       handling_wakeup);
       break;
     case BFD_STATE_init:
-      bfd_set_desired_min_tx (bm, bs, now,
-                             clib_max (bs->config_desired_min_tx_us,
-                                       BFD_DEFAULT_DESIRED_MIN_TX_US),
-                             handling_wakeup);
+      bfd_set_effective_desired_min_tx (bm, bs, now,
+                                       clib_max
+                                       (bs->config_desired_min_tx_clocks,
+                                        bm->default_desired_min_tx_clocks),
+                                       handling_wakeup);
       break;
     case BFD_STATE_up:
-      bfd_set_desired_min_tx (bm, bs, now, bs->config_desired_min_tx_us,
-                             handling_wakeup);
+      if (POLL_NOT_NEEDED == bs->poll_state)
+       {
+         bfd_set_effective_required_min_rx (bm, bs, now,
+                                            bs->config_required_min_rx_clocks,
+                                            handling_wakeup);
+       }
+      bfd_set_effective_desired_min_tx (bm, bs, now,
+                                       bs->config_desired_min_tx_clocks,
+                                       handling_wakeup);
       break;
     }
 }
 
+static void
+bfd_on_config_change (vlib_main_t * vm, vlib_node_runtime_t * rt,
+                     bfd_main_t * bm, bfd_session_t * bs, u64 now)
+{
+  if (bs->remote_demand)
+    {
+      /* TODO - initiate poll sequence here */
+    }
+  else
+    {
+      /* asynchronous - poll is part of periodic - nothing to do here */
+    }
+  bfd_recalc_detection_time (bm, bs);
+  bfd_set_timer (bm, bs, now, 0);
+}
+
 static void
 bfd_add_transport_layer (vlib_main_t * vm, vlib_buffer_t * b,
                         bfd_session_t * bs)
@@ -557,11 +585,10 @@ bfd_init_control_frame (vlib_buffer_t * b, bfd_session_t * bs)
   pkt->head.length = clib_host_to_net_u32 (bfd_length);
   pkt->my_disc = bs->local_discr;
   pkt->your_disc = bs->remote_discr;
-  pkt->des_min_tx = clib_host_to_net_u32 (bs->desired_min_tx_us);
-  pkt->req_min_rx = clib_host_to_net_u32 (bs->required_min_rx_us);
-  pkt->req_min_echo_rx = clib_host_to_net_u32 (bs->required_min_echo_rx_us);
+  pkt->des_min_tx = clib_host_to_net_u32 (bs->config_desired_min_tx_usec);
+  pkt->req_min_rx = clib_host_to_net_u32 (bs->config_required_min_rx_usec);
+  pkt->req_min_echo_rx = clib_host_to_net_u32 (1);
   b->current_length = bfd_length;
-  bfd_add_auth_section (b, bs);
 }
 
 static void
@@ -569,7 +596,7 @@ bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
                   bfd_main_t * bm, bfd_session_t * bs, u64 now,
                   int handling_wakeup)
 {
-  if (!bs->remote_min_rx_us)
+  if (!bs->remote_min_rx_usec)
     {
       BFD_DBG
        ("bfd.RemoteMinRxInterval is zero, not sending periodic control "
@@ -593,6 +620,14 @@ bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
          return;
        }
       bfd_init_control_frame (b, bs);
+      if (POLL_NOT_NEEDED != bs->poll_state)
+       {
+         /* here we are either beginning a new poll sequence or retrying .. */
+         bfd_pkt_set_poll (vlib_buffer_get_current (b));
+         bs->poll_state = POLL_IN_PROGRESS;
+         BFD_DBG ("Setting poll bit in packet, bs_idx=%u", bs->bs_idx);
+       }
+      bfd_add_auth_section (b, bs);
       bfd_add_transport_layer (vm, b, bs);
       bs->last_tx_clocks = now;
       bfd_calc_next_tx (bm, bs, now);
@@ -613,8 +648,14 @@ bfd_init_final_control_frame (vlib_main_t * vm, vlib_buffer_t * b,
   BFD_DBG ("Send final control frame for bs_idx=%lu", bs->bs_idx);
   bfd_init_control_frame (b, bs);
   bfd_pkt_set_final (vlib_buffer_get_current (b));
+  bfd_add_auth_section (b, bs);
   bfd_add_transport_layer (vm, b, bs);
   bs->last_tx_clocks = clib_cpu_time_now ();
+  /*
+   * RFC allows to include changes in final frame, so if there were any
+   * pending, we already did that, thus we can clear any pending poll needs
+   */
+  bs->poll_state = POLL_NOT_NEEDED;
 }
 
 static void
@@ -629,6 +670,14 @@ bfd_check_rx_timeout (bfd_main_t * bm, bfd_session_t * bs, u64 now,
       BFD_DBG ("Rx timeout, session goes down");
       bfd_set_diag (bs, BFD_DIAG_CODE_det_time_exp);
       bfd_set_state (bm, bs, BFD_STATE_down, handling_wakeup);
+      /*
+       * If the remote system does not receive any
+       * BFD Control packets for a Detection Time, it SHOULD reset
+       * bfd.RemoteMinRxInterval to its initial value of 1 (per section 6.8.1,
+       * since it is no longer required to maintain previous session state)
+       * and then can transmit at its own rate.
+       */
+      bfd_set_remote_required_min_rx (bm, bs, now, 1, handling_wakeup);
     }
 }
 
@@ -722,6 +771,19 @@ bfd_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
                       (u32) * event_data);
            }
          break;
+       case BFD_EVENT_CONFIG_CHANGED:
+         if (!pool_is_free_index (bm->sessions, *event_data))
+           {
+             bfd_session_t *bs =
+               pool_elt_at_index (bm->sessions, *event_data);
+             bfd_on_config_change (vm, rt, bm, bs, now);
+           }
+         else
+           {
+             BFD_DBG ("Ignoring event for non-existent session index %u",
+                      (u32) * event_data);
+           }
+         break;
        default:
          clib_warning ("BUG: event type 0x%wx", event_type);
          break;
@@ -810,6 +872,8 @@ bfd_main_init (vlib_main_t * vm)
   memset (&bm->wheel, 0, sizeof (bm->wheel));
   bm->cpu_cps = vm->clib_time.clocks_per_second;
   BFD_DBG ("cps is %.2f", bm->cpu_cps);
+  bm->default_desired_min_tx_clocks =
+    bfd_usec_to_clocks (bm, BFD_DEFAULT_DESIRED_MIN_TX_US);
   const u64 now = clib_cpu_time_now ();
   timing_wheel_init (&bm->wheel, now, bm->cpu_cps);
   bm->wheel_inaccuracy = 2 << bm->wheel.log2_clocks_per_bin;
@@ -1283,7 +1347,8 @@ bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
          while (0);
        }
     }
-  bs->remote_desired_min_tx_us = clib_net_to_host_u32 (pkt->des_min_tx);
+  bs->remote_desired_min_tx_clocks =
+    bfd_usec_to_clocks (bm, clib_net_to_host_u32 (pkt->des_min_tx));
   bs->remote_detect_mult = pkt->head.detect_mult;
   bfd_set_remote_required_min_rx (bm, bs, now,
                                  clib_net_to_host_u32 (pkt->req_min_rx), 0);
@@ -1297,6 +1362,18 @@ bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
    */
   /* FIXME 6.8.2 */
   /* FIXME 6.8.4 */
+  if (bs->poll_state == POLL_IN_PROGRESS && bfd_pkt_get_final (pkt))
+    {
+      bs->poll_state = POLL_NOT_NEEDED;
+      BFD_DBG ("Poll sequence terminated, bs_idx=%u", bs->bs_idx);
+      if (BFD_STATE_up == bs->local_state)
+       {
+         bfd_set_effective_required_min_rx (bm, bs, now,
+                                            bs->config_required_min_rx_clocks,
+                                            0);
+         bfd_recalc_detection_time (bm, bs);
+       }
+    }
   if (BFD_STATE_admin_down == bs->local_state)
     return;
   if (BFD_STATE_admin_down == bs->remote_state)
@@ -1333,6 +1410,20 @@ bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
     }
 }
 
+static const char *
+bfd_poll_state_string (bfd_poll_state_e state)
+{
+  switch (state)
+    {
+#define F(x)     \
+  case POLL_##x: \
+    return "POLL_" #x;
+      foreach_bfd_poll_state (F)
+#undef F
+    }
+  return "UNKNOWN";
+}
+
 u8 *
 format_bfd_session (u8 * s, va_list * args)
 {
@@ -1353,17 +1444,18 @@ format_bfd_session (u8 * s, va_list * args)
              "remote-seq-num=%u, "
              "is-delayed=%s, "
              "curr-key=%U, "
-             "next-key=%U}",
+             "next-key=%U},"
+             "poll-state: %s",
              bs->bs_idx, bfd_state_string (bs->local_state),
              bfd_state_string (bs->remote_state), bs->local_discr,
              bs->remote_discr, bfd_diag_code_string (bs->local_diag),
-             bs->desired_min_tx_us, bs->required_min_rx_us,
-             bs->required_min_echo_rx_us, bs->remote_min_rx_us,
-             (bs->local_demand ? "yes" : "no"),
+             bs->config_desired_min_tx_usec, bs->config_required_min_rx_usec,
+             1, bs->remote_min_rx_usec, (bs->local_demand ? "yes" : "no"),
              (bs->remote_demand ? "yes" : "no"), bs->local_detect_mult,
              bs->auth.local_seq_number, bs->auth.remote_seq_number,
              (bs->auth.is_delayed ? "yes" : "no"), format_bfd_auth_key,
-             bs->auth.curr_key, format_bfd_auth_key, bs->auth.next_key);
+             bs->auth.curr_key, format_bfd_auth_key, bs->auth.next_key,
+             bfd_poll_state_string (bs->poll_state));
   return s;
 }
 
@@ -1462,6 +1554,62 @@ bfd_auth_deactivate (bfd_session_t * bs, u8 is_delayed)
 #endif
 }
 
+vnet_api_error_t
+bfd_session_set_params (bfd_main_t * bm, bfd_session_t * bs,
+                       u32 desired_min_tx_usec,
+                       u32 required_min_rx_usec, u8 detect_mult)
+{
+  if (bs->local_detect_mult != detect_mult ||
+      bs->config_desired_min_tx_usec != desired_min_tx_usec ||
+      bs->config_required_min_rx_usec != required_min_rx_usec)
+    {
+      BFD_DBG ("Changing session params: %U", format_bfd_session, bs);
+      switch (bs->poll_state)
+       {
+       case POLL_NOT_NEEDED:
+         if (BFD_STATE_up == bs->local_state ||
+             BFD_STATE_init == bs->local_state)
+           {
+             /* poll sequence is not needed for detect multiplier change */
+             if (bs->config_desired_min_tx_usec != desired_min_tx_usec ||
+                 bs->config_required_min_rx_usec != required_min_rx_usec)
+               {
+                 bs->poll_state = POLL_NEEDED;
+                 BFD_DBG ("Set poll state=%s, bs_idx=%u",
+                          bfd_poll_state_string (bs->poll_state),
+                          bs->bs_idx);
+               }
+           }
+         break;
+       case POLL_NEEDED:
+         /* nothing to do */
+         break;
+       case POLL_IN_PROGRESS:
+         /* can't change params now ... */
+         BFD_ERR ("Poll in progress, cannot change params for session with "
+                  "bs_idx=%u", bs->bs_idx);
+         return VNET_API_ERROR_BFD_EAGAIN;
+       }
+
+      bs->local_detect_mult = detect_mult;
+      bs->config_desired_min_tx_usec = desired_min_tx_usec;
+      bs->config_desired_min_tx_clocks =
+       bfd_usec_to_clocks (bm, desired_min_tx_usec);
+      bs->config_required_min_rx_usec = required_min_rx_usec;
+      bs->config_required_min_rx_clocks =
+       bfd_usec_to_clocks (bm, required_min_rx_usec);
+      BFD_DBG ("Changed session params: %U", format_bfd_session, bs);
+
+      vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
+                                BFD_EVENT_CONFIG_CHANGED, bs->bs_idx);
+    }
+  else
+    {
+      BFD_DBG ("Ignore parameter change - no change, bs_idx=%u", bs->bs_idx);
+    }
+  return 0;
+}
+
 bfd_main_t bfd_main;
 
 /*
index b66b79e..361ff0b 100644 (file)
@@ -64,6 +64,18 @@ typedef struct
   bfd_auth_type_e auth_type;
 } bfd_auth_key_t;
 
+#define foreach_bfd_poll_state(F)\
+  F(NOT_NEEDED)\
+F(NEEDED)\
+F(IN_PROGRESS)
+
+typedef enum
+{
+#define F(x) POLL_##x,
+  foreach_bfd_poll_state (F)
+#undef F
+} bfd_poll_state_e;
+
 typedef struct
 {
   /* index in bfd_main.sessions pool */
@@ -85,28 +97,34 @@ typedef struct
   u32 remote_discr;
 
   /* configured desired min tx interval (microseconds) */
-  u32 config_desired_min_tx_us;
+  u32 config_desired_min_tx_usec;
+
+  /* configured desired min tx interval (clocks) */
+  u64 config_desired_min_tx_clocks;
 
-  /* desired min tx interval (microseconds) */
-  u32 desired_min_tx_us;
+  /* effective desired min tx interval (clocks) */
+  u64 effective_desired_min_tx_clocks;
 
-  /* desired min tx interval (clocks) */
-  u64 desired_min_tx_clocks;
+  /* configured required min rx interval (microseconds) */
+  u32 config_required_min_rx_usec;
 
-  /* required min rx interval (microseconds) */
-  u32 required_min_rx_us;
+  /* configured required min rx interval (clocks) */
+  u64 config_required_min_rx_clocks;
 
-  /* required min echo rx interval (microseconds) */
-  u32 required_min_echo_rx_us;
+  /* effective required min rx interval (clocks) */
+  u64 effective_required_min_rx_clocks;
 
   /* remote min rx interval (microseconds) */
-  u32 remote_min_rx_us;
+  u64 remote_min_rx_usec;
 
   /* remote min rx interval (clocks) */
   u64 remote_min_rx_clocks;
 
-  /* remote desired min tx interval (microseconds) */
-  u32 remote_desired_min_tx_us;
+  /* remote desired min tx interval (clocks) */
+  u64 remote_desired_min_tx_clocks;
+
+  /* configured detect multiplier */
+  u8 local_detect_mult;
 
   /* 1 if in demand mode, 0 otherwise */
   u8 local_demand;
@@ -114,9 +132,6 @@ typedef struct
   /* 1 if remote system sets demand mode, 0 otherwise */
   u8 remote_demand;
 
-  /* local detect multiplier */
-  u8 local_detect_mult;
-
   /* remote detect multiplier */
   u8 remote_detect_mult;
 
@@ -138,6 +153,9 @@ typedef struct
   /* detection time */
   u64 detection_time_clocks;
 
+  /* state info regarding poll sequence */
+  bfd_poll_state_e poll_state;
+
   /* authentication information */
   struct
   {
@@ -175,6 +193,7 @@ typedef struct
   /* transport type for this session */
   bfd_transport_t transport;
 
+  /* union of transport-specific data */
   union
   {
     bfd_udp_session_t udp;
@@ -205,6 +224,9 @@ typedef struct
   /* cpu clocks per second */
   f64 cpu_cps;
 
+  /* default desired min tx in clocks */
+  u64 default_desired_min_tx_clocks;
+
   /* for generating random numbers */
   u32 random_seed;
 
@@ -243,6 +265,7 @@ enum
 {
   BFD_EVENT_RESCHEDULE = 1,
   BFD_EVENT_NEW_SESSION,
+  BFD_EVENT_CONFIG_CHANGED,
 } bfd_process_event_e;
 
 u8 *bfd_input_format_trace (u8 * s, va_list * args);
@@ -265,6 +288,10 @@ unsigned bfd_auth_type_supported (bfd_auth_type_e auth_type);
 vnet_api_error_t bfd_auth_activate (bfd_session_t * bs, u32 conf_key_id,
                                    u8 bfd_key_id, u8 is_delayed);
 vnet_api_error_t bfd_auth_deactivate (bfd_session_t * bs, u8 is_delayed);
+vnet_api_error_t
+bfd_session_set_params (bfd_main_t * bm, bfd_session_t * bs,
+                       u32 desired_min_tx_usec,
+                       u32 required_min_rx_usec, u8 detect_mult);
 
 #define USEC_PER_MS 1000LL
 #define USEC_PER_SECOND (1000 * USEC_PER_MS)
index 180fc6d..92b226b 100644 (file)
@@ -150,6 +150,32 @@ bfd_auth_type_str (bfd_auth_type_e auth_type)
   return "UNKNOWN";
 }
 
+const char *
+bfd_diag_code_string (bfd_diag_code_e diag)
+{
+#define F(n, t, s)             \
+  case BFD_DIAG_CODE_NAME (t): \
+    return s;
+  switch (diag)
+    {
+    foreach_bfd_diag_code (F)}
+  return "UNKNOWN";
+#undef F
+}
+
+const char *
+bfd_state_string (bfd_state_e state)
+{
+#define F(n, t, s)         \
+  case BFD_STATE_NAME (t): \
+    return s;
+  switch (state)
+    {
+    foreach_bfd_state (F)}
+  return "UNKNOWN";
+#undef F
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 443f425..e21b887 100644 (file)
@@ -141,8 +141,8 @@ bfd_udp_key_init (bfd_udp_key_t * key, u32 sw_if_index,
 
 static vnet_api_error_t
 bfd_udp_add_session_internal (bfd_udp_main_t * bum, u32 sw_if_index,
-                             u32 desired_min_tx_us, u32 required_min_rx_us,
-                             u8 detect_mult,
+                             u32 desired_min_tx_usec,
+                             u32 required_min_rx_usec, u8 detect_mult,
                              const ip46_address_t * local_addr,
                              const ip46_address_t * peer_addr,
                              bfd_session_t ** bs_out)
@@ -189,12 +189,9 @@ bfd_udp_add_session_internal (bfd_udp_main_t * bum, u32 sw_if_index,
               "returns %d", format_ip46_address, &key->peer_addr,
               IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
     }
-  bs->config_desired_min_tx_us = desired_min_tx_us;
-  bs->required_min_rx_us = required_min_rx_us;
-  bs->required_min_echo_rx_us = required_min_rx_us;    /* FIXME */
-  bs->local_detect_mult = detect_mult;
   *bs_out = bs;
-  return 0;
+  return bfd_session_set_params (bum->bfd_main, bs, desired_min_tx_usec,
+                                required_min_rx_usec, detect_mult);
 }
 
 static vnet_api_error_t
@@ -298,8 +295,8 @@ bfd_udp_find_session_by_api_input (u32 sw_if_index,
 }
 
 static vnet_api_error_t
-bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_us,
-                      u32 required_min_rx_us, u8 detect_mult,
+bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_usec,
+                      u32 required_min_rx_usec, u8 detect_mult,
                       const ip46_address_t * local_addr,
                       const ip46_address_t * peer_addr)
 {
@@ -314,9 +311,9 @@ bfd_api_verify_common (u32 sw_if_index, u32 desired_min_tx_us,
       clib_warning ("detect_mult < 1");
       return VNET_API_ERROR_INVALID_ARGUMENT;
     }
-  if (desired_min_tx_us < 1)
+  if (desired_min_tx_usec < 1)
     {
-      clib_warning ("desired_min_tx_us < 1");
+      clib_warning ("desired_min_tx_usec < 1");
       return VNET_API_ERROR_INVALID_ARGUMENT;
     }
   return 0;
@@ -334,22 +331,23 @@ bfd_udp_del_session_internal (bfd_session_t * bs)
 
 vnet_api_error_t
 bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
-                    const ip46_address_t * peer_addr, u32 desired_min_tx_us,
-                    u32 required_min_rx_us, u8 detect_mult,
-                    u8 is_authenticated, u32 conf_key_id, u8 bfd_key_id)
+                    const ip46_address_t * peer_addr,
+                    u32 desired_min_tx_usec, u32 required_min_rx_usec,
+                    u8 detect_mult, u8 is_authenticated, u32 conf_key_id,
+                    u8 bfd_key_id)
 {
-  vnet_api_error_t rv = bfd_api_verify_common (sw_if_index, desired_min_tx_us,
-                                              required_min_rx_us,
-                                              detect_mult,
-                                              local_addr, peer_addr);
+  vnet_api_error_t rv =
+    bfd_api_verify_common (sw_if_index, desired_min_tx_usec,
+                          required_min_rx_usec, detect_mult,
+                          local_addr, peer_addr);
   bfd_session_t *bs = NULL;
   if (!rv)
     {
       rv =
        bfd_udp_add_session_internal (&bfd_udp_main, sw_if_index,
-                                     desired_min_tx_us, required_min_rx_us,
-                                     detect_mult, local_addr, peer_addr,
-                                     &bs);
+                                     desired_min_tx_usec,
+                                     required_min_rx_usec, detect_mult,
+                                     local_addr, peer_addr, &bs);
     }
   if (!rv && is_authenticated)
     {
@@ -373,6 +371,27 @@ bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr,
   return rv;
 }
 
+vnet_api_error_t
+bfd_udp_mod_session (u32 sw_if_index,
+                    const ip46_address_t * local_addr,
+                    const ip46_address_t * peer_addr,
+                    u32 desired_min_tx_usec,
+                    u32 required_min_rx_usec, u8 detect_mult)
+{
+  bfd_session_t *bs = NULL;
+  vnet_api_error_t rv =
+    bfd_udp_find_session_by_api_input (sw_if_index, local_addr, peer_addr,
+                                      &bs);
+  if (rv)
+    {
+      return rv;
+    }
+
+  return bfd_session_set_params (bfd_udp_main.bfd_main, bs,
+                                desired_min_tx_usec, required_min_rx_usec,
+                                detect_mult);
+}
+
 vnet_api_error_t
 bfd_udp_del_session (u32 sw_if_index,
                     const ip46_address_t * local_addr,
index 475a170..dc6f967 100644 (file)
@@ -334,6 +334,25 @@ class VppBFDUDPSession(VppObject):
                                                is_ipv6=is_ipv6,
                                                is_delayed=is_delayed)
 
+    def modify_parameters(self,
+                          detect_mult=None,
+                          desired_min_tx=None,
+                          required_min_rx=None):
+        if detect_mult:
+            self._detect_mult = detect_mult
+        if desired_min_tx:
+            self._desired_min_tx = desired_min_tx
+        if required_min_rx:
+            self._required_min_rx = required_min_rx
+        is_ipv6 = 1 if AF_INET6 == self.af else 0
+        self.test.vapi.bfd_udp_mod(self._interface.sw_if_index,
+                                   self.desired_min_tx,
+                                   self.required_min_rx,
+                                   self.detect_mult,
+                                   self.local_addr_n,
+                                   self.peer_addr_n,
+                                   is_ipv6=is_ipv6)
+
     def add_vpp_config(self):
         is_ipv6 = 1 if AF_INET6 == self.af else 0
         bfd_key_id = self._bfd_key_id if self._sha1_key else None
index 889a304..4185dbf 100644 (file)
@@ -545,6 +545,10 @@ class VppTestCase(unittest.TestCase):
                 name, real_value, expected_min, expected_max)
         self.assertTrue(expected_min <= real_value <= expected_max, msg)
 
+    def sleep(self, timeout):
+        self.logger.debug("Sleeping for %ss" % timeout)
+        time.sleep(timeout)
+
 
 class VppTestResult(unittest.TestResult):
     """
index 5f86147..aedd56e 100644 (file)
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+from __future__ import division
 import unittest
 import hashlib
 import binascii
@@ -81,6 +82,33 @@ class BFDAPITestCase(VppTestCase):
         self.logger.debug("Session state is %s" % str(session.state))
         session.remove_vpp_config()
 
+    def test_mod_bfd(self):
+        """ modify BFD session parameters """
+        session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
+                                   desired_min_tx=50000,
+                                   required_min_rx=10000,
+                                   detect_mult=1)
+        session.add_vpp_config()
+        e = session.get_bfd_udp_session_dump_entry()
+        self.assert_equal(session.desired_min_tx,
+                          e.desired_min_tx,
+                          "desired min transmit interval")
+        self.assert_equal(session.required_min_rx,
+                          e.required_min_rx,
+                          "required min receive interval")
+        self.assert_equal(session.detect_mult, e.detect_mult, "detect mult")
+        session.modify_parameters(desired_min_tx=session.desired_min_tx * 2,
+                                  required_min_rx=session.required_min_rx * 2,
+                                  detect_mult=session.detect_mult * 2)
+        e = session.get_bfd_udp_session_dump_entry()
+        self.assert_equal(session.desired_min_tx,
+                          e.desired_min_tx,
+                          "desired min transmit interval")
+        self.assert_equal(session.required_min_rx,
+                          e.required_min_rx,
+                          "required min receive interval")
+        self.assert_equal(session.detect_mult, e.detect_mult, "detect mult")
+
     def test_add_sha1_keys(self):
         """ add SHA1 keys """
         key_count = 10
@@ -194,6 +222,7 @@ class BFDAPITestCase(VppTestCase):
         session.deactivate_auth()
 
     def test_change_key(self):
+        """ change SHA1 key """
         key1 = self.factory.create_random_key(self)
         key2 = self.factory.create_random_key(self)
         while key2.conf_key_id == key1.conf_key_id:
@@ -273,10 +302,11 @@ class BFDTestSession(object):
             packet[BFD].auth_key_hash = hashlib.sha1(hash_material).digest()
         return packet
 
-    def send_packet(self):
-        p = self.create_packet()
-        self.test.logger.debug(ppp("Sending packet:", p))
-        self.test.pg0.add_stream([p])
+    def send_packet(self, packet=None):
+        if packet is None:
+            packet = self.create_packet()
+        self.test.logger.debug(ppp("Sending packet:", packet))
+        self.test.pg0.add_stream([packet])
         self.test.pg_start()
 
     def verify_sha1_auth(self, packet):
@@ -521,11 +551,25 @@ class BFD4TestCase(VppTestCase, BFDCommonCode):
         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
         self.verify_event(e, expected_state=BFDState.up)
 
-        try:
-            p = self.pg0.wait_for_packet(timeout=1)
-        except:
-            return
-        raise Exception(ppp("Received unexpected BFD packet:", p))
+        cap = 2 * self.vpp_session.desired_min_tx *\
+            self.vpp_session.detect_mult
+        now = time.time()
+        count = 0
+        # busy wait here, trying to collect a packet or event, vpp is not
+        # allowed to send packets and the session will timeout first - so the
+        # Up->Down event must arrive before any packets do
+        while time.time() < now + cap / us_in_sec:
+            try:
+                p, ttp = self.wait_for_bfd_packet(timeout=0)
+                self.logger.error(ppp("Received unexpected packet:", p))
+                count += 1
+            except:
+                pass
+            events = self.vapi.collect_events()
+            if len(events) > 0:
+                self.verify_event(events[0], BFDState.down)
+                break
+        self.assert_equal(count, 0, "number of packets received")
 
     def test_conn_down(self):
         """ verify session goes down after inactivity """
@@ -542,20 +586,27 @@ class BFD4TestCase(VppTestCase, BFDCommonCode):
     def test_large_required_min_rx(self):
         """ large remote RequiredMinRxInterval """
         self.bfd_session_up()
+        self.wait_for_bfd_packet()
         interval = 3000000
         self.test_session.update(required_min_rx_interval=interval)
         self.test_session.send_packet()
         now = time.time()
         count = 0
+        # busy wait here, trying to collect a packet or event, vpp is not
+        # allowed to send packets and the session will timeout first - so the
+        # Up->Down event must arrive before any packets do
         while time.time() < now + interval / us_in_sec:
             try:
-                p = self.wait_for_bfd_packet()
-                if count > 1:
-                    self.logger.error(ppp("Received unexpected packet:", p))
+                p, ttp = self.wait_for_bfd_packet(timeout=0)
+                self.logger.error(ppp("Received unexpected packet:", p))
                 count += 1
             except:
                 pass
-        self.assert_in_range(count, 0, 1, "number of packets received")
+            events = self.vapi.collect_events()
+            if len(events) > 0:
+                self.verify_event(events[0], BFDState.down)
+                break
+        self.assert_equal(count, 0, "number of packets received")
 
     def test_immediate_remote_min_rx_reduce(self):
         """ immediately honor remote min rx reduction """
@@ -583,6 +634,93 @@ class BFD4TestCase(VppTestCase, BFDCommonCode):
                              1.10 * interval / us_in_sec,
                              "time between BFD packets")
 
+    def test_modify_req_min_rx_double(self):
+        """ modify session - double required min rx """
+        self.bfd_session_up()
+        self.wait_for_bfd_packet()
+        self.test_session.update(desired_min_tx_interval=10000,
+                                 required_min_rx_interval=10000)
+        self.test_session.send_packet()
+        # first double required min rx
+        self.vpp_session.modify_parameters(
+            required_min_rx=2 * self.vpp_session.required_min_rx)
+        p, ttp = self.wait_for_bfd_packet()
+        # poll bit needs to be set
+        self.assertIn("P", p.sprintf("%BFD.flags%"),
+                      "Poll bit not set in BFD packet")
+        # finish poll sequence with final packet
+        final = self.test_session.create_packet()
+        final[BFD].flags = "F"
+        self.test_session.send_packet(final)
+        # now we can wait 0.9*3*req-min-rx and the session should still be up
+        self.sleep(0.9 * self.vpp_session.detect_mult *
+                   self.vpp_session.required_min_rx / us_in_sec)
+        self.assert_equal(len(self.vapi.collect_events()), 0,
+                          "number of bfd events")
+
+    def test_modify_req_min_rx_halve(self):
+        """ modify session - halve required min rx """
+        self.vpp_session.modify_parameters(
+            required_min_rx=2 * self.vpp_session.required_min_rx)
+        self.bfd_session_up()
+        self.wait_for_bfd_packet()
+        self.test_session.update(desired_min_tx_interval=10000,
+                                 required_min_rx_interval=10000)
+        self.test_session.send_packet()
+        p, ttp = self.wait_for_bfd_packet()
+        # halve required min rx
+        old_required_min_rx = self.vpp_session.required_min_rx
+        self.vpp_session.modify_parameters(
+            required_min_rx=0.5 * self.vpp_session.required_min_rx)
+        # now we wait 0.8*3*old-req-min-rx and the session should still be up
+        self.sleep(0.8 * self.vpp_session.detect_mult *
+                   old_required_min_rx / us_in_sec)
+        self.assert_equal(len(self.vapi.collect_events()), 0,
+                          "number of bfd events")
+        p, ttp = self.wait_for_bfd_packet()
+        # poll bit needs to be set
+        self.assertIn("P", p.sprintf("%BFD.flags%"),
+                      "Poll bit not set in BFD packet")
+        # finish poll sequence with final packet
+        final = self.test_session.create_packet()
+        final[BFD].flags = "F"
+        self.test_session.send_packet(final)
+        # now the session should time out under new conditions
+        before = time.time()
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+        after = time.time()
+        detection_time = self.vpp_session.detect_mult *\
+            self.vpp_session.required_min_rx / us_in_sec
+        self.assert_in_range(after - before,
+                             0.9 * detection_time,
+                             1.1 * detection_time,
+                             "time before bfd session goes down")
+        self.verify_event(e, expected_state=BFDState.down)
+
+    def test_modify_des_min_tx(self):
+        """ modify desired min tx interval """
+        pass
+
+    def test_modify_detect_mult(self):
+        """ modify detect multiplier """
+        self.bfd_session_up()
+        self.vpp_session.modify_parameters(detect_mult=1)
+        p, ttp = self.wait_for_bfd_packet()
+        self.assert_equal(self.vpp_session.detect_mult,
+                          p[BFD].detect_mult,
+                          "detect mult")
+        # poll bit must not be set
+        self.assertNotIn("P", p.sprintf("%BFD.flags%"),
+                         "Poll bit not set in BFD packet")
+        self.vpp_session.modify_parameters(detect_mult=10)
+        p, ttp = self.wait_for_bfd_packet()
+        self.assert_equal(self.vpp_session.detect_mult,
+                          p[BFD].detect_mult,
+                          "detect mult")
+        # poll bit must not be set
+        self.assertNotIn("P", p.sprintf("%BFD.flags%"),
+                         "Poll bit not set in BFD packet")
+
 
 class BFD6TestCase(VppTestCase, BFDCommonCode):
     """Bidirectional Forwarding Detection (BFD) (IPv6) """
index 2cd02cc..39efa9e 100644 (file)
@@ -1023,6 +1023,19 @@ class VppPapiProvider(object):
                                 'conf_key_id': conf_key_id,
                             })
 
+    def bfd_udp_mod(self, sw_if_index, desired_min_tx, required_min_rx,
+                    detect_mult, local_addr, peer_addr, is_ipv6=0):
+        return self.api(self.papi.bfd_udp_mod,
+                        {
+                            'sw_if_index': sw_if_index,
+                            'desired_min_tx': desired_min_tx,
+                            'required_min_rx': required_min_rx,
+                            'local_addr': local_addr,
+                            'peer_addr': peer_addr,
+                            'is_ipv6': is_ipv6,
+                            'detect_mult': detect_mult,
+                        })
+
     def bfd_udp_auth_activate(self, sw_if_index, local_addr, peer_addr,
                               is_ipv6=0, bfd_key_id=None, conf_key_id=None,
                               is_delayed=False):