From: Emanuele Di Pascale Date: Tue, 29 Mar 2022 10:29:23 +0000 (+0200) Subject: vrrp: add stats support and update API X-Git-Tag: v22.10-rc0~155 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F10%2F35810%2F4;p=vpp.git vrrp: add stats support and update API Add simple counter statistics to VRRP, based on a subset of those defined in RFC8347. Add an update API that allows in-place modification of an existing instance. The method returns a vrrp_index which can be used both for retrieving statistics and to modify non-key parameters. Also add a delete method which will take that vrrp_index as parameter. Type: improvement Signed-off-by: Emanuele Di Pascale Change-Id: I2cd11467b4dbd9dfdb5aa748783144b4883dba57 --- diff --git a/src/plugins/vrrp/node.c b/src/plugins/vrrp/node.c index c6b619ec74c..3cf4ec49635 100644 --- a/src/plugins/vrrp/node.c +++ b/src/plugins/vrrp/node.c @@ -130,6 +130,7 @@ vrrp_input_process_master (vrrp_vr_t *vr, vrrp_input_process_args_t *args) { clib_warning ("Received shutdown message from a peer on VR %U", format_vrrp_vr_key, vr); + vrrp_incr_stat_counter (VRRP_STAT_COUNTER_PRIO0_RCVD, vr->stat_index); vrrp_adv_send (vr, 0); vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV); return; @@ -167,6 +168,7 @@ vrrp_input_process_backup (vrrp_vr_t *vr, vrrp_input_process_args_t *args) { clib_warning ("Master for VR %U is shutting down", format_vrrp_vr_key, vr); + vrrp_incr_stat_counter (VRRP_STAT_COUNTER_PRIO0_RCVD, vr->stat_index); vrt->master_down_int = vrt->skew; vrrp_vr_timer_set (vr, VRRP_VR_TIMER_MASTER_DOWN); return; @@ -201,6 +203,8 @@ vrrp_input_process (vrrp_input_process_args_t * args) return; } + vrrp_incr_stat_counter (VRRP_STAT_COUNTER_ADV_RCVD, vr->stat_index); + switch (vr->runtime.state) { case VRRP_VR_STATE_INIT: @@ -607,6 +611,7 @@ vrrp_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (*ttl0 != 255) { error0 = VRRP_ERROR_BAD_TTL; + vrrp_incr_err_counter (VRRP_ERR_COUNTER_TTL); goto trace; } @@ -614,6 +619,7 @@ vrrp_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if ((vrrp0->vrrp_version_and_type >> 4) != 3) { error0 = VRRP_ERROR_NOT_VERSION_3; + vrrp_incr_err_counter (VRRP_ERR_COUNTER_VERSION); goto trace; } @@ -622,6 +628,7 @@ vrrp_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, ((u32) vrrp0->n_addrs) * addr_len) { error0 = VRRP_ERROR_INCOMPLETE_PKT; + vrrp_incr_err_counter (VRRP_ERR_COUNTER_PKT_LEN); goto trace; } @@ -629,6 +636,7 @@ vrrp_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (rx_csum0 != vrrp_adv_csum (ip0, vrrp0, is_ipv6, payload_len0)) { error0 = VRRP_ERROR_BAD_CHECKSUM; + vrrp_incr_err_counter (VRRP_ERR_COUNTER_CHKSUM); goto trace; } @@ -638,6 +646,7 @@ vrrp_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vrrp0->vr_id, is_ipv6))) { error0 = VRRP_ERROR_UNKNOWN_VR; + vrrp_incr_err_counter (VRRP_ERR_COUNTER_VRID); goto trace; } @@ -646,6 +655,7 @@ vrrp_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (vrrp0->n_addrs != vec_len (vr0->config.vr_addrs)) { error0 = VRRP_ERROR_ADDR_MISMATCH; + vrrp_incr_err_counter (VRRP_ERR_COUNTER_ADDR_LIST); goto trace; } diff --git a/src/plugins/vrrp/vrrp.api b/src/plugins/vrrp/vrrp.api index a34b06ffc57..03193e99a2c 100644 --- a/src/plugins/vrrp/vrrp.api +++ b/src/plugins/vrrp/vrrp.api @@ -5,7 +5,7 @@ * */ -option version = "1.0.1"; +option version = "1.1.1"; import "vnet/interface_types.api"; import "vnet/ip/ip_types.api"; @@ -60,6 +60,55 @@ autoreply define vrrp_vr_add_del { vl_api_address_t addrs[n_addrs]; }; +/** @brief Replace an existing VRRP virtual router in-place or create a new one + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param vrrp_index - an existing VRRP entry to replace, or 0xffffffff to crate a new one + @param sw_if_index - interface backed up by this vr + @param vr_id - the VR ID advertised by this vr + @param priority - the priority advertised for this vr + @param interval - interval between advertisements in centiseconds + @param flags - bit flags for booleans - preempt, accept, unicast, ipv6 + @param n_addrs - number of addresses being backed up by this vr + @param addrs - the addresses backed up by this vr +*/ +define vrrp_vr_update { + u32 client_index; + u32 context; + u32 vrrp_index; + vl_api_interface_index_t sw_if_index; + u8 vr_id; + u8 priority; + u16 interval; + vl_api_vrrp_vr_flags_t flags; + u8 n_addrs; + vl_api_address_t addrs[n_addrs]; +}; + +/** + * @brief Reply to a VRRP add/replace + * @param context - returned sender context, to match reply w/ request + * @param vrrp_index - index of the updated or newly created VRRP instance + * @param retval 0 - no error + */ +define vrrp_vr_update_reply { + u32 context; + i32 retval; + u32 vrrp_index; +}; + +/** + * @brief Delete an existing VRRP instance + * @param client_index - opaque cookie to identify the sender + * @param context - returned sender context, to match reply w/ request + * @param vrrp_index - index of the VRRP instance to delete + */ +autoreply define vrrp_vr_del { + u32 client_index; + u32 context; + u32 vrrp_index; +}; + /** \brief VRRP: dump virtual router data @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/plugins/vrrp/vrrp.c b/src/plugins/vrrp/vrrp.c index 99533edaca0..d28905379b2 100644 --- a/src/plugins/vrrp/vrrp.c +++ b/src/plugins/vrrp/vrrp.c @@ -32,6 +32,97 @@ static const mac_address_t ipv6_vmac = { .bytes = {0x00, 0x00, 0x5e, 0x00, 0x02, 0x00} }; +vlib_simple_counter_main_t vrrp_errs[] = { + /* Total number of VRRP packets received with invalid checksum */ + { + .name = "CHKSUM_ERRS", + .stat_segment_name = "/net/vrrp/chksum-errs", + }, + /* Total number of VRRP packets received with unknown or unsupported version + */ + { + .name = "VERSION_ERRS", + .stat_segment_name = "/net/vrrp/version-errs", + }, + /* Total number of VRRP packets received with invalid VRID */ + { + .name = "VRID_ERRS", + .stat_segment_name = "/net/vrrp/vrid-errs", + }, + /* Total number of VRRP packets received with TTL/Hop limit != 255 */ + { + .name = "TTL_ERRS", + .stat_segment_name = "/net/vrrp/ttl-errs", + }, + /* Number of packets received with an address list not matching the locally + configured one */ + { + .name = "ADDR_LIST_ERRS", + .stat_segment_name = "/net/vrrp/addr-list-errs", + }, + /* Number of packets received with a length less than the VRRP header */ + { + .name = "PACKET_LEN_ERRS", + .stat_segment_name = "/net/vrrp/packet-len-errs", + }, +}; + +void +vrrp_incr_err_counter (vrrp_err_counter_t err_type) +{ + if (err_type >= VRRP_ERR_COUNTER_MAX) + { + clib_warning ("Attempt to increse error counter of unknown type %u", + err_type); + return; + } + vlib_increment_simple_counter (&vrrp_errs[err_type], + vlib_get_main ()->thread_index, 0, 1); +} + +// per-VRRP statistics + +/* Number of times a VRRP instance has transitioned to master */ +vlib_simple_counter_main_t vrrp_stats[] = { + { + .name = "MASTER_TRANS", + .stat_segment_name = "/net/vrrp/master-trans", + }, + /* Number of VRRP advertisements sent by a VRRP instance */ + { + .name = "ADV_SENT", + .stat_segment_name = "/net/vrrp/adv-sent", + }, + /* Number of VRRP advertisements received by a VRRP instance */ + { + .name = "ADV_RCVD", + .stat_segment_name = "/net/vrrp/adv-rcvd", + }, + /* Number of VRRP priority-0 packets sent by a VRRP instance */ + { + .name = "PRIO0_SENT", + .stat_segment_name = "/net/vrrp/prio0-sent", + }, + /* Number of VRRP priority-0 packets received by a VRRP instance */ + { + .name = "PRIO0_RCVD", + .stat_segment_name = "/net/vrrp/prio0-rcvd", + }, +}; + +void +vrrp_incr_stat_counter (vrrp_stat_counter_t stat_type, u32 stat_index) +{ + if (stat_type >= VRRP_STAT_COUNTER_MAX) + { + clib_warning ("Attempt to increse stat counter of unknown type %u", + stat_type); + return; + } + vlib_increment_simple_counter ( + &vrrp_stats[stat_type], vlib_get_main ()->thread_index, stat_index, 1); +} + typedef struct { vrrp_vr_key_t key; @@ -290,6 +381,7 @@ vrrp_vr_transition (vrrp_vr_t * vr, vrrp_vr_state_t new_state, void *data) if (new_state == VRRP_VR_STATE_MASTER) { + vrrp_incr_stat_counter (VRRP_STAT_COUNTER_MASTER_TRANS, vr->stat_index); /* RFC 5798 sec 6.4.1 (105) - startup event for VR with priority 255 * sec 6.4.2 (365) - master down timer fires on backup VR */ @@ -505,7 +597,7 @@ vrrp_vr_valid_addrs_owner (vrrp_vr_config_t * vr_conf) } static int -vrrp_vr_valid_addrs_unused (vrrp_vr_config_t * vr_conf) +vrrp_vr_valid_addrs_unused (vrrp_vr_config_t *vr_conf, index_t vrrp_index) { ip46_address_t *vr_addr; u8 is_ipv6 = (vr_conf->flags & VRRP_VR_IPV6) != 0; @@ -517,7 +609,7 @@ vrrp_vr_valid_addrs_unused (vrrp_vr_config_t * vr_conf) addr = (is_ipv6) ? (void *) &vr_addr->ip6 : (void *) &vr_addr->ip4; vr_index = vrrp_vr_lookup_address (vr_conf->sw_if_index, is_ipv6, addr); - if (vr_index != ~0) + if (vr_index != ~0 && vrrp_index != vr_index) return VNET_API_ERROR_ADDRESS_IN_USE; } @@ -525,7 +617,7 @@ vrrp_vr_valid_addrs_unused (vrrp_vr_config_t * vr_conf) } static int -vrrp_vr_valid_addrs (vrrp_vr_config_t * vr_conf) +vrrp_vr_valid_addrs (vrrp_vr_config_t *vr_conf, index_t vrrp_index) { int ret = 0; @@ -535,7 +627,7 @@ vrrp_vr_valid_addrs (vrrp_vr_config_t * vr_conf) return ret; /* make sure no other VR has already configured any of the VR addresses */ - ret = vrrp_vr_valid_addrs_unused (vr_conf); + ret = vrrp_vr_valid_addrs_unused (vr_conf, vrrp_index); return ret; } @@ -613,9 +705,149 @@ vrrp_vr_addrs_add_del (vrrp_vr_t * vr, u8 is_add, ip46_address_t * vr_addrs) } } +int +vrrp_vr_update (index_t *vrrp_index, vrrp_vr_config_t *vr_conf) +{ + index_t index = *vrrp_index; + vrrp_main_t *vrm = &vrrp_main; + vrrp_vr_t *vr = NULL; + vrrp_vr_key_t key = { 0 }; + uint8_t must_restart = 0; + int ret = 0; + + /* no valid index -> create and return allocated index */ + if (index == INDEX_INVALID) + { + return vrrp_vr_add_del (1, vr_conf, vrrp_index); + } + /* update: lookup vrrp instance */ + if (pool_is_free_index (vrm->vrs, index)) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + /* fetch existing VR */ + vr = pool_elt_at_index (vrm->vrs, index); + + /* populate key */ + key.vr_id = vr->config.vr_id; + key.is_ipv6 = !!(vr->config.flags & VRRP_VR_IPV6); + ; + key.sw_if_index = vr->config.sw_if_index; + + /* Do not allow changes to the keys of the VRRP instance */ + if (vr_conf->vr_id != key.vr_id || vr_conf->sw_if_index != key.sw_if_index || + !!(vr_conf->flags & VRRP_VR_IPV6) != key.is_ipv6) + { + clib_warning ("Attempt to change VR ID, IP version or interface index " + "for VRRP instance with index %u", + index); + return VNET_API_ERROR_INVALID_ARGUMENT; + } + + /* were IPvX addresses included ? */ + if (!vec_len (vr_conf->vr_addrs)) + { + clib_warning ("Conf of VR %u for IPv%d on sw_if_index %u " + " does not contain IP addresses", + key.vr_id, key.is_ipv6 ? 6 : 4, key.sw_if_index); + return VNET_API_ERROR_INVALID_SRC_ADDRESS; + } + + /* Make sure the addresses are ok to use */ + if ((ret = vrrp_vr_valid_addrs (vr_conf, index)) < 0) + return ret; + + /* stop it if needed */ + must_restart = (vr->runtime.state != VRRP_VR_STATE_INIT); + if (must_restart) + vrrp_vr_start_stop (0, &key); + + /* overwrite new config */ + vr->config.priority = vr_conf->priority; + vr->config.adv_interval = vr_conf->adv_interval; + vr->config.flags = vr_conf->flags; + + /* check if any address has changed */ + ip46_address_t *vr_addr, *conf_addr; + uint8_t found; + vec_foreach (vr_addr, vr->config.vr_addrs) + { + found = 0; + vec_foreach (conf_addr, vr_conf->vr_addrs) + { + if (ip46_address_is_equal (vr_addr, conf_addr)) + { + found = 1; + break; + } + } + if (!found) + { + vrrp_vr_addr_add_del (vr, 0, vr_addr); + } + } + vec_foreach (conf_addr, vr_conf->vr_addrs) + { + found = 0; + vec_foreach (vr_addr, vr->config.vr_addrs) + { + if (ip46_address_is_equal (vr_addr, conf_addr)) + { + found = 1; + break; + } + } + if (!found) + { + vrrp_vr_addr_add_del (vr, 1, conf_addr); + } + } + + /* restart it if needed */ + if (must_restart) + vrrp_vr_start_stop (1, &key); + + return 0; +} + +static void +vrrp_vr_del_common (vrrp_vr_t *vr, vrrp_vr_key_t *key) +{ + vrrp_main_t *vrm = &vrrp_main; + + vrrp_vr_tracking_ifs_add_del (vr, vr->tracking.interfaces, 0); + vrrp_vr_addrs_add_del (vr, 0, vr->config.vr_addrs); + mhash_unset (&vrm->vr_index_by_key, key, 0); + vec_free (vr->config.peer_addrs); + vec_free (vr->config.vr_addrs); + vec_free (vr->tracking.interfaces); + pool_put (vrm->vrs, vr); +} + +int +vrrp_vr_del (index_t vrrp_index) +{ + vrrp_main_t *vrm = &vrrp_main; + vrrp_vr_key_t key; + vrrp_vr_t *vr = 0; + + if (pool_is_free_index (vrm->vrs, vrrp_index)) + { + return (VNET_API_ERROR_NO_SUCH_ENTRY); + } + else + { + vr = pool_elt_at_index (vrm->vrs, vrrp_index); + key.sw_if_index = vr->config.sw_if_index; + key.vr_id = vr->config.vr_id; + key.is_ipv6 = vrrp_vr_is_ipv6 (vr); + vrrp_vr_del_common (vr, &key); + return 0; + } +} + /* Action function shared between message handler and debug CLI */ int -vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t * vr_conf) +vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t *vr_conf, index_t *ret_index) { vrrp_main_t *vrm = &vrrp_main; vnet_main_t *vnm = vnet_get_main (); @@ -657,7 +889,7 @@ vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t * vr_conf) } /* Make sure the addresses are ok to use */ - if ((ret = vrrp_vr_valid_addrs (vr_conf)) < 0) + if ((ret = vrrp_vr_valid_addrs (vr_conf, INDEX_INVALID)) < 0) return ret; pool_get_zero (vrm->vrs, vr); @@ -675,6 +907,20 @@ vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t * vr_conf) vr->runtime.mac = (key.is_ipv6) ? ipv6_vmac : ipv4_vmac; vr->runtime.mac.bytes[5] = vr_conf->vr_id; + /* recall pool index for stats */ + vr->stat_index = vr_index; + /* and return it if we were asked to */ + if (ret_index != NULL) + { + *ret_index = vr_index; + } + /* allocate & reset stats */ + for (int i = 0; i < VRRP_STAT_COUNTER_MAX; i++) + { + vlib_validate_simple_counter (&vrrp_stats[i], vr_index); + vlib_zero_simple_counter (&vrrp_stats[i], vr_index); + } + mhash_set (&vrm->vr_index_by_key, &key, vr_index, 0); } else @@ -688,14 +934,7 @@ vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t * vr_conf) vr_index = p[0]; vr = pool_elt_at_index (vrm->vrs, vr_index); - - vrrp_vr_tracking_ifs_add_del (vr, vr->tracking.interfaces, is_add); - vrrp_vr_addrs_add_del (vr, is_add, vr->config.vr_addrs); - mhash_unset (&vrm->vr_index_by_key, &key, 0); - vec_free (vr->config.peer_addrs); - vec_free (vr->config.vr_addrs); - vec_free (vr->tracking.interfaces); - pool_put (vrm->vrs, vr); + vrrp_vr_del_common (vr, &key); } vrrp_intf_vr_add_del (is_add, vr_conf->sw_if_index, vr_index, key.is_ipv6); @@ -1263,6 +1502,13 @@ vrrp_init (vlib_main_t * vm) vrrp_ip6_delegate_id = ip6_link_delegate_register (&vrrp_ip6_delegate_vft); + /* allocate & reset error counters */ + for (int i = 0; i < VRRP_ERR_COUNTER_MAX; i++) + { + vlib_validate_simple_counter (&vrrp_errs[i], 0); + vlib_zero_simple_counter (&vrrp_errs[i], 0); + } + return error; } diff --git a/src/plugins/vrrp/vrrp.h b/src/plugins/vrrp/vrrp.h index c9325921959..cf529a153a4 100644 --- a/src/plugins/vrrp/vrrp.h +++ b/src/plugins/vrrp/vrrp.h @@ -108,6 +108,7 @@ typedef struct vrrp_vr vrrp_vr_config_t config; vrrp_vr_runtime_t runtime; vrrp_vr_tracking_t tracking; + u32 stat_index; } vrrp_vr_t; /* Timers */ @@ -185,9 +186,46 @@ extern vlib_node_registration_t vrrp_periodic_node; #define VRRP_EVENT_VR_STOP 2 #define VRRP_EVENT_PERIODIC_ENABLE_DISABLE 3 +/* global error counter types */ +#define foreach_vrrp_err_counter \ + _ (CHKSUM, 0) \ + _ (VERSION, 1) \ + _ (VRID, 2) \ + _ (TTL, 3) \ + _ (ADDR_LIST, 4) \ + _ (PKT_LEN, 5) + +typedef enum vrrp_err_counter_ +{ +#define _(sym, val) VRRP_ERR_COUNTER_##sym = val, + foreach_vrrp_err_counter +#undef _ +} vrrp_err_counter_t; + +#define VRRP_ERR_COUNTER_MAX 6 + +/* per-instance stats */ +#define foreach_vrrp_stat_counter \ + _ (MASTER_TRANS, 0) \ + _ (ADV_SENT, 1) \ + _ (ADV_RCVD, 2) \ + _ (PRIO0_SENT, 3) \ + _ (PRIO0_RCVD, 4) + +typedef enum vrrp_stat_counter_ +{ +#define _(sym, val) VRRP_STAT_COUNTER_##sym = val, + foreach_vrrp_stat_counter +#undef _ +} vrrp_stat_counter_t; + +#define VRRP_STAT_COUNTER_MAX 5 + clib_error_t *vrrp_plugin_api_hookup (vlib_main_t * vm); -int vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t * conf); +int vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t *conf, index_t *ret_index); +int vrrp_vr_update (index_t *vrrp_index, vrrp_vr_config_t *vr_conf); +int vrrp_vr_del (index_t vrrp_index); int vrrp_vr_start_stop (u8 is_start, vrrp_vr_key_t * vr_key); extern u8 *format_vrrp_vr (u8 * s, va_list * args); extern u8 *format_vrrp_vr_key (u8 * s, va_list * args); @@ -209,6 +247,9 @@ int vrrp_vr_tracking_ifs_add_del (vrrp_vr_t * vr, u8 is_add); void vrrp_vr_event (vrrp_vr_t * vr, vrrp_vr_state_t new_state); +// stats +void vrrp_incr_err_counter (vrrp_err_counter_t err_type); +void vrrp_incr_stat_counter (vrrp_stat_counter_t stat_type, u32 stat_index); always_inline void vrrp_vr_skew_compute (vrrp_vr_t * vr) diff --git a/src/plugins/vrrp/vrrp_api.c b/src/plugins/vrrp/vrrp_api.c index 9a206fa6cdc..5eb0ffc6f20 100644 --- a/src/plugins/vrrp/vrrp_api.c +++ b/src/plugins/vrrp/vrrp_api.c @@ -24,6 +24,109 @@ #include /* API message handlers */ +static void +vl_api_vrrp_vr_update_t_handler (vl_api_vrrp_vr_update_t *mp) +{ + vl_api_vrrp_vr_update_reply_t *rmp; + vrrp_vr_config_t vr_conf; + u32 api_flags; + u32 vrrp_index = INDEX_INVALID; + ip46_address_t *addrs = 0; + int rv; + + VALIDATE_SW_IF_INDEX (mp); + + api_flags = htonl (mp->flags); + + clib_memset (&vr_conf, 0, sizeof (vr_conf)); + + vr_conf.sw_if_index = ntohl (mp->sw_if_index); + vr_conf.vr_id = mp->vr_id; + vr_conf.priority = mp->priority; + vr_conf.adv_interval = ntohs (mp->interval); + + if (api_flags & VRRP_API_VR_PREEMPT) + vr_conf.flags |= VRRP_VR_PREEMPT; + + if (api_flags & VRRP_API_VR_ACCEPT) + vr_conf.flags |= VRRP_VR_ACCEPT; + + if (api_flags & VRRP_API_VR_UNICAST) + vr_conf.flags |= VRRP_VR_UNICAST; + + if (api_flags & VRRP_API_VR_IPV6) + vr_conf.flags |= VRRP_VR_IPV6; + + int i; + for (i = 0; i < mp->n_addrs; i++) + { + ip46_address_t *addr; + void *src, *dst; + int len; + + vec_add2 (addrs, addr, 1); + + if (ntohl (mp->addrs[i].af) == ADDRESS_IP4) + { + src = &mp->addrs[i].un.ip4; + dst = &addr->ip4; + len = sizeof (addr->ip4); + } + else + { + src = &mp->addrs[i].un.ip6; + dst = &addr->ip6; + len = sizeof (addr->ip6); + } + + clib_memcpy (dst, src, len); + } + + vr_conf.vr_addrs = addrs; + + if (vr_conf.priority == 0) + { + clib_warning ("VR priority must be > 0"); + rv = VNET_API_ERROR_INVALID_VALUE; + } + else if (vr_conf.adv_interval == 0) + { + clib_warning ("VR advertisement interval must be > 0"); + rv = VNET_API_ERROR_INVALID_VALUE; + } + else if (vr_conf.vr_id == 0) + { + clib_warning ("VR ID must be > 0"); + rv = VNET_API_ERROR_INVALID_VALUE; + } + else + { + vrrp_index = ntohl (mp->vrrp_index); + rv = vrrp_vr_update (&vrrp_index, &vr_conf); + } + + vec_free (addrs); + + BAD_SW_IF_INDEX_LABEL; + // clang-format off + REPLY_MACRO2 (VL_API_VRRP_VR_UPDATE_REPLY, + ({ + rmp->vrrp_index = htonl (vrrp_index); + })); + // clang-format on +} + +static void +vl_api_vrrp_vr_del_t_handler (vl_api_vrrp_vr_del_t *mp) +{ + vl_api_vrrp_vr_del_reply_t *rmp; + int rv; + + rv = vrrp_vr_del (ntohl (mp->vrrp_index)); + + REPLY_MACRO (VL_API_VRRP_VR_DEL_REPLY); +} + static void vl_api_vrrp_vr_add_del_t_handler (vl_api_vrrp_vr_add_del_t * mp) { @@ -103,7 +206,7 @@ vl_api_vrrp_vr_add_del_t_handler (vl_api_vrrp_vr_add_del_t * mp) rv = VNET_API_ERROR_INVALID_VALUE; } else - rv = vrrp_vr_add_del (mp->is_add, &vr_conf); + rv = vrrp_vr_add_del (mp->is_add, &vr_conf, NULL); vec_free (addrs); diff --git a/src/plugins/vrrp/vrrp_cli.c b/src/plugins/vrrp/vrrp_cli.c index a154a11a8fa..437b896839c 100644 --- a/src/plugins/vrrp/vrrp_cli.c +++ b/src/plugins/vrrp/vrrp_cli.c @@ -102,7 +102,7 @@ vrrp_vr_add_del_command_fn (vlib_main_t * vm, vr_conf.adv_interval = (u16) interval; vr_conf.vr_addrs = addrs; - rv = vrrp_vr_add_del (is_add, &vr_conf); + rv = vrrp_vr_add_del (is_add, &vr_conf, NULL); switch (rv) { diff --git a/src/plugins/vrrp/vrrp_packet.c b/src/plugins/vrrp/vrrp_packet.c index 89a6ede605e..0ae73aa9d0a 100644 --- a/src/plugins/vrrp/vrrp_packet.c +++ b/src/plugins/vrrp/vrrp_packet.c @@ -354,6 +354,12 @@ vrrp_adv_send (vrrp_vr_t * vr, int shutdown) vlib_put_frame_to_node (vm, node_index, to_frame); + vrrp_incr_stat_counter (VRRP_STAT_COUNTER_ADV_SENT, vr->stat_index); + if (shutdown) + { + vrrp_incr_stat_counter (VRRP_STAT_COUNTER_PRIO0_SENT, vr->stat_index); + } + vec_free (bi); return 0; diff --git a/src/plugins/vrrp/vrrp_test.c b/src/plugins/vrrp/vrrp_test.c index 194e6adfc42..d2f79f65c3f 100644 --- a/src/plugins/vrrp/vrrp_test.c +++ b/src/plugins/vrrp/vrrp_test.c @@ -34,6 +34,176 @@ vrrp_test_main_t vrrp_test_main; #define __plugin_msg_base vrrp_test_main.msg_id_base #include +static int +api_vrrp_vr_update (vat_main_t *vam) +{ + unformat_input_t *i = vam->input; + u32 sw_if_index = ~0; + u32 vr_id, priority, interval, vrrp_index; + u8 is_ipv6, no_preempt, accept_mode, vr_unicast; + u8 n_addrs4, n_addrs6; + vl_api_vrrp_vr_update_t *mp; + vl_api_address_t *api_addr; + ip46_address_t *ip_addr, *ip_addrs = 0; + ip46_address_t addr; + int ret = 0; + + interval = priority = 100; + n_addrs4 = n_addrs6 = 0; + vr_id = is_ipv6 = no_preempt = accept_mode = vr_unicast = 0; + vrrp_index = INDEX_INVALID; + + clib_memset (&addr, 0, sizeof (addr)); + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %u", &sw_if_index)) + ; + else if (unformat (i, "vr_id %u", &vr_id)) + ; + else if (unformat (i, "vrrp_index %u", &vrrp_index)) + ; + else if (unformat (i, "ipv6")) + is_ipv6 = 1; + else if (unformat (i, "priority %u", &priority)) + ; + else if (unformat (i, "interval %u", &interval)) + ; + else if (unformat (i, "no_preempt")) + no_preempt = 1; + else if (unformat (i, "accept_mode")) + accept_mode = 1; + else if (unformat (i, "unicast")) + vr_unicast = 1; + else if (unformat (i, "%U", unformat_ip4_address, &addr.ip4)) + { + vec_add1 (ip_addrs, addr); + n_addrs4++; + clib_memset (&addr, 0, sizeof (addr)); + } + else if (unformat (i, "%U", unformat_ip6_address, &addr.ip6)) + { + vec_add1 (ip_addrs, addr); + n_addrs6++; + clib_memset (&addr, 0, sizeof (addr)); + } + else + break; + } + + if (sw_if_index == ~0) + { + errmsg ("Interface not set\n"); + ret = -99; + } + else if (n_addrs4 && (n_addrs6 || is_ipv6)) + { + errmsg ("Address family mismatch\n"); + ret = -99; + } + + if (ret) + goto done; + + /* Construct the API message */ + M2 (VRRP_VR_UPDATE, mp, vec_len (ip_addrs) * sizeof (*api_addr)); + + mp->vrrp_index = htonl (vrrp_index); + mp->sw_if_index = ntohl (sw_if_index); + mp->vr_id = vr_id; + mp->priority = priority; + mp->interval = htons (interval); + mp->flags = VRRP_API_VR_PREEMPT; /* preempt by default */ + + if (no_preempt) + mp->flags &= ~VRRP_API_VR_PREEMPT; + + if (accept_mode) + mp->flags |= VRRP_API_VR_ACCEPT; + + if (vr_unicast) + mp->flags |= VRRP_API_VR_UNICAST; + + if (is_ipv6) + mp->flags |= VRRP_API_VR_IPV6; + + mp->flags = htonl (mp->flags); + + mp->n_addrs = n_addrs4 + n_addrs6; + api_addr = mp->addrs; + + vec_foreach (ip_addr, ip_addrs) + { + void *src, *dst; + int len; + + if (is_ipv6) + { + api_addr->af = ADDRESS_IP6; + src = &ip_addr->ip6; + dst = &api_addr->un.ip6; + len = sizeof (api_addr->un.ip6); + } + else + { + api_addr->af = ADDRESS_IP4; + src = &ip_addr->ip4; + dst = &api_addr->un.ip4; + len = sizeof (api_addr->un.ip4); + } + clib_memcpy (dst, src, len); + api_addr++; + } + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + +done: + vec_free (ip_addrs); + + return ret; +} + +static void +vl_api_vrrp_vr_update_reply_t_handler (vl_api_vrrp_vr_update_reply_t *mp) +{ +} + +static int +api_vrrp_vr_del (vat_main_t *vam) +{ + unformat_input_t *i = vam->input; + vl_api_vrrp_vr_del_t *mp; + u32 vrrp_index = INDEX_INVALID; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "vrrp_index %u", &vrrp_index)) + ; + else + break; + } + + /* Construct the API message */ + M (VRRP_VR_DEL, mp); + mp->vrrp_index = htonl (vrrp_index); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + static int api_vrrp_vr_add_del (vat_main_t * vam) { diff --git a/test/test_vrrp.py b/test/test_vrrp.py index 1f28cf3d18d..f7a0662bf01 100644 --- a/test/test_vrrp.py +++ b/test/test_vrrp.py @@ -37,6 +37,8 @@ VRRP_VR_STATE_BACKUP = 1 VRRP_VR_STATE_MASTER = 2 VRRP_VR_STATE_INTF_DOWN = 3 +VRRP_INDEX_INVALID = 0xffffffff + def is_non_arp(p): """ Want to filter out advertisements, igmp, etc""" @@ -95,6 +97,7 @@ class VppVRRPVirtualRouter(VppObject): self._adv_dest_ip = "224.0.0.18" self._vips = ([intf.local_ip4] if vips is None else vips) self._tracked_ifs = [] + self._vrrp_index = VRRP_INDEX_INVALID def add_vpp_config(self): self._test.vapi.vrrp_vr_add_del(is_add=1, @@ -106,6 +109,20 @@ class VppVRRPVirtualRouter(VppObject): n_addrs=len(self._vips), addrs=self._vips) + def update_vpp_config(self): + r = self._test.vapi.vrrp_vr_update(vrrp_index=self._vrrp_index, + sw_if_index=self._intf.sw_if_index, + vr_id=self._vr_id, + priority=self._prio, + interval=self._intvl, + flags=self._flags, + n_addrs=len(self._vips), + addrs=self._vips) + self._vrrp_index = r.vrrp_index + + def delete_vpp_config(self): + self._test.vapi.vrrp_vr_del(vrrp_index=self._vrrp_index) + def query_vpp_config(self): vrs = self._test.vapi.vrrp_vr_dump(sw_if_index=self._intf.sw_if_index) for vr in vrs: @@ -341,6 +358,56 @@ class TestVRRP4(VppTestCase): vr.remove_vpp_config() self._vrs = [] + # Same as above but with the update API, and add a change + # of parameters to test that too + def test_vrrp4_master_adv_update(self): + """ IPv4 Master VR adv + Update to Backup """ + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + prio = 255 + intvl = self._default_adv + vr = VppVRRPVirtualRouter(self, self.pg0, 100, + prio=prio, intvl=intvl, + flags=self._default_flags) + + vr.update_vpp_config() + vr.start_stop(is_start=1) + self.logger.info(self.vapi.cli("show vrrp vr")) + # Update VR with lower prio and larger interval + # we need to keep old VR for the adv checks + upd_vr = VppVRRPVirtualRouter(self, self.pg0, 100, + prio=100, intvl=2*intvl, + flags=self._default_flags, + vips=[self.pg0.remote_ip4]) + upd_vr._vrrp_index = vr._vrrp_index + upd_vr.update_vpp_config() + start_time = time.time() + self.logger.info(self.vapi.cli("show vrrp vr")) + upd_vr.assert_state_equals(VRRP_VR_STATE_BACKUP) + self._vrs = [upd_vr] + + pkts = self.pg0.get_capture(5) + # Init -> Master: IGMP Join, VRRP adv, gratuitous ARP are sent + self.verify_vrrp4_igmp(pkts[0]) + self.verify_vrrp4_adv(pkts[1], vr, prio=prio) + self.verify_vrrp4_garp(pkts[2], vr.virtual_ips()[0], vr.virtual_mac()) + # Master -> Init: Adv with priority 0 sent to force an election + self.verify_vrrp4_adv(pkts[3], vr, prio=0) + # Init -> Backup: An IGMP join should be sent + self.verify_vrrp4_igmp(pkts[4]) + + # send higher prio advertisements, should not receive any + end_time = start_time + 2 * upd_vr.master_down_seconds() + src_ip = self.pg0.remote_ip4 + pkts = [upd_vr.vrrp_adv_packet(prio=110, src_ip=src_ip)] + while time.time() < end_time: + self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl*0.01) + self.logger.info(self.vapi.cli("show trace")) + + upd_vr.start_stop(is_start=0) + self.logger.info(self.vapi.cli("show vrrp vr")) + # VR with priority < 255 enters backup state and does not advertise as # long as it receives higher priority advertisements def test_vrrp4_backup_noadv(self): @@ -875,6 +942,59 @@ class TestVRRP6(VppTestCase): vr.remove_vpp_config() self._vrs = [] + # Same as above but with the update API, and add a change + # of parameters to test that too + def test_vrrp6_master_adv_update(self): + """ IPv6 Master VR adv + Update to Backup """ + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + prio = 255 + intvl = self._default_adv + vr = VppVRRPVirtualRouter(self, self.pg0, 100, + prio=prio, intvl=intvl, + flags=self._default_flags) + + vr.update_vpp_config() + vr.start_stop(is_start=1) + self.logger.info(self.vapi.cli("show vrrp vr")) + # Update VR with lower prio and larger interval + # we need to keep old VR for the adv checks + upd_vr = VppVRRPVirtualRouter(self, self.pg0, 100, + prio=100, intvl=2*intvl, + flags=self._default_flags, + vips=[self.pg0.remote_ip6]) + upd_vr._vrrp_index = vr._vrrp_index + upd_vr.update_vpp_config() + start_time = time.time() + self.logger.info(self.vapi.cli("show vrrp vr")) + upd_vr.assert_state_equals(VRRP_VR_STATE_BACKUP) + self._vrs = [upd_vr] + + pkts = self.pg0.get_capture(5, filter_out_fn=None) + + # Init -> Master: Multicast group Join, VRRP adv, gratuitous NAs sent + self.verify_vrrp6_mlr(pkts[0], vr) + self.verify_vrrp6_adv(pkts[1], vr, prio=prio) + self.verify_vrrp6_gna(pkts[2], vr) + # Master -> Init: Adv with priority 0 sent to force an election + self.verify_vrrp6_adv(pkts[3], vr, prio=0) + # Init -> Backup: A multicast listener report should be sent + # not actually verified in the test below, where I took this from + + # send higher prio advertisements, should not see VPP send any + src_ip = self.pg0.remote_ip6_ll + pkts = [upd_vr.vrrp_adv_packet(prio=110, src_ip=src_ip)] + self.logger.info(self.vapi.cli("show vlib graph")) + end_time = start_time + 2 * upd_vr.master_down_seconds() + while time.time() < end_time: + self.send_and_assert_no_replies( + self.pg0, pkts, timeout=0.01*upd_vr._intvl) + self.logger.info(self.vapi.cli("show trace")) + + vr.start_stop(is_start=0) + self.logger.info(self.vapi.cli("show vrrp vr")) + # VR with priority < 255 enters backup state and does not advertise as # long as it receives higher priority advertisements def test_vrrp6_backup_noadv(self):