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