Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mvm/mld-key.c
48285 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2022 - 2024 Intel Corporation
4
*/
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
11
static 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 later
42
* (which sounds like a bad sequence, but remember that to mac80211 the
43
* 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 as
45
* the group keys are per link; make sure that is the case by checking
46
* 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 do
48
* 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
59
u32 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 tell
103
* 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
117
struct 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
124
static 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
153
int 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
170
static 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
184
int 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
else
213
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 to
230
* 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
244
int 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 be
289
* invalid when removed.
290
*/
291
keyconf->hw_key_idx = 0;
292
293
return 0;
294
}
295
296
static 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
345
int 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
360
int 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
368
static 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
390
void 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_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
410