From: Mikolaj Kucharski Subject: urtwn: notice when the AP has gone away To: tech@openbsd.org Date: Sat, 31 Oct 2026 12:34:36 +0000 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