network/netdev: do not update MAC address if netdev is already running

Follow-up for 17c5337f7b.

Older kernels (older than v6.5) refuse RTM_NEWLINK messages with IFLA_ADDRESS
attribute when the netdev already exists and is running, even if the MAC
address is unchanged.

So, let's not set IFLA_ADDRESS or IFLA_MTU if they are unchanged, and
set the attributes only when we can update them.
This commit is contained in:
Yu Watanabe 2024-11-13 11:44:46 +09:00
parent ab6d427547
commit 09db410606
8 changed files with 73 additions and 6 deletions

View File

@ -281,12 +281,17 @@ static void bridge_init(NetDev *netdev) {
b->ageing_time = USEC_INFINITY;
}
static bool bridge_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
return true;
}
const NetDevVTable bridge_vtable = {
.object_size = sizeof(Bridge),
.init = bridge_init,
.sections = NETDEV_COMMON_SECTIONS "Bridge\0",
.post_create = netdev_bridge_post_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.can_set_mac = bridge_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};

View File

@ -4,10 +4,15 @@
#include "dummy.h"
static bool dummy_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
return true;
}
const NetDevVTable dummy_vtable = {
.object_size = sizeof(Dummy),
.sections = NETDEV_COMMON_SECTIONS,
.create_type = NETDEV_CREATE_INDEPENDENT,
.can_set_mac = dummy_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};

View File

@ -254,6 +254,10 @@ static int netdev_geneve_verify(NetDev *netdev, const char *filename) {
return 0;
}
static bool geneve_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
return true;
}
static void geneve_init(NetDev *netdev) {
Geneve *v = GENEVE(netdev);
@ -272,6 +276,7 @@ const NetDevVTable geneve_vtable = {
.fill_message_create = netdev_geneve_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_geneve_verify,
.can_set_mac = geneve_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};

View File

@ -636,15 +636,32 @@ finalize:
static bool netdev_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
assert(netdev);
assert(netdev->manager);
assert(hw_addr);
if (hw_addr->length <= 0)
return false;
if (!NETDEV_VTABLE(netdev)->can_set_mac)
return true;
Link *link;
if (link_get_by_index(netdev->manager, netdev->ifindex, &link) < 0)
return true; /* The netdev does not exist yet. We can set MAC address. */
return NETDEV_VTABLE(netdev)->can_set_mac(netdev, hw_addr);
if (hw_addr_equal(&link->hw_addr, hw_addr))
return false; /* Unchanged, not necessary to set. */
/* Soem netdevs refuse to update MAC address even if the interface is not running, e.g. ipvlan.
* Some other netdevs have the IFF_LIVE_ADDR_CHANGE flag and can update update MAC address even if
* the interface is running, e.g. dummy. For those cases, use custom checkers. */
if (NETDEV_VTABLE(netdev)->can_set_mac)
return NETDEV_VTABLE(netdev)->can_set_mac(netdev, hw_addr);
/* Before ad72c4a06acc6762e84994ac2f722da7a07df34e and 0ec92a8f56ff07237dbe8af7c7a72aba7f957baf
* (both in v6.5), the kernel refuse to set MAC address for existing netdevs even if it is unchanged.
* So, by default, do not update MAC address if the it is running. See eth_prepare_mac_addr_change(),
* which is called by eth_mac_addr(). Note, the result of netif_running() is mapped to operstate
* and flags. See rtnl_fill_ifinfo() and dev_get_flags(). */
return link->kernel_operstate == IF_OPER_DOWN &&
(link->flags & (IFF_RUNNING | IFF_LOWER_UP | IFF_DORMANT)) == 0;
}
static bool netdev_can_set_mtu(NetDev *netdev, uint32_t mtu) {
@ -653,10 +670,22 @@ static bool netdev_can_set_mtu(NetDev *netdev, uint32_t mtu) {
if (mtu <= 0)
return false;
if (!NETDEV_VTABLE(netdev)->can_set_mtu)
return true;
Link *link;
if (link_get_by_index(netdev->manager, netdev->ifindex, &link) < 0)
return true; /* The netdev does not exist yet. We can set MTU. */
return NETDEV_VTABLE(netdev)->can_set_mtu(netdev, mtu);
if (mtu < link->min_mtu || link->max_mtu < mtu)
return false; /* The MTU is out of range. */
if (link->mtu == mtu)
return false; /* Unchanged, not necessary to set. */
/* Some netdevs cannot change MTU, e.g. vxlan. Let's use the custom checkers in such cases. */
if (NETDEV_VTABLE(netdev)->can_set_mtu)
return NETDEV_VTABLE(netdev)->can_set_mtu(netdev, mtu);
/* By default, allow to update the MTU. */
return true;
}
static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message *m) {

View File

@ -4,10 +4,15 @@
#include "netdevsim.h"
static bool netdevsim_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
return true;
}
const NetDevVTable netdevsim_vtable = {
.object_size = sizeof(NetDevSim),
.sections = NETDEV_COMMON_SECTIONS,
.create_type = NETDEV_CREATE_INDEPENDENT,
.can_set_mac = netdevsim_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};

View File

@ -1119,6 +1119,11 @@ static void netdev_tunnel_init(NetDev *netdev) {
t->ttl = DEFAULT_IPV6_TTL;
}
static bool tunnel_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
assert(IN_SET(netdev->kind, NETDEV_KIND_GRETAP, NETDEV_KIND_IP6GRETAP, NETDEV_KIND_ERSPAN));
return true;
}
const NetDevVTable ipip_vtable = {
.object_size = sizeof(Tunnel),
.init = netdev_tunnel_init,
@ -1188,6 +1193,7 @@ const NetDevVTable gretap_vtable = {
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.needs_reconfigure = tunnel_needs_reconfigure,
.can_set_mac = tunnel_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
@ -1213,6 +1219,7 @@ const NetDevVTable ip6gretap_vtable = {
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.needs_reconfigure = tunnel_needs_reconfigure,
.can_set_mac = tunnel_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
@ -1238,6 +1245,7 @@ const NetDevVTable erspan_vtable = {
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.needs_reconfigure = tunnel_needs_reconfigure,
.can_set_mac = tunnel_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};

View File

@ -21,11 +21,16 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink
return 0;
}
static bool vrf_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
return true;
}
const NetDevVTable vrf_vtable = {
.object_size = sizeof(Vrf),
.sections = NETDEV_COMMON_SECTIONS "VRF\0",
.fill_message_create = netdev_vrf_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.can_set_mac = vrf_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};

View File

@ -197,6 +197,10 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return 0;
}
static bool vxlan_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
return true;
}
static bool vxlan_can_set_mtu(NetDev *netdev, uint32_t mtu) {
assert(netdev);
@ -452,6 +456,7 @@ const NetDevVTable vxlan_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_vxlan_is_ready_to_create,
.config_verify = netdev_vxlan_verify,
.can_set_mac = vxlan_can_set_mac,
.can_set_mtu = vxlan_can_set_mtu,
.needs_reconfigure = vxlan_needs_reconfigure,
.iftype = ARPHRD_ETHER,