VPP-363: add ability to change mac address of the interface 48/2548/8
authorPavel Kotucek <pkotucek@cisco.com>
Mon, 26 Sep 2016 08:40:02 +0000 (10:40 +0200)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 30 Sep 2016 19:08:24 +0000 (19:08 +0000)
Added ability to change interface address.
Added new CLI and API functions.

Change-Id: Ia336bc75ad8c5858c26f39af851485c4c6f19f58
Signed-off-by: Pavel Kotucek <pkotucek@cisco.com>
vnet/vnet/devices/dpdk/device.c
vnet/vnet/ethernet/arp.c
vnet/vnet/ethernet/ethernet.h
vnet/vnet/fib/ip6_fib.h
vnet/vnet/interface.c
vnet/vnet/interface.h
vnet/vnet/interface_cli.c
vnet/vnet/interface_funcs.h
vnet/vnet/ip/ip6_neighbor.c

index 5d1fcf6..cd22d8a 100644 (file)
@@ -1275,6 +1275,7 @@ VNET_DEVICE_CLASS (dpdk_device_class) = {
   .rx_redirect_to_node = dpdk_set_interface_next_node,
   .no_flatten_output_chains = 1,
   .name_renumber = dpdk_device_renumber,
+  .mac_addr_change_function = dpdk_set_mac_address,
 };
 
 VLIB_DEVICE_TX_FUNCTION_MULTIARCH (dpdk_device_class, dpdk_interface_tx)
index 541355f..7d61843 100644 (file)
@@ -2343,6 +2343,43 @@ arp_term_init (vlib_main_t * vm)
 
 VLIB_INIT_FUNCTION (arp_term_init);
 
+void
+change_arp_mac (u32 sw_if_index, ethernet_arp_ip4_entry_t * e)
+{
+  if (e->sw_if_index == sw_if_index)
+    {
+
+      if (ADJ_INDEX_INVALID != e->adj_index[FIB_LINK_IP4])
+       {
+         // the update rewrite function takes the dst mac (which is not changing)
+         // the new source mac will be retrieved from the interface
+         // when the full rewrite is constructed.
+         adj_nbr_update_rewrite (e->adj_index[FIB_LINK_IP4],
+                                 e->ethernet_address);
+       }
+      if (ADJ_INDEX_INVALID != e->adj_index[FIB_LINK_MPLS])
+       {
+         adj_nbr_update_rewrite (e->adj_index[FIB_LINK_MPLS],
+                                 e->ethernet_address);
+       }
+
+    }
+}
+
+void
+ethernet_arp_change_mac (vnet_main_t * vnm, u32 sw_if_index)
+{
+  ethernet_arp_main_t *am = &ethernet_arp_main;
+  ethernet_arp_ip4_entry_t *e;
+
+  /* *INDENT-OFF* */
+  pool_foreach (e, am->ip4_entry_pool,
+    ({
+      change_arp_mac (sw_if_index, e);
+    }));
+  /* *INDENT-ON* */
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 3b2ef87..1855b91 100644 (file)
@@ -536,6 +536,8 @@ int vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm,
                                       uword type_opaque,
                                       uword data, int is_add);
 
+void ethernet_arp_change_mac (vnet_main_t * vnm, u32 sw_if_index);
+
 extern vlib_node_registration_t ethernet_input_node;
 
 #endif /* included_ethernet_h */
index f6af993..3668234 100644 (file)
@@ -57,6 +57,8 @@ u32 ip6_fib_table_fwding_lookup(ip6_main_t * im,
                                u32 fib_index, 
                                const ip6_address_t * dst);
 
+void ethernet_ndp_change_mac (vlib_main_t * vm, u32 sw_if_index);
+
 /**
  * @biref return the DPO that the LB stacks on.
  */
index 595ed14..ca0df9d 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <vnet/vnet.h>
 #include <vnet/plugin/plugin.h>
+#include <vnet/fib/ip6_fib.h>
 
 #define VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE (1 << 0)
 #define VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE (1 << 1)
@@ -1229,6 +1230,62 @@ vnet_rename_interface (vnet_main_t * vnm, u32 hw_if_index, char *new_name)
   return error;
 }
 
