vrrp: add stats support and update API 10/35810/4
authorEmanuele Di Pascale <lele84@gmail.com>
Tue, 29 Mar 2022 10:29:23 +0000 (12:29 +0200)
committerEmanuele Di Pascale <lele84@gmail.com>
Fri, 1 Apr 2022 16:10:35 +0000 (18:10 +0200)
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 <lele84@gmail.com>
Change-Id: I2cd11467b4dbd9dfdb5aa748783144b4883dba57

src/plugins/vrrp/node.c
src/plugins/vrrp/vrrp.api
src/plugins/vrrp/vrrp.c
src/plugins/vrrp/vrrp.h
src/plugins/vrrp/vrrp_api.c
src/plugins/vrrp/vrrp_cli.c
src/plugins/vrrp/vrrp_packet.c
src/plugins/vrrp/vrrp_test.c
test/test_vrrp.py

index c6b619e..3cf4ec4 100644 (file)
@@ -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;
        }
 
index a34b06f..03193e9 100644 (file)
@@ -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
index 99533ed..d289053 100644 (file)
@@ -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;
 }
 
index c932592..cf529a1 100644 (file)
@@ -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)
index 9a206fa..5eb0ffc 100644 (file)
 #include <vlibapi/api_helper_macros.h>
 
 /* 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);
 
index a154a11..437b896 100644 (file)
@@ -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)
     {
index 89a6ede..0ae73aa 100644 (file)
@@ -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;
index 194e6ad..d2f79f6 100644 (file)
@@ -34,6 +34,176 @@ vrrp_test_main_t vrrp_test_main;
 #define __plugin_msg_base vrrp_test_main.msg_id_base
 #include <vlibapi/vat_helper_macros.h>
 
+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)
 {
index 1f28cf3..f7a0662 100644 (file)
@@ -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):