ip-neighbor: Replace feature for the ip-neighbor data-base

Type: feature

DB replace is implemented with a mark and sweep algorithm (just the the
FIB)

Signed-off-by: Neale Ranns <nranns@cisco.com>
Change-Id: I54ab06e11552219e2a18e1b4a87d531321cf3829
diff --git a/src/vnet/ip-neighbor/ip_neighbor.api b/src/vnet/ip-neighbor/ip_neighbor.api
index b820360..fc8f822 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.api
+++ b/src/vnet/ip-neighbor/ip_neighbor.api
@@ -126,6 +126,43 @@
   bool recycle;
 };
 
+/** \brief IP neighbour replace begin
+
+    The use-case is that, for some unspecified reason, the control plane
+    has a different set of neighbours it than VPP
+    currently has. The CP would thus like to 'replace' VPP's set
+    only by specifying what the new set shall be, i.e. it is not
+    going to delete anything that already eixts, rather, is wants any
+    unspecified neighbors deleted implicitly.
+    The CP declares the start of this procedure with this replace_begin
+    API Call, and when it has populated all neighbours it wants, it calls
+    the below replace_end API. From this point on it is of course free
+    to add and delete neighbours as usual.
+    The underlying mechanism by which VPP implements this replace is
+    intentionally left unspecified.
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+autoreply define ip_neighbor_replace_begin
+{
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief IP neighbour replace end
+
+    see ip_neighbor_replace_begin description.
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+autoreply define ip_neighbor_replace_end
+{
+  u32 client_index;
+  u32 context;
+};
+
 /** \brief Register for IP4 ARP resolution event on receing ARP reply or
            MAC/IP info from ARP requests in L2 BDs
     @param client_index - opaque cookie to identify the sender
diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c
index 960da12..5b18473 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.c
+++ b/src/vnet/ip-neighbor/ip_neighbor.c
@@ -99,6 +99,12 @@
   return (ipn - ip_neighbor_pool);
 }
 
+static void
+ip_neighbor_touch (ip_neighbor_t * ipn)
+{
+  ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
+}
+
 static bool
 ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
 {
@@ -145,6 +151,7 @@
    * list is time sorted, newest first */
   ip_neighbor_elt_t *elt, *head;
 
+  ip_neighbor_touch (ipn);
   ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
   ipn->ipn_n_probes = 0;
 
@@ -473,6 +480,8 @@
 		       format_ip_neighbor_flags, flags, format_mac_address_t,
 		       mac);
 
+      ip_neighbor_touch (ipn);
+
       /* Refuse to over-write static neighbor entry. */
       if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
 	  (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
@@ -1177,6 +1186,60 @@
   vec_free (ipnis);
 }
 
+static walk_rc_t
+ip_neighbor_mark_one (index_t ipni, void *ctx)
+{
+  ip_neighbor_t *ipn;
+
+  ipn = ip_neighbor_get (ipni);
+
+  ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
+
+  return (WALK_CONTINUE);
+}
+
+void
+ip_neighbor_mark (ip46_type_t type)
+{
+  ip_neighbor_walk (type, ~0, ip_neighbor_mark_one, NULL);
+}
+
+typedef struct ip_neighbor_sweep_ctx_t_
+{
+  index_t *ipnsc_stale;
+} ip_neighbor_sweep_ctx_t;
+
+static walk_rc_t
+ip_neighbor_sweep_one (index_t ipni, void *arg)
+{
+  ip_neighbor_sweep_ctx_t *ctx = arg;
+  ip_neighbor_t *ipn;
+
+  ipn = ip_neighbor_get (ipni);
+
+  if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
+    {
+      vec_add1 (ctx->ipnsc_stale, ipni);
+    }
+
+  return (WALK_CONTINUE);
+}
+
+void
+ip_neighbor_sweep (ip46_type_t type)
+{
+  ip_neighbor_sweep_ctx_t ctx = { };
+  index_t *ipni;
+
+  ip_neighbor_walk (type, ~0, ip_neighbor_sweep_one, &ctx);
+
+  vec_foreach (ipni, ctx.ipnsc_stale)
+  {
+    ip_neighbor_free (ip_neighbor_get (*ipni));
+  }
+  vec_free (ctx.ipnsc_stale);
+}
+
 /*
  * Remove any arp entries associated with the specified interface
  */
diff --git a/src/vnet/ip-neighbor/ip_neighbor.h b/src/vnet/ip-neighbor/ip_neighbor.h
index cb384c5..8769fd5 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.h
+++ b/src/vnet/ip-neighbor/ip_neighbor.h
@@ -62,6 +62,9 @@
 extern void ip_neighbor_probe_dst (const ip_adjacency_t * adj,
 				   const ip46_address_t * ip);
 
