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