[PATCH] wifi: Retry association timeouts on non-favorite services

Andrea Ricchi <[email protected]> Thu, 30 Jul 2026 10:03:54 +0200
Newsgroups dev.linux.lists.connman
Message-ID <[email protected]>
Commit dccfb13a5195 ("wifi: Detect invalid key with 4-way handshake
offloading") routed 802.11 status code 16, an association that timed out
waiting for the AP to answer, into handle_4way_handshake_failure(). It
noted that "the status code 16 is relatively generic, so this can lead to
false positives".

Those false positives are unrecoverable for a service that is not a
favorite yet. handle_4way_handshake_failure() only retries favorites, so
a network that has never been joined gets a single association attempt,
and one AP that fails to answer is reported to the user as a wrong
passphrase.

That was not always the case. Commit e0a27dece6b3 ("wifi: Try only twice
when connecting to a non-favorite service") kept two attempts for
non-favorites. Commit 289c7fc5c0e6 ("gsupplicant: Rely on wpa_supplicant
retry mechanism") then dropped them, on the grounds that wpa_supplicant
manages its own retries and duplicating them in connman only delays
reporting a wrong passphrase.

That reasoning holds for a real 4-way handshake failure, but not for an
association timeout, because connman does not let wpa_supplicant retry.
After the rejection wpa_supplicant is ready to recover on its own
(abridged):

  CTRL-EVENT-ASSOC-REJECT bssid=00:00:00:00:00:00 status_code=16
  Added BSSID 1c:0b:8b:4a:b2:4d into ignore list, ignoring for 10 seconds
  Another BSS in this ESS has been seen; try it next
  Consecutive connection failures: 1 --> request scan in 100 ms

but handle_4way_handshake_failure() returns false, the network is
disabled some ten milliseconds later and the recovery never runs:

  No enabled networks - do not scan

This is easy to hit on a multi-AP ESS, where wpa_supplicant ranks
candidates by estimated throughput rather than signal and may pick a weak
5 GHz BSS over a much stronger 2.4 GHz one of the same network.

Retry an association timeout regardless of the favorite flag. The retry
budget is unchanged, and the second attempt runs while wpa_supplicant
still ignores the unresponsive BSSID, so it lands on a different BSS. A
real 4-way handshake failure keeps the favorite check: there the
passphrase was tested and retrying would only delay reporting a wrong
key, which is what that commit set out to avoid.
---
 plugins/wifi.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/plugins/wifi.c b/plugins/wifi.c
index 9ce7b5a3..52fe8f86 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -2499,6 +2499,12 @@ static bool handle_assoc_status_code(GSupplicantInterface *interface,
 	return FALSE;
 }
 
+static bool assoc_timed_out(struct wifi_data *wifi)
+{
+	return wifi->state == G_SUPPLICANT_STATE_ASSOCIATING &&
+			wifi->assoc_code == ASSOC_STATUS_AUTH_TIMEOUT;
+}
+
 static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
 					struct connman_network *network,
 					struct wifi_data *wifi)
@@ -2506,8 +2512,7 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
 	struct connman_service *service;
 
 	if ((wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) &&
-			!((wifi->state == G_SUPPLICANT_STATE_ASSOCIATING) &&
-				(wifi->assoc_code == ASSOC_STATUS_AUTH_TIMEOUT)))
+			!assoc_timed_out(wifi))
 		return false;
 
 	if (wifi->connected)
@@ -2519,7 +2524,14 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
 
 	wifi->retries++;
 
-	if (connman_service_get_favorite(service)) {
+	/*
+	 * An association that timed out waiting for the AP to answer never
+	 * reached the 4-way handshake, so the passphrase was never involved.
+	 * Retry those regardless of the favorite flag: wpa_supplicant keeps the
+	 * unresponsive BSSID in its ignore list for a few seconds, so the next
+	 * attempt lands on a different BSS of the same ESS.
+	 */
+	if (assoc_timed_out(wifi) || connman_service_get_favorite(service)) {
 		if (wifi->retries < FAVORITE_MAXIMUM_RETRIES)
 			return true;
 	}
-- 
2.53.0