--- title: home WiFi setup configs: router: /etc/systemd/network/wan.network: | [Match] # enp1s0 is the name of my router's WAN interface. Name=enp1s0 [Network] # take a IP from my ISP's modem over DHCP. DHCP=yes # use Google's public DNS servers. DNS=8.8.8.8 DNS=8.8.4.4 # prefer DNS-over-TLS if available. DNSOverTLS=opportunistic # forward packets from this interface to other interfaces. IPForward=yes [DHCP] # some ISPs' DNS invent A records, so don't use them. UseDNS=no /etc/systemd/network/bridge.netdev: | [NetDev] Name=br0 Kind=bridge /etc/systemd/network/lan.network: | [Match] Name=enp2s0 enp3s0 [Network] Bridge=br0 /etc/systemd/network/bridge.network: | [Match] Name=br0 [Network] # the router's LAN IP. Address=192.168.16.1/24 # run a DHCP server. DHCPServer=yes # forward packets from this interface to other interfaces. IPForward=yes # make forwarded packets appear to come from this device, # a.k.a. enable NAT. IPMasquerade=yes [DHCPServer] DefaultLeaseTimeSec=600 # LAN DNS & Stubby DNS-over-TLS bridge. DNS=192.168.16.1 # fallback DNS. DNS=8.8.8.8 DNS=8.8.4.4 bouncer: /etc/systemd/network/bridge.netdev: | [NetDev] Name=br0 Kind=bridge /etc/systemd/network/wired.network: | [Match] Name=eth0 [Network] Bridge=br0 /etc/systemd/network/bridge.network: | [Match] Name=br0 [Network] DHCP=yes DNSOverTLS=opportunistic shared: /etc/hostapd/hostapd.conf: | bridge=br0 interface=wlan0 driver=nl80211 ssid=something-witty # select a channel automatically # using the ACS survey-based algorithm, # instead of setting a channel manually. channel=0 country_code=GB ieee80211d=1 # hw_mode=g for 802.11n, hw_mode=a for 802.11ac. hw_mode=g # 802.11n support. ieee80211n=1 wmm_enabled=1 # WPA2 encryption settings. # note that wpa_key_mgmt also has FT-PSK for 802.11r. wpa=2 wpa_passphrase=something-secret wpa_key_mgmt=WPA-PSK FT-PSK wpa_pairwise=TKIP rsn_pairwise=CCMP # 802.11i support. rsn_preauth=1 rsn_preauth_interfaces=br0 # 802.11r support. # mobility_domain must be shared across APs. # nas_identifier must be different between APs. mobility_domain=19fc nas_identifier=8e71540f0467 ft_psk_generate_local=1 diagram: | home network diagram internet internet router router internet -- router kitchen kitchen router -- kitchen powerline laptop laptop router -- laptop WiFi kitchen -- laptop WiFi bulb lightbulb kitchen -- bulb WiFi --- {% extends 'templates/base.html' %} {% block body %}

{{ title }}

