Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/key.c
48287 views
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
10
static 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
59
static 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
119
static 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
159
static 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
181
void 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
218
int 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
276
struct remove_ap_keys_iter_data {
277
u8 link_id;
278
struct ieee80211_sta *sta;
279
};
280
281
static 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
303
void 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
319
struct 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
326
static 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
355
int 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
372