Download raw body.
urtwn: notice when the AP has gone away
Hi.
Below diff makes urtwn(4) notice when the access point (AP) is gone.
It does not depend on the power-off diff and applies on its own.
The diff was heavily assisted by Claude.
The other urtwn(4) diffs:
- https://marc.info/?l=openbsd-tech&m=179304787453866&w=2
- https://marc.info/?l=openbsd-tech&m=179338753723963&w=2
What it fixes: when I power cycle or reboot my AP, urtwn(4) never
notices. The interface stays associated, keeps its DHCP address
until the lease expires, and nothing reconnects it. With the diff,
OpenBSD notices within seconds that the AP is gone, scans, and
associates again once the AP is back.
What it does:
- An AP sends a beacon, a short announcement frame, about ten times a
second. net80211 already computes how many missed beacons mean the
AP is gone (ic_bmissthres, 30 beacons, about 3 seconds), but leaves
it to the driver to act on it. urtwn(4) did nothing with it.
- The diff remembers when urtwn(4) last received a management or data
frame from the AP. Every 2 seconds, while associated, the existing
calibration timer checks how long ago that was.
- When nothing has arrived from the AP for longer than the threshold,
urtwn(4) sends a probe request to the AP, a frame that asks the AP
to answer. If the AP answers with a probe response, net80211 clears
its management timer and nothing changes. If no answer comes within
5 seconds, ieee80211_watchdog() moves the interface to the SCAN
state, the same way it handles a failed association. The interface
then goes down and comes back when it finds the AP again.
Where the code comes from:
- The probe request part is the same as in iwm(4) and qwx(4), which do
this when their firmware reports missed beacons: iwm_rx_bmiss() in
sys/dev/pci/if_iwm.c and qwx_mac_handle_beacon_miss() in
sys/dev/ic/qwx.c. The comment and the IFF_DEBUG message are copied
from there.
https://github.com/openbsd/src/blob/master/sys/dev/pci/if_iwm.c
https://github.com/openbsd/src/blob/master/sys/dev/ic/qwx.c
- The rest is net80211 code that already exists: the threshold is set
in ieee80211_set_beacon_miss_threshold() (ieee80211_proto.c), the
probe response clears ic_mgt_timer in ieee80211_input.c, and
ieee80211_watchdog() (ieee80211.c) switches to SCAN when the timer
runs out.
- I'm not aware of a way to make this chip's firmware report missed
beacons, so the diff detects them in the driver. FreeBSD does the
same in software for this chip: rtwn(4) enables net80211's software
beacon miss handling (IEEE80211_CLONE_NOBEACONS in
sys/dev/rtwn/if_rtwn.c). OpenBSD's net80211 has no such generic
handling, so the check lives in urtwn(4).
https://cgit.freebsd.org/src/tree/sys/dev/rtwn/if_rtwn.c
Tested on RTL8188CUS with an athn(4) hostap AP, by rebooting and
power cycling the AP several times.
urtwn: notice when the AP has gone away
When the AP disappeared without telling us, for example after a
power cut, urtwn(4) never noticed: the interface stayed associated
and kept its DHCP lease until the lease expired. net80211 computes
a missed beacon threshold (ic_bmissthres) when we associate, but
acting on it is left to the driver, and urtwn had no code for it.
Remember when we last received a management or data frame from the
AP. The calibration timer, which runs every 2 seconds while
associated, checks this time. After ic_bmissthres beacon intervals
without anything from the AP (30 beacons, about 3 seconds), send a
probe request to the AP, as iwm(4) and qwx(4) do when their
firmware reports missed beacons. If the AP answers, its probe
response clears the management timer and nothing changes. If it
does not answer, ieee80211_watchdog() moves the interface to SCAN
after 5 seconds, and we associate again once the AP is back.
Tested on RTL8188CUS with an athn(4) hostap AP, by rebooting and
power cycling the AP.
Index: sys/dev/usb/if_urtwn.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_urtwn.c,v
diff -u -p -r1.114 if_urtwn.c
--- sys/dev/usb/if_urtwn.c 18 Jun 2025 13:48:23 -0000 1.114
+++ sys/dev/usb/if_urtwn.c 31 Oct 2026 12:25:52 -0000
@@ -197,6 +197,8 @@ struct urtwn_softc {
struct ieee80211_amrr amrr;
struct ieee80211_amrr_node amn;
+ uint64_t bss_rx_last; /* last frame from AP, ns */
+
#if NBPFILTER > 0
caddr_t sc_drvbpf;
@@ -372,6 +374,7 @@ uint32_t urtwn_read_4(void *, uint16_t);
int urtwn_llt_write(struct urtwn_softc *, uint32_t, uint32_t);
void urtwn_calib_to(void *);
void urtwn_calib_cb(struct urtwn_softc *, void *);
+void urtwn_check_bmiss(struct urtwn_softc *);
void urtwn_scan_to(void *);
void urtwn_next_scan(void *);
void urtwn_cancel_scan(void *);
@@ -912,6 +915,8 @@ urtwn_calib_cb(struct urtwn_softc *sc, v
s = splnet();
if (ic->ic_opmode == IEEE80211_M_STA) {
ieee80211_amrr_choose(&sc->amrr, ic->ic_bss, &sc->amn);
+ if (ic->ic_state == IEEE80211_S_RUN)
+ urtwn_check_bmiss(sc);
}
splx(s);
@@ -991,6 +996,47 @@ urtwn_newstate_cb(struct urtwn_softc *sc
rtwn_newstate(ic, cmd->state, cmd->arg);
}
+/*
+ * Nothing in urtwn(4) tells us when the AP stops sending beacons, so
+ * notice ourselves when nothing has been received from our AP for
+ * ic_bmissthres beacon intervals. Then do what iwm(4) and qwx(4) do on
+ * a missed beacon event from their firmware: send a probe request to
+ * the AP. If the AP does not answer within IEEE80211_TRANS_WAIT
+ * seconds, ieee80211_watchdog() moves us to SCAN.
+ * Called at splnet every 2 seconds while associated.
+ */
+void
+urtwn_check_bmiss(struct urtwn_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_sc.sc_ic;
+ struct ieee80211_node *ni = ic->ic_bss;
+ uint64_t now, silent_ms, thres_ms;
+
+ if (ni == NULL || ni->ni_intval == 0 || ic->ic_mgt_timer != 0)
+ return;
+
+ now = getnsecuptime();
+ if (now <= sc->bss_rx_last)
+ return;
+ silent_ms = (now - sc->bss_rx_last) / 1000000;
+ thres_ms = (uint64_t)ic->ic_bmissthres * ni->ni_intval *
+ IEEE80211_DUR_TU / 1000;
+ if (silent_ms <= thres_ms)
+ return;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: receiving no beacons from %s; checking if "
+ "this AP is still responding to probe requests\n",
+ sc->sc_dev.dv_xname, ether_sprintf(ni->ni_macaddr));
+ /*
+ * Rather than go directly to scan state, try to send a
+ * directed probe request first. If that fails then the
+ * state machine will drop us into scanning after timing
+ * out waiting for a probe response.
+ */
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+}
+
void
urtwn_updateslot(struct ieee80211com *ic)
{
@@ -1267,6 +1313,16 @@ urtwn_rx_frame(struct urtwn_softc *sc, u
bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
}
#endif
+
+ /*
+ * Any management or data frame sent by our AP proves it is there.
+ * Its auth/assoc responses keep this fresh until we reach RUN.
+ */
+ if (ic->ic_bss != NULL &&
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+ IEEE80211_FC0_TYPE_CTL &&
+ IEEE80211_ADDR_EQ(wh->i_addr2, ic->ic_bss->ni_bssid))
+ sc->bss_rx_last = getnsecuptime();
ni = ieee80211_find_rxnode(ic, wh);
memset(&rxi, 0, sizeof(rxi));
--
Regards,
Mikolaj
urtwn: notice when the AP has gone away