File Index
Files and directories
Sign in
FILE

key.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/key.c C
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2/*
  3 * Copyright (C) 2024 Intel Corporation
  4 */
  5#include "key.h"
  6#include "iface.h"
  7#include "sta.h"
  8#include "fw/api/datapath.h"
  9
 10static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
 11				 struct ieee80211_vif *vif,
 12				 struct ieee80211_sta *sta,
 13				 struct ieee80211_key_conf *key)
 14{
 15	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 16	bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;
 17	bool igtk = key->keyidx == 4 || key->keyidx == 5;
 18	u32 flags = 0;
 19
 20	if (!pairwise)
 21		flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;
 22
 23	switch (key->cipher) {
 24	case WLAN_CIPHER_SUITE_TKIP:
 25		flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;
 26		break;
 27	case WLAN_CIPHER_SUITE_AES_CMAC:
 28	case WLAN_CIPHER_SUITE_CCMP:
 29		flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;
 30		break;
 31	case WLAN_CIPHER_SUITE_GCMP_256:
 32	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 33		flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;
 34		fallthrough;
 35	case WLAN_CIPHER_SUITE_GCMP:
 36	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
 37		flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;
 38		break;
 39	}
 40
 41	if (!sta && vif->type == NL80211_IFTYPE_STATION)
 42		sta = mld_vif->ap_sta;
 43
 44	/* If we are installing an iGTK (in AP or STA mode), we need to tell
 45	 * the firmware this key will en/decrypt MGMT frames.
 46	 * Same goes if we are installing a pairwise key for an MFP station.
 47	 * In case we're installing a groupwise key (which is not an iGTK),
 48	 * then, we will not use this key for MGMT frames.
 49	 */
 50	if ((sta && sta->mfp && pairwise) || igtk)
 51		flags |= IWL_SEC_KEY_FLAG_MFP;
 52
 53	if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)
 54		flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;
 55
 56	return flags;
 57}
 58
 59static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
 60				    struct ieee80211_vif *vif,
 61				    struct ieee80211_sta *sta,
 62				    struct ieee80211_key_conf *key)
 63{
 64	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 65	struct ieee80211_link_sta *link_sta;
 66	int sta_id;
 67
 68	lockdep_assert_wiphy(mld->wiphy);
 69
 70	/* AP group keys are per link and should be on the mcast/bcast STA */
 71	if (vif->type == NL80211_IFTYPE_AP &&
 72	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
 73		struct iwl_mld_link *link = NULL;
 74
 75		if (key->link_id >= 0)
 76			link = iwl_mld_link_dereference_check(mld_vif,
 77							      key->link_id);
 78
 79		if (WARN_ON(!link))
 80			return 0;
 81
 82		/* In this stage we should have both the bcast and mcast STAs */
 83		if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA ||
 84			    link->mcast_sta.sta_id == IWL_INVALID_STA))
 85			return 0;
 86
 87		/* IGTK/BIGTK to bcast STA */
 88		if (key->keyidx >= 4)
 89			return BIT(link->bcast_sta.sta_id);
 90
 91		/* GTK for data to mcast STA */
 92		return BIT(link->mcast_sta.sta_id);
 93	}
 94
 95	/* for client mode use the AP STA also for group keys */
 96	if (!sta && vif->type == NL80211_IFTYPE_STATION)
 97		sta = mld_vif->ap_sta;
 98
 99	/* STA should be non-NULL now */
