1 /*
2 * hostapd / IEEE 802.11be EHT
3 * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
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 "crypto/crypto.h"
12 #include "crypto/dh_groups.h"
13 #include "hostapd.h"
14 #include "sta_info.h"
15 #include "ieee802_11.h"
16
17
ieee80211_eht_ppet_size(u16 ppe_thres_hdr,const u8 * phy_cap_info)18 static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
19 {
20 u8 ru;
21 u16 sz = 0;
22
23 if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
24 EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
25 return 0;
26
27 ru = (ppe_thres_hdr &
28 EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
29 while (ru) {
30 if (ru & 0x1)
31 sz++;
32 ru >>= 1;
33 }
34
35 sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
36 EHT_PPE_THRES_NSS_SHIFT));
37 sz = (sz * 6) + 9;
38 if (sz % 8)
39 sz += 8;
40 sz /= 8;
41
42 return sz;
43 }
44
45
ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode,u8 opclass,int he_oper_chwidth,const u8 * he_phy_cap,const u8 * eht_phy_cap)46 static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
47 int he_oper_chwidth, const u8 *he_phy_cap,
48 const u8 *eht_phy_cap)
49 {
50 u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
51 bool band24, band5, band6;
52 u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
53 u8 cap_chwidth;
54
55 switch (he_oper_chwidth) {
56 case CONF_OPER_CHWIDTH_80P80MHZ:
57 he_phy_cap_chwidth |=
58 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
59 /* fall through */
60 case CONF_OPER_CHWIDTH_160MHZ:
61 he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
62 /* fall through */
63 case CONF_OPER_CHWIDTH_80MHZ:
64 case CONF_OPER_CHWIDTH_USE_HT:
65 he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
66 HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
67 break;
68 }
69
70 cap_chwidth = he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
71 if (he_oper_chwidth != -1)
72 he_phy_cap_chwidth &= cap_chwidth;
73 else
74 he_phy_cap_chwidth = cap_chwidth;
75
76 band24 = mode == HOSTAPD_MODE_IEEE80211B ||
77 mode == HOSTAPD_MODE_IEEE80211G ||
78 mode == NUM_HOSTAPD_MODES;
79 band5 = mode == HOSTAPD_MODE_IEEE80211A ||
80 mode == NUM_HOSTAPD_MODES;
81 band6 = is_6ghz_op_class(opclass);
82
83 if (band24 &&
84 (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
85 return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
86
87 if (band5 &&
88 (he_phy_cap_chwidth &
89 (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
90 HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
91 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
92 return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
93
94 if (band5 &&
95 (he_phy_cap_chwidth &
96 (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
97 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
98 sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
99
100 if (band6 &&
101 (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
102 EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK))
103 sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
104
105 return sz;
106 }
107
108
hostapd_eid_eht_capab_len(struct hostapd_data * hapd,enum ieee80211_op_mode opmode)109 size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
110 enum ieee80211_op_mode opmode)
111 {
112 struct hostapd_hw_modes *mode;
113 struct eht_capabilities *eht_cap;
114 size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
115
116 mode = hapd->iface->current_mode;
117 if (!mode)
118 return 0;
119
120 eht_cap = &mode->eht_capab[opmode];
121 if (!eht_cap->eht_supported)
122 return 0;
123
124 len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
125 hapd->iconf->he_oper_chwidth,
126 mode->he_capab[opmode].phy_cap,
127 eht_cap->phy_cap);
128 len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
129 eht_cap->phy_cap);
130
131 return len;
132 }
133
134
hostapd_eid_eht_capab(struct hostapd_data * hapd,u8 * eid,enum ieee80211_op_mode opmode)135 u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
136 enum ieee80211_op_mode opmode)
137 {
138 struct hostapd_hw_modes *mode;
139 struct eht_capabilities *eht_cap;
140 struct ieee80211_eht_capabilities *cap;
141 size_t mcs_nss_len, ppe_thresh_len;
142 u8 *pos = eid, *length_pos;
143
144 mode = hapd->iface->current_mode;
145 if (!mode)
146 return eid;
147
148 eht_cap = &mode->eht_capab[opmode];
149 if (!eht_cap->eht_supported)
150 return eid;
151
152 *pos++ = WLAN_EID_EXTENSION;
153 length_pos = pos++;
154 *pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
155
156 cap = (struct ieee80211_eht_capabilities *) pos;
157 os_memset(cap, 0, sizeof(*cap));
158 cap->mac_cap = host_to_le16(eht_cap->mac_cap);
159 os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
160
161 if (!is_6ghz_op_class(hapd->iconf->op_class))
162 cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
163 ~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
164 if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
165 cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
166 ~EHT_PHYCAP_SU_BEAMFORMER;
167
168 if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
169 cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
170 ~EHT_PHYCAP_SU_BEAMFORMEE;
171
172 if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
173 cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
174 ~EHT_PHYCAP_MU_BEAMFORMER_MASK;
175
176 pos = cap->optional;
177
178 mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
179 hapd->iconf->op_class,
180 hapd->iconf->he_oper_chwidth,
181 mode->he_capab[opmode].phy_cap,
182 eht_cap->phy_cap);
183 if (mcs_nss_len) {
184 os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
185 pos += mcs_nss_len;
186 }
187
188 ppe_thresh_len = ieee80211_eht_ppet_size(
189 WPA_GET_LE16(&eht_cap->ppet[0]),
190 eht_cap->phy_cap);
191 if (ppe_thresh_len) {
192 os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
193 pos += ppe_thresh_len;
194 }
195
196 *length_pos = pos - (eid + 2);
197 return pos;
198 }
199
200
hostapd_eid_eht_operation(struct hostapd_data * hapd,u8 * eid)201 u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
202 {
203 struct hostapd_config *conf = hapd->iconf;
204 struct ieee80211_eht_operation *oper;
205 u8 *pos = eid, seg0 = 0, seg1 = 0;
206 enum oper_chan_width chwidth;
207 size_t elen = 1 + 4;
208 bool eht_oper_info_present;
209 u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
210
211 if (!hapd->iface->current_mode)
212 return eid;
213
214 if (is_6ghz_op_class(conf->op_class))
215 chwidth = op_class_to_ch_width(conf->op_class);
216 else
217 chwidth = conf->eht_oper_chwidth;
218
219 eht_oper_info_present = chwidth == CONF_OPER_CHWIDTH_320MHZ ||
220 punct_bitmap;
221
222 if (eht_oper_info_present)
223 elen += 3;
224
225 if (punct_bitmap)
226 elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
227
228 *pos++ = WLAN_EID_EXTENSION;
229 *pos++ = 1 + elen;
230 *pos++ = WLAN_EID_EXT_EHT_OPERATION;
231
232 oper = (struct ieee80211_eht_operation *) pos;
233 oper->oper_params = 0;
234
235 if (hapd->iconf->eht_default_pe_duration)
236 oper->oper_params |= EHT_OPER_DEFAULT_PE_DURATION;
237
238 /* TODO: Fill in appropriate EHT-MCS max Nss information */
239 oper->basic_eht_mcs_nss_set[0] = 0x11;
240 oper->basic_eht_mcs_nss_set[1] = 0x00;
241 oper->basic_eht_mcs_nss_set[2] = 0x00;
242 oper->basic_eht_mcs_nss_set[3] = 0x00;
243
244 if (!eht_oper_info_present)
245 return pos + elen;
246
247 oper->oper_params |= EHT_OPER_INFO_PRESENT;
248 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
249
250 switch (chwidth) {
251 case CONF_OPER_CHWIDTH_320MHZ:
252 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
253 seg1 = seg0;
254 if (hapd->iconf->channel < seg0)
255 seg0 -= 16;
256 else
257 seg0 += 16;
258 break;
259 case CONF_OPER_CHWIDTH_160MHZ:
260 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
261 seg1 = seg0;
262 if (hapd->iconf->channel < seg0)
263 seg0 -= 8;
264 else
265 seg0 += 8;
266 break;
267 case CONF_OPER_CHWIDTH_80MHZ:
268 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
269 break;
270 case CONF_OPER_CHWIDTH_USE_HT:
271 if (seg0)
272 oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
273 break;
274 default:
275 return eid;
276 }
277
278 oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
279 oper->oper_info.ccfs1 = seg1;
280
281 if (punct_bitmap) {
282 oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
283 oper->oper_info.disabled_chan_bitmap =
284 host_to_le16(punct_bitmap);
285 }
286
287 return pos + elen;
288 }
289
290
check_valid_eht_mcs_nss(struct hostapd_data * hapd,const u8 * ap_mcs,const u8 * sta_mcs,u8 mcs_count,u8 map_len)291 static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
292 const u8 *sta_mcs, u8 mcs_count, u8 map_len)
293 {
294 unsigned int i, j;
295
296 for (i = 0; i < mcs_count; i++) {
297 ap_mcs += i * 3;
298 sta_mcs += i * 3;
299
300 for (j = 0; j < map_len; j++) {
301 if (((ap_mcs[j] >> 4) & 0xFF) == 0)
302 continue;
303
304 if ((sta_mcs[j] & 0xFF) == 0)
305 continue;
306
307 return true;
308 }
309 }
310
311 wpa_printf(MSG_DEBUG,
312 "No matching EHT MCS found between AP TX and STA RX");
313 return false;
314 }
315
316
check_valid_eht_mcs(struct hostapd_data * hapd,const u8 * sta_eht_capab,enum ieee80211_op_mode opmode)317 static bool check_valid_eht_mcs(struct hostapd_data *hapd,
318 const u8 *sta_eht_capab,
319 enum ieee80211_op_mode opmode)
320 {
321 struct hostapd_hw_modes *mode;
322 const struct ieee80211_eht_capabilities *capab;
323 const u8 *ap_mcs, *sta_mcs;
324 u8 mcs_count = 1;
325
326 mode = hapd->iface->current_mode;
327 if (!mode)
328 return true;
329
330 ap_mcs = mode->eht_capab[opmode].mcs;
331 capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
332 sta_mcs = capab->optional;
333
334 if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
335 hapd->iconf->he_oper_chwidth,
336 mode->he_capab[opmode].phy_cap,
337 mode->eht_capab[opmode].phy_cap) ==
338 EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
339 return check_valid_eht_mcs_nss(
340 hapd, ap_mcs, sta_mcs, 1,
341 EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
342
343 switch (hapd->iface->conf->eht_oper_chwidth) {
344 case CONF_OPER_CHWIDTH_320MHZ:
345 mcs_count++;
346 /* fall through */
347 case CONF_OPER_CHWIDTH_80P80MHZ:
348 case CONF_OPER_CHWIDTH_160MHZ:
349 mcs_count++;
350 break;
351 default:
352 break;
353 }
354
355 return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
356 EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
357 }
358
359
ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,u8 opclass,const u8 * he_cap,const u8 * eht_cap,size_t len)360 static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
361 u8 opclass, const u8 *he_cap,
362 const u8 *eht_cap, size_t len)
363 {
364 const struct ieee80211_he_capabilities *he_capab;
365 struct ieee80211_eht_capabilities *cap;
366 const u8 *he_phy_cap;
367 size_t cap_len;
368 u16 ppe_thres_hdr;
369
370 he_capab = (const struct ieee80211_he_capabilities *) he_cap;
371 he_phy_cap = he_capab->he_phy_capab_info;
372 cap = (struct ieee80211_eht_capabilities *) eht_cap;
373 cap_len = sizeof(*cap) - sizeof(cap->optional);
374 if (len < cap_len)
375 return true;
376
377 cap_len += ieee80211_eht_mcs_set_size(mode, opclass, -1, he_phy_cap,
378 cap->phy_cap);
379 if (len < cap_len)
380 return true;
381
382 ppe_thres_hdr = len > cap_len + 1 ?
383 WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
384 cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
385
386 return len < cap_len;
387 }
388
389
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)390 u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
391 enum ieee80211_op_mode opmode,
392 const u8 *he_capab, size_t he_capab_len,
393 const u8 *eht_capab, size_t eht_capab_len)
394 {
395 struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
396 enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
397
398 if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
399 !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
400 !eht_capab ||
401 ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
402 he_capab, eht_capab,
403 eht_capab_len) ||
404 !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
405 sta->flags &= ~WLAN_STA_EHT;
406 os_free(sta->eht_capab);
407 sta->eht_capab = NULL;
408 return WLAN_STATUS_SUCCESS;
409 }
410
411 os_free(sta->eht_capab);
412 sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
413 if (!sta->eht_capab) {
414 sta->eht_capab_len = 0;
415 return WLAN_STATUS_UNSPECIFIED_FAILURE;
416 }
417
418 sta->flags |= WLAN_STA_EHT;
419 sta->eht_capab_len = eht_capab_len;
420
421 return WLAN_STATUS_SUCCESS;
422 }
423
424
hostapd_get_eht_capab(struct hostapd_data * hapd,const struct ieee80211_eht_capabilities * src,struct ieee80211_eht_capabilities * dest,size_t len)425 void hostapd_get_eht_capab(struct hostapd_data *hapd,
426 const struct ieee80211_eht_capabilities *src,
427 struct ieee80211_eht_capabilities *dest,
428 size_t len)
429 {
430 if (!src || !dest)
431 return;
432
433 if (len > sizeof(*dest))
434 len = sizeof(*dest);
435 /* TODO: mask out unsupported features */
436
437 os_memset(dest, 0, sizeof(*dest));
438 os_memcpy(dest, src, len);
439 }
440
441
hostapd_eid_eht_basic_ml_common(struct hostapd_data * hapd,u8 * eid,struct mld_info * mld_info,bool include_mld_id)442 static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
443 u8 *eid, struct mld_info *mld_info,
444 bool include_mld_id)
445 {
446 struct wpabuf *buf;
447 u16 control;
448 u8 *pos = eid;
449 const u8 *ptr;
450 size_t len, slice_len;
451 u8 link_id;
452 u8 common_info_len;
453 u16 mld_cap;
454 u8 max_simul_links, active_links;
455
456 /*
457 * As the Multi-Link element can exceed the size of 255 bytes need to
458 * first build it and then handle fragmentation.
459 */
460 buf = wpabuf_alloc(1024);
461 if (!buf)
462 return pos;
463
464 /* Multi-Link Control field */
465 control = MULTI_LINK_CONTROL_TYPE_BASIC |
466 BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
467 BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
468 BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
469 BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
470
471 /*
472 * Set the basic Multi-Link common information. Hard code the common
473 * info length to 13 based on the length of the present fields:
474 * Length (1) + MLD address (6) + Link ID (1) +
475 * BSS Parameters Change Count (1) + EML Capabilities (2) +
476 * MLD Capabilities and Operations (2)
477 */
478 #define EHT_ML_COMMON_INFO_LEN 13
479 common_info_len = EHT_ML_COMMON_INFO_LEN;
480
481 if (include_mld_id) {
482 /* AP MLD ID */
483 control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
484 common_info_len++;
485 }
486
487 wpabuf_put_le16(buf, control);
488
489 wpabuf_put_u8(buf, common_info_len);
490
491 /* Own MLD MAC Address */
492 wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
493
494 /* Own Link ID */
495 wpabuf_put_u8(buf, hapd->mld_link_id);
496
497 /* Currently hard code the BSS Parameters Change Count to 0x1 */
498 wpabuf_put_u8(buf, 0x1);
499
500 wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
501 hapd->iface->mld_eml_capa);
502 wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
503
504 mld_cap = hapd->iface->mld_mld_capa;
505 max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
506 active_links = hapd->mld->num_links - 1;
507
508 if (active_links > max_simul_links) {
509 wpa_printf(MSG_ERROR,
510 "MLD: Error in max simultaneous links, advertised: 0x%x current: 0x%x",
511 max_simul_links, active_links);
512 active_links = max_simul_links;
513 }
514
515 mld_cap &= ~EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
516 mld_cap |= active_links & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
517
518 /* TODO: Advertise T2LM based on driver support as well */
519 mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
520
521 wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
522 mld_cap);
523 wpabuf_put_le16(buf, mld_cap);
524
525 if (include_mld_id) {
526 wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
527 hostapd_get_mld_id(hapd));
528 wpabuf_put_u8(buf, hostapd_get_mld_id(hapd));
529 }
530
531 if (!mld_info)
532 goto out;
533
534 /* Add link info for the other links */
535 for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
536 struct mld_link_info *link = &mld_info->links[link_id];
537 struct hostapd_data *link_bss;
538
539 /*
540 * control (2) + station info length (1) + MAC address (6) +
541 * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
542 * parameters change counter (1) + station profile length.
543 */
544 #define EHT_ML_STA_INFO_LEN 22
545 size_t total_len = EHT_ML_STA_INFO_LEN +
546 link->resp_sta_profile_len;
547
548 /* Skip the local one */
549 if (link_id == hapd->mld_link_id || !link->valid)
550 continue;
551
552 link_bss = hostapd_mld_get_link_bss(hapd, link_id);
553 if (!link_bss) {
554 wpa_printf(MSG_ERROR,
555 "MLD: Couldn't find link BSS - skip it");
556 continue;
557 }
558
559 /* Per-STA Profile subelement */
560 wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
561
562 if (total_len <= 255)
563 wpabuf_put_u8(buf, total_len);
564 else
565 wpabuf_put_u8(buf, 255);
566
567 /* STA Control */
568 control = (link_id & 0xf) |
569 EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK |
570 EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
571 EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
572 EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
573 EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
574 EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
575 wpabuf_put_le16(buf, control);
576
577 /* STA Info */
578
579 /* STA Info Length */
580 wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
581 wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
582 wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
583
584 /* TSF Offset */
585 /*
586 * TODO: Currently setting TSF offset to zero. However, this
587 * information needs to come from the driver.
588 */
589 wpabuf_put_le64(buf, 0);
590
591 /* DTIM Info */
592 wpabuf_put_u8(buf, 0); /* DTIM Count */
593 wpabuf_put_u8(buf, link_bss->conf->dtim_period);
594
595 /* BSS Parameters Change Count */
596 wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
597
598 if (!link->resp_sta_profile)
599 continue;
600
601 /* Fragment the sub element if needed */
602 if (total_len <= 255) {
603 wpabuf_put_data(buf, link->resp_sta_profile,
604 link->resp_sta_profile_len);
605 } else {
606 ptr = link->resp_sta_profile;
607 len = link->resp_sta_profile_len;
608
609 slice_len = 255 - EHT_ML_STA_INFO_LEN;
610
611 wpabuf_put_data(buf, ptr, slice_len);
612 len -= slice_len;
613 ptr += slice_len;
614
615 while (len) {
616 if (len <= 255)
617 slice_len = len;
618 else
619 slice_len = 255;
620
621 wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT);
622 wpabuf_put_u8(buf, slice_len);
623 wpabuf_put_data(buf, ptr, slice_len);
624
625 len -= slice_len;
626 ptr += slice_len;
627 }
628 }
629 }
630
631 out:
632 /* Fragment the Multi-Link element, if needed */
633 len = wpabuf_len(buf);
634 ptr = wpabuf_head(buf);
635
636 if (len <= 254)
637 slice_len = len;
638 else
639 slice_len = 254;
640
641 *pos++ = WLAN_EID_EXTENSION;
642 *pos++ = slice_len + 1;
643 *pos++ = WLAN_EID_EXT_MULTI_LINK;
644 os_memcpy(pos, ptr, slice_len);
645
646 ptr += slice_len;
647 pos += slice_len;
648 len -= slice_len;
649
650 while (len) {
651 if (len <= 255)
652 slice_len = len;
653 else
654 slice_len = 255;
655
656 *pos++ = WLAN_EID_FRAGMENT;
657 *pos++ = slice_len;
658 os_memcpy(pos, ptr, slice_len);
659
660 ptr += slice_len;
661 pos += slice_len;
662 len -= slice_len;
663 }
664
665 wpabuf_free(buf);
666 return pos;
667 }
668
669
hostapd_eid_eht_reconf_ml(struct hostapd_data * hapd,u8 * eid)670 static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
671 {
672 #ifdef CONFIG_TESTING_OPTIONS
673 struct hostapd_data *other_hapd;
674 u16 control;
675 u8 *pos = eid;
676 unsigned int i;
677
678 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML");
679
680 /* First check if the element needs to be added */
681 for (i = 0; i < hapd->iface->interfaces->count; i++) {
682 other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
683
684 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: %u",
685 other_hapd->eht_mld_link_removal_count);
686
687 if (other_hapd->eht_mld_link_removal_count)
688 break;
689 }
690
691 /* No link is going to be removed */
692 if (i == hapd->iface->interfaces->count)
693 return eid;
694
695 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: Adding element");
696
697 /* The length will be set at the end */
698 *pos++ = WLAN_EID_EXTENSION;
699 *pos++ = 0;
700 *pos++ = WLAN_EID_EXT_MULTI_LINK;
701
702 /* Set the Multi-Link Control field */
703 control = MULTI_LINK_CONTROL_TYPE_RECONF;
704 WPA_PUT_LE16(pos, control);
705 pos += 2;
706
707 /* Common Info doesn't include any information */
708 *pos++ = 1;
709
710 /* Add the per station profiles */
711 for (i = 0; i < hapd->iface->interfaces->count; i++) {
712 other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
713 if (!other_hapd->eht_mld_link_removal_count)
714 continue;
715
716 /* Subelement ID is 0 */
717 *pos++ = 0;
718 *pos++ = 5;
719
720 control = other_hapd->mld_link_id |
721 EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER;
722
723 WPA_PUT_LE16(pos, control);
724 pos += 2;
725
726 /* STA profile length */
727 *pos++ = 3;
728
729 WPA_PUT_LE16(pos, other_hapd->eht_mld_link_removal_count);
730 pos += 2;
731 }
732
733 eid[1] = pos - eid - 2;
734
735 wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration ML", eid, eid[1] + 2);
736 return pos;
737 #else /* CONFIG_TESTING_OPTIONS */
738 return eid;
739 #endif /* CONFIG_TESTING_OPTIONS */
740 }
741
742
hostapd_eid_eht_ml_len(struct mld_info * info,bool include_mld_id)743 static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
744 bool include_mld_id)
745 {
746 size_t len = 0;
747 size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
748 u8 link_id;
749
750 if (include_mld_id)
751 eht_ml_len++;
752
753 for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
754 link_id++) {
755 struct mld_link_info *link;
756 size_t sta_len = EHT_ML_STA_INFO_LEN;
757
758 link = &info->links[link_id];
759 if (!link->valid)
760 continue;
761
762 sta_len += link->resp_sta_profile_len;
763
764 /* Element data and (fragmentation) headers */
765 eht_ml_len += sta_len;
766 eht_ml_len += 2 + sta_len / 255 * 2;
767 }
768
769 /* Element data */
770 len += eht_ml_len;
771
772 /* First header (254 bytes of data) */
773 len += 3;
774
775 /* Fragmentation headers; +1 for shorter first chunk */
776 len += (eht_ml_len + 1) / 255 * 2;
777
778 return len;
779 }
780 #undef EHT_ML_COMMON_INFO_LEN
781 #undef EHT_ML_STA_INFO_LEN
782
783
hostapd_eid_eht_ml_beacon(struct hostapd_data * hapd,struct mld_info * info,u8 * eid,bool include_mld_id)784 u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
785 struct mld_info *info,
786 u8 *eid, bool include_mld_id)
787 {
788 eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id);
789 return hostapd_eid_eht_reconf_ml(hapd, eid);
790 }
791
792
793
hostapd_eid_eht_ml_assoc(struct hostapd_data * hapd,struct sta_info * info,u8 * eid)794 u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
795 u8 *eid)
796 {
797 if (!ap_sta_is_mld(hapd, info))
798 return eid;
799
800 eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info,
801 false);
802 ap_sta_free_sta_profile(&info->mld_info);
803 return hostapd_eid_eht_reconf_ml(hapd, eid);
804 }
805
806
hostapd_eid_eht_ml_beacon_len(struct hostapd_data * hapd,struct mld_info * info,bool include_mld_id)807 size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
808 struct mld_info *info,
809 bool include_mld_id)
810 {
811 return hostapd_eid_eht_ml_len(info, include_mld_id);
812 }
813
814
hostapd_ml_auth_resp(struct hostapd_data * hapd)815 struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
816 {
817 struct wpabuf *buf = wpabuf_alloc(12);
818
819 if (!buf)
820 return NULL;
821
822 wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
823 wpabuf_put_u8(buf, 10);
824 wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
825 wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC);
826 wpabuf_put_u8(buf, ETH_ALEN + 1);
827 wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
828
829 return buf;
830 }
831
832
833 #ifdef CONFIG_SAE
834
835 static const u8 *
sae_commit_skip_fixed_fields(const struct ieee80211_mgmt * mgmt,size_t len,const u8 * pos,u16 status_code)836 sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
837 const u8 *pos, u16 status_code)
838 {
839 u16 group;
840 size_t prime_len;
841 struct crypto_ec *ec;
842
843 if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
844 return pos;
845
846 /* SAE H2E commit message (group, scalar, FFE) */
847 if (len < 2) {
848 wpa_printf(MSG_DEBUG,
849 "EHT: SAE Group is not present");
850 return NULL;
851 }
852
853 group = WPA_GET_LE16(pos);
854 pos += 2;
855
856 /* TODO: How to parse when the group is unknown? */
857 ec = crypto_ec_init(group);
858 if (!ec) {
859 const struct dh_group *dh = dh_groups_get(group);
860
861 if (!dh) {
862 wpa_printf(MSG_DEBUG, "EHT: Unknown SAE group %u",
863 group);
864 return NULL;
865 }
866
867 prime_len = dh->prime_len;
868 } else {
869 prime_len = crypto_ec_prime_len(ec);
870 }
871
872 wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len);
873
874 /* scalar */
875 pos += prime_len;
876
877 if (ec) {
878 pos += prime_len * 2;
879 crypto_ec_deinit(ec);
880 } else {
881 pos += prime_len;
882 }
883
884 if (pos - mgmt->u.auth.variable > (int) len) {
885 wpa_printf(MSG_DEBUG,
886 "EHT: Too short SAE commit Authentication frame");
887 return NULL;
888 }
889
890 wpa_hexdump(MSG_DEBUG, "EHT: SAE: Authentication frame elements",
891 pos, (int) len - (pos - mgmt->u.auth.variable));
892
893 return pos;
894 }
895
896
897 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)898 sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
899 const struct ieee80211_mgmt *mgmt, size_t len,
900 const u8 *pos, u16 status_code)
901 {
902 struct sta_info *sta;
903
904 if (status_code == WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
905 return pos;
906
907 /* send confirm integer */
908 pos += 2;
909
910 /*
911 * At this stage we should already have an MLD station and actually SA
912 * will be replaced with the MLD MAC address by the driver.
913 */
914 sta = ap_get_sta(hapd, mgmt->sa);
915 if (!sta) {
916 wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm");
917 return NULL;
918 }
919
920 if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) {
921 if (sta->sae)
922 wpa_printf(MSG_DEBUG, "SAE: Invalid state=%u",
923 sta->sae->state);
924 else
925 wpa_printf(MSG_DEBUG, "SAE: No SAE context");
926 return NULL;
927 }
928
929 wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
930 sta->sae->tmp->kck_len);
931
932 pos += sta->sae->tmp->kck_len;
933
934 if (pos - mgmt->u.auth.variable > (int) len) {
935 wpa_printf(MSG_DEBUG,
936 "EHT: Too short SAE confirm Authentication frame");
937 return NULL;
938 }
939
940 return pos;
941 }
942
943 #endif /* CONFIG_SAE */
944
945
auth_skip_fixed_fields(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)946 static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
947 const struct ieee80211_mgmt *mgmt,
948 size_t len)
949 {
950 u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
951 #ifdef CONFIG_SAE
952 u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
953 u16 status_code = le_to_host16(mgmt->u.auth.status_code);
954 #endif /* CONFIG_SAE */
955 const u8 *pos = mgmt->u.auth.variable;
956
957 /* Skip fixed fields as based on IEE P802.11-REVme/D3.0, Table 9-69
958 * (Presence of fields and elements in Authentications frames) */
959 switch (auth_alg) {
960 case WLAN_AUTH_OPEN:
961 return pos;
962 #ifdef CONFIG_SAE
963 case WLAN_AUTH_SAE:
964 if (auth_transaction == 1) {
965 if (status_code == WLAN_STATUS_SUCCESS) {
966 wpa_printf(MSG_DEBUG,
967 "EHT: SAE H2E is mandatory for MLD");
968 goto out;
969 }
970
971 return sae_commit_skip_fixed_fields(mgmt, len, pos,
972 status_code);
973 } else if (auth_transaction == 2) {
974 return sae_confirm_skip_fixed_fields(hapd, mgmt, len,
975 pos, status_code);
976 }
977
978 return pos;
979 #endif /* CONFIG_SAE */
980 /* TODO: Support additional algorithms that can be used for MLO */
981 case WLAN_AUTH_FT:
982 case WLAN_AUTH_FILS_SK:
983 case WLAN_AUTH_FILS_SK_PFS:
984 case WLAN_AUTH_FILS_PK:
985 case WLAN_AUTH_PASN:
986 default:
987 break;
988 }
989
990 #ifdef CONFIG_SAE
991 out:
992 #endif /* CONFIG_SAE */
993 wpa_printf(MSG_DEBUG,
994 "TODO: Authentication algorithm %u not supported with MLD",
995 auth_alg);
996 return NULL;
997 }
998
999
hostapd_process_ml_auth(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)1000 const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
1001 const struct ieee80211_mgmt *mgmt,
1002 size_t len)
1003 {
1004 struct ieee802_11_elems elems;
1005 const u8 *pos;
1006
1007 if (!hapd->conf->mld_ap)
1008 return NULL;
1009
1010 len -= offsetof(struct ieee80211_mgmt, u.auth.variable);
1011
1012 pos = auth_skip_fixed_fields(hapd, mgmt, len);
1013 if (!pos)
1014 return NULL;
1015
1016 if (ieee802_11_parse_elems(pos,
1017 (int)len - (pos - mgmt->u.auth.variable),
1018 &elems, 0) == ParseFailed) {
1019 wpa_printf(MSG_DEBUG,
1020 "MLD: Failed parsing Authentication frame");
1021 }
1022
1023 if (!elems.basic_mle || !elems.basic_mle_len)
1024 return NULL;
1025
1026 return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
1027 }
1028
1029
hostapd_mld_validate_assoc_info(struct hostapd_data * hapd,struct sta_info * sta)1030 static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
1031 struct sta_info *sta)
1032 {
1033 u8 link_id;
1034 struct mld_info *info = &sta->mld_info;
1035
1036 if (!ap_sta_is_mld(hapd, sta)) {
1037 wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
1038 return 0;
1039 }
1040
1041 /*
1042 * Iterate over the links negotiated in the (Re)Association Request
1043 * frame and validate that they are indeed valid links in the local AP
1044 * MLD.
1045 *
1046 * While at it, also update the local address for the links in the
1047 * mld_info, so it could be easily available for later flows, e.g., for
1048 * the RSN Authenticator, etc.
1049 */
1050 for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
1051 struct hostapd_data *other_hapd;
1052
1053 if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
1054 continue;
1055
1056 other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
1057 if (!other_hapd) {
1058 wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
1059 link_id);
1060 return -1;
1061 }
1062
1063 os_memcpy(info->links[link_id].local_addr, other_hapd->own_addr,
1064 ETH_ALEN);
1065 }
1066
1067 return 0;
1068 }
1069
1070
hostapd_process_ml_assoc_req_addr(struct hostapd_data * hapd,const u8 * basic_mle,size_t basic_mle_len,u8 * mld_addr)1071 int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
1072 const u8 *basic_mle, size_t basic_mle_len,
1073 u8 *mld_addr)
1074 {
1075 struct wpabuf *mlbuf = ieee802_11_defrag(basic_mle, basic_mle_len,
1076 true);
1077 struct ieee80211_eht_ml *ml;
1078 struct eht_ml_basic_common_info *common_info;
1079 size_t ml_len, common_info_len;
1080 int ret = -1;
1081 u16 ml_control;
1082
1083 if (!mlbuf)
1084 return WLAN_STATUS_SUCCESS;
1085
1086 ml = (struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
1087 ml_len = wpabuf_len(mlbuf);
1088
1089 if (ml_len < sizeof(*ml))
1090 goto out;
1091
1092 ml_control = le_to_host16(ml->ml_control);
1093 if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
1094 MULTI_LINK_CONTROL_TYPE_BASIC) {
1095 wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
1096 ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
1097 goto out;
1098 }
1099
1100 /* Common Info Length and MLD MAC Address must always be present */
1101 common_info_len = 1 + ETH_ALEN;
1102
1103 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
1104 wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected");
1105 goto out;
1106 }
1107
1108 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
1109 wpa_printf(MSG_DEBUG,
1110 "MLD: BSS Parameters Change Count not expected");
1111 goto out;
1112 }
1113
1114 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
1115 wpa_printf(MSG_DEBUG,
1116 "MLD: Medium Synchronization Delay Information not expected");
1117 goto out;
1118 }
1119
1120 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
1121 common_info_len += 2;
1122
1123 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA)
1124 common_info_len += 2;
1125
1126 if (sizeof(*ml) + common_info_len > ml_len) {
1127 wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
1128 goto out;
1129 }
1130
1131 common_info = (struct eht_ml_basic_common_info *) ml->variable;
1132
1133 /* Common information length includes the length octet */
1134 if (common_info->len != common_info_len) {
1135 wpa_printf(MSG_DEBUG,
1136 "MLD: Invalid common info len=%u", common_info->len);
1137 goto out;
1138 }
1139
1140 /* Get the MLD MAC Address */
1141 os_memcpy(mld_addr, common_info->mld_addr, ETH_ALEN);
1142 ret = 0;
1143
1144 out:
1145 wpabuf_free(mlbuf);
1146 return ret;
1147 }
1148
1149
hostapd_process_ml_assoc_req(struct hostapd_data * hapd,struct ieee802_11_elems * elems,struct sta_info * sta)1150 u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
1151 struct ieee802_11_elems *elems,
1152 struct sta_info *sta)
1153 {
1154 struct wpabuf *mlbuf;
1155 const struct ieee80211_eht_ml *ml;
1156 const struct eht_ml_basic_common_info *common_info;
1157 size_t ml_len, common_info_len;
1158 struct mld_link_info *link_info;
1159 struct mld_info *info = &sta->mld_info;
1160 const u8 *pos;
1161 int ret = -1;
1162 u16 ml_control;
1163
1164 mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
1165 if (!mlbuf)
1166 return WLAN_STATUS_SUCCESS;
1167
1168 ml = wpabuf_head(mlbuf);
1169 ml_len = wpabuf_len(mlbuf);
1170
1171 ml_control = le_to_host16(ml->ml_control);
1172 if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
1173 MULTI_LINK_CONTROL_TYPE_BASIC) {
1174 wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
1175 ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
1176 goto out;
1177 }
1178
1179 /* Common Info length and MLD MAC address must always be present */
1180 common_info_len = 1 + ETH_ALEN;
1181
1182 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
1183 wpa_printf(MSG_DEBUG, "MLD: Link ID info not expected");
1184 goto out;
1185 }
1186
1187 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
1188 wpa_printf(MSG_DEBUG, "MLD: BSS params change not expected");
1189 goto out;
1190 }
1191
1192 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
1193 wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
1194 goto out;
1195 }
1196
1197 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
1198 common_info_len += 2;
1199 } else {
1200 wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
1201 }
1202
1203 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
1204 common_info_len += 2;
1205
1206 } else {
1207 wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
1208 goto out;
1209 }
1210
1211 wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
1212 common_info_len);
1213
1214 if (sizeof(*ml) + common_info_len > ml_len) {
1215 wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
1216 goto out;
1217 }
1218
1219 common_info = (const struct eht_ml_basic_common_info *) ml->variable;
1220
1221 /* Common information length includes the length octet */
1222 if (common_info->len != common_info_len) {
1223 wpa_printf(MSG_DEBUG,
1224 "MLD: Invalid common info len=%u (expected %zu)",
1225 common_info->len, common_info_len);
1226 goto out;
1227 }
1228
1229 pos = common_info->variable;
1230
1231 if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
1232 info->common_info.eml_capa = WPA_GET_LE16(pos);
1233 pos += 2;
1234 } else {
1235 info->common_info.eml_capa = 0;
1236 }
1237
1238 info->common_info.mld_capa = WPA_GET_LE16(pos);
1239 pos += 2;
1240
1241 wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
1242 MAC2STR(info->common_info.mld_addr),
1243 info->common_info.eml_capa, info->common_info.mld_capa);
1244
1245 /* Check the MLD MAC Address */
1246 if (!ether_addr_equal(info->common_info.mld_addr,
1247 common_info->mld_addr)) {
1248 wpa_printf(MSG_DEBUG,
1249 "MLD: MLD address mismatch between authentication ("
1250 MACSTR ") and association (" MACSTR ")",
1251 MAC2STR(info->common_info.mld_addr),
1252 MAC2STR(common_info->mld_addr));
1253 goto out;
1254 }
1255
1256 info->links[hapd->mld_link_id].valid = 1;
1257
1258 /* Parse the link info field */
1259 ml_len -= sizeof(*ml) + common_info_len;
1260
1261 while (ml_len > 2) {
1262 size_t sub_elem_len = *(pos + 1);
1263 size_t sta_info_len;
1264 u16 control;
1265
1266 wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
1267 sub_elem_len);
1268
1269 if (2 + sub_elem_len > ml_len) {
1270 wpa_printf(MSG_DEBUG,
1271 "MLD: Invalid link info len: %zu %zu",
1272 2 + sub_elem_len, ml_len);
1273 goto out;
1274 }
1275
1276 if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
1277 wpa_printf(MSG_DEBUG,
1278 "MLD: Skip vendor specific subelement");
1279
1280 pos += 2 + sub_elem_len;
1281 ml_len -= 2 + sub_elem_len;
1282 continue;
1283 }
1284
1285 if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
1286 wpa_printf(MSG_DEBUG,
1287 "MLD: Skip unknown Multi-Link element subelement ID=%u",
1288 *pos);
1289 pos += 2 + sub_elem_len;
1290 ml_len -= 2 + sub_elem_len;
1291 continue;
1292 }
1293
1294 /* Skip the subelement ID and the length */
1295 pos += 2;
1296 ml_len -= 2;
1297
1298 /* Get the station control field */
1299 if (sub_elem_len < 2) {
1300 wpa_printf(MSG_DEBUG,
1301 "MLD: Too short Per-STA Profile subelement");
1302 goto out;
1303 }
1304 control = WPA_GET_LE16(pos);
1305 link_info = &info->links[control &
1306 EHT_PER_STA_CTRL_LINK_ID_MSK];
1307 pos += 2;
1308 ml_len -= 2;
1309 sub_elem_len -= 2;
1310
1311 if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
1312 wpa_printf(MSG_DEBUG,
1313 "MLD: Per-STA complete profile expected");
1314 goto out;
1315 }
1316
1317 if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
1318 wpa_printf(MSG_DEBUG,
1319 "MLD: Per-STA MAC address not present");
1320 goto out;
1321 }
1322
1323 if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
1324 EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
1325 wpa_printf(MSG_DEBUG,
1326 "MLD: Beacon/DTIM interval not expected");
1327 goto out;
1328 }
1329
1330 /* The length octet and the MAC address must be present */
1331 sta_info_len = 1 + ETH_ALEN;
1332
1333 if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
1334 if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
1335 link_info->nstr_bitmap_len = 2;
1336 else
1337 link_info->nstr_bitmap_len = 1;
1338 }
1339
1340 sta_info_len += link_info->nstr_bitmap_len;
1341
1342 if (sta_info_len > ml_len || sta_info_len != *pos ||
1343 sta_info_len > sub_elem_len) {
1344 wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
1345 goto out;
1346 }
1347
1348 /* skip the length */
1349 pos++;
1350 ml_len--;
1351
1352 /* get the link address */
1353 os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
1354 wpa_printf(MSG_DEBUG,
1355 "MLD: assoc: link id=%u, addr=" MACSTR,
1356 control & EHT_PER_STA_CTRL_LINK_ID_MSK,
1357 MAC2STR(link_info->peer_addr));
1358
1359 pos += ETH_ALEN;
1360 ml_len -= ETH_ALEN;
1361
1362 /* Get the NSTR bitmap */
1363 if (link_info->nstr_bitmap_len) {
1364 os_memcpy(link_info->nstr_bitmap, pos,
1365 link_info->nstr_bitmap_len);
1366 pos += link_info->nstr_bitmap_len;
1367 ml_len -= link_info->nstr_bitmap_len;
1368 }
1369
1370 sub_elem_len -= sta_info_len;
1371
1372 wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
1373 if (sub_elem_len > ml_len)
1374 goto out;
1375
1376 if (sub_elem_len > 2)
1377 link_info->capability = WPA_GET_LE16(pos);
1378
1379 pos += sub_elem_len;
1380 ml_len -= sub_elem_len;
1381
1382 wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
1383 ", nstr bitmap len=%u",
1384 control, MAC2STR(link_info->peer_addr),
1385 link_info->nstr_bitmap_len);
1386
1387 link_info->valid = true;
1388 }
1389
1390 if (ml_len) {
1391 wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
1392 ml_len);
1393 goto out;
1394 }
1395
1396 ret = hostapd_mld_validate_assoc_info(hapd, sta);
1397 out:
1398 wpabuf_free(mlbuf);
1399 if (ret) {
1400 os_memset(info, 0, sizeof(*info));
1401 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1402 }
1403
1404 return WLAN_STATUS_SUCCESS;
1405 }
1406