[PATCH] nl80211: wait on udev when creating new device
Benjamin Berg
benjamin at sipsolutions.net
Thu Jun 5 00:05:02 PDT 2025
From: Benjamin Berg <benjamin.berg at intel.com>
udev/systemd will process new network device. This can result in various
issues as for example the MAC address may be randomized. Add the
appropriate integration to wait for the udev "add" event before
continuing to use the device.
This resolves race conditions when reading the MAC address during
interface creation (or even changing it right afterwards when creating a
P2P device).
Enable this feature by default. Systems that do not use udev need to
explicitly disable it at compile time.
See https://github.com/systemd/systemd/issues/13642
Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
src/drivers/driver_nl80211.c | 78 ++++++++++++++++++++++++++++++++++++
src/drivers/drivers.mak | 13 ++++++
2 files changed, 91 insertions(+)
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 7e876dcf80..67da682101 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -21,6 +21,9 @@
#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/errqueue.h>
+#ifndef CONFIG_DRIVER_NL80211_DISABLE_UDEV
+#include <libudev.h>
+#endif
#include "common.h"
#include "eloop.h"
@@ -6239,6 +6242,43 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
int ifidx;
int ret = -ENOBUFS;
+#ifndef CONFIG_DRIVER_NL80211_DISABLE_UDEV
+ /*
+ * systemd/udev insist on processing new interfaces and may
+ * randomize the MAC address. We need to avoid race conditions between
+ * hostap reading the MAC address and systemd/udev changing it.
+ * Setup a monitor and wait for an event for a "wlan" "net" device
+ * with the expected IFINDEX.
+ * We are guaranteed to receive an event because we install the monitor
+ * before creating it.
+ */
+ struct udev *udev;
+ struct udev_monitor *monitor = NULL;
+
+ udev = udev_new();
+ if (udev) {
+ monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (!monitor)
+ wpa_printf(MSG_ERROR, "nl80211: Failed to create udev monitor");
+ } else {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to udev");
+ }
+
+ udev_unref(udev);
+ udev = NULL;
+
+ if (monitor) {
+ if (udev_monitor_filter_add_match_subsystem_devtype(monitor,
+ "net",
+ "wlan") < 0 ||
+ udev_monitor_enable_receiving(monitor) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add match for udev events");
+ udev_monitor_unref(monitor);
+ monitor = NULL;
+ }
+ }
+#endif
+
wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
iftype, nl80211_iftype_str(iftype));
@@ -6282,6 +6322,44 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
if (ifidx <= 0)
return -1;
+#ifndef CONFIG_DRIVER_NL80211_DISABLE_UDEV
+ if (monitor) {
+ /* Set blocking mode on the FD */
+ int fd = udev_monitor_get_fd(monitor);
+ int flags = fcntl(fd, F_GETFL);
+
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+
+ while (1) {
+ struct udev_device *device;
+ const char *dev_ifidx_str;
+ int dev_ifidx;
+
+ device = udev_monitor_receive_device(monitor);
+ if (device == NULL)
+ wpa_printf(MSG_ERROR, "nl80211: Error receiving device from udev");
+
+ dev_ifidx_str = udev_device_get_property_value(device, "IFINDEX");
+ if (!dev_ifidx_str)
+ continue;
+
+ dev_ifidx = atoi(dev_ifidx_str);
+
+ udev_device_unref(device);
+ device = NULL;
+
+ /* We got an (add) event for the device; it is ready */
+ if (dev_ifidx == ifidx) {
+ wpa_printf(MSG_INFO, "nl80211: udev event received, interface is ready");
+ break;
+ }
+ }
+
+ udev_monitor_unref(monitor);
+ monitor = NULL;
+ }
+#endif
+
/*
* Some virtual interfaces need to process EAPOL packets and events on
* the parent interface. This is used mainly with hostapd.
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index a03d4a0345..e9a280c14e 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -57,6 +57,11 @@ NEED_LINUX_IOCTL=y
NEED_RFKILL=y
NEED_RADIOTAP=y
NEED_LIBNL=y
+ifdef CONFIG_DRIVER_NL80211_DISABLE_UDEV
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_DISABLE_UDEV
+else
+NEED_UDEV=y
+endif
endif
ifdef CONFIG_DRIVER_BSD
@@ -202,6 +207,14 @@ else
endif
endif
+# We need to integrate with udev when available, as it may randomize mac
+# addresses on newly created interfaces.
+ifeq ($(NEED_UDEV),y)
+PKG_CONFIG ?= pkg-config
+DRV_LIBS += $(shell $(PKG_CONFIG) --libs libudev)
+DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libudev)
+endif
+
##### COMMON VARS
DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
DRV_WPA_CFLAGS += $(DRV_CFLAGS)
--
2.49.0
More information about the Hostap
mailing list