IMplementation for option to not create a FIB table entry when adding a neighbor entry

Change-Id: I952039e101031ee6a06e63f4c73d8eb359423e1a
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c
index 222415b..d8ae844 100644
--- a/src/vnet/ethernet/arp.c
+++ b/src/vnet/ethernet/arp.c
@@ -100,6 +100,7 @@
   u32 sw_if_index;
   ethernet_arp_ip4_over_ethernet_address_t a;
   int is_static;
+  int is_no_fib_entry;
   int flags;
 #define ETHERNET_ARP_ARGS_REMOVE (1<<0)
 #define ETHERNET_ARP_ARGS_FLUSH  (1<<1)
@@ -254,6 +255,9 @@
   if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
     flags = format (flags, "D");
 
+  if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY)
+    flags = format (flags, "N");
+
   s = format (s, "%=12U%=16U%=6s%=20U%=24U",
 	      format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
 	      format_ip4_address, &e->ip4_address,
@@ -525,6 +529,7 @@
   ethernet_arp_interface_t *arp_int;
   int is_static = args->is_static;
   u32 sw_if_index = args->sw_if_index;
+  int is_no_fib_entry = args->is_no_fib_entry;
 
   vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
 
@@ -546,16 +551,6 @@
 
   if (make_new_arp_cache_entry)
     {
-      fib_prefix_t pfx = {
-	.fp_len = 32,
-	.fp_proto = FIB_PROTOCOL_IP4,
-	.fp_addr = {
-		    .ip4 = a->ip4,
-		    }
-	,
-      };
-      u32 fib_index;
-
       pool_get (am->ip4_entry_pool, e);
 
       if (NULL == arp_int->arp_entries)
@@ -570,17 +565,25 @@
       clib_memcpy (e->ethernet_address,
 		   a->ethernet, sizeof (e->ethernet_address));
 
-      fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
-      e->fib_entry_index =
-	fib_table_entry_update_one_path (fib_index,
-					 &pfx,
-					 FIB_SOURCE_ADJ,
-					 FIB_ENTRY_FLAG_ATTACHED,
-					 FIB_PROTOCOL_IP4,
-					 &pfx.fp_addr,
-					 e->sw_if_index,
-					 ~0,
-					 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
+      if (!is_no_fib_entry)
+	{
+	  fib_prefix_t pfx = {
+	    .fp_len = 32,
+	    .fp_proto = FIB_PROTOCOL_IP4,
+	    .fp_addr.ip4 = a->ip4,
+	  };
+	  u32 fib_index;
+
+	  fib_index =
+	    ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
+	  e->fib_entry_index =
+	    fib_table_entry_update_one_path (fib_index, &pfx, FIB_SOURCE_ADJ,
+					     FIB_ENTRY_FLAG_ATTACHED,
+					     FIB_PROTOCOL_IP4, &pfx.fp_addr,
+					     e->sw_if_index, ~0, 1, NULL,
+					     FIB_ROUTE_PATH_FLAG_NONE);
+	  e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY;
+	}
     }
   else
     {
@@ -1142,7 +1145,7 @@
 
 	      vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index0,
 					      &arp0->ip4_over_ethernet[0],
-					      0 /* is_static */ );
+					      0 /* is_static */ , 0);
 	      error0 = ETHERNET_ARP_ERROR_l3_src_address_learned;
 	    }
 
@@ -1658,6 +1661,8 @@
 {
   ethernet_arp_main_t *am = &ethernet_arp_main;
 
+  /* it's safe to delete the ADJ source on the FIB entry, even if it
+   * was added */
   fib_table_entry_delete_index (e->fib_entry_index, FIB_SOURCE_ADJ);
   hash_unset (eai->arp_entries, e->ip4_address.as_u32);
   pool_put (am->ip4_entry_pool, e);
@@ -1828,13 +1833,15 @@
 
 int
 vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
-				u32 sw_if_index, void *a_arg, int is_static)
+				u32 sw_if_index, void *a_arg,
+				int is_static, int is_no_fib_entry)
 {
   ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
   vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
 
   args.sw_if_index = sw_if_index;
   args.is_static = is_static;
+  args.is_no_fib_entry = is_no_fib_entry;
   args.flags = 0;
   clib_memcpy (&args.a, a, sizeof (*a));
 
@@ -1931,6 +1938,7 @@
   u32 fib_index = 0;
   u32 fib_id;
   int is_static = 0;
+  int is_no_fib_entry = 0;
   int is_proxy = 0;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -1948,6 +1956,9 @@
       else if (unformat (input, "static"))
 	is_static = 1;
 
+      else if (unformat (input, "no-fib-entry"))
+	is_no_fib_entry = 1;
+
       else if (unformat (input, "count %d", &count))
 	;
 
@@ -1991,7 +2002,7 @@
 		 1 /* type */ , 0 /* data */ );
 
 	      vnet_arp_set_ip4_over_ethernet
-		(vnm, sw_if_index, &addr, is_static);
+		(vnm, sw_if_index, &addr, is_static, is_no_fib_entry);
 
 	      vlib_process_wait_for_event (vm);
 	      event_type = vlib_process_get_events (vm, &event_data);
@@ -2046,7 +2057,7 @@
 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
   .path = "set ip arp",
   .short_help =
-  "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
+  "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
   .function = ip_arp_add_del_command_fn,
 };
 /* *INDENT-ON* */
diff --git a/src/vnet/ethernet/arp_packet.h b/src/vnet/ethernet/arp_packet.h
index e762ffa..17e64f4 100644
--- a/src/vnet/ethernet/arp_packet.h
+++ b/src/vnet/ethernet/arp_packet.h
@@ -140,6 +140,13 @@
   };
 } ethernet_arp_header_t;
 
+typedef enum ethernet_arp_entry_flags_t_
+{
+  ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC = (1 << 0),
+  ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC = (1 << 1),
+  ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY = (1 << 2),
+} __attribute__ ((packed)) ethernet_arp_entry_flags_t;
+
 typedef struct
 {
   u32 sw_if_index;
@@ -147,9 +154,7 @@
 
   u8 ethernet_address[6];
 
-  u16 flags;
-#define ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC  (1 << 0)
-#define ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC (1 << 1)
+  ethernet_arp_entry_flags_t flags;
 
   u64 cpu_time_last_updated;
 
diff --git a/src/vnet/ethernet/ethernet.h b/src/vnet/ethernet/ethernet.h
index ba84c69..dcc656a 100644
--- a/src/vnet/ethernet/ethernet.h
+++ b/src/vnet/ethernet/ethernet.h
@@ -403,7 +403,8 @@
 
 int
 vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
-				u32 sw_if_index, void *a_arg, int is_static);
+				u32 sw_if_index, void *a_arg,
+				int is_static, int is_no_fib_entry);
 
 int
 vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm,
diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c
index 715891b..8d355ab 100644
--- a/src/vnet/ip/ip6_neighbor.c
+++ b/src/vnet/ip/ip6_neighbor.c
@@ -235,6 +235,9 @@
   if (n->flags & IP6_NEIGHBOR_FLAG_STATIC)
     flags = format (flags, "S");
 
