WF‑186h and WF‑188n APs on OpenWiFi 5.1.0 (OpenWrt 25.12) lost their uplink about ten seconds after a configuration apply that reshaped the up bridge, and stayed dark until power‑cycled. This page records the root cause, the fix shipped as wlan‑ap rgnets-5.1.0-0.006 (92cbcec63c), and what was tested on the lab fleet on 2026‑10‑05.
hostapd’s nl80211 driver, in i802_check_bridge(), creates the configured bridge itself when it does not exist yet, and marks it as its own (added_bridge=1). Nothing ever clears that mark. When that BSS is later torn down, hostapd sets the bridge down and deletes it — by name.
The trigger was TIP’s factory default config. Its Admin‑UI interface (SSID “Maverick”) sits on br-admin_ui, which netifd deliberately never creates (auto=0), so hostapd created it and marked it. When the real config arrived, that radio’s bridge name became up but the mark stayed, and the next teardown of the radio deleted up — netifd’s live uplink bridge. hostapd then built a bare up of its own (Wi‑Fi only, priority 32768) that netifd could not adopt, so eth0 never rejoined.
Confirmed live on the 186h: added_bridge=1 appeared in hostapd’s STATUS-DRIVER after the default‑config apply. The WF‑189 runs the same code but did not lose its uplink, because its up is never rebuilt in that sequence.
A second bug made it hit every boot: our patch_ucentral.py injected logger() calls that ucode does not have, so every config apply exited 254 and the AP fell back to the default config on each boot. That is fixed separately in rxg !5594 / !5593.
| Commit | Change |
|---|---|
| 62a43a33f | hostapd: i802_check_bridge() never creates a missing bridge; it logs Bridge X does not exist; leaving IF unbridged and leaves the interface for netifd to add. Carried for all three hostapd builds: qca‑wifi‑6 (ipq50xx/ipq60xx), qca‑wifi‑7 (ipq53xx) and base OpenWrt (mediatek). |
| 92cbcec63 | Default config: drop the Admin‑UI service (Maverick), and port the 4.2.4 cleanup that was never carried to 5.1 — no static IP, DHCP server or SSH on the LAN interface. |
Ownership after the fix: hostapd makes the BSS, netifd manages up, and rg_wss_client + rxg_sdan handle client dataplane bridging.
Default config applied, 25 s wait, real config applied — the sequence that hijacked up on every attempt before the fix. Run on stock firmware with only the patched wpad swapped in, with a 1 s state recorder and a 50 ms sampler of up’s identity.
| Check | Before | After |
|---|---|---|
| up replaced by hostapd’s bridge | every flip | 0/4 flips |
| added_bridge in STATUS‑DRIVER | set after default apply | never |
| Recorder had to run network restart | every flip | never |
| up ifindex / bridge id | changed | unchanged throughout |
Each apply still causes a 2–3 s gateway blip while netifd re‑plumbs the bridge; every one recovered on its own. The new code path fired once per flip, for br-admin_ui.
Every AP upgraded through the normal rXg firmware path, took its real config from the rXg, and was inspected on the box.
| AP | hostapd build | Patched wpad | Maverick gone | up | Uplink |
|---|---|---|---|---|---|
| WF186h · .103 | qca‑wifi‑6 | yes | yes | netifd (7fff) · eth0 VLANs + LAN + wlan0/1 | ok |
| WF188n · .104 | qca‑wifi‑6 | yes | yes | netifd (7fff) · eth0, eth1, wlan0/1 | ok |
| WF660a · .102 | qca‑wifi‑6 | yes | yes | netifd (7fff) · eth0, wlan0/1 | ok |
| WF189 · .100 | qca‑wifi‑7 | yes | yes | netifd (7fff) · eth0, eth1, 2G/5G/6G | ok |
| EAP111 · .105 | base OpenWrt | yes | yes | netifd (7fff) · eth0, wlan0/1 | ok |
The 186h first booted 0.006 straight from a power cycle with the default config, then took the rXg config — the exact sequence that used to leave it dark. The 188n had lost its uplink on every 5.1 boot before the fix.
A WF‑189 sensor, unmanaged and radio split per band, joined each 2.4/5 GHz BSSID of OW mPSK WPA2 in turn, took a DHCP lease, then pinged 1.1.1.1 once a second for 15 s through that association. Pass = associated + leased + 15/15 replies.
| AP | 5 GHz | 2.4 GHz |
|---|---|---|
| WF189 | pass · ch 44 · 15/15 | pass · ch 6 · 15/15 |
| WF660a | pass · ch 149 · 15/15 | pass · ch 6 · 15/15 |
| WF186h | pass · ch 157 · 15/15 | pass · ch 11 · 15/15 |
| WF188n | pass · ch 157 · 15/15 | pass · ch 6 · 15/15 |
| EAP111 | pass · ch 149 · 15/15 | pass · ch 11 · 15/15 |
10/10. Every client landed on VLAN 301 (10.20.2.0/24) with a lease from 10.20.2.1.
Reproducer: ucentral.uc applied directly on the AP with rgminiloader stopped, so nothing else touched the config; a shell recorder logged bridge membership, gateway reachability and process state every second and would run network restart if up went missing or was replaced; a Python sampler logged up’s ifindex and bridge id every 50 ms. Fleet checks were read on each AP over ssh: strings /usr/sbin/wpad for the patch’s log text, the shipped default config, brctl show, and a gateway ping. Sweep: ~/lab/sensor/sweep.py driving sensord on the sensor, which places a STA on an exact BSSID, isolates it in its own Linux VRF via the production VrfDomain, leases by DHCP, and sends ICMP from a raw socket bound to the STA with SO_BINDTODEVICE, so a reply proves the path through that association and not the sensor’s own uplink. Targets were read live from each AP’s radios. Raw results: runs/sweep-20261005-060423/results.jsonl.