summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorAlvin Šipraga <alsi@bang-olufsen.dk>2022-12-21 16:14:28 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2023-01-12 13:28:36 +0900
commit9c5b8d46e5c16bbf2bf40217d8f1b68bf76091c0 (patch)
tree009abdfe5596b0b3bfd426f4b75413b85ed6b0e8 /src/network
parentea577968540db7eb4d9b9922506dc0cad0426ec7 (diff)
downloadsystemd-9c5b8d46e5c16bbf2bf40217d8f1b68bf76091c0.tar.gz
network: fix race between RTM_NEWLINK and NL82011_CMD_NEW_INTERFACE
When a new wireless network interface is created by the kernel, it emits both RTM_NEWLINK and NL80211_CMD_NEW_INTERFACE. These events can arrive in either order and networkd must behave correctly in both cases. The typical case is that RTM_NEWLINK is handled first, in which case networkd creates a Link object and starts tracking it. When the NL80211_CMD_NEW_INTERFACE message is handled, networkd then populates the Link object with relevant wireless properties such as wireless interface type (managed, AP, etc.). In the event that the order is reversed however, networkd will fail to populate these wireless properties because at the time of processing the nl80211 message, the link is considered unknown. In that case, a debug message is emitted: systemd-networkd[467]: nl80211: received new_interface(7) message for link '109' we don't know about, ignoring. This is problematic because after the subsequent RTM_NEWLINK message, networkd will have an incomplete view of the link. In particular, if a .network configuration matches on some of the missing wireless properties, such as WLANInterfaceType=, then it will never match. The above race can be reproduced by using the mac80211_hwsim driver. Suppose that there exists a .network configuration: [Match] WLANInterfaceType=ap ... Now loop the creation/destruction of such an AP interface: while true do iw dev wlan0 interface add uap0 type __ap iw dev uap0 del done The above debug message from networkd will then be observed very quickly. And in that event, the .network file will fail to match. To address the above race, have the nl80211 message handler store the interface index in a set in case a Link object is not found on NL80211_CMD_NEW_INTERFACE. The handler for RTM_NEWLINK can then query this set, and explicitly request the wireless properties from nl80211 upon the creation of the Link object.
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkd-link.c9
-rw-r--r--src/network/networkd-manager.c1
-rw-r--r--src/network/networkd-manager.h1
-rw-r--r--src/network/networkd-wifi.c13
4 files changed, 24 insertions, 0 deletions
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 95a7d75e53..e43c04567a 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2515,6 +2515,15 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
log_link_debug(link, "Saved new link: ifindex=%i, iftype=%s(%u), kind=%s",
link->ifindex, strna(arphrd_to_name(link->iftype)), link->iftype, strna(link->kind));
+ /* If contained in this set, the link is wireless and the corresponding NL80211_CMD_NEW_INTERFACE
+ * message arrived too early. Request the wireless link information again.
+ */
+ if (set_remove(manager->new_wlan_ifindices, INT_TO_PTR(link->ifindex))) {
+ r = link_get_wlan_interface(link);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to get wireless interface, ignoring: %m");
+ }
+
*ret = TAKE_PTR(link);
return 0;
}
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index f82117cc1e..ad668215e7 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -614,6 +614,7 @@ Manager* manager_free(Manager *m) {
m->request_queue = ordered_set_free(m->request_queue);
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
+ m->new_wlan_ifindices = set_free(m->new_wlan_ifindices);
m->links_by_name = hashmap_free(m->links_by_name);
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
m->links_by_dhcp_pd_subnet_prefix = hashmap_free(m->links_by_dhcp_pd_subnet_prefix);
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index e6183af0e4..c9cbcf9289 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -38,6 +38,7 @@ struct Manager {
bool manage_foreign_rules;
Set *dirty_links;
+ Set *new_wlan_ifindices;
char *state_file;
LinkOperationalState operational_state;
diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c
index e35857261e..1a3754b29c 100644
--- a/src/network/networkd-wifi.c
+++ b/src/network/networkd-wifi.c
@@ -92,6 +92,19 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me
if (r < 0) {
log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.",
strna(nl80211_cmd_to_string(cmd)), cmd, ifindex);
+
+ /* The NL80211_CMD_NEW_INTERFACE message might arrive before RTM_NEWLINK, in which case a
+ * link will not have been created yet. Store the interface index such that the wireless
+ * properties of the link (such as wireless interface type) are queried again after the link
+ * is created.
+ */
+ if (cmd == NL80211_CMD_NEW_INTERFACE) {
+ r = set_ensure_put(&manager->new_wlan_ifindices, NULL, INT_TO_PTR(ifindex));
+ if (r < 0)
+ log_warning_errno(r, "Failed to add new wireless interface index to set, ignoring: %m");
+ } else if (cmd == NL80211_CMD_DEL_INTERFACE)
+ set_remove(manager->new_wlan_ifindices, INT_TO_PTR(ifindex));
+
return 0;
}