From: Benjamin Berg <benjamin.berg@xxxxxxxxx> 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@xxxxxxxxx> --- v3: - Check udev is running using udev_queue_get_udev_is_active. v2: - Break out of the loop on udev receive failure. We should give up as something bad has happened. Signed-off-by: Benjamin Berg <benjamin.berg@xxxxxxxxx> --- src/drivers/driver_nl80211.c | 92 ++++++++++++++++++++++++++++++++++++ src/drivers/drivers.mak | 13 +++++ 2 files changed, 105 insertions(+) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 0cc5b4b0e1..5c80b75eee 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" @@ -6242,6 +6245,55 @@ 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) { + struct udev_queue *queue = udev_queue_new(udev); + + /* + * We need to check if udev is running. This is just a long + * way of doing an access("/run/udev/control", F_OK); + */ + if (queue && udev_queue_get_udev_is_active(queue)) { + monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!monitor) + wpa_printf(MSG_ERROR, "nl80211: Failed to create udev monitor"); + } + if (queue) + udev_queue_unref(queue); + else + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to get udev queue"); + } 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)); @@ -6285,6 +6337,46 @@ 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) { + wpa_printf(MSG_ERROR, "nl80211: Error receiving device from udev"); + break; + } + + 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.50.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap