From c631f2de6dd06b4cbb92bf8398839b882344fd25 Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Mon, 26 Sep 2016 10:40:02 +0200 Subject: [PATCH] VPP-363: add ability to change mac address of the interface Added ability to change interface address. Added new CLI and API functions. Change-Id: Ia336bc75ad8c5858c26f39af851485c4c6f19f58 Signed-off-by: Pavel Kotucek --- vnet/vnet/devices/dpdk/device.c | 1 + vnet/vnet/ethernet/arp.c | 37 ++++++++++++++++++++++++++ vnet/vnet/ethernet/ethernet.h | 2 ++ vnet/vnet/fib/ip6_fib.h | 2 ++ vnet/vnet/interface.c | 57 +++++++++++++++++++++++++++++++++++++++++ vnet/vnet/interface.h | 7 +++++ vnet/vnet/interface_cli.c | 48 ++++++++++++++++++++++++++++++++++ vnet/vnet/interface_funcs.h | 5 ++++ vnet/vnet/ip/ip6_neighbor.c | 20 +++++++++++++++ 9 files changed, 179 insertions(+) diff --git a/vnet/vnet/devices/dpdk/device.c b/vnet/vnet/devices/dpdk/device.c index 5d1fcf60d2d..cd22d8a4689 100644 --- a/vnet/vnet/devices/dpdk/device.c +++ b/vnet/vnet/devices/dpdk/device.c @@ -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) diff --git a/vnet/vnet/ethernet/arp.c b/vnet/vnet/ethernet/arp.c index 541355f9f89..7d6184381c9 100644 --- a/vnet/vnet/ethernet/arp.c +++ b/vnet/vnet/ethernet/arp.c @@ -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 = ðernet_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 * diff --git a/vnet/vnet/ethernet/ethernet.h b/vnet/vnet/ethernet/ethernet.h index 3b2ef875290..1855b9144db 100644 --- a/vnet/vnet/ethernet/ethernet.h +++ b/vnet/vnet/ethernet/ethernet.h @@ -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 */ diff --git a/vnet/vnet/fib/ip6_fib.h b/vnet/vnet/fib/ip6_fib.h index f6af993a3c2..3668234ec27 100644 --- a/vnet/vnet/fib/ip6_fib.h +++ b/vnet/vnet/fib/ip6_fib.h @@ -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. */ diff --git a/vnet/vnet/interface.c b/vnet/vnet/interface.c index 595ed1432bc..ca0df9d1073 100644 --- a/vnet/vnet/interface.c +++ b/vnet/vnet/interface.c @@ -39,6 +39,7 @@ #include #include +#include #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 = ðernet_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 * diff --git a/vnet/vnet/interface.h b/vnet/vnet/interface.h index 9f032e987bb..5fbf830c01c 100644 --- a/vnet/vnet/interface.h +++ b/vnet/vnet/interface.h @@ -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,...) \ diff --git a/vnet/vnet/interface_cli.c b/vnet/vnet/interface_cli.c index 611ce28140b..03b51332bae 100644 --- a/vnet/vnet/interface_cli.c +++ b/vnet/vnet/interface_cli.c @@ -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 'set interface mac address ' 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 ", + .function = set_interface_mac_address, +}; +/* *INDENT-ON* */ + /* * fd.io coding-style-patch-verification: ON * diff --git a/vnet/vnet/interface_funcs.h b/vnet/vnet/interface_funcs.h index 735d47ec192..f603a03e77b 100644 --- a/vnet/vnet/interface_funcs.h +++ b/vnet/vnet/interface_funcs.h @@ -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; diff --git a/vnet/vnet/ip/ip6_neighbor.c b/vnet/vnet/ip/ip6_neighbor.c index 11df776e1fc..5d059e9b9cf 100644 --- a/vnet/vnet/ip/ip6_neighbor.c +++ b/vnet/vnet/ip/ip6_neighbor.c @@ -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* */ +} -- 2.16.6