network: ignore stale interface renames (#42545)
Fixes #20203. Fixes #42527.
This commit is contained in:
+56
-15
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user