Skip to content

Wi-Fi: a deaf radio is joined again, one join at a time; a boot-time invented address gives way to a lease - #1074

Draft
pdfinn wants to merge 4 commits into
masterfrom
fix/wifi-rejoin-dhcp
Draft

pdfinn wants to merge 4 commits into
masterfrom
fix/wifi-rejoin-dhcp

Conversation

@pdfinn

@pdfinn pdfinn commented Nov 5, 2026

Copy link
Copy Markdown
Member

Three Wi-Fi fixes and one wired fix, found on the Pi 3B+ / Pi 4 boards on 2026-10-11..14 and left on fix/wifi-deaf, fix/etherusb-dhcp-retry and board/wifi-ethtimers without a PR. Rebased onto current master here.

  • wifi: a radio that hears nothing is joined again. An access point that restarts forgets its stations and says nothing. A board whose replies go out the wire stayed "associated" and heard nothing on Wi-Fi for 11 hours. wifiwatch now watches the radio's receive count: after 30 s of silence it ARP-probes the gateway out of the radio, and after 60 s it joins again. The wait doubles up to 16 min when a join doesn't help.
  • etherusb: a boot-time invented address is replaced when a lease comes. A board that booted while the router was off kept QEMU's 10.0.2.15 until it was rebooted. It now asks for a lease every 2 min while the invented address stands.
  • ether4330: one join at a time. Two overlapping joins (the supplicant after "link lost", plus the watch) panicked the kernel with double sleep. Joins now serialise on a per-controller QLock.
  • wifi: the watch joins only a radio that says it is associated. It leaves a radio that has reported its loss to the supplicant.

Rebase notes

  • etherusb.b: master's dhcp() now takes a port-68 conversation, so relet() gets one from dhcpconn(), as the boot path does. The fix is folded into the etherusb commit, so every commit compiles.
  • I dropped the original last commit's change to the re-join address print. The Pi 5 work (local commit "osinit: a Wi-Fi re-join is a new pairwise key, and leaves the address and routes alone", not yet pushed) removes the re-DHCP on re-join altogether, so that hunk would have been dead and a conflict. This PR leaves that block as it is on master. Whichever of the two lands second gets a single conflict hunk around the wifiwatch signature and its comment.

Testing

  • Limbo: osinit.b and etherusb.b compile at each of the 4 commits.
  • os/bcm/ether4330.c compiles with the baremetal harness's kernel flags. No new diagnostics compared with master.
  • Not yet re-run on a board or under the QEMU harness after the rebase. The original commits were board-tested on 2026-10-14 (deaf radio recovered from the join; AP-off for 5 min; router power-cut). That's why this is a draft.

🤖 Generated with Claude Code

pdfinn and others added 4 commits November 5, 2026 09:50
An access point that restarts forgets its stations, and a station
learns it only when it next transmits and is turned away. The Pi 3
with its wired interface on the same subnet sends its replies out the
wire -- 31 frames on the radio in a day -- so after the network's
power came back on 2026-10-11 the board stayed associated as far as
the firmware knew, kept the access point's beacons, reported nothing,
and heard nothing on Wi-Fi from 10:20 until a join was asked for by
hand at 21:30. #792's watcher acts on link-down, deauthentication and
disassociation; none came.

wifiwatch now watches the radio's receive count. The network is quiet
at times, so silence alone proves nothing: after 30 s of it the
gateway is asked for its address, broadcast out of the radio itself
from the radio's own address, and a live link answers. After 60 s the
network is joined again ("essid <name>" to the interface, what the
supplicant writes after "link lost"); the handshake that follows is
counted as a re-join and the address is asked for again, as before.
A join that does not help doubles the next wait, up to 16 minutes, so
a network that is gone is not joined every minute.

On the board the deaf radio recovered at once from that same join:
a fresh handshake, frames arriving, 192.168.1.107 answering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FsRUgdQ797dzH667L9i6HK
…omes

With no DHCP answer at boot, etherusb takes QEMU's fixed addresses
(10.0.2.15, gateway 10.0.2.2). On a real board that booted while its
router was off, that address stayed for good: the link watcher retried
DHCP only when the interface had no address, and it had one. The wired
network was gone until a reboot after the router came back (2026-10-14,
a Pi 3B+ that rebooted during a power-cut of the router).

The watcher now asks for a lease every two minutes while the address is
the invented one, keeping it meanwhile. When a server answers, the
invented address and its default route go and the boot's configuration
runs again: the lease, the route, the clock, the path check. Two
minutes, not every poll: a failed ask holds UDP port 68 for about 45 s,
and the radio's DHCP client needs that port too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each join waits on joinr for the firmware's answer, and a Rendez holds
one sleeper. A second join written while the first waited -- the
supplicant re-associating after "link lost" while osinit's Wi-Fi watch
asked for a join of its own -- panicked the kernel with "double sleep"
(2026-10-14 08:47, the access point unplugged; wljoin <- etherbcmctl <-
etherwrite). Any two overlapping "essid" or "auth" writes could do it.

wljoin now holds a per-controller QLock across the join and its wait;
a second join waits for the first to finish and then joins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the 5 GHz radio switched off at the router for five minutes, the
firmware reported the loss and the supplicant began re-associating, and
the watch joined as well: at 60 s and again at 120 s, while the
supplicant was mid-association. ether4330 now takes one join at a time,
so this no longer panics, but once the network came back it meant two
joins, two handshakes and two address requests, 20 s apart.

The watch exists for a radio that says it is associated and hears
nothing -- an access point that forgot it without a word. A radio that
says it is not associated has reported its loss, and the supplicant is
already on it, so the watch now leaves it alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant