Improve L2FIB delete entry handling and "show l2fib" CLI options

Improve deletion of L2FIB MAC entry using l2fib_add_del API. If
sw_if_index param specified in th API is non-zero, check that its
value match that of the MAC entry to proceed with deletion.

Improve "show l2fib" CLI to allow display of all entries, learned
entries only, or added ovia CLI/API entries only. For added entry,
show "no" under the age column to indicate entry does not age.

Change-Id: I0bd2582c2b6bac268e551e4f8ca6dab2be4400ad
Signed-off-by: John Lo <loj@cisco.com>
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index ca9ebec..dfb0284 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -7354,7 +7354,7 @@
   u8 mac_set = 0;
   u32 bd_id;
   u8 bd_id_set = 0;
-  u32 sw_if_index = ~0;
+  u32 sw_if_index = 0;
   u8 sw_if_index_set = 0;
   u8 is_add = 1;
   u8 static_mac = 0;
@@ -7437,10 +7437,10 @@
       clib_memcpy (mp->mac, mac, 6);
       mp->bd_id = ntohl (bd_id);
       mp->is_add = is_add;
+      mp->sw_if_index = ntohl (sw_if_index);
 
       if (is_add)
 	{
-	  mp->sw_if_index = ntohl (sw_if_index);
 	  mp->static_mac = static_mac;
 	  mp->filter_mac = filter_mac;
 	  mp->bvi_mac = bvi_mac;
diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c
index 0fe119b..ebb64ff 100644
--- a/src/vnet/l2/l2_api.c
+++ b/src/vnet/l2/l2_api.c
@@ -239,7 +239,9 @@
     }
   else
     {
-      l2fib_del_entry (mac, bd_index);
+      u32 sw_if_index = ntohl (mp->sw_if_index);
+      if (l2fib_del_entry (mac, bd_index, sw_if_index))
+	rv = VNET_API_ERROR_NO_SUCH_ENTRY;
     }
 
   BAD_SW_IF_INDEX_LABEL;
diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c
index 918700b..959cf4d 100644
--- a/src/vnet/l2/l2_fib.c
+++ b/src/vnet/l2/l2_fib.c
@@ -140,35 +140,52 @@
   u8 verbose = 0;
   u8 raw = 0;
   u8 learn = 0;
+  u8 add = 0;
   u32 bd_id, bd_index = ~0;
   u8 now = (u8) (vlib_time_now (vm) / 60);
   u8 *s = 0;
 
-  if (unformat (input, "raw"))
-    raw = 1;
-  else if (unformat (input, "verbose"))
-    verbose = 1;
-  else if (unformat (input, "bd_index %d", &bd_index))
-    verbose = 1;
-  else if (unformat (input, "learn"))
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
-      learn = 1;
-      verbose = 0;
-    }
-  else if (unformat (input, "bd_id %d", &bd_id))
-    {
-      uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
-      if (p)
+      if (unformat (input, "raw"))
 	{
-	  if (learn == 0)
-	    verbose = 1;
-	  bd_index = p[0];
+	  raw = 1;
+	  verbose = 0;
+	  break;
+	}
+      else if (unformat (input, "verbose"))
+	verbose = 1;
+      else if (unformat (input, "all"))
+	verbose = 1;
+      else if (unformat (input, "bd_index %d", &bd_index))
+	verbose = 1;
+      else if (unformat (input, "learn"))
+	{
+	  add = 0;
+	  learn = 1;
+	  verbose = 1;
+	}
+      else if (unformat (input, "add"))
+	{
+	  learn = 0;
+	  add = 1;
+	  verbose = 1;
+	}
+      else if (unformat (input, "bd_id %d", &bd_id))
+	{
+	  uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
+	  if (p)
+	    {
+	      verbose = 1;
+	      bd_index = p[0];
+	    }
+	  else
+	    return clib_error_return (0,
+				      "bridge domain id %d doesn't exist\n",
+				      bd_id);
 	}
       else
-	{
-	  vlib_cli_output (vm, "no such bridge domain id");
-	  return 0;
-	}
+	break;
     }
 
   for (i = 0; i < h->nbuckets; i++)
@@ -184,7 +201,7 @@
 	      if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL)
 		continue;
 
-	      if ((verbose || learn) && first_entry)
+	      if (verbose && first_entry)
 		{
 		  first_entry = 0;
 		  vlib_cli_output (vm,
@@ -196,27 +213,30 @@
 
 	      key.raw = v->kvp[k].key;
 	      result.raw = v->kvp[k].value;
+	      total_entries++;
 
-	      if ((verbose || learn)
-		  & ((bd_index >> 31) || (bd_index == key.fields.bd_index)))
+	      if (verbose &&
+		  ((bd_index >> 31) || (bd_index == key.fields.bd_index)))
 		{
 		  if (learn && result.fields.age_not)
-		    {
-		      total_entries++;
-		      continue;	/* skip provisioned macs */
-		    }
+		    continue;	/* skip provisioned macs */
+
+		  if (add && !result.fields.age_not)
+		    continue;	/* skip learned macs */
 
 		  bd_config = vec_elt_at_index (l2input_main.bd_configs,
 						key.fields.bd_index);
 
-		  if (bd_config->mac_age && !result.fields.age_not)
+		  if (result.fields.age_not)
+		    s = format (s, "no");
+		  else if (bd_config->mac_age == 0)
+		    s = format (s, "-");
+		  else
 		    {
 		      i16 delta = now - result.fields.timestamp;
 		      delta += delta < 0 ? 256 : 0;
 		      s = format (s, "%d", delta);
 		    }
-		  else
-		    s = format (s, "-");
 
 		  vlib_cli_output (vm,
 				   "%=19U%=7d%=7d %3d/%-3d%=9v%=7s%=7s%=5s%=30U",
@@ -232,7 +252,6 @@
 				   msm->vnet_main, result.fields.sw_if_index);
 		  vec_reset_length (s);
 		}
-	      total_entries++;
 	    }
 	  v++;
 	}
