new file mode 100644
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include "key.h"
+#include "iface.h"
+#include "sta.h"
+#include "fw/api/datapath.h"
+
+static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;
+ bool igtk = key->keyidx == 4 || key->keyidx == 5;
+ u32 flags = 0;
+
+ if (!pairwise)
+ flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_CCMP:
+ flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;
+ fallthrough;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;
+ break;
+ }
+
+ if (!sta && vif->type == NL80211_IFTYPE_STATION)
+ sta = mld_vif->ap_sta;
+
+ /* If we are installing an iGTK (in AP or STA mode), we need to tell
+ * the firmware this key will en/decrypt MGMT frames.
+ * Same goes if we are installing a pairwise key for an MFP station.
+ * In case we're installing a groupwise key (which is not an iGTK),
+ * then, we will not use this key for MGMT frames.
+ */
+ if ((sta && sta->mfp && pairwise) || igtk)
+ flags |= IWL_SEC_KEY_FLAG_MFP;
+
+ if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)
+ flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;
+
+ return flags;
+}
+
+static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_link_sta *link_sta;
+ int sta_id;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* AP group keys are per link and should be on the mcast/bcast STA */
+ if (vif->type == NL80211_IFTYPE_AP &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ struct iwl_mld_link *link = NULL;
+
+ if (key->link_id >= 0)
+ link = iwl_mld_link_dereference_check(mld_vif,
+ key->link_id);
+
+ if (WARN_ON(!link))
+ return 0;
+
+ /* In this stage we should have both the bcast and mcast STAs */
+ if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA ||
+ link->mcast_sta.sta_id == IWL_INVALID_STA))
+ return 0;
+
+ /* IGTK/BIGTK to bcast STA */
+ if (key->keyidx >= 4)
+ return BIT(link->bcast_sta.sta_id);
+
+ /* GTK for data to mcast STA */
+ return BIT(link->mcast_sta.sta_id);
+ }
+
+ /* for client mode use the AP STA also for group keys */
+ if (!sta && vif->type == NL80211_IFTYPE_STATION)
+ sta = mld_vif->ap_sta;
+
+ /* STA should be non-NULL now */
+ if (WARN_ON(!sta))
+ return 0;
+
+ /* Key is not per-link, get the full sta mask */
+ if (key->link_id < 0)
+ return iwl_mld_fw_sta_id_mask(mld, sta);
+
+ /* The link_sta shouldn't be NULL now, but this is checked in
+ * iwl_mld_fw_sta_id_mask
+ */
+ link_sta = link_sta_dereference_check(sta, key->link_id);
+
+ sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
+ if (sta_id < 0)
+ return 0;
+
+ return BIT(sta_id);
+}
+
+static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask,
+ u32 key_flags, struct ieee80211_key_conf *key)
+{
+ struct iwl_sec_key_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ .u.add.sta_mask = cpu_to_le32(sta_mask),
+ .u.add.key_id = cpu_to_le32(key->keyidx),
+ .u.add.key_flags = cpu_to_le32(key_flags),
+ .u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)),
+ };
+ bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP;
+ int max_key_len = sizeof(cmd.u.add.key);
+
+ if (WARN_ON(!sta_mask))
+ return -EINVAL;
+
+ if (WARN_ON(key->keylen > max_key_len))
+ return -EINVAL;
+
+ memcpy(cmd.u.add.key, key->key, key->keylen);
+
+ if (tkip) {
+ memcpy(cmd.u.add.tkip_mic_rx_key,
+ key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ 8);
+ memcpy(cmd.u.add.tkip_mic_tx_key,
+ key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
+ 8);
+ }
+
+ return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
+ &cmd);
+}
+
+static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,
+ u32 key_flags, u32 keyidx)
+{
+ struct iwl_sec_key_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+ .u.remove.sta_mask = cpu_to_le32(sta_mask),
+ .u.remove.key_id = cpu_to_le32(keyidx),
+ .u.remove.key_flags = cpu_to_le32(key_flags),
+ };
+
+ if (WARN_ON(!sta_mask))
+ return;
+
+ iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);
+}
+
+void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
+ u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!sta_mask)
+ return;
+
+ if (key->keyidx == 4 || key->keyidx == 5) {
+ struct iwl_mld_link *mld_link;
+ unsigned int link_id = 0;
+
+ /* set to -1 for non-MLO right now */
+ if (key->link_id >= 0)
+ link_id = key->link_id;
+
+ mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
+ if (WARN_ON(!mld_link))
+ return;
+
+ if (mld_link->igtk == key)
+ mld_link->igtk = NULL;
+
+ mld->num_igtks--;
+ }
+
+ iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx);
+
+ /* no longer in HW */
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+}
+
+int iwl_mld_add_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
+ u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *mld_link = NULL;
+ bool igtk = key->keyidx == 4 || key->keyidx == 5;
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (!sta_mask)
+ return -EINVAL;
+
+ if (igtk) {
+ if (mld->num_igtks == IWL_MAX_NUM_IGTKS)
+ return -EOPNOTSUPP;
+
+ u8 link_id = 0;
+
+ /* set to -1 for non-MLO right now */
+ if (key->link_id >= 0)
+ link_id = key->link_id;
+
+ mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
+
+ if (WARN_ON(!mld_link))
+ return -EINVAL;
+
+ if (mld_link->igtk) {
+ IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",
+ mld_link->igtk->keyidx);
+ iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);
+ }
+
+ WARN_ON(mld_link->igtk);
+ }
+
+ ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);
+ if (ret)
+ return ret;
+
+ if (mld_link) {
+ mld_link->igtk = key;
+ mld->num_igtks++;
+ }
+
+ /* We don't really need this, but need it to be not invalid,
+ * so we will know if the key is in fw.
+ */
+ key->hw_key_idx = 0;
+
+ return 0;
+}
+
+struct remove_ap_keys_iter_data {
+ u8 link_id;
+ struct ieee80211_sta *sta;
+};
+
+static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct remove_ap_keys_iter_data *data = _data;
+
+ if (key->hw_key_idx == STA_KEY_IDX_INVALID)
+ return;
+
+ /* All the pairwise keys should have been removed by now */
+ if (WARN_ON(sta))
+ return;
+
+ if (key->link_id >= 0 && key->link_id != data->link_id)
+ return;
+
+ iwl_mld_remove_key(mld, vif, data->sta, key);
+}
+
+void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned int link_id)
+{
+ struct remove_ap_keys_iter_data iter_data = {
+ .link_id = link_id,
+ .sta = sta,
+ };
+
+ if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
+ return;
+
+ ieee80211_iter_keys(mld->hw, vif,
+ iwl_mld_remove_ap_keys_iter,
+ &iter_data);
+}
+
+struct iwl_mvm_sta_key_update_data {
+ struct ieee80211_sta *sta;
+ u32 old_sta_mask;
+ u32 new_sta_mask;
+ int err;
+};
+
+static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mvm_sta_key_update_data *data = _data;
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+ struct iwl_sec_key_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
+ .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
+ .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
+ .u.modify.key_id = cpu_to_le32(key->keyidx),
+ .u.modify.key_flags =
+ cpu_to_le32(iwl_mld_get_key_flags(mld, vif, sta, key)),
+ };
+ int err;
+
+ /* only need to do this for pairwise keys (link_id == -1) */
+ if (sta != data->sta || key->link_id >= 0)
+ return;
+
+ err = iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
+ &cmd);
+
+ if (err)
+ data->err = err;
+}
+
+int iwl_mld_update_sta_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask,
+ u32 new_sta_mask)
+{
+ struct iwl_mvm_sta_key_update_data data = {
+ .sta = sta,
+ .old_sta_mask = old_sta_mask,
+ .new_sta_mask = new_sta_mask,
+ };
+
+ ieee80211_iter_keys(mld->hw, vif, iwl_mld_update_sta_key_iter,
+ &data);
+ return data.err;
+}
new file mode 100644
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_key_h__
+#define __iwl_mld_key_h__
+
+#include "mld.h"
+#include <net/mac80211.h>
+#include "fw/api/sta.h"
+
+void iwl_mld_remove_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+int iwl_mld_add_key(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+void iwl_mld_remove_ap_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ unsigned int link_id);
+
+int iwl_mld_update_sta_keys(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 old_sta_mask,
+ u32 new_sta_mask);
+
+static inline void
+iwl_mld_cleanup_keys_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key, void *data)
+{
+ key->hw_key_idx = STA_KEY_IDX_INVALID;
+}
+
+#endif /* __iwl_mld_key_h__ */
this file is handling key-related operations Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> --- drivers/net/wireless/intel/iwlwifi/mld/key.c | 358 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/key.h | 39 ++ 2 files changed, 397 insertions(+) create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/key.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/key.h