[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