+static clib_error_t *
+vnet_hw_interface_change_mac_address_helper (vnet_main_t * vnm,
+                                            u32 hw_if_index, u64 mac_address)
+{
+  clib_error_t *error = 0;
+  vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+  if (hi->hw_address)
+    {
+      vnet_device_class_t *dev_class =
+       vnet_get_device_class (vnm, hi->dev_class_index);
+      if (dev_class->mac_addr_change_function)
+       {
+         error =
+           dev_class->mac_addr_change_function (vnet_get_hw_interface
+                                                (vnm, hw_if_index),
+                                                (char *) &mac_address);
+       }
+      if (!error)
+       {
+         ethernet_main_t *em = &ethernet_main;
+         ethernet_interface_t *ei =
+           pool_elt_at_index (em->interfaces, hi->hw_instance);
+
+         clib_memcpy (hi->hw_address, (u8 *) & mac_address,
+                      sizeof (hi->hw_address));
+         clib_memcpy (ei->address, (u8 *) & mac_address,
+                      sizeof (ei->address));
+         ethernet_arp_change_mac (vnm, hw_if_index);
+         ethernet_ndp_change_mac (vnm->vlib_main, hw_if_index);
+       }
+      else
+       {
+         error =
+           clib_error_return (0,
+                              "MAC Address Change is not supported on this interface");
+       }
+    }
+  else
+    {
+      error =
+       clib_error_return (0,
+                          "mac address change is not supported for interface index %u",
+                          hw_if_index);
+    }
+  return error;
+}
+
+clib_error_t *
+vnet_hw_interface_change_mac_address (vnet_main_t * vnm, u32 hw_if_index,
+                                     u64 mac_address)
+{
+  return vnet_hw_interface_change_mac_address_helper
+    (vnm, hw_if_index, mac_address);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 9f032e9..5fbf830 100644 (file)
@@ -55,6 +55,10 @@ typedef clib_error_t *(vnet_subif_add_del_function_t)
   (struct vnet_main_t * vnm, u32 if_index,
    struct vnet_sw_interface_t * template, int is_add);
 
+/* Interface set mac address callback. */
+typedef clib_error_t *(vnet_interface_set_mac_address_function_t)
+  (struct vnet_hw_interface_t * hi, char *address);
+
 typedef struct _vnet_interface_function_list_elt
 {
   struct _vnet_interface_function_list_elt *next_interface_function;
@@ -151,6 +155,9 @@ typedef struct _vnet_device_class
   /* Do not splice vnet_interface_output_node into TX path */
   u8 no_flatten_output_chains;
 
+  /* Function to set mac address. */
+  vnet_interface_set_mac_address_function_t *mac_addr_change_function;
+
 } vnet_device_class_t;
 
 #define VNET_DEVICE_CLASS(x,...)                                        \
index 611ce28..03b5133 100644 (file)
@@ -1027,6 +1027,54 @@ VLIB_CLI_COMMAND (set_interface_mtu_cmd, static) = {
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+set_interface_mac_address (vlib_main_t * vm, unformat_input_t * input,
+                          vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  clib_error_t *error = 0;
+  u32 sw_if_index = ~0;
+  u64 mac = 0;
+
+  if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
+    {
+      error = clib_error_return (0, "unknown interface `%U'",
+                                format_unformat_error, input);
+      goto done;
+    }
+  if (!unformat_user (input, unformat_ethernet_address, &mac))
+    {
+      error = clib_error_return (0, "expected mac address `%U'",
+                                format_unformat_error, input);
+      goto done;
+    }
+  error = vnet_hw_interface_change_mac_address (vnm, sw_if_index, mac);
+done:
+  return error;
+}
+
+/*?
+ * The '<em>set interface mac address </em>' command allows to set MAC address of given interface.
+ * In case of NIC interfaces the one has to support MAC address change. A side effect of MAC address
+ * change are changes of MAC addresses in FIB tables (ipv4 and ipv6).
+ *
+ * @cliexpar
+ * @parblock
+ * Example of how to change MAC Address of interface:
+ * @cliexcmd{set interface mac address GigabitEthernet0/8/0 aa:bb:cc:dd:ee:01}
+ * @cliexcmd{set interface mac address host-vpp0 aa:bb:cc:dd:ee:02}
+ * @cliexcmd{set interface mac address tap-0 aa:bb:cc:dd:ee:03}
+ * @cliexcmd{set interface mac address pg0 aa:bb:cc:dd:ee:04}
+ * @endparblock
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_interface_mac_address_cmd, static) = {
+  .path = "set interface mac address",
+  .short_help = "set interface mac address <intfc> <mac-address>",
+  .function = set_interface_mac_address,
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 735d47e..f603a03 100644 (file)
@@ -173,6 +173,11 @@ void vnet_hw_interface_init_for_class (vnet_main_t * vnm, u32 hw_if_index,
 clib_error_t *vnet_rename_interface (vnet_main_t * vnm, u32 hw_if_index,
                                     char *new_name);
 
+/* Change interface mac address*/
+clib_error_t *vnet_hw_interface_change_mac_address (vnet_main_t * vnm,
+                                                   u32 hw_if_index,
+                                                   u64 mac_address);
+
 /* Formats sw/hw interface. */
 format_function_t format_vnet_hw_interface;
 format_function_t format_vnet_sw_interface;
index 11df776..5d059e9 100644 (file)
@@ -3472,3 +3472,23 @@ int vnet_ip6_nd_term (vlib_main_t * vm,
   return 0;
 
 }
+
+void
+ethernet_ndp_change_mac (vlib_main_t * vm, u32 sw_if_index)
+{
+  ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+  ip6_neighbor_t * n;
+
+  /* *INDENT-OFF* */
+  pool_foreach (n, nm->neighbor_pool, ({
+    if (n->key.sw_if_index == sw_if_index)
+    {
+      if (ADJ_INDEX_INVALID != n->adj_index)
+        {
+          adj_nbr_update_rewrite(n->adj_index,
+               n->link_layer_address);
+        }
+    }
+  }));
+  /* *INDENT-ON* */
+}