fib: Table Replace 36/23336/6
authorNeale Ranns <nranns@cisco.com>
Fri, 8 Nov 2019 12:42:31 +0000 (12:42 +0000)
committerOle Trøan <otroan@employees.org>
Tue, 26 Nov 2019 09:15:11 +0000 (09:15 +0000)
Type: feature

from the API doc, a table replace is:

"
    The use-case is that, for some unspecified reason, the control plane
    has a very different set of entries it wants in the table than VPP
    currently has. The CP would thus like to 'replace' VPP's current table
    only by specifying what the new set of entries shall be, i.e. it is not
    going to delete anything that already eixts.
    the CP delcartes the start of this procedure with this begin_replace
    API Call, and when it has populated all the entries it wants, it calls
    the below end_replace API. From this point on it is of coursce free
    to add and delete entries as usual.
    The underlying mechanism by which VPP implements this replace is
    purposefully left unspecified.
"

In the FIB, the algorithm is implemented using mark and sweep.

Algorithm goes:
1) replace_begin: this marks all the entries in that table as 'stale'
2) download all the entries that should be in this table
 - this clears the stale flag on those entries
3) signal the table converged: ip_table_replace_end
 - this removes all entries that are still stale

this procedure can be used when an agent first connects to VPP,
as an alternative to dump and diff state reconciliation.

Change-Id: I168edec10cf7670866076b129ebfe6149ea8222e
Signed-off-by: Neale Ranns <nranns@cisco.com>
31 files changed:
src/plugins/nat/test/test_nat.py
src/vat/api_format.c
src/vnet/ethernet/arp.c
src/vnet/ethernet/arp.h
src/vnet/fib/fib_entry.h
src/vnet/fib/fib_entry_src.c
src/vnet/fib/fib_entry_src.h
src/vnet/fib/fib_table.c
src/vnet/fib/fib_table.h
src/vnet/fib/ip4_fib.c
src/vnet/fib/ip6_fib.c
src/vnet/ip/ip.api
src/vnet/ip/ip_api.c
src/vnet/mfib/ip4_mfib.c
src/vnet/mfib/ip6_mfib.c
src/vnet/mfib/mfib_entry.c
src/vnet/mfib/mfib_entry.h
src/vnet/mfib/mfib_entry_src.h
src/vnet/mfib/mfib_entry_src_rr.c
src/vnet/mfib/mfib_table.c
src/vnet/mfib/mfib_table.h
src/vnet/mpls/mpls.c
src/vnet/mpls/mpls.h
src/vpp/api/custom_dump.c
test/test_ip4.py
test/test_ip4_vrf_multi_instance.py
test/test_ip6.py
test/test_ip6_vrf_multi_instance.py
test/vpp_ip.py
test/vpp_ip_route.py
test/vpp_papi_provider.py

index 803f1b0..29fd5ca 100644 (file)
@@ -1501,8 +1501,8 @@ class TestNAT44(MethodHolder):
             cls.pg1.configure_ipv4_neighbors()
 
             cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7]))
-            cls.vapi.ip_table_add_del(is_add=1, table_id=10)
-            cls.vapi.ip_table_add_del(is_add=1, table_id=20)
+            cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 10})
+            cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 20})
 
             cls.pg4._local_ip4 = "172.16.255.1"
             cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2"
@@ -3049,8 +3049,8 @@ class TestNAT44(MethodHolder):
 
         self.pg0.unconfig_ip4()
         self.pg1.unconfig_ip4()
-        self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id1)
-        self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id2)
+        self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1})
+        self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2})
         self.pg0.set_table_ip4(vrf_id1)
         self.pg1.set_table_ip4(vrf_id2)
         self.pg0.config_ip4()
@@ -3097,8 +3097,8 @@ class TestNAT44(MethodHolder):
             self.pg1.config_ip4()
             self.pg0.resolve_arp()
             self.pg1.resolve_arp()
-            self.vapi.ip_table_add_del(is_add=0, table_id=vrf_id1)
-            self.vapi.ip_table_add_del(is_add=0, table_id=vrf_id2)
+            self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id1})
+            self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id2})
 
     def test_vrf_feature_independent(self):
         """ NAT44 tenant VRF independent address pool mode """
@@ -3888,8 +3888,8 @@ class TestNAT44(MethodHolder):
 
         self.pg1.unconfig_ip4()
         self.pg2.unconfig_ip4()
-        self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id1)
-        self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id2)
+        self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1})
+        self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2})
         self.pg1.set_table_ip4(vrf_id1)
         self.pg2.set_table_ip4(vrf_id2)
         self.pg1.config_ip4()
@@ -4435,7 +4435,7 @@ class TestNAT44EndpointDependent(MethodHolder):
             cls.pg4.resolve_arp()
 
             zero_ip4 = socket.inet_pton(socket.AF_INET, "0.0.0.0")
-            cls.vapi.ip_table_add_del(is_add=1, table_id=1)
+            cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 1})
 
             cls.pg5._local_ip4 = "10.1.1.1"
             cls.pg5._remote_hosts[0]._ip4 = "10.1.1.2"
@@ -4783,7 +4783,8 @@ class TestNAT44EndpointDependent(MethodHolder):
             is_add=1)
 
         try:
-            self.vapi.ip_table_add_del(is_add=1, table_id=new_vrf_id)
+            self.vapi.ip_table_add_del(is_add=1,
+                                       table={'table_id': new_vrf_id})
 
             self.pg7.unconfig_ip4()
             self.pg7.set_table_ip4(new_vrf_id)
@@ -4874,7 +4875,8 @@ class TestNAT44EndpointDependent(MethodHolder):
             self.pg8.config_ip4()
             self.pg8.resolve_arp()
 
-            self.vapi.ip_table_add_del(is_add=0, table_id=new_vrf_id)
+            self.vapi.ip_table_add_del(is_add=0,
+                                       table={'table_id': new_vrf_id})
 
     def test_forwarding(self):
         """ NAT44 forwarding test """
@@ -7771,8 +7773,9 @@ class TestNAT64(MethodHolder):
             cls.ip6_interfaces.append(cls.pg_interfaces[2])
             cls.ip4_interfaces = list(cls.pg_interfaces[1:2])
 
-            cls.vapi.ip_table_add_del(is_ipv6=1, is_add=1,
-                                      table_id=cls.vrf1_id)
+            cls.vapi.ip_table_add_del(is_add=1,
+                                      table={'table_id': cls.vrf1_id,
+                                             'is_ip6': 1})
 
             cls.pg_interfaces[2].set_table_ip6(cls.vrf1_id)
 
index 7b5d594..924d9c6 100644 (file)
@@ -5071,6 +5071,9 @@ _(l2fib_flush_int_reply)                                \
 _(l2fib_flush_bd_reply)                                 \
 _(ip_route_add_del_reply)                               \
 _(ip_table_add_del_reply)                               \
+_(ip_table_replace_begin_reply)                         \
+_(ip_table_flush_reply)                                 \
+_(ip_table_replace_end_reply)                           \
 _(ip_mroute_add_del_reply)                              \
 _(mpls_route_add_del_reply)                             \
 _(mpls_table_add_del_reply)                             \
@@ -5081,7 +5084,6 @@ _(proxy_arp_add_del_reply)                              \
 _(proxy_arp_intfc_enable_disable_reply)                 \
 _(sw_interface_set_unnumbered_reply)                    \
 _(ip_neighbor_add_del_reply)                            \
-_(reset_fib_reply)                                      \
 _(set_ip_flow_hash_reply)                               \
 _(sw_interface_ip6_enable_disable_reply)                \
 _(ip6nd_proxy_add_del_reply)                            \
@@ -5270,6 +5272,9 @@ _(SW_INTERFACE_BOND_DETAILS, sw_interface_bond_details)                 \
 _(SW_INTERFACE_SLAVE_DETAILS, sw_interface_slave_details)               \
 _(IP_ROUTE_ADD_DEL_REPLY, ip_route_add_del_reply)                      \
 _(IP_TABLE_ADD_DEL_REPLY, ip_table_add_del_reply)                      \
+_(IP_TABLE_REPLACE_BEGIN_REPLY, ip_table_replace_begin_reply)           \
+_(IP_TABLE_FLUSH_REPLY, ip_table_flush_reply)                           \
+_(IP_TABLE_REPLACE_END_REPLY, ip_table_replace_end_reply)               \
 _(IP_MROUTE_ADD_DEL_REPLY, ip_mroute_add_del_reply)                    \
 _(MPLS_TABLE_ADD_DEL_REPLY, mpls_table_add_del_reply)                  \
 _(MPLS_ROUTE_ADD_DEL_REPLY, mpls_route_add_del_reply)                  \
@@ -5285,7 +5290,6 @@ _(SW_INTERFACE_SET_UNNUMBERED_REPLY,                                    \
 _(IP_NEIGHBOR_ADD_DEL_REPLY, ip_neighbor_add_del_reply)                 \
 _(CREATE_VLAN_SUBIF_REPLY, create_vlan_subif_reply)                     \
 _(CREATE_SUBIF_REPLY, create_subif_reply)                              \
-_(RESET_FIB_REPLY, reset_fib_reply)                                     \
 _(SET_IP_FLOW_HASH_REPLY, set_ip_flow_hash_reply)                       \
 _(SW_INTERFACE_IP6_ENABLE_DISABLE_REPLY,                                \
   sw_interface_ip6_enable_disable_reply)                                \
@@ -9210,19 +9214,18 @@ api_create_subif (vat_main_t * vam)
 }
 
 static int
-api_reset_fib (vat_main_t * vam)
+api_ip_table_replace_begin (vat_main_t * vam)
 {
   unformat_input_t *i = vam->input;
-  vl_api_reset_fib_t *mp;
-  u32 vrf_id = 0;
+  vl_api_ip_table_replace_begin_t *mp;
+  u32 table_id = 0;
   u8 is_ipv6 = 0;
-  u8 vrf_id_set = 0;
 
   int ret;
   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (i, "vrf %d", &vrf_id))
-       vrf_id_set = 1;
+      if (unformat (i, "table %d", &table_id))
+       ;
       else if (unformat (i, "ipv6"))
        is_ipv6 = 1;
       else
@@ -9232,16 +9235,74 @@ api_reset_fib (vat_main_t * vam)
        }
     }
 