@@ -276,7 +295,7 @@
  * @cliexend
  * Example of how to display all the MAC Address entries in the L2
  * FIB table:
- * @cliexstart{show l2fib verbose}
+ * @cliexstart{show l2fib all}
  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
@@ -287,7 +306,7 @@
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_l2fib_cli, static) = {
   .path = "show l2fib",
-  .short_help = "show l2fib [verbose | learn | bd_id <nn> | bd_index <nn> | raw",
+  .short_help = "show l2fib [all] | [bd_id <nn> | bd_index <nn>] [learn | add] | [raw]",
   .function = show_l2fib,
 };
 /* *INDENT-ON* */
@@ -580,7 +599,7 @@
 
       for (i = 0; i < count; i++)
 	{
-	  l2fib_del_entry (mac, bd_index);
+	  l2fib_del_entry (mac, bd_index, 0);
 	  incr_mac_address (mac);
 	}
     }
@@ -636,24 +655,28 @@
 
 /**
  * Delete an entry from the l2fib.
- * Return 0 if the entry was deleted, or 1 if it was not found
+ * Return 0 if the entry was deleted, or 1 it was not found or if
+ * sw_if_index is non-zero and does not match that in the entry.
  */
-static u32
-l2fib_del_entry_by_key (u64 raw_key)
+u32
+l2fib_del_entry (u8 * mac, u32 bd_index, u32 sw_if_index)
 {
-
   l2fib_entry_result_t result;
   l2fib_main_t *mp = &l2fib_main;
   BVT (clib_bihash_kv) kv;
 
   /* set up key */
-  kv.key = raw_key;
+  kv.key = l2fib_make_key (mac, bd_index);
 
   if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
     return 1;
 
   result.raw = kv.value;
 
+  /*  check if sw_if_index of entry match */
+  if ((sw_if_index != 0) && (sw_if_index != result.fields.sw_if_index))
+    return 1;
+
   /* decrement counter if dynamically learned mac */
   if ((result.fields.age_not == 0) && (l2learn_main.global_learn_count))
     l2learn_main.global_learn_count--;
@@ -664,16 +687,6 @@
 }
 
 /**
- * Delete an entry from the l2fib.
- * Return 0 if the entry was deleted, or 1 if it was not found
- */
-u32
-l2fib_del_entry (u8 * mac, u32 bd_index)
-{
-  return l2fib_del_entry_by_key (l2fib_make_key (mac, bd_index));
-}
-
-/**
  * Delete an entry from the L2FIB.
  * The CLI format is:
  *    l2fib del <mac> <bd-id>
@@ -712,7 +725,7 @@
   bd_index = p[0];
 
   /* Delete the entry */
-  if (l2fib_del_entry (mac, bd_index))
+  if (l2fib_del_entry (mac, bd_index, 0))
     {
       error = clib_error_return (0, "mac entry not found");
       goto done;
@@ -733,7 +746,7 @@
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (l2fib_del_cli, static) = {
   .path = "l2fib del",
-  .short_help = "l2fib del <mac> <bridge-domain-id>",
+  .short_help = "l2fib del <mac> <bridge-domain-id> []",
   .function = l2fib_del,
 };
 /* *INDENT-ON* */
diff --git a/src/vnet/l2/l2_fib.h b/src/vnet/l2/l2_fib.h
index ad7035b..50fd474 100644
--- a/src/vnet/l2/l2_fib.h
+++ b/src/vnet/l2/l2_fib.h
@@ -402,7 +402,7 @@
   l2fib_add_entry (mac, bd_index, ~0, 1, 1, 0);
 }
 
-u32 l2fib_del_entry (u8 * mac, u32 bd_index);
+u32 l2fib_del_entry (u8 * mac, u32 bd_index, u32 sw_if_index);
 
 void l2fib_start_ager_scan (vlib_main_t * vm);
 
diff --git a/src/vnet/l2/l2_input.c b/src/vnet/l2/l2_input.c
index 69de281..6e3851e 100644
--- a/src/vnet/l2/l2_input.c
+++ b/src/vnet/l2/l2_input.c
@@ -587,7 +587,7 @@
 	  config->bvi = 0;
 
 	  /* delete the l2fib entry for the bvi interface */
-	  l2fib_del_entry (hi->hw_address, config->bd_index);
+	  l2fib_del_entry (hi->hw_address, config->bd_index, sw_if_index);
 
 	  /* Make loop output node send packet back to ethernet-input node */
 	  slot =