1 /*
2 * hostapd / IEEE 802.11be EHT
3 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "utils/includes.h"
10 #include "utils/common.h"
11 #include "common/ocv.h"
12 #include "common/wpa_ctrl.h"
13 #include "crypto/crypto.h"
14 #include "crypto/dh_groups.h"
15 #include "hostapd.h"
16 #include "sta_info.h"
17 #include "ap_drv_ops.h"
18 #include "wpa_auth.h"
19 #include "ieee802_11.h"
20
21
ieee80211_eht_ppet_size(u16 ppe_thres_hdr,const u8 * phy_cap_info)22 static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
23 {
24 u8 ru;
25 u16 sz = 0;
26
27 if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
28 EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
29 return 0;
30
31 ru = (ppe_thres_hdr &
32 EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
33 while (ru) {
34 if (ru & 0x1)
35 sz++;
36 ru >>= 1;
37 }
38
39 sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
40 EHT_PPE_THRES_NSS_SHIFT));
41 sz = (sz * 6) + 9;
42 if (sz % 8)
43 sz += 8;
44 sz /= 8;
45
46 return sz;
47 }
48
49
ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode,u8 opclass,const u8 * he_phy_cap,const u8 * eht_phy_cap)50 static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
51 const u8 *he_phy_cap,
52 const u8 *eht_phy_cap)
53 {
54 u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
55 bool band24, band5, band6;
56 u8 cap_chwidth;
57
58 cap_chwidth = he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
59
60 band24 = mode == HOSTAPD_MODE_IEEE80211B ||
61 mode == HOSTAPD_MODE_IEEE80211G ||
62 mode == NUM_HOSTAPD_MODES;
63 band5 = mode == HOSTAPD_MODE_IEEE80211A ||
64 mode == NUM_HOSTAPD_MODES;
65 band6 = is_6ghz_op_class(opclass);
66
67 if (band24 &&
68 (cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
69 return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
70
71 if (band5 &&
72 (cap_chwidth &
73 (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
74 HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
75 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
76 return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
77
78 if (band5 &&
79 (cap_chwidth &
80 (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
81 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
82 sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
83
84 if (band6 &&
85 (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
86 EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK))
87 sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
88
89 return sz;
90 }
91
92
hostapd_eid_eht_capab_len(struct hostapd_data * hapd,enum ieee80211_op_mode opmode)93 size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
94 enum ieee80211_op_mode opmode)
95 {
96 struct hostapd_hw_modes *mode;
97 struct eht_capabilities *eht_cap;
98 size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
99
100 mode = hapd->iface->current_mode;
101 if (!mode)
102 return 0;
103
104 eht_cap = &mode->eht_capab[opmode];
105 if (!eht_cap->eht_supported)
106 return 0;
107
108 len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
109 mode->he_capab[opmode].phy_cap,
110 eht_cap->phy_cap);
111 len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
112 eht_cap->phy_cap);
113
114 return len;
115 }
116
117
hostapd_eid_eht_capab(struct hostapd_data * hapd,u8 * eid,enum ieee80211_op_mode opmode)118 u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
119 enum ieee80211_op_mode opmode)
120 {
121 struct hostapd_hw_modes *mode;
122 struct eht_capabilities *eht_cap;
123 struct ieee80211_eht_capabilities *cap;
124 size_t mcs_nss_len, ppe_thresh_len;
125 u8 *pos = eid, *length_pos;
126
127 mode = hapd->iface->current_mode;
128 if (!mode)
129 return eid;
130
131 eht_cap = &mode->eht_capab[opmode];
132 if (!eht_cap->eht_supported)
133 return eid;
134
135 *pos++ = WLAN_EID_EXTENSION;
136 length_pos = pos++;
137 *pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
138
139 cap = (struct ieee80211_eht_capabilities *) pos;
140 os_memset(cap, 0, sizeof(*cap));
141 cap->mac_cap = host_to_le16(eht_cap->mac_cap);
142 os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
143
144 if (!is_6ghz_op_class(hapd->iconf->op_class))
145 cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
146 ~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
147 if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
148 cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
149 ~EHT_PHYCAP_SU_BEAMFORMER;
150
151 if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
152 cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
153 ~EHT_PHYCAP_SU_BEAMFORMEE;
154
155 if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
156 cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
157 ~EHT_PHYCAP_MU_BEAMFORMER_MASK;
158
159 pos = cap->optional;
160
161 mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
162 hapd->iconf->op_class,
163 mode->he_capab[opmode].phy_cap,
164 eht_cap->phy_cap);
165 if (mcs_nss_len) {
166 os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
167 pos += mcs_nss_len;
168 }
169
170 ppe_thresh_len = ieee80211_eht_ppet_size(
171 WPA_GET_LE16(&eht_cap->ppet[0]),
172 eht_cap->phy_cap);
173 if (ppe_thresh_len) {
174 os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
175 pos += ppe_thresh_len;
176 }
177
178 *length_pos = pos - (eid + 2);
179 return pos;
180 }
181
182
hostapd_eid_eht_operation(struct hostapd_data * hapd,u8 * eid)183 u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
184 {
185 struct hostapd_config *conf = hapd->iconf;
186 struct ieee80211_eht_operation *oper;
187 u8 *pos = eid, seg0 = 0, seg1 = 0;
188 enum oper_chan_width chwidth;
189 size_t elen = 1 + 4;
190 bool eht_oper_info_present;
191 u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
192
193 if (!hapd->iface->current_mode)
194 return eid;
195
196 if (is_6ghz_op_class(conf->op_class))
197 chwidth = op_class_to_ch_width(conf->op_class);
198 else
199 chwidth = conf->eht_oper_chwidth;
200
201 eht_oper_info_present = chwidth == CONF_OPER_CHWIDTH_320MHZ ||
202 punct_bitmap;
203
204 if (eht_oper_info_present)
205 elen += 3;
206
207 if (punct_bitmap)
208 elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
209
210 *pos++ = WLAN_EID_EXTENSION;
211 *pos++ = 1 + elen;
212 *pos++ = WLAN_EID_EXT_EHT_OPERATION;
213
214 oper = (struct ieee80211_eht_operation *) pos;
215 oper->oper_params = 0;
216
217 if (hapd->iconf->eht_default_pe_duration)
218 oper->oper_params |= EHT_OPER_DEFAULT_PE_DURATION;
219
220 /* TODO: Fill in appropriate EHT-MCS max Nss information */
221 oper->basic_eht_mcs_nss_set[0] = 0x11;
222 oper->basic_eht_mcs_nss_set[1] = 0x00;
223 oper->basic_eht_mcs_nss_set[2] = 0x00;
224 oper->basic_eht_mcs_nss_set[3] = 0x00;
225
226 if (hapd->iconf->disable_mcs15_rx)
227 oper->oper_params |= EHT_OPER_MCS15_DISABLE;
228
229 if (!eht_oper_info_present)
230 return pos + elen;
231
232 oper->oper_params |= EHT_OPER_INFO_PRESENT;
233 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
234
235 switch (chwidth) {
236 case CONF_OPER_CHWIDTH_320MHZ:
237 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
238 seg1 = seg0;
239 if (hapd->iconf->channel < seg0)
240 seg0 -= 16;
241 else
242 seg0 += 16;
243 break;
244 case CONF_OPER_CHWIDTH_160MHZ:
245 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
246 seg1 = seg0;
247 if (hapd->iconf->channel < seg0)
248 seg0 -= 8;
249 else
250 seg0 += 8;
251 break;
252 case CONF_OPER_CHWIDTH_80MHZ:
253 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
254 break;
255 case CONF_OPER_CHWIDTH_USE_HT:
256 if (seg0)
257 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
258 break;
259 default:
260 return eid;
261 }
262
263 oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
264 oper->oper_info.ccfs1 = seg1;
265
266 if (punct_bitmap) {
267 oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
268 oper->oper_info.disabled_chan_bitmap =
269 host_to_le16(punct_bitmap);
270 }
271
272 return pos + elen;
273 }
274
275
check_valid_eht_mcs_nss(struct hostapd_data * hapd,const u8 * ap_mcs,const u8 * sta_mcs,u8 mcs_count,u8 map_len)276 static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
277 const u8 *sta_mcs, u8 mcs_count, u8 map_len)
278 {
279 unsigned int i, j;
280
281 for (i = 0; i < mcs_count; i++) {
282 ap_mcs += i * 3;
283 sta_mcs += i * 3;
284
285 for (j = 0; j < map_len; j++) {
286 if (((ap_mcs[j] >> 4) & 0xFF) == 0)
287 continue;
288
289 if ((sta_mcs[j] & 0xFF) == 0)
290 continue;
291
292 return true;
293 }
294 }
295
296 wpa_printf(MSG_DEBUG,
297 "No matching EHT MCS found between AP TX and STA RX");
298 return false;
299 }
300
301
check_valid_eht_mcs(struct hostapd_data * hapd,const u8 * sta_eht_capab,enum ieee80211_op_mode opmode)302 static bool check_valid_eht_mcs(struct hostapd_data *hapd,
303 const u8 *sta_eht_capab,
304 enum ieee80211_op_mode opmode)
305 {
306 struct hostapd_hw_modes *mode;
307 const struct ieee80211_eht_capabilities *capab;
308 const u8 *ap_mcs, *sta_mcs;
309 u8 mcs_count = 1;
310
311 mode = hapd->iface->current_mode;
312 if (!mode)
313 return true;
314
315 ap_mcs = mode->eht_capab[opmode].mcs;
316 capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
317 sta_mcs = capab->optional;
318
319 if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
320 mode->he_capab[opmode].phy_cap,
321 mode->eht_capab[opmode].phy_cap) ==
322 EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
323 return check_valid_eht_mcs_nss(
324 hapd, ap_mcs, sta_mcs, 1,
325 EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
326
327 switch (hapd->iface->conf->eht_oper_chwidth) {
328 case CONF_OPER_CHWIDTH_320MHZ:
329 mcs_count++;
330 /* fall through */
331 case CONF_OPER_CHWIDTH_80P80MHZ:
332 case CONF_OPER_CHWIDTH_160MHZ:
333 mcs_count++;
334 break;
335 default:
336 break;
337 }
338
339 return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
340 EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
341 }
342
343
ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,u8 opclass,const u8 * he_cap,const u8 * eht_cap,size_t len)344 static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
345 u8 opclass, const u8 *he_cap,
346 const u8 *eht_cap, size_t len)
347 {
348 const struct ieee80211_he_capabilities *he_capab;
349 struct ieee80211_eht_capabilities *cap;
350 const u8 *he_phy_cap;
351 size_t cap_len;
352 u16 ppe_thres_hdr;
353
354 he_capab = (const struct ieee80211_he_capabilities *) he_cap;
355 he_phy_cap = he_capab->he_phy_capab_info;
356 cap = (struct ieee80211_eht_capabilities *) eht_cap;
357 cap_len = sizeof(*cap) - sizeof(cap->optional);
358 if (len < cap_len)
359 return true;
360
361 cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_phy_cap,
362 cap->phy_cap);
363 if (len < cap_len)
364 return true;
365
366 ppe_thres_hdr = len > cap_len + 1 ?
367 WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
368 cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
369
370 return len < cap_len;
371 }
372
373
copy_sta_eht_capab(struct hostapd_data * hapd,struct sta_info * sta,enum ieee80211_op_mode opmode,const u8 * he_capab,size_t he_capab_len,const u8 * eht_capab,size_t eht_capab_len)374 u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
375 enum ieee80211_op_mode opmode,
376 const u8 *he_capab, size_t he_capab_len,
377 const u8 *eht_capab, size_t eht_capab_len)
378 {
379 struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
380 enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
381
382 if (!hostapd_is_eht_enabled(hapd) ||
383 !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
384 !eht_capab ||
385 ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
386 he_capab, eht_capab,
387 eht_capab_len) ||
388 !check_valid_eht_mcs(hapd, eht_capab, opmode) ||
389 !(sta->flags & WLAN_STA_HE)) {
390 sta->flags &= ~WLAN_STA_EHT;
391 os_free(sta->eht_capab);
392 sta->eht_capab = NULL;
393 return WLAN_STATUS_SUCCESS;
394 }
395
396 os_free(sta->eht_capab);
397 sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
398 if (!sta->eht_capab) {
399 sta->eht_capab_len = 0;
400 return WLAN_STATUS_UNSPECIFIED_FAILURE;
401 }
402
403 sta->flags |= WLAN_STA_EHT;
404 sta->eht_capab_len = eht_capab_len;
405
406 return WLAN_STATUS_SUCCESS;
407 }
408
409
hostapd_get_eht_capab(struct hostapd_data * hapd,const struct ieee80211_eht_capabilities * src,struct ieee80211_eht_capabilities * dest,size_t len)410 void hostapd_get_eht_capab(struct hostapd_data *hapd,
411 const struct ieee80211_eht_capabilities *src,
412 struct ieee80211_eht_capabilities *dest,
413 size_t len)
414 {
415 if (!src || !dest)
416 return;
417
418 if (len > sizeof(*dest))
419 len = sizeof(*dest);
420 /* TODO: mask out unsupported features */
421
422 os_memset(dest, 0, sizeof(*dest));
423 os_memcpy(dest, src, len);
424 }
425
426
427 /* Beacon or a non ML Probe Response frame should include
428 * Common Info Length(1) + MLD MAC Address(6) +
429 * Link ID Info(1) + BSS Parameters Change count(1) +
430 * EML Capabilities (2) + MLD Capabilities (2)
431 */
432 #define EHT_ML_COMMON_INFO_LEN 13
433 /*
434 * control (2) + station info length (1) + MAC address (6) +
435 * beacon interval (2) + TSF offset (8) + DTIM info (2)
436 */
437 #define EHT_ML_STA_INFO_LEN 21
hostapd_eid_eht_basic_ml_common(struct hostapd_data * hapd,u8 * eid,struct mld_info * mld_info,bool include_mld_id,bool include_bpcc)438 u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
439 u8 *eid, struct mld_info *mld_info,
440 bool include_mld_id, bool include_bpcc)
441 {
442 struct wpabuf *buf;
443 u16 control;
444 u8 *pos = eid;
445 const u8 *ptr;
446 size_t len, slice_len;
447 u8 link_id;
448 u8 common_info_len;
449 u16 mld_cap;
450 u8 max_simul_links, active_links;
451 u16 mld_eml_capa;
452
453 /*
454 * As the Multi-Link element can exceed the size of 255 bytes need to
455 * first build it and then handle fragmentation.
456 */
457 buf = wpabuf_alloc(1024);
458 if (!buf)
459 return pos;
460
461 /* Multi-Link Control field */
462 control = MULTI_LINK_CONTROL_TYPE_BASIC |
463 BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
464 BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
465 BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
466 BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
467
468 /*
469 * Set the basic Multi-Link common information. Hard code the common
470 * info length to 13 based on the length of the present fields:
471 * Length (1) + MLD address (6) + Link ID (1) +
472 * BSS Parameters Change Count (1) + EML Capabilities (2) +
473 * MLD Capabilities and Operations (2)
474 */
475 common_info_len = EHT_ML_COMMON_INFO_LEN;
476
477 if (include_mld_id) {
478 /* AP MLD ID */
479 control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
480 common_info_len++;
481 }
482
483 wpabuf_put_le16(buf, control);
484
485 wpabuf_put_u8(buf, common_info_len);
486
487 /* Own MLD MAC Address */
488 wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
489
490 /* Own Link ID */
491 wpabuf_put_u8(buf, hapd->mld_link_id);
492
493 wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
494
495 /* Set EMLSR/EMLMR Transition and Padding delay to zero for AP MLD as
496 * per IEEE Std 802.11be-2024, 9.4.2.322.2.3 (Common Info field of the
497 * Basic Multi-Link element).
498 */
499 mld_eml_capa = hapd->iface->mld_eml_capa &
500 ~(EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK |
501 EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK);
502
503 wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x", mld_eml_capa);
504 wpabuf_put_le16(buf, mld_eml_capa);
505
506 mld_cap = hapd->iface->mld_mld_capa;
507 max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
508 active_links = hostapd_get_active_links(hapd);
509
510 if (active_links > max_simul_links) {
511 wpa_printf(MSG_ERROR,
512 "MLD: Error in max simultaneous links, advertised: 0x%x current: 0x%x",
513 max_simul_links, active_links);
514 active_links = max_simul_links;
515 }
516
517 mld_cap &= ~EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
518 mld_cap |= active_links & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
519
520 /* TODO: Advertise T2LM based on driver support as well */
521 mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
522
523 mld_cap |= EHT_ML_MLD_CAPA_LINK_RECONF_OP_SUPPORT;
524
525 wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
526 mld_cap);
527 wpabuf_put_le16(buf, mld_cap);
528
529 if (include_mld_id) {
530 wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
531 hostapd_get_mld_id(hapd));
532 wpabuf_put_u8(buf, hostapd_get_mld_id(hapd));
533 }
534
535 if (!mld_info)
536 goto out;
537
538 /* Add link info for the other links */
539 for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
540 struct mld_link_info *link = &mld_info->links[link_id];
541 size_t sta_info_len = EHT_ML_STA_INFO_LEN;
542 struct hostapd_data *link_bss;
543 size_t total_len;
544
545 /* Skip the local one */
546 if (link_id == hapd->mld_link_id || !link->valid)
547 continue;
548
549 link_bss = hostapd_mld_get_link_bss(hapd, link_id);
550 if (!link_bss) {
551 wpa_printf(MSG_ERROR,
552 "MLD: Couldn't find link BSS - skip it");
553 continue;
554 }
555
556 /* BSS Parameters Change Count (1) for (Re)Association Response
557 * frames */
558 if (include_bpcc)
559 sta_info_len++;
560
561 total_len = sta_info_len + link->resp_sta_profile_len;
562
563 /* Per-STA Profile subelement */
564 wpabuf_put_u8(buf, MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE);
565
566 if (total_len <= 255)
567 wpabuf_put_u8(buf, total_len);
568 else
569 wpabuf_put_u8(buf, 255);
570
571 /* STA Control */
572 control = (link_id & 0xf) |
573 BASIC_MLE_STA_CTRL_PRES_STA_MAC |
574 BASIC_MLE_STA_CTRL_COMPLETE_PROFILE |
575 BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET |
576 BASIC_MLE_STA_CTRL_PRES_BEACON_INT |
577 BASIC_MLE_STA_CTRL_PRES_DTIM_INFO;
578
579 if (include_bpcc)
580 control |= BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT;
581
582 wpabuf_put_le16(buf, control);
583
584 /* STA Info */
585
586 /* STA Info Length */
587 wpabuf_put_u8(buf, sta_info_len - 2);
588 wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
589 wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
590
591 /* TSF Offset */
592 /*
593 * TODO: Currently setting TSF offset to zero. However, this
594 * information needs to come from the driver.
595 */
596 wpabuf_put_le64(buf, 0);
597
598 /* DTIM Info */
599 wpabuf_put_u8(buf, 0); /* DTIM Count */
600 wpabuf_put_u8(buf, link_bss->conf->dtim_period);
601
602 /* BSS Parameters Change Count */
603 if (include_bpcc)
604 wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
605
606 if (!link->resp_sta_profile)
607 continue;
608
609 /* Fragment the sub element if needed */
610 if (total_len <= 255) {
611 wpabuf_put_data(buf, link->resp_sta_profile,
612 link->resp_sta_profile_len);
613 } else {
614 ptr = link->resp_sta_profile;
615 len = link->resp_sta_profile_len;
616
617 slice_len = 255 - sta_info_len;
618
619 wpabuf_put_data(buf, ptr, slice_len);
620 len -= slice_len;
621 ptr += slice_len;
622
623 while (len) {
624 if (len <= 255)
625 slice_len = len;
626 else
627 slice_len = 255;
628
629 wpabuf_put_u8(buf,
630 MULTI_LINK_SUB_ELEM_ID_FRAGMENT);
631 wpabuf_put_u8(buf, slice_len);
632 wpabuf_put_data(buf, ptr, slice_len);
633
634 len -= slice_len;
635 ptr += slice_len;
636 }
637 }
638 }
639
640 out:
641 /* Fragment the Multi-Link element, if needed */
642 len = wpabuf_len(buf);
643 ptr = wpabuf_head(buf);
644
645 if (len <= 254)
646 slice_len = len;
647 else
648 slice_len = 254;
649
650 *pos++ = WLAN_EID_EXTENSION;
651 *pos++ = slice_len + 1;
652 *pos++ = WLAN_EID_EXT_MULTI_LINK;
653 os_memcpy(pos, ptr, slice_len);
654
655 ptr += slice_len;
656 pos += slice_len;
657 len -= slice_len;
658
659 while (len) {
660 if (len <= 255)
661 slice_len = len;
662 else
663 slice_len = 255;
664
665 *pos++ = WLAN_EID_FRAGMENT;
666 *pos++ = slice_len;
667 os_memcpy(pos, ptr, slice_len);
668
669 ptr += slice_len;
670 pos += slice_len;
671 len -= slice_len;
672 }
673
674 wpabuf_free(buf);
675 return pos;
676 }
677
678
hostapd_eid_eht_basic_ml_len(struct hostapd_data * hapd,struct sta_info * info,bool include_mld_id,bool include_bpcc)679 size_t hostapd_eid_eht_basic_ml_len(struct hostapd_data *hapd,
680 struct sta_info *info,
681 bool include_mld_id, bool include_bpcc)
682 {
683 int link_id;
684 size_t len, num_frags;
685
686 if (!hapd->conf->mld_ap)
687 return 0;
688
689 /* Include WLAN_EID_EXT_MULTI_LINK (1) */
690 len = 1;
691 /* control field */
692 len += 2;
693 /* Common info len for Basic MLE */
694 len += EHT_ML_COMMON_INFO_LEN;
695 if (include_mld_id)
696 len++;
697
698 if (!info)
699 goto out;
700
701 /* Add link info for the other links */
702 for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
703 struct mld_link_info *link = &info->mld_info.links[link_id];
704 struct hostapd_data *link_bss;
705 size_t sta_prof_len = EHT_ML_STA_INFO_LEN +
706 link->resp_sta_profile_len;
707
708 /* Skip the local one */
709 if (link_id == hapd->mld_link_id || !link->valid)
710 continue;
711
712 link_bss = hostapd_mld_get_link_bss(hapd, link_id);
713 if (!link_bss) {
714 wpa_printf(MSG_ERROR,
715 "MLD: Couldn't find link BSS - skip it");
716 continue;
717 }
718
719 /* BSS Parameters Change Count (1) for (Re)Association Response
720 * frames */
721 if (include_bpcc)
722 sta_prof_len++;
723
724 /* Per-STA Profile Subelement(1), Length (1) */
725 len += 2;
726 len += sta_prof_len;
727 /* Consider Fragment EID(1) and Length (1) for each subelement
728 * fragment. */
729 if (sta_prof_len > 255) {
730 num_frags = (sta_prof_len / 255 - 1) +
731 !!(sta_prof_len % 255);
732 len += num_frags * 2;
733 }
734
735 }
736
737 out:
738 if (len > 255) {
739 num_frags = (len / 255 - 1) + !!(len % 255);
740 len += num_frags * 2;
741 }
742
743 /* WLAN_EID_EXTENSION (1) + length (1) */
744 return len + 2;
745 }
746
747
hostapd_eid_eht_reconf_ml(struct hostapd_data * hapd,u8 * eid)748 static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
749 {
750 #ifdef CONFIG_TESTING_OPTIONS
751 struct hostapd_data *other_hapd;
752 u16 control;
753 u8 *pos = eid;
754 unsigned int i;
755
756 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML");
757
758 /* First check if the element needs to be added */
759 for (i = 0; i < hapd->iface->interfaces->count; i++) {
760 other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
761
762 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: %u",
763 other_hapd->eht_mld_link_removal_count);
764
765 if (other_hapd->eht_mld_link_removal_count)
766 break;
767 }
768
769 /* No link is going to be removed */
770 if (i == hapd->iface->interfaces->count)
771 return eid;
772
773 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: Adding element");
774
775 /* The length will be set at the end */
776 *pos++ = WLAN_EID_EXTENSION;
777 *pos++ = 0;
778 *pos++ = WLAN_EID_EXT_MULTI_LINK;
779
780 /* Set the Multi-Link Control field */
781 control = MULTI_LINK_CONTROL_TYPE_RECONF;
782 WPA_PUT_LE16(pos, control);
783 pos += 2;
784
785 /* Common Info doesn't include any information */
786 *pos++ = 1;
787
788 /* Add the per station profiles */
789 for (i = 0; i < hapd->iface->interfaces->count; i++) {
790 other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
791 if (!other_hapd->eht_mld_link_removal_count)
792 continue;
793
794 /* Subelement ID is 0 */
795 *pos++ = 0;
796 *pos++ = 5;
797
798 control = other_hapd->mld_link_id |
799 EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER;
800
801 WPA_PUT_LE16(pos, control);
802 pos += 2;
803
804 /* STA profile length */
805 *pos++ = 3;
806
807 WPA_PUT_LE16(pos, other_hapd->eht_mld_link_removal_count);
808 pos += 2;
809 }
810
811 eid[1] = pos - eid - 2;
812
813 wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration ML", eid, eid[1] + 2);
814 return pos;
815 #else /* CONFIG_TESTING_OPTIONS */
816 return eid;
817 #endif /* CONFIG_TESTING_OPTIONS */
818 }
819
820
hostapd_eid_eht_ml_len(struct mld_info * info,bool include_mld_id,bool include_bpcc)821 static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
822 bool include_mld_id, bool include_bpcc)
823 {
824 size_t len = 0;
825 size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
826 u8 link_id;
827
828 if (include_mld_id)
829 eht_ml_len++;
830
831 for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
832 link_id++) {
833 struct mld_link_info *link;
834 size_t sta_len = EHT_ML_STA_INFO_LEN;
835
836 link = &info->links[link_id];
837 if (!link->valid)
838 continue;
839
840 sta_len += link->resp_sta_profile_len;
841
842 /* BSS Parameters Change Count (1) for (Re)Association Response
843 * frames */
844 if (include_bpcc)
845 sta_len++;
846
847 /* Element data and (fragmentation) headers */
848 eht_ml_len += sta_len;
849 eht_ml_len += 2 + (sta_len - 1) / 255 * 2;
850 }
851
852 /* EID_EXT_MULTI_LINK (1) + Element data */
853 len += 1 + eht_ml_len;
854
855 /* Fragmentation headers */
856 len += (len - 1) / 255 * 2;
857
858 /* Outer header EID_EXT (1) + length (1) */
859 len += 2;
860
861 return len;
862 }
863 #undef EHT_ML_COMMON_INFO_LEN
864 #undef EHT_ML_STA_INFO_LEN
865
866
hostapd_eid_eht_ml_beacon(struct hostapd_data * hapd,struct mld_info * info,u8 * eid,bool include_mld_id)867 u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
868 struct mld_info *info,
869 u8 *eid, bool include_mld_id)
870 {
871 eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id,
872 false);
873 return hostapd_eid_eht_reconf_ml(hapd, eid);
874 }
875
876
877
hostapd_eid_eht_ml_assoc(struct hostapd_data * hapd,struct sta_info * info,u8 * eid)878 u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
879 u8 *eid)
880 {
881 if (!ap_sta_is_mld(hapd, info))
882 return eid;
883
884 eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info,
885 false, true);
886 ap_sta_free_sta_profile(&info->mld_info);
887 return hostapd_eid_eht_reconf_ml(hapd, eid);
888 }
889
890
hostapd_eid_eht_ml_beacon_len(struct hostapd_data * hapd,struct mld_info * info,bool include_mld_id)891 size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
892 struct mld_info *info,
893 bool include_mld_id)
894 {
895 return hostapd_eid_eht_ml_len(info, include_mld_id, false);
896 }
897
898
hostapd_ml_auth_resp(struct hostapd_data * hapd)899 struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
900 {
901 struct wpabuf *buf = wpabuf_alloc(12);
902
903 if (!buf)
904 return NULL;
905
906 wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
907 wpabuf_put_u8(buf, 10);
908 wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
909 wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC);
910 wpabuf_put_u8(buf, ETH_ALEN + 1);
911 wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
912
913 return buf;
914 }
915
916
917 #ifdef CONFIG_SAE
918
919 static const u8 *
sae_commit_skip_fixed_fields(const struct ieee80211_mgmt * mgmt,size_t len,const u8 * pos,u16 status_code)920 sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
921 const u8 *pos, u16 status_code)
922 {
923 u16 group;
924 size_t prime_len;
925 struct crypto_ec *ec;
926
927 if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
928 return pos;
929
930 /* SAE H2E commit message (group, scalar, FFE) */
931 if (len < 2) {
932 wpa_printf(MSG_DEBUG,
933 "EHT: SAE Group is not present");
934 return NULL;
935 }
936
937 group = WPA_GET_LE16(pos);
938 pos += 2;
939
940 /* TODO: How to parse when the group is unknown? */
941 ec = crypto_ec_init(group);
942 if (!ec) {
943 const struct dh_group *dh = dh_groups_get(group);
944
945 if (!dh) {
946 wpa_printf(MSG_DEBUG, "EHT: Unknown SAE group %u",
947 group);
948 return NULL;
949 }
950
951 prime_len = dh->prime_len;
952 } else {
953 prime_len = crypto_ec_prime_len(ec);
954 }
955
956 wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len);
957
958 if (len - 2 < prime_len * (ec ? 3 : 2))
959 goto truncated;
960 /* scalar */
961 pos += prime_len;
962
963 if (ec) {
964 pos += prime_len * 2;
965 crypto_ec_deinit(ec);
966 } else {
967 pos += prime_len;
968 }
969
970 if (pos - mgmt->u.auth.variable > (int) len) {
971 truncated:
972 wpa_printf(MSG_DEBUG,
973 "EHT: Too short SAE commit Authentication frame");
974 return NULL;
975 }
976
977 wpa_hexdump(MSG_DEBUG, "EHT: SAE: Authentication frame elements",
978 pos, (int) len - (pos - mgmt->u.auth.variable));
979
980 return pos;
981 }
982
983
984 static const u8 *
sae_confirm_skip_fixed_fields(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len,const u8 * pos,u16 status_code)985 sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
986 const struct ieee80211_mgmt *mgmt, size_t len,
987 const u8 *pos, u16 status_code)
988 {
989 struct sta_info *sta;
990
991 if (status_code == WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
992 return pos;
993
994 /* send confirm integer */
995 if (len < 2)
996 goto truncated;
997 pos += 2;
998
999 /*
1000 * At this stage we should already have an MLD station and actually SA
1001 * will be replaced with the MLD MAC address by the driver. However,
1002 * there is at least a theoretical race condition in a case where the
1003 * peer sends the SAE confirm message quickly enough for the driver
1004 * translation mechanism to not be available to update the SAE confirm
1005 * message addresses. Work around that by searching for the STA entry
1006 * using the link address of the non-AP MLD if no match is found based
1007 * on the MLD MAC address.
1008 */
1009 sta = ap_get_sta(hapd, mgmt->sa);
1010 if (!sta) {
1011 wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm");
1012 for (sta = hapd->sta_list; sta; sta = sta->next) {
1013 int link_id = hapd->mld_link_id;
1014
1015 if (!sta->mld_info.mld_sta ||
1016 sta->mld_info.links[link_id].valid ||
1017 !ether_addr_equal(
1018 mgmt->sa,
1019 sta->mld_info.links[link_id].peer_addr))
1020 continue;
1021 wpa_printf(MSG_DEBUG,
1022 "SAE: Found MLD STA for SAE confirm based on link address");
1023 break;
1024 }
1025 if (!sta)
1026 return NULL;
1027 }
1028
1029 if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) {
1030 if (sta->sae)
1031 wpa_printf(MSG_DEBUG, "SAE: Invalid state=%u",
1032 sta->sae->state);
1033 else
1034 wpa_printf(MSG_DEBUG, "SAE: No SAE context");
1035 return NULL;
1036 }
1037
1038 wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
1039 sta->sae->tmp->kck_len);
1040
1041 if (len - 2 < sta->sae->tmp->kck_len)
1042 goto truncated;
1043 pos += sta->sae->tmp->kck_len;
1044
1045 if (pos - mgmt->u.auth.variable > (int) len) {
1046 truncated:
1047 wpa_printf(MSG_DEBUG,
1048 "EHT: Too short SAE confirm Authentication frame");
1049 return NULL;
1050 }
1051
1052 return pos;
1053 }
1054
1055 #endif /* CONFIG_SAE */
1056
1057
auth_skip_fixed_fields(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)1058 static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
1059 const struct ieee80211_mgmt *mgmt,
1060 size_t len)
1061 {
1062 u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
1063 #if defined(CONFIG_SAE) || defined(CONFIG_IEEE8021X_AUTH)
1064 u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
1065 u16 status_code = le_to_host16(mgmt->u.auth.status_code);
1066 #endif /* CONFIG_SAE or CONFIG_IEEE8021X_AUTH */
1067 const u8 *pos = mgmt->u.auth.variable;
1068
1069 /* Skip fixed fields as based on IEEE Std 802.11-2024, Table 9-71
1070 * (Presence of fields and elements in Authentications frames) */
1071 switch (auth_alg) {
1072 case WLAN_AUTH_OPEN:
1073 case WLAN_AUTH_EPPKE:
1074 return pos;
1075 #ifdef CONFIG_IEEE8021X_AUTH
1076 case WLAN_AUTH_802_1X: {
1077 u16 encap_len;
1078
1079 /* Extract 2-octet Encapsulation Length from variable field */
1080 if (len < 2)
1081 return NULL;
1082
1083 encap_len = WPA_GET_LE16(pos);
1084 pos += 2;
1085 len -= 2;
1086
1087 /* Skip Encapsulation field if present */
1088 if (encap_len > 0) {
1089 if (len < encap_len)
1090 return NULL;
1091 pos += encap_len;
1092 }
1093
1094 return pos;
1095 }
1096 #endif /* CONFIG_IEEE8021X_AUTH */
1097 #ifdef CONFIG_SAE
1098 case WLAN_AUTH_SAE:
1099 if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
1100 if (status_code == WLAN_STATUS_SUCCESS) {
1101 wpa_printf(MSG_DEBUG,
1102 "EHT: SAE H2E is mandatory for MLD");
1103 goto out;
1104 }
1105
1106 return sae_commit_skip_fixed_fields(mgmt, len, pos,
1107 status_code);
1108 } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) {
1109 return sae_confirm_skip_fixed_fields(hapd, mgmt, len,
1110 pos, status_code);
1111 }
1112
1113 return pos;
1114 #endif /* CONFIG_SAE */
1115 /* TODO: Support additional algorithms that can be used for MLO */
1116 case WLAN_AUTH_FT:
1117 case WLAN_AUTH_FILS_SK:
1118 case WLAN_AUTH_FILS_SK_PFS:
1119 case WLAN_AUTH_FILS_PK:
1120 case WLAN_AUTH_PASN:
1121 default:
1122 break;
1123 }
1124
1125 #ifdef CONFIG_SAE
1126 out:
1127 #endif /* CONFIG_SAE */
1128 wpa_printf(MSG_DEBUG,
1129 "TODO: Authentication algorithm %u not supported with MLD",
1130 auth_alg);
1131 return NULL;
1132 }
1133
1134
hostapd_process_ml_auth(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)1135 const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
1136 const struct ieee80211_mgmt *mgmt,
1137 size_t len)
1138 {
1139 struct ieee802_11_elems elems;
1140 const u8 *pos;
1141
1142 if (!hapd->conf->mld_ap)
1143 return NULL;
1144
1145 len -= offsetof(struct ieee80211_mgmt, u.auth.variable);
1146
1147 pos = auth_skip_fixed_fields(hapd, mgmt, len);
1148 if (!pos)
1149 return NULL;
1150
1151 if (ieee802_11_parse_elems(pos,
1152 (int)len - (pos - mgmt->u.auth.variable),
1153 &elems, 0) == ParseFailed) {
1154 wpa_printf(MSG_DEBUG,
1155 "MLD: Failed parsing Authentication frame");
1156 }
1157
1158 if (!elems.basic_mle || !elems.basic_mle_len)
1159 return NULL;
1160
1161 return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
1162 }
1163
1164
hostapd_mld_validate_assoc_info(struct hostapd_data * hapd,struct sta_info * sta)1165 static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
1166 struct sta_info *sta)
1167 {
1168 u8 link_id;
1169 struct mld_info *info = &sta->mld_info;
1170
1171 if (!ap_sta_is_mld(hapd, sta)) {
1172 wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
1173 return 0;
1174 }
1175
1176 /*
1177 * Iterate over the links negotiated in the (Re)Association Request
1178 * frame and validate that they are indeed valid links in the local AP
1179 * MLD.
1180 *
1181 * While at it, also update the local address for the links in the
1182 * mld_info, so it could be easily available for later flows, e.g., for
1183 * the RSN Authenticator, etc.
1184 */
1185 for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
1186 struct hostapd_data *other_hapd;
1187
1188 if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
1189 continue;
1190
1191 other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
1192 if (!other_hapd) {
1193 wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
1194 link_id);
1195 return -1;
1196 }
1197
1198 os_memcpy(info->links[link_id].local_addr, other_hapd->own_addr,
1199 ETH_ALEN);
1200 }
1201
1202 return 0;
1203 }
1204
1205
hostapd_process_ml_assoc_req_addr(struct hostapd_data * hapd,const u8 * basic_mle,size_t basic_mle_len,u8 * mld_addr)1206 int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
1207 const u8 *basic_mle, size_t basic_mle_len,
1208 u8 *mld_addr)
1209 {
1210 struct wpabuf *mlbuf = ieee802_11_defrag(basic_mle, basic_mle_len,
1211 true);
1212 struct ieee80211_eht_ml *ml;
1213 struct eht_ml_basic_common_info *common_info;
1214 size_t ml_len, common_info_len;
1215 int ret = -1;
1216 u16 ml_control;
1217
1218 if (!mlbuf)
1219 return WLAN_STATUS_SUCCESS;
1220
1221 ml = (struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
1222 ml_len = wpabuf_len(mlbuf);
1223
1224 if (ml_len < sizeof(*ml))
1225 goto out;
1226
1227 ml_control = le_to_host16(ml->ml_control);
1228 if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
1229 MULTI_LINK_CONTROL_TYPE_BASIC) {
1230 wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
1231 ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
1232 goto out;
1233 }
1234
1235 /* Common Info Length and MLD MAC Address must always be present */
1236 common_info_len = 1 + ETH_ALEN;
1237 /* Ignore optional fields */
1238
1239 if (sizeof(*ml) + common_info_len > ml_len) {
1240 wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
1241 goto out;
1242 }
1243
1244 common_info = (struct eht_ml_basic_common_info *) ml->variable;
1245
1246 /* Common information length includes the length octet */
1247 if (common_info->len < common_info_len) {
1248 wpa_printf(MSG_DEBUG,
1249 "MLD: Invalid common info len=%u", common_info->len);
1250 goto out;
1251 }
1252
1253 /* Get the MLD MAC Address */
1254 os_memcpy(mld_addr, common_info->mld_addr, ETH_ALEN);
1255 ret = 0;
1256
1257 out:
1258 wpabuf_free(mlbuf);
1259 return ret;
1260 }
1261
1262
hostapd_process_ml_assoc_req(struct hostapd_data * hapd,struct ieee802_11_elems * elems,struct sta_info * sta)1263 u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
1264 struct ieee802_11_elems *elems,
1265 struct sta_info *sta)
1266 {
1267 struct wpabuf *mlbuf;
1268 const struct ieee80211_eht_ml *ml;
1269 const struct eht_ml_basic_common_info *common_info;
1270 size_t ml_len, common_info_len;
1271 struct mld_link_info *link_info;
1272 struct mld_info *info = &sta->mld_info;
1273 const u8 *pos, *end;
1274 int ret = -1;
1275 u16 ml_control;
1276 const u8 *ml_end;
1277 u8 saved_peer_addr[ETH_ALEN];
1278
1279 mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
1280 if (!mlbuf)
1281 return WLAN_STATUS_SUCCESS;
1282
1283 ml = wpabuf_head(mlbuf);
1284 ml_len = wpabuf_len(mlbuf);
1285 ml_end = ((const u8 *) ml) + ml_len;
1286
1287 ml_control = le_to_host16(ml->ml_control);
1288 if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
1289 MULTI_LINK_CONTROL_TYPE_BASIC) {
1290 wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
1291 ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
1292 goto out;
1293 }
1294
1295 /* Common Info length and MLD MAC address must always be present */
1296 common_info_len = 1 + ETH_ALEN;
1297
1298 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
1299 wpa_printf(MSG_DEBUG, "MLD: Link ID info not expected");
1300 goto out;
1301 }
1302
1303 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
1304 wpa_printf(MSG_DEBUG, "MLD: BSS params change not expected");
1305 goto out;
1306 }
1307
1308 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
1309 wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
1310 goto out;
1311 }
1312
1313 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
1314 common_info_len += 2;
1315 } else {
1316 wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
1317 }
1318
1319 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
1320 common_info_len += 2;
1321
1322 } else {
1323 wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
1324 goto out;
1325 }
1326
1327 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
1328 common_info_len += 2;
1329 } else {
1330 wpa_printf(MSG_DEBUG, "MLD: EXT ML capabilities not present");
1331 }
1332
1333 wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%zu",
1334 common_info_len);
1335
1336 if (sizeof(*ml) + common_info_len > ml_len) {
1337 wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
1338 goto out;
1339 }
1340
1341 common_info = (const struct eht_ml_basic_common_info *) ml->variable;
1342
1343 /* Common information length includes the length octet */
1344 if (common_info->len < common_info_len) {
1345 wpa_printf(MSG_DEBUG,
1346 "MLD: Invalid common info len=%u (expected %zu)",
1347 common_info->len, common_info_len);
1348 goto out;
1349 }
1350
1351 pos = common_info->variable;
1352 end = ((const u8 *) common_info) + common_info->len;
1353
1354 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
1355 info->common_info.eml_capa = WPA_GET_LE16(pos);
1356 pos += 2;
1357 } else {
1358 info->common_info.eml_capa = 0;
1359 }
1360
1361 info->common_info.mld_capa = WPA_GET_LE16(pos);
1362 pos += 2;
1363
1364 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
1365 pos += 2;
1366 }
1367
1368 wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
1369 MAC2STR(info->common_info.mld_addr),
1370 info->common_info.eml_capa, info->common_info.mld_capa);
1371
1372 /* Check the MLD MAC Address */
1373 if (!ether_addr_equal(info->common_info.mld_addr,
1374 common_info->mld_addr)) {
1375 wpa_printf(MSG_DEBUG,
1376 "MLD: MLD address mismatch between authentication ("
1377 MACSTR ") and association (" MACSTR ")",
1378 MAC2STR(info->common_info.mld_addr),
1379 MAC2STR(common_info->mld_addr));
1380 goto out;
1381 }
1382
1383 /* Reset all per-link state before parsing the new MLE. A STA may send
1384 * back-to-back Reassociation Request frames with different MLEs (e.g.,
1385 * fewer links or without NSTR). The valid-flag-only reset would leave
1386 * stale fields (nstr_bitmap_len, peer_addr, nstr_bitmap, capability) in
1387 * non-association link slots and leak resp_sta_profile heap
1388 * allocations. Free those allocations and zero all link slots. The
1389 * association link's peer_addr holds the link-specific MAC (set from
1390 * mgmt->sa at auth time, before driver address translation) and must be
1391 * saved and restored.
1392 */
1393 os_memcpy(saved_peer_addr, info->links[hapd->mld_link_id].peer_addr,
1394 ETH_ALEN);
1395 ap_sta_free_sta_profile(info);
1396 os_memset(info->links, 0, sizeof(info->links));
1397 info->links[hapd->mld_link_id].valid = 1;
1398 os_memcpy(info->links[hapd->mld_link_id].local_addr,
1399 hapd->own_addr, ETH_ALEN);
1400 os_memcpy(info->links[hapd->mld_link_id].peer_addr,
1401 saved_peer_addr, ETH_ALEN);
1402
1403 /* Parse the Link Info field that starts after the end of the variable
1404 * length Common Info field. */
1405 pos = end;
1406 while (ml_end - pos > 2) {
1407 size_t sub_elem_len, sta_info_len;
1408 u16 control;
1409 const u8 *sub_elem_end;
1410 int num_frag_subelems;
1411 u8 link_id;
1412
1413 num_frag_subelems =
1414 ieee802_11_defrag_mle_subelem(mlbuf, pos,
1415 &sub_elem_len);
1416 if (num_frag_subelems < 0) {
1417 wpa_printf(MSG_DEBUG,
1418 "MLD: Failed to parse MLE subelem");
1419 goto out;
1420 }
1421
1422 if ((size_t) num_frag_subelems * 2 > ml_len)
1423 goto out;
1424 ml_len -= num_frag_subelems * 2;
1425 ml_end = ((const u8 *) ml) + ml_len;
1426
1427 wpa_printf(MSG_DEBUG,
1428 "MLD: sub element len=%zu, Fragment subelems=%u",
1429 sub_elem_len, num_frag_subelems);
1430
1431 if (2 + sub_elem_len > (size_t) (ml_end - pos)) {
1432 wpa_printf(MSG_DEBUG,
1433 "MLD: Invalid link info len: %zu %zu",
1434 2 + sub_elem_len, ml_end - pos);
1435 goto out;
1436 }
1437
1438 if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
1439 wpa_printf(MSG_DEBUG,
1440 "MLD: Skip vendor specific subelement");
1441
1442 pos += 2 + sub_elem_len;
1443 continue;
1444 }
1445
1446 if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
1447 wpa_printf(MSG_DEBUG,
1448 "MLD: Skip unknown Multi-Link element subelement ID=%u",
1449 *pos);
1450 pos += 2 + sub_elem_len;
1451 continue;
1452 }
1453
1454 /* Skip the subelement ID and the length */
1455 pos += 2;
1456 sub_elem_end = pos + sub_elem_len;
1457
1458 /* Get the station control field */
1459 if (sub_elem_end - pos < 2) {
1460 wpa_printf(MSG_DEBUG,
1461 "MLD: Too short Per-STA Profile subelement");
1462 goto out;
1463 }
1464 control = WPA_GET_LE16(pos);
1465 link_id = control & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
1466 if (link_id >= MAX_NUM_MLD_LINKS) {
1467 wpa_printf(MSG_DEBUG,
1468 "MLD: Invalid Link ID in Per-STA Profile subelement");
1469 goto out;
1470 }
1471 link_info = &info->links[link_id];
1472 pos += 2;
1473
1474 if (!(control & BASIC_MLE_STA_CTRL_COMPLETE_PROFILE)) {
1475 wpa_printf(MSG_DEBUG,
1476 "MLD: Per-STA complete profile expected");
1477 goto out;
1478 }
1479
1480 if (!(control & BASIC_MLE_STA_CTRL_PRES_STA_MAC)) {
1481 wpa_printf(MSG_DEBUG,
1482 "MLD: Per-STA MAC address not present");
1483 goto out;
1484 }
1485
1486 if ((control & (BASIC_MLE_STA_CTRL_PRES_BEACON_INT |
1487 BASIC_MLE_STA_CTRL_PRES_DTIM_INFO))) {
1488 wpa_printf(MSG_DEBUG,
1489 "MLD: Beacon/DTIM interval not expected");
1490 goto out;
1491 }
1492
1493 /* The length octet and the MAC address must be present */
1494 sta_info_len = 1 + ETH_ALEN;
1495
1496 if (control & BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR) {
1497 if (control & BASIC_MLE_STA_CTRL_NSTR_BITMAP)
1498 link_info->nstr_bitmap_len = 2;
1499 else
1500 link_info->nstr_bitmap_len = 1;
1501 }
1502
1503 sta_info_len += link_info->nstr_bitmap_len;
1504
1505 if (sta_info_len > (size_t) (sub_elem_end - pos) ||
1506 sta_info_len > *pos ||
1507 *pos > sub_elem_end - pos ||
1508 sta_info_len > (size_t) (sub_elem_end - pos)) {
1509 wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
1510 goto out;
1511 }
1512
1513 sta_info_len = *pos;
1514 end = pos + sta_info_len;
1515
1516 /* skip the length */
1517 pos++;
1518
1519 /* get the link address */
1520 os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
1521 wpa_printf(MSG_DEBUG,
1522 "MLD: assoc: link id=%u, addr=" MACSTR,
1523 control & BASIC_MLE_STA_CTRL_LINK_ID_MASK,
1524 MAC2STR(link_info->peer_addr));
1525
1526 pos += ETH_ALEN;
1527
1528 /* Get the NSTR bitmap */
1529 if (link_info->nstr_bitmap_len) {
1530 os_memcpy(link_info->nstr_bitmap, pos,
1531 link_info->nstr_bitmap_len);
1532 pos += link_info->nstr_bitmap_len;
1533 }
1534
1535 pos = end;
1536
1537 if (sub_elem_end - pos >= 2)
1538 link_info->capability = WPA_GET_LE16(pos);
1539
1540 pos = sub_elem_end;
1541
1542 wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
1543 ", nstr bitmap len=%u",
1544 control, MAC2STR(link_info->peer_addr),
1545 link_info->nstr_bitmap_len);
1546
1547 link_info->valid = true;
1548 }
1549
1550 ret = hostapd_mld_validate_assoc_info(hapd, sta);
1551 out:
1552 wpabuf_free(mlbuf);
1553 if (ret) {
1554 ap_sta_free_sta_profile(info);
1555 os_memset(info, 0, sizeof(*info));
1556 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1557 }
1558
1559 return WLAN_STATUS_SUCCESS;
1560 }
1561
1562
ml_deinit_link_reconf_req(struct link_reconf_req_list ** req_list_ptr)1563 void ml_deinit_link_reconf_req(struct link_reconf_req_list **req_list_ptr)
1564 {
1565 struct link_reconf_req_list *req_list;
1566 struct link_reconf_req_info *info, *tmp;
1567
1568 if (!(*req_list_ptr))
1569 return;
1570
1571 wpa_printf(MSG_DEBUG, "MLD: Deinit Link Reconf Request context");
1572
1573 req_list = *req_list_ptr;
1574
1575 dl_list_for_each_safe(info, tmp, &req_list->add_req,
1576 struct link_reconf_req_info, list) {
1577 dl_list_del(&info->list);
1578 os_free(info);
1579 }
1580
1581 dl_list_for_each_safe(info, tmp, &req_list->del_req,
1582 struct link_reconf_req_info, list) {
1583 dl_list_del(&info->list);
1584 os_free(info);
1585 }
1586
1587 os_free(req_list);
1588 *req_list_ptr = NULL;
1589 }
1590
1591
hostapd_link_reconf_resp_tx_status(struct hostapd_data * hapd,struct sta_info * sta,const struct ieee80211_mgmt * mgmt,size_t len,int ok)1592 void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd,
1593 struct sta_info *sta,
1594 const struct ieee80211_mgmt *mgmt,
1595 size_t len, int ok)
1596 {
1597 u8 dialog_token = mgmt->u.action.u.link_reconf_resp.dialog_token;
1598 struct hostapd_data *assoc_hapd, *lhapd, *other_hapd;
1599 struct sta_info *assoc_sta, *lsta, *other_sta;
1600 struct link_reconf_req_list *req_list;
1601 struct link_reconf_req_info *info;
1602 uint8_t link_id;
1603
1604 wpa_printf(MSG_DEBUG,
1605 "MLD: Link Reconf Response TX status - dialog token=%u ok=%d",
1606 dialog_token, ok);
1607
1608 assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
1609 if (!assoc_sta) {
1610 wpa_printf(MSG_INFO, "MLD: Assoc STA not found for " MACSTR,
1611 MAC2STR(mgmt->da));
1612 return;
1613 }
1614
1615 if (!assoc_sta->reconf_req) {
1616 wpa_printf(MSG_DEBUG,
1617 "MLD: Unexpected Link Reconf Request TX status");
1618 return;
1619 }
1620
1621 req_list = assoc_sta->reconf_req;
1622
1623 if (!ether_addr_equal(mgmt->da, req_list->sta_mld_addr)) {
1624 wpa_printf(MSG_DEBUG,
1625 "MLD: Link Reconfiguration Response TX status from wrong STA");
1626 return;
1627 }
1628
1629 if (dialog_token != req_list->dialog_token) {
1630 wpa_printf(MSG_DEBUG,
1631 "MLD: Link Reconfiguration session expired for %u",
1632 dialog_token);
1633 return;
1634 }
1635
1636 if (!ok) {
1637 wpa_printf(MSG_INFO,
1638 "MLD: Link Reconf Response ack failed for " MACSTR
1639 "; revert link additions",
1640 MAC2STR(mgmt->da));
1641
1642 dl_list_for_each(info, &req_list->add_req,
1643 struct link_reconf_req_info, list) {
1644 if (info->status != WLAN_STATUS_SUCCESS)
1645 continue;
1646
1647 lhapd = NULL;
1648 lsta = NULL;
1649 lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
1650 if (lhapd)
1651 lsta = ap_get_sta(lhapd,
1652 req_list->sta_mld_addr);
1653
1654 if (lsta)
1655 ap_free_sta(lhapd, lsta);
1656 }
1657 goto exit;
1658 }
1659
1660 if (dl_list_empty(&req_list->del_req))
1661 goto exit;
1662
1663 dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
1664 list) {
1665 if (info->status != WLAN_STATUS_SUCCESS)
1666 continue;
1667
1668 link_id = info->link_id;
1669 lhapd = hostapd_mld_get_link_bss(hapd, link_id);
1670 if (!lhapd) {
1671 wpa_printf(MSG_INFO,
1672 "MLD: Link (%u) hapd cannot be NULL",
1673 link_id);
1674 continue;
1675 }
1676
1677 lsta = ap_get_sta(lhapd, mgmt->da);
1678 if (!lsta) {
1679 wpa_printf(MSG_INFO,
1680 "MLD: Link (%u) STA cannot be NULL",
1681 link_id);
1682 continue;
1683 }
1684
1685 /* Reassign assoc_sta to the link with lowest link ID */
1686 if (!hostapd_sta_is_link_sta(lhapd, lsta) &&
1687 lsta == assoc_sta) {
1688 struct mld_info *mld_info = &assoc_sta->mld_info;
1689 int i;
1690
1691 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
1692 if (i == assoc_sta->mld_assoc_link_id ||
1693 !mld_info->links[i].valid ||
1694 req_list->links_del_ok & BIT(i)) {
1695 continue;
1696 }
1697 break;
1698 }
1699
1700 if (i == MAX_NUM_MLD_LINKS) {
1701 wpa_printf(MSG_INFO,
1702 "MLD: No new assoc STA could be found; disconnect STA");
1703 hostapd_notif_disassoc_mld(assoc_hapd,
1704 assoc_sta,
1705 assoc_sta->addr);
1706 return;
1707 }
1708 wpa_printf(MSG_DEBUG, "MLD: New assoc link=%d", i);
1709
1710 /* Reset wpa_auth and assoc link ID */
1711 for_each_mld_link(other_hapd, lhapd) {
1712 other_sta = ap_get_sta(other_hapd, mgmt->da);
1713 if (other_sta)
1714 other_sta->mld_assoc_link_id = i;
1715 }
1716
1717 /* Reset reconfig request queue which will be freed
1718 * at the end */
1719 assoc_sta->reconf_req = NULL;
1720
1721 /* assoc_sta switched */
1722 assoc_sta = hostapd_ml_get_assoc_sta(lhapd, lsta,
1723 &assoc_hapd);
1724
1725 /* assoc_sta cannot be NULL since both AP and STA are
1726 * MLD and new valid assoc_sta is already found */
1727 if (!assoc_sta)
1728 goto exit;
1729
1730 if (assoc_hapd == lhapd) {
1731 wpa_printf(MSG_ERROR,
1732 "MLD: assoc_hapd is not updated; please check");
1733 goto exit;
1734 }
1735
1736 assoc_sta->reconf_req = req_list;
1737 wpa_reset_assoc_sm_info(assoc_sta->wpa_sm,
1738 assoc_hapd->wpa_auth, i);
1739 }
1740
1741 /* Free as a link STA */
1742 wpa_msg(hapd->msg_ctx, MSG_INFO,
1743 WPA_EVENT_LINK_STA_REMOVED "sta=" MACSTR " link_id=%u",
1744 MAC2STR(lsta->addr), link_id);
1745 ap_free_sta(lhapd, lsta);
1746
1747 for_each_mld_link(other_hapd, lhapd) {
1748 struct mld_link_info *link;
1749
1750 other_sta = ap_get_sta(other_hapd, mgmt->da);
1751 if (!other_sta)
1752 continue;
1753
1754 link = &other_sta->mld_info.links[link_id];
1755 os_free(link->resp_sta_profile);
1756 link->resp_sta_profile = NULL;
1757 link->resp_sta_profile_len = 0;
1758 link->valid = false;
1759 }
1760 wpa_auth_set_ml_info(assoc_sta->wpa_sm,
1761 assoc_sta->mld_assoc_link_id,
1762 &assoc_sta->mld_info);
1763 }
1764
1765 exit:
1766 ml_deinit_link_reconf_req(&req_list);
1767 if (assoc_sta && assoc_sta->reconf_req)
1768 assoc_sta->reconf_req = NULL;
1769 }
1770
1771
recover_from_zero_links(u16 * links_del_ok,u8 * recovery_link)1772 static bool recover_from_zero_links(u16 *links_del_ok, u8 *recovery_link)
1773 {
1774 u8 pos = 0;
1775 u16 del_links;
1776
1777 del_links = *links_del_ok;
1778
1779 while (del_links) {
1780 if (del_links & 1)
1781 break;
1782 del_links >>= 1;
1783 pos++;
1784 }
1785
1786 /* No link found */
1787 if (!del_links) {
1788 wpa_printf(MSG_DEBUG,
1789 "MLD: Total valid links is 0 and no del-link found to reject for recovery");
1790 return false;
1791 }
1792
1793 *recovery_link = pos;
1794 *links_del_ok &= ~BIT(*recovery_link);
1795 wpa_printf(MSG_INFO,
1796 "MLD: Del-link request for link (%u) rejected to recover from no remaining links",
1797 *recovery_link);
1798 return true;
1799 }
1800
1801
1802 static u16
hostapd_ml_process_reconf_link(struct hostapd_data * hapd,struct sta_info * assoc_sta,const u8 * ies,size_t ies_len,u8 link_id,const u8 * link_addr)1803 hostapd_ml_process_reconf_link(struct hostapd_data *hapd,
1804 struct sta_info *assoc_sta, const u8 *ies,
1805 size_t ies_len, u8 link_id, const u8 *link_addr)
1806 {
1807 struct hostapd_data *lhapd, *other_hapd;
1808 struct mld_link_info link;
1809 struct sta_info *lsta, *other_sta;
1810
1811 lhapd = hostapd_mld_get_link_bss(hapd, link_id);
1812 if (!lhapd) /* This cannot be NULL */
1813 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1814
1815 os_memset(&link, 0, sizeof(link));
1816
1817 link.valid = 1;
1818 os_memcpy(link.local_addr, lhapd->own_addr, ETH_ALEN);
1819 os_memcpy(link.peer_addr, link_addr, ETH_ALEN);
1820
1821 /* Parse STA profile, check the IEs, and send ADD_LINK_STA */
1822 ieee80211_ml_process_link(lhapd, assoc_sta, &link, ies, ies_len,
1823 LINK_PARSE_RECONF, false, NULL);
1824 if (link.status != WLAN_STATUS_SUCCESS)
1825 return link.status;
1826
1827 lsta = ap_get_sta(lhapd, assoc_sta->addr);
1828 if (!lsta)
1829 return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
1830
1831 for_each_mld_link(other_hapd, lhapd) {
1832 struct mld_link_info *_link;
1833
1834 other_sta = ap_get_sta(other_hapd, lsta->addr);
1835 if (!other_sta)
1836 continue;
1837
1838 _link = &other_sta->mld_info.links[link_id];
1839 _link->valid = true;
1840 _link->status = WLAN_STATUS_SUCCESS;
1841 os_memcpy(_link->local_addr, other_hapd->own_addr, ETH_ALEN);
1842 os_memcpy(_link->peer_addr, link_addr, ETH_ALEN);
1843 }
1844 wpa_auth_set_ml_info(lsta->wpa_sm, lsta->mld_assoc_link_id,
1845 &lsta->mld_info);
1846
1847 return WLAN_STATUS_SUCCESS;
1848 }
1849
1850
1851 static int
hostapd_reject_all_reconf_req(struct hostapd_data * hapd,u8 * pos,struct link_reconf_req_list * req_list)1852 hostapd_reject_all_reconf_req(struct hostapd_data *hapd, u8 *pos,
1853 struct link_reconf_req_list *req_list)
1854 {
1855 struct link_reconf_req_info *info;
1856 struct hostapd_data *lhapd;
1857 struct sta_info *lsta;
1858 u16 status;
1859 u8 *buf = pos;
1860
1861 dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info,
1862 list) {
1863 lhapd = NULL;
1864 lsta = NULL;
1865 *pos++ = info->link_id;
1866 status = info->status != WLAN_STATUS_SUCCESS ? info->status :
1867 WLAN_STATUS_UNSPECIFIED_FAILURE;
1868 WPA_PUT_LE16(pos, status);
1869
1870 if (info->status == WLAN_STATUS_SUCCESS) {
1871 lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
1872 if (lhapd)
1873 lsta = ap_get_sta(lhapd,
1874 req_list->sta_mld_addr);
1875
1876 if (lsta)
1877 ap_free_sta(lhapd, lsta);
1878
1879 info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
1880 }
1881 wpa_printf(MSG_DEBUG, "MLD: Reject add-link=%u with status=%u",
1882 info->link_id, status);
1883 pos += 2;
1884 }
1885
1886 dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
1887 list) {
1888 *pos++ = info->link_id;
1889 status = info->status != WLAN_STATUS_SUCCESS ? info->status :
1890 WLAN_STATUS_UNSPECIFIED_FAILURE;
1891 WPA_PUT_LE16(pos, status);
1892
1893 if (info->status == WLAN_STATUS_SUCCESS)
1894 info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
1895
1896 wpa_printf(MSG_DEBUG, "MLD: Reject del-link=%u with status=%u",
1897 info->link_id, status);
1898 pos += 2;
1899 }
1900
1901 return pos - buf;
1902 }
1903
1904
1905 static int
hostapd_send_link_reconf_resp(struct hostapd_data * hapd,struct sta_info * assoc_sta,struct link_reconf_req_list * req_list)1906 hostapd_send_link_reconf_resp(struct hostapd_data *hapd,
1907 struct sta_info *assoc_sta,
1908 struct link_reconf_req_list *req_list)
1909 {
1910 u8 *buf, *orig_pos, *pos;
1911 struct ieee80211_mgmt *mgmt;
1912 struct link_reconf_req_info *info;
1913 struct mld_info mld;
1914 int ret;
1915 unsigned int count;
1916 u8 dialog_token;
1917 bool reject_all = false;
1918 size_t len, pos_len, kde_len, mle_len;
1919
1920 count = dl_list_len(&req_list->add_req) +
1921 dl_list_len(&req_list->del_req);
1922 if (!count)
1923 return 0;
1924
1925 os_memset(&mld, 0, sizeof(mld));
1926
1927 dialog_token = req_list->dialog_token;
1928
1929 /*
1930 * Link Reconfiguration Response:
1931 *
1932 * IEEE80211 Header (24B) +
1933 * Category (1B) + Action code (1B) + Dialog Token (1B) +
1934 * Count (1B) + Status list (count * 3B) +
1935 * Optional: Group Key Data field (variable) +
1936 * Optional: OCI element (6B) +
1937 * Optional: Basic Multi-Link element (variable)
1938 */
1939 len = IEEE80211_HDRLEN + 3 + 1 + count * 3;
1940 kde_len = mle_len = 0;
1941
1942 if (req_list->links_add_ok) {
1943 kde_len = wpa_auth_ml_group_kdes_len(
1944 assoc_sta->wpa_sm, req_list->links_add_ok) + 1;
1945 len += kde_len;
1946
1947 #ifdef CONFIG_OCV
1948 if (wpa_auth_uses_ocv(assoc_sta->wpa_sm))
1949 len += OCV_OCI_EXTENDED_LEN;
1950 #endif /* CONFIG_OCV */
1951
1952 mld.mld_sta = true;
1953 dl_list_for_each(info, &req_list->add_req,
1954 struct link_reconf_req_info, list) {
1955 struct mld_link_info *link = &mld.links[info->link_id];
1956 struct hostapd_data *lhapd = NULL;
1957
1958 if (info->status != WLAN_STATUS_SUCCESS)
1959 continue;
1960
1961 link->status = info->status;
1962
1963 lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
1964 if (!lhapd)
1965 continue;
1966
1967 link->valid = true;
1968 ieee80211_ml_build_assoc_resp(lhapd, link);
1969 }
1970 /* TODO: Basic MLE is not supposed to include BPCC in Link
1971 * Reconfiguration Response, but mac80211 implementation for
1972 * processing this frame requires that to be present. For now,
1973 * include that subfield as a workaround. This should be removed
1974 * once mac80211 is fixed to match the standard (or this comment
1975 * be removed if the standard is modified to match
1976 * implementation). */
1977 mle_len = hostapd_eid_eht_ml_len(&mld, false, true);
1978 len += mle_len;
1979 }
1980
1981 buf = os_zalloc(len);
1982 if (!buf) {
1983 wpa_printf(MSG_INFO,
1984 "MLD: Failed to allocate Link Reconf Response buffer (%zu bytes)",
1985 len);
1986 return -1;
1987 }
1988
1989 mgmt = (struct ieee80211_mgmt *) buf;
1990 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
1991 WLAN_FC_STYPE_ACTION);
1992 os_memcpy(mgmt->da, assoc_sta->addr, ETH_ALEN);
1993 os_memcpy(mgmt->sa, hapd->mld->mld_addr, ETH_ALEN);
1994 os_memcpy(mgmt->bssid, hapd->mld->mld_addr, ETH_ALEN);
1995
1996 mgmt->u.action.category = WLAN_ACTION_PROTECTED_EHT;
1997 mgmt->u.action.u.link_reconf_resp.action =
1998 WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE;
1999 mgmt->u.action.u.link_reconf_resp.dialog_token = dialog_token;
2000 mgmt->u.action.u.link_reconf_resp.count = count;
2001
2002 orig_pos = pos = mgmt->u.action.u.link_reconf_resp.variable;
2003 pos_len = 28; /* IEEE80211 Header, category, code, token, count */
2004
2005 dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info,
2006 list) {
2007 *pos++ = info->link_id;
2008 WPA_PUT_LE16(pos, info->status);
2009 pos += 2;
2010 pos_len += 3;
2011 }
2012
2013 dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
2014 list) {
2015 /* Mark the status as INVALID for rejected link to recover */
2016 if (!(req_list->links_del_ok & BIT(info->link_id)) &&
2017 info->status == WLAN_STATUS_SUCCESS)
2018 info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
2019
2020 *pos++ = info->link_id;
2021 WPA_PUT_LE16(pos, info->status);
2022 pos += 2;
2023 pos_len += 3;
2024 }
2025
2026 if (!req_list->links_add_ok)
2027 goto send_resp;
2028
2029 /* Key Data for add links */
2030 if (kde_len) {
2031 u8 *kde_pos = pos;
2032
2033 kde_pos = wpa_auth_ml_group_kdes(assoc_sta->wpa_sm, ++kde_pos,
2034 req_list->links_add_ok);
2035 *pos = kde_pos - pos - 1;
2036 if (kde_len - 1 != *pos) {
2037 reject_all = true;
2038 goto reject_all_req;
2039 }
2040
2041 wpa_hexdump_key(MSG_DEBUG, "MLD: Group KDE", pos + 1, *pos);
2042
2043 pos += kde_len;
2044 pos_len += kde_len;
2045 }
2046
2047 #ifdef CONFIG_OCV
2048 /* OCI element for add links */
2049 if (wpa_auth_uses_ocv(assoc_sta->wpa_sm)) {
2050 struct wpa_channel_info ci;
2051
2052 if (hostapd_drv_channel_info(hapd, &ci)) {
2053 wpa_printf(MSG_DEBUG,
2054 "MLD: Failed to fetch OCI; reject all requests");
2055 reject_all = true;
2056 goto reject_all_req;
2057 }
2058
2059 if (ocv_insert_extended_oci(&ci, pos)) {
2060 wpa_printf(MSG_DEBUG,
2061 "MLD: Failed to add OCI element; reject all requests");
2062 reject_all = true;
2063 goto reject_all_req;
2064 }
2065
2066 pos += OCV_OCI_EXTENDED_LEN;
2067 pos_len += OCV_OCI_EXTENDED_LEN;
2068 }
2069 #endif /* CONFIG_OCV */
2070
2071 /* Basic Multi-Link element for add links */
2072 if (mle_len) {
2073 u8 *mle_pos = pos;
2074
2075 /* TODO: Basic MLE is not supposed to include BPCC in Link
2076 * Reconfiguration Response, but mac80211 implementation for
2077 * processing this frame requires that to be present. For now,
2078 * include that subfield as a workaround. This should be removed
2079 * once mac80211 is fixed to match the standard (or this comment
2080 * be removed if the standard is modified to match
2081 * implementation). */
2082 mle_pos = hostapd_eid_eht_basic_ml_common(hapd, mle_pos, &mld,
2083 false, true);
2084 if ((size_t) (mle_pos - pos) != mle_len) {
2085 wpa_printf(MSG_DEBUG,
2086 "MLD: Unexpected MLE length: %td != %zu",
2087 mle_pos - pos, mle_len);
2088 reject_all = true;
2089 goto reject_all_req;
2090 }
2091
2092 pos += mle_len;
2093 pos_len += mle_len;
2094 }
2095
2096 reject_all_req:
2097 if (reject_all) {
2098 pos = orig_pos;
2099 pos_len = 28; /* reset pos_len */
2100 pos += hostapd_reject_all_reconf_req(hapd, orig_pos, req_list);
2101 pos_len += pos - orig_pos;
2102
2103 req_list->links_add_ok = req_list->links_del_ok = 0;
2104 req_list->new_valid_links = 0;
2105 }
2106
2107 send_resp:
2108 ret = hostapd_drv_send_mlme(hapd, mgmt, pos_len, 0, NULL, 0, 0);
2109 os_free(buf);
2110
2111 if (mld.mld_sta)
2112 ap_sta_free_sta_profile(&mld);
2113
2114 return ret;
2115 }
2116
2117
2118 static int
hostapd_ml_check_sta_entry_by_link_addr_iter(struct hostapd_data * hapd,struct sta_info * sta,void * ctx)2119 hostapd_ml_check_sta_entry_by_link_addr_iter(struct hostapd_data *hapd,
2120 struct sta_info *sta, void *ctx)
2121 {
2122 const u8 *link_addr = ctx;
2123 struct mld_link_info li;
2124
2125 if (!link_addr)
2126 return 0;
2127
2128 if (sta->mld_info.mld_sta) {
2129 li = sta->mld_info.links[hapd->mld_link_id];
2130 if (!li.valid || !ether_addr_equal(li.peer_addr, link_addr))
2131 return 0;
2132
2133 wpa_printf(MSG_DEBUG, "MLD: STA with address " MACSTR
2134 " exists for AP (link_id=%u) as a non-AP STA affiliated with non-AP MLD "
2135 MACSTR, MAC2STR(link_addr),
2136 hapd->mld_link_id, MAC2STR(sta->addr));
2137 return 1;
2138 }
2139
2140 if (ether_addr_equal(sta->addr, link_addr)) {
2141 wpa_printf(MSG_DEBUG, "MLD: STA with address " MACSTR
2142 " exists for AP (link_id=%u) as a legacy STA",
2143 MAC2STR(link_addr), hapd->mld_link_id);
2144 return 1;
2145 }
2146
2147 return 0;
2148 }
2149
2150
2151 /* Returns:
2152 * 0 = successful parsing
2153 * 1 = per-STA profile (subelement) skipped or rejected
2154 * -1 = fail due to fatal errors
2155 */
2156 static int
hostapd_parse_link_reconf_req_sta_profile(struct hostapd_data * hapd,struct link_reconf_req_list * req,const u8 * buf,size_t len)2157 hostapd_parse_link_reconf_req_sta_profile(struct hostapd_data *hapd,
2158 struct link_reconf_req_list *req,
2159 const u8 *buf, size_t len)
2160 {
2161 struct link_reconf_req_info *info = NULL;
2162 const struct ieee80211_eht_per_sta_profile *per_sta_prof;
2163 const struct element *elem;
2164 struct hostapd_data *lhapd = NULL;
2165 struct sta_info *lsta;
2166 size_t sta_info_len, sta_prof_len = 0;
2167 u16 sta_control, reconf_type_mask;
2168 u8 link_id, reconf_type;
2169 const u8 *sta_info = NULL, *end;
2170 u8 sta_addr[ETH_ALEN];
2171 size_t nstr_bitmap_size = 0;
2172 int ret = -1;
2173 u16 status;
2174
2175 wpa_hexdump(MSG_MSGDUMP, "MLD: Link Reconfig Request subelement",
2176 buf, len);
2177
2178 if (len < sizeof(*elem) + 2UL)
2179 goto out;
2180
2181 elem = (const struct element *) buf;
2182 end = buf + len;
2183
2184 os_memset(sta_addr, 0, ETH_ALEN);
2185
2186 if (elem->id != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
2187 wpa_printf(MSG_DEBUG, "MLD: Unexpected subelement (%u) found",
2188 elem->id);
2189 ret = 1; /* skip this subelement */
2190 goto out;
2191 }
2192
2193 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
2194
2195 per_sta_prof = (const struct ieee80211_eht_per_sta_profile *)
2196 elem->data;
2197 sta_control = le_to_host16(per_sta_prof->sta_control);
2198 sta_info_len = 1;
2199
2200 link_id = sta_control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
2201 wpa_printf(MSG_DEBUG, "MLD: Per-STA profile for link=%u", link_id);
2202 if (link_id >= MAX_NUM_MLD_LINKS)
2203 goto out;
2204
2205 reconf_type_mask =
2206 sta_control & EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK;
2207 reconf_type =
2208 EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_VAL(reconf_type_mask);
2209
2210 switch (reconf_type) {
2211 case EHT_RECONF_TYPE_ADD_LINK:
2212 case EHT_RECONF_TYPE_DELETE_LINK:
2213 break;
2214 default:
2215 wpa_printf(MSG_ERROR,
2216 "MLD: Unsupported Reconfiguration type %u",
2217 reconf_type);
2218 ret = 1; /* skip this per-STA profile */
2219 goto out;
2220 }
2221
2222 if (!(sta_control & EHT_PER_STA_RECONF_CTRL_MAC_ADDR)) {
2223 wpa_printf(MSG_DEBUG,
2224 "MLD: STA MAC address not set in STA control");
2225 ret = 1; /* reject this per-STA profile */
2226 goto add_to_list;
2227 }
2228 sta_info_len += ETH_ALEN;
2229
2230 if (sta_control & EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER) {
2231 wpa_printf(MSG_DEBUG,
2232 "MLD: AP removal timer set in STA control");
2233 sta_info_len += 2;
2234 }
2235
2236 if (sta_control & EHT_PER_STA_RECONF_CTRL_OP_PARAMS) {
2237 wpa_printf(MSG_DEBUG, "MLD: Op params set in STA control");
2238 sta_info_len += 3;
2239 }
2240
2241 if (!(sta_control & EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE)) {
2242 if (reconf_type == EHT_RECONF_TYPE_ADD_LINK) {
2243 wpa_printf(MSG_DEBUG,
2244 "MLD: Complete profile not set in STA control");
2245 ret = 1; /* reject this per-STA profile */
2246 goto add_to_list;
2247 }
2248 } else {
2249 if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK)
2250 wpa_printf(MSG_DEBUG,
2251 "MLD: Complete profile set in STA control");
2252 }
2253
2254 if (sta_control & EHT_PER_STA_RECONF_CTRL_NSTR_INDICATION) {
2255 nstr_bitmap_size = 1;
2256 if (sta_control &
2257 EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE)
2258 nstr_bitmap_size = 2;
2259
2260 if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK)
2261 wpa_printf(MSG_DEBUG,
2262 "MLD: NSTR Indication set in STA control");
2263 }
2264 sta_info_len += nstr_bitmap_size;
2265
2266 sta_info = per_sta_prof->variable;
2267 if (*sta_info > end - sta_info) {
2268 wpa_printf(MSG_DEBUG, "MLD: Not enough room for STA Info");
2269 goto out;
2270 }
2271
2272 if (*sta_info < sta_info_len) {
2273 wpa_printf(MSG_DEBUG,
2274 "MLD: Invalid Reconf STA Info len (%u); min expected=%zu",
2275 *sta_info, sta_info_len);
2276 goto out;
2277 }
2278
2279 sta_info_len = *sta_info;
2280 wpa_hexdump(MSG_MSGDUMP, "MLD: STA Info", sta_info, sta_info_len);
2281
2282 os_memcpy(sta_addr, sta_info + 1, ETH_ALEN);
2283 wpa_printf(MSG_DEBUG, "MLD: Link STA addr=" MACSTR, MAC2STR(sta_addr));
2284
2285 sta_info += sta_info_len;
2286
2287 lhapd = hostapd_mld_get_link_bss(hapd, link_id);
2288 if (!lhapd) {
2289 wpa_printf(MSG_DEBUG, "MLD: No AP link found for link id=%u",
2290 link_id);
2291 ret = 1; /* reject this per-STA profile */
2292 goto add_to_list;
2293 }
2294
2295 lsta = ap_get_sta(lhapd, req->sta_mld_addr);
2296
2297 if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) {
2298 /* DELETE_LINK request shall not have STA profile */
2299 if (len != sizeof(sta_control) + sta_info_len)
2300 wpa_printf(MSG_DEBUG,
2301 "MLD: Delete link request has STA profile");
2302
2303 if (!lsta || !ap_sta_is_mld(lhapd, lsta) ||
2304 !lsta->mld_info.links[link_id].valid) {
2305 wpa_printf(MSG_DEBUG,
2306 "MLD: STA invalid for link id=%u peer addr="
2307 MACSTR, link_id, MAC2STR(sta_addr));
2308 ret = 1; /* reject this per-STA profile */
2309 goto add_to_list;
2310 }
2311
2312 if (!ether_addr_equal(lsta->mld_info.links[link_id].peer_addr,
2313 sta_addr)) {
2314 wpa_printf(MSG_DEBUG,
2315 "MLD: STA invalid for addr=" MACSTR,
2316 MAC2STR(sta_addr));
2317 ret = 1; /* reject this per-STA profile */
2318 goto add_to_list;
2319 }
2320
2321 status = WLAN_STATUS_SUCCESS;
2322 ret = 0;
2323 goto add_to_list;
2324 }
2325
2326 /* EHT_RECONF_TYPE_ADD_LINK */
2327 if (len < sizeof(sta_control) + sta_info_len + 2)
2328 goto out;
2329 sta_prof_len = len - sizeof(sta_control) - sta_info_len - 2;
2330 if (sta_prof_len > (size_t) (end - sta_info)) {
2331 wpa_printf(MSG_DEBUG, "MLD: STA Profile with excess length");
2332 goto out;
2333 }
2334
2335 if (lsta) {
2336 wpa_printf(MSG_DEBUG,
2337 "MLD: STA exists for link id=%u MLD addr=" MACSTR,
2338 link_id, MAC2STR(req->sta_mld_addr));
2339 ret = 1; /* reject this per-STA profile */
2340 goto add_to_list;
2341 }
2342
2343 /* Check if link address is already used by any connected legacy
2344 * non-AP STA or non-AP STA affiliated with a non-AP MLD.
2345 */
2346 if (ap_for_each_sta(lhapd, hostapd_ml_check_sta_entry_by_link_addr_iter,
2347 sta_addr)) {
2348 ret = 1; /* Reject this per-STA profile */
2349 goto add_to_list;
2350 }
2351
2352 status = WLAN_STATUS_SUCCESS; /* IE validations done later */
2353 ret = 0;
2354
2355 add_to_list:
2356 if (sta_info && sta_prof_len)
2357 wpa_hexdump(MSG_MSGDUMP, "MLD: STA Profile",
2358 sta_info, sta_prof_len);
2359
2360 info = os_zalloc(sizeof(struct link_reconf_req_info) + sta_prof_len);
2361 if (!info) {
2362 wpa_printf(MSG_DEBUG, "MLD: Failed to allocate request info");
2363 ret = 1; /* skip this per-STA profile */
2364 goto out;
2365 }
2366
2367 info->link_id = link_id;
2368 info->status = status;
2369 os_memcpy(info->peer_addr, sta_addr, ETH_ALEN);
2370 if (lhapd)
2371 os_memcpy(info->local_addr, lhapd->own_addr, ETH_ALEN);
2372
2373 if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) {
2374 dl_list_add_tail(&req->del_req, &info->list);
2375 } else if (sta_info) {
2376 os_memcpy(info->sta_prof, sta_info, sta_prof_len);
2377 info->sta_prof_len = sta_prof_len;
2378
2379 dl_list_add_tail(&req->add_req, &info->list);
2380 } else {
2381 os_free(info);
2382 }
2383 wpa_printf(MSG_INFO, "MLD: Link (%d) parsed to %s request; status=%u",
2384 link_id,
2385 reconf_type == EHT_RECONF_TYPE_DELETE_LINK ? "del" : "add",
2386 status);
2387
2388 out:
2389 if (ret < 0)
2390 wpa_printf(MSG_DEBUG,
2391 "MLD: Failed to parse reconf req STA profile");
2392 return ret;
2393 }
2394
2395
2396 static int
hostapd_parse_link_reconf_req_reconf_mle(struct hostapd_data * hapd,const u8 * mle,size_t mle_len,struct link_reconf_req_list ** req_list_ptr)2397 hostapd_parse_link_reconf_req_reconf_mle(
2398 struct hostapd_data *hapd, const u8 *mle, size_t mle_len,
2399 struct link_reconf_req_list **req_list_ptr)
2400 {
2401 struct link_reconf_req_list *req_list;
2402 struct wpabuf *mlbuf = NULL;
2403 struct sta_info *sta;
2404 const struct ieee80211_eht_ml *ml;
2405 const struct eht_ml_reconf_common_info *ml_common_info;
2406 size_t len, ml_common_len;
2407 u16 ml_control;
2408 const u8 *pos, *end;
2409 int ret = -1;
2410
2411 mlbuf = ieee802_11_defrag(mle, mle_len, true);
2412 if (!mlbuf) {
2413 wpa_printf(MSG_DEBUG,
2414 "MLD: Failed to defrag Reconfiguration MLE");
2415 goto fail;
2416 }
2417
2418 ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
2419 len = wpabuf_len(mlbuf);
2420 end = ((const u8 *) ml) + len;
2421
2422 wpa_hexdump(MSG_DEBUG, "MLD: Defragged Reconfiguration MLE",
2423 (const void *) ml, len);
2424
2425 if (len < sizeof(*ml) + ETH_ALEN + 1UL)
2426 goto fail;
2427
2428 ml_control = WPA_GET_LE16((const u8 *) ml) >> 4;
2429 ml_common_len = 1;
2430 if (!(ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR))
2431 goto fail;
2432 ml_common_len += ETH_ALEN;
2433
2434 if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_EML_CAPA)
2435 ml_common_len += 2;
2436
2437 if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_CAPA)
2438 ml_common_len += 2;
2439
2440 if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP)
2441 ml_common_len += 2;
2442
2443 ml_common_info =
2444 (const struct eht_ml_reconf_common_info *) ml->variable;
2445 if (len < sizeof(*ml) + ml_common_info->len) {
2446 wpa_printf(MSG_DEBUG,
2447 "MLD: Unexpected Reconfiguration ML element length (%zu < %zu)",
2448 len, sizeof(*ml) + ml_common_info->len);
2449 goto fail;
2450 }
2451
2452 if (ml_common_info->len < ml_common_len) {
2453 wpa_printf(MSG_DEBUG,
2454 "MLD: Invalid Reconf common info len (%u); min expected=%zu",
2455 ml_common_info->len, ml_common_len);
2456 goto fail;
2457 }
2458
2459 pos = (const u8 *) ml_common_info->variable;
2460
2461 sta = ap_get_sta(hapd, pos);
2462 if (!sta || !ap_sta_is_mld(hapd, sta)) {
2463 wpa_printf(MSG_DEBUG, "MLD: STA invalid%s for " MACSTR,
2464 sta ? "" : " (NULL)", MAC2STR(pos));
2465 goto fail;
2466 }
2467
2468 *req_list_ptr = os_zalloc(sizeof(struct link_reconf_req_list));
2469 if (!(*req_list_ptr)) {
2470 wpa_printf(MSG_ERROR, "MLD: Failed to allocate request list");
2471 goto fail;
2472 }
2473 req_list = *req_list_ptr;
2474
2475 os_memcpy(req_list->sta_mld_addr, pos, ETH_ALEN);
2476 dl_list_init(&req_list->del_req);
2477 dl_list_init(&req_list->add_req);
2478
2479 pos = ml->variable + ml_common_info->len;
2480
2481 while (end - pos > 2) {
2482 size_t sub_elem_len;
2483 int num_frag_subelems;
2484
2485 num_frag_subelems =
2486 ieee802_11_defrag_mle_subelem(mlbuf, pos,
2487 &sub_elem_len);
2488 if (num_frag_subelems < 0) {
2489 wpa_printf(MSG_DEBUG,
2490 "MLD: Failed to parse Reconfiguration MLE subelem");
2491 goto fail;
2492 }
2493
2494 if ((size_t) num_frag_subelems * 2 > len)
2495 goto fail;
2496 len -= num_frag_subelems * 2;
2497 end = ((const u8 *) ml) + len;
2498
2499 if (sub_elem_len + 2 > (size_t) (end - pos))
2500 goto fail;
2501
2502 if (hostapd_parse_link_reconf_req_sta_profile(
2503 hapd, req_list, pos, sub_elem_len + 2) < 0)
2504 goto fail;
2505
2506 pos += sub_elem_len + 2;
2507 }
2508
2509 ret = 0;
2510
2511 fail:
2512 if (ret)
2513 ml_deinit_link_reconf_req(req_list_ptr);
2514
2515 wpabuf_free(mlbuf);
2516 return ret;
2517 }
2518
2519
2520 static bool
hostapd_validate_link_reconf_req(struct hostapd_data * hapd,struct sta_info * sta,struct link_reconf_req_list * req_list)2521 hostapd_validate_link_reconf_req(struct hostapd_data *hapd,
2522 struct sta_info *sta,
2523 struct link_reconf_req_list *req_list)
2524 {
2525 struct hostapd_data *assoc_hapd, *lhapd;
2526 struct link_reconf_req_info *info;
2527 struct sta_info *assoc_sta, *lsta;
2528 struct mld_info *mld_info;
2529 u8 recovery_link;
2530 u16 valid_links = 0, links_add_ok = 0, links_del_ok = 0, status;
2531 size_t link_kde_len, total_kde_len = 0;
2532 int i;
2533
2534 assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
2535 if (!assoc_sta)
2536 return false;
2537
2538 if (dl_list_empty(&req_list->add_req) &&
2539 dl_list_empty(&req_list->del_req)) {
2540 wpa_printf(MSG_DEBUG, "MLD: No add or delete request found");
2541 return false;
2542 }
2543
2544 mld_info = &assoc_sta->mld_info;
2545 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
2546 if (mld_info->links[i].valid &&
2547 mld_info->links[i].status == WLAN_STATUS_SUCCESS)
2548 valid_links |= BIT(i);
2549 }
2550
2551 /* Check IEs for add-link STA profiles */
2552 dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info,
2553 list) {
2554 lhapd = NULL;
2555 lsta = NULL;
2556
2557 wpa_printf(MSG_DEBUG,
2558 "MLD: Add Link Reconf STA for link id=%u status=%u",
2559 info->link_id, info->status);
2560 if (info->status != WLAN_STATUS_SUCCESS ||
2561 info->sta_prof_len < 2)
2562 continue;
2563
2564 /* Offset 2 bytes for Capabilities in STA Profile */
2565 status = hostapd_ml_process_reconf_link(hapd, assoc_sta,
2566 info->sta_prof + 2,
2567 info->sta_prof_len - 2,
2568 info->link_id,
2569 info->peer_addr);
2570 if (status != WLAN_STATUS_SUCCESS) {
2571 wpa_printf(MSG_DEBUG,
2572 "MLD: Add link IE validation failed for link=%u",
2573 info->link_id);
2574 info->status = status;
2575 continue;
2576 }
2577
2578 link_kde_len = wpa_auth_ml_group_kdes_len(assoc_sta->wpa_sm,
2579 BIT(info->link_id));
2580
2581 /* Since Group KDE element Length subfield is one byte,
2582 * accept as many add-link requests as can be fit.
2583 */
2584 if (total_kde_len + link_kde_len >
2585 LINK_RECONF_GROUP_KDE_MAX_LEN) {
2586 wpa_printf(MSG_INFO,
2587 "MLD: Group KDEs cannot fit (%zu > %u) for link=%u",
2588 total_kde_len + link_kde_len,
2589 LINK_RECONF_GROUP_KDE_MAX_LEN,
2590 info->link_id);
2591 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
2592
2593 lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
2594 if (lhapd)
2595 lsta = ap_get_sta(lhapd,
2596 req_list->sta_mld_addr);
2597
2598 if (lsta)
2599 ap_free_sta(lhapd, lsta);
2600 } else {
2601 total_kde_len += link_kde_len;
2602 links_add_ok |= BIT(info->link_id);
2603 wpa_msg(hapd->msg_ctx, MSG_INFO,
2604 WPA_EVENT_LINK_STA_ADDED "sta=" MACSTR
2605 " link_id=%u", MAC2STR(req_list->sta_mld_addr),
2606 info->link_id);
2607 }
2608
2609 info->status = status;
2610 }
2611
2612 dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
2613 list) {
2614 wpa_printf(MSG_DEBUG,
2615 "MLD: Del Link Reconf STA for link id=%u status=%u",
2616 info->link_id, info->status);
2617 if (info->status == WLAN_STATUS_SUCCESS)
2618 links_del_ok |= BIT(info->link_id);
2619 }
2620
2621 wpa_printf(MSG_INFO, "MLD: valid_links=0x%x add_ok=0x%x del_ok=0x%x",
2622 valid_links, links_add_ok, links_del_ok);
2623
2624 if ((links_add_ok && (valid_links & links_add_ok)) ||
2625 (links_del_ok && !(valid_links & links_del_ok))) {
2626 wpa_printf(MSG_INFO,
2627 "MLD: Links requested failed to satisfy valid links");
2628 return false;
2629 }
2630
2631 if (links_add_ok & links_del_ok) {
2632 wpa_printf(MSG_INFO,
2633 "MLD: Links (0x%x) present in both valid add and delete requests",
2634 links_add_ok & links_del_ok);
2635 return false;
2636 }
2637
2638 valid_links |= links_add_ok;
2639 valid_links &= ~links_del_ok;
2640 if (!valid_links) {
2641 if (!recover_from_zero_links(&links_del_ok, &recovery_link)) {
2642 wpa_printf(MSG_INFO,
2643 "MLD: Total-links validation failed");
2644 return false;
2645 }
2646 /* Add the recovery link back to valid_links */
2647 valid_links |= BIT(recovery_link);
2648 }
2649
2650 req_list->new_valid_links = valid_links;
2651 req_list->links_add_ok = links_add_ok;
2652 req_list->links_del_ok = links_del_ok;
2653
2654 /* TODO: Add support to handle multiple requests from the non-AP MLD */
2655 assoc_sta->reconf_req = req_list;
2656
2657 return true;
2658 }
2659
2660
2661 static int
hostapd_handle_link_reconf_req(struct hostapd_data * hapd,const u8 * buf,size_t len)2662 hostapd_handle_link_reconf_req(struct hostapd_data *hapd, const u8 *buf,
2663 size_t len)
2664 {
2665 struct ieee802_11_elems elems;
2666 struct hostapd_data *assoc_hapd;
2667 struct sta_info *sta, *assoc_sta = NULL;
2668 u8 dialog_token;
2669 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
2670 struct link_reconf_req_list *req_list = NULL;
2671 const u8 *pos = NULL;
2672 int ret = -1;
2673
2674 wpa_printf(MSG_DEBUG,
2675 "MLD: Link Reconfiguration Request frame from " MACSTR,
2676 MAC2STR(mgmt->sa));
2677
2678 /* Min length: IEEE80211 Header (24B) + Category (1B) + Action (1B) +
2679 * Dialog token (1B) +
2680 * Reconfiguration MLE header and extension ID (3B)
2681 */
2682 if (len < IEEE80211_HDRLEN + 3 + 3) {
2683 wpa_printf(MSG_DEBUG,
2684 "MLD: Invalid minimum length (%zu) for Link Reconfiguration Request",
2685 len);
2686 goto out;
2687 }
2688
2689 dialog_token = mgmt->u.action.u.link_reconf_req.dialog_token;
2690 pos = mgmt->u.action.u.link_reconf_req.variable;
2691
2692 sta = ap_get_sta(hapd, mgmt->sa);
2693 if (!sta) {
2694 wpa_printf(MSG_DEBUG, "MLD: No STA found for " MACSTR
2695 "; drop Link Reconfiguration Request",
2696 MAC2STR(mgmt->sa));
2697 goto out;
2698 }
2699
2700 if (!ap_sta_is_mld(hapd, sta)) {
2701 wpa_printf(MSG_DEBUG,
2702 "MLD: Not an MLD connection; drop Link Reconfiguration Request");
2703 goto out;
2704 }
2705
2706 assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
2707 if (!assoc_sta) {
2708 wpa_printf(MSG_DEBUG,
2709 "MLD: Not able to get assoc link STA; drop Link Reconfiguration Request");
2710 goto out;
2711 }
2712
2713 if (assoc_sta->reconf_req) {
2714 wpa_printf(MSG_INFO,
2715 "MLD: Link Reconfiguration Request from this STA with token=%u is already in progress",
2716 assoc_sta->reconf_req->dialog_token);
2717 goto out;
2718 }
2719
2720 /* Parse Reconfiguration Multi-Link element and OCI elements */
2721 if (ieee802_11_parse_elems(pos, len - (pos - buf), &elems, 1) ==
2722 ParseFailed) {
2723 wpa_printf(MSG_DEBUG,
2724 "MLD: Could not parse Link Reconfiguration Request");
2725 goto out;
2726 }
2727
2728 if (!elems.reconf_mle || !elems.reconf_mle_len) {
2729 wpa_printf(MSG_DEBUG, "MLD: No Reconfiguration ML element");
2730 goto out;
2731 }
2732
2733 /* Process Reconfiguration MLE */
2734 if (hostapd_parse_link_reconf_req_reconf_mle(hapd, elems.reconf_mle,
2735 elems.reconf_mle_len,
2736 &req_list)) {
2737 wpa_printf(MSG_INFO,
2738 "MLD: Reconfiguration MLE parsing failed; drop Link Reconfiguration Request");
2739 goto out;
2740 }
2741
2742 /* Do OCI element validation */
2743 if (dl_list_empty(&req_list->add_req))
2744 goto skip_oci_validation;
2745
2746 #ifdef CONFIG_OCV
2747 if (!elems.oci || !elems.oci_len) {
2748 if (wpa_auth_uses_ocv(assoc_sta->wpa_sm) == 1) {
2749 wpa_printf(MSG_INFO,
2750 "MLD: No OCI element present; drop Link Reconfiguration Request");
2751 goto out;
2752 }
2753 } else {
2754 struct wpa_channel_info ci;
2755
2756 if (!wpa_auth_uses_ocv(assoc_sta->wpa_sm)) {
2757 wpa_printf(MSG_INFO,
2758 "MLD: Unexpected OCI element found; drop Link Reconfiguration Request");
2759 goto out;
2760 }
2761
2762 if (hostapd_drv_channel_info(hapd, &ci)) {
2763 wpa_printf(MSG_DEBUG,
2764 "MLD: Failed to get channel info to verify OCI element");
2765 goto out;
2766 }
2767
2768 if (!ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
2769 channel_width_to_int(ci.chanwidth),
2770 ci.seg1_idx)) {
2771 wpa_printf(MSG_INFO,
2772 "MLD: OCI verification failed; drop Link Reconfiguration Request");
2773 goto out;
2774 }
2775 }
2776 #endif /* CONFIG_OCV */
2777
2778 skip_oci_validation:
2779 /* Do STA profile validation */
2780 if (!hostapd_validate_link_reconf_req(hapd, assoc_sta, req_list))
2781 goto out;
2782
2783 req_list->dialog_token = dialog_token;
2784 ret = hostapd_send_link_reconf_resp(hapd, assoc_sta, req_list);
2785 if (ret)
2786 wpa_printf(MSG_INFO,
2787 "MLD: Failed to send Link Reconfiguration Response (%d)",
2788 ret);
2789
2790 out:
2791 if (ret) {
2792 ml_deinit_link_reconf_req(&req_list);
2793 if (assoc_sta && assoc_sta->reconf_req)
2794 assoc_sta->reconf_req = NULL;
2795 }
2796 return ret;
2797 }
2798
2799
ieee802_11_rx_protected_eht_action(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)2800 void ieee802_11_rx_protected_eht_action(struct hostapd_data *hapd,
2801 const struct ieee80211_mgmt *mgmt,
2802 size_t len)
2803 {
2804 const u8 *payload;
2805 u8 action;
2806
2807 if (!hapd->conf->mld_ap)
2808 return;
2809
2810 payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
2811 action = *payload++;
2812
2813 switch (action) {
2814 case WLAN_PROT_EHT_LINK_RECONFIG_REQUEST:
2815 if (hostapd_handle_link_reconf_req(hapd, (const u8 *) mgmt,
2816 len))
2817 wpa_printf(MSG_INFO,
2818 "MLD: Link Reconf Request processing failed");
2819 return;
2820 }
2821
2822 wpa_printf(MSG_DEBUG,
2823 "MLD: Unsupported Protected EHT Action %u from " MACSTR
2824 " discarded", action, MAC2STR(mgmt->sa));
2825 }
2826
2827
hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data * hapd)2828 size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data *hapd)
2829 {
2830 if (!hapd->conf->mld_ap)
2831 return 0;
2832
2833 #ifdef CONFIG_TESTING_OPTIONS
2834 /*
2835 * Allocate enough space for mld_indicate_disabled. i.e.:
2836 * Element ID, Length, and Element ID Extension (3) +
2837 * Control including presence bitmap (2) + 8 * 2 byte link mappings
2838 */
2839 return 3 + 2 + 8 * 2;
2840 #else /* CONFIG_TESTING_OPTIONS */
2841 return 0;
2842 #endif /* CONFIG_TESTING_OPTIONS */
2843 }
2844
2845
hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data * hapd,u8 * eid)2846 u8 * hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 *eid)
2847 {
2848 #ifdef CONFIG_TESTING_OPTIONS
2849 struct hostapd_data *other_hapd;
2850 bool need_ttlm = false;
2851 u16 ttlm = 0;
2852 #endif /* CONFIG_TESTING_OPTIONS */
2853 u8 *pos = eid;
2854
2855 if (!hapd->conf->mld_ap)
2856 return eid;
2857
2858 #ifdef CONFIG_TESTING_OPTIONS
2859 for_each_mld_link(other_hapd, hapd) {
2860 if (other_hapd->conf->mld_indicate_disabled)
2861 need_ttlm = true;
2862 else
2863 ttlm |= BIT(other_hapd->mld_link_id);
2864 }
2865
2866 if (need_ttlm) {
2867 int i;
2868
2869 *pos++ = WLAN_EID_EXTENSION;
2870 /* ext EID + 2 bytes control + 8 * link mappings */
2871 *pos++ = 1 + 2 + 8 * 2;
2872 *pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING;
2873 *pos++ = EHT_TID_TO_LINK_MAP_DIRECTION_BOTH;
2874 *pos++ = 0xff; /* link mapping presence bitmap */
2875
2876 for (i = 0; i < 8; i++) {
2877 WPA_PUT_LE16(pos, ttlm);
2878 pos += 2;
2879 }
2880 }
2881 #endif /* CONFIG_TESTING_OPTIONS */
2882
2883 return pos;
2884 }
2885