100	if (WARN_ON(!sta))
101		return 0;
102
103	/* Key is not per-link, get the full sta mask */
104	if (key->link_id < 0)
105		return iwl_mld_fw_sta_id_mask(mld, sta);
106
107	/* The link_sta shouldn't be NULL now, but this is checked in
108	 * iwl_mld_fw_sta_id_mask
109	 */
110	link_sta = link_sta_dereference_check(sta, key->link_id);
111
112	sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
113	if (sta_id < 0)
114		return 0;
115
116	return BIT(sta_id);
117}
118
119static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask,
120				 u32 key_flags, struct ieee80211_key_conf *key)
121{
122	struct iwl_sec_key_cmd cmd = {
123		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
124		.u.add.sta_mask = cpu_to_le32(sta_mask),
125		.u.add.key_id = cpu_to_le32(key->keyidx),
126		.u.add.key_flags = cpu_to_le32(key_flags),
127		.u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)),
128	};
129	bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP;
130	int max_key_len = sizeof(cmd.u.add.key);
131
132#ifdef CONFIG_PM_SLEEP
133	/* If there was a rekey in wowlan, FW already has the key */
134	if (mld->fw_status.resuming)
135		return 0;
136#endif
137
138	if (WARN_ON(!sta_mask))
139		return -EINVAL;
140
141	if (WARN_ON(key->keylen > max_key_len))
142		return -EINVAL;
143
144	memcpy(cmd.u.add.key, key->key, key->keylen);
145
146	if (tkip) {
147		memcpy(cmd.u.add.tkip_mic_rx_key,
148		       key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
149		       8);
150		memcpy(cmd.u.add.tkip_mic_tx_key,
151		       key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
152		       8);
153	}
154
155	return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
156				    &cmd);
157}
158
159static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,
160				       u32 key_flags, u32 keyidx)
161{
162	struct iwl_sec_key_cmd cmd = {
163		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
164		.u.remove.sta_mask = cpu_to_le32(sta_mask),
165		.u.remove.key_id = cpu_to_le32(keyidx),
166		.u.remove.key_flags = cpu_to_le32(key_flags),
167	};
168
169#ifdef CONFIG_PM_SLEEP
170	/* If there was a rekey in wowlan, FW already removed the key */
171	if (mld->fw_status.resuming)
172		return;
173#endif
174
175	if (WARN_ON(!sta_mask))
176		return;
177
178	iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);
179}
180
181void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif,
182			struct ieee80211_sta *sta,
183			struct ieee80211_key_conf *key)
184{
185	u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
186	u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
187	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
188
189	lockdep_assert_wiphy(mld->wiphy);
190
191	if (!sta_mask)
192		return;
193
194	if (key->keyidx == 4 || key->keyidx == 5) {
195		struct iwl_mld_link *mld_link;
196		unsigned int link_id = 0;
197
198		/* set to -1 for non-MLO right now */
199		if (key->link_id >= 0)
200			link_id = key->link_id;
201
202		mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
203		if (WARN_ON(!mld_link))
204			return;
205
206		if (mld_link->igtk == key)
207			mld_link->igtk = NULL;
208
209		mld->num_igtks--;
210	}
211
212	iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx);
213
214	/* no longer in HW */
215	key->hw_key_idx = STA_KEY_IDX_INVALID;
216}
217
218int iwl_mld_add_key(struct iwl_mld *mld,
219		    struct ieee80211_vif *vif,
220		    struct ieee80211_sta *sta,
221		    struct ieee80211_key_conf *key)
222{
223	u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
224	u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
225	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
226	struct iwl_mld_link *mld_link = NULL;
227	bool igtk = key->keyidx == 4 || key->keyidx == 5;
228	int ret;
229
230	lockdep_assert_wiphy(mld->wiphy);
231
232	if (!sta_mask)
233		return -EINVAL;
234
235	if (igtk) {
236		if (mld->num_igtks == IWL_MAX_NUM_IGTKS)
237			return -EOPNOTSUPP;
238
239		u8 link_id = 0;
240
241		/* set to -1 for non-MLO right now */
242		if (key->link_id >= 0)
243			link_id = key->link_id;
244
245		mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
246
247		if (WARN_ON(!mld_link))
248			return -EINVAL;
249
250		if (mld_link->igtk) {
251			IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",
252					   mld_link->igtk->keyidx);
253			iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);
254		}
255
256		WARN_ON(mld_link->igtk);
257	}
258
259	ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);
260	if (ret)
261		return ret;
262
263	if (mld_link) {
264		mld_link->igtk = key;
265		mld->num_igtks++;
266	}
267
268	/* We don't really need this, but need it to be not invalid,
269	 * so we will know if the key is in fw.
270	 */
271	key->hw_key_idx = 0;
272
273	return 0;
274}
275
276struct remove_ap_keys_iter_data {
277	u8 link_id;
278	struct ieee80211_sta *sta;
279};
280
281static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw,
282					struct ieee80211_vif *vif,
283					struct ieee80211_sta *sta,
284					struct ieee80211_key_conf *key,
285					void *_data)
286{
287	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
288	struct remove_ap_keys_iter_data *data = _data;
289
290	if (key->hw_key_idx == STA_KEY_IDX_INVALID)
291		return;
292
293	/* All the pairwise keys should have been removed by now */
294	if (WARN_ON(sta))
295		return;
296
297	if (key->link_id >= 0 && key->link_id != data->link_id)
298		return;
299
300	iwl_mld_remove_key(mld, vif, data->sta, key);
301}
302
303void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif,
304			    struct ieee80211_sta *sta, unsigned int link_id)
305{
306	struct remove_ap_keys_iter_data iter_data = {
307		.link_id = link_id,
308		.sta = sta,
309	};
310
311	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
312		return;
313
314	ieee80211_iter_keys(mld->hw, vif,
315			    iwl_mld_remove_ap_keys_iter,
316			    &iter_data);
317}
318
319struct iwl_mvm_sta_key_update_data {
320	struct ieee80211_sta *sta;
321	u32 old_sta_mask;
322	u32 new_sta_mask;
323	int err;
324};
325
326static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw,
327					struct ieee80211_vif *vif,
328					struct ieee80211_sta *sta,
329					struct ieee80211_key_conf *key,
330					void *_data)
331{
332	struct iwl_mvm_sta_key_update_data *data = _data;
333	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
334	struct iwl_sec_key_cmd cmd = {
335		.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
336		.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
337		.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
338		.u.modify.key_id = cpu_to_le32(key->keyidx),
339		.u.modify.key_flags =
340			cpu_to_le32(iwl_mld_get_key_flags(mld, vif, sta, key)),
341	};
342	int err;
343
344	/* only need to do this for pairwise keys (link_id == -1) */
345	if (sta != data->sta || key->link_id >= 0)
346		return;
347
348	err = iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
349				   &cmd);
350
351	if (err)
352		data->err = err;
353}
354
355int iwl_mld_update_sta_keys(struct iwl_mld *mld,
356			    struct ieee80211_vif *vif,
357			    struct ieee80211_sta *sta,
358			    u32 old_sta_mask,
359			    u32 new_sta_mask)
360{
361	struct iwl_mvm_sta_key_update_data data = {
362		.sta = sta,
363		.old_sta_mask = old_sta_mask,
364		.new_sta_mask = new_sta_mask,
365	};
366
367	ieee80211_iter_keys(mld->hw, vif, iwl_mld_update_sta_key_iter,
368			    &data);
369	return data.err;
370}
371
372void iwl_mld_track_bigtk(struct iwl_mld *mld,
373			 struct ieee80211_vif *vif,
374			 struct ieee80211_key_conf *key, bool add)
375{
376	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
377	struct iwl_mld_link *link;
378
379	if (vif->type != NL80211_IFTYPE_STATION)
380		return;
381
382	if (WARN_ON(key->keyidx < 6 || key->keyidx > 7))
383		return;
384
385	if (WARN_ON(key->link_id < 0))
386		return;
387
388	link = iwl_mld_link_dereference_check(mld_vif, key->link_id);
389	if (WARN_ON(!link))
390		return;
391
392	if (add)
393		rcu_assign_pointer(link->bigtks[key->keyidx - 6], key);
394	else
395		RCU_INIT_POINTER(link->bigtks[key->keyidx - 6], NULL);
396}
397
398bool iwl_mld_beacon_protection_enabled(struct iwl_mld *mld,
399				       struct ieee80211_bss_conf *link)
400{
401	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
402
403	if (WARN_ON(!mld_link))
404		return false;
405
406	return rcu_access_pointer(mld_link->bigtks[0]) ||
407		rcu_access_pointer(mld_link->bigtks[1]);
408}
~/ Read-only file preview.