diff options
-rw-r--r-- | src/vnet/api_errno.h | 3 | ||||
-rw-r--r-- | src/vnet/bfd/bfd.api | 40 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_api.c | 24 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_api.h | 14 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_debug.h | 7 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_main.c | 296 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_main.h | 57 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_protocol.c | 26 | ||||
-rw-r--r-- | src/vnet/bfd/bfd_udp.c | 61 | ||||
-rw-r--r-- | test/bfd.py | 19 | ||||
-rw-r--r-- | test/framework.py | 4 | ||||
-rw-r--r-- | test/test_bfd.py | 164 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 13 |
13 files changed, 601 insertions, 127 deletions
diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index 32880232ec9..0daba1691ef 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -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 { diff --git a/src/vnet/bfd/bfd.api b/src/vnet/bfd/bfd.api index 17ca35b6004..f307ed2a193 100644 --- a/src/vnet/bfd/bfd.api +++ b/src/vnet/bfd/bfd.api @@ -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 diff --git a/src/vnet/bfd/bfd_api.c b/src/vnet/bfd/bfd_api.c index cfc3a38d575..af70f0ec2d5 100644 --- a/src/vnet/bfd/bfd_api.c +++ b/src/vnet/bfd/bfd_api.c @@ -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) \ @@ -98,6 +99,25 @@ vl_api_bfd_udp_add_t_handler (vl_api_bfd_udp_add_t * mp) } 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) { vl_api_bfd_udp_del_reply_t *rmp; @@ -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); } diff --git a/src/vnet/bfd/bfd_api.h b/src/vnet/bfd/bfd_api.h index 128a3dc42bf..f4486a79196 100644 --- a/src/vnet/bfd/bfd_api.h +++ b/src/vnet/bfd/bfd_api.h @@ -26,9 +26,17 @@ 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, diff --git a/src/vnet/bfd/bfd_debug.h b/src/vnet/bfd/bfd_debug.h index 707ebab2ddd..a06e934f560 100644 --- a/src/vnet/bfd/bfd_debug.h +++ b/src/vnet/bfd/bfd_debug.h @@ -63,6 +63,13 @@ } \ 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(...) diff --git a/src/vnet/bfd/bfd_main.c b/src/vnet/bfd/bfd_main.c index 8f2fae2b795..798d0631dd7 100644 --- a/src/vnet/bfd/bfd_main.c +++ b/src/vnet/bfd/bfd_main.c @@ -30,11 +30,16 @@ #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,14 +267,14 @@ 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); @@ -274,15 +282,29 @@ bfd_set_desired_min_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now, } 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,31 +400,63 @@ 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; /* diff --git a/src/vnet/bfd/bfd_main.h b/src/vnet/bfd/bfd_main.h index b66b79e71a0..361ff0b7e1c 100644 --- a/src/vnet/bfd/bfd_main.h +++ b/src/vnet/bfd/bfd_main.h @@ -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) diff --git a/src/vnet/bfd/bfd_protocol.c b/src/vnet/bfd/bfd_protocol.c index 180fc6df8fd..92b226bda8b 100644 --- a/src/vnet/bfd/bfd_protocol.c +++ b/src/vnet/bfd/bfd_protocol.c @@ -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 * diff --git a/src/vnet/bfd/bfd_udp.c b/src/vnet/bfd/bfd_udp.c index 443f4253b96..e21b887c47e 100644 --- a/src/vnet/bfd/bfd_udp.c +++ b/src/vnet/bfd/bfd_udp.c @@ -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) { @@ -374,6 +372,27 @@ bfd_udp_add_session (u32 sw_if_index, const ip46_address_t * local_addr, } 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, const ip46_address_t * peer_addr) diff --git a/test/bfd.py b/test/bfd.py index 475a1707b40..dc6f9674c00 100644 --- a/test/bfd.py +++ b/test/bfd.py @@ -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 diff --git a/test/framework.py b/test/framework.py index 889a30469ba..4185dbfbb74 100644 --- a/test/framework.py +++ b/test/framework.py @@ -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): """ diff --git a/test/test_bfd.py b/test/test_bfd.py index 5f8614777ca..aedd56e44b0 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -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) """ diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 2cd02cc7954..39efa9e4c0e 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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): |