network: ignore stale interface renames (#42545)

Fixes #20203.
Fixes #42527.
This commit is contained in:
Yu Watanabe
2026-06-15 07:20:52 +09:00
committed by GitHub
+56 -15
View File
@@ -30,7 +30,6 @@
#include "errno-util.h"
#include "ethtool-util.h"
#include "event-util.h"
#include "format-ifname.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "logarithm.h"
@@ -1151,14 +1150,14 @@ static Link *link_drop(Link *link) {
link_clean(link);
STRV_FOREACH(n, link->alternative_names)
hashmap_remove(link->manager->links_by_name, *n);
hashmap_remove(link->manager->links_by_name, link->ifname);
hashmap_remove_value(link->manager->links_by_name, *n, link);
hashmap_remove_value(link->manager->links_by_name, link->ifname, link);
/* bonding master and its slaves have the same hardware address. */
hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
/* The following must be called at last. */
assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link);
hashmap_remove_value(link->manager->links_by_index, INT_TO_PTR(link->ifindex), link);
if (notify)
manager_notify_hook_filters(link->manager);
@@ -2594,8 +2593,21 @@ static int link_update_alternative_names(Link *link, sd_netlink_message *message
if (strv_equal(altnames, link->alternative_names))
return 0;
/* See the comment in link_update_name(). If one of the new alternative names is already in use by
* another interface, the alternative-name information in this message is likely stale. Ignore the
* entire IFLA_ALT_IFNAME attribute in that case. */
STRV_FOREACH(n, altnames) {
Link *existing;
if (link_get_by_name(link->manager, *n, &existing) >= 0 && existing != link) {
log_link_debug(link,
"Alternative interface name update detected, but the new name '%s' is already in use by another interface (ifindex=%i), ignoring the update as it may be stale.",
*n, existing->ifindex);
return 0;
}
}
STRV_FOREACH(n, link->alternative_names)
hashmap_remove(link->manager->links_by_name, *n);
hashmap_remove_value(link->manager->links_by_name, *n, link);
strv_free_and_replace(link->alternative_names, altnames);
@@ -2609,7 +2621,6 @@ static int link_update_alternative_names(Link *link, sd_netlink_message *message
}
static int link_update_name(Link *link, sd_netlink_message *message) {
char ifname_from_index[IF_NAMESIZE];
const char *ifname;
int r;
@@ -2626,20 +2637,50 @@ static int link_update_name(Link *link, sd_netlink_message *message) {
if (streq(ifname, link->ifname))
return 0;
r = format_ifname(link->ifindex, ifname_from_index);
if (r < 0)
return log_link_debug_errno(link, r, "Could not get interface name for index %i.", link->ifindex);
if (!streq(ifname, ifname_from_index)) {
log_link_debug(link, "New interface name '%s' received from the kernel does not correspond "
"with the name currently configured on the actual interface '%s'. Ignoring.",
ifname, ifname_from_index);
/* Check if the new interface name is already used by another interface. If so, the rename is likely
* stale. Consider the following race:
*
* 1. networkd enables rtnl matches in manager_connect_rtnl().
* 2. The kernel sends an RTM_NEWLINK notification for an interface (say, ifindex=2, ifname="eth0"),
* and the notification message (A) is queued in networkd's sd-netlink object.
* 3. The interface is renamed (say, "eth0" -> "enp0"), e.g. by udevd. The kernel sends another
* RTM_NEWLINK notification for the rename, and the notification message (B) is also queued in
* networkd's sd-netlink object.
* 4. The kernel detects another new interface (say, ifindex=3). Since the name "eth0" is now unused,
* the interface is named "eth0".
* 5. networkd enumerates links and creates Link objects for:
* - ifindex=2, ifname="enp0"
* - ifindex=3, ifname="eth0"
* 6. After enumeration, when processing message (A), networkd becomes confused and thinks that the
* interface with ifindex=2 was renamed from "enp0" to "eth0". However, it fails to update the
* Manager.links_by_name hashmap because "eth0" is already used by the interface with ifindex=3.
* 7. When processing message (B), networkd thinks that the interface with ifindex=2 has been renamed
* again from "eth0" to "enp0", and renames the Link object back to "enp0".
*
* When this happens, we get something like the following:
*
* systemd-networkd[5164]: enp0: Interface name change detected, renamed to eth0.
* systemd-networkd[5164]: eth0: Failed to manage link by its new name: File exists
* systemd-networkd[5164]: Could not process link message: File exists
* systemd-networkd[5164]: eth0: Failed
* systemd-networkd[5164]: eth0: State changed: initialized -> failed
* systemd-networkd[5164]: eth0: Interface name change detected, renamed to enp0.
*
* See also #20203.
*
* To avoid the race, ignore the rename. A subsequent RTM_NEWLINK message should eventually provide
* the current interface name, e.g. message (B) above. */
Link *existing;
if (link_get_by_name(link->manager, ifname, &existing) >= 0 && existing != link) {
log_link_debug(link,
"Interface name change detected, but the new interface name '%s' is already in use by another interface (ifindex=%i), ignoring the rename as it may be stale.",
ifname, existing->ifindex);
return 0;
}
log_link_info(link, "Interface name change detected, renamed to %s.", ifname);
hashmap_remove(link->manager->links_by_name, link->ifname);
hashmap_remove_value(link->manager->links_by_name, link->ifname, link);
r = free_and_strdup(&link->ifname, ifname);
if (r < 0)