Commit 90cc4bd6 authored by Alexander Wetzel's avatar Alexander Wetzel Committed by Johannes Berg

mac80211: AMPDU handling for Extended Key ID

IEEE 802.11 - 2016 forbids mixing MPDUs with different keyIDs in one
A-MPDU. Drivers supporting A-MPDUs and Extended Key ID must actively
enforce that requirement due to the available two unicast keyIDs.

Allow driver to signal mac80211 that they will not check the keyID in
MPDUs when aggregating them and that they expect mac80211 to stop Tx
aggregation when rekeying a connection using Extended Key ID.
Signed-off-by: default avatarAlexander Wetzel <alexander@wetzel-home.de>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1a28ed21
...@@ -2269,6 +2269,9 @@ struct ieee80211_txq { ...@@ -2269,6 +2269,9 @@ struct ieee80211_txq {
* @IEEE80211_HW_EXT_KEY_ID_NATIVE: Driver and hardware are supporting Extended * @IEEE80211_HW_EXT_KEY_ID_NATIVE: Driver and hardware are supporting Extended
* Key ID and can handle two unicast keys per station for Rx and Tx. * Key ID and can handle two unicast keys per station for Rx and Tx.
* *
* @IEEE80211_HW_NO_AMPDU_KEYBORDER_SUPPORT: The card/driver can't handle
* active Tx A-MPDU sessions with Extended Key IDs during rekey.
*
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
...@@ -2321,6 +2324,7 @@ enum ieee80211_hw_flags { ...@@ -2321,6 +2324,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_MULTI_BSSID, IEEE80211_HW_SUPPORTS_MULTI_BSSID,
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID, IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
IEEE80211_HW_EXT_KEY_ID_NATIVE, IEEE80211_HW_EXT_KEY_ID_NATIVE,
IEEE80211_HW_NO_AMPDU_KEYBORDER_SUPPORT,
/* keep last, obviously */ /* keep last, obviously */
NUM_IEEE80211_HW_FLAGS NUM_IEEE80211_HW_FLAGS
......
...@@ -274,6 +274,7 @@ static const char *hw_flag_names[] = { ...@@ -274,6 +274,7 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_MULTI_BSSID), FLAG(SUPPORTS_MULTI_BSSID),
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
FLAG(EXT_KEY_ID_NATIVE), FLAG(EXT_KEY_ID_NATIVE),
FLAG(NO_AMPDU_KEYBORDER_SUPPORT),
#undef FLAG #undef FLAG
}; };
......
...@@ -274,50 +274,61 @@ int ieee80211_set_tx_key(struct ieee80211_key *key) ...@@ -274,50 +274,61 @@ int ieee80211_set_tx_key(struct ieee80211_key *key)
old = key_mtx_dereference(local, sta->ptk[sta->ptk_idx]); old = key_mtx_dereference(local, sta->ptk[sta->ptk_idx]);
sta->ptk_idx = key->conf.keyidx; sta->ptk_idx = key->conf.keyidx;
if (ieee80211_hw_check(&local->hw, NO_AMPDU_KEYBORDER_SUPPORT))
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_check_fast_xmit(sta); ieee80211_check_fast_xmit(sta);
return 0; return 0;
} }
static int ieee80211_hw_key_replace(struct ieee80211_key *old_key, static void ieee80211_pairwise_rekey(struct ieee80211_key *old,
struct ieee80211_key *new_key, struct ieee80211_key *new)
bool pairwise)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local = new->local;
struct ieee80211_local *local; struct sta_info *sta = new->sta;
struct sta_info *sta; int i;
int ret;
/* Aggregation sessions are OK when running on SW crypto. assert_key_lock(local);
if (new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) {
/* Extended Key ID key install, initial one or rekey */
if (sta->ptk_idx != INVALID_PTK_KEYIDX &&
ieee80211_hw_check(&local->hw,
NO_AMPDU_KEYBORDER_SUPPORT)) {
/* Aggregation Sessions with Extended Key ID must not
* mix MPDUs with different keyIDs within one A-MPDU.
* Tear down any running Tx aggregation and all new
* Rx/Tx aggregation request during rekey if the driver
* asks us to do so. (Blocking Tx only would be
* sufficient but WLAN_STA_BLOCK_BA gets the job done
* for the few ms we need it.)
*/
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
mutex_lock(&sta->ampdu_mlme.mtx);
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
___ieee80211_stop_tx_ba_session(sta, i,
AGG_STOP_LOCAL_REQUEST);
mutex_unlock(&sta->ampdu_mlme.mtx);
}
} else if (old) {
/* Rekey without Extended Key ID.
* Aggregation sessions are OK when running on SW crypto.
* A broken remote STA may cause issues not observed with HW * A broken remote STA may cause issues not observed with HW
* crypto, though. * crypto, though.
*/ */
if (!(old_key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) if (!(old->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
return 0; return;
assert_key_lock(old_key->local);
sta = old_key->sta;
/* Unicast rekey without Extended Key ID needs special handling */
if (new_key && sta && pairwise &&
rcu_access_pointer(sta->ptk[sta->ptk_idx]) == old_key) {
local = old_key->local;
sdata = old_key->sdata;
/* Stop TX till we are on the new key */ /* Stop Tx till we are on the new key */
old_key->flags |= KEY_FLAG_TAINTED; old->flags |= KEY_FLAG_TAINTED;
ieee80211_clear_fast_xmit(sta); ieee80211_clear_fast_xmit(sta);
/* Aggregation sessions during rekey are complicated due to the
* reorder buffer and retransmits. Side step that by blocking
* aggregation during rekey and tear down running sessions.
*/
if (ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION)) { if (ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION)) {
set_sta_flag(sta, WLAN_STA_BLOCK_BA); set_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_tear_down_BA_sessions(sta, ieee80211_sta_tear_down_BA_sessions(sta,
AGG_STOP_LOCAL_REQUEST); AGG_STOP_LOCAL_REQUEST);
} }
if (!wiphy_ext_feature_isset(local->hw.wiphy, if (!wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0)) { NL80211_EXT_FEATURE_CAN_REPLACE_PTK0)) {
pr_warn_ratelimited("Rekeying PTK for STA %pM but driver can't safely do that.", pr_warn_ratelimited("Rekeying PTK for STA %pM but driver can't safely do that.",
...@@ -325,18 +336,9 @@ static int ieee80211_hw_key_replace(struct ieee80211_key *old_key, ...@@ -325,18 +336,9 @@ static int ieee80211_hw_key_replace(struct ieee80211_key *old_key,
/* Flushing the driver queues *may* help prevent /* Flushing the driver queues *may* help prevent
* the clear text leaks and freezes. * the clear text leaks and freezes.
*/ */
ieee80211_flush_queues(local, sdata, false); ieee80211_flush_queues(local, old->sdata, false);
} }
} }
ieee80211_key_disable_hw_accel(old_key);
if (new_key)
ret = ieee80211_key_enable_hw_accel(new_key);
else
ret = 0;
return ret;
} }
static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
...@@ -394,7 +396,6 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, ...@@ -394,7 +396,6 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&sdata->local->key_mtx); mutex_unlock(&sdata->local->key_mtx);
} }
static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sta_info *sta,
bool pairwise, bool pairwise,
...@@ -402,7 +403,7 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, ...@@ -402,7 +403,7 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct ieee80211_key *new) struct ieee80211_key *new)
{ {
int idx; int idx;
int ret; int ret = 0;
bool defunikey, defmultikey, defmgmtkey; bool defunikey, defmultikey, defmgmtkey;
/* caller must provide at least one old/new */ /* caller must provide at least one old/new */
...@@ -414,16 +415,27 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, ...@@ -414,16 +415,27 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
if (new && sta && pairwise) {
/* Unicast rekey needs special handling. With Extended Key ID
* old is still NULL for the first rekey.
*/
ieee80211_pairwise_rekey(old, new);
}
if (old) { if (old) {
idx = old->conf.keyidx; idx = old->conf.keyidx;
ret = ieee80211_hw_key_replace(old, new, pairwise);
if (old->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
ieee80211_key_disable_hw_accel(old);
if (new)
ret = ieee80211_key_enable_hw_accel(new);
}
} else { } else {
/* new must be provided in case old is not */ /* new must be provided in case old is not */
idx = new->conf.keyidx; idx = new->conf.keyidx;
if (!new->local->wowlan) if (!new->local->wowlan)
ret = ieee80211_key_enable_hw_accel(new); ret = ieee80211_key_enable_hw_accel(new);
else
ret = 0;
} }
if (ret) if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment