|
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c |
|
index 6dfc61a9a..76291c489 100644 |
|
--- a/net/mac80211/cfg.c |
|
+++ b/net/mac80211/cfg.c |
|
@@ -511,7 +511,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, |
|
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 && |
|
+ !test_sta_flag(sta, WLAN_STA_ASSOC) && |
|
+ (sdata->vif.type == NL80211_IFTYPE_AP || |
|
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && |
|
+ !ieee80211_hw_check(&local->hw, SW_CRYPTO_CONTROL); |
|
/* |
|
* The ASSOC test makes sure the driver is ready to |
|
* receive the key. When wpa_supplicant has roamed |
|
@@ -519,10 +527,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, |
|
* association has completed, this rejects that attempt |
|
* so it will set the key again after association. |
|
* |
|
- * TODO: accept the key if we have a station entry and |
|
- * add it to the device after the station. |
|
+ * AP-side FT may also install a pairwise key before the |
|
+ * station is associated. If automatic software fallback is |
|
+ * allowed, keep it in mac80211 and upload it after the station |
|
+ * reaches ASSOC. Drivers with SW_CRYPTO_CONTROL cannot use this |
|
+ * path since only their set_key() return value can permit |
|
+ * software crypto. |
|
*/ |
|
- if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) { |
|
+ if (!sta || (!test_sta_flag(sta, WLAN_STA_ASSOC) && |
|
+ !defer_hw_upload)) { |
|
ieee80211_key_free_unused(key); |
|
return -ENOENT; |
|
} |
|
diff --git a/net/mac80211/key.c b/net/mac80211/key.c |
|
index 67ecfea22..cf0002846 100644 |
|
--- a/net/mac80211/key.c |
|
+++ b/net/mac80211/key.c |
|
@@ -143,7 +143,13 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) |
|
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; |
|
return -EINVAL; |
|
} |
|
- |
|
+ if (key->flags & KEY_FLAG_DEFERRED_HW_UPLOAD) { |
|
+ /* |
|
+ * Deferred keys allow automatic software fallback. Keep ret at |
|
+ * -EOPNOTSUPP until the station is ready for hardware upload. |
|
+ */ |
|
+ goto out_unsupported; |
|
+ } |
|
if (!key->local->ops->set_key) |
|
goto out_unsupported; |
|
|
|
@@ -987,6 +993,41 @@ 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; |
|
+ u16 flags; |
|
+ 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; |
|
+ |
|
+ /* |
|
+ * Capabilities may have changed since key installation. |
|
+ * Avoid exposing temporarily cleared flags to RX/TX. |
|
+ */ |
|
+ flags = key->conf.flags & ~(IEEE80211_KEY_FLAG_RX_MGMT | |
|
+ IEEE80211_KEY_FLAG_SPP_AMSDU); |
|
+ if (test_sta_flag(sta, WLAN_STA_MFP)) |
|
+ flags |= IEEE80211_KEY_FLAG_RX_MGMT; |
|
+ if (sta->sta.spp_amsdu) |
|
+ flags |= IEEE80211_KEY_FLAG_SPP_AMSDU; |
|
+ WRITE_ONCE(key->conf.flags, flags); |
|
+ |
|
+ 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 1fa0f4f78..691268ce2 100644 |
|
--- a/net/mac80211/key.h |
|
+++ b/net/mac80211/key.h |
|
@@ -35,6 +35,7 @@ struct sta_info; |
|
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 +166,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, |
|
void ieee80211_free_sta_keys(struct ieee80211_local *local, |
|
struct sta_info *sta); |
|
void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata); |
|
+void ieee80211_upload_deferred_sta_keys(struct sta_info *sta); |
|
int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata, |
|
unsigned long del_links_mask, |
|
unsigned long add_links_mask); |
|
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c |
|
index aa22f09e6..e82215ae3 100644 |
|
--- a/net/mac80211/sta_info.c |
|
+++ b/net/mac80211/sta_info.c |
|
@@ -1348,6 +1348,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); |
This should be generalised to do things like specify device config more generically. I am just sharing what I'm using locally on my Flint 2.