[PATCH] SME: handle 802.11w association comeback

Jouni Malinen j at w1.fi
Sun Jan 14 10:59:00 PST 2024

On Wed, Jan 10, 2024 at 02:31:17PM -0500, Harry Bock wrote:
> In 802.11w/MFP networks, the infrastructure implements SA teardown
> protection by rejecting an Association Request from an
> already-associated client.  The AP responds with error 30 (Association
> request rejected temporarily) to instruct the (potentially spoofing)
> client to back off, while it issues an SA-Query procedure to the
> already-associated client.  If the client can respond to it within the
> back-off period, it considers the new association to be a spoof
> attempt.
> However, there are cases where a legitimate client might need to
> handle this error response - consider if the STA has deauthenticated,
> but the AP cannot hear it (out of range).  If the MFP STA has deleted
> its keys, it cannot respond to the SA-Query procedure.
> The current implementation interprets this association error as a true
> error, and will either add the BSS to the blacklist, or continue to
> try other BSSes - all of which will respond with the same error.  This
> can cause the supplicant to back off trying to reconnect for
> progressively longer intervals, depending on the infrastructure's
> configured comeback timeout.
> This patch allows the supplicant to interpret the error, searching for
> the Timeout Interval IE in the Association response and starting a
> timer in the SME layer to re-associate after the timeout.  This can be
> a long delay (1-4 seconds in my experience), but it is likely much
> shorter than bouncing between nearby BSSes.

Which driver are you using and would you be able to share a
wpa_supplicant debug log (ideally with both -d and -t on the command
line to include timestamps) showing this behavior both before and after
applying this patch?

I've tested the SME-in-wpa_supplicant case, i.e., the functions modified
here, only with mac80211-based drivers and in those cases, it is
mac80211 that takes care of the association comeback mechanism. Does
that not work for you or are you using a driver that does not use
mac80211? With mac80211, wpa_supplicant does not receive the association
event based on the comeback delay response and instead, mac80211 tries
automatically again at the indicated time and then reports successful
association to user space. The is an NL80211_CMD_ASSOC_COMEBACK event to
indicate this happened, but there is no need to take any action on that
from user space.

> diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
> @@ -3007,6 +3007,12 @@ struct ieee80211_neighbor_ap_info {
>  	u8 data[0];
> +struct ieee80211_timeout_interval {
> +	u8 timeout_interval_type;
> +	u32 timeout_interval_value;

That should be le32, not u32, since this is defining a struct for the
payload of the information element that uses little endian byte order
for integers.

> diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
> +static void sme_assoc_comeback_timer(void *eloop_ctx, void *timeout_ctx)
> +{
> +	struct wpa_supplicant *wpa_s = eloop_ctx;
> +	wpa_msg(wpa_s, MSG_DEBUG, "SME: Comeback timeout expired; retry associating to " MACSTR "; mode=%d auth_type=%u",
> +			MAC2STR(wpa_s->current_bss->bssid),
> +			wpa_s->current_ssid->mode,
> +			wpa_s->sme.assoc_auth_type);
> +
> +	/* auth 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);
> +}

This should like verify that wpa_s->current_bss and wpa_s->current_ssid
are not NULL..

> +static int sme_try_assoc_comeback(struct wpa_supplicant *wpa_s, 
> +					union wpa_event_data *data)
> +{
> +	int try_comeback = 0;
> +	struct ieee802_11_elems elems;
> +	if (ParseOK == ieee802_11_parse_elems(data->assoc_reject.resp_ies, data->assoc_reject.resp_ies_len, &elems, 0)) {
> +		if (elems.timeout_int) {
> +			struct ieee80211_timeout_interval* timeout = (struct ieee80211_timeout_interval*)elems.timeout_int;
> +			if (timeout->timeout_interval_type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
> +				wpa_msg(wpa_s, MSG_DEBUG, "SME: Association comeback interval: %u TUs",
> +					timeout->timeout_interval_value);
> +
> +				u64 comeback_usec = ((u64)timeout->timeout_interval_value * 1024uLL);

That does not work on big endian CPUs. It would likely be better to use
WPA_GET_LE32(&elems.timeout_int[1]) instead of defining this struct
ieee80211_timeout_interval which would now have need to do byte swapping
and potentially also a separate copy to avoid unaligned 32-bit accesses.

Jouni Malinen                                            PGP id EFC895FA

More information about the Hostap mailing list