ip-neighbor: Handle local MAC address change for incomplete adjacencies

Type: fix

When the local MAC address of an interface changes the rewrite strings
of all adjacency types need to be updated - this patch fixes the missing
case of incomplete adjacencies.
I moved the update of all adj types into the adj module, since if the
complete adjs were done by the ip-neighbour module and incomplete ones
by adj module, that would mean two walks of the adj DB, as it is not
possible to walk only a specific type.
UT is updated to include the missing case.

Signed-off-by: Neale Ranns <neale@graphiant.com>
Signed-off-by: Ivan Shvedunov <ivan4th@gmail.com>
Change-Id: I36af94976c645bdd0d4d3bc0093b24d7d077e9d7
diff --git a/src/vnet/adj/adj_glean.c b/src/vnet/adj/adj_glean.c
index 8df104b..e8ca043 100644
--- a/src/vnet/adj/adj_glean.c
+++ b/src/vnet/adj/adj_glean.c
@@ -187,12 +187,6 @@
 }
 
 void
-adj_glean_update_rewrite_itf (u32 sw_if_index)
-{
-    adj_glean_walk (sw_if_index, adj_glean_update_rewrite_walk, NULL);
-}
-
-void
 adj_glean_walk (u32 sw_if_index,
                 adj_walk_cb_t cb,
                 void *data)
@@ -425,6 +419,16 @@
 
 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_glean_interface_delete);
 
+/**
+ * Callback function invoked when an interface's MAC Address changes
+ */
+static void
+adj_glean_ethernet_change_mac (ethernet_main_t * em,
+                             u32 sw_if_index, uword opaque)
+{
+    adj_glean_walk (sw_if_index, adj_glean_update_rewrite_walk, NULL);
+}
+
 u8*
 format_adj_glean (u8* s, va_list *ap)
 {
@@ -509,4 +513,10 @@
 adj_glean_module_init (void)
 {
     dpo_register(DPO_ADJACENCY_GLEAN, &adj_glean_dpo_vft, glean_nodes);
+
+    ethernet_address_change_ctx_t ctx = {
+        .function = adj_glean_ethernet_change_mac,
+        .function_opaque = 0,
+    };
+    vec_add1 (ethernet_main.address_change_callbacks, ctx);
 }
diff --git a/src/vnet/adj/adj_glean.h b/src/vnet/adj/adj_glean.h
index a06b9e8..9e25fd9 100644
--- a/src/vnet/adj/adj_glean.h
+++ b/src/vnet/adj/adj_glean.h
@@ -67,7 +67,6 @@
  * glean behaviour on an adjacency liked to a connected prefix.
  */
 extern void adj_glean_update_rewrite(adj_index_t adj_index);
-extern void adj_glean_update_rewrite_itf(u32 sw_if_index);
 
 /**
  * Return the source address from the glean
diff --git a/src/vnet/adj/adj_nbr.c b/src/vnet/adj/adj_nbr.c
index 3344d6e..8524c6c 100644
--- a/src/vnet/adj/adj_nbr.c
+++ b/src/vnet/adj/adj_nbr.c
@@ -910,13 +910,40 @@
     }
 
     return (NULL);
-   
 }
 
 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_nbr_interface_add_del);
 
 
 static adj_walk_rc_t
+adj_nbr_ethernet_mac_change_one (adj_index_t ai,
+                                 void *arg)
+{
+    vnet_update_adjacency_for_sw_interface(vnet_get_main(),
+                                           adj_get_sw_if_index(ai),
+                                           ai);
+
+    return (ADJ_WALK_RC_CONTINUE);
+}
+
+/**
+ * Callback function invoked when an interface's MAC Address changes
+ */
+static void
+adj_nbr_ethernet_change_mac (ethernet_main_t * em,
+                             u32 sw_if_index, uword opaque)
+{
+    fib_protocol_t proto;
+
+    FOR_EACH_FIB_IP_PROTOCOL(proto)
+    {
+	adj_nbr_walk(sw_if_index, proto,
+		     adj_nbr_ethernet_mac_change_one,
+		     NULL);
+    }
+}
+
+static adj_walk_rc_t
 adj_nbr_show_one (adj_index_t ai,
 		  void *arg)
 {
@@ -1156,4 +1183,10 @@
     dpo_register(DPO_ADJACENCY_INCOMPLETE,
                  &adj_nbr_incompl_dpo_vft,
                  nbr_incomplete_nodes);
+
+    ethernet_address_change_ctx_t ctx = {
+        .function = adj_nbr_ethernet_change_mac,
+        .function_opaque = 0,
+    };
+    vec_add1 (ethernet_main.address_change_callbacks, ctx);
 }
diff --git a/src/vnet/ethernet/interface.c b/src/vnet/ethernet/interface.c
index f72b493..d287748 100644
--- a/src/vnet/ethernet/interface.c
+++ b/src/vnet/ethernet/interface.c
@@ -216,6 +216,7 @@
 	  adj_glean_update_rewrite (ai);
 	  break;
 	case IP_LOOKUP_NEXT_ARP:
+	case IP_LOOKUP_NEXT_REWRITE:
 	  ip_neighbor_update (vnm, ai);
 	  break;
 	case IP_LOOKUP_NEXT_BCAST:
@@ -257,7 +258,6 @@
 	case IP_LOOKUP_NEXT_DROP:
 	case IP_LOOKUP_NEXT_PUNT:
 	case IP_LOOKUP_NEXT_LOCAL:
-	case IP_LOOKUP_NEXT_REWRITE:
 	case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
 	case IP_LOOKUP_NEXT_MIDCHAIN:
 	case IP_LOOKUP_NEXT_ICMP_ERROR:
diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c
index 8637e16..6c97356 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.c
+++ b/src/vnet/ip-neighbor/ip_neighbor.c
@@ -692,13 +692,18 @@
 	  ip_neighbor_probe (adj);
 	}
       break;
