From e2578c2f2bc4d0d45b1a1d46bae923c2e2e2967c Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sat, 26 Sep 2026 21:22:56 +0300 Subject: [PATCH] wifi: mac80211: Gracefully deauthenticate on association timeout When an association with AP times out, and the connection is an Enhanced Privacy Protection (EPP) connection where the TK is already installed, send an explicit deauthentication frame to the AP so it would clear its local state. Not doing so might impact (fail) future connection attempts with this AP (that might think that the station is still connected). This cannot be done by user space, since as part of the association timeout flow, the station and corresponding keys are removed. Assisted-by: GithubCopilot:claude-opus-5.0 Signed-off-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260926212249.736b01b4a76d.Ie528ea57d4d8dc8abfca406f8bae55a3902ff740@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 64 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 76c45a0eea62..3a17d1ccd32a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9110,6 +9110,57 @@ void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, wiphy_work_queue(local->hw.wiphy, &sdata->work); } +static void +ieee80211_assoc_timeout_teardown(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; + struct ieee80211_local *local = sdata->local; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = ASSOC_EVENT, + .u.mlme.status = MLME_TIMEOUT, + }; + struct sta_info *sta; + + lockdep_assert_wiphy(local->hw.wiphy); + + /* + * With an EPP station, the AP is already maintaining a state for the + * station. Send a deauthentication frame, so that the AP clears its + * state for this station (to allow additional connection attempts). + * Note that this needs to be done before the station is removed + * locally as the deauthentication frame needs to be sent encrypted. + */ + sta = sta_info_get_bss(sdata, assoc_data->ap_addr); + if (sta && sta->sta.epp_peer && + wiphy_dereference(local->hw.wiphy, sta->ptk[sta->ptk_idx])) { + u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + struct ieee80211_prep_tx_info info = { + .subtype = IEEE80211_STYPE_DEAUTH, + .link_id = assoc_data->assoc_link_id, + }; + + drv_mgd_prepare_tx(local, sdata, &info); + + ieee80211_send_deauth_disassoc(sdata, assoc_data->ap_addr, + assoc_data->ap_addr, + IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DEAUTH_LEAVING, + true, frame_buf); + + /* make sure the deauth is out before the station is removed */ + ieee80211_flush_queues(local, sdata, false); + + drv_mgd_complete_tx(local, sdata, &info); + + cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, sizeof(frame_buf), + false); + } + + ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT, NULL); + drv_event_callback(local, sdata, &event); +} + void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -9192,17 +9243,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) time_after(jiffies, ifmgd->assoc_data->timeout)) { if ((ifmgd->assoc_data->need_beacon && !sdata->deflink.u.mgd.have_beacon) || - ieee80211_do_assoc(sdata)) { - struct ieee80211_event event = { - .type = MLME_EVENT, - .u.mlme.data = ASSOC_EVENT, - .u.mlme.status = MLME_TIMEOUT, - }; - - ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT, - NULL); - drv_event_callback(sdata->local, sdata, &event); - } + ieee80211_do_assoc(sdata)) + ieee80211_assoc_timeout_teardown(sdata); } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) run_again(sdata, ifmgd->assoc_data->timeout);