From 70d83c3d35119006e4cab273236fe162379e8a40 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 11 Jun 2026 09:49:41 +0900 Subject: [PATCH 1/3] Revert "network: check the received interface name is actually new" This reverts commit 176b8be10ffce2f8c1fc931a37904a528057016f. The check introduced by the commit is racy, as when format_ifname() is called, the interface may be already renamed (again) to another name. This is typically problematic when we swap interface names: ``` ip link set aaa name tmpname ip link set bbb name aaa ip link set tmpname bbb ``` When networkd received the notification about name change aaa -> tmpname, the interface is already renamed from tmpname to bbb. So, the reverted logic skips updating Link.ifname: ``` aaa: New interface name 'tmpname' received from the kernel does not correspond with the name currently configured on the actual interface 'bbb'. Ignoring. ``` On the second notification about name change bbb -> aaa, networkd fails to update the mapping Manager.links_by_name, as we skipped previous renaming and the mapping still has an outdated entry for 'aaa'. ``` bbb: Interface name change detected, renamed to aaa. aaa: Failed to manage link by its new name: File exists aaa: Could not process link message: File exists aaa: Failed aaa: State changed: configured -> failed ``` By reverting the commit, the issue is fixed. But the commit intended to fix another issue #20203. So this reintroduces #20203. Let's fix it in a later commit. Fixes #42527. Reintroduces #20203. --- src/network/networkd-link.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index c57657597f..1b47b8cc3f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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" @@ -2609,7 +2608,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,17 +2624,6 @@ 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); - return 0; - } - log_link_info(link, "Interface name change detected, renamed to %s.", ifname); hashmap_remove(link->manager->links_by_name, link->ifname); From c96f69e63c041965f77421f631dacb9ec2f7c769 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 11 Jun 2026 11:06:09 +0900 Subject: [PATCH 2/3] network: use hashmap_remove_value() on updating mapping for Link objects E.g. on issue #20203, we may wrongly remove entry for another interface. Let's mitigate issue on such situation for safety, though this does not solve the issue. --- src/network/networkd-link.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 1b47b8cc3f..20db5fa73a 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1150,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,7 +2594,7 @@ static int link_update_alternative_names(Link *link, sd_netlink_message *message 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); @@ -2626,7 +2626,7 @@ static int link_update_name(Link *link, sd_netlink_message *message) { 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) From 4cf729805942025f5105436f2564f4e31827648a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 11 Jun 2026 11:02:40 +0900 Subject: [PATCH 3/3] network: ignore stale interface renames After enumeration, networkd may receive RTM_NEWLINK messages carrying a stale interface name. This can happen when interface rename notifications are queued before link enumeration and processed afterwards. Previously, networkd could become confused by such a message and put the corresponding Link into the failed state. Avoid this by checking whether the new interface name is already in use by another interface and ignoring the rename if so. Fixes #20203. --- src/network/networkd-link.c | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 20db5fa73a..f505031e1e 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2593,6 +2593,19 @@ 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_value(link->manager->links_by_name, *n, link); @@ -2624,6 +2637,47 @@ static int link_update_name(Link *link, sd_netlink_message *message) { if (streq(ifname, link->ifname)) return 0; + /* 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_value(link->manager->links_by_name, link->ifname, link);