1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
|
---
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: |
<svg xmlns='http://www.w3.org/2000/svg'
viewBox='0.00 0.00 466.59 106.00'
width='467pt' height='106pt'
style='font-family: "Times New Roman", Times, serif; font-size: 12pt; font-weight: lighter;'>
<g id='graph0' class='graph' transform='scale(1 1) rotate(0) translate(4 102)'>
<title>home network diagram</title>
<g id='home-network-diagram/internet' class='node'>
<title>internet</title>
<polygon fill='none' points='43.19,-8.05 45.61,-8.15 48.01,-8.3 50.37,-8.49 52.69,-8.74 54.96,-9.03 57.16,-9.36 59.29,-9.75 61.34,-10.18 63.31,-10.65 65.18,-11.16 66.95,-11.71 68.61,-12.31 70.17,-12.94 71.6,-13.61 72.92,-14.31 74.11,-15.04 75.18,-15.8 76.11,-16.59 76.92,-17.41 77.6,-18.25 78.15,-19.11 78.57,-19.99 78.87,-20.89 79.04,-21.8 79.08,-22.72 79.01,-23.65 78.81,-24.59 78.51,-25.53 78.1,-26.47 77.58,-27.41 76.96,-28.35 76.25,-29.28 75.46,-30.2 74.57,-31.11 73.61,-32.01 72.58,-32.89 71.47,-33.75 70.31,-34.59 69.09,-35.41 67.81,-36.2 66.49,-36.96 65.13,-37.69 63.73,-38.39 62.29,-39.06 60.83,-39.69 59.33,-40.29 57.82,-40.84 56.28,-41.35 54.73,-41.82 53.16,-42.25 51.58,-42.64 49.99,-42.97 48.4,-43.26 46.79,-43.51 45.18,-43.7 43.57,-43.85 41.95,-43.95 40.34,-44 38.72,-44 37.1,-43.95 35.48,-43.85 33.87,-43.7 32.26,-43.51 30.66,-43.26 29.06,-42.97 27.47,-42.64 25.89,-42.25 24.32,-41.82 22.77,-41.35 21.24,-40.84 19.72,-40.29 18.23,-39.69 16.76,-39.06 15.33,-38.39 13.92,-37.69 12.56,-36.96 11.24,-36.2 9.97,-35.41 8.74,-34.59 7.58,-33.75 6.48,-32.89 5.44,-32.01 4.48,-31.11 3.6,-30.2 2.8,-29.28 2.09,-28.35 1.47,-27.41 0.96,-26.47 0.54,-25.53 0.24,-24.59 0.05,-23.65 -0.03,-22.72 0.02,-21.8 0.19,-20.89 0.48,-19.99 0.9,-19.11 1.45,-18.25 2.13,-17.41 2.94,-16.59 3.88,-15.8 4.94,-15.04 6.14,-14.31 7.45,-13.61 8.89,-12.94 10.44,-12.31 12.1,-11.71 13.88,-11.16 15.75,-10.65 17.71,-10.18 19.76,-9.75 21.89,-9.36 24.1,-9.03 26.36,-8.74 28.68,-8.49 31.05,-8.3 33.44,-8.15 35.87,-8.05 38.31,-8 40.75,-8 43.19,-8.05'/>
<text text-anchor='middle' x='39.53' y='-21.8'>internet</text>
</g>
<g id='home-network-diagram/router' class='node'>
<title>router</title>
<polygon fill='none' points='170.05,-44 116.05,-44 116.05,-8 170.05,-8 170.05,-44'/>
<text text-anchor='middle' x='143.05' y='-21.8'>router</text>
</g>
<g id='edge1' class='edge'>
<title>internet -- router</title>
<path fill='none' d='M78.33,-26C90.85,-26 104.49,-26 115.93,-26'/>
</g>
<g id='node3' class='node'>
<title>kitchen</title>
<polygon fill='none' points='327.91,-78 270.49,-78 270.49,-42 327.91,-42 327.91,-78'/>
<text text-anchor='middle' x='299.2' y='-55.8'>kitchen</text>
</g>
<g id='edge2' class='edge'>
<title>router -- kitchen</title>
<path d='M170.2,-31.77C198.03,-37.91 241.9,-47.58 270.36,-53.86'/>
<text text-anchor='middle' x='220.33' y='-51.8'>powerline</text>
</g>
<g id='node4' class='node'>
<title>laptop</title>
<text text-anchor='middle' x='425.31' y='-13.8'>laptop</text>
</g>
<g id='edge3' class='edge'>
<title>router -- laptop</title>
<path fill='none' stroke-dasharray='5,2' d='M170.21,-24.18C195.79,-22.49 235.85,-20.07 270.6,-19 315.41,-17.62 367.61,-17.62 398.19,-17.78'/>
<text text-anchor='middle' x='299.2' y='-21.8'>WiFi</text>
</g>
<g id='edge4' class='edge'>
<title>kitchen -- laptop</title>
<path stroke-dasharray='5,2' d='M328.05,-50.57C349.07,-43.46 377.69,-33.78 398.25,-26.82'/>
<text text-anchor='middle' x='359.92' y='-45.8'>WiFi</text>
</g>
<g id='node5' class='node'>
<title>bulb</title>
<text text-anchor='middle' x='425.31' y='-75.8'>lightbulb</text>
</g>
<g id='edge5' class='edge'>
<title>kitchen -- bulb</title>
<path stroke-dasharray='5,2' d='M328.05,-64.49C347.06,-67.55 372.3,-71.62 392.17,-74.82'/>
<text text-anchor='middle' x='359.92' y='-73.8'>WiFi</text>
</g>
</g>
</svg>
---
{% extends 'templates/base.html' %}
{% block body %}
<nav>
<a href='/projects'>> projects</a>
</nav>
<article>
<h1>{{ title }}</h1>
{% macro file(d, path) %}
<pre><code>$ cat {{ path }}
{{ d[path] }}</code></pre>
{% endmacro %}
{% markdown %}
<details markdown='1'>
<summary>contents…</summary>
<div markdown='block'>
[TOC]
</div>
</details>
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.
<details markdown='1'>
<summary>How does 802.11r actually work?</summary>
<div markdown='block'>
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.
</div>
</details>
## 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 %}
</article>
{% endblock %}
|