67 trials · roam mode · 2026-09-15 18:44 · 13 min
A single client was driven between every pair of BSSIDs in the testbed and each transition timed from the ROAM command to the CTRL-EVENT-CONNECTED event. Trials were interleaved across pairs rather than run in blocks, so anything drifting over the run spreads across all groups instead of loading onto one.
The comparison that matters is a cross-radio roam within one AP against the same 2.4↔ 5 GHz transition between two APs. The client does identical work either way; the only difference is whether the two radios share one ath11k_base.
Cross-radio roams on the same AP are not materially worse than the same transition between two APs (median 0.93s vs 1.06s, completion 95% vs 100%). On this chipset the shared peer table is not costing anything measurable.
Every completed roam is followed by a real DHCP exchange from the client. This is a forwarding test, not a DHCP test: sdan drops a downstream frame whose destination MAC is absent from the MAC table for that VLAN, so a station that is associated and authorized but missing from the table receives nothing — the server answers every message and the client keeps asking. That failure is only visible from the client.
These stations finished the 802.11 transition and then had no working dataplane. That is the reported symptom, reproduced.
| Chipset / class | Roams | Got a lease | No lease | median DHCP | max DHCP |
|---|---|---|---|---|---|
| ipq6018 / inter-ap-same-band-diff-channel | 20 | 15 (75%) | 5 | 0.30s | 0.40s |
| ipq6018 / inter-ap-cross-band | 20 | 12 (60%) | 8 | 0.30s | 0.40s |
| ipq6018 / static-control | 7 | 5 (71%) | 2 | 0.30s | 0.30s |
| ipq6018 / intra-ap-cross-radio | 19 | 19 (100%) | 0 | 0.30s | 0.40s |
| # | Class | From | To | Roam | Reason |
|---|---|---|---|---|---|
| 6 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 1.78s | no-lease(rc=1) |
| 8 | inter-ap-cross-band | 188n-99/2.4 | 188n-96/5 | 1.82s | no-lease(rc=1) |
| 8 | static-control | (none) | 188n-96/5 | — | no-lease(rc=1) |
| 15 | inter-ap-same-band-diff-channel | 188n-96/5 | 188n-99/5 | 2.07s | no-lease(rc=1) |
| 17 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 1.82s | no-lease(rc=1) |
| 19 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 1.77s | no-lease(rc=1) |
| 26 | inter-ap-same-band-diff-channel | 188n-99/2.4 | 188n-96/2.4 | 1.80s | no-lease(rc=1) |
| 28 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 1.99s | no-lease(rc=1) |
| 30 | inter-ap-cross-band | 188n-96/2.4 | 188n-99/5 | 1.80s | no-lease(rc=1) |
| 32 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 1.82s | no-lease(rc=1) |
| 32 | static-control | (none) | 188n-99/2.4 | — | no-lease(rc=1) |
| 43 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 1.75s | no-lease(rc=1) |
| 50 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 2.09s | no-lease(rc=1) |
| 52 | inter-ap-same-band-diff-channel | 188n-96/2.4 | 188n-99/2.4 | 1.79s | no-lease(rc=1) |
| 55 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 1.78s | no-lease(rc=1) |
| Group | Trials | Completed | ≤2s | ≤5s | median | p90 | max | |
|---|---|---|---|---|---|---|---|---|
| inter-ap-same-band-diff-channel Two APs, same band, different channel. A re-tune without a band change. | 20 | 20 (100%) | 18 (90%) | 20 (100%) | 1.02s | 2.07s | 2.09s | |
| inter-ap-cross-band The same 2.4↔ 5 GHz transition, but between two APs — separate ath11k instances, so no shared peer table. The control that matters. | 20 | 20 (100%) | 20 (100%) | 20 (100%) | 1.06s | 1.82s | 1.82s | |
| static-control | 7 | 0 (0%) | 0 (0%) | 0 (0%) | — | — | — | |
| intra-ap-cross-radio Both radios of one AP. On a shared-peer-table SoC this is the only path where a cross-radio peer delete can be attributed to the wrong radio. | 20 | 19 (95%) | 19 (95%) | 19 (95%) | 0.93s | 1.08s | 1.19s |
| Group | Trials | Completed | ≤2s | ≤5s | median | p90 | max | |
|---|---|---|---|---|---|---|---|---|
| (none) → 188n-96/5 | 3 | 0 (0%) | 0 (0%) | 0 (0%) | — | — | — | |
| (none) → 188n-99/2.4 | 2 | 0 (0%) | 0 (0%) | 0 (0%) | — | — | — | |
| (none) → 188n-99/5 | 2 | 0 (0%) | 0 (0%) | 0 (0%) | — | — | — | |
| 188n-96/2.4 → 188n-96/5 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 1.05s | 1.08s | 1.08s | |
| 188n-96/2.4 → 188n-99/2.4 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 0.70s | 1.79s | 1.79s | |
| 188n-96/2.4 → 188n-99/5 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 0.98s | 1.80s | 1.80s | |
| 188n-96/5 → 188n-96/2.4 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 0.73s | 0.96s | 0.96s | |
| 188n-96/5 → 188n-99/2.4 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 1.75s | 1.82s | 1.82s | |
| 188n-96/5 → 188n-99/5 | 5 | 5 (100%) | 4 (80%) | 5 (100%) | 1.02s | 2.07s | 2.07s | |
| 188n-99/2.4 → 188n-96/2.4 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 0.64s | 1.80s | 1.80s | |
| 188n-99/2.4 → 188n-96/5 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 1.06s | 1.82s | 1.82s | |
| 188n-99/2.4 → 188n-99/5 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 0.94s | 1.19s | 1.19s | |
| 188n-99/5 → 188n-96/2.4 | 5 | 5 (100%) | 5 (100%) | 5 (100%) | 1.78s | 1.82s | 1.82s | |
| 188n-99/5 → 188n-96/5 | 5 | 5 (100%) | 4 (80%) | 5 (100%) | 1.13s | 2.09s | 2.09s | |
| 188n-99/5 → 188n-99/2.4 | 5 | 4 (80%) | 4 (80%) | 4 (80%) | 0.76s | 0.91s | 0.91s |
| Mode | Count | Share of all trials |
|---|---|---|
| stayed_on_source | 1 | 1.5% |
stayed_on_source the client never left the old BSS. landed_on_other it moved somewhere unasked. ended_disconnected it fell off entirely. roam_rejected wpa_supplicant refused the command, usually a target missing from the scan cache.
| BSSID | AP | Chipset | Band | Freq |
|---|---|---|---|---|
| d4:ba:ba:ab:ba:ab | 188n-99 | ipq6018 | 2.4 GHz | 2437 MHz |
| d4:ba:ba:ab:ba:aa | 188n-99 | ipq6018 | 5 GHz | 5785 MHz |
| d4:ba:ba:ab:ce:6d | 188n-96 | ipq6018 | 2.4 GHz | 2412 MHz |
| d4:ba:ba:ab:ce:6c | 188n-96 | ipq6018 | 5 GHz | 5180 MHz |
Chipset topology is read from sysfs, not inferred from the model name: on IPQ6018 both phy0 and phy1 resolve to soc/c000000.wifi, while on IPQ5018 phy1 resolves to soc:wifi1@c000000 — a separate device with its own peer table.
ATTACHed and the CTRL-EVENT-CONNECTED event timestamped on arrival, so the figure is not quantised by a polling interval.| # | Round | Class | From | To | Latency | Outcome |
|---|---|---|---|---|---|---|
| 1 | 1 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 1.05s | completed |
| 2 | 1 | inter-ap-same-band-diff-channel | 188n-96/2.4 | 188n-99/2.4 | 0.95s | completed |
| 3 | 1 | inter-ap-same-band-diff-channel | 188n-96/5 | 188n-99/5 | 0.87s | completed |
| 4 | 1 | inter-ap-same-band-diff-channel | 188n-99/2.4 | 188n-96/2.4 | 0.83s | completed |
| 5 | 1 | inter-ap-cross-band | 188n-96/2.4 | 188n-99/5 | 0.92s | completed |
| 6 | 1 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 1.78s | completed |
| 7 | 1 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 0.98s | completed |
| 8 | 1 | inter-ap-cross-band | 188n-99/2.4 | 188n-96/5 | 1.82s | completed |
| 9 | 1 | static-control | (none) | 188n-96/5 | — | completed |
| 10 | 1 | intra-ap-cross-radio | 188n-96/5 | 188n-96/2.4 | 0.96s | completed |
| 11 | 1 | intra-ap-cross-radio | 188n-99/2.4 | 188n-99/5 | 1.19s | completed |
| 12 | 1 | intra-ap-cross-radio | 188n-96/2.4 | 188n-96/5 | 1.08s | completed |
| 13 | 1 | intra-ap-cross-radio | 188n-99/5 | 188n-99/2.4 | 0.73s | completed |
| 14 | 2 | inter-ap-same-band-diff-channel | 188n-99/2.4 | 188n-96/2.4 | 0.59s | completed |
| 15 | 2 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 1.07s | completed |
| 16 | 2 | inter-ap-same-band-diff-channel | 188n-96/5 | 188n-99/5 | 2.07s | completed |
| 17 | 2 | inter-ap-same-band-diff-channel | 188n-96/2.4 | 188n-99/2.4 | 0.70s | completed |
| 18 | 2 | static-control | (none) | 188n-99/2.4 | — | completed |
| 19 | 2 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 1.82s | completed |
| 20 | 2 | inter-ap-cross-band | 188n-99/2.4 | 188n-96/5 | 1.06s | completed |
| 21 | 2 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 1.77s | completed |
| 22 | 2 | inter-ap-cross-band | 188n-96/2.4 | 188n-99/5 | 1.03s | completed |
| 23 | 2 | intra-ap-cross-radio | 188n-96/2.4 | 188n-96/5 | 0.98s | completed |
| 24 | 2 | intra-ap-cross-radio | 188n-96/5 | 188n-96/2.4 | 0.66s | completed |
| 25 | 2 | intra-ap-cross-radio | 188n-99/5 | 188n-99/2.4 | — | stayed_on_source |
| 26 | 2 | intra-ap-cross-radio | 188n-99/2.4 | 188n-99/5 | 0.94s | completed |
| 27 | 2 | static-control | (none) | 188n-99/5 | — | completed |
| 28 | 3 | inter-ap-same-band-diff-channel | 188n-96/2.4 | 188n-99/2.4 | 0.61s | completed |
| 29 | 3 | inter-ap-same-band-diff-channel | 188n-99/2.4 | 188n-96/2.4 | 1.80s | completed |
| 30 | 3 | inter-ap-same-band-diff-channel | 188n-96/5 | 188n-99/5 | 1.16s | completed |
| 31 | 3 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 1.99s | completed |
| 32 | 3 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 0.70s | completed |
| 33 | 3 | inter-ap-cross-band | 188n-96/2.4 | 188n-99/5 | 1.80s | completed |
| 34 | 3 | inter-ap-cross-band | 188n-99/2.4 | 188n-96/5 | 1.20s | completed |
| 35 | 3 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 1.82s | completed |
| 36 | 3 | static-control | (none) | 188n-99/2.4 | — | completed |
| 37 | 3 | intra-ap-cross-radio | 188n-96/5 | 188n-96/2.4 | 0.72s | completed |
| 38 | 3 | intra-ap-cross-radio | 188n-99/5 | 188n-99/2.4 | 0.91s | completed |
| 39 | 3 | intra-ap-cross-radio | 188n-96/2.4 | 188n-96/5 | 0.98s | completed |
| 40 | 3 | intra-ap-cross-radio | 188n-99/2.4 | 188n-99/5 | 1.06s | completed |
| 41 | 4 | inter-ap-same-band-diff-channel | 188n-96/2.4 | 188n-99/2.4 | 0.67s | completed |
| 42 | 4 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 1.13s | completed |
| 43 | 4 | inter-ap-same-band-diff-channel | 188n-99/2.4 | 188n-96/2.4 | 0.64s | completed |
| 44 | 4 | inter-ap-same-band-diff-channel | 188n-96/5 | 188n-99/5 | 1.02s | completed |
| 45 | 4 | static-control | (none) | 188n-99/5 | — | completed |
| 46 | 4 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 0.92s | completed |
| 47 | 4 | inter-ap-cross-band | 188n-99/2.4 | 188n-96/5 | 0.88s | completed |
| 48 | 4 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 1.75s | completed |
| 49 | 4 | inter-ap-cross-band | 188n-96/2.4 | 188n-99/5 | 0.98s | completed |
| 50 | 4 | intra-ap-cross-radio | 188n-99/2.4 | 188n-99/5 | 0.92s | completed |
| 51 | 4 | intra-ap-cross-radio | 188n-96/5 | 188n-96/2.4 | 0.73s | completed |
| 52 | 4 | intra-ap-cross-radio | 188n-99/5 | 188n-99/2.4 | 0.76s | completed |
| 53 | 4 | intra-ap-cross-radio | 188n-96/2.4 | 188n-96/5 | 1.07s | completed |
| 54 | 4 | static-control | (none) | 188n-96/5 | — | completed |
| 55 | 5 | inter-ap-same-band-diff-channel | 188n-96/5 | 188n-99/5 | 0.89s | completed |
| 56 | 5 | inter-ap-same-band-diff-channel | 188n-99/5 | 188n-96/5 | 2.09s | completed |
| 57 | 5 | inter-ap-same-band-diff-channel | 188n-99/2.4 | 188n-96/2.4 | 0.58s | completed |
| 58 | 5 | inter-ap-same-band-diff-channel | 188n-96/2.4 | 188n-99/2.4 | 1.79s | completed |
| 59 | 5 | inter-ap-cross-band | 188n-96/5 | 188n-99/2.4 | 0.69s | completed |
| 60 | 5 | inter-ap-cross-band | 188n-96/2.4 | 188n-99/5 | 0.97s | completed |
| 61 | 5 | inter-ap-cross-band | 188n-99/5 | 188n-96/2.4 | 1.78s | completed |
| 62 | 5 | inter-ap-cross-band | 188n-99/2.4 | 188n-96/5 | 0.89s | completed |
| 63 | 5 | static-control | (none) | 188n-96/5 | — | completed |
| 64 | 5 | intra-ap-cross-radio | 188n-96/5 | 188n-96/2.4 | 0.93s | completed |
| 65 | 5 | intra-ap-cross-radio | 188n-99/5 | 188n-99/2.4 | 0.71s | completed |
| 66 | 5 | intra-ap-cross-radio | 188n-99/2.4 | 188n-99/5 | 0.66s | completed |
| 67 | 5 | intra-ap-cross-radio | 188n-96/2.4 | 188n-96/5 | 1.05s | completed |