+  if (n->flags & IP6_NEIGHBOR_FLAG_NO_FIB_ENTRY)
+    flags = format (flags, "N");
+
   si = vnet_get_sw_interface (vnm, n->key.sw_if_index);
   s = format (s, "%=12U%=20U%=6s%=20U%=40U",
 	      format_vlib_cpu_time, vm, n->cpu_time_last_updated,
@@ -317,6 +320,7 @@
 {
   u8 is_add;
   u8 is_static;
+  u8 is_no_fib_entry;
   u8 link_layer_address[6];
   u32 sw_if_index;
   ip6_address_t addr;
@@ -328,7 +332,8 @@
 static void set_unset_ip6_neighbor_rpc
   (vlib_main_t * vm,
    u32 sw_if_index,
-   ip6_address_t * a, u8 * link_layer_address, int is_add, int is_static)
+   ip6_address_t * a, u8 * link_layer_address, int is_add, int is_static,
+   int is_no_fib_entry)
 {
   ip6_neighbor_set_unset_rpc_args_t args;
   void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
@@ -336,6 +341,7 @@
   args.sw_if_index = sw_if_index;
   args.is_add = is_add;
   args.is_static = is_static;
+  args.is_no_fib_entry = is_no_fib_entry;
   clib_memcpy (&args.addr, a, sizeof (*a));
   if (NULL != link_layer_address)
     clib_memcpy (args.link_layer_address, link_layer_address, 6);
@@ -565,7 +571,7 @@
 				ip6_address_t * a,
 				u8 * link_layer_address,
 				uword n_bytes_link_layer_address,
-				int is_static)
+				int is_static, int is_no_fib_entry)
 {
   ip6_neighbor_main_t *nm = &ip6_neighbor_main;
   ip6_neighbor_key_t k;
@@ -578,7 +584,8 @@
   if (os_get_cpu_number ())
     {
       set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
-				  1 /* set new neighbor */ , is_static);
+				  1 /* set new neighbor */ , is_static,
+				  is_no_fib_entry);
       return 0;
     }
 
@@ -598,16 +605,6 @@
 
   if (make_new_nd_cache_entry)
     {
-      fib_prefix_t pfx = {
-	.fp_len = 128,
-	.fp_proto = FIB_PROTOCOL_IP6,
-	.fp_addr = {
-		    .ip6 = k.ip6_address,
-		    }
-	,
-      };
-      u32 fib_index;
-
       pool_get (nm->neighbor_pool, n);
       mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
 		 /* old value */ 0);
@@ -619,9 +616,25 @@
       /*
        * create the adj-fib. the entry in the FIB table for and to the peer.
        */
-      fib_index = ip6_main.fib_index_by_sw_if_index[n->key.sw_if_index];
-      n->fib_entry_index = fib_table_entry_update_one_path (fib_index, &pfx, FIB_SOURCE_ADJ, FIB_ENTRY_FLAG_NONE, FIB_PROTOCOL_IP6, &pfx.fp_addr, n->key.sw_if_index, ~0, 1, NULL,	// no label stack
-							    FIB_ROUTE_PATH_FLAG_NONE);
+      if (!is_no_fib_entry)
+	{
+	  fib_prefix_t pfx = {
+	    .fp_len = 128,
+	    .fp_proto = FIB_PROTOCOL_IP6,
+	    .fp_addr.ip6 = k.ip6_address,
+	  };
+	  u32 fib_index;
+
+	  fib_index = ip6_main.fib_index_by_sw_if_index[n->key.sw_if_index];
+	  n->fib_entry_index =
+	    fib_table_entry_update_one_path (fib_index, &pfx,
+					     FIB_SOURCE_ADJ,
+					     FIB_ENTRY_FLAG_NONE,
+					     FIB_PROTOCOL_IP6, &pfx.fp_addr,
+					     n->key.sw_if_index, ~0, 1, NULL,
+					     FIB_ROUTE_PATH_FLAG_NONE);
+	  n->flags |= IP6_NEIGHBOR_FLAG_NO_FIB_ENTRY;
+	}
     }
   else
     {
@@ -712,7 +725,7 @@
   if (os_get_cpu_number ())
     {
       set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
-				  0 /* unset */ , 0);
+				  0 /* unset */ , 0, 0);
       return 0;
     }
 
@@ -746,7 +759,8 @@
   vlib_main_t *vm = vlib_get_main ();
   if (a->is_add)
     vnet_set_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr,
-				    a->link_layer_address, 6, a->is_static);
+				    a->link_layer_address, 6, a->is_static,
+				    a->is_no_fib_entry);
   else
     vnet_unset_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr,
 				      a->link_layer_address, 6);
