From c9c8790ff2d7315933865d3dcf481bdd0d8488c0 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 22 May 2025 11:41:52 -0700 Subject: [PATCH] netdev: support handling NL80211_CMD_ASSOC_COMEBACK 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; } }