[PATCH] ap: never disconnect idle stations sooner than ap_max_inactivity
hc86862997 at gmail.com
hc86862997 at gmail.com
Sat Sep 26 08:39:57 PDT 2026
Hi,
hostapd adopts the client-advertised BSS Max Idle Period from the
(re)association request and lets it override the AP-wide
ap_max_inactivity, in two places:
- ap_handle_timer():
if (sta->max_idle_period)
max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
- ap_sta_alloc(): the same computation for the initial timer
registration.
Some clients (Apple) advertise a short BSS Max Idle Period (~20 units,
about 20 s) for power saving - they expect the AP to buffer frames for
them, not to be disconnected. The result is deterministic: with the
screen off (idle), the client is removed exactly 20 s after
association:
deauthenticated due to inactivity (timer DEAUTH/REMOVE)
Raising ap_max_inactivity has no effect for such clients because the
adoption overwrites it - we verified 20-second kicks with
ap_max_inactivity=86400 present in the running configuration (MT7996
AP-MLD, hostapd 831364bf). Clients without the element keep using the
AP-wide timeout, which is why the bug looks client-specific.
Fix: never disconnect a station sooner than the AP-wide
ap_max_inactivity - floor the adopted value in both places.
Verification: an Apple MLO client left idle with the screen off was
previously kicked at exactly 20 s on every attempt; with this patch it
stays associated indefinitely. Clients that do not advertise BSS Max
Idle Period are unaffected. (Also reported to OpenWrt:
https://github.com/openwrt/openwrt/issues/25416 )
---
src/ap/sta_info.c | 36 ++++++++++++++++++++++++++++++++----
1 file changed, 32 insertions(+), 4 deletions(-)
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -631,8 +631,22 @@
return;
}
- if (sta->max_idle_period)
- max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+ if (sta->max_idle_period) {
+ /*
+ * Never disconnect a station sooner than the AP-wide
+ * ap_max_inactivity: some clients (e.g. Apple) advertise a
+ * short BSS Max Idle Period for power saving and expect
+ * buffered keep-alives, not disassociation. Adopting the
+ * value as-is made idle stations get kicked ~20s after
+ * association when the data poll went unanswered.
+ */
+ int adopted = (sta->max_idle_period * 1024 + 999) / 1000;
+
+ if (adopted < max_inactivity)
+ adopted = max_inactivity;
+ max_inactivity = adopted;
+ }
if ((sta->flags & WLAN_STA_ASSOC) &&
(sta->timeout_next == STA_NULLFUNC ||
@@ -945,8 +959,22 @@
}
sta->supported_rates_len = i;
- if (sta->max_idle_period)
- max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+ if (sta->max_idle_period) {
+ /*
+ * Never disconnect a station sooner than the AP-wide
+ * ap_max_inactivity: some clients (e.g. Apple) advertise a
+ * short BSS Max Idle Period for power saving and expect
+ * buffered keep-alives, not disassociation. Adopting the
+ * value as-is made idle stations get kicked ~20s after
+ * association when the data poll went unanswered.
+ */
+ int adopted = (sta->max_idle_period * 1024 + 999) / 1000;
+
+ if (adopted < max_inactivity)
+ adopted = max_inactivity;
+ max_inactivity = adopted;
+ }
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
More information about the Hostap
mailing list