Path: blob/main/sys/contrib/dev/iwlwifi/mvm/mld-key.c
48285 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2022 - 2024 Intel Corporation3*/4#include <linux/kernel.h>5#include <net/mac80211.h>6#include "mvm.h"7#include "fw/api/context.h"8#include "fw/api/datapath.h"910static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm,11struct ieee80211_vif *vif,12struct ieee80211_sta *sta,13struct ieee80211_key_conf *keyconf)14{15struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);16struct iwl_mvm_vif_link_info *link_info = &mvmvif->deflink;1718lockdep_assert_held(&mvm->mutex);1920if (keyconf->link_id >= 0) {21link_info = mvmvif->link[keyconf->link_id];22if (!link_info)23return 0;24}2526/* AP group keys are per link and should be on the mcast/bcast STA */27if (vif->type == NL80211_IFTYPE_AP &&28!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {29/* IGTK/BIGTK to bcast STA */30if (keyconf->keyidx >= 4)31return BIT(link_info->bcast_sta.sta_id);32/* GTK for data to mcast STA */33return BIT(link_info->mcast_sta.sta_id);34}3536/* for client mode use the AP STA also for group keys */37if (!sta && vif->type == NL80211_IFTYPE_STATION)38sta = mvmvif->ap_sta;3940/* During remove the STA was removed and the group keys come later41* (which sounds like a bad sequence, but remember that to mac80211 the42* group keys have no sta pointer), so we don't have a STA now.43* Since this happens for group keys only, just use the link_info as44* the group keys are per link; make sure that is the case by checking45* we do have a link_id or are not doing MLO.46* Of course the same can be done during add as well, but we must do47* it during remove, since we don't have the mvmvif->ap_sta pointer.48*/49if (!sta && (keyconf->link_id >= 0 || !ieee80211_vif_is_mld(vif)))50return BIT(link_info->ap_sta_id);5152/* STA should be non-NULL now, but iwl_mvm_sta_fw_id_mask() checks */5354/* pass link_id to filter by it if not -1 (GTK on client) */55return iwl_mvm_sta_fw_id_mask(mvm, sta, keyconf->link_id);56}5758u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,59struct ieee80211_vif *vif,60struct ieee80211_sta *sta,61struct ieee80211_key_conf *keyconf)62{63struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);64bool pairwise = keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE;65bool igtk = keyconf->keyidx == 4 || keyconf->keyidx == 5;66u32 flags = 0;6768lockdep_assert_held(&mvm->mutex);6970if (!pairwise)71flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;7273switch (keyconf->cipher) {74case WLAN_CIPHER_SUITE_WEP104:75flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;76fallthrough;77case WLAN_CIPHER_SUITE_WEP40:78flags |= IWL_SEC_KEY_FLAG_CIPHER_WEP;79break;80case WLAN_CIPHER_SUITE_TKIP:81flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;82break;83case WLAN_CIPHER_SUITE_AES_CMAC:84case WLAN_CIPHER_SUITE_CCMP:85flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;86break;87case WLAN_CIPHER_SUITE_GCMP_256:88case WLAN_CIPHER_SUITE_BIP_GMAC_256:89flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;90fallthrough;91case WLAN_CIPHER_SUITE_GCMP:92case WLAN_CIPHER_SUITE_BIP_GMAC_128:93flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;94break;95}9697if (!sta && vif->type == NL80211_IFTYPE_STATION)98sta = mvmvif->ap_sta;99100/*101* If we are installing an iGTK (in AP or STA mode), we need to tell102* the firmware this key will en/decrypt MGMT frames.103* Same goes if we are installing a pairwise key for an MFP station.104* In case we're installing a groupwise key (which is not an iGTK),105* then, we will not use this key for MGMT frames.106*/107if ((!IS_ERR_OR_NULL(sta) && sta->mfp && pairwise) || igtk)108flags |= IWL_SEC_KEY_FLAG_MFP;109110if (keyconf->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)111flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;112113return flags;114}115116struct iwl_mvm_sta_key_update_data {117struct ieee80211_sta *sta;118u32 old_sta_mask;119u32 new_sta_mask;120int err;121};122123static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw,124struct ieee80211_vif *vif,125struct ieee80211_sta *sta,126struct ieee80211_key_conf *key,127void *_data)128{129u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);130struct iwl_mvm_sta_key_update_data *data = _data;131struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);132struct iwl_sec_key_cmd cmd = {133.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),134.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),135.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),136.u.modify.key_id = cpu_to_le32(key->keyidx),137.u.modify.key_flags =138cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)),139};140int err;141142/* only need to do this for pairwise keys (link_id == -1) */143if (sta != data->sta || key->link_id >= 0)144return;145146err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);147148if (err)149data->err = err;150}151152int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,153struct ieee80211_vif *vif,154struct ieee80211_sta *sta,155u32 old_sta_mask,156u32 new_sta_mask)157{158struct iwl_mvm_sta_key_update_data data = {159.sta = sta,160.old_sta_mask = old_sta_mask,161.new_sta_mask = new_sta_mask,162};163164ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key,165&data);166return data.err;167}168169static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask,170u32 key_flags, u32 keyidx, u32 flags)171{172u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);173struct iwl_sec_key_cmd cmd = {174.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),175.u.remove.sta_mask = cpu_to_le32(sta_mask),176.u.remove.key_id = cpu_to_le32(keyidx),177.u.remove.key_flags = cpu_to_le32(key_flags),178};179180return iwl_mvm_send_cmd_pdu(mvm, cmd_id, flags, sizeof(cmd), &cmd);181}182183int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags,184struct ieee80211_key_conf *keyconf)185{186u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);187struct iwl_sec_key_cmd cmd = {188.action = cpu_to_le32(FW_CTXT_ACTION_ADD),189.u.add.sta_mask = cpu_to_le32(sta_mask),190.u.add.key_id = cpu_to_le32(keyconf->keyidx),191.u.add.key_flags = cpu_to_le32(key_flags),192.u.add.tx_seq = cpu_to_le64(atomic64_read(&keyconf->tx_pn)),193};194int max_key_len = sizeof(cmd.u.add.key);195int ret;196197if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||198keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)199max_key_len -= IWL_SEC_WEP_KEY_OFFSET;200201if (WARN_ON(keyconf->keylen > max_key_len))202return -EINVAL;203204if (WARN_ON(!sta_mask))205return -EINVAL;206207if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||208keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)209memcpy(cmd.u.add.key + IWL_SEC_WEP_KEY_OFFSET, keyconf->key,210keyconf->keylen);211else212memcpy(cmd.u.add.key, keyconf->key, keyconf->keylen);213214if (keyconf->cipher == WLAN_CIPHER_SUITE_TKIP) {215memcpy(cmd.u.add.tkip_mic_rx_key,216keyconf->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,2178);218memcpy(cmd.u.add.tkip_mic_tx_key,219keyconf->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,2208);221}222223ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);224if (ret)225return ret;226227/*228* For WEP, the same key is used for multicast and unicast so need to229* upload it again. If this fails, remove the original as well.230*/231if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||232keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {233cmd.u.add.key_flags ^= cpu_to_le32(IWL_SEC_KEY_FLAG_MCAST_KEY);234ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);235if (ret)236__iwl_mvm_sec_key_del(mvm, sta_mask, key_flags,237keyconf->keyidx, 0);238}239240return ret;241}242243int iwl_mvm_sec_key_add(struct iwl_mvm *mvm,244struct ieee80211_vif *vif,245struct ieee80211_sta *sta,246struct ieee80211_key_conf *keyconf)247{248u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf);249u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);250struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);251struct iwl_mvm_vif_link_info *mvm_link = NULL;252int ret;253254if (keyconf->keyidx == 4 || keyconf->keyidx == 5) {255unsigned int link_id = 0;256257/* set to -1 for non-MLO right now */258if (keyconf->link_id >= 0)259link_id = keyconf->link_id;260261mvm_link = mvmvif->link[link_id];262if (WARN_ON(!mvm_link))263return -EINVAL;264265if (mvm_link->igtk) {266IWL_DEBUG_MAC80211(mvm, "remove old IGTK %d\n",267mvm_link->igtk->keyidx);268ret = iwl_mvm_sec_key_del(mvm, vif, sta,269mvm_link->igtk);270if (ret)271IWL_ERR(mvm,272"failed to remove old IGTK (ret=%d)\n",273ret);274}275276WARN_ON(mvm_link->igtk);277}278279ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf);280if (ret)281return ret;282283if (mvm_link)284mvm_link->igtk = keyconf;285286/* We don't really need this, but need it to be not invalid,287* and if we switch links multiple times it might go to be288* invalid when removed.289*/290keyconf->hw_key_idx = 0;291292return 0;293}294295static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm,296struct ieee80211_vif *vif,297struct ieee80211_sta *sta,298struct ieee80211_key_conf *keyconf,299u32 flags)300{301u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf);302u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);303struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);304int ret;305306if (WARN_ON(!sta_mask))307return -EINVAL;308309if (keyconf->keyidx == 4 || keyconf->keyidx == 5) {310struct iwl_mvm_vif_link_info *mvm_link;311unsigned int link_id = 0;312313/* set to -1 for non-MLO right now */314if (keyconf->link_id >= 0)315link_id = keyconf->link_id;316317mvm_link = mvmvif->link[link_id];318if (WARN_ON(!mvm_link))319return -EINVAL;320321if (mvm_link->igtk == keyconf) {322/* no longer in HW - mark for later */323mvm_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID;324mvm_link->igtk = NULL;325}326}327328ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx,329flags);330if (ret)331return ret;332333/* For WEP, delete the key again as unicast */334if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||335keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {336key_flags ^= IWL_SEC_KEY_FLAG_MCAST_KEY;337ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags,338keyconf->keyidx, flags);339}340341return ret;342}343344int iwl_mvm_sec_key_del_pasn(struct iwl_mvm *mvm,345struct ieee80211_vif *vif,346u32 sta_mask,347struct ieee80211_key_conf *keyconf)348{349u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) |350IWL_SEC_KEY_FLAG_MFP;351352if (WARN_ON(!sta_mask))353return -EINVAL;354355return __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx,3560);357}358359int iwl_mvm_sec_key_del(struct iwl_mvm *mvm,360struct ieee80211_vif *vif,361struct ieee80211_sta *sta,362struct ieee80211_key_conf *keyconf)363{364return _iwl_mvm_sec_key_del(mvm, vif, sta, keyconf, 0);365}366367static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw,368struct ieee80211_vif *vif,369struct ieee80211_sta *sta,370struct ieee80211_key_conf *key,371void *data)372{373struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);374unsigned int link_id = (uintptr_t)data;375376if (key->hw_key_idx == STA_KEY_IDX_INVALID)377return;378379if (sta)380return;381382if (key->link_id >= 0 && key->link_id != link_id)383return;384385_iwl_mvm_sec_key_del(mvm, vif, NULL, key, CMD_ASYNC);386key->hw_key_idx = STA_KEY_IDX_INVALID;387}388389void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm,390struct ieee80211_vif *vif,391struct iwl_mvm_vif_link_info *link,392unsigned int link_id)393{394u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);395u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0);396397if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION ||398link->ap_sta_id == IWL_INVALID_STA))399return;400401if (!sec_key_ver)402return;403404ieee80211_iter_keys(mvm->hw, vif,405iwl_mvm_sec_key_remove_ap_iter,406(void *)(uintptr_t)link_id);407}408409410