|
|
@@ -0,0 +1,237 @@
|
|
|
+From 9a394fd149502394c20dc2ebecb8acfde6f6aeac Mon Sep 17 00:00:00 2001
|
|
|
+From: Rameshkumar Sundaram <[email protected]>
|
|
|
+Date: Sun, 10 Aug 2025 22:30:18 +0530
|
|
|
+Subject: wifi: ath11k: fix group data packet drops during rekey
|
|
|
+MIME-Version: 1.0
|
|
|
+Content-Type: text/plain; charset=UTF-8
|
|
|
+Content-Transfer-Encoding: 8bit
|
|
|
+
|
|
|
+[ Upstream commit 97acb0259cc9cbfbd7ab689e25684f3d8ce10e26 ]
|
|
|
+
|
|
|
+During GTK rekey, mac80211 issues a clear key (if the old key exists)
|
|
|
+followed by an install key operation in the same context. This causes
|
|
|
+ath11k to send two WMI commands in quick succession: one to clear the
|
|
|
+old key and another to install the new key in the same slot.
|
|
|
+
|
|
|
+Under certain conditions—especially under high load or time sensitive
|
|
|
+scenarios, firmware may process these commands asynchronously in a way
|
|
|
+that firmware assumes the key is cleared whereas hardware has a valid key.
|
|
|
+This inconsistency between hardware and firmware leads to group addressed
|
|
|
+packet drops. Only setting the same key again can restore a valid key in
|
|
|
+firmware and allow packets to be transmitted.
|
|
|
+
|
|
|
+This issue remained latent because the host's clear key commands were
|
|
|
+not effective in firmware until commit 436a4e886598 ("ath11k: clear the
|
|
|
+keys properly via DISABLE_KEY"). That commit enabled the host to
|
|
|
+explicitly clear group keys, which inadvertently exposed the race.
|
|
|
+
|
|
|
+To mitigate this, restrict group key clearing across all modes (AP, STA,
|
|
|
+MESH). During rekey, the new key can simply be set on top of the previous
|
|
|
+one, avoiding the need for a clear followed by a set.
|
|
|
+
|
|
|
+However, in AP mode specifically, permit group key clearing when no
|
|
|
+stations are associated. This exception supports transitions from secure
|
|
|
+modes (e.g., WPA2/WPA3) to open mode, during which all associated peers
|
|
|
+are removed and the group key is cleared as part of the transition.
|
|
|
+
|
|
|
+Add a per-BSS station counter to track the presence of stations during
|
|
|
+set key operations. Also add a reset_group_keys flag to track the key
|
|
|
+re-installation state and avoid repeated installation of the same key
|
|
|
+when the number of connected stations transitions to non-zero within a
|
|
|
+rekey period.
|
|
|
+
|
|
|
+Additionally, for AP and Mesh modes, when the first station associates,
|
|
|
+reinstall the same group key that was last set. This ensures that the
|
|
|
+firmware recovers from any race that may have occurred during a previous
|
|
|
+key clear when no stations were associated.
|
|
|
+
|
|
|
+This change ensures that key clearing is permitted only when no clients
|
|
|
+are connected, avoiding packet loss while enabling dynamic security mode
|
|
|
+transitions.
|
|
|
+
|
|
|
+Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1
|
|
|
+Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41
|
|
|
+
|
|
|
+Reported-by: Steffen Moser <[email protected]>
|
|
|
+Closes: https://lore.kernel.org/linux-wireless/[email protected]
|
|
|
+Fixes: 436a4e886598 ("ath11k: clear the keys properly via DISABLE_KEY")
|
|
|
+Signed-off-by: Rameshkumar Sundaram <[email protected]>
|
|
|
+Tested-by: Nicolas Escande <[email protected]>
|
|
|
+Reviewed-by: Vasanthakumar Thiagarajan <[email protected]>
|
|
|
+Link: https://patch.msgid.link/[email protected]
|
|
|
+Signed-off-by: Jeff Johnson <[email protected]>
|
|
|
+Signed-off-by: Sasha Levin <[email protected]>
|
|
|
+---
|
|
|
+ drivers/net/wireless/ath/ath11k/core.h | 2 +
|
|
|
+ drivers/net/wireless/ath/ath11k/mac.c | 111 ++++++++++++++++++++++++++++++---
|
|
|
+ 2 files changed, 104 insertions(+), 9 deletions(-)
|
|
|
+
|
|
|
+(limited to 'drivers/net/wireless/ath/ath11k')
|
|
|
+
|
|
|
+--- a/drivers/net/wireless/ath/ath11k/core.h
|
|
|
++++ b/drivers/net/wireless/ath/ath11k/core.h
|
|
|
+@@ -414,6 +414,8 @@ struct ath11k_vif {
|
|
|
+ bool do_not_send_tmpl;
|
|
|
+ struct ath11k_arp_ns_offload arp_ns_offload;
|
|
|
+ struct ath11k_rekey_data rekey_data;
|
|
|
++ u32 num_stations;
|
|
|
++ bool reinstall_group_keys;
|
|
|
+
|
|
|
+ struct ath11k_reg_tpc_power_info reg_tpc_info;
|
|
|
+
|
|
|
+--- a/drivers/net/wireless/ath/ath11k/mac.c
|
|
|
++++ b/drivers/net/wireless/ath/ath11k/mac.c
|
|
|
+@@ -4317,6 +4317,40 @@ static int ath11k_clear_peer_keys(struct
|
|
|
+ return first_errno;
|
|
|
+ }
|
|
|
+
|
|
|
++static int ath11k_set_group_keys(struct ath11k_vif *arvif)
|
|
|
++{
|
|
|
++ struct ath11k *ar = arvif->ar;
|
|
|
++ struct ath11k_base *ab = ar->ab;
|
|
|
++ const u8 *addr = arvif->bssid;
|
|
|
++ int i, ret, first_errno = 0;
|
|
|
++ struct ath11k_peer *peer;
|
|
|
++
|
|
|
++ spin_lock_bh(&ab->base_lock);
|
|
|
++ peer = ath11k_peer_find(ab, arvif->vdev_id, addr);
|
|
|
++ spin_unlock_bh(&ab->base_lock);
|
|
|
++
|
|
|
++ if (!peer)
|
|
|
++ return -ENOENT;
|
|
|
++
|
|
|
++ for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
|
|
|
++ struct ieee80211_key_conf *key = peer->keys[i];
|
|
|
++
|
|
|
++ if (!key || (key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
|
|
|
++ continue;
|
|
|
++
|
|
|
++ ret = ath11k_install_key(arvif, key, SET_KEY, addr,
|
|
|
++ WMI_KEY_GROUP);
|
|
|
++ if (ret < 0 && first_errno == 0)
|
|
|
++ first_errno = ret;
|
|
|
++
|
|
|
++ if (ret < 0)
|
|
|
++ ath11k_warn(ab, "failed to set group key of idx %d for vdev %d: %d\n",
|
|
|
++ i, arvif->vdev_id, ret);
|
|
|
++ }
|
|
|
++
|
|
|
++ return first_errno;
|
|
|
++}
|
|
|
++
|
|
|
+ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|
|
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
|
|
|
+ struct ieee80211_key_conf *key)
|
|
|
+@@ -4326,6 +4360,7 @@ static int ath11k_mac_op_set_key(struct
|
|
|
+ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
|
|
|
+ struct ath11k_peer *peer;
|
|
|
+ struct ath11k_sta *arsta;
|
|
|
++ bool is_ap_with_no_sta;
|
|
|
+ const u8 *peer_addr;
|
|
|
+ int ret = 0;
|
|
|
+ u32 flags = 0;
|
|
|
+@@ -4386,16 +4421,57 @@ static int ath11k_mac_op_set_key(struct
|
|
|
+ else
|
|
|
+ flags |= WMI_KEY_GROUP;
|
|
|
+
|
|
|
+- ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags);
|
|
|
+- if (ret) {
|
|
|
+- ath11k_warn(ab, "ath11k_install_key failed (%d)\n", ret);
|
|
|
+- goto exit;
|
|
|
+- }
|
|
|
++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
|
|
|
++ "%s for peer %pM on vdev %d flags 0x%X, type = %d, num_sta %d\n",
|
|
|
++ cmd == SET_KEY ? "SET_KEY" : "DEL_KEY", peer_addr, arvif->vdev_id,
|
|
|
++ flags, arvif->vdev_type, arvif->num_stations);
|
|
|
++
|
|
|
++ /* Allow group key clearing only in AP mode when no stations are
|
|
|
++ * associated. There is a known race condition in firmware where
|
|
|
++ * group addressed packets may be dropped if the key is cleared
|
|
|
++ * and immediately set again during rekey.
|
|
|
++ *
|
|
|
++ * During GTK rekey, mac80211 issues a clear key (if the old key
|
|
|
++ * exists) followed by an install key operation for same key
|
|
|
++ * index. This causes ath11k to send two WMI commands in quick
|
|
|
++ * succession: one to clear the old key and another to install the
|
|
|
++ * new key in the same slot.
|
|
|
++ *
|
|
|
++ * Under certain conditions—especially under high load or time
|
|
|
++ * sensitive scenarios, firmware may process these commands
|
|
|
++ * asynchronously in a way that firmware assumes the key is
|
|
|
++ * cleared whereas hardware has a valid key. This inconsistency
|
|
|
++ * between hardware and firmware leads to group addressed packet
|
|
|
++ * drops after rekey.
|
|
|
++ * Only setting the same key again can restore a valid key in
|
|
|
++ * firmware and allow packets to be transmitted.
|
|
|
++ *
|
|
|
++ * There is a use case where an AP can transition from Secure mode
|
|
|
++ * to open mode without a vdev restart by just deleting all
|
|
|
++ * associated peers and clearing key, Hence allow clear key for
|
|
|
++ * that case alone. Mark arvif->reinstall_group_keys in such cases
|
|
|
++ * and reinstall the same key when the first peer is added,
|
|
|
++ * allowing firmware to recover from the race if it had occurred.
|
|
|
++ */
|
|
|
+
|
|
|
+- ret = ath11k_dp_peer_rx_pn_replay_config(arvif, peer_addr, cmd, key);
|
|
|
+- if (ret) {
|
|
|
+- ath11k_warn(ab, "failed to offload PN replay detection %d\n", ret);
|
|
|
+- goto exit;
|
|
|
++ is_ap_with_no_sta = (vif->type == NL80211_IFTYPE_AP &&
|
|
|
++ !arvif->num_stations);
|
|
|
++ if ((flags & WMI_KEY_PAIRWISE) || cmd == SET_KEY || is_ap_with_no_sta) {
|
|
|
++ ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags);
|
|
|
++ if (ret) {
|
|
|
++ ath11k_warn(ab, "ath11k_install_key failed (%d)\n", ret);
|
|
|
++ goto exit;
|
|
|
++ }
|
|
|
++
|
|
|
++ ret = ath11k_dp_peer_rx_pn_replay_config(arvif, peer_addr, cmd, key);
|
|
|
++ if (ret) {
|
|
|
++ ath11k_warn(ab, "failed to offload PN replay detection %d\n",
|
|
|
++ ret);
|
|
|
++ goto exit;
|
|
|
++ }
|
|
|
++
|
|
|
++ if ((flags & WMI_KEY_GROUP) && cmd == SET_KEY && is_ap_with_no_sta)
|
|
|
++ arvif->reinstall_group_keys = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_bh(&ab->base_lock);
|
|
|
+@@ -4994,6 +5070,7 @@ static int ath11k_mac_inc_num_stations(s
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
+ ar->num_stations++;
|
|
|
++ arvif->num_stations++;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+@@ -5009,6 +5086,7 @@ static void ath11k_mac_dec_num_stations(
|
|
|
+ return;
|
|
|
+
|
|
|
+ ar->num_stations--;
|
|
|
++ arvif->num_stations--;
|
|
|
+ }
|
|
|
+
|
|
|
+ static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar,
|
|
|
+@@ -9536,6 +9614,21 @@ static int ath11k_mac_station_add(struct
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
++ /* Driver allows the DEL KEY followed by SET KEY sequence for
|
|
|
++ * group keys for only when there is no clients associated, if at
|
|
|
++ * all firmware has entered the race during that window,
|
|
|
++ * reinstalling the same key when the first sta connects will allow
|
|
|
++ * firmware to recover from the race.
|
|
|
++ */
|
|
|
++ if (arvif->num_stations == 1 && arvif->reinstall_group_keys) {
|
|
|
++ ath11k_dbg(ab, ATH11K_DBG_MAC, "set group keys on 1st station add for vdev %d\n",
|
|
|
++ arvif->vdev_id);
|
|
|
++ ret = ath11k_set_group_keys(arvif);
|
|
|
++ if (ret)
|
|
|
++ goto dec_num_station;
|
|
|
++ arvif->reinstall_group_keys = false;
|
|
|
++ }
|
|
|
++
|
|
|
+ arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL);
|
|
|
+ if (!arsta->rx_stats) {
|
|
|
+ ret = -ENOMEM;
|