OpenWiFi 5.1.0: uplink loss after config apply

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.

Root cause

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.

The fix (wlan‑ap rgnets-5.1.0)

CommitChange
62a43a33fhostapd: 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).
92cbcec63Default 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.

1 — Reproducer on the 186h, patched hostapd only

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.

CheckBeforeAfter
up replaced by hostapd’s bridgeevery flip0/4 flips
added_bridge in STATUS‑DRIVERset after default applynever
Recorder had to run network restartevery flipnever
up ifindex / bridge idchangedunchanged 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.

2 — Fleet on 0.006

Every AP upgraded through the normal rXg firmware path, took its real config from the rXg, and was inspected on the box.

APhostapd buildPatched wpadMaverick goneupUplink
WF186h · .103qca‑wifi‑6yesyesnetifd (7fff) · eth0 VLANs + LAN + wlan0/1ok
WF188n · .104qca‑wifi‑6yesyesnetifd (7fff) · eth0, eth1, wlan0/1ok
WF660a · .102qca‑wifi‑6yesyesnetifd (7fff) · eth0, wlan0/1ok
WF189 · .100qca‑wifi‑7yesyesnetifd (7fff) · eth0, eth1, 2G/5G/6Gok
EAP111 · .105base OpenWrtyesyesnetifd (7fff) · eth0, wlan0/1ok

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.

3 — Client sweep: every BSSID

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.

AP5 GHz2.4 GHz
WF189pass · ch 44 · 15/15pass · ch 6 · 15/15
WF660apass · ch 149 · 15/15pass · ch 6 · 15/15
WF186hpass · ch 157 · 15/15pass · ch 11 · 15/15
WF188npass · ch 157 · 15/15pass · ch 6 · 15/15
EAP111pass · ch 149 · 15/15pass · 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.

What this does not show.

Method

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.