p.simianer.de /blog

Home WiFi Setup With 802.11s (Meshing) And 802.11r (Roaming)

Moving to Berlin, during the apartment hunt, my wife and I learned about particular type of apartment that's apparently specific to Berlin (at least within Germany): In old buildings [Altbau] the apartments are often stretched out quite a bit, because they range from the front of the building (facing the street) all the way to the courtyard (and those buildings are massive). The walk-through [Durchgangszimmer] room with a window to (on the corner of) the courtyard is referred to as a "Berliner Zimmer", and is often used as a dining room and/or kitchen.

We were lucky enough to find an apartment with four (bed-)rooms and a Berliner Zimmer. The appartment is about 23 meters long (30 m corner to corner), front to back! While that is pretty great, it's not so easy to get a good (or even usable) Wi-Fi signal throughout the apartment using a single access point (AP). Standard consumer-grade routers with included Wi-Fi are supposed to have a range of about 100 meters, but I don't think that this calculation included a number of quite thick walls (well, it's an Altbau) that need to be crossed to cover all rooms.

To make matters worse, the main internet connection provided through TV cable (Vodafone, 200 Mbit/s down, 50 up) is located in the room facing the street, i.e. in one of the extreme points of the apartment.

Installing a fairly beefy (I guess?) Ubiquiti UniFi AP AC PRO, we found that there was only a "single bar" of Wi-Fi left in the Berliner Zimmer, and left two additional rooms without any usable signal.

Now you could simply buy a so-called repeater (also called Wi-Fi booster), plug it in and call it a day. However, since I run a rather extensive home network (consisting of about 10 Raspberry Pis — at the time of writing — and even more "smart" plugs) this cheap solution — as tried with a generic device from Amazon — didn't cut it: Speed was effectively halved for the clients connected to the repeater and there were problems with applications like video streaming using a Google Chromecast Ultra and audio streaming via AirPlay (running a combination of shairport-sync and forked-daapd).

Coincidentally (maybe not), at the same time I got generally more interested in networking, a topic of which I only had a rough understanding before. I also decided that I wanted to run all of my network appliances with free software. In the case of the router and APs an obvious choice is OpenWRT, which I wanted to try for some time.

The access point could be flashed relatively easily with OpenWRT 19.0.7. Reassured, I bought a second one of the same model thinking there must some technology built in OpenWRT to link two APs together in order to achieve a better Wi-Fi experience throughout the apartment.

Wi-Fi Mesh AKA 802.11s

Turns out I was right. While the gold standard to do this would be to simply have an Ethernet cable running to the second AP, that's unfortunately not possible in our case since we do not actually own the apartment and thus cannot run cables wherever we like. The next best thing I learned is a so called Wi-Fi mesh.

Network Topology

A picture is worth a thousand words, so here's the network topology I envisioned (didn't find a free sketching tool ...):

802.11s Wi-Fi Mesh With OpenWRT

A Wi-Fi mesh node is quite similar to a aforementioned repeater, in the sense that it forwards wireless network packets / frames from one access point to another, eventually to one that is connected to the internet. However, with meshing, a proper, transparent, network is built up, instead of simply forwarding packets. I think of a mesh node like a switch with an uplink (that is probably a coarse simiplification, since there can be more than two nodes involved).

The (or rather a) standard for this is 802.11s which allows to interconnect Wi-Fi APs together. Just what I need.

In practice, with OpenWRT, separate Wi-Fi interfaces need to be created and connected with one another:

config wifi-iface 'mesh0'
	option device 'radio0'
	option ifname 'mesh0'
	option mode 'mesh'
	option mesh_id 'mesh'
	option encryption 'sae'
	option key 'anotherverysecretpassword'
	option network 'lan'
	option mesh_fwding '1'
 	option mesh_rssi_threshold '0'
With this configuration snippet in both of the AP's /etc/config/wireless, there will be an encrypted 802.11s link between the two devices. I'm a bit confused about the mesh_fwding option, as this setup seems to work either it being set to both '0' and '1' ...

From what I gather, it's best to leave mesh_rssi_threshold at 0 unless one has a reason to change that. One thing to note in this context is that the client is the one that makes the decision whether to switch APs, not the APs themselves.

Note that the settings for the actual, physical radio device also need to be the same (same channel etc.):

config wifi-device 'radio0'
	option type 'mac80211'
	option hwmode '11a'
	option htmode 'VHT80'
	option path 'pci0000:00/0000:00:00.0'
	option country 'DE'
	option channel '44'
	option legacy_rates '0'
	option distance '15'
