WNM: AP configuration to allow BSS max idle period requests

Add a new hostapd configuration parameter max_acceptable_idle_period to
allow the AP to accept per-STA requested BSS max idle periods.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
Jouni Malinen 2024-05-29 19:41:59 +03:00 committed by Jouni Malinen
parent 6594ea9ef1
commit 58ac46baf7
9 changed files with 54 additions and 11 deletions

View file

@ -2558,6 +2558,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1; return 1;
} }
bss->bss_max_idle = val; bss->bss_max_idle = val;
} else if (os_strcmp(buf, "max_acceptable_idle_period") == 0) {
bss->max_acceptable_idle_period = atoi(pos);
} else if (os_strcmp(buf, "no_disconnect_on_group_keyerror") == 0) { } else if (os_strcmp(buf, "no_disconnect_on_group_keyerror") == 0) {
int val = atoi(pos); int val = atoi(pos);

View file

@ -530,6 +530,11 @@ wmm_ac_vo_acm=0
# period and require STAs to use protected keep-alive frames) # period and require STAs to use protected keep-alive frames)
#bss_max_idle=1 #bss_max_idle=1
# #
# Maximum acceptable BSS maximum idle period
# If this is set to a nonzero value, the AP allows STAs to request different
# maximum idle period values. This is in the units to 1000 TUs (1.024 s)
#max_acceptable_idle_period=600
#
# Allow STA to skip group key handshake without getting disconnection when # Allow STA to skip group key handshake without getting disconnection when
# BSS max idle period management is enabled. # BSS max idle period management is enabled.
# 0 = disconnect STA if it does not reply to group key handshake (default) # 0 = disconnect STA if it does not reply to group key handshake (default)

View file

@ -466,6 +466,7 @@ struct hostapd_bss_config {
int ap_max_inactivity; int ap_max_inactivity;
int bss_max_idle; int bss_max_idle;
int max_acceptable_idle_period;
bool no_disconnect_on_group_keyerror; bool no_disconnect_on_group_keyerror;
int ignore_broadcast_ssid; int ignore_broadcast_ssid;
int no_probe_resp_if_max_sta; int no_probe_resp_if_max_sta;

View file

@ -277,6 +277,14 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
return len; return len;
len += ret; len += ret;
if (sta->max_idle_period) {
ret = os_snprintf(buf + len, buflen - len,
"max_idle_period=%d\n", sta->max_idle_period);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
if (res >= 0) if (res >= 0)
len += res; len += res;

View file

@ -4328,6 +4328,19 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
sta->power_capab = 0; sta->power_capab = 0;
} }
if (elems->bss_max_idle_period &&
hapd->conf->max_acceptable_idle_period) {
u16 req;
req = WPA_GET_LE16(elems->bss_max_idle_period);
if (req <= hapd->conf->max_acceptable_idle_period)
sta->max_idle_period = req;
else if (hapd->conf->max_acceptable_idle_period >
hapd->conf->ap_max_inactivity)
sta->max_idle_period =
hapd->conf->max_acceptable_idle_period;
}
return WLAN_STATUS_SUCCESS; return WLAN_STATUS_SUCCESS;
} }
@ -4902,7 +4915,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211AX */ #endif /* CONFIG_IEEE80211AX */
p = hostapd_eid_ext_capab(hapd, p, false); p = hostapd_eid_ext_capab(hapd, p, false);
p = hostapd_eid_bss_max_idle_period(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p, sta->max_idle_period);
if (sta && sta->qos_map_enabled) if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p); p = hostapd_eid_qos_map_set(hapd, p);

View file

@ -147,7 +147,8 @@ u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
int hostapd_update_time_adv(struct hostapd_data *hapd); int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
u16 value);
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta); int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_SAE #ifdef CONFIG_SAE

View file

