summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2021-08-05 13:19:28 +0300
committerLuca Coelho <luciano.coelho@intel.com>2021-08-26 23:35:17 +0300
commit79e561f0f05afaa10b8d9bd18f89ec7d7168126d (patch)
tree92c4455e21c0b8d729901149542d2879890eb373 /drivers/net/wireless/intel/iwlwifi/mvm/d3.c
parentaf3aab9ce2989d7378e468c4d6a4eb39e0479c9b (diff)
downloadlinux-79e561f0f05afaa10b8d9bd18f89ec7d7168126d.tar.gz
linux-79e561f0f05afaa10b8d9bd18f89ec7d7168126d.tar.bz2
linux-79e561f0f05afaa10b8d9bd18f89ec7d7168126d.zip
iwlwifi: mvm: d3: implement RSC command version 5
In later firmware we haven't needed the TSC anyway since we have it already (and firmware image doesn't change), but the new version adds the ability to send down replay counters for more than one GTK. Implement that. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Link: https://lore.kernel.org/r/iwlwifi.20210805130823.28cd065e8c4a.Ic8406a78ee46b07e0ca1b8199522ef08ec6eef53@changeid Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/d3.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c196
1 files changed, 174 insertions, 22 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index ad7308cc4b7f..00403b337060 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -211,7 +211,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
}
struct wowlan_key_rsc_tsc_data {
- struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
+ struct iwl_wowlan_rsc_tsc_params_cmd_v4 *rsc_tsc;
bool have_rsc_tsc;
};
@@ -327,6 +327,127 @@ static void iwl_mvm_wowlan_get_rsc_tsc_data(struct ieee80211_hw *hw,
}
}
+struct wowlan_key_rsc_v5_data {
+ struct iwl_wowlan_rsc_tsc_params_cmd *rsc;
+ bool have_rsc;
+ int gtks;
+ int gtk_ids[4];
+};
+
+static void iwl_mvm_wowlan_get_rsc_v5_data(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct wowlan_key_rsc_v5_data *data = _data;
+ struct ieee80211_key_seq seq;
+ __le64 *rsc;
+ int i;
+
+ /* only for ciphers that can be PTK/GTK */
+ switch (key->cipher) {
+ default:
+ return;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ break;
+ }
+
+ if (sta) {
+ rsc = data->rsc->ucast_rsc;
+ } else {
+ if (WARN_ON(data->gtks > ARRAY_SIZE(data->gtk_ids)))
+ return;
+ data->gtk_ids[data->gtks] = key->keyidx;
+ rsc = data->rsc->mcast_rsc[data->gtks % 2];
+ if (WARN_ON(key->keyidx >
+ ARRAY_SIZE(data->rsc->mcast_key_id_map)))
+ return;
+ data->rsc->mcast_key_id_map[key->keyidx] = data->gtks % 2;
+ if (data->gtks >= 2) {
+ int prev = data->gtks - 2;
+ int prev_idx = data->gtk_ids[prev];
+
+ data->rsc->mcast_key_id_map[prev_idx] =
+ IWL_MCAST_KEY_MAP_INVALID;
+ }
+ data->gtks++;
+ }
+
+ switch (key->cipher) {
+ default:
+ WARN_ON(1);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211 use TID 0 (as they need to to avoid replay attacks)
+ * for checking the IV in the frames.
+ */
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ ieee80211_get_key_rx_seq(key, i, &seq);
+
+ rsc[i] = cpu_to_le64(((u64)seq.tkip.iv32 << 16) |
+ seq.tkip.iv16);
+ }
+
+ data->have_rsc = true;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ /*
+ * For non-QoS this relies on the fact that both the uCode and
+ * mac80211/our RX code use TID 0 for checking the PN.
+ */
+ if (sta) {
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
+ const u8 *pn;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ rcu_read_lock();
+ ptk_pn = rcu_dereference(mvmsta->ptk_pn[key->keyidx]);
+ if (WARN_ON(!ptk_pn)) {
+ rcu_read_unlock();
+ break;
+ }
+
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i,
+ mvm->trans->num_rx_queues);
+ rsc[i] = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+
+ rcu_read_unlock();
+ } else {
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u8 *pn = seq.ccmp.pn;
+
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ rsc[i] = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+ }
+ data->have_rsc = true;
+ break;
+ }
+}
+
static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
@@ -334,35 +455,66 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,
int ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP,
WOWLAN_TSC_RSC_PARAM,
IWL_FW_CMD_VER_UNKNOWN);
- struct wowlan_key_rsc_tsc_data data = {};
- int size;
int ret;
- data.rsc_tsc = kzalloc(sizeof(*data.rsc_tsc), GFP_KERNEL);
- if (!data.rsc_tsc)
- return -ENOMEM;
+ if (ver == 5) {
+ struct wowlan_key_rsc_v5_data data = {};
+ int i;
+
+ data.rsc = kmalloc(sizeof(*data.rsc), GFP_KERNEL);
+ if (!data.rsc)
+ return -ENOMEM;
+
+ memset(data.rsc, 0xff, sizeof(*data.rsc));
+
+ for (i = 0; i < ARRAY_SIZE(data.rsc->mcast_key_id_map); i++)
+ data.rsc->mcast_key_id_map[i] =
+ IWL_MCAST_KEY_MAP_INVALID;
+ data.rsc->sta_id = cpu_to_le32(mvmvif->ap_sta_id);
+
+ ieee80211_iter_keys(mvm->hw, vif,
+ iwl_mvm_wowlan_get_rsc_v5_data,
+ &data);
+
+ if (data.have_rsc)
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_TSC_RSC_PARAM,
+ CMD_ASYNC, sizeof(*data.rsc),
+ data.rsc);
+ else
+ ret = 0;
+ kfree(data.rsc);
+ } else if (ver == 4 || ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN) {
+ struct wowlan_key_rsc_tsc_data data = {};
+ int size;
- if (ver == 4) {
- size = sizeof(*data.rsc_tsc);
- data.rsc_tsc->sta_id = cpu_to_le32(mvmvif->ap_sta_id);
- } else if (ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN) {
- size = sizeof(data.rsc_tsc->params);
+ data.rsc_tsc = kzalloc(sizeof(*data.rsc_tsc), GFP_KERNEL);
+ if (!data.rsc_tsc)
+ return -ENOMEM;
+
+ if (ver == 4) {
+ size = sizeof(*data.rsc_tsc);
+ data.rsc_tsc->sta_id = cpu_to_le32(mvmvif->ap_sta_id);
+ } else {
+ /* ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN */
+ size = sizeof(data.rsc_tsc->params);
+ }
+
+ ieee80211_iter_keys(mvm->hw, vif,
+ iwl_mvm_wowlan_get_rsc_tsc_data,
+ &data);
+
+ if (data.have_rsc_tsc)
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_TSC_RSC_PARAM,
+ CMD_ASYNC, size,
+ data.rsc_tsc);
+ else
+ ret = 0;
+ kfree(data.rsc_tsc);
} else {
ret = 0;
WARN_ON_ONCE(1);
- goto out;
}
- ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_wowlan_get_rsc_tsc_data,
- &data);
-
- if (data.have_rsc_tsc)
- ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_TSC_RSC_PARAM,
- CMD_ASYNC, size, data.rsc_tsc);
- else
- ret = 0;
-out:
- kfree(data.rsc_tsc);
return ret;
}