Path: blob/main/sys/contrib/dev/iwlwifi/mld/key.c
106882 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024 Intel Corporation3*/4#include "key.h"5#include "iface.h"6#include "sta.h"7#include "fw/api/datapath.h"89static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,10struct ieee80211_vif *vif,11struct ieee80211_sta *sta,12struct ieee80211_key_conf *key)13{14struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);15bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;16bool igtk = key->keyidx == 4 || key->keyidx == 5;17u32 flags = 0;1819if (!pairwise)20flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;2122switch (key->cipher) {23case WLAN_CIPHER_SUITE_TKIP:24flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;25break;26case WLAN_CIPHER_SUITE_AES_CMAC:27case WLAN_CIPHER_SUITE_CCMP:28flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;29break;30case WLAN_CIPHER_SUITE_GCMP_256:31case WLAN_CIPHER_SUITE_BIP_GMAC_256:32flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;33fallthrough;34case WLAN_CIPHER_SUITE_GCMP:35case WLAN_CIPHER_SUITE_BIP_GMAC_128:36flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;37break;38}3940if (!sta && vif->type == NL80211_IFTYPE_STATION)41sta = mld_vif->ap_sta;4243/* If we are installing an iGTK (in AP or STA mode), we need to tell44* the firmware this key will en/decrypt MGMT frames.45* Same goes if we are installing a pairwise key for an MFP station.46* In case we're installing a groupwise key (which is not an iGTK),47* then, we will not use this key for MGMT frames.48*/49if ((sta && sta->mfp && pairwise) || igtk)50flags |= IWL_SEC_KEY_FLAG_MFP;5152if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)53flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;5455return flags;56}5758static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,59struct ieee80211_vif *vif,60struct ieee80211_sta *sta,61struct ieee80211_key_conf *key)62{63struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);64struct ieee80211_link_sta *link_sta;65int sta_id;6667lockdep_assert_wiphy(mld->wiphy);6869/* AP group keys are per link and should be on the mcast/bcast STA */70if (vif->type == NL80211_IFTYPE_AP &&71!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {72struct iwl_mld_link *link = NULL;7374if (key->link_id >= 0)75link = iwl_mld_link_dereference_check(mld_vif,76key->link_id);7778if (WARN_ON(!link))79return 0;8081/* In this stage we should have both the bcast and mcast STAs */82if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA ||83link->mcast_sta.sta_id == IWL_INVALID_STA))84return 0;8586/* IGTK/BIGTK to bcast STA */87if (key->keyidx >= 4)88return BIT(link->bcast_sta.sta_id);8990/* GTK for data to mcast STA */91return BIT(link->mcast_sta.sta_id);92}9394/* for client mode use the AP STA also for group keys */95if (!sta && vif->type == NL80211_IFTYPE_STATION)96sta = mld_vif->ap_sta;9798/* STA should be non-NULL now */99if (WARN_ON(!sta))100return 0;101102/* Key is not per-link, get the full sta mask */103if (key->link_id < 0)104return iwl_mld_fw_sta_id_mask(mld, sta);105106/* The link_sta shouldn't be NULL now, but this is checked in107* iwl_mld_fw_sta_id_mask108*/109link_sta = link_sta_dereference_check(sta, key->link_id);110111sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);112if (sta_id < 0)113return 0;114115return BIT(sta_id);116}117118static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask,119u32 key_flags, struct ieee80211_key_conf *key)120{121struct iwl_sec_key_cmd cmd = {122.action = cpu_to_le32(FW_CTXT_ACTION_ADD),123.u.add.sta_mask = cpu_to_le32(sta_mask),124.u.add.key_id = cpu_to_le32(key->keyidx),125.u.add.key_flags = cpu_to_le32(key_flags),126.u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)),127};128bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP;129int max_key_len = sizeof(cmd.u.add.key);130131#ifdef CONFIG_PM_SLEEP132/* If there was a rekey in wowlan, FW already has the key */133if (mld->fw_status.resuming)134return 0;135#endif136137if (WARN_ON(!sta_mask))138return -EINVAL;139140if (WARN_ON(key->keylen > max_key_len))141return -EINVAL;142143memcpy(cmd.u.add.key, key->key, key->keylen);144145if (tkip) {146memcpy(cmd.u.add.tkip_mic_rx_key,147key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,1488);149memcpy(cmd.u.add.tkip_mic_tx_key,150key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,1518);152}153154return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),155&cmd);156}157158static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,159u32 key_flags, u32 keyidx)160{161struct iwl_sec_key_cmd cmd = {162.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),163.u.remove.sta_mask = cpu_to_le32(sta_mask),164.u.remove.key_id = cpu_to_le32(keyidx),165.u.remove.key_flags = cpu_to_le32(key_flags),166};167168#ifdef CONFIG_PM_SLEEP169/* If there was a rekey in wowlan, FW already removed the key */170if (mld->fw_status.resuming)171return;172#endif173174if (WARN_ON(!sta_mask))175return;176177iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);178}179180void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif,181struct ieee80211_sta *sta,182struct ieee80211_key_conf *key)183{184u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);185u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);186struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);187188lockdep_assert_wiphy(mld->wiphy);189190if (!sta_mask)191return;192193if (key->keyidx == 4 || key->keyidx == 5) {194struct iwl_mld_link *mld_link;195unsigned int link_id = 0;196197/* set to -1 for non-MLO right now */198if (key->link_id >= 0)199link_id = key->link_id;200201mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);202if (WARN_ON(!mld_link))203return;204205if (mld_link->igtk == key)206mld_link->igtk = NULL;207208mld->num_igtks--;209}210211iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx);212213/* no longer in HW */214key->hw_key_idx = STA_KEY_IDX_INVALID;215}216217int iwl_mld_add_key(struct iwl_mld *mld,218struct ieee80211_vif *vif,219struct ieee80211_sta *sta,220struct ieee80211_key_conf *key)221{222u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);223u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);224struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);225struct iwl_mld_link *mld_link = NULL;226bool igtk = key->keyidx == 4 || key->keyidx == 5;227int ret;228229lockdep_assert_wiphy(mld->wiphy);230231if (!sta_mask)232return -EINVAL;233234if (igtk) {235if (mld->num_igtks == IWL_MAX_NUM_IGTKS)236return -EOPNOTSUPP;237238u8 link_id = 0;239240/* set to -1 for non-MLO right now */241if (key->link_id >= 0)242link_id = key->link_id;243244mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);245246if (WARN_ON(!mld_link))247return -EINVAL;248249if (mld_link->igtk) {250IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",251mld_link->igtk->keyidx);252iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);253}254255WARN_ON(mld_link->igtk);256}257258ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);259if (ret)260return ret;261262if (mld_link) {263mld_link->igtk = key;264mld->num_igtks++;265}266267/* We don't really need this, but need it to be not invalid,268* so we will know if the key is in fw.269*/270key->hw_key_idx = 0;271272return 0;273}274275struct remove_ap_keys_iter_data {276u8 link_id;277struct ieee80211_sta *sta;278};279280static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw,281struct ieee80211_vif *vif,282struct ieee80211_sta *sta,283struct ieee80211_key_conf *key,284void *_data)285{286struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);287struct remove_ap_keys_iter_data *data = _data;288289if (key->hw_key_idx == STA_KEY_IDX_INVALID)290return;291292/* All the pairwise keys should have been removed by now */293if (WARN_ON(sta))294return;295296if (key->link_id >= 0 && key->link_id != data->link_id)297return;298299iwl_mld_remove_key(mld, vif, data->sta, key);300}301302void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif,303struct ieee80211_sta *sta, unsigned int link_id)304{305struct remove_ap_keys_iter_data iter_data = {306.link_id = link_id,307.sta = sta,308};309310if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))311return;312313ieee80211_iter_keys(mld->hw, vif,314iwl_mld_remove_ap_keys_iter,315&iter_data);316}317318struct iwl_mvm_sta_key_update_data {319struct ieee80211_sta *sta;320u32 old_sta_mask;321u32 new_sta_mask;322int err;323};324325static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw,326struct ieee80211_vif *vif,327struct ieee80211_sta *sta,328struct ieee80211_key_conf *key,329void *_data)330{331struct iwl_mvm_sta_key_update_data *data = _data;332struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);333struct iwl_sec_key_cmd cmd = {334.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),335.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),336.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),337.u.modify.key_id = cpu_to_le32(key->keyidx),338.u.modify.key_flags =339cpu_to_le32(iwl_mld_get_key_flags(mld, vif, sta, key)),340};341int err;342343/* only need to do this for pairwise keys (link_id == -1) */344if (sta != data->sta || key->link_id >= 0)345return;346347err = iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),348&cmd);349350if (err)351data->err = err;352}353354int iwl_mld_update_sta_keys(struct iwl_mld *mld,355struct ieee80211_vif *vif,356struct ieee80211_sta *sta,357u32 old_sta_mask,358u32 new_sta_mask)359{360struct iwl_mvm_sta_key_update_data data = {361.sta = sta,362.old_sta_mask = old_sta_mask,363.new_sta_mask = new_sta_mask,364};365366ieee80211_iter_keys(mld->hw, vif, iwl_mld_update_sta_key_iter,367&data);368return data.err;369}370371372