+    case IP_LOOKUP_NEXT_REWRITE:
+      /* Update of an existing rewrite adjacency happens e.g. when the
+       * interface's MAC address changes */
+      if (NULL != ipn)
+	ip_neighbor_mk_complete (ai, ipn);
+      break;
     case IP_LOOKUP_NEXT_GLEAN:
     case IP_LOOKUP_NEXT_BCAST:
     case IP_LOOKUP_NEXT_MCAST:
     case IP_LOOKUP_NEXT_DROP:
     case IP_LOOKUP_NEXT_PUNT:
     case IP_LOOKUP_NEXT_LOCAL:
-    case IP_LOOKUP_NEXT_REWRITE:
     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
     case IP_LOOKUP_NEXT_MIDCHAIN:
     case IP_LOOKUP_NEXT_ICMP_ERROR:
@@ -1142,31 +1147,6 @@
   return -1;
 }
 
-static void
-ip_neighbor_ethernet_change_mac (ethernet_main_t * em,
-				 u32 sw_if_index, uword opaque)
-{
-  ip_neighbor_t *ipn;
-
-  IP_NEIGHBOR_DBG ("mac-change: %U",
-		   format_vnet_sw_if_index_name, vnet_get_main (),
-		   sw_if_index);
-
-  /* *INDENT-OFF* */
-  pool_foreach (ipn, ip_neighbor_pool)
-   {
-    if (ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
-      adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
-                       ip_address_family_to_fib_proto(ip_neighbor_get_af(ipn)),
-                       &ip_addr_46(&ipn->ipn_key->ipnk_ip),
-                       ip_neighbor_mk_complete_walk,
-                       ipn);
-  }
-  /* *INDENT-ON* */
-
-  adj_glean_update_rewrite_itf (sw_if_index);
-}
-
 void
 ip_neighbor_populate (ip_address_family_t af, u32 sw_if_index)
 {
@@ -1806,14 +1786,6 @@
     };
     vec_add1 (ip6_main.table_bind_callbacks, cb);
   }
-  {
-    ethernet_address_change_ctx_t ctx = {
-      .function = ip_neighbor_ethernet_change_mac,
-      .function_opaque = 0,
-    };
-    vec_add1 (ethernet_main.address_change_callbacks, ctx);
-  }
-
   ipn_logger = vlib_log_register_class ("ip", "neighbor");
 
   ip_address_family_t af;