brintos

brintos / linux-shallow public Read only

0
0
Text · 11.4 KiB · 455f5f4 Raw
409 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2022 - 2024 Intel Corporation4 */5#include <linux/kernel.h>6#include <net/mac80211.h>7#include "mvm.h"8#include "fw/api/context.h"9#include "fw/api/datapath.h"10 11static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm,12				    struct ieee80211_vif *vif,13				    struct ieee80211_sta *sta,14				    struct ieee80211_key_conf *keyconf)15{16	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);17	struct iwl_mvm_vif_link_info *link_info = &mvmvif->deflink;18 19	lockdep_assert_held(&mvm->mutex);20 21	if (keyconf->link_id >= 0) {22		link_info = mvmvif->link[keyconf->link_id];23		if (!link_info)24			return 0;25	}26 27	/* AP group keys are per link and should be on the mcast/bcast STA */28	if (vif->type == NL80211_IFTYPE_AP &&29	    !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {30		/* IGTK/BIGTK to bcast STA */31		if (keyconf->keyidx >= 4)32			return BIT(link_info->bcast_sta.sta_id);33		/* GTK for data to mcast STA */34		return BIT(link_info->mcast_sta.sta_id);35	}36 37	/* for client mode use the AP STA also for group keys */38	if (!sta && vif->type == NL80211_IFTYPE_STATION)39		sta = mvmvif->ap_sta;40 41	/* During remove the STA was removed and the group keys come later42	 * (which sounds like a bad sequence, but remember that to mac80211 the43	 * group keys have no sta pointer), so we don't have a STA now.44	 * Since this happens for group keys only, just use the link_info as45	 * the group keys are per link; make sure that is the case by checking46	 * we do have a link_id or are not doing MLO.47	 * Of course the same can be done during add as well, but we must do48	 * it during remove, since we don't have the mvmvif->ap_sta pointer.49	 */50	if (!sta && (keyconf->link_id >= 0 || !ieee80211_vif_is_mld(vif)))51		return BIT(link_info->ap_sta_id);52 53	/* STA should be non-NULL now, but iwl_mvm_sta_fw_id_mask() checks */54 55	/* pass link_id to filter by it if not -1 (GTK on client) */56	return iwl_mvm_sta_fw_id_mask(mvm, sta, keyconf->link_id);57}58 59u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,60			  struct ieee80211_vif *vif,61			  struct ieee80211_sta *sta,62			  struct ieee80211_key_conf *keyconf)63{64	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);65	bool pairwise = keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE;66	bool igtk = keyconf->keyidx == 4 || keyconf->keyidx == 5;67	u32 flags = 0;68 69	lockdep_assert_held(&mvm->mutex);70 71	if (!pairwise)72		flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;73 74	switch (keyconf->cipher) {75	case WLAN_CIPHER_SUITE_WEP104:76		flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;77		fallthrough;78	case WLAN_CIPHER_SUITE_WEP40:79		flags |= IWL_SEC_KEY_FLAG_CIPHER_WEP;80		break;81	case WLAN_CIPHER_SUITE_TKIP:82		flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;83		break;84	case WLAN_CIPHER_SUITE_AES_CMAC:85	case WLAN_CIPHER_SUITE_CCMP:86		flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;87		break;88	case WLAN_CIPHER_SUITE_GCMP_256:89	case WLAN_CIPHER_SUITE_BIP_GMAC_256:90		flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;91		fallthrough;92	case WLAN_CIPHER_SUITE_GCMP:93	case WLAN_CIPHER_SUITE_BIP_GMAC_128:94		flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;95		break;96	}97 98	if (!sta && vif->type == NL80211_IFTYPE_STATION)99		sta = mvmvif->ap_sta;100 101	/*102	 * If we are installing an iGTK (in AP or STA mode), we need to tell103	 * the firmware this key will en/decrypt MGMT frames.104	 * Same goes if we are installing a pairwise key for an MFP station.105	 * In case we're installing a groupwise key (which is not an iGTK),106	 * then, we will not use this key for MGMT frames.107	 */108	if ((!IS_ERR_OR_NULL(sta) && sta->mfp && pairwise) || igtk)109		flags |= IWL_SEC_KEY_FLAG_MFP;110 111	if (keyconf->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)112		flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;113 114	return flags;115}116 117struct iwl_mvm_sta_key_update_data {118	struct ieee80211_sta *sta;119	u32 old_sta_mask;120	u32 new_sta_mask;121	int err;122};123 124static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw,125				       struct ieee80211_vif *vif,126				       struct ieee80211_sta *sta,127				       struct ieee80211_key_conf *key,128				       void *_data)129{130	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);131	struct iwl_mvm_sta_key_update_data *data = _data;132	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);133	struct iwl_sec_key_cmd cmd = {134		.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),135		.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),136		.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),137		.u.modify.key_id = cpu_to_le32(key->keyidx),138		.u.modify.key_flags =139			cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)),140	};141	int err;142 143	/* only need to do this for pairwise keys (link_id == -1) */144	if (sta != data->sta || key->link_id >= 0)145		return;146 147	err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);148 149	if (err)150		data->err = err;151}152 153int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,154				struct ieee80211_vif *vif,155				struct ieee80211_sta *sta,156				u32 old_sta_mask,157				u32 new_sta_mask)158{159	struct iwl_mvm_sta_key_update_data data = {160		.sta = sta,161		.old_sta_mask = old_sta_mask,162		.new_sta_mask = new_sta_mask,163	};164 165	ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key,166			    &data);167	return data.err;168}169 170static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask,171				 u32 key_flags, u32 keyidx, u32 flags)172{173	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);174	struct iwl_sec_key_cmd cmd = {175		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),176		.u.remove.sta_mask = cpu_to_le32(sta_mask),177		.u.remove.key_id = cpu_to_le32(keyidx),178		.u.remove.key_flags = cpu_to_le32(key_flags),179	};180 181	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, flags, sizeof(cmd), &cmd);182}183 184int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags,185			 struct ieee80211_key_conf *keyconf)186{187	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);188	struct iwl_sec_key_cmd cmd = {189		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),190		.u.add.sta_mask = cpu_to_le32(sta_mask),191		.u.add.key_id = cpu_to_le32(keyconf->keyidx),192		.u.add.key_flags = cpu_to_le32(key_flags),193		.u.add.tx_seq = cpu_to_le64(atomic64_read(&keyconf->tx_pn)),194	};195	int max_key_len = sizeof(cmd.u.add.key);196	int ret;197 198	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||199	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)200		max_key_len -= IWL_SEC_WEP_KEY_OFFSET;201 202	if (WARN_ON(keyconf->keylen > max_key_len))203		return -EINVAL;204 205	if (WARN_ON(!sta_mask))206		return -EINVAL;207 208	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||209	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)210		memcpy(cmd.u.add.key + IWL_SEC_WEP_KEY_OFFSET, keyconf->key,211		       keyconf->keylen);212	else213		memcpy(cmd.u.add.key, keyconf->key, keyconf->keylen);214 215	if (keyconf->cipher == WLAN_CIPHER_SUITE_TKIP) {216		memcpy(cmd.u.add.tkip_mic_rx_key,217		       keyconf->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,218		       8);219		memcpy(cmd.u.add.tkip_mic_tx_key,220		       keyconf->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,221		       8);222	}223 224	ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);225	if (ret)226		return ret;227 228	/*229	 * For WEP, the same key is used for multicast and unicast so need to230	 * upload it again. If this fails, remove the original as well.231	 */232	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||233	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {234		cmd.u.add.key_flags ^= cpu_to_le32(IWL_SEC_KEY_FLAG_MCAST_KEY);235		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);236		if (ret)237			__iwl_mvm_sec_key_del(mvm, sta_mask, key_flags,238					      keyconf->keyidx, 0);239	}240 241	return ret;242}243 244int iwl_mvm_sec_key_add(struct iwl_mvm *mvm,245			struct ieee80211_vif *vif,246			struct ieee80211_sta *sta,247			struct ieee80211_key_conf *keyconf)248{249	u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf);250	u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);251	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);252	struct iwl_mvm_vif_link_info *mvm_link = NULL;253	int ret;254 255	if (keyconf->keyidx == 4 || keyconf->keyidx == 5) {256		unsigned int link_id = 0;257 258		/* set to -1 for non-MLO right now */259		if (keyconf->link_id >= 0)260			link_id = keyconf->link_id;261 262		mvm_link = mvmvif->link[link_id];263		if (WARN_ON(!mvm_link))264			return -EINVAL;265 266		if (mvm_link->igtk) {267			IWL_DEBUG_MAC80211(mvm, "remove old IGTK %d\n",268					   mvm_link->igtk->keyidx);269			ret = iwl_mvm_sec_key_del(mvm, vif, sta,270						  mvm_link->igtk);271			if (ret)272				IWL_ERR(mvm,273					"failed to remove old IGTK (ret=%d)\n",274					ret);275		}276 277		WARN_ON(mvm_link->igtk);278	}279 280	ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf);281	if (ret)282		return ret;283 284	if (mvm_link)285		mvm_link->igtk = keyconf;286 287	/* We don't really need this, but need it to be not invalid,288	 * and if we switch links multiple times it might go to be289	 * invalid when removed.290	 */291	keyconf->hw_key_idx = 0;292 293	return 0;294}295 296static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm,297				struct ieee80211_vif *vif,298				struct ieee80211_sta *sta,299				struct ieee80211_key_conf *keyconf,300				u32 flags)301{302	u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf);303	u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);304	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);305	int ret;306 307	if (WARN_ON(!sta_mask))308		return -EINVAL;309 310	if (keyconf->keyidx == 4 || keyconf->keyidx == 5) {311		struct iwl_mvm_vif_link_info *mvm_link;312		unsigned int link_id = 0;313 314		/* set to -1 for non-MLO right now */315		if (keyconf->link_id >= 0)316			link_id = keyconf->link_id;317 318		mvm_link = mvmvif->link[link_id];319		if (WARN_ON(!mvm_link))320			return -EINVAL;321 322		if (mvm_link->igtk == keyconf) {323			/* no longer in HW - mark for later */324			mvm_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID;325			mvm_link->igtk = NULL;326		}327	}328 329	ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx,330				    flags);331	if (ret)332		return ret;333 334	/* For WEP, delete the key again as unicast */335	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||336	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {337		key_flags ^= IWL_SEC_KEY_FLAG_MCAST_KEY;338		ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags,339					    keyconf->keyidx, flags);340	}341 342	return ret;343}344 345int iwl_mvm_sec_key_del_pasn(struct iwl_mvm *mvm,346			     struct ieee80211_vif *vif,347			     u32 sta_mask,348			     struct ieee80211_key_conf *keyconf)349{350	u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) |351		IWL_SEC_KEY_FLAG_MFP;352 353	if (WARN_ON(!sta_mask))354		return -EINVAL;355 356	return  __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx,357				      0);358}359 360int iwl_mvm_sec_key_del(struct iwl_mvm *mvm,361			struct ieee80211_vif *vif,362			struct ieee80211_sta *sta,363			struct ieee80211_key_conf *keyconf)364{365	return _iwl_mvm_sec_key_del(mvm, vif, sta, keyconf, 0);366}367 368static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw,369					   struct ieee80211_vif *vif,370					   struct ieee80211_sta *sta,371					   struct ieee80211_key_conf *key,372					   void *data)373{374	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);375	unsigned int link_id = (uintptr_t)data;376 377	if (key->hw_key_idx == STA_KEY_IDX_INVALID)378		return;379 380	if (sta)381		return;382 383	if (key->link_id >= 0 && key->link_id != link_id)384		return;385 386	_iwl_mvm_sec_key_del(mvm, vif, NULL, key, CMD_ASYNC);387	key->hw_key_idx = STA_KEY_IDX_INVALID;388}389 390void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm,391			       struct ieee80211_vif *vif,392			       struct iwl_mvm_vif_link_info *link,393			       unsigned int link_id)394{395	u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);396	u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0);397 398	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION ||399			 link->ap_sta_id == IWL_MVM_INVALID_STA))400		return;401 402	if (!sec_key_ver)403		return;404 405	ieee80211_iter_keys(mvm->hw, vif,406			    iwl_mvm_sec_key_remove_ap_iter,407			    (void *)(uintptr_t)link_id);408}409