xref: /freebsd/contrib/wpa/src/ap/ieee802_11_eht.c (revision 71e72c9e91c4b8007a4292e09669e8b549c29e97)
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