+extern void ip_neighbor_mark (ip46_type_t type);
+extern void ip_neighbor_sweep (ip46_type_t type);
+
 /**
  * From the watcher to the API to publish a new neighbor
  */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_api.c b/src/vnet/ip-neighbor/ip_neighbor_api.c
index ec1e493..86587fa 100644
--- a/src/vnet/ip-neighbor/ip_neighbor_api.c
+++ b/src/vnet/ip-neighbor/ip_neighbor_api.c
@@ -275,6 +275,32 @@
   REPLY_MACRO (VL_API_IP_NEIGHBOR_CONFIG_REPLY);
 }
 
+static void
+vl_api_ip_neighbor_replace_begin_t_handler (vl_api_ip_neighbor_replace_begin_t
+					    * mp)
+{
+  vl_api_ip_neighbor_replace_begin_reply_t *rmp;
+  int rv = 0;
+
+  ip_neighbor_mark (IP46_TYPE_IP4);
+  ip_neighbor_mark (IP46_TYPE_IP6);
+
+  REPLY_MACRO (VL_API_IP_NEIGHBOR_REPLACE_BEGIN_REPLY);
+}
+
+static void
+vl_api_ip_neighbor_replace_end_t_handler (vl_api_ip_neighbor_replace_end_t *
+					  mp)
+{
+  vl_api_ip_neighbor_replace_end_reply_t *rmp;
+  int rv = 0;
+
+  ip_neighbor_sweep (IP46_TYPE_IP4);
+  ip_neighbor_sweep (IP46_TYPE_IP6);
+
+  REPLY_MACRO (VL_API_IP_NEIGHBOR_REPLACE_END_REPLY);
+}
+
 #define vl_msg_name_crc_list
 #include <vnet/ip-neighbor/ip_neighbor.api.h>
 #undef vl_msg_name_crc_list
diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.c b/src/vnet/ip-neighbor/ip_neighbor_types.c
index 27262a5..32c674d 100644
--- a/src/vnet/ip-neighbor/ip_neighbor_types.c
+++ b/src/vnet/ip-neighbor/ip_neighbor_types.c
@@ -22,19 +22,14 @@
 {
   ip_neighbor_flags_t flags = va_arg (*args, int);
 
-  if (flags & IP_NEIGHBOR_FLAG_STATIC)
-    s = format (s, "S");
-
-  if (flags & IP_NEIGHBOR_FLAG_DYNAMIC)
-    s = format (s, "D");
-
-  if (flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY)
-    s = format (s, "N");
-
-  return s;
+#define _(a,b,c,d)                              \
+  if (flags & IP_NEIGHBOR_FLAG_##a)             \
+    s = format (s, "%s", d);
+  foreach_ip_neighbor_flag
+#undef _
+    return s;
 }
 
-
 u8 *
 format_ip_neighbor_key (u8 * s, va_list * va)
 {
diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.h b/src/vnet/ip-neighbor/ip_neighbor_types.h
index c6d4e10..82c5417 100644
--- a/src/vnet/ip-neighbor/ip_neighbor_types.h
+++ b/src/vnet/ip-neighbor/ip_neighbor_types.h
@@ -37,13 +37,19 @@
   u8 stale_threshold;		/* Threshold in minutes to delete nei entry */
 } ip_neighbor_scan_arg_t;
 
+#define foreach_ip_neighbor_flag                 \
+  _(STATIC, 1 << 0, "static", "S")               \
+  _(DYNAMIC, 1 << 1, "dynamic", "D")             \
+  _(NO_FIB_ENTRY, 1 << 2, "no-fib-entry", "N")   \
+  _(PENDING, 1 << 3, "pending", "P")             \
+  _(STALE, 1 << 4, "stale", "A")                 \
+
 typedef enum ip_neighbor_flags_t_
 {
   IP_NEIGHBOR_FLAG_NONE = 0,
-  IP_NEIGHBOR_FLAG_STATIC = (1 << 0),
-  IP_NEIGHBOR_FLAG_DYNAMIC = (1 << 1),
-  IP_NEIGHBOR_FLAG_NO_FIB_ENTRY = (1 << 2),
-  IP_NEIGHBOR_FLAG_PENDING = (1 << 3),
+#define _(a,b,c,d) IP_NEIGHBOR_FLAG_##a = b,
+  foreach_ip_neighbor_flag
+#undef _
 } __attribute__ ((packed)) ip_neighbor_flags_t;
 
 typedef struct ip_neighbor_watcher_t_