(Don't mind the hwmode '11a': link.) Setting the distance is optional, but in my testing it seemed to help a bit with throughput. Crucial however was the setting of htmode 'VHT80', which is the channel width in a 802.11ac 5 GHz Wi-Fi. Lower widths reduced the throughput dramatically (~50%) when being connected to the mesh node, AP2.

Ideally I would use a separate radio device to run the Wi-Fi uplink on a different channel, but since I need the second device of the UI AC PRO for a legacy 2.4GHz network I cannot do that. Initially I thought I'd have to add another mesh on the 2.4 GHz radio too, but turns out I didn't — just having one uplink (preferably through 5 GHz) is sufficient and serves both the 5 GHz and 2.4 GHz clients. 4K streaming to my Chromecast Ultra works, and that's all that matters. 😉

The actual interface that the clients connect to is configured as follows, using the same radio device:

config wifi-iface 'radio0_iface'
	option device 'radio0'
	option ifname 'wlan0'
	option ssid 'myprettyssid'
	option mode 'ap'
	option encryption 'sae-mixed'
	option key 'mysupersecretpassword'
	option network 'lan'
	option skip_inactivity_poll '0'
 	# macfilter
	option macfilter 'allow'
	list maclist 'de:ad:be:ef:13:37'
	[...]
(Interestingly I actually couldn't connect using WPA3 [sae-mixed] with iPhones [Xr and Xs], instead I had to downgrade encryption to WPA2 [psk2]!)

Since the mesh interfaces and the client facing ones are in the network 'lan', all should be able to talk to each other (same in both APs):

config interface 'lan'
        option type 'bridge'
        option proto 'dhcp'
        option hostname 'AP2'
        option ifname 'eth0.1 eth0.2'
        option stp '1'
        option igmp_snooping '1'
        option delegate '0'
eth0.1 and eth0.2 refer to the two ports of the AC PRO. In Luci (the web-interface of OpenWRT) the interface will look like this (all interfaces, including the mesh, bridged together):

With those settings, all devices should be able to connect to the internet through AP1 as well as AP2 and also see each other as if they were directly connected with one another via a wired switch (ok, latency may be a bit higher).

(Fast) Roaming AKA 802.11r

Now this is all well and good for standard stuff like browsing the web, but one of the most frequent things we do in the home network is listening to web radio over AirPlay. For this we have two devices (Raspberry Pis using shairport-sync and forked-daapd for synchronisation) connected to active speakers, one in the Berliner Zimmer, wired to an Ethernet port on AP2, and one in the living room, connected to AP1 via Wi-Fi.

In principle the vanilla 802.11s setup worked, but every time one of us moved with the phone around the apartment while being connected to one (or both) of the AirPlay devices, playback just stopped, as the phone (iPhones to be specific) connected to another AP. I set-up static leases for all known devices, but it seems that this application wasn't working with the 802.11s setup as described above.

Reading a bit, I found out that I needed to add roaming capability between the two APs. There's another extension to the 802.11 standard that describes just that: 802.11r. I'm not qualified enough to discuss details, but in principle the described method speeds up the authentication with the APs/mesh points, such that the connection appears to be seamless to the client. Alongside with other extensions (namely 802.11k and v) I should be able to walk around the apartment with the phone and it would just connect to the AP with the strongest signal, while not ever losing connection.

To get this to work on OpenWRT, I found that the following snippet does the trick (after much trial and error):

config wifi-iface 'radio0_iface'
	[...]
	option ieee80211r '1'
	option ieee80211k '1'
	option ieee80211v '1'
	option bss_transition '1'
	option ft_psk_generate_local '1'
	option ft_over_ds '1'
	option pmk_r1_push '1'
	option mobility_domain '1337'
	option nasid 'FEECDAFB1658'
	[...]
I only got it to work after reading this reddit thread, albeit some stuff may be not needed anymore, specifically the explicit setting of nasid (here it's just the MAC address without the ':'), and mobility_domain. Regarding ft_over_ds, I've found it more reliable than the alternative, setting it to 0, i.e. "ft over the air".

One important tidbit here was that 802.11w needs to turned off! With it on it would work, but my iPhone with iOS 14.X would be able to switch the AP just once, after that it would lose the connection, and it would not authenticate with the Wi-Fi anymore. This situation could only be resolved by restarting the phone. So make sure that it's off:

config wifi-iface 'radio0_iface'
	[...]
	option ieee80211w '0'
	[...]

Making a Google Chromecast Work

Somehow our phones were able to connect to the Chromecast device once and then lost the connection after a while. To make matters worse it was hard to re-establish the link, or the device wasn't even visible to the mobile devices. That was quite inconvenient since there's no separate remote for the Chromecast, and play/pause/stop is done through the mobile device.

Luckily, there's the following workaround, which alleviated these issues:

config wifi-iface 'radio0_iface'
	[...]
	option disassoc_low_ack '0'
	[...]
While I did not observe excessive connection failures of the Chromecast device (what this is supposed to control), this option however seemed to help. People on the internet also suggest to turn on igmp_snooping but that doesn't seem to make a difference (as I understood IGMP is only relevant for IPTV anyway, not streaming traffic like from Netflix or Amazon Prime Video). But I believe it won't hurt to have it on (see above).

Conclusion

I assumed that I had a fairly standard setup in mind when buying the second AP, and that it should be a no-brainer to set that up the way I intended (two APs, one connected to the internet, one connected over the air). However, I didn't find exactly that, so I wrote this blog post. There's more advanced stuff like B.A.T.M.A.N., but I couldn't really understand what the advantage was, especially for home use, and with 802.11s+r everything I wanted seems to work just fine:

The 5 GHz uplink also seems pretty stable:

#  iw dev mesh0 station dump | grep bitrate
	tx bitrate:	351.0 MBit/s VHT-MCS 3 80MHz VHT-NSS 3
	rx bitrate:	351.0 MBit/s VHT-MCS 3 80MHz VHT-NSS 3

After some fiddling with the positions of the APs I could get that to stable:

# iw dev mesh0 station dump | grep -i bitrate
	tx bitrate:	526.5 MBit/s VHT-MCS 4 80MHz VHT-NSS 3
	rx bitrate:	526.5 MBit/s VHT-MCS 4 80MHz VHT-NSS

I'm pretty happy that I could achieve all that using free software, namely OpenWRT — the Ubiquiti software is also quite nice, but you need to set-up an account with them to use your own hardware and that's usually a no-go for me. They also try to push you further into their ecosystem, as the web-interface prominently shows that you're missing out on some things if you didn't buy this or that device.

I hope this helps someone!

Appendix

Some interesting or helpful links: