Commit ba3943b0 authored by Johannes Berg's avatar Johannes Berg Committed by Emmanuel Grumbach

iwlwifi: mvm: add WEP RX hardware offload support

In the original driver, we decided to not implement WEP RX hardware
offload because of a quirk with the firmware API - it allows setting
global WEP keys that then get used for all virtual interfaces, which
is clearly wrong if more than one exists, and it allows setting per-
station keys but then separates multicast and unicast keys.

In order to implement WEP RX hardware offload, work around these
limitations by uploading each WEP key twice, once as multicast and
once as unicast, but point them both to the same key slot (offset)
and use the same key material so the slot overwrite on the second
upload doesn't actually change anything. Upon removal, also remove
the key twice so the station no longer references it.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 2f6319d1
...@@ -2330,12 +2330,16 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, ...@@ -2330,12 +2330,16 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
break; break;
case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_WEP104:
/* /* For non-client mode, only use WEP keys for TX as we probably
* Support for TX only, at least for now, so accept * don't have a station yet anyway and would then have to keep
* the key and do nothing else. Then mac80211 will * track of the keys, linking them to each of the clients/peers
* pass it for TX but we don't have to use it for RX. * as they appear. For now, don't do that, for performance WEP
* offload doesn't really matter much, but we need it for some
* other offload features in client mode.
*/ */
return 0; if (vif->type != NL80211_IFTYPE_STATION)
return 0;
break;
default: default:
/* currently FW supports only one optional cipher scheme */ /* currently FW supports only one optional cipher scheme */
if (hw->n_cipher_schemes && if (hw->n_cipher_schemes &&
......
...@@ -1071,7 +1071,7 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, ...@@ -1071,7 +1071,7 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvm_sta, struct iwl_mvm_sta *mvm_sta,
struct ieee80211_key_conf *keyconf, struct ieee80211_key_conf *keyconf, bool mcast,
u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags)
{ {
struct iwl_mvm_add_sta_key_cmd cmd = {}; struct iwl_mvm_add_sta_key_cmd cmd = {};
...@@ -1099,12 +1099,18 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, ...@@ -1099,12 +1099,18 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
memcpy(cmd.key, keyconf->key, keyconf->keylen); memcpy(cmd.key, keyconf->key, keyconf->keylen);
break; break;
case WLAN_CIPHER_SUITE_WEP104:
key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
case WLAN_CIPHER_SUITE_WEP40:
key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
break;
default: default:
key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
memcpy(cmd.key, keyconf->key, keyconf->keylen); memcpy(cmd.key, keyconf->key, keyconf->keylen);
} }
if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) if (mcast)
key_flags |= cpu_to_le16(STA_KEY_MULTICAST); key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
cmd.key_offset = keyconf->hw_key_idx; cmd.key_offset = keyconf->hw_key_idx;
...@@ -1199,7 +1205,8 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, ...@@ -1199,7 +1205,8 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,
static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf) struct ieee80211_key_conf *keyconf,
bool mcast)
{ {
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
int ret; int ret;
...@@ -1213,15 +1220,17 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ...@@ -1213,15 +1220,17 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
/* get phase 1 key from mac80211 */ /* get phase 1 key from mac80211 */
ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_key_rx_seq(keyconf, 0, &seq);
ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
seq.tkip.iv32, p1k, 0); seq.tkip.iv32, p1k, 0);
break; break;
case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP:
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
0, NULL, 0); 0, NULL, 0);
break; break;
default: default:
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
0, NULL, 0); 0, NULL, 0);
} }
...@@ -1229,7 +1238,8 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ...@@ -1229,7 +1238,8 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
} }
static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
struct ieee80211_key_conf *keyconf) struct ieee80211_key_conf *keyconf,
bool mcast)
{ {
struct iwl_mvm_add_sta_key_cmd cmd = {}; struct iwl_mvm_add_sta_key_cmd cmd = {};
__le16 key_flags; __le16 key_flags;
...@@ -1241,7 +1251,7 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, ...@@ -1241,7 +1251,7 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) if (mcast)
key_flags |= cpu_to_le16(STA_KEY_MULTICAST); key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
cmd.key_flags = key_flags; cmd.key_flags = key_flags;
...@@ -1271,6 +1281,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ...@@ -1271,6 +1281,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_key_conf *keyconf, struct ieee80211_key_conf *keyconf,
bool have_key_offset) bool have_key_offset)
{ {
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
u8 sta_id; u8 sta_id;
int ret; int ret;
...@@ -1315,9 +1326,26 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ...@@ -1315,9 +1326,26 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
return -ENOSPC; return -ENOSPC;
} }
ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf); ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast);
if (ret) if (ret) {
__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
goto end;
}
/*
* For WEP, the same key is used for multicast and unicast. Upload it
* again, using the same key offset, and now pointing the other one
* to the same key slot (offset).
* If this fails, remove the original as well.
*/
if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast);
if (ret) {
__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
__iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
}
}
end: end:
IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
...@@ -1331,7 +1359,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, ...@@ -1331,7 +1359,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf) struct ieee80211_key_conf *keyconf)
{ {
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
u8 sta_id; u8 sta_id;
int ret;
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
...@@ -1373,7 +1403,16 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, ...@@ -1373,7 +1403,16 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
return -EINVAL; return -EINVAL;
return __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf); ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
if (ret)
return ret;
/* delete WEP key twice to get rid of (now useless) offset */
if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast);
return ret;
} }
void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
...@@ -1384,6 +1423,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, ...@@ -1384,6 +1423,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
{ {
struct iwl_mvm_sta *mvm_sta; struct iwl_mvm_sta *mvm_sta;
u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta); u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
return; return;
...@@ -1399,7 +1439,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, ...@@ -1399,7 +1439,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
} }
mvm_sta = iwl_mvm_sta_from_mac80211(sta); mvm_sta = iwl_mvm_sta_from_mac80211(sta);
iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
iv32, phase1key, CMD_ASYNC); iv32, phase1key, CMD_ASYNC);
rcu_read_unlock(); rcu_read_unlock();
} }
......
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