• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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