-  if (vrf_id_set == 0)
+  M (IP_TABLE_REPLACE_BEGIN, mp);
+
+  mp->table.table_id = ntohl (table_id);
+  mp->table.is_ip6 = is_ipv6;
+
+  S (mp);
+  W (ret);
+  return ret;
+}
+
+static int
+api_ip_table_flush (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ip_table_flush_t *mp;
+  u32 table_id = 0;
+  u8 is_ipv6 = 0;
+
+  int ret;
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
     {
-      errmsg ("missing vrf id");
-      return -99;
+      if (unformat (i, "table %d", &table_id))
+       ;
+      else if (unformat (i, "ipv6"))
+       is_ipv6 = 1;
+      else
+       {
+         clib_warning ("parse error '%U'", format_unformat_error, i);
+         return -99;
+       }
     }
 
-  M (RESET_FIB, mp);
+  M (IP_TABLE_FLUSH, mp);
 
-  mp->vrf_id = ntohl (vrf_id);
-  mp->is_ipv6 = is_ipv6;
+  mp->table.table_id = ntohl (table_id);
+  mp->table.is_ip6 = is_ipv6;
+
+  S (mp);
+  W (ret);
+  return ret;
+}
+
+static int
+api_ip_table_replace_end (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ip_table_replace_end_t *mp;
+  u32 table_id = 0;
+  u8 is_ipv6 = 0;
+
+  int ret;
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "table %d", &table_id))
+       ;
+      else if (unformat (i, "ipv6"))
+       is_ipv6 = 1;
+      else
+       {
+         clib_warning ("parse error '%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  M (IP_TABLE_REPLACE_END, mp);
+
+  mp->table.table_id = ntohl (table_id);
+  mp->table.is_ip6 = is_ipv6;
 
   S (mp);
   W (ret);
@@ -21546,7 +21607,9 @@ _(create_subif, "<intfc> | sw_if_index <id> sub_id <n>\n"               \
   "[outer_vlan_id <n>][inner_vlan_id <n>]\n"                            \
   "[no_tags][one_tag][two_tags][dot1ad][exact_match][default_sub]\n"    \
   "[outer_vlan_id_any][inner_vlan_id_any]")                             \
-_(reset_fib, "vrf <n> [ipv6]")                                          \
+_(ip_table_replace_begin, "table <n> [ipv6]")                           \
+_(ip_table_flush, "table <n> [ipv6]")                                   \
+_(ip_table_replace_end, "table <n> [ipv6]")                             \
 _(set_ip_flow_hash,                                                     \
   "vrf <n> [src] [dst] [sport] [dport] [proto] [reverse] [ipv6]")       \
 _(sw_interface_ip6_enable_disable,                                      \
index ca9a21c..6b1069e 100644 (file)
@@ -2476,41 +2476,6 @@ proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data)
   }
 }
 
-/*
- * Remove any proxy arp entries associated with the
- * specified fib.
- */
-int
-vnet_proxy_arp_fib_reset (u32 fib_id)
-{
-  ethernet_arp_main_t *am = &ethernet_arp_main;
-  ethernet_proxy_arp_t *pa;
-  u32 *entries_to_delete = 0;
-  u32 fib_index;
-  int i;
-
-  fib_index = fib_table_find (FIB_PROTOCOL_IP4, fib_id);
-  if (~0 == fib_index)
-    return VNET_API_ERROR_NO_SUCH_ENTRY;
-
-  vec_foreach (pa, am->proxy_arps)
-  {
-    if (pa->fib_index == fib_index)
-      {
-       vec_add1 (entries_to_delete, pa - am->proxy_arps);
-      }
-  }
-
-  for (i = 0; i < vec_len (entries_to_delete); i++)
-    {
-      vec_delete (am->proxy_arps, 1, entries_to_delete[i]);
-    }
-
-  vec_free (entries_to_delete);
-
-  return 0;
-}
-
 static clib_error_t *
 ip_arp_add_del_command_fn (vlib_main_t * vm,
                           unformat_input_t * input, vlib_cli_command_t * cmd)
index 56d9f29..de92c4f 100644 (file)
@@ -62,8 +62,6 @@ extern int vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm,
                                             ethernet_arp_ip4_over_ethernet_address_t
                                             * a);
 
-extern int vnet_proxy_arp_fib_reset (u32 fib_id);
-
 void vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm,
                                             void *address_arg,
                                             uword node_index,