@ -737,7 +737,8 @@ int hostapd_update_time_adv(struct hostapd_data *hapd)
} }
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
u16 value)
{ {
u8 *pos = eid; u8 *pos = eid;
@ -756,6 +757,8 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
val = 1; val = 1;
if (val > 65535) if (val > 65535)
val = 65535; val = 65535;
if (value)
val = value;
WPA_PUT_LE16(pos, val); WPA_PUT_LE16(pos, val);
pos += 2; pos += 2;
/* Set the Protected Keep-Alive Required bit based on /* Set the Protected Keep-Alive Required bit based on

View file

@ -532,6 +532,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
struct sta_info *sta = timeout_ctx; struct sta_info *sta = timeout_ctx;
unsigned long next_time = 0; unsigned long next_time = 0;
int reason; int reason;
int max_inactivity = hapd->conf->ap_max_inactivity;
wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d", wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags, hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
@ -544,6 +545,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
return; return;
} }
if (sta->max_idle_period)
max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
if ((sta->flags & WLAN_STA_ASSOC) && if ((sta->flags & WLAN_STA_ASSOC) &&
(sta->timeout_next == STA_NULLFUNC || (sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC)) { sta->timeout_next == STA_DISASSOC)) {
@ -565,7 +569,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
* Anyway, try again after the next inactivity timeout, * Anyway, try again after the next inactivity timeout,
* but do not disconnect the station now. * but do not disconnect the station now.
*/ */
next_time = hapd->conf->ap_max_inactivity + fuzz; next_time = max_inactivity + fuzz;
} else if (inactive_sec == -ENOENT) { } else if (inactive_sec == -ENOENT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has lost its driver entry", "Station " MACSTR " has lost its driver entry",
@ -574,20 +578,19 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
/* Avoid sending client probe on removed client */ /* Avoid sending client probe on removed client */
sta->timeout_next = STA_DISASSOC; sta->timeout_next = STA_DISASSOC;
goto skip_poll; goto skip_poll;
} else if (inactive_sec < hapd->conf->ap_max_inactivity) { } else if (inactive_sec < max_inactivity) {
/* station activity detected; reset timeout state */ /* station activity detected; reset timeout state */
wpa_msg(hapd->msg_ctx, MSG_DEBUG, wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been active %is ago", "Station " MACSTR " has been active %is ago",
MAC2STR(sta->addr), inactive_sec); MAC2STR(sta->addr), inactive_sec);
sta->timeout_next = STA_NULLFUNC; sta->timeout_next = STA_NULLFUNC;
next_time = hapd->conf->ap_max_inactivity + fuzz - next_time = max_inactivity + fuzz - inactive_sec;
inactive_sec;
} else { } else {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been " "Station " MACSTR " has been "
"inactive too long: %d sec, max allowed: %d", "inactive too long: %d sec, max allowed: %d",
MAC2STR(sta->addr), inactive_sec, MAC2STR(sta->addr), inactive_sec,
hapd->conf->ap_max_inactivity); max_inactivity);
if (hapd->conf->skip_inactivity_poll) if (hapd->conf->skip_inactivity_poll)
sta->timeout_next = STA_DISASSOC; sta->timeout_next = STA_DISASSOC;
@ -603,7 +606,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
/* data nullfunc frame poll did not produce TX errors; assume /* data nullfunc frame poll did not produce TX errors; assume
* station ACKed it */ * station ACKed it */
sta->timeout_next = STA_NULLFUNC; sta->timeout_next = STA_NULLFUNC;
next_time = hapd->conf->ap_max_inactivity; next_time = max_inactivity;
} }
skip_poll: skip_poll:
@ -791,6 +794,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
{ {
struct sta_info *sta; struct sta_info *sta;
int i; int i;
int max_inactivity = hapd->conf->ap_max_inactivity;
sta = ap_get_sta(hapd, addr); sta = ap_get_sta(hapd, addr);
if (sta) if (sta)
@ -824,12 +828,15 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
} }
sta->supported_rates_len = i; sta->supported_rates_len = i;
if (sta->max_idle_period)
max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
"for " MACSTR " (%d seconds - ap_max_inactivity)", "for " MACSTR " (%d seconds - ap_max_inactivity)",
__func__, MAC2STR(addr), __func__, MAC2STR(addr),
hapd->conf->ap_max_inactivity); max_inactivity);
eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, eloop_register_timeout(max_inactivity, 0,
ap_handle_timer, hapd, sta); ap_handle_timer, hapd, sta);
} }

View file

@ -332,6 +332,9 @@ struct sta_info {
struct mld_info mld_info; struct mld_info mld_info;
u8 mld_assoc_link_id; u8 mld_assoc_link_id;
#endif /* CONFIG_IEEE80211BE */ #endif /* CONFIG_IEEE80211BE */
u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
* units of 1000 TUs */
}; };