diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 7ec8740a2..eff7430ef 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2205,6 +2205,9 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, os_memset(¶ms, 0, sizeof(params)); + /* Save auth type, in case we need to retry after comeback timer. */ + wpa_s->sme.assoc_auth_type = auth_type; + #ifdef CONFIG_FILS if (auth_type == WLAN_AUTH_FILS_SK || auth_type == WLAN_AUTH_FILS_SK_PFS) { @@ -2711,15 +2714,109 @@ static void sme_deauth(struct wpa_supplicant *wpa_s, const u8 **link_bssids) } +static void sme_assoc_comeback_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->current_bss || !wpa_s->current_ssid) { + wpa_msg(wpa_s, MSG_DEBUG, + "SME: Comeback timeout expired; SSID/BSSID cleared; ignoring"); + return; + } + + wpa_msg(wpa_s, MSG_DEBUG, + "SME: Comeback timeout expired; retry associating with " + MACSTR "; mode=%d auth_type=%u", + MAC2STR(wpa_s->current_bss->bssid), + wpa_s->current_ssid->mode, + wpa_s->sme.assoc_auth_type); + + /* Authentication state was completed already; just try association + * again. */ + sme_associate(wpa_s, wpa_s->current_ssid->mode, + wpa_s->current_bss->bssid, + wpa_s->sme.assoc_auth_type); +} + + +static bool sme_try_assoc_comeback(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct ieee802_11_elems elems; + u32 timeout_interval; + unsigned long comeback_usec; + + if (ieee802_11_parse_elems(data->assoc_reject.resp_ies, + data->assoc_reject.resp_ies_len, + &elems, 0) == ParseFailed) { + wpa_msg(wpa_s, MSG_INFO, + "SME: Temporary assoc reject: failed to parse (Re)Association Response frame elements"); + return false; + } + + if (!elems.timeout_int) { + wpa_msg(wpa_s, MSG_INFO, + "SME: Temporary assoc reject: missing timeout interval IE"); + return false; + } + + if (elems.timeout_int[0] != WLAN_TIMEOUT_ASSOC_COMEBACK) { + wpa_msg(wpa_s, MSG_INFO, + "SME: Temporary assoc reject: missing association comeback time"); + return false; + } + + timeout_interval = WPA_GET_LE32(&elems.timeout_int[1]); + if (timeout_interval > 60000) { + /* This is unprotected information and there is no point in + * getting stuck waiting for very long duration based on it */ + wpa_msg(wpa_s, MSG_DEBUG, + "SME: Ignore overly long association comeback interval: %u TUs", + timeout_interval); + return false; + } + wpa_msg(wpa_s, MSG_DEBUG, "SME: Association comeback interval: %u TUs", + timeout_interval); + + comeback_usec = timeout_interval * 1024; + eloop_register_timeout(comeback_usec / 1000000, comeback_usec % 1000000, + sme_assoc_comeback_timer, wpa_s, NULL); + return true; +} + + void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, union wpa_event_data *data, const u8 **link_bssids) { + const u8 *bssid; + + if (wpa_s->valid_links) + bssid = wpa_s->ap_mld_addr; + else + bssid = wpa_s->pending_bssid; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: " "status code %d", MAC2STR(wpa_s->pending_bssid), data->assoc_reject.status_code); eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_assoc_comeback_timer, wpa_s, NULL); + + /* Authentication phase has been completed at this point. Check whether + * the AP rejected association temporarily due to still holding a + * security associationis with us (MFP). If so, we must wait for the + * AP's association comeback timeout period before associating again. */ + if (data->assoc_reject.status_code == + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + wpa_msg(wpa_s, MSG_DEBUG, + "SME: Temporary association reject from BSS " MACSTR, + MAC2STR(bssid)); + if (sme_try_assoc_comeback(wpa_s, data)) { + /* Break out early; comeback error is not a failure. */ + return; + } + } #ifdef CONFIG_SAE if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid && @@ -2731,12 +2828,6 @@ void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, if (wpa_s->current_bss) { struct wpa_bss *bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; - const u8 *bssid; - - if (wpa_s->valid_links) - bssid = wpa_s->ap_mld_addr; - else - bssid = wpa_s->pending_bssid; wpa_drv_deauthenticate(wpa_s, bssid, WLAN_REASON_DEAUTH_LEAVING); @@ -2847,8 +2938,10 @@ static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx) void sme_state_changed(struct wpa_supplicant *wpa_s) { /* Make sure timers are cleaned up appropriately. */ - if (wpa_s->wpa_state != WPA_ASSOCIATING) + if (wpa_s->wpa_state != WPA_ASSOCIATING) { eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_assoc_comeback_timer, wpa_s, NULL); + } if (wpa_s->wpa_state != WPA_AUTHENTICATING) eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); } @@ -2881,6 +2974,7 @@ void sme_deinit(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); + eloop_cancel_timeout(sme_assoc_comeback_timer, wpa_s, NULL); } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 36e682f3e..301be2051 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1033,6 +1033,7 @@ struct wpa_supplicant { bool ext_ml_auth; int *sae_rejected_groups; #endif /* CONFIG_SAE */ + u16 assoc_auth_type; } sme; #endif /* CONFIG_SME */