index 4ce28b1..f0e6e8d 100644 (file)
@@ -314,6 +314,10 @@ typedef enum fib_entry_src_attribute_t_ {
      * the source is active/best
      */
     FIB_ENTRY_SRC_ATTRIBUTE_ACTIVE,
+    /**
+     * the source is stale
+     */
+    FIB_ENTRY_SRC_ATTRIBUTE_STALE,
     /**
      * the source is inherited from its cover
      */
@@ -329,6 +333,7 @@ typedef enum fib_entry_src_attribute_t_ {
     [FIB_ENTRY_SRC_ATTRIBUTE_ADDED]  = "added",         \
     [FIB_ENTRY_SRC_ATTRIBUTE_CONTRIBUTING] = "contributing", \
     [FIB_ENTRY_SRC_ATTRIBUTE_ACTIVE] = "active", \
+    [FIB_ENTRY_SRC_ATTRIBUTE_STALE] = "stale",      \
     [FIB_ENTRY_SRC_ATTRIBUTE_INHERITED] = "inherited", \
 }
 
@@ -342,6 +347,7 @@ typedef enum fib_entry_src_flag_t_ {
     FIB_ENTRY_SRC_FLAG_ADDED  = (1 << FIB_ENTRY_SRC_ATTRIBUTE_ADDED),
     FIB_ENTRY_SRC_FLAG_CONTRIBUTING = (1 << FIB_ENTRY_SRC_ATTRIBUTE_CONTRIBUTING),
     FIB_ENTRY_SRC_FLAG_ACTIVE = (1 << FIB_ENTRY_SRC_ATTRIBUTE_ACTIVE),
+    FIB_ENTRY_SRC_FLAG_STALE = (1 << FIB_ENTRY_SRC_ATTRIBUTE_STALE),
     FIB_ENTRY_SRC_FLAG_INHERITED = (1 << FIB_ENTRY_SRC_ATTRIBUTE_INHERITED),
 } __attribute__ ((packed)) fib_entry_src_flag_t;
 
@@ -627,6 +633,8 @@ extern int fib_entry_is_sourced(fib_node_index_t fib_entry_index,
 extern fib_node_index_t fib_entry_get_path_list(fib_node_index_t fib_entry_index);
 extern int fib_entry_is_resolved(fib_node_index_t fib_entry_index);
 extern int fib_entry_is_host(fib_node_index_t fib_entry_index);
+extern int fib_entry_is_marked(fib_node_index_t fib_entry_index, fib_source_t source);
+extern void fib_entry_mark(fib_node_index_t fib_entry_index, fib_source_t source);
 extern void fib_entry_set_flow_hash_config(fib_node_index_t fib_entry_index,
                                            flow_hash_config_t hash_config);
 
index 6ed13a3..176818a 100644 (file)
@@ -141,6 +141,44 @@ fib_entry_is_sourced (fib_node_index_t fib_entry_index,
     return (NULL != fib_entry_src_find(fib_entry, source));
 }
 
+int
+fib_entry_is_marked (fib_node_index_t fib_entry_index,
+                      fib_source_t source)
+{
+    fib_entry_t *fib_entry;
+    fib_entry_src_t *esrc;
+
+    fib_entry = fib_entry_get(fib_entry_index);
+
+    esrc = fib_entry_src_find(fib_entry, source);
+
+    if (NULL == esrc)
+    {
+        return (0);
+    }
+    else
+    {
+        return (!!(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_STALE));
+    }
+}
+
+void
+fib_entry_mark (fib_node_index_t fib_entry_index,
+                fib_source_t source)
+{
+    fib_entry_t *fib_entry;
+    fib_entry_src_t *esrc;
+
+    fib_entry = fib_entry_get(fib_entry_index);
+
+    esrc = fib_entry_src_find(fib_entry, source);
+
+    if (NULL != esrc)
+    {
+        esrc->fes_flags |= FIB_ENTRY_SRC_FLAG_STALE;
+    }
+}
+
 static fib_entry_src_t *
 fib_entry_src_find_or_create (fib_entry_t *fib_entry,
                              fib_source_t source,
index a859b9c..8f13ae8 100644 (file)
@@ -226,20 +226,24 @@ typedef struct fib_entry_src_vft_t_ {
     }                                                          \
 }
 
-#define FIB_ENTRY_SRC_VFT_INVOKE(esrc, func, args)  \
-{                                                   \
-    const fib_entry_src_vft_t *_vft;                \
-    _vft = fib_entry_src_get_vft(esrc);             \
-    if (_vft->func)                                 \
-        _vft->func args;                            \
+#define FIB_ENTRY_SRC_VFT_INVOKE(esrc, func, args)             \
+{                                                              \
+    const fib_entry_src_vft_t *_vft;                           \
+    _vft = fib_entry_src_get_vft(esrc);                        \
+    if (_vft->func) {                                          \
+        (esrc)->fes_flags &= ~FIB_ENTRY_SRC_FLAG_STALE;        \
+        _vft->func args;                                       \
+    }                                                          \
 }
 
 #define FIB_ENTRY_SRC_VFT_INVOKE_AND_RETURN(esrc, func, args)  \
-{                                                   \
-    const fib_entry_src_vft_t *_vft;                \
-    _vft = fib_entry_src_get_vft(esrc);             \
-    if (_vft->func)                                 \
-        return (_vft->func args);                   \
+{                                                              \
+    const fib_entry_src_vft_t *_vft;                           \
+    _vft = fib_entry_src_get_vft(esrc);                        \
+    if (_vft->func) {                                          \
+        (esrc)->fes_flags &= ~FIB_ENTRY_SRC_FLAG_STALE;        \
+        return (_vft->func args);                              \
+    }                                                          \
 }
 
 #define FIB_ENTRY_SRC_VFT_EXISTS(esrc, func)        \
index 3778fa9..d3cf5dc 100644 (file)
@@ -23,6 +23,8 @@
 #include <vnet/fib/ip6_fib.h>
 #include <vnet/fib/mpls_fib.h>
 
+const static char * fib_table_flags_strings[] = FIB_TABLE_ATTRIBUTES;
+
 fib_table_t *
 fib_table_get (fib_node_index_t index,
               fib_protocol_t proto)
@@ -1305,6 +1307,26 @@ format_fib_table_name (u8* s, va_list* ap)
     return (s);
 }
 
+u8*
+format_fib_table_flags (u8 *s, va_list *args)
+{
+    fib_table_flags_t flags = va_arg(*args, int);
+    fib_table_attribute_t attr;
+
+    if (!flags)
+    {
+        return format(s, "none");
+    }
+
+    FOR_EACH_FIB_TABLE_ATTRIBUTE(attr) {
+        if (1 << attr & flags) {
+            s = format(s, "%s", fib_table_flags_strings[attr]);
+        }
+    }
+
+    return (s);
+}
+
 /**
  * @brief Table flush context. Store the indicies of matching FIB entries
  * that need to be removed.
@@ -1335,7 +1357,6 @@ fib_table_flush_cb (fib_node_index_t fib_entry_index,
     return (FIB_TABLE_WALK_CONTINUE);
 }
 
-
 void
 fib_table_flush (u32 fib_index,
                 fib_protocol_t proto,
@@ -1359,6 +1380,79 @@ fib_table_flush (u32 fib_index,
     vec_free(ctx.ftf_entries);
 }
 
+static fib_table_walk_rc_t
+fib_table_mark_cb (fib_node_index_t fib_entry_index,
+                   void *arg)
+{
+    fib_table_flush_ctx_t *ctx = arg;
+
+    if (fib_entry_is_sourced(fib_entry_index, ctx->ftf_source))
+    {
+        fib_entry_mark(fib_entry_index, ctx->ftf_source);
+    }
+    return (FIB_TABLE_WALK_CONTINUE);
+}
+
+void
+fib_table_mark (u32 fib_index,
+                fib_protocol_t proto,
+                fib_source_t source)
+{
+    fib_table_flush_ctx_t ctx = {
+        .ftf_source = source,
+    };
+    fib_table_t *fib_table;
+
+    fib_table = fib_table_get(fib_index, proto);
+
+    fib_table->ft_epoch++;
+    fib_table->ft_flags |= FIB_TABLE_FLAG_RESYNC;
+
+    fib_table_walk(fib_index, proto,
+                   fib_table_mark_cb,
+                   &ctx);
+}
+
+static fib_table_walk_rc_t
+fib_table_sweep_cb (fib_node_index_t fib_entry_index,
+                    void *arg)
+{
+    fib_table_flush_ctx_t *ctx = arg;
+
+    if (fib_entry_is_marked(fib_entry_index, ctx->ftf_source))
+    {
+        vec_add1(ctx->ftf_entries, fib_entry_index);
+    }
+    return (FIB_TABLE_WALK_CONTINUE);
+}
+
+void
+fib_table_sweep (u32 fib_index,
+                 fib_protocol_t proto,
+                 fib_source_t source)
+{
+    fib_table_flush_ctx_t ctx = {
+        .ftf_source = source,
+    };
+    fib_node_index_t *fib_entry_index;
+    fib_table_t *fib_table;
+
+    fib_table = fib_table_get(fib_index, proto);
+
+    fib_table->ft_flags &= ~FIB_TABLE_FLAG_RESYNC;
+
+    fib_table_walk(fib_index, proto,
+                   fib_table_sweep_cb,
+                   &ctx);
+
+    vec_foreach(fib_entry_index, ctx.ftf_entries)
+    {
+        fib_table_entry_delete_index(*fib_entry_index, source);
+    }
+
+    vec_free(ctx.ftf_entries);
+}
+
 u8 *
 format_fib_table_memory (u8 *s, va_list *args)
 {
index f13dd77..74be63d 100644 (file)
@@ -40,16 +40,21 @@ typedef enum fib_table_attribute_t_ {
      * the table is for IP6 link local addresses
      */
     FIB_TABLE_ATTRIBUTE_IP6_LL = FIB_TABLE_ATTRIBUTE_FIRST,
+    /**
+     * the table is currently resync-ing
+     */
+    FIB_TABLE_ATTRIBUTE_RESYNC,
     /**
      * Marker. add new entries before this one.
      */
-    FIB_TABLE_ATTRIBUTE_LAST = FIB_TABLE_ATTRIBUTE_IP6_LL,
+    FIB_TABLE_ATTRIBUTE_LAST = FIB_TABLE_ATTRIBUTE_RESYNC,
 } fib_table_attribute_t;
 
 #define FIB_TABLE_ATTRIBUTE_MAX (FIB_TABLE_ATTRIBUTE_LAST+1)
 
 #define FIB_TABLE_ATTRIBUTES {                  \
     [FIB_TABLE_ATTRIBUTE_IP6_LL]  = "ip6-ll",   \
+    [FIB_TABLE_ATTRIBUTE_RESYNC]  = "resync",    \
 }
 
 #define FOR_EACH_FIB_TABLE_ATTRIBUTE(_item)            \
@@ -60,8 +65,11 @@ typedef enum fib_table_attribute_t_ {
 typedef enum fib_table_flags_t_ {
     FIB_TABLE_FLAG_NONE   = 0,
     FIB_TABLE_FLAG_IP6_LL  = (1 << FIB_TABLE_ATTRIBUTE_IP6_LL),
+    FIB_TABLE_FLAG_RESYNC  = (1 << FIB_TABLE_ATTRIBUTE_RESYNC),
 } __attribute__ ((packed)) fib_table_flags_t;
 
+extern u8* format_fib_table_flags(u8 *s, va_list *args);
+
 /**
  * @brief 
  *   A protocol Independent FIB table
@@ -108,6 +116,11 @@ typedef struct fib_table_t_
      */
     u32 ft_total_route_counts;
 
+    /**
+     * Epoch - number of resyncs performed
+     */
+    u32 ft_epoch;
+
     /**
      * Table description
      */
@@ -622,6 +635,46 @@ extern void fib_table_flush(u32 fib_index,
                            fib_protocol_t proto,
                            fib_source_t source);
 
+/**
+ * @brief
+ *  Resync all entries from a table for the source
+ *  this is the mark part of the mark and sweep algorithm.
+ *  All entries in this FIB that are sourced by 'source' are marked
+ *  as stale.
+ *
+ * @param fib_index
+ *  The index of the FIB
+ *
+ * @paran proto
+ *  The protocol of the entries in the table
+ *
+ * @param source
+ *  the source to flush
+ */
+extern void fib_table_mark(u32 fib_index,
+                           fib_protocol_t proto,
+                           fib_source_t source);
+
+/**
+ * @brief
+ *  Signal that the table has converged, i.e. all updates are complete.
+ *  this is the sweep part of the mark and sweep algorithm.
+ *  All entries in this FIB that are sourced by 'source' and marked
+ *  as stale are flushed.
+ *
+ * @param fib_index
+ *  The index of the FIB
+ *
+ * @paran proto
+ *  The protocol of the entries in the table
+ *
+ * @param source
+ *  the source to flush
+ */
+extern void fib_table_sweep(u32 fib_index,
+                            fib_protocol_t proto,
+                            fib_source_t source);
+
 /**
  * @brief
  *  Get the index of the FIB bound to the interface
index 0204b7a..02348af 100644 (file)
@@ -671,12 +671,14 @@ ip4_show_fib (vlib_main_t * vm,
             continue;
         }
 
-       s = format(s, "%U, fib_index:%d, flow hash:[%U] locks:[",
+       s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
                    format_fib_table_name, fib->index,
                    FIB_PROTOCOL_IP4,
                    fib->index,
                    format_ip_flow_hash_config,
-                   fib_table->ft_flow_hash_config);
+                   fib_table->ft_flow_hash_config,
+                   fib_table->ft_epoch,
+                   format_fib_table_flags, fib_table->ft_flags);
        FOR_EACH_FIB_SOURCE(source)
         {
             if (0 != fib_table->ft_locks[source])
index cecfcbd..991fbc1 100644 (file)
@@ -679,12 +679,15 @@ ip6_show_fib (vlib_main_t * vm,
         if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL)
             continue;
 
-       s = format(s, "%U, fib_index:%d, flow hash:[%U] locks:[",
+       s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
                    format_fib_table_name, fib->index,
                    FIB_PROTOCOL_IP6,
                    fib->index,
                    format_ip_flow_hash_config,
-                   fib_table->ft_flow_hash_config);
+                   fib_table->ft_flow_hash_config,
+                   fib_table->ft_epoch,
+                   format_fib_table_flags, fib_table->ft_flags);
+
        FOR_EACH_FIB_SOURCE(source)
         {
             if (0 != fib_table->ft_locks[source])
index 69c7b88..b37530e 100644 (file)
@@ -65,6 +65,59 @@ define ip_table_dump
   u32 context;
 };
 
+/** \brief IP table replace being
+
+    The use-case is that, for some unspecified reason, the control plane
+    has a very different set of entries it wants in the table than VPP
+    currently has. The CP would thus like to 'replace' VPP's current table
+    only by specifying what the new set of entries shall be, i.e. it is not
+    going to delete anything that already eixts.
+    the CP delcartes the start of this procedure with this begin_replace
+    API Call, and when it has populated all the entries it wants, it calls
+    the below end_replace API. From this point on it is of coursce free
+    to add and delete entries as usual.
+    The underlying mechanism by which VPP implements this replace is
+    purposefully left unspecified.
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table - The table to resync
+*/
+autoreply define ip_table_replace_begin
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ip_table_t table;
+};
+
+/** \brief IP table replace end
+
+    see replace start/
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table - The table that has converged
+*/
+autoreply define ip_table_replace_end
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ip_table_t table;
+};
+
+/** \brief IP table flush
+    Flush a table of all routes
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param table - The table to flush
+*/
+autoreply define ip_table_flush
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ip_table_t table;
+};
+
 /** \brief IP FIB table response
     @param context - sender context
     @param table - description of the table
@@ -957,20 +1010,6 @@ define proxy_arp_intfc_details
   u32 sw_if_index;
 };
 
-/** \brief Reset fib table request
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param vrf_id - vrf/table id of the fib table to reset
-    @param is_ipv6 - an ipv6 fib to reset if non-zero, else ipv4
-*/
-autoreply define reset_fib
-{
-  u32 client_index;
-  u32 context;
-  u32 vrf_id;
-  u8 is_ipv6;
-};
-
 /** \brief Set max allowed ARP or ip6 neighbor entries request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
index 322a3ed..0f9e370 100644 (file)
@@ -92,7 +92,9 @@ _(PROXY_ARP_ADD_DEL, proxy_arp_add_del)                                 \
 _(PROXY_ARP_DUMP, proxy_arp_dump)                                       \
 _(PROXY_ARP_INTFC_ENABLE_DISABLE, proxy_arp_intfc_enable_disable)       \
  _(PROXY_ARP_INTFC_DUMP, proxy_arp_intfc_dump)                          \
-_(RESET_FIB, reset_fib)                                                        \
+_(IP_TABLE_REPLACE_BEGIN, ip_table_replace_begin)                       \
+_(IP_TABLE_REPLACE_END, ip_table_replace_end)                           \
+_(IP_TABLE_FLUSH, ip_table_flush)                                       \
 _(IP_ROUTE_ADD_DEL, ip_route_add_del)                                   \
 _(IP_TABLE_ADD_DEL, ip_table_add_del)                                   \
 _(IP_PUNT_POLICE, ip_punt_police)                                       \
@@ -313,6 +315,7 @@ send_ip_route_details (vpe_api_main_t * am,
   }
 
   vl_api_send_msg (reg, (u8 *) mp);
+  vec_free (rpaths);
 }
 
 typedef struct apt_ip6_fib_show_ctx_t_
@@ -399,14 +402,14 @@ typedef struct vl_api_ip_mfib_dump_ctx_t_
   fib_node_index_t *entries;
 } vl_api_ip_mfib_dump_ctx_t;
 
-static int
+static walk_rc_t
 mfib_route_dump_walk (fib_node_index_t fei, void *arg)
 {
   vl_api_ip_mfib_dump_ctx_t *ctx = arg;
 
   vec_add1 (ctx->entries, fei);
 
-  return (0);
+  return (WALK_CONTINUE);
 }
 
 static void
@@ -2572,137 +2575,90 @@ static void
   REPLY_MACRO (VL_API_IP_SCAN_NEIGHBOR_ENABLE_DISABLE_REPLY);
 }
 
-static int
-ip4_reset_fib_t_handler (vl_api_reset_fib_t * mp)
+static void
+vl_api_ip_table_replace_begin_t_handler (vl_api_ip_table_replace_begin_t * mp)
 {
-  vnet_main_t *vnm = vnet_get_main ();
-  vnet_interface_main_t *im = &vnm->interface_main;
-  ip4_main_t *im4 = &ip4_main;
-  static u32 *sw_if_indices_to_shut;
-  fib_table_t *fib_table;
-  ip4_fib_t *fib;
-  u32 sw_if_index;
-  int i;
-  int rv = VNET_API_ERROR_NO_SUCH_FIB;
-  u32 target_fib_id = ntohl (mp->vrf_id);
-
-  /* *INDENT-OFF* */
-  pool_foreach (fib_table, im4->fibs,
-  ({
-    vnet_sw_interface_t * si;
-
-    fib = pool_elt_at_index (im4->v4_fibs, fib_table->ft_index);
-
-    if (fib->table_id != target_fib_id)
-      continue;
-
-    /* remove any mpls encap/decap labels */
-    mpls_fib_reset_labels (fib->table_id);
-
-    /* remove any proxy arps in this fib */
-    vnet_proxy_arp_fib_reset (fib->table_id);
-
-    /* Set the flow hash for this fib to the default */
-    vnet_set_ip4_flow_hash (fib->table_id, IP_FLOW_HASH_DEFAULT);
-
-    vec_reset_length (sw_if_indices_to_shut);
-
-    /* Shut down interfaces in this FIB / clean out intfc routes */
-    pool_foreach (si, im->sw_interfaces,
-    ({
-      u32 sw_if_index = si->sw_if_index;
-
-      if (sw_if_index < vec_len (im4->fib_index_by_sw_if_index)
-          && (im4->fib_index_by_sw_if_index[si->sw_if_index] ==
-              fib->index))
-        vec_add1 (sw_if_indices_to_shut, si->sw_if_index);
-    }));
+  vl_api_ip_table_replace_begin_reply_t *rmp;
+  fib_protocol_t fproto;
+  u32 fib_index;
+  int rv = 0;
 
-    for (i = 0; i < vec_len (sw_if_indices_to_shut); i++) {
-      sw_if_index = sw_if_indices_to_shut[i];
+  fproto = (mp->table.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
+  fib_index = fib_table_find (fproto, ntohl (mp->table.table_id));
 
-      u32 flags = vnet_sw_interface_get_flags (vnm, sw_if_index);
-      flags &= ~(VNET_SW_INTERFACE_FLAG_ADMIN_UP);
-      vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
+  if (INDEX_INVALID == fib_index)
+    rv = VNET_API_ERROR_NO_SUCH_FIB;
+  else
+    {
+      fib_table_mark (fib_index, fproto, FIB_SOURCE_API);
+      mfib_table_mark (mfib_table_find (fproto, ntohl (mp->table.table_id)),
+                      fproto, MFIB_SOURCE_API);
     }
-
-    fib_table_flush(fib->index, FIB_PROTOCOL_IP4, FIB_SOURCE_API);
-
-    rv = 0;
-    break;
-    })); /* pool_foreach (fib) */
-    /* *INDENT-ON* */
-
-  return rv;
+  REPLY_MACRO (VL_API_IP_TABLE_REPLACE_BEGIN_REPLY);
 }
 
-static int
-ip6_reset_fib_t_handler (vl_api_reset_fib_t * mp)
+static void
+vl_api_ip_table_replace_end_t_handler (vl_api_ip_table_replace_end_t * mp)
 {
-  vnet_main_t *vnm = vnet_get_main ();
-  vnet_interface_main_t *im = &vnm->interface_main;
-  ip6_main_t *im6 = &ip6_main;
-  static u32 *sw_if_indices_to_shut;
-  fib_table_t *fib_table;
-  ip6_fib_t *fib;
-  u32 sw_if_index;
-  int i;
-  int rv = VNET_API_ERROR_NO_SUCH_FIB;
-  u32 target_fib_id = ntohl (mp->vrf_id);
-
-  /* *INDENT-OFF* */
-  pool_foreach (fib_table, im6->fibs,
-  ({
-    vnet_sw_interface_t * si;
-
-    fib = pool_elt_at_index (im6->v6_fibs, fib_table->ft_index);
-
-    if (fib->table_id != target_fib_id)
-      continue;
-
-    vec_reset_length (sw_if_indices_to_shut);
-
-    /* Set the flow hash for this fib to the default */
-    vnet_set_ip6_flow_hash (fib->table_id, IP_FLOW_HASH_DEFAULT);
-
-    /* Shut down interfaces in this FIB / clean out intfc routes */
-    pool_foreach (si, im->sw_interfaces,
-    ({
-      if (im6->fib_index_by_sw_if_index[si->sw_if_index] ==
-          fib->index)
-        vec_add1 (sw_if_indices_to_shut, si->sw_if_index);
-    }));
+  vl_api_ip_table_replace_end_reply_t *rmp;
+  fib_protocol_t fproto;
+  u32 fib_index;
+  int rv = 0;
 
-    for (i = 0; i < vec_len (sw_if_indices_to_shut); i++) {
-      sw_if_index = sw_if_indices_to_shut[i];
+  fproto = (mp->table.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
+  fib_index = fib_table_find (fproto, ntohl (mp->table.table_id));
 
-      u32 flags = vnet_sw_interface_get_flags (vnm, sw_if_index);
-      flags &= ~(VNET_SW_INTERFACE_FLAG_ADMIN_UP);
-      vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
+  if (INDEX_INVALID == fib_index)
+    rv = VNET_API_ERROR_NO_SUCH_FIB;
+  else
+    {
+      fib_table_sweep (fib_index, fproto, FIB_SOURCE_API);
+      mfib_table_sweep (mfib_table_find
+                       (fproto, ntohl (mp->table.table_id)), fproto,
+                       MFIB_SOURCE_API);
     }
-
-    fib_table_flush(fib->index, FIB_PROTOCOL_IP6, FIB_SOURCE_API);
-
-    rv = 0;
-    break;
-  })); /* pool_foreach (fib) */
-  /* *INDENT-ON* */
-
-  return rv;
+  REPLY_MACRO (VL_API_IP_TABLE_REPLACE_END_REPLY);
 }
 
 static void
-vl_api_reset_fib_t_handler (vl_api_reset_fib_t * mp)
+vl_api_ip_table_flush_t_handler (vl_api_ip_table_flush_t * mp)
 {
-  int rv;
-  vl_api_reset_fib_reply_t *rmp;
+  vl_api_ip_table_flush_reply_t *rmp;
+  fib_protocol_t fproto;
+  u32 fib_index;
+  int rv = 0;
 
-  if (mp->is_ipv6)
-    rv = ip6_reset_fib_t_handler (mp);
+  fproto = (mp->table.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
+  fib_index = fib_table_find (fproto, ntohl (mp->table.table_id));
+
+  if (INDEX_INVALID == fib_index)
+    rv = VNET_API_ERROR_NO_SUCH_FIB;
   else
-    rv = ip4_reset_fib_t_handler (mp);
+    {
+      vnet_main_t *vnm = vnet_get_main ();
+      vnet_interface_main_t *im = &vnm->interface_main;
+      vnet_sw_interface_t *si;
+
+      /* Shut down interfaces in this FIB / clean out intfc routes */
+      /* *INDENT-OFF* */
+      pool_foreach (si, im->sw_interfaces,
+      ({
+        if (fib_index == fib_table_get_index_for_sw_if_index (fproto,
+                                                              si->sw_if_index))
+          {
+            u32 flags = si->flags;
+            flags &= ~VNET_SW_INTERFACE_FLAG_ADMIN_UP;
+            vnet_sw_interface_set_flags (vnm, si->sw_if_index, flags);
+          }
+      }));
+      /* *INDENT-ON* */
+
+      fib_table_flush (fib_index, fproto, FIB_SOURCE_API);
+      mfib_table_flush (mfib_table_find (fproto, ntohl (mp->table.table_id)),
+                       fproto, MFIB_SOURCE_API);
+    }
 
-  REPLY_MACRO (VL_API_RESET_FIB_REPLY);
+  REPLY_MACRO (VL_API_IP_TABLE_FLUSH_REPLY);
 }
 
 static void
index 1d872f9..4da1be6 100644 (file)
@@ -506,9 +506,10 @@ ip4_show_mfib (vlib_main_t * vm,
             continue;
         }
 
-        vlib_cli_output (vm, "%U, fib_index %d",
+        vlib_cli_output (vm, "%U, fib_index:%d flags:%U",
                          format_mfib_table_name, mfib->index, FIB_PROTOCOL_IP4,
-                         mfib->index);
+                         mfib->index,
+                         format_mfib_table_flags, mfib_table->mft_flags);
 
         /* Show summary? */
         if (! verbose)
index 5b15c8d..690f4ed 100644 (file)
@@ -562,14 +562,14 @@ typedef struct ip6_mfib_show_ctx_t_ {
 } ip6_mfib_show_ctx_t;
 
 
-static int
+static walk_rc_t
 ip6_mfib_table_collect_entries (fib_node_index_t mfei, void *arg)
 {
     ip6_mfib_show_ctx_t *ctx = arg;
 
     vec_add1(ctx->entries, mfei);
 
-    return (0);
+    return (WALK_CONTINUE);
 }
 
 static void
@@ -609,7 +609,7 @@ typedef struct ip6_mfib_walk_ctx_t_
     void *i6w_ctx;
 } ip6_mfib_walk_ctx_t;
 
-static int
+static void
 ip6_mfib_walk_cb (clib_bihash_kv_40_8_t * kvp,
                  void *arg)
 {
@@ -617,9 +617,8 @@ ip6_mfib_walk_cb (clib_bihash_kv_40_8_t * kvp,
 
     if ((kvp->key[4] >> 32) == ctx->i6w_mfib_index)
     {
-        return (ctx->i6w_fn(kvp->value, ctx->i6w_ctx));
+        ctx->i6w_fn(kvp->value, ctx->i6w_ctx);
     }
-    return (FIB_TABLE_WALK_CONTINUE);
 }
 
 void
index f169dc0..448e6c5 100644 (file)
@@ -38,6 +38,7 @@ static mfib_path_ext_t *mfib_path_ext_pool;
  * String names for each source
  */
 static const char *mfib_source_names[] = MFIB_SOURCE_NAMES;
+static const char *mfib_src_attribute_names[] = MFIB_ENTRY_SRC_ATTRIBUTES;
 
 /*
  * Pool for all fib_entries
@@ -101,6 +102,26 @@ format_mfib_entry_path_ext (u8 * s, va_list * args)
                    format_mfib_itf_flags, path_ext->mfpe_flags));
 }
 
+u8 *
+format_mfib_entry_src_flags (u8 *s, va_list *args)
+{
+    mfib_entry_src_attribute_t sattr;
+    mfib_entry_src_flags_t flag = va_arg(*args, int);
+
+    if (!flag)
+    {
+        return format(s, "none");
+    }
+
+    FOR_EACH_MFIB_SRC_ATTRIBUTE(sattr) {
+        if ((1 << sattr) & flag) {
+            s = format (s, "%s,", mfib_src_attribute_names[sattr]);
+        }
+    }
+
+    return (s);
+}
+
 u8 *
 format_mfib_entry (u8 * s, va_list * args)
 {
@@ -127,14 +148,15 @@ format_mfib_entry (u8 * s, va_list * args)
         s = format (s, " locks:%d\n", mfib_entry->mfe_node.fn_locks);
         vec_foreach(msrc, mfib_entry->mfe_srcs)
         {
-            s = format (s, "  src:%s locks:%d:",
+            s = format (s, "  src:%s flags:%U locks:%d:",
                         mfib_source_names[msrc->mfes_src],
+                        format_mfib_entry_src_flags, msrc->mfes_flags,
                         msrc->mfes_ref_count);
             if (msrc->mfes_cover != FIB_NODE_INDEX_INVALID)
             {
                 s = format (s, " cover:%d", msrc->mfes_cover);
             }
-            s = format (s, " %U\n", format_mfib_entry_flags, msrc->mfes_flags);
+            s = format (s, " %U\n", format_mfib_entry_flags, msrc->mfes_route_flags);
             if (FIB_NODE_INDEX_INVALID != msrc->mfes_pl)
             {
                 s = fib_path_list_format(msrc->mfes_pl, s);
@@ -201,7 +223,7 @@ mfib_entry_src_init (mfib_entry_t *mfib_entry,
 {
     mfib_entry_src_t esrc = {
         .mfes_pl = FIB_NODE_INDEX_INVALID,
-        .mfes_flags = MFIB_ENTRY_FLAG_NONE,
+        .mfes_route_flags = MFIB_ENTRY_FLAG_NONE,
         .mfes_src = source,
         .mfes_cover = FIB_NODE_INDEX_INVALID,
         .mfes_sibling = FIB_NODE_INDEX_INVALID,
@@ -215,8 +237,8 @@ mfib_entry_src_init (mfib_entry_t *mfib_entry,
 
 static mfib_entry_src_t *
 mfib_entry_src_find (const mfib_entry_t *mfib_entry,
-                    mfib_source_t source,
-                    u32 *index)
+                     mfib_source_t source,
+                     u32 *index)
 
 {
     mfib_entry_src_t *esrc;
@@ -269,8 +291,9 @@ mfib_entry_src_update (mfib_entry_t *mfib_entry,
 
     msrc = mfib_entry_src_find_or_create(mfib_entry, source);
 
-    msrc->mfes_flags = entry_flags;
+    msrc->mfes_route_flags = entry_flags;
     msrc->mfes_rpf_id = rpf_id;
+    msrc->mfes_flags &= ~MFIB_ENTRY_SRC_FLAG_STALE;
 
     return (msrc);
 }
@@ -286,6 +309,7 @@ mfib_entry_src_update_and_lock (mfib_entry_t *mfib_entry,
     msrc = mfib_entry_src_update(mfib_entry, source, rpf_id, entry_flags);
 
     msrc->mfes_ref_count++;
+    msrc->mfes_flags &= ~MFIB_ENTRY_SRC_FLAG_STALE;
 
     return (msrc);
 }
@@ -331,6 +355,44 @@ mfib_entry_is_sourced (fib_node_index_t mfib_entry_index,
     return (NULL != mfib_entry_src_find(mfib_entry, source, NULL));
 }
 
+int
+mfib_entry_is_marked (fib_node_index_t mfib_entry_index,
+                      mfib_source_t source)
+{
+    mfib_entry_t *mfib_entry;
+    mfib_entry_src_t *esrc;
+
+    mfib_entry = mfib_entry_get(mfib_entry_index);
+
+    esrc = mfib_entry_src_find(mfib_entry, source, NULL);
+
+    if (NULL == esrc)
+    {
+        return (0);
+    }
+    else
+    {
+        return (!!(esrc->mfes_flags & MFIB_ENTRY_SRC_FLAG_STALE));
+    }
+}
+
+void
+mfib_entry_mark (fib_node_index_t fib_entry_index,
+                 mfib_source_t source)
+{
+    mfib_entry_t *mfib_entry;
+    mfib_entry_src_t *esrc;
+
+    mfib_entry = mfib_entry_get(fib_entry_index);
+
+    esrc = mfib_entry_src_find(mfib_entry, source, NULL);
+
+    if (NULL != esrc)
+    {
+        esrc->mfes_flags |= MFIB_ENTRY_SRC_FLAG_STALE;
+    }
+}
+
 int
 mfib_entry_is_host (fib_node_index_t mfib_entry_index)
 {
@@ -578,7 +640,7 @@ mfib_entry_stack (mfib_entry_t *mfib_entry,
          * updates to recalculate forwarding.
          */
         mfib_entry->mfe_pl = msrc->mfes_pl;
-        mfib_entry->mfe_flags = msrc->mfes_flags;
+        mfib_entry->mfe_flags = msrc->mfes_route_flags;
         mfib_entry->mfe_itfs = msrc->mfes_itfs;
         mfib_entry->mfe_rpf_id = msrc->mfes_rpf_id;
 
@@ -677,7 +739,9 @@ static fib_node_index_t*
 mfib_entry_src_paths_add (mfib_entry_src_t *msrc,
                           const fib_route_path_t *rpaths)
 {
-    ASSERT(!(MFIB_ENTRY_FLAG_EXCLUSIVE & msrc->mfes_flags));
+    ASSERT(!(MFIB_ENTRY_FLAG_EXCLUSIVE & msrc->mfes_route_flags));
+
+    msrc->mfes_flags &= ~MFIB_ENTRY_SRC_FLAG_STALE;
 
     if (FIB_NODE_INDEX_INVALID == msrc->mfes_pl)
     {
@@ -694,7 +758,9 @@ static fib_node_index_t*
 mfib_entry_src_paths_remove (mfib_entry_src_t *msrc,
                              const fib_route_path_t *rpaths)
 {
-    ASSERT(!(MFIB_ENTRY_FLAG_EXCLUSIVE & msrc->mfes_flags));
+    ASSERT(!(MFIB_ENTRY_FLAG_EXCLUSIVE & msrc->mfes_route_flags));
+
+    msrc->mfes_flags &= ~MFIB_ENTRY_SRC_FLAG_STALE;
 
     return (fib_path_list_paths_remove(msrc->mfes_pl, rpaths));
 }
@@ -792,11 +858,11 @@ static int
 mfib_entry_src_ok_for_delete (const mfib_entry_src_t *msrc)
 {
     return ((INDEX_INVALID == msrc->mfes_cover &&
-             MFIB_ENTRY_FLAG_NONE == msrc->mfes_flags &&
+             MFIB_ENTRY_FLAG_NONE == msrc->mfes_route_flags &&
              0 == fib_path_list_get_n_paths(msrc->mfes_pl)) &&
             (0 == hash_elts(msrc->mfes_itfs)));
 
-    /* return ((MFIB_ENTRY_FLAG_NONE == msrc->mfes_flags) && */
+    /* return ((MFIB_ENTRY_FLAG_NONE == msrc->mfes_route_flags) && */
     /*         (0 == fib_path_list_get_n_paths(msrc->mfes_pl)) && */
     /*         (0 == hash_elts(msrc->mfes_itfs))); */
 }
index 4a1121b..7087fe8 100644 (file)
@@ -161,6 +161,8 @@ extern u32 mfib_entry_get_fib_index(fib_node_index_t fib_entry_index);
 extern int mfib_entry_is_sourced(fib_node_index_t fib_entry_index,
                                  mfib_source_t source);
 extern int mfib_entry_is_host(fib_node_index_t fib_entry_index);
+extern int mfib_entry_is_marked(fib_node_index_t fib_entry_index, mfib_source_t source);
+extern void mfib_entry_mark(fib_node_index_t fib_entry_index, mfib_source_t source);
 extern u32 mfib_entry_get_stats_index(fib_node_index_t fib_entry_index);
 extern void mfib_entry_cover_changed(fib_node_index_t fib_entry_index);
 extern void mfib_entry_cover_updated(fib_node_index_t fib_entry_index);
index 86752e0..b85c010 100644 (file)
@@ -27,6 +27,48 @@ typedef struct mfib_path_ext_t_
     fib_node_index_t mfpe_path;
 } mfib_path_ext_t;
 
+/**
+ * Flags for the source data
+ */
+typedef enum mfib_entry_src_attribute_t_ {
+    /**
+     * Marker. Add new values after this one.
+     */
+    MFIB_ENTRY_SRC_ATTRIBUTE_FIRST,
+    /**
+     * the source has been added to the entry
+     */
+    MFIB_ENTRY_SRC_ATTRIBUTE_STALE = MFIB_ENTRY_SRC_ATTRIBUTE_FIRST,
+    /**
+     * Marker. add new entries before this one.
+     */
+    MFIB_ENTRY_SRC_ATTRIBUTE_LAST = MFIB_ENTRY_SRC_ATTRIBUTE_STALE,
+} mfib_entry_src_attribute_t;
+
+
+#define MFIB_ENTRY_SRC_ATTRIBUTES {                  \
+    [MFIB_ENTRY_SRC_ATTRIBUTE_STALE] = "stale",      \
+}
+
+#define FOR_EACH_MFIB_SRC_ATTRIBUTE(_item)                      \
+    for (_item = MFIB_ENTRY_SRC_ATTRIBUTE_FIRST;               \
+        _item <= MFIB_ENTRY_SRC_ATTRIBUTE_LAST;                \
+        _item++)
+
+typedef enum mfib_entry_src_flag_t_ {
+    MFIB_ENTRY_SRC_FLAG_NONE   = 0,
+    MFIB_ENTRY_SRC_FLAG_STALE = (1 << MFIB_ENTRY_SRC_ATTRIBUTE_STALE),
+} __attribute__ ((packed)) mfib_entry_src_flags_t;
+
+extern u8 * format_mfib_entry_src_flags(u8 *s, va_list *args);
+
+/*
+ * Keep the size of the flags field to 2 bytes, so it
+ * can be placed next to the 2 bytes reference count
+ */
+STATIC_ASSERT (sizeof(mfib_entry_src_flags_t) <= 2,
+              "FIB entry flags field size too big");
+
 /**
  * The source of an MFIB entry
  */
@@ -40,7 +82,12 @@ typedef struct mfib_entry_src_t_
     /**
      * Route flags
      */
-    mfib_entry_flags_t mfes_flags;
+    mfib_entry_flags_t mfes_route_flags;
+
+    /**
+     * Source flags
+     */
+    mfib_entry_src_flags_t mfes_flags;
 
     /**
      * The reference count on the entry. this is a u32
index 4512f74..a6a1e0d 100644 (file)
@@ -62,7 +62,7 @@ mfib_entry_src_rr_activiate (mfib_entry_t *mfib_entry,
 
     msrc->mfes_pl = csrc->mfes_pl;
     fib_path_list_lock(msrc->mfes_pl);
-    msrc->mfes_flags = csrc->mfes_flags;
+    msrc->mfes_route_flags = csrc->mfes_route_flags;
     msrc->mfes_itfs = csrc->mfes_itfs;
     msrc->mfes_exts = csrc->mfes_exts;
     msrc->mfes_rpf_id = csrc->mfes_rpf_id;
@@ -91,7 +91,7 @@ mfib_entry_src_rr_cover_update (mfib_entry_t *mfib_entry,
 
     cover = mfib_entry_get(msrc->mfes_cover);
 
-    msrc->mfes_flags = cover->mfe_flags;
+    msrc->mfes_route_flags = cover->mfe_flags;
     msrc->mfes_itfs = cover->mfe_itfs;
     msrc->mfes_rpf_id = cover->mfe_rpf_id;
 
index 504333a..a6a8277 100644 (file)
@@ -24,6 +24,8 @@
 #include <vnet/mfib/mfib_entry_cover.h>
 #include <vnet/mfib/mfib_signal.h>
 
+const static char * mfib_table_flags_strings[] = MFIB_TABLE_ATTRIBUTES;
+
 mfib_table_t *
 mfib_table_get (fib_node_index_t index,
                 fib_protocol_t proto)
@@ -643,7 +645,7 @@ typedef struct mfib_table_flush_ctx_t_
     mfib_source_t mftf_source;
 } mfib_table_flush_ctx_t;
 
-static int
+static walk_rc_t
 mfib_table_flush_cb (fib_node_index_t mfib_entry_index,
                      void *arg)
 {
@@ -653,7 +655,7 @@ mfib_table_flush_cb (fib_node_index_t mfib_entry_index,
     {
         vec_add1(ctx->mftf_entries, mfib_entry_index);
     }
-    return (1);
+    return (WALK_CONTINUE);
 }
 
 void
@@ -679,6 +681,79 @@ mfib_table_flush (u32 mfib_index,
     vec_free(ctx.mftf_entries);
 }
 
+static walk_rc_t
+mfib_table_mark_cb (fib_node_index_t fib_entry_index,
+                   void *arg)
+{
+    mfib_table_flush_ctx_t *ctx = arg;
+
+    if (mfib_entry_is_sourced(fib_entry_index, ctx->mftf_source))
+    {
+        mfib_entry_mark(fib_entry_index, ctx->mftf_source);
+    }
+    return (WALK_CONTINUE);
+}
+
+void
+mfib_table_mark (u32 fib_index,
+                 fib_protocol_t proto,
+                 mfib_source_t source)
+{
+    mfib_table_flush_ctx_t ctx = {
+        .mftf_source = source,
+    };
+    mfib_table_t *mfib_table;
+
+    mfib_table = mfib_table_get(fib_index, proto);
+
+    mfib_table->mft_epoch++;
+    mfib_table->mft_flags |= MFIB_TABLE_FLAG_RESYNC;
+
+    mfib_table_walk(fib_index, proto,
+                   mfib_table_mark_cb,
+                   &ctx);
+}
+
+static walk_rc_t
+mfib_table_sweep_cb (fib_node_index_t fib_entry_index,
+                    void *arg)
+{
+    mfib_table_flush_ctx_t *ctx = arg;
+
+    if (mfib_entry_is_marked(fib_entry_index, ctx->mftf_source))
+    {
+        vec_add1(ctx->mftf_entries, fib_entry_index);
+    }
+    return (WALK_CONTINUE);
+}
+
+void
+mfib_table_sweep (u32 fib_index,
+                  fib_protocol_t proto,
+                  mfib_source_t source)
+{
+    mfib_table_flush_ctx_t ctx = {
+        .mftf_source = source,
+    };
+    fib_node_index_t *fib_entry_index;
+    mfib_table_t *mfib_table;
+
+    mfib_table = mfib_table_get(fib_index, proto);
+
+    mfib_table->mft_flags &= ~MFIB_TABLE_FLAG_RESYNC;
+
+    mfib_table_walk(fib_index, proto,
+                    mfib_table_sweep_cb,
+                    &ctx);
+
+    vec_foreach(fib_entry_index, ctx.mftf_entries)
+    {
+        mfib_table_entry_delete_index(*fib_entry_index, source);
+    }
+
+    vec_free(ctx.mftf_entries);
+}
+
 static void
 mfib_table_destroy (mfib_table_t *mfib_table)
 {
@@ -769,6 +844,26 @@ mfib_table_walk (u32 fib_index,
     }
 }
 
+u8*
+format_mfib_table_flags (u8 *s, va_list *args)
+{
+    mfib_table_flags_t flags = va_arg(*args, int);
+    mfib_table_attribute_t attr;
+
+    if (!flags)
+    {
+        return format(s, "none");
+    }
+
+    FOR_EACH_MFIB_TABLE_ATTRIBUTE(attr) {
+        if (1 << attr & flags) {
+            s = format(s, "%s", mfib_table_flags_strings[attr]);
+        }
+    }
+
+    return (s);
+}
+
 u8*
 format_mfib_table_name (u8* s, va_list *ap)
 {
index 4746137..d6b6dc3 100644 (file)
 #define MFIB_TABLE_N_LOCKS (MFIB_N_SOURCES+1)
 #define MFIB_TABLE_TOTAL_LOCKS MFIB_N_SOURCES
 
+/**
+ * Flags for the source data
+ */
+typedef enum mfib_table_attribute_t_ {
+    /**
+     * Marker. Add new values after this one.
+     */
+    MFIB_TABLE_ATTRIBUTE_FIRST,
+    /**
+     * the table is currently resync-ing
+     */
+    MFIB_TABLE_ATTRIBUTE_RESYNC = MFIB_TABLE_ATTRIBUTE_FIRST,
+    /**
+     * Marker. add new entries before this one.
+     */
+    MFIB_TABLE_ATTRIBUTE_LAST = MFIB_TABLE_ATTRIBUTE_RESYNC,
+} mfib_table_attribute_t;
+
+#define MFIB_TABLE_ATTRIBUTE_MAX (MFIB_TABLE_ATTRIBUTE_LAST+1)
+
+#define MFIB_TABLE_ATTRIBUTES {                         \
+    [MFIB_TABLE_ATTRIBUTE_RESYNC]  = "resync",    \
+}
+
+#define FOR_EACH_MFIB_TABLE_ATTRIBUTE(_item)           \
+    for (_item = MFIB_TABLE_ATTRIBUTE_FIRST;           \
+        _item < MFIB_TABLE_ATTRIBUTE_MAX;              \
+        _item++)
+
+typedef enum mfib_table_flags_t_ {
+    MFIB_TABLE_FLAG_NONE   = 0,
+    MFIB_TABLE_FLAG_RESYNC  = (1 << MFIB_TABLE_ATTRIBUTE_RESYNC),
+} __attribute__ ((packed)) mfib_table_flags_t;
+
+extern u8* format_mfib_table_flags(u8 *s, va_list *args);
+
 /**
  * @brief
  *   A protocol Independent IP multicast FIB table
@@ -55,6 +91,11 @@ typedef struct mfib_table_t_
      */
     fib_protocol_t mft_proto;
 
+    /**
+     * table falgs
+     */
+    mfib_table_flags_t mft_flags;
+
     /**
      * number of locks on the table
      */
@@ -65,6 +106,11 @@ typedef struct mfib_table_t_
      */
     u32 mft_table_id;
 
+    /**
+     * resync epoch
+     */
+    u32 mft_epoch;
+
     /**
      * Index into FIB vector.
      */
@@ -280,6 +326,46 @@ extern void mfib_table_flush(u32 fib_index,
                              fib_protocol_t proto,
                              mfib_source_t source);
 
+/**
+ * @brief
+ *  Resync all entries from a table for the source
+ *  this is the mark part of the mark and sweep algorithm.
+ *  All entries in this FIB that are sourced by 'source' are marked
+ *  as stale.
+ *
+ * @param fib_index
+ *  The index of the FIB
+ *
+ * @paran proto
+ *  The protocol of the entries in the table
+ *
+ * @param source
+ *  the source to flush
+ */
+extern void mfib_table_mark(u32 fib_index,
+                            fib_protocol_t proto,
+                            mfib_source_t source);
+
+/**
+ * @brief
+ *  Signal that the table has converged, i.e. all updates are complete.
+ *  this is the sweep part of the mark and sweep algorithm.
+ *  All entries in this FIB that are sourced by 'source' and marked
+ *  as stale are flushed.
+ *
+ * @param fib_index
+ *  The index of the FIB
+ *
+ * @paran proto
+ *  The protocol of the entries in the table
+ *
+ * @param source
+ *  the source to flush
+ */
+extern void mfib_table_sweep(u32 fib_index,
+                             fib_protocol_t proto,
+                             mfib_source_t source);
+
 /**
  * @brief
  *  Get the index of the FIB bound to the interface
@@ -465,8 +551,8 @@ extern mfib_table_t *mfib_table_get(fib_node_index_t index,
 /**
  * @brief Call back function when walking entries in a FIB table
  */
-typedef int (*mfib_table_walk_fn_t)(fib_node_index_t fei,
-                                    void *ctx);
+typedef walk_rc_t (*mfib_table_walk_fn_t)(fib_node_index_t fei,
+                                          void *ctx);
 
 /**
  * @brief Walk all entries in a FIB table
index 05361de..4076a89 100644 (file)
@@ -443,13 +443,6 @@ VLIB_CLI_COMMAND (mpls_table_command, static) = {
   .is_mp_safe = 1,
 };
 
-int
-mpls_fib_reset_labels (u32 fib_id)
-{
-  // FIXME
-  return 0;
-}
-
 static clib_error_t *
 mpls_init (vlib_main_t * vm)
 {
index 918a3d8..2cd41ea 100644 (file)
@@ -90,8 +90,6 @@ int mpls_sw_interface_enable_disable (mpls_main_t * mm,
 
 u8 mpls_sw_interface_is_enabled (u32 sw_if_index);
 
-int mpls_fib_reset_labels (u32 fib_id);
-
 int mpls_dest_cmp (void *a1, void *a2);
 
 int mpls_fib_index_cmp (void *a1, void *a2);
index 83761bf..2424efc 100644 (file)
@@ -1021,17 +1021,42 @@ static void *vl_api_delete_subif_t_print
 }
 
 static void *
-vl_api_reset_fib_t_print (vl_api_reset_fib_t * mp, void *handle)
+vl_api_ip_table_replace_begin_t_print (vl_api_ip_table_replace_begin_t * mp,
+                                      void *handle)
 {
   u8 *s;
 
-  s = format (0, "SCRIPT: reset_fib ");
+  s = format (0, "SCRIPT: ip_table_replace_begin ");
 
-  if (mp->vrf_id)
-    s = format (s, "vrf %d ", (mp->vrf_id));
+  s = format (s, "v%s-table %d ",
+             mp->table.is_ip6 ? "6" : "4", (mp->table.table_id));
 
-  if (mp->is_ipv6 != 0)
-    s = format (s, "ipv6 ");
+  FINISH;
+}
+
+static void *
+vl_api_ip_table_flush_t_print (vl_api_ip_table_flush_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ip_table_flush ");
+
+  s = format (s, "v%s-table %d ",
+             mp->table.is_ip6 ? "6" : "4", (mp->table.table_id));
+
+  FINISH;
+}
+
+static void *
+vl_api_ip_table_replace_end_t_print (vl_api_ip_table_replace_end_t * mp,
+                                    void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: ip_table_replace_end ");
+
+  s = format (s, "v%s-table %d ",
+             mp->table.is_ip6 ? "6" : "4", (mp->table.table_id));
 
   FINISH;
 }
@@ -3737,7 +3762,9 @@ _(SW_INTERFACE_SET_UNNUMBERED, sw_interface_set_unnumbered)             \
 _(IP_NEIGHBOR_ADD_DEL, ip_neighbor_add_del)                             \
 _(CREATE_VLAN_SUBIF, create_vlan_subif)                                 \
 _(CREATE_SUBIF, create_subif)                                           \
-_(RESET_FIB, reset_fib)                                                 \
+_(IP_TABLE_REPLACE_BEGIN, ip_table_replace_begin)                       \
+_(IP_TABLE_FLUSH, ip_table_flush)                                       \
+_(IP_TABLE_REPLACE_END, ip_table_replace_end)                           \
 _(SET_IP_FLOW_HASH, set_ip_flow_hash)                                   \
 _(SW_INTERFACE_IP6ND_RA_PREFIX, sw_interface_ip6nd_ra_prefix)           \
 _(SW_INTERFACE_IP6ND_RA_CONFIG, sw_interface_ip6nd_ra_config)           \
index 5c268a8..7300679 100644 (file)
@@ -16,7 +16,7 @@ from util import ppp
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \
     VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
     VppMplsTable, VppIpTable, FibPathType, find_route, \
-    VppIpInterfaceAddress
+    VppIpInterfaceAddress, find_route_in_dump, find_mroute_in_dump
 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
 from vpp_papi import VppEnum
 from vpp_neighbor import VppNeighbor
@@ -1961,5 +1961,135 @@ class TestIPv4Frag(VppTestCase):
         self.assert_equal(payload, saved_payload, "payload")
 
 
+class TestIPReplace(VppTestCase):
+    """ IPv4 Table Replace """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestIPReplace, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestIPReplace, cls).tearDownClass()
+
+    def setUp(self):
+        super(TestIPReplace, self).setUp()
+
+        self.create_pg_interfaces(range(4))
+
+        table_id = 1
+        self.tables = []
+
+        for i in self.pg_interfaces:
+            i.admin_up()
+            i.config_ip4()
+            i.resolve_arp()
+            i.generate_remote_hosts(2)
+            self.tables.append(VppIpTable(self, table_id).add_vpp_config())
+            table_id += 1
+
+    def tearDown(self):
+        super(TestIPReplace, self).tearDown()
+        for i in self.pg_interfaces:
+            i.admin_down()
+            i.unconfig_ip4()
+
+    def test_replace(self):
+        """ IP Table Replace """
+
+        N_ROUTES = 20
+        links = [self.pg0, self.pg1, self.pg2, self.pg3]
+        routes = [[], [], [], []]
+
+        # load up the tables with some routes
+        for ii, t in enumerate(self.tables):
+            for jj in range(N_ROUTES):
+                uni = VppIpRoute(
+                    self, "10.0.0.%d" % jj, 32,
+                    [VppRoutePath(links[ii].remote_hosts[0].ip4,
+                                  links[ii].sw_if_index),
+                     VppRoutePath(links[ii].remote_hosts[1].ip4,
+                                  links[ii].sw_if_index)],
+                    table_id=t.table_id).add_vpp_config()
+                multi = VppIpMRoute(
+                    self, "0.0.0.0",
+                    "239.0.0.%d" % jj, 32,
+                    MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+                    [VppMRoutePath(self.pg0.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+                     VppMRoutePath(self.pg1.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+                     VppMRoutePath(self.pg2.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
+                     VppMRoutePath(self.pg3.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
+                    table_id=t.table_id).add_vpp_config()
+                routes[ii].append({'uni': uni,
+                                   'multi': multi})
+
+        #
+        # replace the tables a few times
+        #
+        for kk in range(3):
+            # replace_begin each table
+            for t in self.tables:
+                t.replace_begin()
+
+            # all the routes are still there
+            for ii, t in enumerate(self.tables):
+                dump = t.dump()
+                mdump = t.mdump()
+                for r in routes[ii]:
+                    self.assertTrue(find_route_in_dump(dump, r['uni'], t))
+                    self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
+
+            # redownload the even numbered routes
+            for ii, t in enumerate(self.tables):
+                for jj in range(0, N_ROUTES, 2):
+                    routes[ii][jj]['uni'].add_vpp_config()
+                    routes[ii][jj]['multi'].add_vpp_config()
+
+            # signal each table replace_end
+            for t in self.tables:
+                t.replace_end()
+
+            # we should find the even routes, but not the odd
+            for ii, t in enumerate(self.tables):
+                dump = t.dump()
+                mdump = t.mdump()
+                for jj in range(0, N_ROUTES, 2):
+                    self.assertTrue(find_route_in_dump(
+                        dump, routes[ii][jj]['uni'], t))
+                    self.assertTrue(find_mroute_in_dump(
+                        mdump, routes[ii][jj]['multi'], t))
+                for jj in range(1, N_ROUTES - 1, 2):
+                    self.assertFalse(find_route_in_dump(
+                        dump, routes[ii][jj]['uni'], t))
+                    self.assertFalse(find_mroute_in_dump(
+                        mdump, routes[ii][jj]['multi'], t))
+
+            # reload all the routes
+            for ii, t in enumerate(self.tables):
+                for r in routes[ii]:
+                    r['uni'].add_vpp_config()
+                    r['multi'].add_vpp_config()
+
+            # all the routes are still there
+            for ii, t in enumerate(self.tables):
+                dump = t.dump()
+                mdump = t.mdump()
+                for r in routes[ii]:
+                    self.assertTrue(find_route_in_dump(dump, r['uni'], t))
+                    self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
+
+        #
+        # finally flush the tables for good measure
+        #
+        for t in self.tables:
+            t.flush()
+            self.assertEqual(len(t.dump()), 5)
+            self.assertEqual(len(t.mdump()), 1)
+
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index f1ad670..fba00eb 100644 (file)
@@ -184,7 +184,7 @@ class TestIp4VrfMultiInst(VppTestCase):
             pg_if = self.pg_if_by_vrf_id[vrf_id][0]
             dest_addr = pg_if.local_ip4n
             dest_addr_len = 24
-            self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id)
+            self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id})
             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
             if vrf_id not in self.vrf_list:
                 self.vrf_list.append(vrf_id)
@@ -210,8 +210,7 @@ class TestIp4VrfMultiInst(VppTestCase):
 
         :param int vrf_id: The FIB table / VRF ID to be reset.
         """
-        # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
-        self.vapi.reset_fib(vrf_id, is_ipv6=0)
+        self.vapi.ip_table_flush(table={'table_id': vrf_id})
         if vrf_id in self.vrf_list:
             self.vrf_list.remove(vrf_id)
         if vrf_id not in self.vrf_reset_list:
@@ -226,7 +225,7 @@ class TestIp4VrfMultiInst(VppTestCase):
         self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
         self.logger.debug(self.vapi.ppcli("show ip fib"))
         self.logger.debug(self.vapi.ppcli("show ip arp"))
-        self.vapi.ip_table_add_del(is_add=0, table_id=vrf_id)
+        self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id})
 
     def create_stream(self, src_if, packet_sizes):
         """
index 36532ce..c6cced9 100644 (file)
@@ -23,8 +23,8 @@ from util import ppp, ip6_normalize, mk_ll_addr
 from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \
     VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
-    VppMplsRoute, VppMplsTable, VppIpTable, FibPathType, \
-    VppIpInterfaceAddress
+    VppMplsRoute, VppMplsTable, VppIpTable, FibPathType, FibPathProto, \
+    VppIpInterfaceAddress, find_route_in_dump, find_mroute_in_dump
 from vpp_neighbor import find_nbr, VppNeighbor
 from vpp_pg_interface import is_ipv6_misc
 from vpp_sub_interface import VppSubInterface, VppDot1QSubint
@@ -2370,5 +2370,146 @@ class TestIP6Input(VppTestCase):
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
+
+class TestIPReplace(VppTestCase):
+    """ IPv6 Table Replace """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestIPReplace, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestIPReplace, cls).tearDownClass()
+
+    def setUp(self):
+        super(TestIPReplace, self).setUp()
+
+        self.create_pg_interfaces(range(4))
+
+        table_id = 1
+        self.tables = []
+
+        for i in self.pg_interfaces:
+            i.admin_up()
+            i.config_ip6()
+            i.resolve_arp()
+            i.generate_remote_hosts(2)
+            self.tables.append(VppIpTable(self, table_id,
+                                          True).add_vpp_config())
+            table_id += 1
+
+    def tearDown(self):
+        super(TestIPReplace, self).tearDown()
+        for i in self.pg_interfaces:
+            i.admin_down()
+            i.unconfig_ip4()
+
+    def test_replace(self):
+        """ IP Table Replace """
+
+        N_ROUTES = 20
+        links = [self.pg0, self.pg1, self.pg2, self.pg3]
+        routes = [[], [], [], []]
+
+        # the sizes of 'empty' tables
+        for t in self.tables:
+            self.assertEqual(len(t.dump()), 2)
+            self.assertEqual(len(t.mdump()), 5)
+
+        # load up the tables with some routes
+        for ii, t in enumerate(self.tables):
+            for jj in range(1, N_ROUTES):
+                uni = VppIpRoute(
+                    self, "2001::%d" % jj if jj != 0 else "2001::", 128,
+                    [VppRoutePath(links[ii].remote_hosts[0].ip6,
+                                  links[ii].sw_if_index),
+                     VppRoutePath(links[ii].remote_hosts[1].ip6,
+                                  links[ii].sw_if_index)],
+                    table_id=t.table_id).add_vpp_config()
+                multi = VppIpMRoute(
+                    self, "::",
+                    "ff:2001::%d" % jj, 128,
+                    MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+                    [VppMRoutePath(self.pg0.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
+                                   proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
+                     VppMRoutePath(self.pg1.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                                   proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
+                     VppMRoutePath(self.pg2.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                                   proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
+                     VppMRoutePath(self.pg3.sw_if_index,
+                                   MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
+                                   proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)],
+                    table_id=t.table_id).add_vpp_config()
+                routes[ii].append({'uni': uni,
+                                   'multi': multi})
+
+        #
+        # replace the tables a few times
+        #
+        for kk in range(3):
+            # replace each table
+            for t in self.tables:
+                t.replace_begin()
+
+            # all the routes are still there
+            for ii, t in enumerate(self.tables):
+                dump = t.dump()
+                mdump = t.mdump()
+                for r in routes[ii]:
+                    self.assertTrue(find_route_in_dump(dump, r['uni'], t))
+                    self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
+
+            # redownload the even numbered routes
+            for ii, t in enumerate(self.tables):
+                for jj in range(0, N_ROUTES, 2):
+                    routes[ii][jj]['uni'].add_vpp_config()
+                    routes[ii][jj]['multi'].add_vpp_config()
+
+            # signal each table converged
+            for t in self.tables:
+                t.replace_end()
+
+            # we should find the even routes, but not the odd
+            for ii, t in enumerate(self.tables):
+                dump = t.dump()
+                mdump = t.mdump()
+                for jj in range(0, N_ROUTES, 2):
+                    self.assertTrue(find_route_in_dump(
+                        dump, routes[ii][jj]['uni'], t))
+                    self.assertTrue(find_mroute_in_dump(
+                        mdump, routes[ii][jj]['multi'], t))
+                for jj in range(1, N_ROUTES - 1, 2):
+                    self.assertFalse(find_route_in_dump(
+                        dump, routes[ii][jj]['uni'], t))
+                    self.assertFalse(find_mroute_in_dump(
+                        mdump, routes[ii][jj]['multi'], t))
+
+            # reload all the routes
+            for ii, t in enumerate(self.tables):
+                for r in routes[ii]:
+                    r['uni'].add_vpp_config()
+                    r['multi'].add_vpp_config()
+
+            # all the routes are still there
+            for ii, t in enumerate(self.tables):
+                dump = t.dump()
+                mdump = t.mdump()
+                for r in routes[ii]:
+                    self.assertTrue(find_route_in_dump(dump, r['uni'], t))
+                    self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
+
+        #
+        # finally flush the tables for good measure
+        #
+        for t in self.tables:
+            t.flush()
+            self.assertEqual(len(t.dump()), 2)
+            self.assertEqual(len(t.mdump()), 5)
+
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 31e00dc..c23e301 100644 (file)
@@ -195,7 +195,8 @@ class TestIP6VrfMultiInst(VppTestCase):
             pg_if = self.pg_if_by_vrf_id[vrf_id][0]
             dest_addr = pg_if.local_ip6n
             dest_addr_len = 64
-            self.vapi.ip_table_add_del(is_ipv6=1, is_add=1, table_id=vrf_id)
+            self.vapi.ip_table_add_del(is_add=1,
+                                       table={'table_id': vrf_id, 'is_ip6': 1})
             self.logger.info("IPv6 VRF ID %d created" % vrf_id)
             if vrf_id not in self.vrf_list:
                 self.vrf_list.append(vrf_id)
@@ -222,8 +223,7 @@ class TestIP6VrfMultiInst(VppTestCase):
 
         :param int vrf_id: The FIB table / VRF ID to be reset.
         """
-        # self.vapi.reset_vrf(vrf_id, is_ipv6=1)
-        self.vapi.reset_fib(vrf_id, is_ipv6=1)
+        self.vapi.ip_table_flush(table={'table_id': vrf_id, 'is_ip6': 1})
         if vrf_id in self.vrf_list:
             self.vrf_list.remove(vrf_id)
         if vrf_id not in self.vrf_reset_list:
@@ -238,7 +238,8 @@ class TestIP6VrfMultiInst(VppTestCase):
         self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id)
         self.logger.debug(self.vapi.ppcli("show ip6 fib"))
         self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
-        self.vapi.ip_table_add_del(is_ipv6=1, is_add=0, table_id=vrf_id)
+        self.vapi.ip_table_add_del(is_add=0,
+                                   table={'table_id': vrf_id, 'is_ip6': 1})
 
     def create_stream(self, src_if, packet_sizes):
         """
index 8c3bbba..43af5e0 100644 (file)
@@ -67,14 +67,16 @@ class VppIpAddressUnion():
         elif hasattr(other, "ip4") and hasattr(other, "ip6"):
             # vl_api_address_union_t
             if 4 == self.version:
-                return self.ip_addr.packed == other.ip4
+                return self.ip_addr == other.ip4
             else:
-                return self.ip_addr.packed == other.ip6
+                return self.ip_addr == other.ip6
         else:
-            _log.error("Comparing VppIpAddressUnions:%s"
-                       " with incomparable type: %s",
-                       self, other)
-            return NotImplemented
+            raise Exception("Comparing VppIpAddressUnions:%s"
+                            " with incomparable type: %s",
+                            self, other)
+
+    def __ne__(self, other):
+        return not (self == other)
 
     def __str__(self):
         return str(self.ip_addr)
@@ -143,7 +145,6 @@ class VppIpMPrefix():
                 return (self.glen == other.grp_address_length and
                         self.gaddr == str(other.grp_address.ip6) and
                         self.saddr == str(other.src_address.ip6))
-        else:
-            raise Exception("Comparing VppIpPrefix:%s with unknown type: %s" %
+            raise Exception("Comparing VppIpMPrefix:%s with unknown type: %s" %
                             (self, other))
         return False
index d600475..031412c 100644 (file)
@@ -108,6 +108,23 @@ def find_route(test, addr, len, table_id=0):
     return False
 
 
+def find_route_in_dump(dump, route, table):
+    for r in dump:
+        if table.table_id == r.route.table_id \
+           and route.prefix == r.route.prefix:
+            if len(route.paths) == r.route.n_paths:
+                return True
+    return False
+
+
+def find_mroute_in_dump(dump, route, table):
+    for r in dump:
+        if table.table_id == r.route.table_id \
+           and route.prefix == r.route.prefix:
+            return True
+    return False
+
+
 def find_mroute(test, grp_addr, src_addr, grp_addr_len,
                 table_id=0):
     ip_mprefix = VppIpMPrefix(text_type(src_addr),
@@ -168,13 +185,36 @@ class VppIpTable(VppObject):
         self.is_ip6 = is_ip6
 
     def add_vpp_config(self):
-        self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=1,
-                                         table_id=self.table_id)
+        self._test.vapi.ip_table_add_del(is_add=1,
+                                         table={'is_ip6': self.is_ip6,
+                                                'table_id': self.table_id})
         self._test.registry.register(self, self._test.logger)
+        return self
 
     def remove_vpp_config(self):
-        self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=0,
-                                         table_id=self.table_id)
+        self._test.vapi.ip_table_add_del(is_add=0,
+                                         table={'is_ip6': self.is_ip6,
+                                                'table_id': self.table_id})
+
+    def replace_begin(self):
+        self._test.vapi.ip_table_replace_begin(
+            table={'is_ip6': self.is_ip6,
+                   'table_id': self.table_id})
+
+    def replace_end(self):
+        self._test.vapi.ip_table_replace_end(
+            table={'is_ip6': self.is_ip6,
+                   'table_id': self.table_id})
+
+    def flush(self):
+        self._test.vapi.ip_table_flush(table={'is_ip6': self.is_ip6,
+                                              'table_id': self.table_id})
+
+    def dump(self):
+        return self._test.vapi.ip_route_dump(self.table_id, self.is_ip6)
+
+    def mdump(self):
+        return self._test.vapi.ip_mroute_dump(self.table_id, self.is_ip6)
 
     def query_vpp_config(self):
         if self.table_id == 0:
@@ -468,6 +508,7 @@ class VppIpRoute(VppObject):
         self.stats_index = r.stats_index
         if self.register:
             self._test.registry.register(self, self._test.logger)
+        return self
 
     def remove_vpp_config(self):
         # there's no need to issue different deletes for modified routes
@@ -540,6 +581,7 @@ class VppIpMRoute(VppObject):
                                               is_add=1)
         self.stats_index = r.stats_index
         self._test.registry.register(self, self._test.logger)
+        return self
 
     def remove_vpp_config(self):
         self._test.vapi.ip_mroute_add_del(self.table_id,
index a440312..692d007 100644 (file)
@@ -65,7 +65,6 @@ defaultmapping = {
     'ip_punt_police': {'is_add': 1, },
     'ip_punt_redirect': {'is_add': 1, },
     'ip_route_add_del': {'is_add': 1, },
-    'ip_table_add_del': {'is_add': 1, },
     'ip_unnumbered_dump': {'sw_if_index': 4294967295, },
     'ipsec_interface_add_del_spd': {'is_add': 1, },
     'ipsec_sad_entry_add_del': {'is_add': 1, },
@@ -460,30 +459,6 @@ class VppPapiProvider(object):
         return self.api(self.papi.create_loopback,
                         {'mac_address': mac})
 
-    def ip_table_add_del(self,
-                         table_id,
-                         is_add=1,
-                         is_ipv6=0):
-        """
-
-        :param table_id
-        :param is_add:  (Default value = 1)
-        :param is_ipv6:  (Default value = 0)
-
-        """
-
-        return self.api(
-            self.papi.ip_table_add_del,
-            {'table':
-             {
-                 'table_id': table_id,
-                 'is_ip6': is_ipv6
-             },
-             'is_add': is_add})
-
-    def ip_table_dump(self):
-        return self.api(self.papi.ip_table_dump, {})
-
     def ip_route_dump(self, table_id, is_ip6=False):
         return self.api(self.papi.ip_route_dump,
                         {'table': {