From 738844871220f853629504f61c248f0c9402dc77 Mon Sep 17 00:00:00 2001 From: Klement Sekera Date: Thu, 23 Feb 2017 09:26:30 +0100 Subject: BFD: command line interface Implement command line interface to the BFD binary APIs. Add corresponding unit tests. Change-Id: Ia0542d0bc4c8d78e6f7b777a08fd94ebfe4d524f Signed-off-by: Klement Sekera --- src/vnet.am | 1 + src/vnet/api_errno.h | 1 - src/vnet/bfd/bfd.api | 28 +- src/vnet/bfd/bfd_api.c | 22 +- src/vnet/bfd/bfd_api.h | 1 - src/vnet/bfd/bfd_cli.c | 900 ++++++++++++++++++++++++++++++++++++++++++++ src/vnet/bfd/bfd_main.c | 154 ++++++-- src/vnet/bfd/bfd_main.h | 11 +- src/vnet/bfd/bfd_protocol.c | 6 + src/vnet/bfd/bfd_udp.c | 120 ++---- src/vnet/bfd/bfd_udp.h | 5 + test/bfd.py | 9 +- test/test_bfd.py | 579 +++++++++++++++++++++++++++- test/vpp_object.py | 1 + test/vpp_papi_provider.py | 12 +- 15 files changed, 1659 insertions(+), 191 deletions(-) create mode 100644 src/vnet/bfd/bfd_cli.c diff --git a/src/vnet.am b/src/vnet.am index 84930f05b0a..d89d516e81c 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -369,6 +369,7 @@ libvnet_la_SOURCES += \ vnet/bfd/bfd_udp.c \ vnet/bfd/bfd_main.c \ vnet/bfd/bfd_protocol.c \ + vnet/bfd/bfd_cli.c \ vnet/bfd/bfd_api.c API_FILES += vnet/bfd/bfd.api diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index 861a5767af2..a5bcb3776bb 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -51,7 +51,6 @@ _(ADDRESS_LENGTH_MISMATCH, -59, "Address length mismatch") \ _(ADDRESS_NOT_FOUND_FOR_INTERFACE, -60, "Address not found for interface") \ _(ADDRESS_NOT_LINK_LOCAL, -61, "Address not link-local") \ _(IP6_NOT_ENABLED, -62, "ip6 not enabled") \ -_(ADDRESS_MATCHES_INTERFACE_ADDRESS, -63, "Address matches interface address") \ _(IN_PROGRESS, 10, "Operation in progress") \ _(NO_SUCH_NODE, -63, "No such graph node") \ _(NO_SUCH_NODE2, -64, "No such graph node #2") \ diff --git a/src/vnet/bfd/bfd.api b/src/vnet/bfd/bfd.api index 93bf0fb90e8..2cdcfad3fc8 100644 --- a/src/vnet/bfd/bfd.api +++ b/src/vnet/bfd/bfd.api @@ -45,7 +45,7 @@ define bfd_udp_del_echo_source u32 context; }; -/** \brief Delete BFD feature response +/** \brief Delete BFD echo source response @param context - sender context, to match reply w/ request @param retval - return code for the request */ @@ -55,32 +55,6 @@ define bfd_udp_del_echo_source_reply i32 retval; }; -/** \brief Get BFD configuration -*/ -define bfd_get_config -{ - u32 client_index; - u32 context; -}; - -/** \brief Get BFD configuration response - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param slow_timer - slow timer (seconds) - @param min_tx - desired min tx interval - @param min_rx - desired min rx interval - @param detect_mult - desired detection multiplier -*/ -define bfd_get_config_reply -{ - u32 client_index; - u32 context; - u32 slow_timer; - u32 min_tx; - u32 min_rx; - u8 detect_mult; -}; - /** \brief Add UDP BFD session on interface @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/vnet/bfd/bfd_api.c b/src/vnet/bfd/bfd_api.c index 6632eae4c91..e64df48e11f 100644 --- a/src/vnet/bfd/bfd_api.c +++ b/src/vnet/bfd/bfd_api.c @@ -154,6 +154,21 @@ send_bfd_udp_session_details (unix_shared_memory_queue_t * q, u32 context, bfd_udp_key_t *key = &bus->key; mp->sw_if_index = clib_host_to_net_u32 (key->sw_if_index); mp->is_ipv6 = !(ip46_address_is_ip4 (&key->local_addr)); + if ((!bs->auth.is_delayed && bs->auth.curr_key) || + (bs->auth.is_delayed && bs->auth.next_key)) + { + mp->is_authenticated = 1; + } + if (bs->auth.is_delayed && bs->auth.next_key) + { + mp->bfd_key_id = bs->auth.next_bfd_key_id; + mp->conf_key_id = clib_host_to_net_u32 (bs->auth.next_key->conf_key_id); + } + else if (!bs->auth.is_delayed && bs->auth.curr_key) + { + mp->bfd_key_id = bs->auth.curr_bfd_key_id; + mp->conf_key_id = clib_host_to_net_u32 (bs->auth.curr_key->conf_key_id); + } if (mp->is_ipv6) { clib_memcpy (mp->local_addr, &key->local_addr, @@ -289,10 +304,9 @@ vl_api_bfd_udp_auth_activate_t_handler (vl_api_bfd_udp_auth_activate_t * mp) BFD_UDP_API_PARAM_COMMON_CODE; - rv = - bfd_udp_auth_activate (BFD_UDP_API_PARAM_FROM_MP (mp), - clib_net_to_host_u32 (mp->conf_key_id), - mp->bfd_key_id, mp->is_delayed); + rv = bfd_udp_auth_activate (BFD_UDP_API_PARAM_FROM_MP (mp), + clib_net_to_host_u32 (mp->conf_key_id), + mp->bfd_key_id, mp->is_delayed); BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_BFD_UDP_AUTH_ACTIVATE_REPLY); diff --git a/src/vnet/bfd/bfd_api.h b/src/vnet/bfd/bfd_api.h index 63d4a62e2e3..35ad3cda9ab 100644 --- a/src/vnet/bfd/bfd_api.h +++ b/src/vnet/bfd/bfd_api.h @@ -22,7 +22,6 @@ #include #include #include -#include #define foreach_bfd_transport(F) \ F (UDP4, "ip4-rewrite") \ diff --git a/src/vnet/bfd/bfd_cli.c b/src/vnet/bfd/bfd_cli.c new file mode 100644 index 00000000000..a3736d98b3c --- /dev/null +++ b/src/vnet/bfd/bfd_cli.c @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2011-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief BFD CLI implementation + */ + +#include +#include +#include +#include +#include +#include +#include + +static u8 * +format_bfd_session_cli (u8 * s, va_list * args) +{ + bfd_main_t *bm = va_arg (*args, bfd_main_t *); + bfd_session_t *bs = va_arg (*args, bfd_session_t *); + switch (bs->transport) + { + case BFD_TRANSPORT_UDP4: + s = format (s, "%=10u %-32s %20U %20U\n", bs->bs_idx, "IPv4 address", + format_ip4_address, bs->udp.key.local_addr.ip4.as_u8, + format_ip4_address, bs->udp.key.peer_addr.ip4.as_u8); + break; + case BFD_TRANSPORT_UDP6: + s = format (s, "%=10u %-32s %20U %20U\n", bs->bs_idx, "IPv6 address", + format_ip6_address, &bs->udp.key.local_addr.ip6, + format_ip6_address, &bs->udp.key.peer_addr.ip6); + break; + } + s = format (s, "%10s %-32s %20s %20s\n", "", "Session state", + bfd_state_string (bs->local_state), + bfd_state_string (bs->remote_state)); + s = format (s, "%10s %-32s %20s %20s\n", "", "Diagnostic code", + bfd_diag_code_string (bs->local_diag), + bfd_diag_code_string (bs->remote_diag)); + s = format (s, "%10s %-32s %20u %20u\n", "", "Detect multiplier", + bs->local_detect_mult, bs->remote_detect_mult); + s = format (s, "%10s %-32s %20u %20u\n", "", + "Required Min Rx Interval (usec)", + bs->config_required_min_rx_usec, bs->remote_min_rx_usec); + s = format (s, "%10s %-32s %20u %20u\n", "", + "Desired Min Tx Interval (usec)", + bs->config_desired_min_tx_usec, bfd_clocks_to_usec (bm, + bs->remote_desired_min_tx_clocks)); + s = + format (s, "%10s %-32s %20u\n", "", "Transmit interval", + bfd_clocks_to_usec (bm, bs->transmit_interval_clocks)); + s = + format (s, "%10s %-32s %20s %20s\n", "", "Demand mode", "no", + bs->remote_demand ? "yes" : "no"); + s = + format (s, "%10s %-32s %20s\n", "", "Poll state", + bfd_poll_state_string (bs->poll_state)); + if (bs->auth.curr_key) + { + s = format (s, "%10s %-32s %20u\n", "", "Authentication config key ID", + bs->auth.curr_key->conf_key_id); + s = format (s, "%10s %-32s %20u\n", "", "Authentication BFD key ID", + bs->auth.curr_bfd_key_id); + } + return s; +} + +static clib_error_t * +show_bfd (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + bfd_main_t *bm = &bfd_main; + bfd_session_t *bs = NULL; + + if (unformat (input, "keys")) + { + bfd_auth_key_t *key = NULL; + u8 *s = format (NULL, "%=10s %=25s %=10s\n", "Configuration Key ID", + "Type", "Use Count"); + /* *INDENT-OFF* */ + pool_foreach (key, bm->auth_keys, { + s = format (s, "%10u %-25s %10u\n", key->conf_key_id, + bfd_auth_type_str (key->auth_type), key->use_count); + }); + /* *INDENT-ON* */ + vlib_cli_output (vm, "%v\n", s); + vlib_cli_output (vm, "Number of configured BFD keys: %lu\n", + (u64) pool_elts (bm->auth_keys)); + } + else if (unformat (input, "sessions")) + { + u8 *s = format (NULL, "%=10s %=32s %=20s %=20s\n", "Index", "Property", + "Local value", "Remote value"); + /* *INDENT-OFF* */ + pool_foreach (bs, bm->sessions, + { s = format (s, "%U", format_bfd_session_cli, bm, bs); }); + /* *INDENT-ON* */ + vlib_cli_output (vm, "%v", s); + vec_free (s); + vlib_cli_output (vm, "Number of configured BFD sessions: %lu\n", + (u64) pool_elts (bm->sessions)); + } + else if (unformat (input, "echo-source")) + { + int is_set; + u32 sw_if_index; + int have_usable_ip4; + ip4_address_t ip4; + int have_usable_ip6; + ip6_address_t ip6; + bfd_udp_get_echo_source (&is_set, &sw_if_index, &have_usable_ip4, &ip4, + &have_usable_ip6, &ip6); + if (is_set) + { + vnet_sw_interface_t *sw_if = + vnet_get_sw_interface_safe (&vnet_main, sw_if_index); + vnet_hw_interface_t *hw_if = + vnet_get_hw_interface (&vnet_main, sw_if->hw_if_index); + u8 *s = format (NULL, "UDP echo source is: %v\n", hw_if->name); + s = format (s, "IPv4 address usable as echo source: "); + if (have_usable_ip4) + { + s = format (s, "%U\n", format_ip4_address, &ip4); + } + else + { + s = format (s, "none\n"); + } + s = format (s, "IPv6 address usable as echo source: "); + if (have_usable_ip6) + { + s = format (s, "%U\n", format_ip6_address, &ip6); + } + else + { + s = format (s, "none\n"); + } + vlib_cli_output (vm, "%v", s); + vec_free (s); + } + else + { + vlib_cli_output (vm, "UDP echo source is not set.\n"); + } + } + else + { + vlib_cli_output (vm, "Number of configured BFD sessions: %lu\n", + (u64) pool_elts (bm->sessions)); + vlib_cli_output (vm, "Number of configured BFD keys: %lu\n", + (u64) pool_elts (bm->auth_keys)); + } + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_bfd_command, static) = { + .path = "show bfd", + .short_help = "show bfd [keys|sessions|echo-source]", + .function = show_bfd, +}; +/* *INDENT-ON* */ + +static u8 * +format_vnet_api_errno (u8 * s, va_list * args) +{ + vnet_api_error_t api_error = va_arg (*args, vnet_api_error_t); +#define _(a, b, c) \ + case b: \ + s = format (s, "%s", c); \ + break; + switch (api_error) + { + foreach_vnet_api_error default:s = format (s, "UNKNOWN"); + break; + } + return s; +} + +static clib_error_t * +bfd_cli_key_add (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; + int have_key_id = 0; + u32 key_id = 0; + u8 *vec_auth_type = NULL; + bfd_auth_type_e auth_type = BFD_AUTH_TYPE_reserved; + u8 *secret = NULL; + static const u8 keyed_sha1[] = "keyed-sha1"; + static const u8 meticulous_keyed_sha1[] = "meticulous-keyed-sha1"; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "conf-key-id %u", &key_id)) + { + have_key_id = 1; + } + else if (unformat (input, "type %U", unformat_token, "a-zA-Z0-9-", + &vec_auth_type)) + { + if (vec_len (vec_auth_type) == sizeof (keyed_sha1) - 1 && + 0 == memcmp (vec_auth_type, keyed_sha1, + sizeof (keyed_sha1) - 1)) + { + auth_type = BFD_AUTH_TYPE_keyed_sha1; + } + else if (vec_len (vec_auth_type) == + sizeof (meticulous_keyed_sha1) - 1 && + 0 == memcmp (vec_auth_type, meticulous_keyed_sha1, + sizeof (meticulous_keyed_sha1) - 1)) + { + auth_type = BFD_AUTH_TYPE_meticulous_keyed_sha1; + } + else + { + ret = clib_error_return (0, "invalid type `%v'", vec_auth_type); + goto out; + } + } + else if (unformat (input, "secret %U", unformat_hex_string, &secret)) + { + /* nothing to do here */ + } + else + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + if (!have_key_id) + { + ret = + clib_error_return (0, "required parameter missing: `conf-key-id'"); + goto out; + } + if (!vec_auth_type) + { + ret = clib_error_return (0, "required parameter missing: `type'"); + goto out; + } + if (!secret) + { + ret = clib_error_return (0, "required parameter missing: `secret'"); + goto out; + } + + vnet_api_error_t rv = + bfd_auth_set_key (key_id, auth_type, vec_len (secret), secret); + if (rv) + { + ret = + clib_error_return (0, "`bfd_auth_set_key' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + } + +out: + vec_free (vec_auth_type); + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_key_add_command, static) = { + .path = "bfd key set", + .short_help = "bfd key set" + " conf-key-id " + " type " + " secret ", + .function = bfd_cli_key_add, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_key_del (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; + u32 key_id = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (!unformat (input, "conf-key-id %u", &key_id)) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + vnet_api_error_t rv = bfd_auth_del_key (key_id); + if (rv) + { + ret = + clib_error_return (0, "`bfd_auth_del_key' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_key_del_command, static) = { + .path = "bfd key del", + .short_help = "bfd key del conf-key-id ", + .function = bfd_cli_key_del, +}; +/* *INDENT-ON* */ + +#define INTERFACE_STR "interface" +#define LOCAL_ADDR_STR "local-addr" +#define PEER_ADDR_STR "peer-addr" +#define CONF_KEY_ID_STR "conf-key-id" +#define BFD_KEY_ID_STR "bfd-key-id" +#define DESIRED_MIN_TX_STR "desired-min-tx" +#define REQUIRED_MIN_RX_STR "required-min-rx" +#define DETECT_MULT_STR "detect-mult" +#define ADMIN_STR "admin" +#define DELAYED_STR "delayed" + +static const unsigned mandatory = 1; +static const unsigned optional = 0; + +#define DECLARE(t, n, s, r, ...) \ + int have_##n = 0; \ + t n; + +#define UNFORMAT(t, n, s, r, ...) \ + if (unformat (input, s " " __VA_ARGS__, &n)) \ + { \ + something_parsed = 1; \ + have_##n = 1; \ + } + +#define CHECK_MANDATORY(t, n, s, r, ...) \ + if (mandatory == r && !have_##n) \ + { \ + ret = clib_error_return (0, "Required parameter `%s' missing.", n); \ + goto out; \ + } + +static clib_error_t * +bfd_cli_udp_session_add (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_session_add_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) \ + F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (u32, desired_min_tx, DESIRED_MIN_TX_STR, mandatory, "%u") \ + F (u32, required_min_rx, REQUIRED_MIN_RX_STR, mandatory, "%u") \ + F (u32, detect_mult, DETECT_MULT_STR, mandatory, "%u") \ + F (u32, conf_key_id, CONF_KEY_ID_STR, optional, "%u") \ + F (u32, bfd_key_id, BFD_KEY_ID_STR, optional, "%u") + + foreach_bfd_cli_udp_session_add_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_session_add_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_session_add_cli_param (CHECK_MANDATORY); + + if (1 == have_conf_key_id + have_bfd_key_id) + { + ret = clib_error_return (0, "Incompatible parameter combination, `%s' " + "and `%s' must be either both specified or none", + CONF_KEY_ID_STR, BFD_KEY_ID_STR); + goto out; + } + + if (detect_mult > 255) + { + ret = clib_error_return (0, "%s value `%u' out of range <1,255>", + DETECT_MULT_STR, detect_mult); + goto out; + } + + if (have_bfd_key_id && bfd_key_id > 255) + { + ret = clib_error_return (0, "%s value `%u' out of range <1,255>", + BFD_KEY_ID_STR, bfd_key_id); + goto out; + } + + vnet_api_error_t rv = + bfd_udp_add_session (sw_if_index, &local_addr, &peer_addr, desired_min_tx, + required_min_rx, + detect_mult, have_conf_key_id, conf_key_id, + bfd_key_id); + if (rv) + { + ret = + clib_error_return (0, + "`bfd_add_add_session' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_session_add_command, static) = { + .path = "bfd udp session add", + .short_help = "bfd udp session add" + " interface " + " local-addr " + " peer-addr " + " desired-min-tx " + " required-min-rx " + " detect-mult " + "[" + " conf-key-id " + " bfd-key-id " + "]", + .function = bfd_cli_udp_session_add, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_udp_session_mod (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_session_mod_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) \ + F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (u32, desired_min_tx, DESIRED_MIN_TX_STR, mandatory, "%u") \ + F (u32, required_min_rx, REQUIRED_MIN_RX_STR, mandatory, "%u") \ + F (u32, detect_mult, DETECT_MULT_STR, mandatory, "%u") + + foreach_bfd_cli_udp_session_mod_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_session_mod_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_session_mod_cli_param (CHECK_MANDATORY); + + if (detect_mult > 255) + { + ret = clib_error_return (0, "%s value `%u' out of range <1,255>", + DETECT_MULT_STR, detect_mult); + goto out; + } + + vnet_api_error_t rv = + bfd_udp_mod_session (sw_if_index, &local_addr, &peer_addr, + desired_min_tx, required_min_rx, detect_mult); + if (rv) + { + ret = + clib_error_return (0, + "`bfd_udp_mod_session' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_session_mod_command, static) = { + .path = "bfd udp session mod", + .short_help = "bfd udp session mod interface" + " local-addr" + " peer-addr" + " desired-min-tx" + " required-min-rx" + " detect-mult" + " ", + .function = bfd_cli_udp_session_mod, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_udp_session_del (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_session_del_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) \ + F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) + + foreach_bfd_cli_udp_session_del_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_session_del_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_session_del_cli_param (CHECK_MANDATORY); + + vnet_api_error_t rv = + bfd_udp_del_session (sw_if_index, &local_addr, &peer_addr); + if (rv) + { + ret = + clib_error_return (0, + "`bfd_udp_del_session' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_session_del_command, static) = { + .path = "bfd udp session del", + .short_help = "bfd udp session del interface" + " local-addr" + " peer-addr" + " ", + .function = bfd_cli_udp_session_del, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_udp_session_set_flags (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_session_set_flags_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) \ + F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (u8 *, admin_up_down_token, ADMIN_STR, mandatory, "%v", \ + &admin_up_down_token) + + foreach_bfd_cli_udp_session_set_flags_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_session_set_flags_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_session_set_flags_cli_param (CHECK_MANDATORY); + + u8 admin_up_down; + static const char up[] = "up"; + static const char down[] = "down"; + if (!memcmp (admin_up_down_token, up, sizeof (up) - 1)) + { + admin_up_down = 1; + } + else if (!memcmp (admin_up_down_token, down, sizeof (down) - 1)) + { + admin_up_down = 0; + } + else + { + ret = + clib_error_return (0, "Unrecognized value for `%s' parameter: `%v'", + ADMIN_STR, admin_up_down_token); + goto out; + } + vnet_api_error_t rv = bfd_udp_session_set_flags (sw_if_index, &local_addr, + &peer_addr, admin_up_down); + if (rv) + { + ret = + clib_error_return (0, + "`bfd_udp_session_set_flags' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_session_set_flags_command, static) = { + .path = "bfd udp session set-flags", + .short_help = "bfd udp session set-flags" + " interface " + " local-addr " + " peer-addr " + " admin ", + .function = bfd_cli_udp_session_set_flags, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_udp_session_auth_activate (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_session_auth_activate_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) \ + F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (u8 *, delayed_token, DELAYED_STR, optional, "%v") \ + F (u32, conf_key_id, CONF_KEY_ID_STR, mandatory, "%u") \ + F (u32, bfd_key_id, BFD_KEY_ID_STR, mandatory, "%u") + + foreach_bfd_cli_udp_session_auth_activate_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_session_auth_activate_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_session_auth_activate_cli_param (CHECK_MANDATORY); + + u8 is_delayed = 0; + if (have_delayed_token) + { + static const char yes[] = "yes"; + static const char no[] = "no"; + if (!memcmp (delayed_token, yes, sizeof (yes) - 1)) + { + is_delayed = 1; + } + else if (!memcmp (delayed_token, no, sizeof (no) - 1)) + { + is_delayed = 0; + } + else + { + ret = + clib_error_return (0, + "Unrecognized value for `%s' parameter: `%v'", + DELAYED_STR, delayed_token); + goto out; + } + } + + if (have_bfd_key_id && bfd_key_id > 255) + { + ret = clib_error_return (0, "%s value `%u' out of range <1,255>", + BFD_KEY_ID_STR, bfd_key_id); + goto out; + } + + vnet_api_error_t rv = + bfd_udp_auth_activate (sw_if_index, &local_addr, &peer_addr, conf_key_id, + bfd_key_id, is_delayed); + if (rv) + { + ret = + clib_error_return (0, + "`bfd_udp_auth_activate' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_session_auth_activate_command, static) = { + .path = "bfd udp session auth activate", + .short_help = "bfd udp session auth activate" + " interface " + " local-addr " + " peer-addr " + " conf-key-id " + " bfd-key-id " + " [ delayed ]", + .function = bfd_cli_udp_session_auth_activate, +}; + +static clib_error_t * +bfd_cli_udp_session_auth_deactivate (vlib_main_t *vm, unformat_input_t *input, + CLIB_UNUSED (vlib_cli_command_t *lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_session_auth_deactivate_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) \ + F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U", \ + unformat_ip46_address) \ + F (u8 *, delayed_token, DELAYED_STR, optional, "%v") + + foreach_bfd_cli_udp_session_auth_deactivate_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_session_auth_deactivate_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_session_auth_deactivate_cli_param (CHECK_MANDATORY); + + u8 is_delayed = 0; + if (have_delayed_token) + { + static const char yes[] = "yes"; + static const char no[] = "no"; + if (!memcmp (delayed_token, yes, sizeof (yes) - 1)) + { + is_delayed = 1; + } + else if (!memcmp (delayed_token, no, sizeof (no) - 1)) + { + is_delayed = 0; + } + else + { + ret = clib_error_return ( + 0, "Unrecognized value for `%s' parameter: `%v'", DELAYED_STR, + delayed_token); + goto out; + } + } + + vnet_api_error_t rv = bfd_udp_auth_deactivate (sw_if_index, &local_addr, + &peer_addr, is_delayed); + if (rv) + { + ret = clib_error_return ( + 0, "`bfd_udp_auth_deactivate' API call failed, rv=%d:%U", (int)rv, + format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_session_auth_deactivate_command, static) = { + .path = "bfd udp session auth deactivate", + .short_help = "bfd udp session auth deactivate" + " interface " + " local-addr " + " peer-addr " + "[ delayed ]", + .function = bfd_cli_udp_session_auth_deactivate, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_udp_set_echo_source (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + clib_error_t *ret = NULL; +#define foreach_bfd_cli_udp_set_echo_source_cli_param(F) \ + F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U", \ + unformat_vnet_sw_interface, &vnet_main) + + foreach_bfd_cli_udp_set_echo_source_cli_param (DECLARE); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + int something_parsed = 0; + foreach_bfd_cli_udp_set_echo_source_cli_param (UNFORMAT); + + if (!something_parsed) + { + ret = clib_error_return (0, "Unknown input `%U'", + format_unformat_error, input); + goto out; + } + } + + foreach_bfd_cli_udp_set_echo_source_cli_param (CHECK_MANDATORY); + + vnet_api_error_t rv = bfd_udp_set_echo_source (sw_if_index); + if (rv) + { + ret = + clib_error_return (0, + "`bfd_udp_set_echo_source' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + goto out; + } + +out: + return ret; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_set_echo_source_cmd, static) = { + .path = "bfd udp echo-source set", + .short_help = "bfd udp echo-source set interface ", + .function = bfd_cli_udp_set_echo_source, +}; +/* *INDENT-ON* */ + +static clib_error_t * +bfd_cli_udp_del_echo_source (vlib_main_t * vm, unformat_input_t * input, + CLIB_UNUSED (vlib_cli_command_t * lmd)) +{ + vnet_api_error_t rv = bfd_udp_del_echo_source (); + if (rv) + { + return clib_error_return (0, + "`bfd_udp_del_echo_source' API call failed, rv=%d:%U", + (int) rv, format_vnet_api_errno, rv); + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (bfd_cli_udp_del_echo_source_cmd, static) = { + .path = "bfd udp echo-source del", + .short_help = "bfd udp echo-source del", + .function = bfd_cli_udp_del_echo_source, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/bfd/bfd_main.c b/src/vnet/bfd/bfd_main.c index 29c40458bf5..01de637531d 100644 --- a/src/vnet/bfd/bfd_main.c +++ b/src/vnet/bfd/bfd_main.c @@ -55,7 +55,7 @@ bfd_usec_to_clocks (const bfd_main_t * bm, u64 us) return bm->cpu_cps * ((f64) us / USEC_PER_SECOND); } -static u32 +u32 bfd_clocks_to_usec (const bfd_main_t * bm, u64 clocks) { return (clocks / bm->cpu_cps) * USEC_PER_SECOND; @@ -70,7 +70,7 @@ static u32 bfd_node_index_by_transport[] = { #undef F }; -static u8 * +u8 * format_bfd_auth_key (u8 * s, va_list * args) { const bfd_auth_key_t *key = va_arg (*args, bfd_auth_key_t *); @@ -147,7 +147,7 @@ bfd_set_state (bfd_main_t * bm, bfd_session_t * bs, } } -static const char * +const char * bfd_poll_state_string (bfd_poll_state_e state) { switch (state) @@ -265,12 +265,16 @@ bfd_calc_next_echo_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now) static void bfd_recalc_detection_time (bfd_main_t * bm, bfd_session_t * bs) { - bs->detection_time_clocks = - bs->remote_detect_mult * clib_max (bs->effective_required_min_rx_clocks, - bs->remote_desired_min_tx_clocks); - BFD_DBG ("Recalculated detection time %lu clocks/%.2fs", - bs->detection_time_clocks, - bs->detection_time_clocks / bm->cpu_cps); + if (bs->local_state == BFD_STATE_init || bs->local_state == BFD_STATE_up) + { + bs->detection_time_clocks = + bs->remote_detect_mult * + clib_max (bs->effective_required_min_rx_clocks, + bs->remote_desired_min_tx_clocks); + BFD_DBG ("Recalculated detection time %lu clocks/%.2fs", + bs->detection_time_clocks, + bs->detection_time_clocks / bm->cpu_cps); + } } static void @@ -284,7 +288,8 @@ bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now, { rx_timeout = bs->last_rx_clocks + bs->detection_time_clocks; } - if (BFD_STATE_up != bs->local_state || !bs->remote_demand || + if (BFD_STATE_up != bs->local_state || + (!bs->remote_demand && bs->remote_min_rx_usec) || BFD_POLL_NOT_NEEDED != bs->poll_state) { tx_timeout = bs->tx_timeout_clocks; @@ -348,7 +353,7 @@ bfd_set_effective_desired_min_tx (bfd_main_t * bm, static void bfd_set_effective_required_min_rx (bfd_main_t * bm, - bfd_session_t * bs, u64 now, + bfd_session_t * bs, u64 required_min_rx_clocks) { bs->effective_required_min_rx_clocks = required_min_rx_clocks; @@ -393,44 +398,33 @@ void bfd_session_start (bfd_main_t * bm, bfd_session_t * bs) { BFD_DBG ("\nStarting session: %U", format_bfd_session, bs); + bfd_set_effective_required_min_rx (bm, bs, + bs->config_required_min_rx_clocks); bfd_recalc_tx_interval (bm, bs); vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index, BFD_EVENT_NEW_SESSION, bs->bs_idx); } -vnet_api_error_t -bfd_del_session (uword bs_idx) -{ - const bfd_main_t *bm = &bfd_main; - if (!pool_is_free_index (bm->sessions, bs_idx)) - { - bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx); - pool_put (bm->sessions, bs); - return 0; - } - else - { - BFD_ERR ("no such session"); - return VNET_API_ERROR_BFD_ENOENT; - } - return 0; -} - void bfd_session_set_flags (bfd_session_t * bs, u8 admin_up_down) { bfd_main_t *bm = &bfd_main; + u64 now = clib_cpu_time_now (); if (admin_up_down) { BFD_DBG ("Session set admin-up, bs-idx=%u", bs->bs_idx); bfd_set_state (bm, bs, BFD_STATE_down, 0); bfd_set_diag (bs, BFD_DIAG_CODE_no_diag); + bfd_calc_next_tx (bm, bs, now); + bfd_set_timer (bm, bs, now, 0); } else { BFD_DBG ("Session set admin-down, bs-idx=%u", bs->bs_idx); bfd_set_diag (bs, BFD_DIAG_CODE_admin_down); bfd_set_state (bm, bs, BFD_STATE_admin_down, 0); + bfd_calc_next_tx (bm, bs, now); + bfd_set_timer (bm, bs, now, 0); } } @@ -515,7 +509,7 @@ bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now, clib_max (bs->config_desired_min_tx_clocks, bm->default_desired_min_tx_clocks)); - bfd_set_effective_required_min_rx (bm, bs, now, + bfd_set_effective_required_min_rx (bm, bs, bs->config_required_min_rx_clocks); bfd_set_timer (bm, bs, now, handling_wakeup); break; @@ -525,7 +519,7 @@ bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now, clib_max (bs->config_desired_min_tx_clocks, bm->default_desired_min_tx_clocks)); - bfd_set_effective_required_min_rx (bm, bs, now, + bfd_set_effective_required_min_rx (bm, bs, bs->config_required_min_rx_clocks); bfd_set_timer (bm, bs, now, handling_wakeup); break; @@ -540,7 +534,7 @@ bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now, bs->config_desired_min_tx_clocks); if (BFD_POLL_NOT_NEEDED == bs->poll_state) { - bfd_set_effective_required_min_rx (bm, bs, now, + bfd_set_effective_required_min_rx (bm, bs, bs->config_required_min_rx_clocks); } bfd_set_timer (bm, bs, now, handling_wakeup); @@ -932,7 +926,7 @@ bfd_on_timeout (vlib_main_t * vm, vlib_node_runtime_t * rt, bfd_main_t * bm, bs->echo = 1; bs->echo_last_rx_clocks = now; bs->echo_tx_timeout_clocks = now; - bfd_set_effective_required_min_rx (bm, bs, now, + bfd_set_effective_required_min_rx (bm, bs, clib_max (bm->min_required_min_rx_while_echo_clocks, bs->config_required_min_rx_clocks)); @@ -1569,6 +1563,7 @@ bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx) bs->remote_discr = pkt->my_disc; bs->remote_state = bfd_pkt_get_state (pkt); bs->remote_demand = bfd_pkt_get_demand (pkt); + bs->remote_diag = bfd_pkt_get_diag_code (pkt); u64 now = clib_cpu_time_now (); bs->last_rx_clocks = now; if (bfd_pkt_get_auth_present (pkt)) @@ -1621,7 +1616,7 @@ bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx) bfd_set_poll_state (bs, BFD_POLL_NOT_NEEDED); if (BFD_STATE_up == bs->local_state) { - bfd_set_effective_required_min_rx (bm, bs, now, + bfd_set_effective_required_min_rx (bm, bs, clib_max (bs->echo * bm->min_required_min_rx_while_echo_clocks, bs->config_required_min_rx_clocks)); @@ -1914,6 +1909,97 @@ bfd_session_set_params (bfd_main_t * bm, bfd_session_t * bs, return 0; } +vnet_api_error_t +bfd_auth_set_key (u32 conf_key_id, u8 auth_type, u8 key_len, + const u8 * key_data) +{ +#if WITH_LIBSSL > 0 + bfd_auth_key_t *auth_key = NULL; + if (!key_len || key_len > bfd_max_len_for_auth_type (auth_type)) + { + clib_warning ("Invalid authentication key length for auth_type=%d:%s " + "(key_len=%u, must be " + "non-zero, expected max=%u)", + auth_type, bfd_auth_type_str (auth_type), key_len, + (u32) bfd_max_len_for_auth_type (auth_type)); + return VNET_API_ERROR_INVALID_VALUE; + } + if (!bfd_auth_type_supported (auth_type)) + { + clib_warning ("Unsupported auth type=%d:%s", auth_type, + bfd_auth_type_str (auth_type)); + return VNET_API_ERROR_BFD_NOTSUPP; + } + bfd_main_t *bm = &bfd_main; + uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id); + if (key_idx_p) + { + /* modifying existing key - must not be used */ + const uword key_idx = *key_idx_p; + auth_key = pool_elt_at_index (bm->auth_keys, key_idx); + if (auth_key->use_count > 0) + { + clib_warning ("Authentication key with conf ID %u in use by %u BFD " + "session(s) - cannot modify", + conf_key_id, auth_key->use_count); + return VNET_API_ERROR_BFD_EINUSE; + } + } + else + { + /* adding new key */ + pool_get (bm->auth_keys, auth_key); + auth_key->conf_key_id = conf_key_id; + hash_set (bm->auth_key_by_conf_key_id, conf_key_id, + auth_key - bm->auth_keys); + } + auth_key->auth_type = auth_type; + memset (auth_key->key, 0, sizeof (auth_key->key)); + clib_memcpy (auth_key->key, key_data, key_len); + return 0; +#else + clib_warning ("SSL missing, cannot manipulate authentication keys"); + return VNET_API_ERROR_BFD_NOTSUPP; +#endif +} + +vnet_api_error_t +bfd_auth_del_key (u32 conf_key_id) +{ +#if WITH_LIBSSL > 0 + bfd_auth_key_t *auth_key = NULL; + bfd_main_t *bm = &bfd_main; + uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id); + if (key_idx_p) + { + /* deleting existing key - must not be used */ + const uword key_idx = *key_idx_p; + auth_key = pool_elt_at_index (bm->auth_keys, key_idx); + if (auth_key->use_count > 0) + { + clib_warning ("Authentication key with conf ID %u in use by %u BFD " + "session(s) - cannot delete", + conf_key_id, auth_key->use_count); + return VNET_API_ERROR_BFD_EINUSE; + } + hash_unset (bm->auth_key_by_conf_key_id, conf_key_id); + memset (auth_key, 0, sizeof (*auth_key)); + pool_put (bm->auth_keys, auth_key); + } + else + { + /* no such key */ + clib_warning ("Authentication key with conf ID %u does not exist", + conf_key_id); + return VNET_API_ERROR_BFD_ENOENT; + } + return 0; +#else + clib_warning ("SSL missing, cannot manipulate authentication keys"); + return VNET_API_ERROR_BFD_NOTSUPP; +#endif +} + bfd_main_t bfd_main; /* diff --git a/src/vnet/bfd/bfd_main.h b/src/vnet/bfd/bfd_main.h index d8063f9de33..3be3694c0f5 100644 --- a/src/vnet/bfd/bfd_main.h +++ b/src/vnet/bfd/bfd_main.h @@ -74,11 +74,14 @@ typedef struct bfd_session_s /* session state */ bfd_state_e local_state; + /* remote session state */ + bfd_state_e remote_state; + /* local diagnostics */ bfd_diag_code_e local_diag; - /* remote session state */ - bfd_state_e remote_state; + /* remote diagnostics */ + bfd_diag_code_e remote_diag; /* local discriminator */ u32 local_discr; @@ -315,6 +318,7 @@ void bfd_event (bfd_main_t * bm, bfd_session_t * bs); void bfd_init_final_control_frame (vlib_main_t * vm, vlib_buffer_t * b, bfd_main_t * bm, bfd_session_t * bs); u8 *format_bfd_session (u8 * s, va_list * args); +u8 *format_bfd_auth_key (u8 * s, va_list * args); void bfd_session_set_flags (bfd_session_t * bs, u8 admin_up_down); 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, @@ -325,6 +329,9 @@ vnet_api_error_t bfd_session_set_params (bfd_main_t * bm, bfd_session_t * bs, u32 required_min_rx_usec, u8 detect_mult); +u32 bfd_clocks_to_usec (const bfd_main_t * bm, u64 clocks); +const char *bfd_poll_state_string (bfd_poll_state_e state); + #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 92b226bda8b..5deb9702eb3 100644 --- a/src/vnet/bfd/bfd_protocol.c +++ b/src/vnet/bfd/bfd_protocol.c @@ -82,11 +82,13 @@ bfd_pkt_get_control_plane_independent (const bfd_pkt_t * pkt) return (pkt->head.sta_flags >> 3) & 1; } +#if 0 void bfd_pkt_set_control_plane_independent (bfd_pkt_t * pkt) { pkt->head.sta_flags |= 1 << 3; } +#endif u8 bfd_pkt_get_auth_present (const bfd_pkt_t * pkt) @@ -106,11 +108,13 @@ bfd_pkt_get_demand (const bfd_pkt_t * pkt) return (pkt->head.sta_flags >> 1) & 1; } +#if 0 void bfd_pkt_set_demand (bfd_pkt_t * pkt) { pkt->head.sta_flags |= 1 << 1; } +#endif u8 bfd_pkt_get_multipoint (const bfd_pkt_t * pkt) @@ -118,11 +122,13 @@ bfd_pkt_get_multipoint (const bfd_pkt_t * pkt) return (pkt->head.sta_flags >> 0) & 1; } +#if 0 void bfd_pkt_set_multipoint (bfd_pkt_t * pkt) { pkt->head.sta_flags |= 1 << 0; } +#endif u32 bfd_max_len_for_auth_type (bfd_auth_type_e auth_type) diff --git a/src/vnet/bfd/bfd_udp.c b/src/vnet/bfd/bfd_udp.c index cf05089b430..207f3b8cae9 100644 --- a/src/vnet/bfd/bfd_udp.c +++ b/src/vnet/bfd/bfd_udp.c @@ -60,8 +60,7 @@ vnet_api_error_t bfd_udp_set_echo_source (u32 sw_if_index) { vnet_sw_interface_t *sw_if = - vnet_get_sw_interface_safe (bfd_udp_main.vnet_main, - bfd_udp_main.echo_source_sw_if_index); + vnet_get_sw_interface_safe (bfd_udp_main.vnet_main, sw_if_index); if (sw_if) { bfd_udp_main.echo_source_sw_if_index = sw_if_index; @@ -84,6 +83,7 @@ bfd_udp_is_echo_available (bfd_transport_e transport) { if (!bfd_udp_main.echo_source_is_set) { + BFD_DBG ("UDP echo source not set - echo not available"); return 0; } /* @@ -127,6 +127,7 @@ bfd_udp_is_echo_available (bfd_transport_e transport) /* *INDENT-ON* */ } } + BFD_DBG ("No usable IP address for UDP echo - echo not available"); return 0; } @@ -144,11 +145,6 @@ bfd_udp_bs_idx_to_sport (u32 bs_idx) return 49152 + bs_idx % (65535 - 49152 + 1); } -static void -lol () -{ -} - int bfd_udp_get_echo_src_ip4 (ip4_address_t * addr) { @@ -204,7 +200,6 @@ bfd_udp_get_echo_src_ip6 (ip6_address_t * addr) { *addr = *x; addr->as_u8[15] ^= 1; /* flip the last bit of the address */ - lol (); return 1; } })); @@ -213,6 +208,24 @@ bfd_udp_get_echo_src_ip6 (ip6_address_t * addr) return 0; } +void +bfd_udp_get_echo_source (int *is_set, u32 * sw_if_index, int *have_usable_ip4, + ip4_address_t * ip4, int *have_usable_ip6, + ip6_address_t * ip6) +{ + if (bfd_udp_main.echo_source_is_set) + { + *is_set = 1; + *sw_if_index = bfd_udp_main.echo_source_sw_if_index; + *have_usable_ip4 = bfd_udp_get_echo_src_ip4 (ip4); + *have_usable_ip6 = bfd_udp_get_echo_src_ip6 (ip6); + } + else + { + *is_set = 0; + } +} + int bfd_add_udp4_transport (vlib_main_t * vm, vlib_buffer_t * b, const bfd_session_t * bs, int is_echo) @@ -660,97 +673,6 @@ bfd_udp_session_set_flags (u32 sw_if_index, return 0; } -vnet_api_error_t -bfd_auth_set_key (u32 conf_key_id, u8 auth_type, u8 key_len, - const u8 * key_data) -{ -#if WITH_LIBSSL > 0 - bfd_auth_key_t *auth_key = NULL; - if (!key_len || key_len > bfd_max_len_for_auth_type (auth_type)) - { - clib_warning ("Invalid authentication key length for auth_type=%d:%s " - "(key_len=%u, must be " - "non-zero, expected max=%u)", - auth_type, bfd_auth_type_str (auth_type), key_len, - (u32) bfd_max_len_for_auth_type (auth_type)); - return VNET_API_ERROR_INVALID_VALUE; - } - if (!bfd_auth_type_supported (auth_type)) - { - clib_warning ("Unsupported auth type=%d:%s", auth_type, - bfd_auth_type_str (auth_type)); - return VNET_API_ERROR_BFD_NOTSUPP; - } - bfd_main_t *bm = bfd_udp_main.bfd_main; - uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id); - if (key_idx_p) - { - /* modifying existing key - must not be used */ - const uword key_idx = *key_idx_p; - auth_key = pool_elt_at_index (bm->auth_keys, key_idx); - if (auth_key->use_count > 0) - { - clib_warning ("Authentication key with conf ID %u in use by %u BFD " - "sessions - cannot modify", - conf_key_id, auth_key->use_count); - return VNET_API_ERROR_BFD_EINUSE; - } - } - else - { - /* adding new key */ - pool_get (bm->auth_keys, auth_key); - auth_key->conf_key_id = conf_key_id; - hash_set (bm->auth_key_by_conf_key_id, conf_key_id, - auth_key - bm->auth_keys); - } - auth_key->auth_type = auth_type; - memset (auth_key->key, 0, sizeof (auth_key->key)); - clib_memcpy (auth_key->key, key_data, key_len); - return 0; -#else - clib_warning ("SSL missing, cannot manipulate authentication keys"); - return VNET_API_ERROR_BFD_NOTSUPP; -#endif -} - -vnet_api_error_t -bfd_auth_del_key (u32 conf_key_id) -{ -#if WITH_LIBSSL > 0 - bfd_auth_key_t *auth_key = NULL; - bfd_main_t *bm = bfd_udp_main.bfd_main; - uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id); - if (key_idx_p) - { - /* deleting existing key - must not be used */ - const uword key_idx = *key_idx_p; - auth_key = pool_elt_at_index (bm->auth_keys, key_idx); - if (auth_key->use_count > 0) - { - clib_warning ("Authentication key with conf ID %u in use by %u BFD " - "sessions - cannot delete", - conf_key_id, auth_key->use_count); - return VNET_API_ERROR_BFD_EINUSE; - } - hash_unset (bm->auth_key_by_conf_key_id, conf_key_id); - memset (auth_key, 0, sizeof (*auth_key)); - pool_put (bm->auth_keys, auth_key); - } - else - { - /* no such key */ - clib_warning ("Authentication key with conf ID %u does not exist", - conf_key_id); - return VNET_API_ERROR_BFD_ENOENT; - } - return 0; -#else - clib_warning ("SSL missing, cannot manipulate authentication keys"); - return VNET_API_ERROR_BFD_NOTSUPP; -#endif -} - vnet_api_error_t bfd_udp_auth_activate (u32 sw_if_index, const ip46_address_t * local_addr, diff --git a/src/vnet/bfd/bfd_udp.h b/src/vnet/bfd/bfd_udp.h index ce2ee3cbf48..e33b7407d56 100644 --- a/src/vnet/bfd/bfd_udp.h +++ b/src/vnet/bfd/bfd_udp.h @@ -62,6 +62,11 @@ int bfd_add_udp6_transport (vlib_main_t * vm, vlib_buffer_t * b, */ int bfd_udp_is_echo_available (bfd_transport_e transport); +void +bfd_udp_get_echo_source (int *is_set, u32 * sw_if_index, int *have_usable_ip4, + ip4_address_t * ip4, int *have_usable_ip6, + ip6_address_t * ip6); + #endif /* __included_bfd_udp_h__ */ /* diff --git a/test/bfd.py b/test/bfd.py index b467cc79eea..d1d948b323e 100644 --- a/test/bfd.py +++ b/test/bfd.py @@ -198,6 +198,10 @@ class VppBFDAuthKey(VppObject): """ key data """ return self._key + @key.setter + def key(self, value): + self._key = value + @property def conf_key_id(self): """ configuration key ID """ @@ -249,7 +253,10 @@ class VppBFDUDPSession(VppObject): self._required_min_rx = required_min_rx self._detect_mult = detect_mult self._sha1_key = sha1_key - self._bfd_key_id = bfd_key_id if bfd_key_id else randint(0, 255) + if bfd_key_id is not None: + self._bfd_key_id = bfd_key_id + else: + self._bfd_key_id = randint(0, 255) @property def test(self): diff --git a/test/test_bfd.py b/test/test_bfd.py index ce0cca55516..e7ebb214d9a 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -6,8 +6,9 @@ import unittest import hashlib import binascii import time +from struct import pack, unpack from random import randint, shuffle, getrandbits -from socket import AF_INET, AF_INET6 +from socket import AF_INET, AF_INET6, inet_ntop from scapy.packet import Raw from scapy.layers.l2 import Ether from scapy.layers.inet import UDP, IP @@ -17,6 +18,7 @@ from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \ from framework import VppTestCase, VppTestRunner from vpp_pg_interface import CaptureTimeoutError from util import ppp +from vpp_papi_provider import UnexpectedApiReturnValueError USEC_IN_SEC = 1000000 @@ -461,19 +463,25 @@ def bfd_session_up(test): test.vpp_clock_offset) if old_offset: test.assertAlmostEqual( - old_offset, test.vpp_clock_offset, delta=0.1, + old_offset, test.vpp_clock_offset, delta=0.5, msg="vpp clock offset not stable (new: %s, old: %s)" % (test.vpp_clock_offset, old_offset)) test.logger.info("BFD: Sending Init") test.test_session.update(my_discriminator=randint(0, 40000000), your_discriminator=p[BFD].my_discriminator, state=BFDState.init) + if test.test_session.sha1_key and test.test_session.sha1_key.auth_type == \ + BFDAuthType.meticulous_keyed_sha1: + test.test_session.inc_seq_num() test.test_session.send_packet() test.logger.info("BFD: Waiting for event") e = test.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(test, e, expected_state=BFDState.up) test.logger.info("BFD: Session is Up") test.test_session.update(state=BFDState.up) + if test.test_session.sha1_key and test.test_session.sha1_key.auth_type == \ + BFDAuthType.meticulous_keyed_sha1: + test.test_session.inc_seq_num() test.test_session.send_packet() test.assert_equal(test.vpp_session.state, BFDState.up, BFDState) @@ -482,6 +490,9 @@ def bfd_session_down(test): """ Bring BFD session down """ test.assert_equal(test.vpp_session.state, BFDState.up, BFDState) test.test_session.update(state=BFDState.down) + if test.test_session.sha1_key and test.test_session.sha1_key.auth_type == \ + BFDAuthType.meticulous_keyed_sha1: + test.test_session.inc_seq_num() test.test_session.send_packet() test.logger.info("BFD: Waiting for event") e = test.vapi.wait_for_event(1, "bfd_udp_session_details") @@ -490,6 +501,30 @@ def bfd_session_down(test): test.assert_equal(test.vpp_session.state, BFDState.down, BFDState) +def verify_bfd_session_config(test, session, state=None): + dump = session.get_bfd_udp_session_dump_entry() + test.assertIsNotNone(dump) + # since dump is not none, we have verified that sw_if_index and addresses + # are valid (in get_bfd_udp_session_dump_entry) + if state: + test.assert_equal(dump.state, state, "session state") + test.assert_equal(dump.required_min_rx, session.required_min_rx, + "required min rx interval") + test.assert_equal(dump.desired_min_tx, session.desired_min_tx, + "desired min tx interval") + test.assert_equal(dump.detect_mult, session.detect_mult, + "detect multiplier") + if session.sha1_key is None: + test.assert_equal(dump.is_authenticated, 0, "is_authenticated flag") + else: + test.assert_equal(dump.is_authenticated, 1, "is_authenticated flag") + test.assert_equal(dump.bfd_key_id, session.bfd_key_id, + "bfd key id") + test.assert_equal(dump.conf_key_id, + session.sha1_key.conf_key_id, + "config key id") + + def verify_ip(test, packet): """ Verify correctness of IP layer. """ if test.vpp_session.af == AF_INET6: @@ -626,6 +661,32 @@ class BFD4TestCase(VppTestCase): """ bring BFD session up """ bfd_session_up(self) + def test_session_up_by_ip(self): + """ bring BFD session up - first frame looked up by address pair """ + self.logger.info("BFD: Sending Slow control frame") + self.test_session.update(my_discriminator=randint(0, 40000000)) + self.test_session.send_packet() + self.pg0.enable_capture() + p = self.pg0.wait_for_packet(1) + self.assert_equal(p[BFD].your_discriminator, + self.test_session.my_discriminator, + "BFD - your discriminator") + self.assert_equal(p[BFD].state, BFDState.init, BFDState) + self.test_session.update(your_discriminator=p[BFD].my_discriminator, + state=BFDState.up) + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.init) + self.logger.info("BFD: Sending Up") + self.test_session.send_packet() + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.up) + self.logger.info("BFD: Session is Up") + self.test_session.update(state=BFDState.up) + self.test_session.send_packet() + self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) + def test_session_down(self): """ bring BFD session down """ bfd_session_up(self) @@ -905,6 +966,16 @@ class BFD4TestCase(VppTestCase): self.assertNotIn("P", p.sprintf("%BFD.flags%"), "Poll bit set in BFD packet") + def test_poll_response(self): + """ test correct response to control frame with poll bit set """ + bfd_session_up(self) + poll = self.test_session.create_packet() + poll[BFD].flags = "P" + self.test_session.send_packet(poll) + final = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assertIn("F", final.sprintf("%BFD.flags%")) + def test_no_periodic_if_remote_demand(self): """ no periodic frames outside poll sequence if remote demand set """ bfd_session_up(self) @@ -1091,6 +1162,36 @@ class BFD4TestCase(VppTestCase): events = self.vapi.collect_events() self.assert_equal(len(events), 0, "number of bfd events") + def test_echo_source_removed(self): + """ echo function stops if echo source is removed """ + bfd_session_up(self) + self.test_session.update(required_min_echo_rx=50000) + self.test_session.send_packet() + self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + # wait for first echo packet + while True: + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + if p[UDP].dport == BFD.udp_dport_echo: + self.logger.debug(ppp("Looping back packet:", p)) + self.pg0.add_stream(p) + self.pg_start() + break + elif p.haslayer(BFD): + # ignore BFD + pass + else: + raise Exception(ppp("Received unknown packet:", p)) + self.vapi.bfd_udp_del_echo_source() + self.test_session.send_packet() + # echo packets shouldn't arrive anymore + for dummy in range(5): + wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.test_session.send_packet() + events = self.vapi.collect_events() + self.assert_equal(len(events), 0, "number of bfd events") + def test_stale_echo(self): """ stale echo packets don't keep a session up """ bfd_session_up(self) @@ -1199,28 +1300,31 @@ class BFD4TestCase(VppTestCase): verify_event(self, e, expected_state=BFDState.admin_down) for dummy in range(2): p = wait_for_bfd_packet(self) - self.assert_equal(BFDState.admin_down, p[BFD].state, BFDState) + self.assert_equal(p[BFD].state, BFDState.admin_down, BFDState) # try to bring session up - shouldn't be possible self.test_session.update(state=BFDState.init) self.test_session.send_packet() for dummy in range(2): p = wait_for_bfd_packet(self) - self.assert_equal(BFDState.admin_down, p[BFD].state, BFDState) + self.assert_equal(p[BFD].state, BFDState.admin_down, BFDState) self.vpp_session.admin_up() self.test_session.update(state=BFDState.down) e = self.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(self, e, expected_state=BFDState.down) - p = wait_for_bfd_packet(self) - self.assert_equal(BFDState.down, p[BFD].state, BFDState) + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assert_equal(p[BFD].state, BFDState.down, BFDState) self.test_session.send_packet() - p = wait_for_bfd_packet(self) - self.assert_equal(BFDState.init, p[BFD].state, BFDState) + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assert_equal(p[BFD].state, BFDState.init, BFDState) e = self.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(self, e, expected_state=BFDState.init) self.test_session.update(state=BFDState.up) self.test_session.send_packet() - p = wait_for_bfd_packet(self) - self.assert_equal(BFDState.up, p[BFD].state, BFDState) + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) e = self.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(self, e, expected_state=BFDState.up) @@ -1232,7 +1336,8 @@ class BFD4TestCase(VppTestCase): self.test_session.send_packet(demand) self.vpp_session.modify_parameters( required_min_rx=2 * self.vpp_session.required_min_rx) - p = wait_for_bfd_packet(self) + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) # poll bit must be set self.assertIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set") # terminate poll sequence @@ -1314,6 +1419,32 @@ class BFD6TestCase(VppTestCase): """ bring BFD session up """ bfd_session_up(self) + def test_session_up_by_ip(self): + """ bring BFD session up - first frame looked up by address pair """ + self.logger.info("BFD: Sending Slow control frame") + self.test_session.update(my_discriminator=randint(0, 40000000)) + self.test_session.send_packet() + self.pg0.enable_capture() + p = self.pg0.wait_for_packet(1) + self.assert_equal(p[BFD].your_discriminator, + self.test_session.my_discriminator, + "BFD - your discriminator") + self.assert_equal(p[BFD].state, BFDState.init, BFDState) + self.test_session.update(your_discriminator=p[BFD].my_discriminator, + state=BFDState.up) + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.init) + self.logger.info("BFD: Sending Up") + self.test_session.send_packet() + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.up) + self.logger.info("BFD: Session is Up") + self.test_session.update(state=BFDState.up) + self.test_session.send_packet() + self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) + def test_hold_up(self): """ hold BFD session up """ bfd_session_up(self) @@ -1512,7 +1643,6 @@ class BFDSHA1TestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) @@ -1547,7 +1677,6 @@ class BFDSHA1TestCase(VppTestCase): self.vpp_session = vpp_bfd_udp_session self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = legitimate_test_session # bring vpp session up bfd_session_up(self) @@ -1625,7 +1754,6 @@ class BFDSHA1TestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id, our_seq_number=0) @@ -1633,7 +1761,7 @@ class BFDSHA1TestCase(VppTestCase): # don't send any packets for 2*detection_time detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC - self.sleep(detection_time, "simulating peer restart") + self.sleep(2*detection_time, "simulating peer restart") events = self.vapi.collect_events() self.assert_equal(len(events), 1, "number of bfd events") verify_event(self, events[0], expected_state=BFDState.down) @@ -1685,7 +1813,6 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) bfd_session_up(self) for dummy in range(self.test_session.detect_mult * 2): @@ -1710,7 +1837,6 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) @@ -1742,7 +1868,6 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key1) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key1, bfd_key_id=self.vpp_session.bfd_key_id) @@ -1769,7 +1894,6 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) bfd_session_up(self) for dummy in range(self.test_session.detect_mult * 2): @@ -1798,7 +1922,6 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) @@ -1857,5 +1980,421 @@ class BFDAuthOnOffTestCase(VppTestCase): self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") + +class BFDCLITestCase(VppTestCase): + """Bidirectional Forwarding Detection (BFD) (CLI) """ + pg0 = None + + @classmethod + def setUpClass(cls): + super(BFDCLITestCase, cls).setUpClass() + + try: + cls.create_pg_interfaces((0,)) + cls.pg0.config_ip4() + cls.pg0.config_ip6() + cls.pg0.resolve_arp() + cls.pg0.resolve_ndp() + + except Exception: + super(BFDCLITestCase, cls).tearDownClass() + raise + + def setUp(self): + super(BFDCLITestCase, self).setUp() + self.factory = AuthKeyFactory() + self.pg0.enable_capture() + + def tearDown(self): + try: + self.vapi.want_bfd_events(enable_disable=0) + except UnexpectedApiReturnValueError: + # some tests aren't subscribed, so this is not an issue + pass + self.vapi.collect_events() # clear the event queue + super(BFDCLITestCase, self).tearDown() + + def cli_verify_no_response(self, cli): + """ execute a CLI, asserting that the response is empty """ + self.assert_equal(self.vapi.cli(cli), + "", + "CLI command response") + + def cli_verify_response(self, cli, expected): + """ execute a CLI, asserting that the response matches expectation """ + self.assert_equal(self.vapi.cli(cli).strip(), + expected, + "CLI command response") + + def test_show(self): + """ show commands """ + k1 = self.factory.create_random_key(self) + k1.add_vpp_config() + k2 = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + k2.add_vpp_config() + s1 = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + s1.add_vpp_config() + s2 = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, + sha1_key=k2) + s2.add_vpp_config() + self.logger.info(self.vapi.ppcli("show bfd keys")) + self.logger.info(self.vapi.ppcli("show bfd sessions")) + self.logger.info(self.vapi.ppcli("show bfd")) + + def test_set_del_sha1_key(self): + """ set/delete SHA1 auth key """ + k = self.factory.create_random_key(self) + self.registry.register(k, self.logger) + self.cli_verify_no_response( + "bfd key set conf-key-id %s type keyed-sha1 secret %s" % + (k.conf_key_id, + "".join("{:02x}".format(ord(c)) for c in k.key))) + self.assertTrue(k.query_vpp_config()) + self.vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, sha1_key=k) + self.vpp_session.add_vpp_config() + self.test_session = \ + BFDTestSession(self, self.pg0, AF_INET, sha1_key=k, + bfd_key_id=self.vpp_session.bfd_key_id) + self.vapi.want_bfd_events() + bfd_session_up(self) + bfd_session_down(self) + # try to replace the secret for the key - should fail because the key + # is in-use + k2 = self.factory.create_random_key(self) + self.cli_verify_response( + "bfd key set conf-key-id %s type keyed-sha1 secret %s" % + (k.conf_key_id, + "".join("{:02x}".format(ord(c)) for c in k2.key)), + "bfd key set: `bfd_auth_set_key' API call failed, " + "rv=-103:BFD object in use") + # manipulating the session using old secret should still work + bfd_session_up(self) + bfd_session_down(self) + self.vpp_session.remove_vpp_config() + self.cli_verify_no_response( + "bfd key del conf-key-id %s" % k.conf_key_id) + self.assertFalse(k.query_vpp_config()) + + def test_set_del_meticulous_sha1_key(self): + """ set/delete meticulous SHA1 auth key """ + k = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + self.registry.register(k, self.logger) + self.cli_verify_no_response( + "bfd key set conf-key-id %s type meticulous-keyed-sha1 secret %s" % + (k.conf_key_id, + "".join("{:02x}".format(ord(c)) for c in k.key))) + self.assertTrue(k.query_vpp_config()) + self.vpp_session = VppBFDUDPSession(self, self.pg0, + self.pg0.remote_ip6, af=AF_INET6, + sha1_key=k) + self.vpp_session.add_vpp_config() + self.vpp_session.admin_up() + self.test_session = \ + BFDTestSession(self, self.pg0, AF_INET6, sha1_key=k, + bfd_key_id=self.vpp_session.bfd_key_id) + self.vapi.want_bfd_events() + bfd_session_up(self) + bfd_session_down(self) + # try to replace the secret for the key - should fail because the key + # is in-use + k2 = self.factory.create_random_key(self) + self.cli_verify_response( + "bfd key set conf-key-id %s type keyed-sha1 secret %s" % + (k.conf_key_id, + "".join("{:02x}".format(ord(c)) for c in k2.key)), + "bfd key set: `bfd_auth_set_key' API call failed, " + "rv=-103:BFD object in use") + # manipulating the session using old secret should still work + bfd_session_up(self) + bfd_session_down(self) + self.vpp_session.remove_vpp_config() + self.cli_verify_no_response( + "bfd key del conf-key-id %s" % k.conf_key_id) + self.assertFalse(k.query_vpp_config()) + + def test_add_mod_del_bfd_udp(self): + """ create/modify/delete IPv4 BFD UDP session """ + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s" % (self.pg0.name, self.pg0.local_ip4, + self.pg0.remote_ip4, + vpp_session.desired_min_tx, + vpp_session.required_min_rx, + vpp_session.detect_mult) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + mod_session.desired_min_tx, mod_session.required_min_rx, + mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_add_mod_del_bfd_udp6(self): + """ create/modify/delete IPv6 BFD UDP session """ + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s" % (self.pg0.name, self.pg0.local_ip6, + self.pg0.remote_ip6, + vpp_session.desired_min_tx, + vpp_session.required_min_rx, + vpp_session.detect_mult) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip6, self.pg0.remote_ip6, + mod_session.desired_min_tx, + mod_session.required_min_rx, mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip6, self.pg0.remote_ip6) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, + "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_add_mod_del_bfd_udp_auth(self): + """ create/modify/delete IPv4 BFD UDP session (authenticated) """ + key = self.factory.create_random_key(self) + key.add_vpp_config() + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, sha1_key=key) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s conf-key-id %s bfd-key-id %s"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + vpp_session.desired_min_tx, vpp_session.required_min_rx, + vpp_session.detect_mult, key.conf_key_id, + vpp_session.bfd_key_id) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, sha1_key=key, + bfd_key_id=vpp_session.bfd_key_id, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + mod_session.desired_min_tx, + mod_session.required_min_rx, mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, + "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_add_mod_del_bfd_udp6_auth(self): + """ create/modify/delete IPv6 BFD UDP session (authenticated) """ + key = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + key.add_vpp_config() + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, sha1_key=key) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s conf-key-id %s bfd-key-id %s" \ + % (self.pg0.name, self.pg0.local_ip6, self.pg0.remote_ip6, + vpp_session.desired_min_tx, vpp_session.required_min_rx, + vpp_session.detect_mult, key.conf_key_id, + vpp_session.bfd_key_id) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, sha1_key=key, + bfd_key_id=vpp_session.bfd_key_id, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip6, self.pg0.remote_ip6, + mod_session.desired_min_tx, + mod_session.required_min_rx, mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip6, self.pg0.remote_ip6) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, + "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_auth_on_off(self): + """ turn authentication on and off """ + key = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + key.add_vpp_config() + session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + auth_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, + sha1_key=key) + session.add_vpp_config() + cli_activate = \ + "bfd udp session auth activate interface %s local-addr %s "\ + "peer-addr %s conf-key-id %s bfd-key-id %s"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + key.conf_key_id, auth_session.bfd_key_id) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + cli_deactivate = \ + "bfd udp session auth deactivate interface %s local-addr %s "\ + "peer-addr %s "\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + + def test_auth_on_off_delayed(self): + """ turn authentication on and off (delayed) """ + key = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + key.add_vpp_config() + session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + auth_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, + sha1_key=key) + session.add_vpp_config() + cli_activate = \ + "bfd udp session auth activate interface %s local-addr %s "\ + "peer-addr %s conf-key-id %s bfd-key-id %s delayed yes"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + key.conf_key_id, auth_session.bfd_key_id) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + cli_deactivate = \ + "bfd udp session auth deactivate interface %s local-addr %s "\ + "peer-addr %s delayed yes"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + + def test_admin_up_down(self): + """ put session admin-up and admin-down """ + session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + session.add_vpp_config() + cli_down = \ + "bfd udp session set-flags admin down interface %s local-addr %s "\ + "peer-addr %s "\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + cli_up = \ + "bfd udp session set-flags admin up interface %s local-addr %s "\ + "peer-addr %s "\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_down) + verify_bfd_session_config(self, session, state=BFDState.admin_down) + self.cli_verify_no_response(cli_up) + verify_bfd_session_config(self, session, state=BFDState.down) + + def test_set_del_udp_echo_source(self): + """ set/del udp echo source """ + self.create_loopback_interfaces([0]) + self.loopback0 = self.lo_interfaces[0] + self.loopback0.admin_up() + self.cli_verify_response("show bfd echo-source", + "UDP echo source is not set.") + cli_set = "bfd udp echo-source set interface %s" % self.loopback0.name + self.cli_verify_no_response(cli_set) + self.cli_verify_response("show bfd echo-source", + "UDP echo source is: %s\n" + "IPv4 address usable as echo source: none\n" + "IPv6 address usable as echo source: none" % + self.loopback0.name) + self.loopback0.config_ip4() + unpacked = unpack("!L", self.loopback0.local_ip4n) + echo_ip4 = inet_ntop(AF_INET, pack("!L", unpacked[0] ^ 1)) + self.cli_verify_response("show bfd echo-source", + "UDP echo source is: %s\n" + "IPv4 address usable as echo source: %s\n" + "IPv6 address usable as echo source: none" % + (self.loopback0.name, echo_ip4)) + unpacked = unpack("!LLLL", self.loopback0.local_ip6n) + echo_ip6 = inet_ntop(AF_INET6, pack("!LLLL", unpacked[0], unpacked[1], + unpacked[2], unpacked[3] ^ 1)) + self.loopback0.config_ip6() + self.cli_verify_response("show bfd echo-source", + "UDP echo source is: %s\n" + "IPv4 address usable as echo source: %s\n" + "IPv6 address usable as echo source: %s" % + (self.loopback0.name, echo_ip4, echo_ip6)) + cli_del = "bfd udp echo-source del" + self.cli_verify_no_response(cli_del) + self.cli_verify_response("show bfd echo-source", + "UDP echo source is not set.") + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_object.py b/test/vpp_object.py index 0d74baa53ab..61a96ec2993 100644 --- a/test/vpp_object.py +++ b/test/vpp_object.py @@ -46,6 +46,7 @@ class VppObjectRegistry(object): if obj.object_id() not in self._object_dict: self._object_registry.append(obj) self._object_dict[obj.object_id()] = obj + logger.debug("REG: registering %s" % obj) else: logger.debug("REG: duplicate add, ignoring (%s)" % obj) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 67b3e141447..bebbe76dfd6 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -29,6 +29,11 @@ class L2_VTR_OP: L2_POP_1 = 3 +class UnexpectedApiReturnValueError(Exception): + """ exception raised when the API return value is unexpected """ + pass + + class VppPapiProvider(object): """VPP-api provider using vpp-papi @@ -144,13 +149,13 @@ class VppPapiProvider(object): "return value instead of %d in %s" % \ (reply.retval, repr(reply)) self.test_class.logger.info(msg) - raise Exception(msg) + raise UnexpectedApiReturnValueError(msg) elif self._expect_api_retval == self._zero: if hasattr(reply, 'retval') and reply.retval != expected_retval: msg = "API call failed, expected zero return value instead "\ "of %d in %s" % (expected_retval, repr(reply)) self.test_class.logger.info(msg) - raise Exception(msg) + raise UnexpectedApiReturnValueError(msg) else: raise Exception("Internal error, unexpected value for " "self._expect_api_retval %s" % @@ -1188,6 +1193,9 @@ class VppPapiProvider(object): return self.api(self.papi.bfd_udp_set_echo_source, {'sw_if_index': sw_if_index}) + def bfd_udp_del_echo_source(self): + return self.api(self.papi.bfd_udp_del_echo_source, {}) + def classify_add_del_table( self, is_add, -- cgit 1.2.3-korg