[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