From 746a5902ad85ec0611a3e6ebfd7b68b45621a40b Mon Sep 17 00:00:00 2001 From: Jan Fooken Date: Tue, 4 Nov 2025 09:43:06 +0100 Subject: [PATCH] wifi: use authentication retry mechanism While NetworkManager tries it's best to determine whether a new PSK is needed, it can still run into edge cases. One of these edge cases is that a device can leave the range of an access point and therefore fail a 4-way handshake. Because these cases can't be confidently detected, a device which was previously connected, should try to exhaust it's authentication retries before requesting new secrets. This leads to less user-facing prompts while increasing the time from PSK change to prompt. --- src/core/devices/wifi/nm-device-wifi.c | 90 ++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 12 deletions(-) diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 1fd096426e..6a9e5655c8 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -2251,6 +2251,26 @@ wps_timeout_cb(gpointer user_data) return G_SOURCE_REMOVE; } +static gboolean +wifi_connection_is_new(NMDeviceWifi *self) +{ + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + NMSettingsConnection *connection; + guint64 timestamp = 0; + + req = nm_device_get_act_request(device); + g_return_val_if_fail(NM_IS_ACT_REQUEST(req), TRUE); + + connection = nm_act_request_get_settings_connection(req); + g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(connection), TRUE); + + if (nm_settings_connection_get_timestamp(connection, ×tamp) && timestamp != 0) + return FALSE; + + return TRUE; +} + static void wifi_secrets_get_secrets(NMDeviceWifi *self, const char *setting_name, @@ -2405,10 +2425,11 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, NMSupplicantInterfaceState old_state, int disconnect_reason) { - NMDevice *device = NM_DEVICE(self); - NMActRequest *req; - const char *setting_name = NULL; - gboolean handled = FALSE; + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + const char *setting_name = NULL; + NMSecretAgentGetSecretsFlags secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; g_return_val_if_fail(new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); @@ -2418,8 +2439,7 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, req = nm_device_get_act_request(NM_DEVICE(self)); g_return_val_if_fail(req != NULL, FALSE); - if (need_new_8021x_secrets(self, old_state, &setting_name) - || need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) { + if (need_new_8021x_secrets(self, old_state, &setting_name)) { nm_act_request_clear_secrets(req); _LOGI(LOGD_DEVICE | LOGD_WIFI, @@ -2429,14 +2449,54 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, nm_device_state_changed(device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - wifi_secrets_get_secrets(self, - setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); - handled = TRUE; + wifi_secrets_get_secrets(self, setting_name, secret_flags); + return TRUE; } - return handled; + if (need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) { + nm_act_request_clear_secrets(req); + cleanup_association_attempt(self, TRUE); + + if (wifi_connection_is_new(self)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) new connection disconnected during association, asking for " + "new key"); + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, setting_name, secret_flags); + return TRUE; + } + + if (!nm_device_auth_retries_try_next(device)) { + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + return TRUE; + } + + if (nm_device_auth_retries_has_next(device)) { + secret_flags &= ~NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + _LOGI( + LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, reauthenticating connection"); + } else { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, asking for new key"); + } + + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, setting_name, secret_flags); + + return TRUE; + } + + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, retrying connection"); + + return FALSE; } static gboolean @@ -2868,6 +2928,12 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG) return; + if (!wifi_connection_is_new(self) && nm_device_auth_retries_has_next(device)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) psk mismatch reported by supplicant, retrying connection"); + return; + } + _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) psk mismatch reported by supplicant, asking for new key");