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