{% macro file(d, path) %}
$ cat {{ path }}
{{ d[path] }}
{% endmacro %} {% markdown %}
contents…
[TOC]
There are two Access Points (APs) in my house: one in the living room, and one in the kitchen. They are connected via LAN-over-powerline, and have WiFi Roaming set up for a seamless connection throughout. {{ diagram | safe }} ## Roaming WiFi Roaming allows a client device (e.g. a phone) to transparently switch from one AP to another. Unlike "repeaters" or "boosters", which create a second WiFi network that the client needs to disconnect from and reconnect to, Roaming is _the same network_ across APs and allows for a seamless experience. This is implemented with two extensions of the WiFi spec, _802.11i_ and _802.11r_: - _802.11i_ is pre-authentication, which allows clients to start associating to a new AP while still connected to the previous one. - _802.11r_ is Fast Transition (FT), where APs can share keys with each other. On large networks (e.g. corporate ones) this is done properly where the APs communicate and use intermediate keys and the like, but for a home network with a Pre-Shared Key (PSK), the APs can derive all the keys themselves.
How does 802.11r actually work?
In a given roaming connection, there are 3 keys: - Master Session Key (MSK). - R0, a key derived from the MSK. - R1, a key derived from R0. There are 3 players: - The client. - R0KH, the R0 Key Handler, may or may not be an AP. - R1KH, the R1 Key Handler, an AP. In "pull mode": 1. The client connects to the first AP. 2. The client is given a key and an R0KH-ID (the _NAS Identifier_, named for [RADIUS'](https://en.wikipedia.org/wiki/RADIUS) [Network Access Server](https://en.wikipedia.org/wiki/Network_access_server)). 3. The client connects to the second AP. 4. The client gives R0KH-ID to the second AP. 5. The second AP, now R1KH, contacts R0KH using the ID and its own R1KH-ID. 6. R0KH sends R1KH a key R1 derived from R0 and R1KH-ID. 7. R1KH, gives the client R1. In "push mode": 1. R0KH knows all its APs. 2. R0KH sends each AP a unique R1 derived from R0 and the AP's R1KH-ID. 3. the client connects normally. For PSK mode, the MSK is the PSK, so any AP can generate R0 and R1 for any NAS Identifier.
## Router configuration { #router } My router is a [PC Engines APU 1d4](https://www.pcengines.ch/apu1d4.htm), which has: - 3 ethernet ports. - 1 wifi card. One of the ethernet ports is for WAN, and the other two and wifi card are LAN. The LAN interfaces are joined with a bridge, `br0`. ### WAN { #router/wan } This is a fairly normal [`systemd-networkd`](https://wiki.archlinux.org/index.php/Systemd-networkd) client config, with the addition of `IPForward=yes`: {{ file( configs.router, '/etc/systemd/network/wan.network' ) }} ### LAN { #router/lan } The LAN is controlled with `systemd-networkd` where possible, and the wifi is managed by `hostapd`. There are three parts to the `networkd` configuration: - Create a bridge network device. - Connect the LAN ethernet ports to it. - Configure the bridge to be a DHCP server & NAT gateway. First, create a bridge `br0`: {{ file( configs.router, '/etc/systemd/network/bridge.netdev' ) }} Connect the LAN ethernet ports to the bridge: {{ file( configs.router, '/etc/systemd/network/lan.network' ) }} Finally, configure the local network on the bridge itself. - `systemd-networkd` includes a minimal DHCP server, so also enable that. - Note the addition of both `IPForward=yes` and `IPMasquerade=yes` to set up NAT. {{ file( configs.router, '/etc/systemd/network/bridge.network' ) }} ### WiFi { #router/wifi } Because `hostapd` has its own bridge management, it isn't included in the `systemd-networkd` configuration. This configuration: - Uses our bridge `br0`. - Uses the standard Linux WiFi driver set (others exist for other chipsets). - Automatically selects a channel at start. - Enables 802.11n (`hostapd` also supports 802.11ac). - Enables WPA2 with a Pre-Shared Key (PSK). - Enables pre-authentication (802.11i) on our bridge `br0`. - Enables roaming (802.11r): - `nas_identifier` must be 6 bytes, and different between APs. I use the interface MAC. - `mobility_domain` must be 2 bytes, and is shared between APs. {{ file( configs.shared, '/etc/hostapd/hostapd.conf' ) }} ## Kitchen The WiFi in the kitchen is provided by a [Raspberry Pi 2B](https://www.raspberrypi.org/products/raspberry-pi-2-model-b/) and a USB WiFi adapter that supports _host mode_. I chose this hardware because I already owned it. ### LAN { #kitchen/lan } This is a simpler version of the [Router's LAN configuration](#router/lan), but with only two steps: - Create a bridge network device. - Connect the LAN ethernet ports to it. As before, create a bridge `br0`: {{ file( configs.bouncer, '/etc/systemd/network/bridge.netdev' ) }} Connect the ethernet port to the bridge: {{ file( configs.bouncer, '/etc/systemd/network/wired.network' ) }} Configure the local network on the bridge itself. Because this is not the primary router, it can be just another DHCP client: {{ file( configs.bouncer, '/etc/systemd/network/bridge.network' ) }} ### WiFi { #kitchen/wifi } This is basically the same as the [Router's WiFi configuration](#router/wifi), although remember to change the `nas_identifier`! {% endmarkdown %}
{% endblock %}