The iNet Wireless Daemon (or iwd for short) is the new superior tool for managing wireless devices on Linux. For the reason why and other details of iwd shall be left to others.

iwd has at time of writing two major issues:

  • requiring a (to time of writing) comparatively recent Linux version (<= 4.20) due to upstream bugfixes in its crypto subsystem
  • being so fast that it might bring up the wireless interface before in can be renamed by udev

While the first issue will resolve itself over time, the second one is a little harder to tackle.

The Problem

When a Linux system boots up, all the interfaces get initialized. They are named by linux in order of showing up, typically to eth0, eth1, etc. for wired cards and wlan0, wlan1 etc. for wireless cards. Which card gets which identifier depends solely on the order of getting noticed by Linux and it isn't even guaranteed that wireless cards will be named wlan*, on some systems they also end up with a eth*-indentifier. Therefore every piece of software doing things with interfaces based on their names is at risk of picking the wrong network card at some point.

To tackle this problem, systemd introduced persistent naming. Now your network cards are no longer named eth0, eth1 and wlan0, but enp2s5 or something like that, generated from properties of the network card. Much less beautiful, but persistent and therefore very desirable, fixing the race condition described above – however, doing so by introducing another race condition.

The reason for this is that this rename can only happen when the network interface is down. iwd, however, being much faster than other alternatives (namely wpa_supplicant and everything that's built on it) might already bring the wireless interface up before udev had the chance to rename the interface, which is then no longer possible, leaving it with the (race condition afflicted) kernel name.

The Solution

To solve this issue, we can delay starting iwd until the rename has happened, removing the second race condition from the mix. To do so we first need to find the name of our wireless interface via ip link, yielding something like

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT ...
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: wlp3s2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN ...
    link/ether 8c:7b:83:7d:04:82 brd ff:ff:ff:ff:ff:ff
3: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode ...
    link/ether e4:2d:9a:bb:e4:f4 brd ff:ff:ff:ff:ff:ff

The wireless device here is named wlp3s2, the wired one is enp4s0. In systemd, both cards get a device-unit upon appearance, for the wireless device this is sys-subsystem-net-devices-wlp3s2.device. They can be found in the unit list one gets from systemctl list-units.

To ensure iwd is only started after the wireless card was renamed to wlp3s2, we just need to make the unit starting it wait for said device-unit. This is easiest accomplished by issuing a systemctl edit iwd.service and entering:

[Unit]
Requires=sys-subsystem-net-devices-wlp3s2.device
After=sys-subsystem-net-devices-wlp3s2.device

systemd will then add those options dynamically to the loaded iwd.service-file when loading it, making it wait for the device wlp3s2 and start after that unit is done setting up, by when the rename has already been performed.

This, of course, requires manual intervention and only works for a single wireless network card, but should suffice for most setups and while not removing the root of the race condition resolves its symptoms with iwd.