Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH] wifi: mac80211: defer AP-side FT key upload until association
@ 2026-07-30 16:14 andcov23
  2026-07-30 16:21 ` Johannes Berg
  0 siblings, 1 reply; 5+ messages in thread
From: andcov23 @ 2026-07-30 16:14 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Andrea Covelli, linux-wireless, Kavita Kavita,
	Sai Pratyusha Magam

From: Andrea Covelli <andcov23@gmail.com>

During an AP-side Fast Transition, hostapd may install the PTK after
creating a station entry but before marking it associated. The ASSOC gate
in ieee80211_add_key() rejects the request with -ENOENT, producing:

  nl80211: kernel reports: key addition failed

Userspace may retry after association, but this race can instead break
the roam, particularly with PMF.

Accept pre-association pairwise keys on AP and AP_VLAN interfaces once
the station exists. Mark only those keys as deferred so hardware upload
is skipped while the key is stored in mac80211. Upload the marked PTKs
after the driver's AUTH-to-ASSOC state transition succeeds.

Track deferred state on each key and scan the station's PTK slots at
ASSOC so hardware upload is limited to keys accepted before association.
EPP peers are excluded from this deferral because EPP requires the PTK
to be available before association to encrypt and decrypt
(Re)Association Request and Response frames.

Fixes: 1626e0fa740d ("mac80211: fix FT roaming")
Cc: stable@vger.kernel.org
Link: https://github.com/openwrt/openwrt/pull/23181
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Andrea Covelli <andcov23@gmail.com>
---
A backport of this change was tested with mt76 on Cudy WR3000E v1 and
WR3000P v1 devices running OpenWrt 25.12.5. Repeated bidirectional
802.11r FT roams completed without new "key addition failed" messages.

 net/mac80211/cfg.c      | 18 +++++++++++++++---
 net/mac80211/key.c      | 27 +++++++++++++++++++++++++++
 net/mac80211/key.h      |  4 ++++
 net/mac80211/sta_info.c |  1 +
 4 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 0a9247be26af..a95435b720a9 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -666,7 +666,14 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
 		key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX;
 
 	if (mac_addr) {
+		bool defer_hw_upload;
+
 		sta = sta_info_get_bss(sdata, mac_addr);
+		defer_hw_upload =
+			sta && pairwise && !sta->sta.epp_peer &&
+			!test_sta_flag(sta, WLAN_STA_ASSOC) &&
+			(sdata->vif.type == NL80211_IFTYPE_AP ||
+			 sdata->vif.type == NL80211_IFTYPE_AP_VLAN);
 		/*
 		 * The ASSOC test makes sure the driver is ready to
 		 * receive the key. When wpa_supplicant has roamed
@@ -681,14 +688,19 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
 		 * If (re)association frame encryption support is not present,
 		 * cfg80211 will not allow key installation in non‑AP STA mode.
 		 *
-		 * TODO: accept the key if we have a station entry and
-		 *	 add it to the device after the station associates.
+		 * AP-side FT may also install a pairwise key before the
+		 * station is associated. Keep it in mac80211 and upload it
+		 * to the driver after the station reaches ASSOC.
 		 */
 		if (!sta || (!sta->sta.epp_peer &&
-			     !test_sta_flag(sta, WLAN_STA_ASSOC))) {
+			     !test_sta_flag(sta, WLAN_STA_ASSOC) &&
+			     !defer_hw_upload)) {
 			ieee80211_key_free_unused(key);
 			return -ENOENT;
 		}
+
+		if (defer_hw_upload)
+			key->flags |= KEY_FLAG_DEFERRED_HW_UPLOAD;
 	}
 
 	switch (sdata->vif.type) {
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index f45e792abede..180e2aa9649e 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -144,6 +144,11 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 		return -EINVAL;
 	}
 
+	if (key->flags & KEY_FLAG_DEFERRED_HW_UPLOAD) {
+		ret = 1;
+		goto out_unsupported;
+	}
+
 	if (!key->local->ops->set_key)
 		goto out_unsupported;
 
@@ -997,6 +1002,28 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata)
 	}
 }
 
+void ieee80211_upload_deferred_sta_keys(struct sta_info *sta)
+{
+	struct ieee80211_local *local = sta->local;
+	struct ieee80211_key *key;
+	int i, ret;
+
+	lockdep_assert_wiphy(local->hw.wiphy);
+
+	for (i = 0; i < ARRAY_SIZE(sta->ptk); i++) {
+		key = wiphy_dereference(local->hw.wiphy, sta->ptk[i]);
+		if (!key || !(key->flags & KEY_FLAG_DEFERRED_HW_UPLOAD))
+			continue;
+
+		key->flags &= ~KEY_FLAG_DEFERRED_HW_UPLOAD;
+		ret = ieee80211_key_enable_hw_accel(key);
+		if (ret)
+			sdata_err(key->sdata,
+				  "failed to enable deferred key (%d, %pM): %d\n",
+				  key->conf.keyidx, sta->sta.addr, ret);
+	}
+}
+
 static void
 ieee80211_key_iter(struct ieee80211_hw *hw,
 		   struct ieee80211_vif *vif,
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 826e4e9387c5..97d3bbcf0ac2 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -32,10 +32,13 @@ struct sta_info;
  * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present
  *	in the hardware for TX crypto hardware acceleration.
  * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped.
+ * @KEY_FLAG_DEFERRED_HW_UPLOAD: Key upload is deferred until the station
+ *	is associated.
  */
 enum ieee80211_internal_key_flags {
 	KEY_FLAG_UPLOADED_TO_HARDWARE	= BIT(0),
 	KEY_FLAG_TAINTED		= BIT(1),
+	KEY_FLAG_DEFERRED_HW_UPLOAD	= BIT(2),
 };
 
 enum ieee80211_internal_tkip_state {
@@ -165,6 +168,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
 			 bool force_synchronize);
 void ieee80211_free_sta_keys(struct ieee80211_local *local,
 			     struct sta_info *sta);
+void ieee80211_upload_deferred_sta_keys(struct sta_info *sta);
 void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata);
 int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata,
 			       unsigned long del_links_mask,
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d12aed9c1756..625f628b46a0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1468,6 +1468,7 @@ static int _sta_info_move_state(struct sta_info *sta,
 	case IEEE80211_STA_ASSOC:
 		if (sta->sta_state == IEEE80211_STA_AUTH) {
 			set_bit(WLAN_STA_ASSOC, &sta->_flags);
+			ieee80211_upload_deferred_sta_keys(sta);
 			sta->assoc_at = ktime_get_boottime_ns();
 			if (recalc) {
 				ieee80211_recalc_min_chandef(sta->sdata, -1);
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-03 22:31 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 16:14 [PATCH] wifi: mac80211: defer AP-side FT key upload until association andcov23
2026-07-30 16:21 ` Johannes Berg
2026-07-30 17:00   ` Andrea Covelli
2026-07-30 18:54     ` Johannes Berg
2026-08-03 22:30       ` Andrea Covelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox