[PATCH 3/3] netdev: support handling NL80211_CMD_ASSOC_COMEBACK

James Prestwood
2025-05-22T18:41:57Z

A BSS can temporarily reject associations and provide a delay that
the station should wait for before retrying. This is useful when
sane values are used, but taking it to the extreme an AP could
potentially request the client wait UINT32_MAX TU's which equates
to 49 days.

Either due to a bug, or worse by design, the kernel will wait for
however long that timeout is. Luckily the kernel also sends an event
to userspace with the amount of time it will be waiting. To guard
against excessive timeouts IWD will now handle this event and enforce
a maximum allowed value. If the timeout exceeds this IWD will
deauthenticate.
---
src/netdev.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/src/netdev.c b/src/netdev.c
index a26a484e..3bdc3e69 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -5451,6 +5451,39 @@ static void netdev_michael_mic_failure(struct l_genl_msg *msg,
l_debug("ifindex=%u key_idx=%u type=%u", netdev->index, idx, type);
}

+#define MAX_COMEBACK_DELAY 1200
+
+static void netdev_assoc_comeback(struct l_genl_msg *msg,
+ struct netdev *netdev)
+{
+ const uint8_t *mac;
+ uint32_t timeout;
+
+ if (L_WARN_ON(!netdev->connected))
+ return;
+
+ if (nl80211_parse_attrs(msg, NL80211_ATTR_MAC, &mac,
+ NL80211_ATTR_TIMEOUT, &timeout,
+ NL80211_ATTR_UNSPEC) < 0)
+ return;
+
+ if (L_WARN_ON(memcmp(mac, netdev->handshake->aa, ETH_ALEN)))
+ return;
+
+ if (timeout <= MAX_COMEBACK_DELAY) {
+ l_debug(MAC" requested an association comeback delay of %u TU",
+ MAC_STR(netdev->handshake->aa), timeout);
+ return;
+ }
+
+ l_debug("Comeback delay of %u exceeded maximum of %u, deauthenticating",
+ timeout, MAX_COMEBACK_DELAY);
+
+ netdev_deauth_and_fail_connection(netdev,
+ NETDEV_RESULT_ASSOCIATION_FAILED,
+ MMPDU_STATUS_CODE_REFUSED_TEMPORARILY);
+}
+
static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = NULL;
@@ -5504,6 +5537,9 @@ static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
case NL80211_CMD_MICHAEL_MIC_FAILURE:
netdev_michael_mic_failure(msg, netdev);
break;
+ case NL80211_CMD_ASSOC_COMEBACK:
+ netdev_assoc_comeback(msg, netdev);
+ break;
}
}

--
2.34.1