@@ -850,6 +864,7 @@
   int addr_valid = 0;
   int is_del = 0;
   int is_static = 0;
+  int is_no_fib_entry = 0;
   u32 sw_if_index;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -865,6 +880,8 @@
 	is_del = 1;
       else if (unformat (input, "static"))
 	is_static = 1;
+      else if (unformat (input, "no-fib-entry"))
+	is_no_fib_entry = 1;
       else
 	break;
     }
@@ -875,7 +892,7 @@
   if (!is_del)
     vnet_set_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
 				    mac_address, sizeof (mac_address),
-				    is_static);
+				    is_static, is_no_fib_entry);
   else
     vnet_unset_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
 				      mac_address, sizeof (mac_address));
@@ -1023,7 +1040,7 @@
 					      &h0->target_address,
 					      o0->ethernet_address,
 					      sizeof (o0->ethernet_address),
-					      0);
+					      0, 0);
 	    }
 
 	  if (is_solicitation && error0 == ICMP6_ERROR_NONE)
@@ -1338,7 +1355,7 @@
 					      &ip0->src_address,
 					      o0->ethernet_address,
 					      sizeof (o0->ethernet_address),
-					      0);
+					      0, 0);
 	    }
 
 	  /* default is to drop */
diff --git a/src/vnet/ip/ip6_neighbor.h b/src/vnet/ip/ip6_neighbor.h
index 3d25631..ef1e84c 100644
--- a/src/vnet/ip/ip6_neighbor.h
+++ b/src/vnet/ip/ip6_neighbor.h
@@ -28,13 +28,18 @@
   u32 pad;
 } ip6_neighbor_key_t;
 
+typedef enum ip6_neighbor_flags_t_
+{
+  IP6_NEIGHBOR_FLAG_STATIC = (1 << 0),
+  IP6_NEIGHBOR_FLAG_DYNAMIC = (1 << 1),
+  IP6_NEIGHBOR_FLAG_NO_FIB_ENTRY = (1 << 2),
+} __attribute__ ((packed)) ip6_neighbor_flags_t;
+
 typedef struct
 {
   ip6_neighbor_key_t key;
   u8 link_layer_address[8];
-  u16 flags;
-#define IP6_NEIGHBOR_FLAG_STATIC (1 << 0)
-#define IP6_NEIGHBOR_FLAG_DYNAMIC  (2 << 0)
+  ip6_neighbor_flags_t flags;
   u64 cpu_time_last_updated;
   fib_node_index_t fib_entry_index;
 } ip6_neighbor_t;
@@ -69,7 +74,8 @@
 					   ip6_address_t * a,
 					   u8 * link_layer_address,
 					   uword n_bytes_link_layer_address,
-					   int is_static);
+					   int is_static,
+					   int is_no_fib_entry);
 
 extern int vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
 					     u32 sw_if_index,
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index 0ca058a..e3a1fee 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -655,7 +655,8 @@
 	rv = vnet_set_ip6_ethernet_neighbor
 	  (vm, ntohl (mp->sw_if_index),
 	   (ip6_address_t *) (mp->dst_address),
-	   mp->mac_address, sizeof (mp->mac_address), mp->is_static);
+	   mp->mac_address, sizeof (mp->mac_address), mp->is_static,
+	   mp->is_no_adj_fib);
       else
 	rv = vnet_unset_ip6_ethernet_neighbor
 	  (vm, ntohl (mp->sw_if_index),
@@ -671,7 +672,8 @@
 
       if (mp->is_add)
 	rv = vnet_arp_set_ip4_over_ethernet (vnm, ntohl (mp->sw_if_index),
-					     &a, mp->is_static);
+					     &a, mp->is_static,
+					     mp->is_no_adj_fib);
       else
 	rv =
 	  vnet_arp_unset_ip4_over_ethernet (vnm, ntohl (mp->sw_if_index), &a);