xref: /freebsd/contrib/wpa/src/ap/beacon.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
3e28a4053SRui Paulo  * Copyright (c) 2002-2004, Instant802 Networks, Inc.
4e28a4053SRui Paulo  * Copyright (c) 2005-2006, Devicescape Software, Inc.
5f05cddf9SRui Paulo  * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
6e28a4053SRui Paulo  *
75b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
85b9c547cSRui Paulo  * See README for more details.
9e28a4053SRui Paulo  */
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "utils/includes.h"
12e28a4053SRui Paulo 
13e28a4053SRui Paulo #ifndef CONFIG_NATIVE_WINDOWS
14e28a4053SRui Paulo 
15e28a4053SRui Paulo #include "utils/common.h"
16e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
17e28a4053SRui Paulo #include "common/ieee802_11_common.h"
185b9c547cSRui Paulo #include "common/hw_features_common.h"
1985732ac8SCy Schubert #include "common/wpa_ctrl.h"
20*a90b9d01SCy Schubert #include "crypto/sha1.h"
21f05cddf9SRui Paulo #include "wps/wps_defs.h"
22f05cddf9SRui Paulo #include "p2p/p2p.h"
23e28a4053SRui Paulo #include "hostapd.h"
24e28a4053SRui Paulo #include "ieee802_11.h"
25e28a4053SRui Paulo #include "wpa_auth.h"
26e28a4053SRui Paulo #include "wmm.h"
27e28a4053SRui Paulo #include "ap_config.h"
28e28a4053SRui Paulo #include "sta_info.h"
29f05cddf9SRui Paulo #include "p2p_hostapd.h"
30f05cddf9SRui Paulo #include "ap_drv_ops.h"
31e28a4053SRui Paulo #include "beacon.h"
32f05cddf9SRui Paulo #include "hs20.h"
335b9c547cSRui Paulo #include "dfs.h"
34780fb4a2SCy Schubert #include "taxonomy.h"
3585732ac8SCy Schubert #include "ieee802_11_auth.h"
36e28a4053SRui Paulo 
37e28a4053SRui Paulo 
38f05cddf9SRui Paulo #ifdef NEED_AP_MLME
39f05cddf9SRui Paulo 
hostapd_eid_bss_load(struct hostapd_data * hapd,u8 * eid,size_t len)405b9c547cSRui Paulo static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
415b9c547cSRui Paulo {
425b9c547cSRui Paulo 	if (len < 2 + 5)
435b9c547cSRui Paulo 		return eid;
445b9c547cSRui Paulo 
455b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
465b9c547cSRui Paulo 	if (hapd->conf->bss_load_test_set) {
475b9c547cSRui Paulo 		*eid++ = WLAN_EID_BSS_LOAD;
485b9c547cSRui Paulo 		*eid++ = 5;
495b9c547cSRui Paulo 		os_memcpy(eid, hapd->conf->bss_load_test, 5);
505b9c547cSRui Paulo 		eid += 5;
515b9c547cSRui Paulo 		return eid;
525b9c547cSRui Paulo 	}
535b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
545b9c547cSRui Paulo 	if (hapd->conf->bss_load_update_period) {
555b9c547cSRui Paulo 		*eid++ = WLAN_EID_BSS_LOAD;
565b9c547cSRui Paulo 		*eid++ = 5;
575b9c547cSRui Paulo 		WPA_PUT_LE16(eid, hapd->num_sta);
585b9c547cSRui Paulo 		eid += 2;
595b9c547cSRui Paulo 		*eid++ = hapd->iface->channel_utilization;
605b9c547cSRui Paulo 		WPA_PUT_LE16(eid, 0); /* no available admission capabity */
615b9c547cSRui Paulo 		eid += 2;
625b9c547cSRui Paulo 	}
635b9c547cSRui Paulo 	return eid;
645b9c547cSRui Paulo }
655b9c547cSRui Paulo 
665b9c547cSRui Paulo 
ieee802_11_erp_info(struct hostapd_data * hapd)67e28a4053SRui Paulo static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
68e28a4053SRui Paulo {
69e28a4053SRui Paulo 	u8 erp = 0;
70e28a4053SRui Paulo 
71e28a4053SRui Paulo 	if (hapd->iface->current_mode == NULL ||
72e28a4053SRui Paulo 	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
73e28a4053SRui Paulo 		return 0;
74e28a4053SRui Paulo 
75e28a4053SRui Paulo 	if (hapd->iface->olbc)
76e28a4053SRui Paulo 		erp |= ERP_INFO_USE_PROTECTION;
77e28a4053SRui Paulo 	if (hapd->iface->num_sta_non_erp > 0) {
78e28a4053SRui Paulo 		erp |= ERP_INFO_NON_ERP_PRESENT |
79e28a4053SRui Paulo 			ERP_INFO_USE_PROTECTION;
80e28a4053SRui Paulo 	}
81e28a4053SRui Paulo 	if (hapd->iface->num_sta_no_short_preamble > 0 ||
82e28a4053SRui Paulo 	    hapd->iconf->preamble == LONG_PREAMBLE)
83e28a4053SRui Paulo 		erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
84e28a4053SRui Paulo 
85e28a4053SRui Paulo 	return erp;
86e28a4053SRui Paulo }
87e28a4053SRui Paulo 
88e28a4053SRui Paulo 
hostapd_eid_ds_params(struct hostapd_data * hapd,u8 * eid)89e28a4053SRui Paulo static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
90e28a4053SRui Paulo {
91*a90b9d01SCy Schubert 	enum hostapd_hw_mode hw_mode = hapd->iconf->hw_mode;
92*a90b9d01SCy Schubert 
93*a90b9d01SCy Schubert 	if (hw_mode != HOSTAPD_MODE_IEEE80211G &&
94*a90b9d01SCy Schubert 	    hw_mode != HOSTAPD_MODE_IEEE80211B)
95*a90b9d01SCy Schubert 		return eid;
96*a90b9d01SCy Schubert 
97e28a4053SRui Paulo 	*eid++ = WLAN_EID_DS_PARAMS;
98e28a4053SRui Paulo 	*eid++ = 1;
99e28a4053SRui Paulo 	*eid++ = hapd->iconf->channel;
100e28a4053SRui Paulo 	return eid;
101e28a4053SRui Paulo }
102e28a4053SRui Paulo 
103e28a4053SRui Paulo 
hostapd_eid_erp_info(struct hostapd_data * hapd,u8 * eid)104e28a4053SRui Paulo static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
105e28a4053SRui Paulo {
106e28a4053SRui Paulo 	if (hapd->iface->current_mode == NULL ||
107e28a4053SRui Paulo 	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
108e28a4053SRui Paulo 		return eid;
109e28a4053SRui Paulo 
110e28a4053SRui Paulo 	/* Set NonERP_present and use_protection bits if there
111e28a4053SRui Paulo 	 * are any associated NonERP stations. */
112e28a4053SRui Paulo 	/* TODO: use_protection bit can be set to zero even if
113e28a4053SRui Paulo 	 * there are NonERP stations present. This optimization
114e28a4053SRui Paulo 	 * might be useful if NonERP stations are "quiet".
115e28a4053SRui Paulo 	 * See 802.11g/D6 E-1 for recommended practice.
116e28a4053SRui Paulo 	 * In addition, Non ERP present might be set, if AP detects Non ERP
117e28a4053SRui Paulo 	 * operation on other APs. */
118e28a4053SRui Paulo 
119e28a4053SRui Paulo 	/* Add ERP Information element */
120e28a4053SRui Paulo 	*eid++ = WLAN_EID_ERP_INFO;
121e28a4053SRui Paulo 	*eid++ = 1;
122e28a4053SRui Paulo 	*eid++ = ieee802_11_erp_info(hapd);
123e28a4053SRui Paulo 
124e28a4053SRui Paulo 	return eid;
125e28a4053SRui Paulo }
126e28a4053SRui Paulo 
127e28a4053SRui Paulo 
hostapd_eid_pwr_constraint(struct hostapd_data * hapd,u8 * eid)1285b9c547cSRui Paulo static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
1295b9c547cSRui Paulo {
1305b9c547cSRui Paulo 	u8 *pos = eid;
1315b9c547cSRui Paulo 	u8 local_pwr_constraint = 0;
1325b9c547cSRui Paulo 	int dfs;
1335b9c547cSRui Paulo 
1345b9c547cSRui Paulo 	if (hapd->iface->current_mode == NULL ||
1355b9c547cSRui Paulo 	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
1365b9c547cSRui Paulo 		return eid;
1375b9c547cSRui Paulo 
1385b9c547cSRui Paulo 	/* Let host drivers add this IE if DFS support is offloaded */
1395b9c547cSRui Paulo 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1405b9c547cSRui Paulo 		return eid;
1415b9c547cSRui Paulo 
1425b9c547cSRui Paulo 	/*
1435b9c547cSRui Paulo 	 * There is no DFS support and power constraint was not directly
1445b9c547cSRui Paulo 	 * requested by config option.
1455b9c547cSRui Paulo 	 */
1465b9c547cSRui Paulo 	if (!hapd->iconf->ieee80211h &&
1475b9c547cSRui Paulo 	    hapd->iconf->local_pwr_constraint == -1)
1485b9c547cSRui Paulo 		return eid;
1495b9c547cSRui Paulo 
1505b9c547cSRui Paulo 	/* Check if DFS is required by regulatory. */
1515b9c547cSRui Paulo 	dfs = hostapd_is_dfs_required(hapd->iface);
1525b9c547cSRui Paulo 	if (dfs < 0) {
1535b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
1545b9c547cSRui Paulo 			   dfs);
1555b9c547cSRui Paulo 		dfs = 0;
1565b9c547cSRui Paulo 	}
1575b9c547cSRui Paulo 
1585b9c547cSRui Paulo 	if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
1595b9c547cSRui Paulo 		return eid;
1605b9c547cSRui Paulo 
1615b9c547cSRui Paulo 	/*
1625b9c547cSRui Paulo 	 * ieee80211h (DFS) is enabled so Power Constraint element shall
1635b9c547cSRui Paulo 	 * be added when running on DFS channel whenever local_pwr_constraint
1645b9c547cSRui Paulo 	 * is configured or not. In order to meet regulations when TPC is not
1655b9c547cSRui Paulo 	 * implemented using a transmit power that is below the legal maximum
1665b9c547cSRui Paulo 	 * (including any mitigation factor) should help. In this case,
1675b9c547cSRui Paulo 	 * indicate 3 dB below maximum allowed transmit power.
1685b9c547cSRui Paulo 	 */
1695b9c547cSRui Paulo 	if (hapd->iconf->local_pwr_constraint == -1)
1705b9c547cSRui Paulo 		local_pwr_constraint = 3;
1715b9c547cSRui Paulo 
1725b9c547cSRui Paulo 	/*
1735b9c547cSRui Paulo 	 * A STA that is not an AP shall use a transmit power less than or
1745b9c547cSRui Paulo 	 * equal to the local maximum transmit power level for the channel.
1755b9c547cSRui Paulo 	 * The local maximum transmit power can be calculated from the formula:
1765b9c547cSRui Paulo 	 * local max TX pwr = max TX pwr - local pwr constraint
1775b9c547cSRui Paulo 	 * Where max TX pwr is maximum transmit power level specified for
1785b9c547cSRui Paulo 	 * channel in Country element and local pwr constraint is specified
1795b9c547cSRui Paulo 	 * for channel in this Power Constraint element.
1805b9c547cSRui Paulo 	 */
1815b9c547cSRui Paulo 
1825b9c547cSRui Paulo 	/* Element ID */
1835b9c547cSRui Paulo 	*pos++ = WLAN_EID_PWR_CONSTRAINT;
1845b9c547cSRui Paulo 	/* Length */
1855b9c547cSRui Paulo 	*pos++ = 1;
1865b9c547cSRui Paulo 	/* Local Power Constraint */
1875b9c547cSRui Paulo 	if (local_pwr_constraint)
1885b9c547cSRui Paulo 		*pos++ = local_pwr_constraint;
1895b9c547cSRui Paulo 	else
1905b9c547cSRui Paulo 		*pos++ = hapd->iconf->local_pwr_constraint;
1915b9c547cSRui Paulo 
1925b9c547cSRui Paulo 	return pos;
1935b9c547cSRui Paulo }
1945b9c547cSRui Paulo 
1955b9c547cSRui Paulo 
hostapd_eid_country_add(struct hostapd_data * hapd,u8 * pos,u8 * end,int chan_spacing,struct hostapd_channel_data * start,struct hostapd_channel_data * prev)196*a90b9d01SCy Schubert static u8 * hostapd_eid_country_add(struct hostapd_data *hapd, u8 *pos,
197*a90b9d01SCy Schubert 				    u8 *end, int chan_spacing,
198e28a4053SRui Paulo 				    struct hostapd_channel_data *start,
199e28a4053SRui Paulo 				    struct hostapd_channel_data *prev)
200e28a4053SRui Paulo {
201e28a4053SRui Paulo 	if (end - pos < 3)
202e28a4053SRui Paulo 		return pos;
203e28a4053SRui Paulo 
204e28a4053SRui Paulo 	/* first channel number */
205e28a4053SRui Paulo 	*pos++ = start->chan;
206e28a4053SRui Paulo 	/* number of channels */
207e28a4053SRui Paulo 	*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
208e28a4053SRui Paulo 	/* maximum transmit power level */
209*a90b9d01SCy Schubert 	if (!is_6ghz_op_class(hapd->iconf->op_class))
210e28a4053SRui Paulo 		*pos++ = start->max_tx_power;
211*a90b9d01SCy Schubert 	else
212*a90b9d01SCy Schubert 		*pos++ = 0; /* Reserved when operating on the 6 GHz band */
213e28a4053SRui Paulo 
214e28a4053SRui Paulo 	return pos;
215e28a4053SRui Paulo }
216e28a4053SRui Paulo 
217e28a4053SRui Paulo 
hostapd_fill_subband_triplets(struct hostapd_data * hapd,u8 * pos,u8 * end)218*a90b9d01SCy Schubert static u8 * hostapd_fill_subband_triplets(struct hostapd_data *hapd, u8 *pos,
219*a90b9d01SCy Schubert 					    u8 *end)
220e28a4053SRui Paulo {
221e28a4053SRui Paulo 	int i;
222e28a4053SRui Paulo 	struct hostapd_hw_modes *mode;
223e28a4053SRui Paulo 	struct hostapd_channel_data *start, *prev;
224e28a4053SRui Paulo 	int chan_spacing = 1;
225e28a4053SRui Paulo 
226e28a4053SRui Paulo 	mode = hapd->iface->current_mode;
227e28a4053SRui Paulo 	if (mode->mode == HOSTAPD_MODE_IEEE80211A)
228e28a4053SRui Paulo 		chan_spacing = 4;
229e28a4053SRui Paulo 
230e28a4053SRui Paulo 	start = prev = NULL;
231e28a4053SRui Paulo 	for (i = 0; i < mode->num_channels; i++) {
232e28a4053SRui Paulo 		struct hostapd_channel_data *chan = &mode->channels[i];
233e28a4053SRui Paulo 		if (chan->flag & HOSTAPD_CHAN_DISABLED)
234e28a4053SRui Paulo 			continue;
235e28a4053SRui Paulo 		if (start && prev &&
236e28a4053SRui Paulo 		    prev->chan + chan_spacing == chan->chan &&
237e28a4053SRui Paulo 		    start->max_tx_power == chan->max_tx_power) {
238e28a4053SRui Paulo 			prev = chan;
239e28a4053SRui Paulo 			continue; /* can use same entry */
240e28a4053SRui Paulo 		}
241e28a4053SRui Paulo 
242*a90b9d01SCy Schubert 		if (start && prev)
243*a90b9d01SCy Schubert 			pos = hostapd_eid_country_add(hapd, pos, end,
244*a90b9d01SCy Schubert 						      chan_spacing,
245e28a4053SRui Paulo 						      start, prev);
246e28a4053SRui Paulo 
247e28a4053SRui Paulo 		/* Start new group */
248e28a4053SRui Paulo 		start = prev = chan;
249e28a4053SRui Paulo 	}
250e28a4053SRui Paulo 
251e28a4053SRui Paulo 	if (start) {
252*a90b9d01SCy Schubert 		pos = hostapd_eid_country_add(hapd, pos, end, chan_spacing,
253e28a4053SRui Paulo 					      start, prev);
254e28a4053SRui Paulo 	}
255e28a4053SRui Paulo 
256*a90b9d01SCy Schubert 	return pos;
257*a90b9d01SCy Schubert }
258*a90b9d01SCy Schubert 
259*a90b9d01SCy Schubert 
hostapd_eid_country(struct hostapd_data * hapd,u8 * eid,int max_len)260*a90b9d01SCy Schubert static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
261*a90b9d01SCy Schubert 				int max_len)
262*a90b9d01SCy Schubert {
263*a90b9d01SCy Schubert 	u8 *pos = eid;
264*a90b9d01SCy Schubert 	u8 *end = eid + max_len;
265*a90b9d01SCy Schubert 
266*a90b9d01SCy Schubert 	if (!hapd->iconf->ieee80211d || max_len < 6 ||
267*a90b9d01SCy Schubert 	    hapd->iface->current_mode == NULL)
268*a90b9d01SCy Schubert 		return eid;
269*a90b9d01SCy Schubert 
270*a90b9d01SCy Schubert 	*pos++ = WLAN_EID_COUNTRY;
271*a90b9d01SCy Schubert 	pos++; /* length will be set later */
272*a90b9d01SCy Schubert 	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
273*a90b9d01SCy Schubert 	pos += 3;
274*a90b9d01SCy Schubert 
275*a90b9d01SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
276*a90b9d01SCy Schubert 		/* Force the third octet of the country string to indicate
277*a90b9d01SCy Schubert 		 * Global Operating Class (Table E-4) */
278*a90b9d01SCy Schubert 		eid[4] = 0x04;
279*a90b9d01SCy Schubert 
280*a90b9d01SCy Schubert 		/* Operating Triplet field */
281*a90b9d01SCy Schubert 		/* Operating Extension Identifier (>= 201 to indicate this is
282*a90b9d01SCy Schubert 		 * not a Subband Triplet field) */
283*a90b9d01SCy Schubert 		*pos++ = 201;
284*a90b9d01SCy Schubert 		/* Operating Class */
285*a90b9d01SCy Schubert 		*pos++ = hapd->iconf->op_class;
286*a90b9d01SCy Schubert 		/* Coverage Class */
287*a90b9d01SCy Schubert 		*pos++ = 0;
288*a90b9d01SCy Schubert 		/* Subband Triplets are required only for the 20 MHz case */
289*a90b9d01SCy Schubert 		if (hapd->iconf->op_class == 131 ||
290*a90b9d01SCy Schubert 		    hapd->iconf->op_class == 136)
291*a90b9d01SCy Schubert 			pos = hostapd_fill_subband_triplets(hapd, pos, end);
292*a90b9d01SCy Schubert 	} else {
293*a90b9d01SCy Schubert 		pos = hostapd_fill_subband_triplets(hapd, pos, end);
294*a90b9d01SCy Schubert 	}
295*a90b9d01SCy Schubert 
296e28a4053SRui Paulo 	if ((pos - eid) & 1) {
297e28a4053SRui Paulo 		if (end - pos < 1)
298e28a4053SRui Paulo 			return eid;
299e28a4053SRui Paulo 		*pos++ = 0; /* pad for 16-bit alignment */
300e28a4053SRui Paulo 	}
301e28a4053SRui Paulo 
302e28a4053SRui Paulo 	eid[1] = (pos - eid) - 2;
303e28a4053SRui Paulo 
304e28a4053SRui Paulo 	return pos;
305e28a4053SRui Paulo }
306e28a4053SRui Paulo 
307e28a4053SRui Paulo 
hostapd_wpa_ie(struct hostapd_data * hapd,u8 eid)308c1d255d3SCy Schubert const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid)
309c1d255d3SCy Schubert {
310c1d255d3SCy Schubert 	const u8 *ies;
311c1d255d3SCy Schubert 	size_t ies_len;
312c1d255d3SCy Schubert 
313c1d255d3SCy Schubert 	ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
314c1d255d3SCy Schubert 	if (!ies)
315c1d255d3SCy Schubert 		return NULL;
316c1d255d3SCy Schubert 
317c1d255d3SCy Schubert 	return get_ie(ies, ies_len, eid);
318c1d255d3SCy Schubert }
319c1d255d3SCy Schubert 
320c1d255d3SCy Schubert 
hostapd_vendor_wpa_ie(struct hostapd_data * hapd,u32 vendor_type)321c1d255d3SCy Schubert static const u8 * hostapd_vendor_wpa_ie(struct hostapd_data *hapd,
322c1d255d3SCy Schubert 					u32 vendor_type)
323c1d255d3SCy Schubert {
324c1d255d3SCy Schubert 	const u8 *ies;
325c1d255d3SCy Schubert 	size_t ies_len;
326c1d255d3SCy Schubert 
327c1d255d3SCy Schubert 	ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
328c1d255d3SCy Schubert 	if (!ies)
329c1d255d3SCy Schubert 		return NULL;
330c1d255d3SCy Schubert 
331c1d255d3SCy Schubert 	return get_vendor_ie(ies, ies_len, vendor_type);
332c1d255d3SCy Schubert }
333c1d255d3SCy Schubert 
334c1d255d3SCy Schubert 
hostapd_get_rsne(struct hostapd_data * hapd,u8 * pos,size_t len)335c1d255d3SCy Schubert static u8 * hostapd_get_rsne(struct hostapd_data *hapd, u8 *pos, size_t len)
336e28a4053SRui Paulo {
337e28a4053SRui Paulo 	const u8 *ie;
338e28a4053SRui Paulo 
339c1d255d3SCy Schubert 	ie = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
340c1d255d3SCy Schubert 	if (!ie || 2U + ie[1] > len)
341c1d255d3SCy Schubert 		return pos;
342e28a4053SRui Paulo 
343c1d255d3SCy Schubert 	os_memcpy(pos, ie, 2 + ie[1]);
344c1d255d3SCy Schubert 	return pos + 2 + ie[1];
345c1d255d3SCy Schubert }
346c1d255d3SCy Schubert 
347c1d255d3SCy Schubert 
hostapd_get_mde(struct hostapd_data * hapd,u8 * pos,size_t len)348c1d255d3SCy Schubert static u8 * hostapd_get_mde(struct hostapd_data *hapd, u8 *pos, size_t len)
349c1d255d3SCy Schubert {
350c1d255d3SCy Schubert 	const u8 *ie;
351c1d255d3SCy Schubert 
352c1d255d3SCy Schubert 	ie = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
353c1d255d3SCy Schubert 	if (!ie || 2U + ie[1] > len)
354c1d255d3SCy Schubert 		return pos;
355c1d255d3SCy Schubert 
356c1d255d3SCy Schubert 	os_memcpy(pos, ie, 2 + ie[1]);
357c1d255d3SCy Schubert 	return pos + 2 + ie[1];
358c1d255d3SCy Schubert }
359c1d255d3SCy Schubert 
360c1d255d3SCy Schubert 
hostapd_get_rsnxe(struct hostapd_data * hapd,u8 * pos,size_t len)361c1d255d3SCy Schubert static u8 * hostapd_get_rsnxe(struct hostapd_data *hapd, u8 *pos, size_t len)
362c1d255d3SCy Schubert {
363c1d255d3SCy Schubert 	const u8 *ie;
364c1d255d3SCy Schubert 
365c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
366c1d255d3SCy Schubert 	if (hapd->conf->no_beacon_rsnxe) {
367c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Do not add RSNXE into Beacon");
368c1d255d3SCy Schubert 		return pos;
369c1d255d3SCy Schubert 	}
370c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
371c1d255d3SCy Schubert 	ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
372c1d255d3SCy Schubert 	if (!ie || 2U + ie[1] > len)
373c1d255d3SCy Schubert 		return pos;
374c1d255d3SCy Schubert 
375c1d255d3SCy Schubert 	os_memcpy(pos, ie, 2 + ie[1]);
376c1d255d3SCy Schubert 	return pos + 2 + ie[1];
377c1d255d3SCy Schubert }
378c1d255d3SCy Schubert 
379c1d255d3SCy Schubert 
hostapd_get_wpa_ie(struct hostapd_data * hapd,u8 * pos,size_t len)380c1d255d3SCy Schubert static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
381c1d255d3SCy Schubert {
382c1d255d3SCy Schubert 	const u8 *ie;
383c1d255d3SCy Schubert 
384c1d255d3SCy Schubert 	ie = hostapd_vendor_wpa_ie(hapd, WPA_IE_VENDOR_TYPE);
385c1d255d3SCy Schubert 	if (!ie || 2U + ie[1] > len)
386c1d255d3SCy Schubert 		return pos;
387c1d255d3SCy Schubert 
388c1d255d3SCy Schubert 	os_memcpy(pos, ie, 2 + ie[1]);
389c1d255d3SCy Schubert 	return pos + 2 + ie[1];
390c1d255d3SCy Schubert }
391c1d255d3SCy Schubert 
392c1d255d3SCy Schubert 
hostapd_get_osen_ie(struct hostapd_data * hapd,u8 * pos,size_t len)393c1d255d3SCy Schubert static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
394c1d255d3SCy Schubert {
395c1d255d3SCy Schubert 	const u8 *ie;
396c1d255d3SCy Schubert 
397c1d255d3SCy Schubert 	ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
398c1d255d3SCy Schubert 	if (!ie || 2U + ie[1] > len)
399c1d255d3SCy Schubert 		return pos;
400c1d255d3SCy Schubert 
401c1d255d3SCy Schubert 	os_memcpy(pos, ie, 2 + ie[1]);
402c1d255d3SCy Schubert 	return pos + 2 + ie[1];
403e28a4053SRui Paulo }
404e28a4053SRui Paulo 
405e28a4053SRui Paulo 
hostapd_eid_csa(struct hostapd_data * hapd,u8 * eid)4065b9c547cSRui Paulo static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
4075b9c547cSRui Paulo {
408780fb4a2SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
409780fb4a2SCy Schubert 	if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
4105b9c547cSRui Paulo 		return eid;
411780fb4a2SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
4125b9c547cSRui Paulo 
413780fb4a2SCy Schubert 	if (!hapd->cs_freq_params.channel)
4145b9c547cSRui Paulo 		return eid;
4155b9c547cSRui Paulo 
4165b9c547cSRui Paulo 	*eid++ = WLAN_EID_CHANNEL_SWITCH;
4175b9c547cSRui Paulo 	*eid++ = 3;
4185b9c547cSRui Paulo 	*eid++ = hapd->cs_block_tx;
419780fb4a2SCy Schubert 	*eid++ = hapd->cs_freq_params.channel;
4205b9c547cSRui Paulo 	*eid++ = hapd->cs_count;
4215b9c547cSRui Paulo 
4225b9c547cSRui Paulo 	return eid;
4235b9c547cSRui Paulo }
4245b9c547cSRui Paulo 
4255b9c547cSRui Paulo 
hostapd_eid_ecsa(struct hostapd_data * hapd,u8 * eid)426780fb4a2SCy Schubert static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
4275b9c547cSRui Paulo {
428780fb4a2SCy Schubert 	if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
4295b9c547cSRui Paulo 		return eid;
4305b9c547cSRui Paulo 
431780fb4a2SCy Schubert 	*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
432780fb4a2SCy Schubert 	*eid++ = 4;
433780fb4a2SCy Schubert 	*eid++ = hapd->cs_block_tx;
434780fb4a2SCy Schubert 	*eid++ = hapd->iface->cs_oper_class;
435780fb4a2SCy Schubert 	*eid++ = hapd->cs_freq_params.channel;
436780fb4a2SCy Schubert 	*eid++ = hapd->cs_count;
4375b9c547cSRui Paulo 
4385b9c547cSRui Paulo 	return eid;
4395b9c547cSRui Paulo }
4405b9c547cSRui Paulo 
4415b9c547cSRui Paulo 
hostapd_eid_supported_op_classes(struct hostapd_data * hapd,u8 * eid)442780fb4a2SCy Schubert static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
4435b9c547cSRui Paulo {
444780fb4a2SCy Schubert 	u8 op_class, channel;
4455b9c547cSRui Paulo 
446780fb4a2SCy Schubert 	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
447780fb4a2SCy Schubert 	    !hapd->iface->freq)
448780fb4a2SCy Schubert 		return eid;
4495b9c547cSRui Paulo 
450780fb4a2SCy Schubert 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
451780fb4a2SCy Schubert 					  hapd->iconf->secondary_channel,
452206b73d0SCy Schubert 					  hostapd_get_oper_chwidth(hapd->iconf),
453780fb4a2SCy Schubert 					  &op_class, &channel) ==
454780fb4a2SCy Schubert 	    NUM_HOSTAPD_MODES)
455780fb4a2SCy Schubert 		return eid;
4565b9c547cSRui Paulo 
457780fb4a2SCy Schubert 	*eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
458780fb4a2SCy Schubert 	*eid++ = 2;
4595b9c547cSRui Paulo 
460780fb4a2SCy Schubert 	/* Current Operating Class */
461780fb4a2SCy Schubert 	*eid++ = op_class;
462780fb4a2SCy Schubert 
463780fb4a2SCy Schubert 	/* TODO: Advertise all the supported operating classes */
464780fb4a2SCy Schubert 	*eid++ = 0;
465780fb4a2SCy Schubert 
466780fb4a2SCy Schubert 	return eid;
4675b9c547cSRui Paulo }
4685b9c547cSRui Paulo 
4695b9c547cSRui Paulo 
470*a90b9d01SCy Schubert static int
ieee802_11_build_ap_params_mbssid(struct hostapd_data * hapd,struct wpa_driver_ap_params * params)471*a90b9d01SCy Schubert ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
472*a90b9d01SCy Schubert 				  struct wpa_driver_ap_params *params)
473e28a4053SRui Paulo {
474*a90b9d01SCy Schubert 	struct hostapd_iface *iface = hapd->iface;
475*a90b9d01SCy Schubert 	struct hostapd_data *tx_bss;
476*a90b9d01SCy Schubert 	size_t len, rnr_len = 0;
477*a90b9d01SCy Schubert 	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
478*a90b9d01SCy Schubert 	u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
479*a90b9d01SCy Schubert 	size_t i;
480e28a4053SRui Paulo 
481*a90b9d01SCy Schubert 	if (!iface->mbssid_max_interfaces ||
482*a90b9d01SCy Schubert 	    iface->num_bss > iface->mbssid_max_interfaces ||
483*a90b9d01SCy Schubert 	    (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
484*a90b9d01SCy Schubert 	     !iface->ema_max_periodicity))
485*a90b9d01SCy Schubert 		goto fail;
486*a90b9d01SCy Schubert 
487*a90b9d01SCy Schubert 	/* Make sure bss->xrates_supported is set for all BSSs to know whether
488*a90b9d01SCy Schubert 	 * it need to be non-inherited. */
489*a90b9d01SCy Schubert 	for (i = 0; i < iface->num_bss; i++) {
490*a90b9d01SCy Schubert 		u8 buf[100];
491*a90b9d01SCy Schubert 
492*a90b9d01SCy Schubert 		hostapd_eid_ext_supp_rates(iface->bss[i], buf);
493*a90b9d01SCy Schubert 	}
494*a90b9d01SCy Schubert 
495*a90b9d01SCy Schubert 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
496*a90b9d01SCy Schubert 	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
497*a90b9d01SCy Schubert 				     NULL, 0, &rnr_len);
498*a90b9d01SCy Schubert 	if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
499*a90b9d01SCy Schubert 		     elem_count > iface->ema_max_periodicity))
500*a90b9d01SCy Schubert 		goto fail;
501*a90b9d01SCy Schubert 
502*a90b9d01SCy Schubert 	elem = os_zalloc(len);
503*a90b9d01SCy Schubert 	if (!elem)
504*a90b9d01SCy Schubert 		goto fail;
505*a90b9d01SCy Schubert 
506*a90b9d01SCy Schubert 	elem_offset = os_zalloc(elem_count * sizeof(u8 *));
507*a90b9d01SCy Schubert 	if (!elem_offset)
508*a90b9d01SCy Schubert 		goto fail;
509*a90b9d01SCy Schubert 
510*a90b9d01SCy Schubert 	if (rnr_len) {
511*a90b9d01SCy Schubert 		rnr_elem = os_zalloc(rnr_len);
512*a90b9d01SCy Schubert 		if (!rnr_elem)
513*a90b9d01SCy Schubert 			goto fail;
514*a90b9d01SCy Schubert 
515*a90b9d01SCy Schubert 		rnr_elem_offset = os_calloc(elem_count + 1, sizeof(u8 *));
516*a90b9d01SCy Schubert 		if (!rnr_elem_offset)
517*a90b9d01SCy Schubert 			goto fail;
518*a90b9d01SCy Schubert 	}
519*a90b9d01SCy Schubert 
520*a90b9d01SCy Schubert 	end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
521*a90b9d01SCy Schubert 				 elem_count, elem_offset, NULL, 0, rnr_elem,
522*a90b9d01SCy Schubert 				 &rnr_elem_count, rnr_elem_offset, rnr_len);
523*a90b9d01SCy Schubert 
524*a90b9d01SCy Schubert 	params->mbssid_tx_iface = tx_bss->conf->iface;
525*a90b9d01SCy Schubert 	params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
526*a90b9d01SCy Schubert 	params->mbssid_elem = elem;
527*a90b9d01SCy Schubert 	params->mbssid_elem_len = end - elem;
528*a90b9d01SCy Schubert 	params->mbssid_elem_count = elem_count;
529*a90b9d01SCy Schubert 	params->mbssid_elem_offset = elem_offset;
530*a90b9d01SCy Schubert 	params->rnr_elem = rnr_elem;
531*a90b9d01SCy Schubert 	params->rnr_elem_len = rnr_len;
532*a90b9d01SCy Schubert 	params->rnr_elem_count = rnr_elem_count;
533*a90b9d01SCy Schubert 	params->rnr_elem_offset = rnr_elem_offset;
534*a90b9d01SCy Schubert 	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
535*a90b9d01SCy Schubert 		params->ema = true;
536*a90b9d01SCy Schubert 
537*a90b9d01SCy Schubert 	return 0;
538*a90b9d01SCy Schubert 
539*a90b9d01SCy Schubert fail:
540*a90b9d01SCy Schubert 	os_free(rnr_elem);
541*a90b9d01SCy Schubert 	os_free(rnr_elem_offset);
542*a90b9d01SCy Schubert 	os_free(elem_offset);
543*a90b9d01SCy Schubert 	os_free(elem);
544*a90b9d01SCy Schubert 	wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
545*a90b9d01SCy Schubert 	return -1;
546*a90b9d01SCy Schubert }
547*a90b9d01SCy Schubert 
548*a90b9d01SCy Schubert 
hostapd_eid_mbssid_config(struct hostapd_data * hapd,u8 * eid,u8 mbssid_elem_count)549*a90b9d01SCy Schubert static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
550*a90b9d01SCy Schubert 				      u8 mbssid_elem_count)
551*a90b9d01SCy Schubert {
552*a90b9d01SCy Schubert 	struct hostapd_iface *iface = hapd->iface;
553*a90b9d01SCy Schubert 
554*a90b9d01SCy Schubert 	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
555*a90b9d01SCy Schubert 		*eid++ = WLAN_EID_EXTENSION;
556*a90b9d01SCy Schubert 		*eid++ = 3;
557*a90b9d01SCy Schubert 		*eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
558*a90b9d01SCy Schubert 		*eid++ = iface->num_bss;
559*a90b9d01SCy Schubert 		*eid++ = mbssid_elem_count;
560*a90b9d01SCy Schubert 	}
561*a90b9d01SCy Schubert 
562*a90b9d01SCy Schubert 	return eid;
563*a90b9d01SCy Schubert }
564*a90b9d01SCy Schubert 
565*a90b9d01SCy Schubert 
he_elem_len(struct hostapd_data * hapd)566*a90b9d01SCy Schubert static size_t he_elem_len(struct hostapd_data *hapd)
567*a90b9d01SCy Schubert {
568*a90b9d01SCy Schubert 	size_t len = 0;
569*a90b9d01SCy Schubert 
570*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AX
571*a90b9d01SCy Schubert 	if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
572*a90b9d01SCy Schubert 		return len;
573*a90b9d01SCy Schubert 
574*a90b9d01SCy Schubert 	len += 3 + sizeof(struct ieee80211_he_capabilities) +
575*a90b9d01SCy Schubert 		3 + sizeof(struct ieee80211_he_operation) +
576*a90b9d01SCy Schubert 		3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
577*a90b9d01SCy Schubert 		3 + sizeof(struct ieee80211_spatial_reuse);
578*a90b9d01SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
579*a90b9d01SCy Schubert 		len += sizeof(struct ieee80211_he_6ghz_oper_info) +
580*a90b9d01SCy Schubert 			3 + sizeof(struct ieee80211_he_6ghz_band_cap);
581*a90b9d01SCy Schubert 		/* An additional Transmit Power Envelope element for
582*a90b9d01SCy Schubert 		 * subordinate client */
583*a90b9d01SCy Schubert 		if (he_reg_is_indoor(hapd->iconf->he_6ghz_reg_pwr_type))
584*a90b9d01SCy Schubert 			len += 4;
585*a90b9d01SCy Schubert 
586*a90b9d01SCy Schubert 		/* An additional Transmit Power Envelope element for
587*a90b9d01SCy Schubert 		 * default client with unit interpretation of regulatory
588*a90b9d01SCy Schubert 		 * client EIRP */
589*a90b9d01SCy Schubert 		if (hapd->iconf->reg_def_cli_eirp != -1 &&
590*a90b9d01SCy Schubert 		    he_reg_is_sp(hapd->iconf->he_6ghz_reg_pwr_type))
591*a90b9d01SCy Schubert 			len += 4;
592*a90b9d01SCy Schubert 	}
593*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AX */
594*a90b9d01SCy Schubert 
595*a90b9d01SCy Schubert 	return len;
596*a90b9d01SCy Schubert }
597*a90b9d01SCy Schubert 
598*a90b9d01SCy Schubert 
599*a90b9d01SCy Schubert struct probe_resp_params {
600*a90b9d01SCy Schubert 	const struct ieee80211_mgmt *req;
601*a90b9d01SCy Schubert 	bool is_p2p;
602*a90b9d01SCy Schubert 
603*a90b9d01SCy Schubert 	/* Generated IEs will be included inside an ML element */
604*a90b9d01SCy Schubert 	bool is_ml_sta_info;
605*a90b9d01SCy Schubert 	struct hostapd_data *mld_ap;
606*a90b9d01SCy Schubert 	struct mld_info *mld_info;
607*a90b9d01SCy Schubert 
608*a90b9d01SCy Schubert 	struct ieee80211_mgmt *resp;
609*a90b9d01SCy Schubert 	size_t resp_len;
610*a90b9d01SCy Schubert 	u8 *csa_pos;
611*a90b9d01SCy Schubert 	u8 *ecsa_pos;
612*a90b9d01SCy Schubert 	const u8 *known_bss;
613*a90b9d01SCy Schubert 	u8 known_bss_len;
614*a90b9d01SCy Schubert 
615*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AX
616*a90b9d01SCy Schubert 	u8 *cca_pos;
617*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AX */
618*a90b9d01SCy Schubert };
619*a90b9d01SCy Schubert 
620*a90b9d01SCy Schubert 
hostapd_free_probe_resp_params(struct probe_resp_params * params)621*a90b9d01SCy Schubert static void hostapd_free_probe_resp_params(struct probe_resp_params *params)
622*a90b9d01SCy Schubert {
623*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
624*a90b9d01SCy Schubert 	if (!params)
625*a90b9d01SCy Schubert 		return;
626*a90b9d01SCy Schubert 	ap_sta_free_sta_profile(params->mld_info);
627*a90b9d01SCy Schubert 	os_free(params->mld_info);
628*a90b9d01SCy Schubert 	params->mld_info = NULL;
629*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
630*a90b9d01SCy Schubert }
631*a90b9d01SCy Schubert 
632*a90b9d01SCy Schubert 
hostapd_probe_resp_elems_len(struct hostapd_data * hapd,struct probe_resp_params * params)633*a90b9d01SCy Schubert static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
634*a90b9d01SCy Schubert 					   struct probe_resp_params *params)
635*a90b9d01SCy Schubert {
636*a90b9d01SCy Schubert 	size_t buflen = 0;
637*a90b9d01SCy Schubert 
638e28a4053SRui Paulo #ifdef CONFIG_WPS
639e28a4053SRui Paulo 	if (hapd->wps_probe_resp_ie)
640e28a4053SRui Paulo 		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
641e28a4053SRui Paulo #endif /* CONFIG_WPS */
642f05cddf9SRui Paulo #ifdef CONFIG_P2P
643f05cddf9SRui Paulo 	if (hapd->p2p_probe_resp_ie)
644f05cddf9SRui Paulo 		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
645f05cddf9SRui Paulo #endif /* CONFIG_P2P */
646325151a3SRui Paulo #ifdef CONFIG_FST
647325151a3SRui Paulo 	if (hapd->iface->fst_ies)
648325151a3SRui Paulo 		buflen += wpabuf_len(hapd->iface->fst_ies);
649325151a3SRui Paulo #endif /* CONFIG_FST */
650f05cddf9SRui Paulo 	if (hapd->conf->vendor_elements)
651f05cddf9SRui Paulo 		buflen += wpabuf_len(hapd->conf->vendor_elements);
652*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
653*a90b9d01SCy Schubert 	if (hapd->conf->presp_elements)
654*a90b9d01SCy Schubert 		buflen += wpabuf_len(hapd->conf->presp_elements);
655*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
6565b9c547cSRui Paulo 	if (hapd->conf->vendor_vht) {
6575b9c547cSRui Paulo 		buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
6585b9c547cSRui Paulo 			2 + sizeof(struct ieee80211_vht_operation);
6595b9c547cSRui Paulo 	}
660780fb4a2SCy Schubert 
661*a90b9d01SCy Schubert 	buflen += he_elem_len(hapd);
66285732ac8SCy Schubert 
663*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
664*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
665*a90b9d01SCy Schubert 		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
666*a90b9d01SCy Schubert 		buflen += 3 + sizeof(struct ieee80211_eht_operation);
667*a90b9d01SCy Schubert 		if (hapd->iconf->punct_bitmap)
668*a90b9d01SCy Schubert 			buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
669*a90b9d01SCy Schubert 
670*a90b9d01SCy Schubert 		if (!params->is_ml_sta_info && hapd->conf->mld_ap) {
671*a90b9d01SCy Schubert 			struct hostapd_data *ml_elem_ap =
672*a90b9d01SCy Schubert 				params->mld_ap ? params->mld_ap : hapd;
673*a90b9d01SCy Schubert 
674*a90b9d01SCy Schubert 			buflen += hostapd_eid_eht_ml_beacon_len(
675*a90b9d01SCy Schubert 				ml_elem_ap, params->mld_info, !!params->mld_ap);
676*a90b9d01SCy Schubert 		}
677*a90b9d01SCy Schubert 	}
678*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
679*a90b9d01SCy Schubert 
680*a90b9d01SCy Schubert 	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
681*a90b9d01SCy Schubert 					 params->known_bss,
682*a90b9d01SCy Schubert 					 params->known_bss_len, NULL);
683*a90b9d01SCy Schubert 	if (!params->is_ml_sta_info)
684*a90b9d01SCy Schubert 		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP,
685*a90b9d01SCy Schubert 					      true);
686780fb4a2SCy Schubert 	buflen += hostapd_mbo_ie_len(hapd);
68785732ac8SCy Schubert 	buflen += hostapd_eid_owe_trans_len(hapd);
688c1d255d3SCy Schubert 	buflen += hostapd_eid_dpp_cc_len(hapd);
689780fb4a2SCy Schubert 
690*a90b9d01SCy Schubert 	return buflen;
691*a90b9d01SCy Schubert }
692f05cddf9SRui Paulo 
693e28a4053SRui Paulo 
hostapd_probe_resp_fill_elems(struct hostapd_data * hapd,struct probe_resp_params * params,u8 * pos,size_t len)694*a90b9d01SCy Schubert static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
695*a90b9d01SCy Schubert 					  struct probe_resp_params *params,
696*a90b9d01SCy Schubert 					  u8 *pos, size_t len)
697*a90b9d01SCy Schubert {
698*a90b9d01SCy Schubert 	u8 *csa_pos;
699*a90b9d01SCy Schubert 	u8 *epos;
700e28a4053SRui Paulo 
701*a90b9d01SCy Schubert 	epos = pos + len;
702e28a4053SRui Paulo 
703*a90b9d01SCy Schubert 	if (!params->is_ml_sta_info) {
704e28a4053SRui Paulo 		*pos++ = WLAN_EID_SSID;
705f05cddf9SRui Paulo 		*pos++ = hapd->conf->ssid.ssid_len;
706*a90b9d01SCy Schubert 		os_memcpy(pos, hapd->conf->ssid.ssid,
707*a90b9d01SCy Schubert 			  hapd->conf->ssid.ssid_len);
708f05cddf9SRui Paulo 		pos += hapd->conf->ssid.ssid_len;
709*a90b9d01SCy Schubert 	}
710e28a4053SRui Paulo 
711e28a4053SRui Paulo 	/* Supported rates */
712e28a4053SRui Paulo 	pos = hostapd_eid_supp_rates(hapd, pos);
713e28a4053SRui Paulo 
714e28a4053SRui Paulo 	/* DS Params */
715e28a4053SRui Paulo 	pos = hostapd_eid_ds_params(hapd, pos);
716e28a4053SRui Paulo 
717e28a4053SRui Paulo 	pos = hostapd_eid_country(hapd, pos, epos - pos);
718e28a4053SRui Paulo 
7195b9c547cSRui Paulo 	/* Power Constraint element */
7205b9c547cSRui Paulo 	pos = hostapd_eid_pwr_constraint(hapd, pos);
7215b9c547cSRui Paulo 
722*a90b9d01SCy Schubert 	/*
723*a90b9d01SCy Schubert 	 * CSA IE
724*a90b9d01SCy Schubert 	 * TODO: This should be included inside the ML sta profile
725*a90b9d01SCy Schubert 	 */
726*a90b9d01SCy Schubert 	if (!params->is_ml_sta_info) {
727780fb4a2SCy Schubert 		csa_pos = hostapd_eid_csa(hapd, pos);
728780fb4a2SCy Schubert 		if (csa_pos != pos)
729*a90b9d01SCy Schubert 			params->csa_pos = csa_pos - 1;
730*a90b9d01SCy Schubert 		else
731*a90b9d01SCy Schubert 			params->csa_pos = NULL;
732780fb4a2SCy Schubert 		pos = csa_pos;
733*a90b9d01SCy Schubert 	}
734780fb4a2SCy Schubert 
735e28a4053SRui Paulo 	/* ERP Information element */
736e28a4053SRui Paulo 	pos = hostapd_eid_erp_info(hapd, pos);
737e28a4053SRui Paulo 
738e28a4053SRui Paulo 	/* Extended supported rates */
739e28a4053SRui Paulo 	pos = hostapd_eid_ext_supp_rates(hapd, pos);
740e28a4053SRui Paulo 
741c1d255d3SCy Schubert 	pos = hostapd_get_rsne(hapd, pos, epos - pos);
7425b9c547cSRui Paulo 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
743*a90b9d01SCy Schubert 	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
744*a90b9d01SCy Schubert 				 NULL, params->known_bss, params->known_bss_len,
745*a90b9d01SCy Schubert 				 NULL, NULL, NULL, 0);
7465b9c547cSRui Paulo 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
747c1d255d3SCy Schubert 	pos = hostapd_get_mde(hapd, pos, epos - pos);
7485b9c547cSRui Paulo 
749*a90b9d01SCy Schubert 	/*
750*a90b9d01SCy Schubert 	 * eCSA IE
751*a90b9d01SCy Schubert 	 * TODO: This should be included inside the ML sta profile
752*a90b9d01SCy Schubert 	 */
753*a90b9d01SCy Schubert 	if (!params->is_ml_sta_info) {
754780fb4a2SCy Schubert 		csa_pos = hostapd_eid_ecsa(hapd, pos);
755780fb4a2SCy Schubert 		if (csa_pos != pos)
756*a90b9d01SCy Schubert 			params->ecsa_pos = csa_pos - 1;
757*a90b9d01SCy Schubert 		else
758*a90b9d01SCy Schubert 			params->ecsa_pos = NULL;
759780fb4a2SCy Schubert 		pos = csa_pos;
760*a90b9d01SCy Schubert 	}
761780fb4a2SCy Schubert 
762780fb4a2SCy Schubert 	pos = hostapd_eid_supported_op_classes(hapd, pos);
763e28a4053SRui Paulo 	pos = hostapd_eid_ht_capabilities(hapd, pos);
764e28a4053SRui Paulo 	pos = hostapd_eid_ht_operation(hapd, pos);
765e28a4053SRui Paulo 
766*a90b9d01SCy Schubert 	/* Probe Response frames always include all non-TX profiles except
767*a90b9d01SCy Schubert 	 * when a list of known BSSes is included in the Probe Request frame. */
768*a90b9d01SCy Schubert 	pos = hostapd_eid_ext_capab(hapd, pos,
769*a90b9d01SCy Schubert 				    hapd->iconf->mbssid >= MBSSID_ENABLED &&
770*a90b9d01SCy Schubert 				    !params->known_bss_len);
771f05cddf9SRui Paulo 
772f05cddf9SRui Paulo 	pos = hostapd_eid_time_adv(hapd, pos);
773f05cddf9SRui Paulo 	pos = hostapd_eid_time_zone(hapd, pos);
774f05cddf9SRui Paulo 
775f05cddf9SRui Paulo 	pos = hostapd_eid_interworking(hapd, pos);
776f05cddf9SRui Paulo 	pos = hostapd_eid_adv_proto(hapd, pos);
777f05cddf9SRui Paulo 	pos = hostapd_eid_roaming_consortium(hapd, pos);
778f05cddf9SRui Paulo 
779325151a3SRui Paulo #ifdef CONFIG_FST
780325151a3SRui Paulo 	if (hapd->iface->fst_ies) {
781325151a3SRui Paulo 		os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
782325151a3SRui Paulo 			  wpabuf_len(hapd->iface->fst_ies));
783325151a3SRui Paulo 		pos += wpabuf_len(hapd->iface->fst_ies);
784325151a3SRui Paulo 	}
785325151a3SRui Paulo #endif /* CONFIG_FST */
786325151a3SRui Paulo 
787f05cddf9SRui Paulo #ifdef CONFIG_IEEE80211AC
788c1d255d3SCy Schubert 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
789c1d255d3SCy Schubert 	    !is_6ghz_op_class(hapd->iconf->op_class)) {
790780fb4a2SCy Schubert 		pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
791f05cddf9SRui Paulo 		pos = hostapd_eid_vht_operation(hapd, pos);
792780fb4a2SCy Schubert 		pos = hostapd_eid_txpower_envelope(hapd, pos);
7935b9c547cSRui Paulo 	}
79485732ac8SCy Schubert #endif /* CONFIG_IEEE80211AC */
79585732ac8SCy Schubert 
796c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX
797c1d255d3SCy Schubert 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
798c1d255d3SCy Schubert 	    is_6ghz_op_class(hapd->iconf->op_class))
799c1d255d3SCy Schubert 		pos = hostapd_eid_txpower_envelope(hapd, pos);
800c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */
801c1d255d3SCy Schubert 
802c1d255d3SCy Schubert 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
803c1d255d3SCy Schubert 
804*a90b9d01SCy Schubert 	if (!params->is_ml_sta_info)
805*a90b9d01SCy Schubert 		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
806*a90b9d01SCy Schubert 				      true);
80785732ac8SCy Schubert 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
808c1d255d3SCy Schubert 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
80985732ac8SCy Schubert 
81085732ac8SCy Schubert #ifdef CONFIG_IEEE80211AX
811c1d255d3SCy Schubert 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
812*a90b9d01SCy Schubert 		u8 *cca_pos;
813*a90b9d01SCy Schubert 
814206b73d0SCy Schubert 		pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
81585732ac8SCy Schubert 		pos = hostapd_eid_he_operation(hapd, pos);
816*a90b9d01SCy Schubert 
817*a90b9d01SCy Schubert 		/* BSS Color Change Announcement element */
818*a90b9d01SCy Schubert 		cca_pos = hostapd_eid_cca(hapd, pos);
819*a90b9d01SCy Schubert 		if (cca_pos != pos)
820*a90b9d01SCy Schubert 			params->cca_pos = cca_pos - 2;
821*a90b9d01SCy Schubert 		else
822*a90b9d01SCy Schubert 			params->cca_pos = NULL;
823*a90b9d01SCy Schubert 		pos = cca_pos;
824*a90b9d01SCy Schubert 
825206b73d0SCy Schubert 		pos = hostapd_eid_spatial_reuse(hapd, pos);
826c1d255d3SCy Schubert 		pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
827c1d255d3SCy Schubert 		pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
82885732ac8SCy Schubert 	}
82985732ac8SCy Schubert #endif /* CONFIG_IEEE80211AX */
83085732ac8SCy Schubert 
831*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
832*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
833*a90b9d01SCy Schubert 		struct hostapd_data *ml_elem_ap =
834*a90b9d01SCy Schubert 			params->mld_ap ? params->mld_ap : hapd;
835*a90b9d01SCy Schubert 
836*a90b9d01SCy Schubert 		if (ml_elem_ap->conf->mld_ap)
837*a90b9d01SCy Schubert 			pos = hostapd_eid_eht_ml_beacon(
838*a90b9d01SCy Schubert 				ml_elem_ap, params->mld_info,
839*a90b9d01SCy Schubert 				pos, !!params->mld_ap);
840*a90b9d01SCy Schubert 
841*a90b9d01SCy Schubert 		pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
842*a90b9d01SCy Schubert 		pos = hostapd_eid_eht_operation(hapd, pos);
843*a90b9d01SCy Schubert 	}
844*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
845*a90b9d01SCy Schubert 
84685732ac8SCy Schubert #ifdef CONFIG_IEEE80211AC
8475b9c547cSRui Paulo 	if (hapd->conf->vendor_vht)
8485b9c547cSRui Paulo 		pos = hostapd_eid_vendor_vht(hapd, pos);
849f05cddf9SRui Paulo #endif /* CONFIG_IEEE80211AC */
850f05cddf9SRui Paulo 
851c1d255d3SCy Schubert 	/* WPA / OSEN */
852c1d255d3SCy Schubert 	pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
853c1d255d3SCy Schubert 	pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
85485732ac8SCy Schubert 
855e28a4053SRui Paulo 	/* Wi-Fi Alliance WMM */
856e28a4053SRui Paulo 	pos = hostapd_eid_wmm(hapd, pos);
857e28a4053SRui Paulo 
858e28a4053SRui Paulo #ifdef CONFIG_WPS
859e28a4053SRui Paulo 	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
860e28a4053SRui Paulo 		os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
861e28a4053SRui Paulo 			  wpabuf_len(hapd->wps_probe_resp_ie));
862e28a4053SRui Paulo 		pos += wpabuf_len(hapd->wps_probe_resp_ie);
863e28a4053SRui Paulo 	}
864e28a4053SRui Paulo #endif /* CONFIG_WPS */
865e28a4053SRui Paulo 
866f05cddf9SRui Paulo #ifdef CONFIG_P2P
867*a90b9d01SCy Schubert 	if ((hapd->conf->p2p & P2P_ENABLED) && params->is_p2p &&
868f05cddf9SRui Paulo 	    hapd->p2p_probe_resp_ie) {
869f05cddf9SRui Paulo 		os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
870f05cddf9SRui Paulo 			  wpabuf_len(hapd->p2p_probe_resp_ie));
871f05cddf9SRui Paulo 		pos += wpabuf_len(hapd->p2p_probe_resp_ie);
872f05cddf9SRui Paulo 	}
873f05cddf9SRui Paulo #endif /* CONFIG_P2P */
874f05cddf9SRui Paulo #ifdef CONFIG_P2P_MANAGER
875f05cddf9SRui Paulo 	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
876f05cddf9SRui Paulo 	    P2P_MANAGE)
877f05cddf9SRui Paulo 		pos = hostapd_eid_p2p_manage(hapd, pos);
878f05cddf9SRui Paulo #endif /* CONFIG_P2P_MANAGER */
879f05cddf9SRui Paulo 
880f05cddf9SRui Paulo #ifdef CONFIG_HS20
881f05cddf9SRui Paulo 	pos = hostapd_eid_hs20_indication(hapd, pos);
882f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
883f05cddf9SRui Paulo 
884*a90b9d01SCy Schubert 	pos = hostapd_eid_mbo(hapd, pos, epos - pos);
885*a90b9d01SCy Schubert 	pos = hostapd_eid_owe_trans(hapd, pos, epos - pos);
886*a90b9d01SCy Schubert 	pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos);
887780fb4a2SCy Schubert 
888f05cddf9SRui Paulo 	if (hapd->conf->vendor_elements) {
889f05cddf9SRui Paulo 		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
890f05cddf9SRui Paulo 			  wpabuf_len(hapd->conf->vendor_elements));
891f05cddf9SRui Paulo 		pos += wpabuf_len(hapd->conf->vendor_elements);
892f05cddf9SRui Paulo 	}
893f05cddf9SRui Paulo 
894*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
895*a90b9d01SCy Schubert 	if (hapd->conf->presp_elements) {
896*a90b9d01SCy Schubert 		os_memcpy(pos, wpabuf_head(hapd->conf->presp_elements),
897*a90b9d01SCy Schubert 			  wpabuf_len(hapd->conf->presp_elements));
898*a90b9d01SCy Schubert 		pos += wpabuf_len(hapd->conf->presp_elements);
899f05cddf9SRui Paulo 	}
900*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
901*a90b9d01SCy Schubert 
902*a90b9d01SCy Schubert 	return pos;
903*a90b9d01SCy Schubert }
904*a90b9d01SCy Schubert 
905*a90b9d01SCy Schubert 
hostapd_gen_probe_resp(struct hostapd_data * hapd,struct probe_resp_params * params)906*a90b9d01SCy Schubert static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
907*a90b9d01SCy Schubert 				   struct probe_resp_params *params)
908*a90b9d01SCy Schubert {
909*a90b9d01SCy Schubert 	u8 *pos;
910*a90b9d01SCy Schubert 	size_t buflen;
911*a90b9d01SCy Schubert 
912*a90b9d01SCy Schubert 	hapd = hostapd_mbssid_get_tx_bss(hapd);
913*a90b9d01SCy Schubert 
914*a90b9d01SCy Schubert #define MAX_PROBERESP_LEN 768
915*a90b9d01SCy Schubert 	buflen = MAX_PROBERESP_LEN;
916*a90b9d01SCy Schubert 	buflen += hostapd_probe_resp_elems_len(hapd, params);
917*a90b9d01SCy Schubert 	params->resp = os_zalloc(buflen);
918*a90b9d01SCy Schubert 	if (!params->resp) {
919*a90b9d01SCy Schubert 		params->resp_len = 0;
920*a90b9d01SCy Schubert 		return;
921*a90b9d01SCy Schubert 	}
922*a90b9d01SCy Schubert 
923*a90b9d01SCy Schubert 	params->resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
924*a90b9d01SCy Schubert 						   WLAN_FC_STYPE_PROBE_RESP);
925*a90b9d01SCy Schubert 	/* Unicast the response to all requests on bands other than 6 GHz. For
926*a90b9d01SCy Schubert 	 * the 6 GHz, unicast is used only if the actual SSID is not included in
927*a90b9d01SCy Schubert 	 * the Beacon frames. Otherwise, broadcast response is used per IEEE
928*a90b9d01SCy Schubert 	 * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
929*a90b9d01SCy Schubert 	 * the Probe Response frame template for the unsolicited (i.e., not as
930*a90b9d01SCy Schubert 	 * a response to a specific request) case. */
931*a90b9d01SCy Schubert 	if (params->req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
932*a90b9d01SCy Schubert 		    hapd->conf->ignore_broadcast_ssid))
933*a90b9d01SCy Schubert 		os_memcpy(params->resp->da, params->req->sa, ETH_ALEN);
934*a90b9d01SCy Schubert 	else
935*a90b9d01SCy Schubert 		os_memset(params->resp->da, 0xff, ETH_ALEN);
936*a90b9d01SCy Schubert 	os_memcpy(params->resp->sa, hapd->own_addr, ETH_ALEN);
937*a90b9d01SCy Schubert 
938*a90b9d01SCy Schubert 	os_memcpy(params->resp->bssid, hapd->own_addr, ETH_ALEN);
939*a90b9d01SCy Schubert 	params->resp->u.probe_resp.beacon_int =
940*a90b9d01SCy Schubert 		host_to_le16(hapd->iconf->beacon_int);
941*a90b9d01SCy Schubert 
942*a90b9d01SCy Schubert 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
943*a90b9d01SCy Schubert 	params->resp->u.probe_resp.capab_info =
944*a90b9d01SCy Schubert 		host_to_le16(hostapd_own_capab_info(hapd));
945*a90b9d01SCy Schubert 
946*a90b9d01SCy Schubert 	pos = hostapd_probe_resp_fill_elems(hapd, params,
947*a90b9d01SCy Schubert 					    params->resp->u.probe_resp.variable,
948*a90b9d01SCy Schubert 					    buflen);
949*a90b9d01SCy Schubert 
950*a90b9d01SCy Schubert 	params->resp_len = pos - (u8 *) params->resp;
951*a90b9d01SCy Schubert }
952*a90b9d01SCy Schubert 
953*a90b9d01SCy Schubert 
954*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
hostapd_fill_probe_resp_ml_params(struct hostapd_data * hapd,struct probe_resp_params * params,const struct ieee80211_mgmt * mgmt,int mld_id,u16 links)955*a90b9d01SCy Schubert static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
956*a90b9d01SCy Schubert 					      struct probe_resp_params *params,
957*a90b9d01SCy Schubert 					      const struct ieee80211_mgmt *mgmt,
958*a90b9d01SCy Schubert 					      int mld_id, u16 links)
959*a90b9d01SCy Schubert {
960*a90b9d01SCy Schubert 	struct probe_resp_params sta_info_params;
961*a90b9d01SCy Schubert 	struct hostapd_data *link;
962*a90b9d01SCy Schubert 
963*a90b9d01SCy Schubert 	params->mld_ap = NULL;
964*a90b9d01SCy Schubert 	params->mld_info = os_zalloc(sizeof(*params->mld_info));
965*a90b9d01SCy Schubert 	if (!params->mld_info)
966*a90b9d01SCy Schubert 		return;
967*a90b9d01SCy Schubert 
968*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG,
969*a90b9d01SCy Schubert 		   "MLD: Got ML probe request with AP MLD ID %d for links %04x",
970*a90b9d01SCy Schubert 		   mld_id, links);
971*a90b9d01SCy Schubert 
972*a90b9d01SCy Schubert 	for_each_mld_link(link, hapd) {
973*a90b9d01SCy Schubert 		struct mld_link_info *link_info;
974*a90b9d01SCy Schubert 		size_t buflen;
975*a90b9d01SCy Schubert 		u8 mld_link_id = link->mld_link_id;
976*a90b9d01SCy Schubert 		u8 *epos;
977*a90b9d01SCy Schubert 		u8 buf[EHT_ML_MAX_STA_PROF_LEN];
978*a90b9d01SCy Schubert 
979*a90b9d01SCy Schubert 		/*
980*a90b9d01SCy Schubert 		 * Set mld_ap iff the ML probe request explicitly
981*a90b9d01SCy Schubert 		 * requested a specific MLD ID. In that case, the targeted
982*a90b9d01SCy Schubert 		 * AP may have been a nontransmitted BSSID on the same
983*a90b9d01SCy Schubert 		 * interface.
984*a90b9d01SCy Schubert 		 */
985*a90b9d01SCy Schubert 		if (mld_id != -1 && link->iface == hapd->iface)
986*a90b9d01SCy Schubert 			params->mld_ap = link;
987*a90b9d01SCy Schubert 
988*a90b9d01SCy Schubert 		/* Never duplicate main Probe Response frame body */
989*a90b9d01SCy Schubert 		if (link == hapd)
990*a90b9d01SCy Schubert 			continue;
991*a90b9d01SCy Schubert 
992*a90b9d01SCy Schubert 		/* Only include requested links */
993*a90b9d01SCy Schubert 		if (!(BIT(mld_link_id) & links))
994*a90b9d01SCy Schubert 			continue;
995*a90b9d01SCy Schubert 
996*a90b9d01SCy Schubert 		link_info = &params->mld_info->links[mld_link_id];
997*a90b9d01SCy Schubert 
998*a90b9d01SCy Schubert 		sta_info_params.req = params->req;
999*a90b9d01SCy Schubert 		sta_info_params.is_p2p = false;
1000*a90b9d01SCy Schubert 		sta_info_params.is_ml_sta_info = true;
1001*a90b9d01SCy Schubert 		sta_info_params.mld_ap = NULL;
1002*a90b9d01SCy Schubert 		sta_info_params.mld_info = NULL;
1003*a90b9d01SCy Schubert 
1004*a90b9d01SCy Schubert 		buflen = MAX_PROBERESP_LEN;
1005*a90b9d01SCy Schubert 		buflen += hostapd_probe_resp_elems_len(link, &sta_info_params);
1006*a90b9d01SCy Schubert 
1007*a90b9d01SCy Schubert 		if (buflen > EHT_ML_MAX_STA_PROF_LEN) {
1008*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1009*a90b9d01SCy Schubert 				   "MLD: Not including link %d in ML probe response (%zu bytes is too long)",
1010*a90b9d01SCy Schubert 				   mld_link_id, buflen);
1011*a90b9d01SCy Schubert 			goto fail;
1012*a90b9d01SCy Schubert 		}
1013*a90b9d01SCy Schubert 
1014*a90b9d01SCy Schubert 		/*
1015*a90b9d01SCy Schubert 		 * NOTE: This does not properly handle inheritance and
1016*a90b9d01SCy Schubert 		 * various other things.
1017*a90b9d01SCy Schubert 		 */
1018*a90b9d01SCy Schubert 		link_info->valid = true;
1019*a90b9d01SCy Schubert 		epos = buf;
1020*a90b9d01SCy Schubert 
1021*a90b9d01SCy Schubert 		/* Capabilities is the only fixed parameter */
1022*a90b9d01SCy Schubert 		WPA_PUT_LE16(epos, hostapd_own_capab_info(hapd));
1023*a90b9d01SCy Schubert 		epos += 2;
1024*a90b9d01SCy Schubert 
1025*a90b9d01SCy Schubert 		epos = hostapd_probe_resp_fill_elems(
1026*a90b9d01SCy Schubert 			link, &sta_info_params, epos,
1027*a90b9d01SCy Schubert 			EHT_ML_MAX_STA_PROF_LEN - 2);
1028*a90b9d01SCy Schubert 		link_info->resp_sta_profile_len = epos - buf;
1029*a90b9d01SCy Schubert 		os_free(link_info->resp_sta_profile);
1030*a90b9d01SCy Schubert 		link_info->resp_sta_profile = os_memdup(
1031*a90b9d01SCy Schubert 			buf, link_info->resp_sta_profile_len);
1032*a90b9d01SCy Schubert 		if (!link_info->resp_sta_profile)
1033*a90b9d01SCy Schubert 			link_info->resp_sta_profile_len = 0;
1034*a90b9d01SCy Schubert 		os_memcpy(link_info->local_addr, link->own_addr, ETH_ALEN);
1035*a90b9d01SCy Schubert 
1036*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1037*a90b9d01SCy Schubert 			   "MLD: ML probe response includes link sta info for %d: %u bytes (estimate %zu)",
1038*a90b9d01SCy Schubert 			   mld_link_id, link_info->resp_sta_profile_len,
1039*a90b9d01SCy Schubert 			   buflen);
1040*a90b9d01SCy Schubert 	}
1041*a90b9d01SCy Schubert 
1042*a90b9d01SCy Schubert 	if (mld_id != -1 && !params->mld_ap) {
1043*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1044*a90b9d01SCy Schubert 			   "MLD: No nontransmitted BSSID for MLD ID %d",
1045*a90b9d01SCy Schubert 			   mld_id);
1046*a90b9d01SCy Schubert 		goto fail;
1047*a90b9d01SCy Schubert 	}
1048*a90b9d01SCy Schubert 
1049*a90b9d01SCy Schubert 	return;
1050*a90b9d01SCy Schubert 
1051*a90b9d01SCy Schubert fail:
1052*a90b9d01SCy Schubert 	hostapd_free_probe_resp_params(params);
1053*a90b9d01SCy Schubert 	params->mld_ap = NULL;
1054*a90b9d01SCy Schubert 	params->mld_info = NULL;
1055*a90b9d01SCy Schubert }
1056*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
1057f05cddf9SRui Paulo 
1058f05cddf9SRui Paulo 
1059f05cddf9SRui Paulo enum ssid_match_result {
1060f05cddf9SRui Paulo 	NO_SSID_MATCH,
1061f05cddf9SRui Paulo 	EXACT_SSID_MATCH,
10624b72b91aSCy Schubert 	WILDCARD_SSID_MATCH,
10634b72b91aSCy Schubert 	CO_LOCATED_SSID_MATCH,
1064f05cddf9SRui Paulo };
1065f05cddf9SRui Paulo 
ssid_match(struct hostapd_data * hapd,const u8 * ssid,size_t ssid_len,const u8 * ssid_list,size_t ssid_list_len,const u8 * short_ssid_list,size_t short_ssid_list_len)1066f05cddf9SRui Paulo static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
1067f05cddf9SRui Paulo 					 const u8 *ssid, size_t ssid_len,
1068f05cddf9SRui Paulo 					 const u8 *ssid_list,
1069c1d255d3SCy Schubert 					 size_t ssid_list_len,
1070c1d255d3SCy Schubert 					 const u8 *short_ssid_list,
1071c1d255d3SCy Schubert 					 size_t short_ssid_list_len)
1072f05cddf9SRui Paulo {
1073f05cddf9SRui Paulo 	const u8 *pos, *end;
10744b72b91aSCy Schubert 	struct hostapd_iface *iface = hapd->iface;
1075f05cddf9SRui Paulo 	int wildcard = 0;
10764b72b91aSCy Schubert 	size_t i, j;
1077f05cddf9SRui Paulo 
1078f05cddf9SRui Paulo 	if (ssid_len == 0)
1079f05cddf9SRui Paulo 		wildcard = 1;
1080f05cddf9SRui Paulo 	if (ssid_len == hapd->conf->ssid.ssid_len &&
1081f05cddf9SRui Paulo 	    os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
1082f05cddf9SRui Paulo 		return EXACT_SSID_MATCH;
1083f05cddf9SRui Paulo 
1084c1d255d3SCy Schubert 	if (ssid_list) {
1085f05cddf9SRui Paulo 		pos = ssid_list;
1086f05cddf9SRui Paulo 		end = ssid_list + ssid_list_len;
1087206b73d0SCy Schubert 		while (end - pos >= 2) {
1088780fb4a2SCy Schubert 			if (2 + pos[1] > end - pos)
1089f05cddf9SRui Paulo 				break;
1090f05cddf9SRui Paulo 			if (pos[1] == 0)
1091f05cddf9SRui Paulo 				wildcard = 1;
1092f05cddf9SRui Paulo 			if (pos[1] == hapd->conf->ssid.ssid_len &&
1093c1d255d3SCy Schubert 			    os_memcmp(pos + 2, hapd->conf->ssid.ssid,
1094c1d255d3SCy Schubert 				      pos[1]) == 0)
1095f05cddf9SRui Paulo 				return EXACT_SSID_MATCH;
1096f05cddf9SRui Paulo 			pos += 2 + pos[1];
1097f05cddf9SRui Paulo 		}
1098c1d255d3SCy Schubert 	}
1099c1d255d3SCy Schubert 
1100c1d255d3SCy Schubert 	if (short_ssid_list) {
1101c1d255d3SCy Schubert 		pos = short_ssid_list;
1102c1d255d3SCy Schubert 		end = short_ssid_list + short_ssid_list_len;
1103c1d255d3SCy Schubert 		while (end - pos >= 4) {
1104c1d255d3SCy Schubert 			if (hapd->conf->ssid.short_ssid == WPA_GET_LE32(pos))
1105c1d255d3SCy Schubert 				return EXACT_SSID_MATCH;
1106c1d255d3SCy Schubert 			pos += 4;
1107c1d255d3SCy Schubert 		}
1108c1d255d3SCy Schubert 	}
1109f05cddf9SRui Paulo 
11104b72b91aSCy Schubert 	if (wildcard)
11114b72b91aSCy Schubert 		return WILDCARD_SSID_MATCH;
11124b72b91aSCy Schubert 
11134b72b91aSCy Schubert 	if (!iface->interfaces || iface->interfaces->count <= 1 ||
11144b72b91aSCy Schubert 	    is_6ghz_op_class(hapd->iconf->op_class))
11154b72b91aSCy Schubert 		return NO_SSID_MATCH;
11164b72b91aSCy Schubert 
11174b72b91aSCy Schubert 	for (i = 0; i < iface->interfaces->count; i++) {
11184b72b91aSCy Schubert 		struct hostapd_iface *colocated;
11194b72b91aSCy Schubert 
11204b72b91aSCy Schubert 		colocated = iface->interfaces->iface[i];
11214b72b91aSCy Schubert 
11224b72b91aSCy Schubert 		if (colocated == iface ||
11234b72b91aSCy Schubert 		    !is_6ghz_op_class(colocated->conf->op_class))
11244b72b91aSCy Schubert 			continue;
11254b72b91aSCy Schubert 
11264b72b91aSCy Schubert 		for (j = 0; j < colocated->num_bss; j++) {
11274b72b91aSCy Schubert 			struct hostapd_bss_config *conf;
11284b72b91aSCy Schubert 
11294b72b91aSCy Schubert 			conf = colocated->bss[j]->conf;
11304b72b91aSCy Schubert 			if (ssid_len == conf->ssid.ssid_len &&
11314b72b91aSCy Schubert 			    os_memcmp(ssid, conf->ssid.ssid, ssid_len) == 0)
11324b72b91aSCy Schubert 				return CO_LOCATED_SSID_MATCH;
11334b72b91aSCy Schubert 		}
11344b72b91aSCy Schubert 	}
11354b72b91aSCy Schubert 
11364b72b91aSCy Schubert 	return NO_SSID_MATCH;
1137f05cddf9SRui Paulo }
1138f05cddf9SRui Paulo 
1139f05cddf9SRui Paulo 
sta_track_expire(struct hostapd_iface * iface,int force)1140325151a3SRui Paulo void sta_track_expire(struct hostapd_iface *iface, int force)
1141325151a3SRui Paulo {
1142325151a3SRui Paulo 	struct os_reltime now;
1143325151a3SRui Paulo 	struct hostapd_sta_info *info;
1144325151a3SRui Paulo 
1145325151a3SRui Paulo 	if (!iface->num_sta_seen)
1146325151a3SRui Paulo 		return;
1147325151a3SRui Paulo 
1148325151a3SRui Paulo 	os_get_reltime(&now);
1149325151a3SRui Paulo 	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
1150325151a3SRui Paulo 				     list))) {
1151325151a3SRui Paulo 		if (!force &&
1152325151a3SRui Paulo 		    !os_reltime_expired(&now, &info->last_seen,
1153325151a3SRui Paulo 					iface->conf->track_sta_max_age))
1154325151a3SRui Paulo 			break;
1155325151a3SRui Paulo 		force = 0;
1156325151a3SRui Paulo 
1157325151a3SRui Paulo 		wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
1158325151a3SRui Paulo 			   MACSTR, iface->bss[0]->conf->iface,
1159325151a3SRui Paulo 			   MAC2STR(info->addr));
1160325151a3SRui Paulo 		dl_list_del(&info->list);
1161325151a3SRui Paulo 		iface->num_sta_seen--;
1162780fb4a2SCy Schubert 		sta_track_del(info);
1163325151a3SRui Paulo 	}
1164325151a3SRui Paulo }
1165325151a3SRui Paulo 
1166325151a3SRui Paulo 
sta_track_get(struct hostapd_iface * iface,const u8 * addr)1167325151a3SRui Paulo static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
1168325151a3SRui Paulo 					       const u8 *addr)
1169325151a3SRui Paulo {
1170325151a3SRui Paulo 	struct hostapd_sta_info *info;
1171325151a3SRui Paulo 
1172325151a3SRui Paulo 	dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
1173*a90b9d01SCy Schubert 		if (ether_addr_equal(addr, info->addr))
1174325151a3SRui Paulo 			return info;
1175325151a3SRui Paulo 
1176325151a3SRui Paulo 	return NULL;
1177325151a3SRui Paulo }
1178325151a3SRui Paulo 
1179325151a3SRui Paulo 
sta_track_add(struct hostapd_iface * iface,const u8 * addr,int ssi_signal)118085732ac8SCy Schubert void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
1181325151a3SRui Paulo {
1182325151a3SRui Paulo 	struct hostapd_sta_info *info;
1183325151a3SRui Paulo 
1184325151a3SRui Paulo 	info = sta_track_get(iface, addr);
1185325151a3SRui Paulo 	if (info) {
1186325151a3SRui Paulo 		/* Move the most recent entry to the end of the list */
1187325151a3SRui Paulo 		dl_list_del(&info->list);
1188325151a3SRui Paulo 		dl_list_add_tail(&iface->sta_seen, &info->list);
1189325151a3SRui Paulo 		os_get_reltime(&info->last_seen);
119085732ac8SCy Schubert 		info->ssi_signal = ssi_signal;
1191325151a3SRui Paulo 		return;
1192325151a3SRui Paulo 	}
1193325151a3SRui Paulo 
1194325151a3SRui Paulo 	/* Add a new entry */
1195325151a3SRui Paulo 	info = os_zalloc(sizeof(*info));
1196780fb4a2SCy Schubert 	if (info == NULL)
1197780fb4a2SCy Schubert 		return;
1198325151a3SRui Paulo 	os_memcpy(info->addr, addr, ETH_ALEN);
1199325151a3SRui Paulo 	os_get_reltime(&info->last_seen);
120085732ac8SCy Schubert 	info->ssi_signal = ssi_signal;
1201325151a3SRui Paulo 
1202325151a3SRui Paulo 	if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
1203325151a3SRui Paulo 		/* Expire oldest entry to make room for a new one */
1204325151a3SRui Paulo 		sta_track_expire(iface, 1);
1205325151a3SRui Paulo 	}
1206325151a3SRui Paulo 
1207325151a3SRui Paulo 	wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
1208325151a3SRui Paulo 		   MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
1209325151a3SRui Paulo 	dl_list_add_tail(&iface->sta_seen, &info->list);
1210325151a3SRui Paulo 	iface->num_sta_seen++;
1211325151a3SRui Paulo }
1212325151a3SRui Paulo 
1213325151a3SRui Paulo 
1214325151a3SRui Paulo struct hostapd_data *
sta_track_seen_on(struct hostapd_iface * iface,const u8 * addr,const char * ifname)1215325151a3SRui Paulo sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
1216325151a3SRui Paulo 		  const char *ifname)
1217325151a3SRui Paulo {
1218325151a3SRui Paulo 	struct hapd_interfaces *interfaces = iface->interfaces;
1219325151a3SRui Paulo 	size_t i, j;
1220325151a3SRui Paulo 
1221325151a3SRui Paulo 	for (i = 0; i < interfaces->count; i++) {
1222325151a3SRui Paulo 		struct hostapd_data *hapd = NULL;
1223325151a3SRui Paulo 
1224325151a3SRui Paulo 		iface = interfaces->iface[i];
1225325151a3SRui Paulo 		for (j = 0; j < iface->num_bss; j++) {
1226325151a3SRui Paulo 			hapd = iface->bss[j];
1227325151a3SRui Paulo 			if (os_strcmp(ifname, hapd->conf->iface) == 0)
1228325151a3SRui Paulo 				break;
1229325151a3SRui Paulo 			hapd = NULL;
1230325151a3SRui Paulo 		}
1231325151a3SRui Paulo 
1232325151a3SRui Paulo 		if (hapd && sta_track_get(iface, addr))
1233325151a3SRui Paulo 			return hapd;
1234325151a3SRui Paulo 	}
1235325151a3SRui Paulo 
1236325151a3SRui Paulo 	return NULL;
1237325151a3SRui Paulo }
1238325151a3SRui Paulo 
1239325151a3SRui Paulo 
1240780fb4a2SCy Schubert #ifdef CONFIG_TAXONOMY
sta_track_claim_taxonomy_info(struct hostapd_iface * iface,const u8 * addr,struct wpabuf ** probe_ie_taxonomy)1241780fb4a2SCy Schubert void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
1242780fb4a2SCy Schubert 				   struct wpabuf **probe_ie_taxonomy)
1243780fb4a2SCy Schubert {
1244780fb4a2SCy Schubert 	struct hostapd_sta_info *info;
1245780fb4a2SCy Schubert 
1246780fb4a2SCy Schubert 	info = sta_track_get(iface, addr);
1247780fb4a2SCy Schubert 	if (!info)
1248780fb4a2SCy Schubert 		return;
1249780fb4a2SCy Schubert 
1250780fb4a2SCy Schubert 	wpabuf_free(*probe_ie_taxonomy);
1251780fb4a2SCy Schubert 	*probe_ie_taxonomy = info->probe_ie_taxonomy;
1252780fb4a2SCy Schubert 	info->probe_ie_taxonomy = NULL;
1253780fb4a2SCy Schubert }
1254780fb4a2SCy Schubert #endif /* CONFIG_TAXONOMY */
1255780fb4a2SCy Schubert 
1256780fb4a2SCy Schubert 
1257*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
parse_ml_probe_req(const struct ieee80211_eht_ml * ml,size_t ml_len,int * mld_id,u16 * links)1258*a90b9d01SCy Schubert static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len,
1259*a90b9d01SCy Schubert 			       int *mld_id, u16 *links)
1260*a90b9d01SCy Schubert {
1261*a90b9d01SCy Schubert 	u16 ml_control;
1262*a90b9d01SCy Schubert 	const struct element *sub;
1263*a90b9d01SCy Schubert 	const u8 *pos;
1264*a90b9d01SCy Schubert 	size_t len;
1265*a90b9d01SCy Schubert 
1266*a90b9d01SCy Schubert 	*mld_id = -1;
1267*a90b9d01SCy Schubert 	*links = 0xffff;
1268*a90b9d01SCy Schubert 
1269*a90b9d01SCy Schubert 	if (ml_len < sizeof(struct ieee80211_eht_ml))
1270*a90b9d01SCy Schubert 		return false;
1271*a90b9d01SCy Schubert 
1272*a90b9d01SCy Schubert 	ml_control = le_to_host16(ml->ml_control);
1273*a90b9d01SCy Schubert 	if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
1274*a90b9d01SCy Schubert 	    MULTI_LINK_CONTROL_TYPE_PROBE_REQ) {
1275*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: Not an ML probe req");
1276*a90b9d01SCy Schubert 		return false;
1277*a90b9d01SCy Schubert 	}
1278*a90b9d01SCy Schubert 
1279*a90b9d01SCy Schubert 	if (sizeof(struct ieee80211_eht_ml) + 1 > ml_len) {
1280*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: ML probe req too short");
1281*a90b9d01SCy Schubert 		return false;
1282*a90b9d01SCy Schubert 	}
1283*a90b9d01SCy Schubert 
1284*a90b9d01SCy Schubert 	pos = ml->variable;
1285*a90b9d01SCy Schubert 	len = pos[0];
1286*a90b9d01SCy Schubert 	if (len < 1 || sizeof(struct ieee80211_eht_ml) + len > ml_len) {
1287*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1288*a90b9d01SCy Schubert 			   "MLD: ML probe request with invalid length");
1289*a90b9d01SCy Schubert 		return false;
1290*a90b9d01SCy Schubert 	}
1291*a90b9d01SCy Schubert 
1292*a90b9d01SCy Schubert 	if (ml_control & EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID) {
1293*a90b9d01SCy Schubert 		if (len < 2) {
1294*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1295*a90b9d01SCy Schubert 				   "MLD: ML probe req too short for MLD ID");
1296*a90b9d01SCy Schubert 			return false;
1297*a90b9d01SCy Schubert 		}
1298*a90b9d01SCy Schubert 
1299*a90b9d01SCy Schubert 		*mld_id = pos[1];
1300*a90b9d01SCy Schubert 	}
1301*a90b9d01SCy Schubert 	pos += len;
1302*a90b9d01SCy Schubert 
1303*a90b9d01SCy Schubert 	/* Parse subelements (if there are any) */
1304*a90b9d01SCy Schubert 	len = ml_len - len - sizeof(struct ieee80211_eht_ml);
1305*a90b9d01SCy Schubert 	for_each_element_id(sub, 0, pos, len) {
1306*a90b9d01SCy Schubert 		const struct ieee80211_eht_per_sta_profile *sta;
1307*a90b9d01SCy Schubert 		u16 sta_control;
1308*a90b9d01SCy Schubert 
1309*a90b9d01SCy Schubert 		if (*links == 0xffff)
1310*a90b9d01SCy Schubert 			*links = 0;
1311*a90b9d01SCy Schubert 
1312*a90b9d01SCy Schubert 		if (sub->datalen <
1313*a90b9d01SCy Schubert 		    sizeof(struct ieee80211_eht_per_sta_profile)) {
1314*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1315*a90b9d01SCy Schubert 				   "MLD: ML probe req %d too short for sta profile",
1316*a90b9d01SCy Schubert 				   sub->datalen);
1317*a90b9d01SCy Schubert 			return false;
1318*a90b9d01SCy Schubert 		}
1319*a90b9d01SCy Schubert 
1320*a90b9d01SCy Schubert 		sta = (struct ieee80211_eht_per_sta_profile *) sub->data;
1321*a90b9d01SCy Schubert 
1322*a90b9d01SCy Schubert 		/*
1323*a90b9d01SCy Schubert 		 * Extract the link ID, do not return whether a complete or
1324*a90b9d01SCy Schubert 		 * partial profile was requested.
1325*a90b9d01SCy Schubert 		 */
1326*a90b9d01SCy Schubert 		sta_control = le_to_host16(sta->sta_control);
1327*a90b9d01SCy Schubert 		*links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK);
1328*a90b9d01SCy Schubert 	}
1329*a90b9d01SCy Schubert 
1330*a90b9d01SCy Schubert 	if (!for_each_element_completed(sub, pos, len)) {
1331*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1332*a90b9d01SCy Schubert 			   "MLD: ML probe req sub-elements parsing error");
1333*a90b9d01SCy Schubert 		return false;
1334*a90b9d01SCy Schubert 	}
1335*a90b9d01SCy Schubert 
1336*a90b9d01SCy Schubert 	return true;
1337*a90b9d01SCy Schubert }
1338*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
1339*a90b9d01SCy Schubert 
1340*a90b9d01SCy Schubert 
handle_probe_req(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len,int ssi_signal)1341f05cddf9SRui Paulo void handle_probe_req(struct hostapd_data *hapd,
1342f05cddf9SRui Paulo 		      const struct ieee80211_mgmt *mgmt, size_t len,
1343f05cddf9SRui Paulo 		      int ssi_signal)
1344f05cddf9SRui Paulo {
1345f05cddf9SRui Paulo 	struct ieee802_11_elems elems;
1346f05cddf9SRui Paulo 	const u8 *ie;
1347f05cddf9SRui Paulo 	size_t ie_len;
1348*a90b9d01SCy Schubert 	size_t i;
1349f05cddf9SRui Paulo 	int noack;
1350f05cddf9SRui Paulo 	enum ssid_match_result res;
1351780fb4a2SCy Schubert 	int ret;
1352780fb4a2SCy Schubert 	u16 csa_offs[2];
1353780fb4a2SCy Schubert 	size_t csa_offs_len;
1354c1d255d3SCy Schubert 	struct radius_sta rad_info;
1355*a90b9d01SCy Schubert 	struct probe_resp_params params;
1356*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
1357*a90b9d01SCy Schubert 	int mld_id;
1358*a90b9d01SCy Schubert 	u16 links;
1359*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
1360c1d255d3SCy Schubert 
1361c1d255d3SCy Schubert 	if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
1362c1d255d3SCy Schubert 	    ssi_signal < hapd->iconf->rssi_ignore_probe_request)
1363c1d255d3SCy Schubert 		return;
1364f05cddf9SRui Paulo 
1365780fb4a2SCy Schubert 	if (len < IEEE80211_HDRLEN)
1366f05cddf9SRui Paulo 		return;
1367780fb4a2SCy Schubert 	ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
1368325151a3SRui Paulo 	if (hapd->iconf->track_sta_max_num)
136985732ac8SCy Schubert 		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
1370780fb4a2SCy Schubert 	ie_len = len - IEEE80211_HDRLEN;
1371f05cddf9SRui Paulo 
1372c1d255d3SCy Schubert 	ret = hostapd_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
1373c1d255d3SCy Schubert 				      &rad_info, 1);
137485732ac8SCy Schubert 	if (ret == HOSTAPD_ACL_REJECT) {
137585732ac8SCy Schubert 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
137685732ac8SCy Schubert 			"Ignore Probe Request frame from " MACSTR
137785732ac8SCy Schubert 			" due to ACL reject ", MAC2STR(mgmt->sa));
137885732ac8SCy Schubert 		return;
137985732ac8SCy Schubert 	}
138085732ac8SCy Schubert 
1381f05cddf9SRui Paulo 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
1382f05cddf9SRui Paulo 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
1383f05cddf9SRui Paulo 					    mgmt->sa, mgmt->da, mgmt->bssid,
1384f05cddf9SRui Paulo 					    ie, ie_len, ssi_signal) > 0)
1385f05cddf9SRui Paulo 			return;
1386f05cddf9SRui Paulo 
13874bc52338SCy Schubert 	if (!hapd->conf->send_probe_response)
1388f05cddf9SRui Paulo 		return;
1389f05cddf9SRui Paulo 
1390f05cddf9SRui Paulo 	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
1391f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
1392f05cddf9SRui Paulo 			   MAC2STR(mgmt->sa));
1393f05cddf9SRui Paulo 		return;
1394f05cddf9SRui Paulo 	}
1395f05cddf9SRui Paulo 
1396f05cddf9SRui Paulo 	if ((!elems.ssid || !elems.supp_rates)) {
1397f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
1398f05cddf9SRui Paulo 			   "without SSID or supported rates element",
1399f05cddf9SRui Paulo 			   MAC2STR(mgmt->sa));
1400f05cddf9SRui Paulo 		return;
1401f05cddf9SRui Paulo 	}
1402f05cddf9SRui Paulo 
14035b9c547cSRui Paulo 	/*
14045b9c547cSRui Paulo 	 * No need to reply if the Probe Request frame was sent on an adjacent
14055b9c547cSRui Paulo 	 * channel. IEEE Std 802.11-2012 describes this as a requirement for an
14065b9c547cSRui Paulo 	 * AP with dot11RadioMeasurementActivated set to true, but strictly
14075b9c547cSRui Paulo 	 * speaking does not allow such ignoring of Probe Request frames if
14085b9c547cSRui Paulo 	 * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
14095b9c547cSRui Paulo 	 * number of unnecessary Probe Response frames for cases where the STA
14105b9c547cSRui Paulo 	 * is less likely to see them (Probe Request frame sent on a
14115b9c547cSRui Paulo 	 * neighboring, but partially overlapping, channel).
14125b9c547cSRui Paulo 	 */
1413325151a3SRui Paulo 	if (elems.ds_params &&
14145b9c547cSRui Paulo 	    hapd->iface->current_mode &&
14155b9c547cSRui Paulo 	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
14165b9c547cSRui Paulo 	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
14175b9c547cSRui Paulo 	    hapd->iconf->channel != elems.ds_params[0]) {
14185b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
14195b9c547cSRui Paulo 			   "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
14205b9c547cSRui Paulo 			   hapd->iconf->channel, elems.ds_params[0]);
14215b9c547cSRui Paulo 		return;
14225b9c547cSRui Paulo 	}
14235b9c547cSRui Paulo 
1424f05cddf9SRui Paulo #ifdef CONFIG_P2P
1425780fb4a2SCy Schubert 	if (hapd->p2p && hapd->p2p_group && elems.wps_ie) {
1426f05cddf9SRui Paulo 		struct wpabuf *wps;
1427f05cddf9SRui Paulo 		wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
1428f05cddf9SRui Paulo 		if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
1429f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
1430f05cddf9SRui Paulo 				   "due to mismatch with Requested Device "
1431f05cddf9SRui Paulo 				   "Type");
1432f05cddf9SRui Paulo 			wpabuf_free(wps);
1433f05cddf9SRui Paulo 			return;
1434f05cddf9SRui Paulo 		}
1435f05cddf9SRui Paulo 		wpabuf_free(wps);
1436f05cddf9SRui Paulo 	}
1437f05cddf9SRui Paulo 
1438780fb4a2SCy Schubert 	if (hapd->p2p && hapd->p2p_group && elems.p2p) {
1439f05cddf9SRui Paulo 		struct wpabuf *p2p;
1440f05cddf9SRui Paulo 		p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
1441f05cddf9SRui Paulo 		if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
1442f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
1443f05cddf9SRui Paulo 				   "due to mismatch with Device ID");
1444f05cddf9SRui Paulo 			wpabuf_free(p2p);
1445f05cddf9SRui Paulo 			return;
1446f05cddf9SRui Paulo 		}
1447f05cddf9SRui Paulo 		wpabuf_free(p2p);
1448f05cddf9SRui Paulo 	}
1449f05cddf9SRui Paulo #endif /* CONFIG_P2P */
1450f05cddf9SRui Paulo 
1451f05cddf9SRui Paulo 	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
1452c1d255d3SCy Schubert 	    elems.ssid_list_len == 0 && elems.short_ssid_list_len == 0) {
1453f05cddf9SRui Paulo 		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
1454f05cddf9SRui Paulo 			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
1455f05cddf9SRui Paulo 		return;
1456f05cddf9SRui Paulo 	}
1457f05cddf9SRui Paulo 
1458f05cddf9SRui Paulo #ifdef CONFIG_P2P
1459f05cddf9SRui Paulo 	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
1460f05cddf9SRui Paulo 	    elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
1461f05cddf9SRui Paulo 	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
1462f05cddf9SRui Paulo 		      P2P_WILDCARD_SSID_LEN) == 0) {
1463f05cddf9SRui Paulo 		/* Process P2P Wildcard SSID like Wildcard SSID */
1464f05cddf9SRui Paulo 		elems.ssid_len = 0;
1465f05cddf9SRui Paulo 	}
1466f05cddf9SRui Paulo #endif /* CONFIG_P2P */
1467f05cddf9SRui Paulo 
1468780fb4a2SCy Schubert #ifdef CONFIG_TAXONOMY
1469780fb4a2SCy Schubert 	{
1470780fb4a2SCy Schubert 		struct sta_info *sta;
1471780fb4a2SCy Schubert 		struct hostapd_sta_info *info;
1472780fb4a2SCy Schubert 
1473780fb4a2SCy Schubert 		if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) {
1474780fb4a2SCy Schubert 			taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len);
1475780fb4a2SCy Schubert 		} else if ((info = sta_track_get(hapd->iface,
1476780fb4a2SCy Schubert 						 mgmt->sa)) != NULL) {
1477780fb4a2SCy Schubert 			taxonomy_hostapd_sta_info_probe_req(hapd, info,
1478780fb4a2SCy Schubert 							    ie, ie_len);
1479780fb4a2SCy Schubert 		}
1480780fb4a2SCy Schubert 	}
1481780fb4a2SCy Schubert #endif /* CONFIG_TAXONOMY */
1482780fb4a2SCy Schubert 
1483f05cddf9SRui Paulo 	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
1484c1d255d3SCy Schubert 			 elems.ssid_list, elems.ssid_list_len,
1485c1d255d3SCy Schubert 			 elems.short_ssid_list, elems.short_ssid_list_len);
1486325151a3SRui Paulo 	if (res == NO_SSID_MATCH) {
1487f05cddf9SRui Paulo 		if (!(mgmt->da[0] & 0x01)) {
1488f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
1489f05cddf9SRui Paulo 				   " for foreign SSID '%s' (DA " MACSTR ")%s",
14905b9c547cSRui Paulo 				   MAC2STR(mgmt->sa),
14915b9c547cSRui Paulo 				   wpa_ssid_txt(elems.ssid, elems.ssid_len),
1492f05cddf9SRui Paulo 				   MAC2STR(mgmt->da),
1493f05cddf9SRui Paulo 				   elems.ssid_list ? " (SSID list)" : "");
1494f05cddf9SRui Paulo 		}
1495f05cddf9SRui Paulo 		return;
1496f05cddf9SRui Paulo 	}
1497f05cddf9SRui Paulo 
1498c1d255d3SCy Schubert 	if (hapd->conf->ignore_broadcast_ssid && res == WILDCARD_SSID_MATCH) {
1499c1d255d3SCy Schubert 		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
1500c1d255d3SCy Schubert 			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
1501c1d255d3SCy Schubert 		return;
1502c1d255d3SCy Schubert 	}
1503c1d255d3SCy Schubert 
1504f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING
15055b9c547cSRui Paulo 	if (hapd->conf->interworking &&
15065b9c547cSRui Paulo 	    elems.interworking && elems.interworking_len >= 1) {
1507f05cddf9SRui Paulo 		u8 ant = elems.interworking[0] & 0x0f;
1508f05cddf9SRui Paulo 		if (ant != INTERWORKING_ANT_WILDCARD &&
1509f05cddf9SRui Paulo 		    ant != hapd->conf->access_network_type) {
1510f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
1511f05cddf9SRui Paulo 				   " for mismatching ANT %u ignored",
1512f05cddf9SRui Paulo 				   MAC2STR(mgmt->sa), ant);
1513f05cddf9SRui Paulo 			return;
1514f05cddf9SRui Paulo 		}
1515f05cddf9SRui Paulo 	}
1516f05cddf9SRui Paulo 
15175b9c547cSRui Paulo 	if (hapd->conf->interworking && elems.interworking &&
1518f05cddf9SRui Paulo 	    (elems.interworking_len == 7 || elems.interworking_len == 9)) {
1519f05cddf9SRui Paulo 		const u8 *hessid;
1520f05cddf9SRui Paulo 		if (elems.interworking_len == 7)
1521f05cddf9SRui Paulo 			hessid = elems.interworking + 1;
1522f05cddf9SRui Paulo 		else
1523f05cddf9SRui Paulo 			hessid = elems.interworking + 1 + 2;
1524f05cddf9SRui Paulo 		if (!is_broadcast_ether_addr(hessid) &&
1525*a90b9d01SCy Schubert 		    !ether_addr_equal(hessid, hapd->conf->hessid)) {
1526f05cddf9SRui Paulo 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
1527f05cddf9SRui Paulo 				   " for mismatching HESSID " MACSTR
1528f05cddf9SRui Paulo 				   " ignored",
1529f05cddf9SRui Paulo 				   MAC2STR(mgmt->sa), MAC2STR(hessid));
1530f05cddf9SRui Paulo 			return;
1531f05cddf9SRui Paulo 		}
1532f05cddf9SRui Paulo 	}
1533f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */
1534f05cddf9SRui Paulo 
15355b9c547cSRui Paulo #ifdef CONFIG_P2P
15365b9c547cSRui Paulo 	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
15375b9c547cSRui Paulo 	    supp_rates_11b_only(&elems)) {
15385b9c547cSRui Paulo 		/* Indicates support for 11b rates only */
15395b9c547cSRui Paulo 		wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
15405b9c547cSRui Paulo 			   MACSTR " with only 802.11b rates",
15415b9c547cSRui Paulo 			   MAC2STR(mgmt->sa));
15425b9c547cSRui Paulo 		return;
15435b9c547cSRui Paulo 	}
15445b9c547cSRui Paulo #endif /* CONFIG_P2P */
15455b9c547cSRui Paulo 
1546f05cddf9SRui Paulo 	/* TODO: verify that supp_rates contains at least one matching rate
1547f05cddf9SRui Paulo 	 * with AP configuration */
1548f05cddf9SRui Paulo 
1549325151a3SRui Paulo 	if (hapd->conf->no_probe_resp_if_seen_on &&
1550325151a3SRui Paulo 	    is_multicast_ether_addr(mgmt->da) &&
1551325151a3SRui Paulo 	    is_multicast_ether_addr(mgmt->bssid) &&
1552325151a3SRui Paulo 	    sta_track_seen_on(hapd->iface, mgmt->sa,
1553325151a3SRui Paulo 			      hapd->conf->no_probe_resp_if_seen_on)) {
1554325151a3SRui Paulo 		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
1555325151a3SRui Paulo 			   " since STA has been seen on %s",
1556325151a3SRui Paulo 			   hapd->conf->iface, MAC2STR(mgmt->sa),
1557325151a3SRui Paulo 			   hapd->conf->no_probe_resp_if_seen_on);
1558325151a3SRui Paulo 		return;
1559325151a3SRui Paulo 	}
1560325151a3SRui Paulo 
1561780fb4a2SCy Schubert 	if (hapd->conf->no_probe_resp_if_max_sta &&
1562780fb4a2SCy Schubert 	    is_multicast_ether_addr(mgmt->da) &&
1563780fb4a2SCy Schubert 	    is_multicast_ether_addr(mgmt->bssid) &&
1564780fb4a2SCy Schubert 	    hapd->num_sta >= hapd->conf->max_num_sta &&
1565780fb4a2SCy Schubert 	    !ap_get_sta(hapd, mgmt->sa)) {
1566780fb4a2SCy Schubert 		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
1567780fb4a2SCy Schubert 			   " since no room for additional STA",
1568780fb4a2SCy Schubert 			   hapd->conf->iface, MAC2STR(mgmt->sa));
1569780fb4a2SCy Schubert 		return;
1570780fb4a2SCy Schubert 	}
1571780fb4a2SCy Schubert 
15725b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
15735b9c547cSRui Paulo 	if (hapd->iconf->ignore_probe_probability > 0.0 &&
15745b9c547cSRui Paulo 	    drand48() < hapd->iconf->ignore_probe_probability) {
15755b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
15765b9c547cSRui Paulo 			   "TESTING: ignoring probe request from " MACSTR,
15775b9c547cSRui Paulo 			   MAC2STR(mgmt->sa));
15785b9c547cSRui Paulo 		return;
15795b9c547cSRui Paulo 	}
15805b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
15815b9c547cSRui Paulo 
1582*a90b9d01SCy Schubert 	/* Do not send Probe Response frame from a non-transmitting multiple
1583*a90b9d01SCy Schubert 	 * BSSID profile unless the Probe Request frame is directed at that
1584*a90b9d01SCy Schubert 	 * particular BSS. */
1585*a90b9d01SCy Schubert 	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
1586*a90b9d01SCy Schubert 		return;
1587*a90b9d01SCy Schubert 
158885732ac8SCy Schubert 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
158985732ac8SCy Schubert 		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
159085732ac8SCy Schubert 
1591*a90b9d01SCy Schubert 	os_memset(&params, 0, sizeof(params));
1592*a90b9d01SCy Schubert 
1593*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
1594*a90b9d01SCy Schubert 	if (hapd->conf->mld_ap && elems.probe_req_mle &&
1595*a90b9d01SCy Schubert 	    parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle,
1596*a90b9d01SCy Schubert 			       elems.probe_req_mle_len, &mld_id, &links)) {
1597*a90b9d01SCy Schubert 		hostapd_fill_probe_resp_ml_params(hapd, &params, mgmt,
1598*a90b9d01SCy Schubert 						  mld_id, links);
1599*a90b9d01SCy Schubert 	}
1600*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
1601*a90b9d01SCy Schubert 
1602*a90b9d01SCy Schubert 	params.req = mgmt;
1603*a90b9d01SCy Schubert 	params.is_p2p = !!elems.p2p;
1604*a90b9d01SCy Schubert 	params.known_bss = elems.mbssid_known_bss;
1605*a90b9d01SCy Schubert 	params.known_bss_len = elems.mbssid_known_bss_len;
1606*a90b9d01SCy Schubert 	params.is_ml_sta_info = false;
1607*a90b9d01SCy Schubert 
1608*a90b9d01SCy Schubert 	hostapd_gen_probe_resp(hapd, &params);
1609*a90b9d01SCy Schubert 
1610*a90b9d01SCy Schubert 	hostapd_free_probe_resp_params(&params);
1611*a90b9d01SCy Schubert 
1612*a90b9d01SCy Schubert 	if (!params.resp)
1613f05cddf9SRui Paulo 		return;
1614f05cddf9SRui Paulo 
1615f05cddf9SRui Paulo 	/*
1616f05cddf9SRui Paulo 	 * If this is a broadcast probe request, apply no ack policy to avoid
1617f05cddf9SRui Paulo 	 * excessive retries.
1618f05cddf9SRui Paulo 	 */
1619f05cddf9SRui Paulo 	noack = !!(res == WILDCARD_SSID_MATCH &&
1620f05cddf9SRui Paulo 		   is_broadcast_ether_addr(mgmt->da));
1621f05cddf9SRui Paulo 
1622780fb4a2SCy Schubert 	csa_offs_len = 0;
1623780fb4a2SCy Schubert 	if (hapd->csa_in_progress) {
1624*a90b9d01SCy Schubert 		if (params.csa_pos)
1625780fb4a2SCy Schubert 			csa_offs[csa_offs_len++] =
1626*a90b9d01SCy Schubert 				params.csa_pos - (u8 *) params.resp;
1627780fb4a2SCy Schubert 
1628*a90b9d01SCy Schubert 		if (params.ecsa_pos)
1629780fb4a2SCy Schubert 			csa_offs[csa_offs_len++] =
1630*a90b9d01SCy Schubert 				params.ecsa_pos - (u8 *) params.resp;
1631780fb4a2SCy Schubert 	}
1632780fb4a2SCy Schubert 
1633*a90b9d01SCy Schubert 	ret = hostapd_drv_send_mlme(hapd, params.resp, params.resp_len, noack,
1634780fb4a2SCy Schubert 				    csa_offs_len ? csa_offs : NULL,
1635c1d255d3SCy Schubert 				    csa_offs_len, 0);
1636780fb4a2SCy Schubert 
1637780fb4a2SCy Schubert 	if (ret < 0)
16385b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "handle_probe_req: send failed");
1639e28a4053SRui Paulo 
1640*a90b9d01SCy Schubert 	os_free(params.resp);
1641e28a4053SRui Paulo 
1642f05cddf9SRui Paulo 	wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
1643e28a4053SRui Paulo 		   "SSID", MAC2STR(mgmt->sa),
1644e28a4053SRui Paulo 		   elems.ssid_len == 0 ? "broadcast" : "our");
1645e28a4053SRui Paulo }
1646e28a4053SRui Paulo 
1647e28a4053SRui Paulo 
hostapd_probe_resp_offloads(struct hostapd_data * hapd,size_t * resp_len)1648f05cddf9SRui Paulo static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
1649f05cddf9SRui Paulo 					size_t *resp_len)
1650f05cddf9SRui Paulo {
1651*a90b9d01SCy Schubert 	struct probe_resp_params params;
1652*a90b9d01SCy Schubert 
1653f05cddf9SRui Paulo 	/* check probe response offloading caps and print warnings */
1654f05cddf9SRui Paulo 	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
1655f05cddf9SRui Paulo 		return NULL;
1656f05cddf9SRui Paulo 
1657f05cddf9SRui Paulo #ifdef CONFIG_WPS
1658f05cddf9SRui Paulo 	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
1659f05cddf9SRui Paulo 	    (!(hapd->iface->probe_resp_offloads &
1660f05cddf9SRui Paulo 	       (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
1661f05cddf9SRui Paulo 		WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
1662f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
1663f05cddf9SRui Paulo 			   "Probe Response while not supporting this");
1664f05cddf9SRui Paulo #endif /* CONFIG_WPS */
1665f05cddf9SRui Paulo 
1666f05cddf9SRui Paulo #ifdef CONFIG_P2P
1667f05cddf9SRui Paulo 	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
1668f05cddf9SRui Paulo 	    !(hapd->iface->probe_resp_offloads &
1669f05cddf9SRui Paulo 	      WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
1670f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
1671f05cddf9SRui Paulo 			   "Probe Response while not supporting this");
1672f05cddf9SRui Paulo #endif  /* CONFIG_P2P */
1673f05cddf9SRui Paulo 
1674f05cddf9SRui Paulo 	if (hapd->conf->interworking &&
1675f05cddf9SRui Paulo 	    !(hapd->iface->probe_resp_offloads &
1676f05cddf9SRui Paulo 	      WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
1677f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "Device is trying to offload "
1678f05cddf9SRui Paulo 			   "Interworking Probe Response while not supporting "
1679f05cddf9SRui Paulo 			   "this");
1680f05cddf9SRui Paulo 
1681f05cddf9SRui Paulo 	/* Generate a Probe Response template for the non-P2P case */
1682*a90b9d01SCy Schubert 	os_memset(&params, 0, sizeof(params));
1683*a90b9d01SCy Schubert 	params.req = NULL;
1684*a90b9d01SCy Schubert 	params.is_p2p = false;
1685*a90b9d01SCy Schubert 	params.known_bss = NULL;
1686*a90b9d01SCy Schubert 	params.known_bss_len = 0;
1687*a90b9d01SCy Schubert 	params.is_ml_sta_info = false;
1688*a90b9d01SCy Schubert 	params.mld_ap = NULL;
1689*a90b9d01SCy Schubert 	params.mld_info = NULL;
1690*a90b9d01SCy Schubert 
1691*a90b9d01SCy Schubert 	hostapd_gen_probe_resp(hapd, &params);
1692*a90b9d01SCy Schubert 	*resp_len = params.resp_len;
1693*a90b9d01SCy Schubert 	if (!params.resp)
1694*a90b9d01SCy Schubert 		return NULL;
1695*a90b9d01SCy Schubert 
1696*a90b9d01SCy Schubert 	/* TODO: Avoid passing these through struct hostapd_data */
1697*a90b9d01SCy Schubert 	if (params.csa_pos)
1698*a90b9d01SCy Schubert 		hapd->cs_c_off_proberesp = params.csa_pos - (u8 *) params.resp;
1699*a90b9d01SCy Schubert 	if (params.ecsa_pos)
1700*a90b9d01SCy Schubert 		hapd->cs_c_off_ecsa_proberesp = params.ecsa_pos -
1701*a90b9d01SCy Schubert 			(u8 *) params.resp;
1702*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AX
1703*a90b9d01SCy Schubert 	if (params.cca_pos)
1704*a90b9d01SCy Schubert 		hapd->cca_c_off_proberesp = params.cca_pos - (u8 *) params.resp;
1705*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AX */
1706*a90b9d01SCy Schubert 
1707*a90b9d01SCy Schubert 	return (u8 *) params.resp;
1708f05cddf9SRui Paulo }
1709f05cddf9SRui Paulo 
1710f05cddf9SRui Paulo #endif /* NEED_AP_MLME */
1711f05cddf9SRui Paulo 
1712f05cddf9SRui Paulo 
1713c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX
1714c1d255d3SCy Schubert /* Unsolicited broadcast Probe Response transmission, 6 GHz only */
hostapd_unsol_bcast_probe_resp(struct hostapd_data * hapd,struct unsol_bcast_probe_resp * ubpr)1715*a90b9d01SCy Schubert u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
1716*a90b9d01SCy Schubert 				    struct unsol_bcast_probe_resp *ubpr)
1717c1d255d3SCy Schubert {
1718*a90b9d01SCy Schubert 	struct probe_resp_params probe_params;
1719*a90b9d01SCy Schubert 
1720c1d255d3SCy Schubert 	if (!is_6ghz_op_class(hapd->iconf->op_class))
1721c1d255d3SCy Schubert 		return NULL;
1722c1d255d3SCy Schubert 
1723*a90b9d01SCy Schubert 	ubpr->unsol_bcast_probe_resp_interval =
1724c1d255d3SCy Schubert 		hapd->conf->unsol_bcast_probe_resp_interval;
1725c1d255d3SCy Schubert 
1726*a90b9d01SCy Schubert 	os_memset(&probe_params, 0, sizeof(probe_params));
1727*a90b9d01SCy Schubert 	probe_params.req = NULL;
1728*a90b9d01SCy Schubert 	probe_params.is_p2p = false;
1729*a90b9d01SCy Schubert 	probe_params.known_bss = NULL;
1730*a90b9d01SCy Schubert 	probe_params.known_bss_len = 0;
1731*a90b9d01SCy Schubert 	probe_params.is_ml_sta_info = false;
1732*a90b9d01SCy Schubert 	probe_params.mld_ap = NULL;
1733*a90b9d01SCy Schubert 	probe_params.mld_info = NULL;
1734*a90b9d01SCy Schubert 
1735*a90b9d01SCy Schubert 	hostapd_gen_probe_resp(hapd, &probe_params);
1736*a90b9d01SCy Schubert 	ubpr->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len;
1737*a90b9d01SCy Schubert 	return (u8 *) probe_params.resp;
1738c1d255d3SCy Schubert }
1739c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */
1740c1d255d3SCy Schubert 
1741c1d255d3SCy Schubert 
sta_track_del(struct hostapd_sta_info * info)1742780fb4a2SCy Schubert void sta_track_del(struct hostapd_sta_info *info)
1743780fb4a2SCy Schubert {
1744780fb4a2SCy Schubert #ifdef CONFIG_TAXONOMY
1745780fb4a2SCy Schubert 	wpabuf_free(info->probe_ie_taxonomy);
1746780fb4a2SCy Schubert 	info->probe_ie_taxonomy = NULL;
1747780fb4a2SCy Schubert #endif /* CONFIG_TAXONOMY */
1748780fb4a2SCy Schubert 	os_free(info);
1749780fb4a2SCy Schubert }
1750780fb4a2SCy Schubert 
1751780fb4a2SCy Schubert 
1752c1d255d3SCy Schubert #ifdef CONFIG_FILS
1753c1d255d3SCy Schubert 
hostapd_gen_fils_discovery_phy_index(struct hostapd_data * hapd)1754*a90b9d01SCy Schubert static u16 hostapd_gen_fils_discovery_phy_index(struct hostapd_data *hapd)
1755*a90b9d01SCy Schubert {
1756*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
1757*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be)
1758*a90b9d01SCy Schubert 		return FD_CAP_PHY_INDEX_EHT;
1759*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
1760*a90b9d01SCy Schubert 
1761*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AX
1762*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
1763*a90b9d01SCy Schubert 		return FD_CAP_PHY_INDEX_HE;
1764*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AX */
1765*a90b9d01SCy Schubert 
1766*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AC
1767*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
1768*a90b9d01SCy Schubert 		return FD_CAP_PHY_INDEX_VHT;
1769*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AC */
1770*a90b9d01SCy Schubert 
1771*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
1772*a90b9d01SCy Schubert 		return FD_CAP_PHY_INDEX_HT;
1773*a90b9d01SCy Schubert 
1774*a90b9d01SCy Schubert 	return 0;
1775*a90b9d01SCy Schubert }
1776*a90b9d01SCy Schubert 
1777*a90b9d01SCy Schubert 
hostapd_gen_fils_discovery_nss(struct hostapd_hw_modes * mode,u16 phy_index,u8 he_mcs_nss_size)1778*a90b9d01SCy Schubert static u16 hostapd_gen_fils_discovery_nss(struct hostapd_hw_modes *mode,
1779*a90b9d01SCy Schubert 					  u16 phy_index, u8 he_mcs_nss_size)
1780*a90b9d01SCy Schubert {
1781*a90b9d01SCy Schubert 	u16 nss = 0;
1782*a90b9d01SCy Schubert 
1783*a90b9d01SCy Schubert 	if (!mode)
1784*a90b9d01SCy Schubert 		return 0;
1785*a90b9d01SCy Schubert 
1786*a90b9d01SCy Schubert 	if (phy_index == FD_CAP_PHY_INDEX_HE) {
1787*a90b9d01SCy Schubert 		const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs;
1788*a90b9d01SCy Schubert 		int i;
1789*a90b9d01SCy Schubert 		u16 mcs[6];
1790*a90b9d01SCy Schubert 
1791*a90b9d01SCy Schubert 		os_memset(mcs, 0xff, 6 * sizeof(u16));
1792*a90b9d01SCy Schubert 
1793*a90b9d01SCy Schubert 		if (he_mcs_nss_size == 4) {
1794*a90b9d01SCy Schubert 			mcs[0] = WPA_GET_LE16(&he_mcs[0]);
1795*a90b9d01SCy Schubert 			mcs[1] = WPA_GET_LE16(&he_mcs[2]);
1796*a90b9d01SCy Schubert 		}
1797*a90b9d01SCy Schubert 
1798*a90b9d01SCy Schubert 		if (he_mcs_nss_size == 8) {
1799*a90b9d01SCy Schubert 			mcs[2] = WPA_GET_LE16(&he_mcs[4]);
1800*a90b9d01SCy Schubert 			mcs[3] = WPA_GET_LE16(&he_mcs[6]);
1801*a90b9d01SCy Schubert 		}
1802*a90b9d01SCy Schubert 
1803*a90b9d01SCy Schubert 		if (he_mcs_nss_size == 12) {
1804*a90b9d01SCy Schubert 			mcs[4] = WPA_GET_LE16(&he_mcs[8]);
1805*a90b9d01SCy Schubert 			mcs[5] = WPA_GET_LE16(&he_mcs[10]);
1806*a90b9d01SCy Schubert 		}
1807*a90b9d01SCy Schubert 
1808*a90b9d01SCy Schubert 		for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
1809*a90b9d01SCy Schubert 			u16 nss_mask = 0x3 << (i * 2);
1810*a90b9d01SCy Schubert 
1811*a90b9d01SCy Schubert 			/*
1812*a90b9d01SCy Schubert 			 * If Tx and/or Rx indicate support for a given NSS,
1813*a90b9d01SCy Schubert 			 * count it towards the maximum NSS.
1814*a90b9d01SCy Schubert 			 */
1815*a90b9d01SCy Schubert 			if (he_mcs_nss_size == 4 &&
1816*a90b9d01SCy Schubert 			    (((mcs[0] & nss_mask) != nss_mask) ||
1817*a90b9d01SCy Schubert 			     ((mcs[1] & nss_mask) != nss_mask))) {
1818*a90b9d01SCy Schubert 				nss++;
1819*a90b9d01SCy Schubert 				continue;
1820*a90b9d01SCy Schubert 			}
1821*a90b9d01SCy Schubert 
1822*a90b9d01SCy Schubert 			if (he_mcs_nss_size == 8 &&
1823*a90b9d01SCy Schubert 			    (((mcs[2] & nss_mask) != nss_mask) ||
1824*a90b9d01SCy Schubert 			     ((mcs[3] & nss_mask) != nss_mask))) {
1825*a90b9d01SCy Schubert 				nss++;
1826*a90b9d01SCy Schubert 				continue;
1827*a90b9d01SCy Schubert 			}
1828*a90b9d01SCy Schubert 
1829*a90b9d01SCy Schubert 			if (he_mcs_nss_size == 12 &&
1830*a90b9d01SCy Schubert 			    (((mcs[4] & nss_mask) != nss_mask) ||
1831*a90b9d01SCy Schubert 			     ((mcs[5] & nss_mask) != nss_mask))) {
1832*a90b9d01SCy Schubert 				nss++;
1833*a90b9d01SCy Schubert 				continue;
1834*a90b9d01SCy Schubert 			}
1835*a90b9d01SCy Schubert 		}
1836*a90b9d01SCy Schubert 	} else if (phy_index == FD_CAP_PHY_INDEX_EHT) {
1837*a90b9d01SCy Schubert 		u8 rx_nss, tx_nss, max_nss = 0, i;
1838*a90b9d01SCy Schubert 		u8 *mcs = mode->eht_capab[IEEE80211_MODE_AP].mcs;
1839*a90b9d01SCy Schubert 
1840*a90b9d01SCy Schubert 		/*
1841*a90b9d01SCy Schubert 		 * The Supported EHT-MCS And NSS Set field for the AP contains
1842*a90b9d01SCy Schubert 		 * one to three EHT-MCS Map fields based on the supported
1843*a90b9d01SCy Schubert 		 * bandwidth. Check the first byte (max NSS for Rx/Tx that
1844*a90b9d01SCy Schubert 		 * supports EHT-MCS 0-9) for each bandwidth (<= 80,
1845*a90b9d01SCy Schubert 		 * 160, 320) to find the maximum NSS. This assumes that
1846*a90b9d01SCy Schubert 		 * the lowest MCS rates support the largest number of spatial
1847*a90b9d01SCy Schubert 		 * streams. If values are different between Tx, Rx or the
1848*a90b9d01SCy Schubert 		 * bandwidths, choose the highest value.
1849*a90b9d01SCy Schubert 		 */
1850*a90b9d01SCy Schubert 		for (i = 0; i < 3; i++) {
1851*a90b9d01SCy Schubert 			rx_nss = mcs[3 * i] & 0x0F;
1852*a90b9d01SCy Schubert 			if (rx_nss > max_nss)
1853*a90b9d01SCy Schubert 				max_nss = rx_nss;
1854*a90b9d01SCy Schubert 
1855*a90b9d01SCy Schubert 			tx_nss = (mcs[3 * i] & 0xF0) >> 4;
1856*a90b9d01SCy Schubert 			if (tx_nss > max_nss)
1857*a90b9d01SCy Schubert 				max_nss = tx_nss;
1858*a90b9d01SCy Schubert 		}
1859*a90b9d01SCy Schubert 
1860*a90b9d01SCy Schubert 		nss = max_nss;
1861*a90b9d01SCy Schubert 	}
1862*a90b9d01SCy Schubert 
1863*a90b9d01SCy Schubert 	if (nss > 4)
1864*a90b9d01SCy Schubert 		return FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
1865*a90b9d01SCy Schubert 	if (nss)
1866*a90b9d01SCy Schubert 		return (nss - 1) << FD_CAP_NSS_SHIFT;
1867*a90b9d01SCy Schubert 
1868*a90b9d01SCy Schubert 	return 0;
1869*a90b9d01SCy Schubert }
1870*a90b9d01SCy Schubert 
1871*a90b9d01SCy Schubert 
hostapd_fils_discovery_cap(struct hostapd_data * hapd)1872c1d255d3SCy Schubert static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd)
1873c1d255d3SCy Schubert {
1874*a90b9d01SCy Schubert 	u16 cap_info, phy_index;
1875*a90b9d01SCy Schubert 	u8 chwidth = FD_CAP_BSS_CHWIDTH_20, he_mcs_nss_size = 4;
1876c1d255d3SCy Schubert 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
1877c1d255d3SCy Schubert 
1878c1d255d3SCy Schubert 	cap_info = FD_CAP_ESS;
1879c1d255d3SCy Schubert 	if (hapd->conf->wpa)
1880c1d255d3SCy Schubert 		cap_info |= FD_CAP_PRIVACY;
1881c1d255d3SCy Schubert 
1882c1d255d3SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
1883c1d255d3SCy Schubert 		switch (hapd->iconf->op_class) {
1884*a90b9d01SCy Schubert 		case 137:
1885*a90b9d01SCy Schubert 			chwidth = FD_CAP_BSS_CHWIDTH_320;
1886*a90b9d01SCy Schubert 			break;
1887c1d255d3SCy Schubert 		case 135:
1888*a90b9d01SCy Schubert 			he_mcs_nss_size += 4;
1889c1d255d3SCy Schubert 			/* fallthrough */
1890c1d255d3SCy Schubert 		case 134:
1891*a90b9d01SCy Schubert 			he_mcs_nss_size += 4;
1892c1d255d3SCy Schubert 			chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
1893c1d255d3SCy Schubert 			break;
1894c1d255d3SCy Schubert 		case 133:
1895c1d255d3SCy Schubert 			chwidth = FD_CAP_BSS_CHWIDTH_80;
1896c1d255d3SCy Schubert 			break;
1897c1d255d3SCy Schubert 		case 132:
1898c1d255d3SCy Schubert 			chwidth = FD_CAP_BSS_CHWIDTH_40;
1899c1d255d3SCy Schubert 			break;
1900c1d255d3SCy Schubert 		}
1901c1d255d3SCy Schubert 	} else {
1902c1d255d3SCy Schubert 		switch (hostapd_get_oper_chwidth(hapd->iconf)) {
1903*a90b9d01SCy Schubert 		case CONF_OPER_CHWIDTH_80P80MHZ:
1904*a90b9d01SCy Schubert 			he_mcs_nss_size += 4;
1905c1d255d3SCy Schubert 			/* fallthrough */
1906*a90b9d01SCy Schubert 		case CONF_OPER_CHWIDTH_160MHZ:
1907*a90b9d01SCy Schubert 			he_mcs_nss_size += 4;
1908c1d255d3SCy Schubert 			chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
1909c1d255d3SCy Schubert 			break;
1910*a90b9d01SCy Schubert 		case CONF_OPER_CHWIDTH_80MHZ:
1911c1d255d3SCy Schubert 			chwidth = FD_CAP_BSS_CHWIDTH_80;
1912c1d255d3SCy Schubert 			break;
1913*a90b9d01SCy Schubert 		case CONF_OPER_CHWIDTH_USE_HT:
1914c1d255d3SCy Schubert 			if (hapd->iconf->secondary_channel)
1915c1d255d3SCy Schubert 				chwidth = FD_CAP_BSS_CHWIDTH_40;
1916c1d255d3SCy Schubert 			else
1917c1d255d3SCy Schubert 				chwidth = FD_CAP_BSS_CHWIDTH_20;
1918c1d255d3SCy Schubert 			break;
1919*a90b9d01SCy Schubert 		default:
1920*a90b9d01SCy Schubert 			break;
1921*a90b9d01SCy Schubert 		}
1922c1d255d3SCy Schubert 	}
1923c1d255d3SCy Schubert 
1924*a90b9d01SCy Schubert 	phy_index = hostapd_gen_fils_discovery_phy_index(hapd);
1925c1d255d3SCy Schubert 	cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
1926c1d255d3SCy Schubert 	cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
1927*a90b9d01SCy Schubert 	cap_info |= hostapd_gen_fils_discovery_nss(mode, phy_index,
1928*a90b9d01SCy Schubert 						   he_mcs_nss_size);
1929c1d255d3SCy Schubert 	return cap_info;
1930c1d255d3SCy Schubert }
1931c1d255d3SCy Schubert 
1932c1d255d3SCy Schubert 
hostapd_gen_fils_discovery(struct hostapd_data * hapd,size_t * len)1933c1d255d3SCy Schubert static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
1934c1d255d3SCy Schubert {
1935c1d255d3SCy Schubert 	struct ieee80211_mgmt *head;
1936c1d255d3SCy Schubert 	const u8 *mobility_domain;
1937c1d255d3SCy Schubert 	u8 *pos, *length_pos, buf[200];
1938c1d255d3SCy Schubert 	u16 ctl = 0;
1939c1d255d3SCy Schubert 	u8 fd_rsn_info[5];
1940c1d255d3SCy Schubert 	size_t total_len, buf_len;
1941c1d255d3SCy Schubert 
1942c1d255d3SCy Schubert 	total_len = 24 + 2 + 12;
1943c1d255d3SCy Schubert 
1944c1d255d3SCy Schubert 	/* FILS Discovery Frame Control */
1945c1d255d3SCy Schubert 	ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) |
1946c1d255d3SCy Schubert 		FD_FRAME_CTL_SHORT_SSID_PRESENT |
1947c1d255d3SCy Schubert 		FD_FRAME_CTL_LENGTH_PRESENT |
1948c1d255d3SCy Schubert 		FD_FRAME_CTL_CAP_PRESENT;
1949c1d255d3SCy Schubert 	total_len += 4 + 1 + 2;
1950c1d255d3SCy Schubert 
1951*a90b9d01SCy Schubert 	/* Fill primary channel information for 6 GHz channels with over 20 MHz
1952*a90b9d01SCy Schubert 	 * bandwidth, if the primary channel is not a PSC */
1953*a90b9d01SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
1954*a90b9d01SCy Schubert 	    !is_6ghz_psc_frequency(ieee80211_chan_to_freq(
1955*a90b9d01SCy Schubert 					   NULL, hapd->iconf->op_class,
1956*a90b9d01SCy Schubert 					   hapd->iconf->channel)) &&
1957*a90b9d01SCy Schubert 	    op_class_to_bandwidth(hapd->iconf->op_class) > 20) {
1958*a90b9d01SCy Schubert 		ctl |= FD_FRAME_CTL_PRI_CHAN_PRESENT;
1959*a90b9d01SCy Schubert 		total_len += 2;
1960*a90b9d01SCy Schubert 	}
1961*a90b9d01SCy Schubert 
1962c1d255d3SCy Schubert 	/* Check for optional subfields and calculate length */
1963c1d255d3SCy Schubert 	if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
1964c1d255d3SCy Schubert 		ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
1965c1d255d3SCy Schubert 		total_len += sizeof(fd_rsn_info);
1966c1d255d3SCy Schubert 	}
1967c1d255d3SCy Schubert 
1968c1d255d3SCy Schubert 	mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
1969c1d255d3SCy Schubert 	if (mobility_domain) {
1970c1d255d3SCy Schubert 		ctl |= FD_FRAME_CTL_MD_PRESENT;
1971c1d255d3SCy Schubert 		total_len += 3;
1972c1d255d3SCy Schubert 	}
1973c1d255d3SCy Schubert 
1974*a90b9d01SCy Schubert 	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
19754b72b91aSCy Schubert 
1976c1d255d3SCy Schubert 	pos = hostapd_eid_fils_indic(hapd, buf, 0);
1977c1d255d3SCy Schubert 	buf_len = pos - buf;
1978c1d255d3SCy Schubert 	total_len += buf_len;
1979c1d255d3SCy Schubert 
1980*a90b9d01SCy Schubert 	/* he_elem_len() may return too large a value for FD frame, but that is
1981*a90b9d01SCy Schubert 	 * fine here since this is used as the maximum length of the buffer. */
1982*a90b9d01SCy Schubert 	total_len += he_elem_len(hapd);
1983*a90b9d01SCy Schubert 
1984c1d255d3SCy Schubert 	head = os_zalloc(total_len);
1985c1d255d3SCy Schubert 	if (!head)
1986c1d255d3SCy Schubert 		return NULL;
1987c1d255d3SCy Schubert 
1988c1d255d3SCy Schubert 	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
1989c1d255d3SCy Schubert 					   WLAN_FC_STYPE_ACTION);
1990c1d255d3SCy Schubert 	os_memset(head->da, 0xff, ETH_ALEN);
1991c1d255d3SCy Schubert 	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
1992c1d255d3SCy Schubert 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
1993c1d255d3SCy Schubert 
1994c1d255d3SCy Schubert 	head->u.action.category = WLAN_ACTION_PUBLIC;
1995c1d255d3SCy Schubert 	head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY;
1996c1d255d3SCy Schubert 
1997c1d255d3SCy Schubert 	pos = &head->u.action.u.public_action.variable[0];
1998c1d255d3SCy Schubert 
1999c1d255d3SCy Schubert 	/* FILS Discovery Information field */
2000c1d255d3SCy Schubert 
2001c1d255d3SCy Schubert 	/* FILS Discovery Frame Control */
2002c1d255d3SCy Schubert 	WPA_PUT_LE16(pos, ctl);
2003c1d255d3SCy Schubert 	pos += 2;
2004c1d255d3SCy Schubert 
2005c1d255d3SCy Schubert 	/* Hardware or low-level driver will fill in the Timestamp value */
2006c1d255d3SCy Schubert 	pos += 8;
2007c1d255d3SCy Schubert 
2008c1d255d3SCy Schubert 	/* Beacon Interval */
2009c1d255d3SCy Schubert 	WPA_PUT_LE16(pos, hapd->iconf->beacon_int);
2010c1d255d3SCy Schubert 	pos += 2;
2011c1d255d3SCy Schubert 
2012c1d255d3SCy Schubert 	/* Short SSID */
2013c1d255d3SCy Schubert 	WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid);
2014c1d255d3SCy Schubert 	pos += sizeof(hapd->conf->ssid.short_ssid);
2015c1d255d3SCy Schubert 
2016c1d255d3SCy Schubert 	/* Store position of FILS discovery information element Length field */
2017c1d255d3SCy Schubert 	length_pos = pos++;
2018c1d255d3SCy Schubert 
2019c1d255d3SCy Schubert 	/* FD Capability */
2020c1d255d3SCy Schubert 	WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
2021c1d255d3SCy Schubert 	pos += 2;
2022c1d255d3SCy Schubert 
2023*a90b9d01SCy Schubert 	/* Operating Class and Primary Channel - if a 6 GHz chan is non PSC */
2024*a90b9d01SCy Schubert 	if (ctl & FD_FRAME_CTL_PRI_CHAN_PRESENT) {
2025*a90b9d01SCy Schubert 		*pos++ = hapd->iconf->op_class;
2026*a90b9d01SCy Schubert 		*pos++ = hapd->iconf->channel;
2027*a90b9d01SCy Schubert 	}
2028c1d255d3SCy Schubert 
2029c1d255d3SCy Schubert 	/* AP Configuration Sequence Number - not present */
2030c1d255d3SCy Schubert 
2031c1d255d3SCy Schubert 	/* Access Network Options - not present */
2032c1d255d3SCy Schubert 
2033c1d255d3SCy Schubert 	/* FD RSN Information */
2034c1d255d3SCy Schubert 	if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) {
2035c1d255d3SCy Schubert 		os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info));
2036c1d255d3SCy Schubert 		pos += sizeof(fd_rsn_info);
2037c1d255d3SCy Schubert 	}
2038c1d255d3SCy Schubert 
2039c1d255d3SCy Schubert 	/* Channel Center Frequency Segment 1 - not present */
2040c1d255d3SCy Schubert 
2041c1d255d3SCy Schubert 	/* Mobility Domain */
2042c1d255d3SCy Schubert 	if (ctl & FD_FRAME_CTL_MD_PRESENT) {
2043c1d255d3SCy Schubert 		os_memcpy(pos, &mobility_domain[2], 3);
2044c1d255d3SCy Schubert 		pos += 3;
2045c1d255d3SCy Schubert 	}
2046c1d255d3SCy Schubert 
2047c1d255d3SCy Schubert 	/* Fill in the Length field value */
2048c1d255d3SCy Schubert 	*length_pos = pos - (length_pos + 1);
2049c1d255d3SCy Schubert 
2050*a90b9d01SCy Schubert 	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
20514b72b91aSCy Schubert 
2052c1d255d3SCy Schubert 	/* FILS Indication element */
2053c1d255d3SCy Schubert 	if (buf_len) {
2054c1d255d3SCy Schubert 		os_memcpy(pos, buf, buf_len);
2055c1d255d3SCy Schubert 		pos += buf_len;
2056c1d255d3SCy Schubert 	}
2057c1d255d3SCy Schubert 
2058*a90b9d01SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class))
2059*a90b9d01SCy Schubert 		pos = hostapd_eid_txpower_envelope(hapd, pos);
2060*a90b9d01SCy Schubert 
2061c1d255d3SCy Schubert 	*len = pos - (u8 *) head;
2062c1d255d3SCy Schubert 	wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
2063c1d255d3SCy Schubert 		    head, pos - (u8 *) head);
2064c1d255d3SCy Schubert 	return (u8 *) head;
2065c1d255d3SCy Schubert }
2066c1d255d3SCy Schubert 
2067c1d255d3SCy Schubert 
2068c1d255d3SCy Schubert /* Configure FILS Discovery frame transmission parameters */
hostapd_fils_discovery(struct hostapd_data * hapd,struct wpa_driver_ap_params * params)2069c1d255d3SCy Schubert static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
2070c1d255d3SCy Schubert 				   struct wpa_driver_ap_params *params)
2071c1d255d3SCy Schubert {
2072c1d255d3SCy Schubert 	params->fd_max_int = hapd->conf->fils_discovery_max_int;
2073c1d255d3SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class) &&
2074c1d255d3SCy Schubert 	    params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
2075c1d255d3SCy Schubert 		params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
2076c1d255d3SCy Schubert 
2077c1d255d3SCy Schubert 	params->fd_min_int = hapd->conf->fils_discovery_min_int;
2078c1d255d3SCy Schubert 	if (params->fd_min_int > params->fd_max_int)
2079c1d255d3SCy Schubert 		params->fd_min_int = params->fd_max_int;
2080c1d255d3SCy Schubert 
2081c1d255d3SCy Schubert 	if (params->fd_max_int)
2082c1d255d3SCy Schubert 		return hostapd_gen_fils_discovery(hapd,
2083c1d255d3SCy Schubert 						  &params->fd_frame_tmpl_len);
2084c1d255d3SCy Schubert 
2085c1d255d3SCy Schubert 	return NULL;
2086c1d255d3SCy Schubert }
2087c1d255d3SCy Schubert 
2088c1d255d3SCy Schubert #endif /* CONFIG_FILS */
2089c1d255d3SCy Schubert 
2090c1d255d3SCy Schubert 
ieee802_11_build_ap_params(struct hostapd_data * hapd,struct wpa_driver_ap_params * params)20915b9c547cSRui Paulo int ieee802_11_build_ap_params(struct hostapd_data *hapd,
20925b9c547cSRui Paulo 			       struct wpa_driver_ap_params *params)
2093e28a4053SRui Paulo {
2094f05cddf9SRui Paulo 	struct ieee80211_mgmt *head = NULL;
2095f05cddf9SRui Paulo 	u8 *tail = NULL;
2096f05cddf9SRui Paulo 	size_t head_len = 0, tail_len = 0;
2097f05cddf9SRui Paulo 	u8 *resp = NULL;
2098f05cddf9SRui Paulo 	size_t resp_len = 0;
2099f05cddf9SRui Paulo #ifdef NEED_AP_MLME
2100e28a4053SRui Paulo 	u16 capab_info;
2101c1d255d3SCy Schubert 	u8 *pos, *tailpos, *tailend, *csa_pos;
2102*a90b9d01SCy Schubert 	bool complete = false;
2103*a90b9d01SCy Schubert #endif /* NEED_AP_MLME */
2104e28a4053SRui Paulo 
2105*a90b9d01SCy Schubert 	os_memset(params, 0, sizeof(*params));
2106*a90b9d01SCy Schubert 
2107*a90b9d01SCy Schubert #ifdef NEED_AP_MLME
2108e28a4053SRui Paulo #define BEACON_HEAD_BUF_SIZE 256
2109e28a4053SRui Paulo #define BEACON_TAIL_BUF_SIZE 512
2110e28a4053SRui Paulo 	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
2111e28a4053SRui Paulo 	tail_len = BEACON_TAIL_BUF_SIZE;
2112e28a4053SRui Paulo #ifdef CONFIG_WPS
2113e28a4053SRui Paulo 	if (hapd->conf->wps_state && hapd->wps_beacon_ie)
2114e28a4053SRui Paulo 		tail_len += wpabuf_len(hapd->wps_beacon_ie);
2115e28a4053SRui Paulo #endif /* CONFIG_WPS */
2116f05cddf9SRui Paulo #ifdef CONFIG_P2P
2117f05cddf9SRui Paulo 	if (hapd->p2p_beacon_ie)
2118f05cddf9SRui Paulo 		tail_len += wpabuf_len(hapd->p2p_beacon_ie);
2119f05cddf9SRui Paulo #endif /* CONFIG_P2P */
2120325151a3SRui Paulo #ifdef CONFIG_FST
2121325151a3SRui Paulo 	if (hapd->iface->fst_ies)
2122325151a3SRui Paulo 		tail_len += wpabuf_len(hapd->iface->fst_ies);
2123325151a3SRui Paulo #endif /* CONFIG_FST */
2124f05cddf9SRui Paulo 	if (hapd->conf->vendor_elements)
2125f05cddf9SRui Paulo 		tail_len += wpabuf_len(hapd->conf->vendor_elements);
21265b9c547cSRui Paulo 
21275b9c547cSRui Paulo #ifdef CONFIG_IEEE80211AC
21285b9c547cSRui Paulo 	if (hapd->conf->vendor_vht) {
21295b9c547cSRui Paulo 		tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
21305b9c547cSRui Paulo 			2 + sizeof(struct ieee80211_vht_operation);
21315b9c547cSRui Paulo 	}
21325b9c547cSRui Paulo #endif /* CONFIG_IEEE80211AC */
21335b9c547cSRui Paulo 
2134*a90b9d01SCy Schubert 	tail_len += he_elem_len(hapd);
213585732ac8SCy Schubert 
2136*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2137*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
2138*a90b9d01SCy Schubert 		tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
2139*a90b9d01SCy Schubert 		tail_len += 3 + sizeof(struct ieee80211_eht_operation);
2140*a90b9d01SCy Schubert 		if (hapd->iconf->punct_bitmap)
2141*a90b9d01SCy Schubert 			tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
2142*a90b9d01SCy Schubert 
2143*a90b9d01SCy Schubert 		/*
2144*a90b9d01SCy Schubert 		 * TODO: Multi-Link element has variable length and can be
2145*a90b9d01SCy Schubert 		 * long based on the common info and number of per
2146*a90b9d01SCy Schubert 		 * station profiles. For now use 256.
2147*a90b9d01SCy Schubert 		 */
2148*a90b9d01SCy Schubert 		if (hapd->conf->mld_ap)
2149*a90b9d01SCy Schubert 			tail_len += 256;
2150*a90b9d01SCy Schubert 	}
2151*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2152*a90b9d01SCy Schubert 
2153*a90b9d01SCy Schubert 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
2154*a90b9d01SCy Schubert 	    hapd == hostapd_mbssid_get_tx_bss(hapd))
2155*a90b9d01SCy Schubert 		tail_len += 5; /* Multiple BSSID Configuration element */
2156*a90b9d01SCy Schubert 	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
2157780fb4a2SCy Schubert 	tail_len += hostapd_mbo_ie_len(hapd);
215885732ac8SCy Schubert 	tail_len += hostapd_eid_owe_trans_len(hapd);
2159c1d255d3SCy Schubert 	tail_len += hostapd_eid_dpp_cc_len(hapd);
2160780fb4a2SCy Schubert 
2161e28a4053SRui Paulo 	tailpos = tail = os_malloc(tail_len);
2162e28a4053SRui Paulo 	if (head == NULL || tail == NULL) {
2163e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "Failed to set beacon data");
2164e28a4053SRui Paulo 		os_free(head);
2165e28a4053SRui Paulo 		os_free(tail);
21665b9c547cSRui Paulo 		return -1;
2167e28a4053SRui Paulo 	}
2168c1d255d3SCy Schubert 	tailend = tail + tail_len;
2169e28a4053SRui Paulo 
2170e28a4053SRui Paulo 	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
2171e28a4053SRui Paulo 					   WLAN_FC_STYPE_BEACON);
2172e28a4053SRui Paulo 	head->duration = host_to_le16(0);
2173e28a4053SRui Paulo 	os_memset(head->da, 0xff, ETH_ALEN);
2174e28a4053SRui Paulo 
2175e28a4053SRui Paulo 	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
2176e28a4053SRui Paulo 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
2177e28a4053SRui Paulo 	head->u.beacon.beacon_int =
2178e28a4053SRui Paulo 		host_to_le16(hapd->iconf->beacon_int);
2179e28a4053SRui Paulo 
2180e28a4053SRui Paulo 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
2181325151a3SRui Paulo 	capab_info = hostapd_own_capab_info(hapd);
2182e28a4053SRui Paulo 	head->u.beacon.capab_info = host_to_le16(capab_info);
2183e28a4053SRui Paulo 	pos = &head->u.beacon.variable[0];
2184e28a4053SRui Paulo 
2185e28a4053SRui Paulo 	/* SSID */
2186e28a4053SRui Paulo 	*pos++ = WLAN_EID_SSID;
2187e28a4053SRui Paulo 	if (hapd->conf->ignore_broadcast_ssid == 2) {
2188e28a4053SRui Paulo 		/* clear the data, but keep the correct length of the SSID */
2189e28a4053SRui Paulo 		*pos++ = hapd->conf->ssid.ssid_len;
2190e28a4053SRui Paulo 		os_memset(pos, 0, hapd->conf->ssid.ssid_len);
2191e28a4053SRui Paulo 		pos += hapd->conf->ssid.ssid_len;
2192e28a4053SRui Paulo 	} else if (hapd->conf->ignore_broadcast_ssid) {
2193e28a4053SRui Paulo 		*pos++ = 0; /* empty SSID */
2194e28a4053SRui Paulo 	} else {
2195e28a4053SRui Paulo 		*pos++ = hapd->conf->ssid.ssid_len;
2196e28a4053SRui Paulo 		os_memcpy(pos, hapd->conf->ssid.ssid,
2197e28a4053SRui Paulo 			  hapd->conf->ssid.ssid_len);
2198e28a4053SRui Paulo 		pos += hapd->conf->ssid.ssid_len;
2199e28a4053SRui Paulo 	}
2200e28a4053SRui Paulo 
2201e28a4053SRui Paulo 	/* Supported rates */
2202e28a4053SRui Paulo 	pos = hostapd_eid_supp_rates(hapd, pos);
2203e28a4053SRui Paulo 
2204e28a4053SRui Paulo 	/* DS Params */
2205e28a4053SRui Paulo 	pos = hostapd_eid_ds_params(hapd, pos);
2206e28a4053SRui Paulo 
2207e28a4053SRui Paulo 	head_len = pos - (u8 *) head;
2208e28a4053SRui Paulo 
2209c1d255d3SCy Schubert 	tailpos = hostapd_eid_country(hapd, tailpos, tailend - tailpos);
2210e28a4053SRui Paulo 
22115b9c547cSRui Paulo 	/* Power Constraint element */
22125b9c547cSRui Paulo 	tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
22135b9c547cSRui Paulo 
2214780fb4a2SCy Schubert 	/* CSA IE */
2215780fb4a2SCy Schubert 	csa_pos = hostapd_eid_csa(hapd, tailpos);
2216780fb4a2SCy Schubert 	if (csa_pos != tailpos)
2217780fb4a2SCy Schubert 		hapd->cs_c_off_beacon = csa_pos - tail - 1;
2218780fb4a2SCy Schubert 	tailpos = csa_pos;
2219780fb4a2SCy Schubert 
2220e28a4053SRui Paulo 	/* ERP Information element */
2221e28a4053SRui Paulo 	tailpos = hostapd_eid_erp_info(hapd, tailpos);
2222e28a4053SRui Paulo 
2223e28a4053SRui Paulo 	/* Extended supported rates */
2224e28a4053SRui Paulo 	tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
2225e28a4053SRui Paulo 
2226c1d255d3SCy Schubert 	tailpos = hostapd_get_rsne(hapd, tailpos, tailend - tailpos);
2227c1d255d3SCy Schubert 	tailpos = hostapd_eid_bss_load(hapd, tailpos, tailend - tailpos);
22285b9c547cSRui Paulo 	tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
2229c1d255d3SCy Schubert 					       tailend - tailpos);
2230c1d255d3SCy Schubert 	tailpos = hostapd_get_mde(hapd, tailpos, tailend - tailpos);
22315b9c547cSRui Paulo 
2232780fb4a2SCy Schubert 	/* eCSA IE */
2233780fb4a2SCy Schubert 	csa_pos = hostapd_eid_ecsa(hapd, tailpos);
2234780fb4a2SCy Schubert 	if (csa_pos != tailpos)
2235780fb4a2SCy Schubert 		hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
2236780fb4a2SCy Schubert 	tailpos = csa_pos;
2237780fb4a2SCy Schubert 
2238780fb4a2SCy Schubert 	tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
2239e28a4053SRui Paulo 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
2240e28a4053SRui Paulo 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
2241e28a4053SRui Paulo 
2242*a90b9d01SCy Schubert 	if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
2243*a90b9d01SCy Schubert 		if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
2244*a90b9d01SCy Schubert 			os_free(head);
2245*a90b9d01SCy Schubert 			os_free(tail);
2246*a90b9d01SCy Schubert 			wpa_printf(MSG_ERROR,
2247*a90b9d01SCy Schubert 				   "MBSSID: Failed to set beacon data");
2248*a90b9d01SCy Schubert 			return -1;
2249*a90b9d01SCy Schubert 		}
2250*a90b9d01SCy Schubert 		complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
2251*a90b9d01SCy Schubert 			(hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
2252*a90b9d01SCy Schubert 			 params->mbssid_elem_count == 1);
2253*a90b9d01SCy Schubert 	}
2254*a90b9d01SCy Schubert 
2255*a90b9d01SCy Schubert 	tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
2256f05cddf9SRui Paulo 
2257f05cddf9SRui Paulo 	/*
2258f05cddf9SRui Paulo 	 * TODO: Time Advertisement element should only be included in some
2259f05cddf9SRui Paulo 	 * DTIM Beacon frames.
2260f05cddf9SRui Paulo 	 */
2261f05cddf9SRui Paulo 	tailpos = hostapd_eid_time_adv(hapd, tailpos);
2262f05cddf9SRui Paulo 
2263f05cddf9SRui Paulo 	tailpos = hostapd_eid_interworking(hapd, tailpos);
2264f05cddf9SRui Paulo 	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
2265f05cddf9SRui Paulo 	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
2266325151a3SRui Paulo 
2267325151a3SRui Paulo #ifdef CONFIG_FST
2268325151a3SRui Paulo 	if (hapd->iface->fst_ies) {
2269325151a3SRui Paulo 		os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
2270325151a3SRui Paulo 			  wpabuf_len(hapd->iface->fst_ies));
2271325151a3SRui Paulo 		tailpos += wpabuf_len(hapd->iface->fst_ies);
2272325151a3SRui Paulo 	}
2273325151a3SRui Paulo #endif /* CONFIG_FST */
2274325151a3SRui Paulo 
2275f05cddf9SRui Paulo #ifdef CONFIG_IEEE80211AC
2276c1d255d3SCy Schubert 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
2277c1d255d3SCy Schubert 	    !is_6ghz_op_class(hapd->iconf->op_class)) {
2278780fb4a2SCy Schubert 		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
2279f05cddf9SRui Paulo 		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
2280780fb4a2SCy Schubert 		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
22815b9c547cSRui Paulo 	}
228285732ac8SCy Schubert #endif /* CONFIG_IEEE80211AC */
228385732ac8SCy Schubert 
2284c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX
2285c1d255d3SCy Schubert 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
2286c1d255d3SCy Schubert 	    is_6ghz_op_class(hapd->iconf->op_class))
2287c1d255d3SCy Schubert 		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
2288c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */
2289c1d255d3SCy Schubert 
2290c1d255d3SCy Schubert 	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
2291c1d255d3SCy Schubert 
2292*a90b9d01SCy Schubert 	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
229385732ac8SCy Schubert 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
2294c1d255d3SCy Schubert 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
2295*a90b9d01SCy Schubert 	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
2296*a90b9d01SCy Schubert 					    params->mbssid_elem_count);
229785732ac8SCy Schubert 
229885732ac8SCy Schubert #ifdef CONFIG_IEEE80211AX
2299c1d255d3SCy Schubert 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
2300*a90b9d01SCy Schubert 		u8 *cca_pos;
2301*a90b9d01SCy Schubert 
2302206b73d0SCy Schubert 		tailpos = hostapd_eid_he_capab(hapd, tailpos,
2303206b73d0SCy Schubert 					       IEEE80211_MODE_AP);
230485732ac8SCy Schubert 		tailpos = hostapd_eid_he_operation(hapd, tailpos);
2305*a90b9d01SCy Schubert 
2306*a90b9d01SCy Schubert 		/* BSS Color Change Announcement element */
2307*a90b9d01SCy Schubert 		cca_pos = hostapd_eid_cca(hapd, tailpos);
2308*a90b9d01SCy Schubert 		if (cca_pos != tailpos)
2309*a90b9d01SCy Schubert 			hapd->cca_c_off_beacon = cca_pos - tail - 2;
2310*a90b9d01SCy Schubert 		tailpos = cca_pos;
2311*a90b9d01SCy Schubert 
2312206b73d0SCy Schubert 		tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
2313c1d255d3SCy Schubert 		tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
2314c1d255d3SCy Schubert 		tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
231585732ac8SCy Schubert 	}
231685732ac8SCy Schubert #endif /* CONFIG_IEEE80211AX */
231785732ac8SCy Schubert 
2318*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2319*a90b9d01SCy Schubert 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
2320*a90b9d01SCy Schubert 		if (hapd->conf->mld_ap)
2321*a90b9d01SCy Schubert 			tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
2322*a90b9d01SCy Schubert 							    tailpos, false);
2323*a90b9d01SCy Schubert 		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
2324*a90b9d01SCy Schubert 						IEEE80211_MODE_AP);
2325*a90b9d01SCy Schubert 		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
2326*a90b9d01SCy Schubert 	}
2327*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2328*a90b9d01SCy Schubert 
232985732ac8SCy Schubert #ifdef CONFIG_IEEE80211AC
23305b9c547cSRui Paulo 	if (hapd->conf->vendor_vht)
23315b9c547cSRui Paulo 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
2332f05cddf9SRui Paulo #endif /* CONFIG_IEEE80211AC */
2333f05cddf9SRui Paulo 
2334c1d255d3SCy Schubert 	/* WPA / OSEN */
2335c1d255d3SCy Schubert 	tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
2336c1d255d3SCy Schubert 	tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
233785732ac8SCy Schubert 
2338e28a4053SRui Paulo 	/* Wi-Fi Alliance WMM */
2339e28a4053SRui Paulo 	tailpos = hostapd_eid_wmm(hapd, tailpos);
2340e28a4053SRui Paulo 
2341e28a4053SRui Paulo #ifdef CONFIG_WPS
2342e28a4053SRui Paulo 	if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
2343e28a4053SRui Paulo 		os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
2344e28a4053SRui Paulo 			  wpabuf_len(hapd->wps_beacon_ie));
2345e28a4053SRui Paulo 		tailpos += wpabuf_len(hapd->wps_beacon_ie);
2346e28a4053SRui Paulo 	}
2347e28a4053SRui Paulo #endif /* CONFIG_WPS */
2348e28a4053SRui Paulo 
2349f05cddf9SRui Paulo #ifdef CONFIG_P2P
2350f05cddf9SRui Paulo 	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
2351f05cddf9SRui Paulo 		os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
2352f05cddf9SRui Paulo 			  wpabuf_len(hapd->p2p_beacon_ie));
2353f05cddf9SRui Paulo 		tailpos += wpabuf_len(hapd->p2p_beacon_ie);
2354f05cddf9SRui Paulo 	}
2355f05cddf9SRui Paulo #endif /* CONFIG_P2P */
2356f05cddf9SRui Paulo #ifdef CONFIG_P2P_MANAGER
2357f05cddf9SRui Paulo 	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
2358f05cddf9SRui Paulo 	    P2P_MANAGE)
2359f05cddf9SRui Paulo 		tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
2360f05cddf9SRui Paulo #endif /* CONFIG_P2P_MANAGER */
2361f05cddf9SRui Paulo 
2362f05cddf9SRui Paulo #ifdef CONFIG_HS20
2363f05cddf9SRui Paulo 	tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
2364f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
2365f05cddf9SRui Paulo 
2366780fb4a2SCy Schubert 	tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
236785732ac8SCy Schubert 	tailpos = hostapd_eid_owe_trans(hapd, tailpos,
236885732ac8SCy Schubert 					tail + tail_len - tailpos);
2369c1d255d3SCy Schubert 	tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
2370780fb4a2SCy Schubert 
2371f05cddf9SRui Paulo 	if (hapd->conf->vendor_elements) {
2372f05cddf9SRui Paulo 		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
2373f05cddf9SRui Paulo 			  wpabuf_len(hapd->conf->vendor_elements));
2374f05cddf9SRui Paulo 		tailpos += wpabuf_len(hapd->conf->vendor_elements);
2375f05cddf9SRui Paulo 	}
2376f05cddf9SRui Paulo 
2377e28a4053SRui Paulo 	tail_len = tailpos > tail ? tailpos - tail : 0;
2378e28a4053SRui Paulo 
2379f05cddf9SRui Paulo 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
2380f05cddf9SRui Paulo #endif /* NEED_AP_MLME */
2381f05cddf9SRui Paulo 
2382*a90b9d01SCy Schubert 	/* If key management offload is enabled, configure PSK to the driver. */
2383*a90b9d01SCy Schubert 	if (wpa_key_mgmt_wpa_psk_no_sae(hapd->conf->wpa_key_mgmt) &&
2384*a90b9d01SCy Schubert 	    (hapd->iface->drv_flags2 &
2385*a90b9d01SCy Schubert 	     WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) {
2386*a90b9d01SCy Schubert 		if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) {
2387*a90b9d01SCy Schubert 			os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk,
2388*a90b9d01SCy Schubert 				  PMK_LEN);
2389*a90b9d01SCy Schubert 			params->psk_len = PMK_LEN;
2390*a90b9d01SCy Schubert 		} else if (hapd->conf->ssid.wpa_passphrase &&
2391*a90b9d01SCy Schubert 			   pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase,
2392*a90b9d01SCy Schubert 				       hapd->conf->ssid.ssid,
2393*a90b9d01SCy Schubert 				       hapd->conf->ssid.ssid_len, 4096,
2394*a90b9d01SCy Schubert 				       params->psk, PMK_LEN) == 0) {
2395*a90b9d01SCy Schubert 			params->psk_len = PMK_LEN;
2396*a90b9d01SCy Schubert 		}
2397*a90b9d01SCy Schubert 	}
2398*a90b9d01SCy Schubert 
2399*a90b9d01SCy Schubert #ifdef CONFIG_SAE
2400*a90b9d01SCy Schubert 	/* If SAE offload is enabled, provide password to lower layer for
2401*a90b9d01SCy Schubert 	 * SAE authentication and PMK generation.
2402*a90b9d01SCy Schubert 	 */
2403*a90b9d01SCy Schubert 	if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
2404*a90b9d01SCy Schubert 	    (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
2405*a90b9d01SCy Schubert 		if (hostapd_sae_pk_in_use(hapd->conf)) {
2406*a90b9d01SCy Schubert 			wpa_printf(MSG_ERROR,
2407*a90b9d01SCy Schubert 				   "SAE PK not supported with SAE offload");
2408*a90b9d01SCy Schubert 			return -1;
2409*a90b9d01SCy Schubert 		}
2410*a90b9d01SCy Schubert 
2411*a90b9d01SCy Schubert 		if (hostapd_sae_pw_id_in_use(hapd->conf)) {
2412*a90b9d01SCy Schubert 			wpa_printf(MSG_ERROR,
2413*a90b9d01SCy Schubert 				   "SAE Password Identifiers not supported with SAE offload");
2414*a90b9d01SCy Schubert 			return -1;
2415*a90b9d01SCy Schubert 		}
2416*a90b9d01SCy Schubert 
2417*a90b9d01SCy Schubert 		params->sae_password = sae_get_password(hapd, NULL, NULL, NULL,
2418*a90b9d01SCy Schubert 							NULL, NULL);
2419*a90b9d01SCy Schubert 		if (!params->sae_password) {
2420*a90b9d01SCy Schubert 			wpa_printf(MSG_ERROR, "SAE password not configured for offload");
2421*a90b9d01SCy Schubert 			return -1;
2422*a90b9d01SCy Schubert 		}
2423*a90b9d01SCy Schubert 	}
2424*a90b9d01SCy Schubert #endif /* CONFIG_SAE */
2425*a90b9d01SCy Schubert 
24265b9c547cSRui Paulo 	params->head = (u8 *) head;
24275b9c547cSRui Paulo 	params->head_len = head_len;
24285b9c547cSRui Paulo 	params->tail = tail;
24295b9c547cSRui Paulo 	params->tail_len = tail_len;
24305b9c547cSRui Paulo 	params->proberesp = resp;
24315b9c547cSRui Paulo 	params->proberesp_len = resp_len;
24325b9c547cSRui Paulo 	params->dtim_period = hapd->conf->dtim_period;
24335b9c547cSRui Paulo 	params->beacon_int = hapd->iconf->beacon_int;
24345b9c547cSRui Paulo 	params->basic_rates = hapd->iface->basic_rates;
243585732ac8SCy Schubert 	params->beacon_rate = hapd->iconf->beacon_rate;
243685732ac8SCy Schubert 	params->rate_type = hapd->iconf->rate_type;
24375b9c547cSRui Paulo 	params->ssid = hapd->conf->ssid.ssid;
24385b9c547cSRui Paulo 	params->ssid_len = hapd->conf->ssid.ssid_len;
2439325151a3SRui Paulo 	if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
2440325151a3SRui Paulo 	    (WPA_PROTO_WPA | WPA_PROTO_RSN))
24415b9c547cSRui Paulo 		params->pairwise_ciphers = hapd->conf->wpa_pairwise |
24425b9c547cSRui Paulo 			hapd->conf->rsn_pairwise;
2443325151a3SRui Paulo 	else if (hapd->conf->wpa & WPA_PROTO_RSN)
2444325151a3SRui Paulo 		params->pairwise_ciphers = hapd->conf->rsn_pairwise;
2445325151a3SRui Paulo 	else if (hapd->conf->wpa & WPA_PROTO_WPA)
2446325151a3SRui Paulo 		params->pairwise_ciphers = hapd->conf->wpa_pairwise;
24475b9c547cSRui Paulo 	params->group_cipher = hapd->conf->wpa_group;
24485b9c547cSRui Paulo 	params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
24495b9c547cSRui Paulo 	params->auth_algs = hapd->conf->auth_algs;
24505b9c547cSRui Paulo 	params->wpa_version = hapd->conf->wpa;
2451c1d255d3SCy Schubert 	params->privacy = hapd->conf->wpa;
2452c1d255d3SCy Schubert #ifdef CONFIG_WEP
2453c1d255d3SCy Schubert 	params->privacy |= hapd->conf->ssid.wep.keys_set ||
2454f05cddf9SRui Paulo 		(hapd->conf->ieee802_1x &&
2455f05cddf9SRui Paulo 		 (hapd->conf->default_wep_key_len ||
2456f05cddf9SRui Paulo 		  hapd->conf->individual_wep_key_len));
2457c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2458f05cddf9SRui Paulo 	switch (hapd->conf->ignore_broadcast_ssid) {
2459f05cddf9SRui Paulo 	case 0:
24605b9c547cSRui Paulo 		params->hide_ssid = NO_SSID_HIDING;
2461f05cddf9SRui Paulo 		break;
2462f05cddf9SRui Paulo 	case 1:
24635b9c547cSRui Paulo 		params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
2464f05cddf9SRui Paulo 		break;
2465f05cddf9SRui Paulo 	case 2:
24665b9c547cSRui Paulo 		params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
2467f05cddf9SRui Paulo 		break;
2468f05cddf9SRui Paulo 	}
24695b9c547cSRui Paulo 	params->isolate = hapd->conf->isolate;
2470f05cddf9SRui Paulo #ifdef NEED_AP_MLME
24715b9c547cSRui Paulo 	params->cts_protect = !!(ieee802_11_erp_info(hapd) &
2472f05cddf9SRui Paulo 				ERP_INFO_USE_PROTECTION);
24735b9c547cSRui Paulo 	params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
2474f05cddf9SRui Paulo 		hapd->iconf->preamble == SHORT_PREAMBLE;
2475f05cddf9SRui Paulo 	if (hapd->iface->current_mode &&
2476f05cddf9SRui Paulo 	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
24775b9c547cSRui Paulo 		params->short_slot_time =
2478f05cddf9SRui Paulo 			hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
2479f05cddf9SRui Paulo 	else
24805b9c547cSRui Paulo 		params->short_slot_time = -1;
2481f05cddf9SRui Paulo 	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
24825b9c547cSRui Paulo 		params->ht_opmode = -1;
2483f05cddf9SRui Paulo 	else
24845b9c547cSRui Paulo 		params->ht_opmode = hapd->iface->ht_op_mode;
2485f05cddf9SRui Paulo #endif /* NEED_AP_MLME */
24865b9c547cSRui Paulo 	params->interworking = hapd->conf->interworking;
2487f05cddf9SRui Paulo 	if (hapd->conf->interworking &&
2488f05cddf9SRui Paulo 	    !is_zero_ether_addr(hapd->conf->hessid))
24895b9c547cSRui Paulo 		params->hessid = hapd->conf->hessid;
24905b9c547cSRui Paulo 	params->access_network_type = hapd->conf->access_network_type;
24915b9c547cSRui Paulo 	params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
24925b9c547cSRui Paulo #ifdef CONFIG_P2P
24935b9c547cSRui Paulo 	params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
24945b9c547cSRui Paulo #endif /* CONFIG_P2P */
2495f05cddf9SRui Paulo #ifdef CONFIG_HS20
24965b9c547cSRui Paulo 	params->disable_dgaf = hapd->conf->disable_dgaf;
24975b9c547cSRui Paulo 	if (hapd->conf->osen) {
24985b9c547cSRui Paulo 		params->privacy = 1;
24995b9c547cSRui Paulo 		params->osen = 1;
25005b9c547cSRui Paulo 	}
2501f05cddf9SRui Paulo #endif /* CONFIG_HS20 */
250285732ac8SCy Schubert 	params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
2503780fb4a2SCy Schubert 	params->pbss = hapd->conf->pbss;
25044bc52338SCy Schubert 
25054bc52338SCy Schubert 	if (hapd->conf->ftm_responder) {
25064bc52338SCy Schubert 		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
25074bc52338SCy Schubert 			params->ftm_responder = 1;
25084bc52338SCy Schubert 			params->lci = hapd->iface->conf->lci;
25094bc52338SCy Schubert 			params->civic = hapd->iface->conf->civic;
25104bc52338SCy Schubert 		} else {
25114bc52338SCy Schubert 			wpa_printf(MSG_WARNING,
25124bc52338SCy Schubert 				   "Not configuring FTM responder as the driver doesn't advertise support for it");
25134bc52338SCy Schubert 		}
25144bc52338SCy Schubert 	}
25154bc52338SCy Schubert 
2516*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2517*a90b9d01SCy Schubert 	if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
2518*a90b9d01SCy Schubert 	    !hapd->conf->disable_11be) {
2519*a90b9d01SCy Schubert 		params->mld_ap = true;
2520*a90b9d01SCy Schubert 		params->mld_link_id = hapd->mld_link_id;
2521*a90b9d01SCy Schubert 	}
2522*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2523*a90b9d01SCy Schubert 
25245b9c547cSRui Paulo 	return 0;
2525e28a4053SRui Paulo }
2526e28a4053SRui Paulo 
2527e28a4053SRui Paulo 
ieee802_11_free_ap_params(struct wpa_driver_ap_params * params)25285b9c547cSRui Paulo void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
25295b9c547cSRui Paulo {
25305b9c547cSRui Paulo 	os_free(params->tail);
25315b9c547cSRui Paulo 	params->tail = NULL;
25325b9c547cSRui Paulo 	os_free(params->head);
25335b9c547cSRui Paulo 	params->head = NULL;
25345b9c547cSRui Paulo 	os_free(params->proberesp);
25355b9c547cSRui Paulo 	params->proberesp = NULL;
2536*a90b9d01SCy Schubert 	os_free(params->mbssid_elem);
2537*a90b9d01SCy Schubert 	params->mbssid_elem = NULL;
2538*a90b9d01SCy Schubert 	os_free(params->mbssid_elem_offset);
2539*a90b9d01SCy Schubert 	params->mbssid_elem_offset = NULL;
2540*a90b9d01SCy Schubert 	os_free(params->rnr_elem);
2541*a90b9d01SCy Schubert 	params->rnr_elem = NULL;
2542*a90b9d01SCy Schubert 	os_free(params->rnr_elem_offset);
2543*a90b9d01SCy Schubert 	params->rnr_elem_offset = NULL;
2544c1d255d3SCy Schubert #ifdef CONFIG_FILS
2545c1d255d3SCy Schubert 	os_free(params->fd_frame_tmpl);
2546c1d255d3SCy Schubert 	params->fd_frame_tmpl = NULL;
2547c1d255d3SCy Schubert #endif /* CONFIG_FILS */
2548c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX
2549*a90b9d01SCy Schubert 	os_free(params->ubpr.unsol_bcast_probe_resp_tmpl);
2550*a90b9d01SCy Schubert 	params->ubpr.unsol_bcast_probe_resp_tmpl = NULL;
2551c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */
2552*a90b9d01SCy Schubert 	os_free(params->allowed_freqs);
2553*a90b9d01SCy Schubert 	params->allowed_freqs = NULL;
25545b9c547cSRui Paulo }
25555b9c547cSRui Paulo 
25565b9c547cSRui Paulo 
__ieee802_11_set_beacon(struct hostapd_data * hapd)25574b72b91aSCy Schubert static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
25585b9c547cSRui Paulo {
25595b9c547cSRui Paulo 	struct wpa_driver_ap_params params;
25605b9c547cSRui Paulo 	struct hostapd_freq_params freq;
25615b9c547cSRui Paulo 	struct hostapd_iface *iface = hapd->iface;
25625b9c547cSRui Paulo 	struct hostapd_config *iconf = iface->conf;
2563206b73d0SCy Schubert 	struct hostapd_hw_modes *cmode = iface->current_mode;
25645b9c547cSRui Paulo 	struct wpabuf *beacon, *proberesp, *assocresp;
2565*a90b9d01SCy Schubert 	bool twt_he_responder = false;
2566*a90b9d01SCy Schubert 	int res, ret = -1, i;
2567*a90b9d01SCy Schubert 	struct hostapd_hw_modes *mode;
25685b9c547cSRui Paulo 
2569c1d255d3SCy Schubert 	if (!hapd->drv_priv) {
2570c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "Interface is disabled");
2571c1d255d3SCy Schubert 		return -1;
2572c1d255d3SCy Schubert 	}
2573c1d255d3SCy Schubert 
25745b9c547cSRui Paulo 	if (hapd->csa_in_progress) {
25755b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
25765b9c547cSRui Paulo 		return -1;
25775b9c547cSRui Paulo 	}
25785b9c547cSRui Paulo 
25795b9c547cSRui Paulo 	hapd->beacon_set_done = 1;
25805b9c547cSRui Paulo 
25815b9c547cSRui Paulo 	if (ieee802_11_build_ap_params(hapd, &params) < 0)
25825b9c547cSRui Paulo 		return -1;
25835b9c547cSRui Paulo 
25845b9c547cSRui Paulo 	if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
25855b9c547cSRui Paulo 	    0)
25865b9c547cSRui Paulo 		goto fail;
25875b9c547cSRui Paulo 
25885b9c547cSRui Paulo 	params.beacon_ies = beacon;
25895b9c547cSRui Paulo 	params.proberesp_ies = proberesp;
25905b9c547cSRui Paulo 	params.assocresp_ies = assocresp;
25915b9c547cSRui Paulo 	params.reenable = hapd->reenable_beacon;
2592c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX
2593c1d255d3SCy Schubert 	params.he_spr_ctrl = hapd->iface->conf->spr.sr_control;
2594c1d255d3SCy Schubert 	params.he_spr_non_srg_obss_pd_max_offset =
2595c1d255d3SCy Schubert 		hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
2596c1d255d3SCy Schubert 	params.he_spr_srg_obss_pd_min_offset =
2597c1d255d3SCy Schubert 		hapd->iface->conf->spr.srg_obss_pd_min_offset;
2598c1d255d3SCy Schubert 	params.he_spr_srg_obss_pd_max_offset =
2599c1d255d3SCy Schubert 		hapd->iface->conf->spr.srg_obss_pd_max_offset;
2600c1d255d3SCy Schubert 	os_memcpy(params.he_spr_bss_color_bitmap,
2601c1d255d3SCy Schubert 		  hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
2602c1d255d3SCy Schubert 	os_memcpy(params.he_spr_partial_bssid_bitmap,
2603c1d255d3SCy Schubert 		  hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
2604c1d255d3SCy Schubert 	params.he_bss_color_disabled =
2605c1d255d3SCy Schubert 		hapd->iface->conf->he_op.he_bss_color_disabled;
2606c1d255d3SCy Schubert 	params.he_bss_color_partial =
2607c1d255d3SCy Schubert 		hapd->iface->conf->he_op.he_bss_color_partial;
2608c1d255d3SCy Schubert 	params.he_bss_color = hapd->iface->conf->he_op.he_bss_color;
2609*a90b9d01SCy Schubert 	twt_he_responder = hostapd_get_he_twt_responder(hapd,
2610c1d255d3SCy Schubert 							IEEE80211_MODE_AP);
2611*a90b9d01SCy Schubert 	params.ubpr.unsol_bcast_probe_resp_tmpl =
2612*a90b9d01SCy Schubert 		hostapd_unsol_bcast_probe_resp(hapd, &params.ubpr);
2613c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */
2614*a90b9d01SCy Schubert 	params.twt_responder =
2615*a90b9d01SCy Schubert 		twt_he_responder || hostapd_get_ht_vht_twt_responder(hapd);
26165b9c547cSRui Paulo 	hapd->reenable_beacon = 0;
2617c1d255d3SCy Schubert #ifdef CONFIG_SAE
2618c1d255d3SCy Schubert 	params.sae_pwe = hapd->conf->sae_pwe;
2619c1d255d3SCy Schubert #endif /* CONFIG_SAE */
2620c1d255d3SCy Schubert 
2621c1d255d3SCy Schubert #ifdef CONFIG_FILS
2622c1d255d3SCy Schubert 	params.fd_frame_tmpl = hostapd_fils_discovery(hapd, &params);
2623c1d255d3SCy Schubert #endif /* CONFIG_FILS */
26245b9c547cSRui Paulo 
2625*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2626*a90b9d01SCy Schubert 	params.punct_bitmap = iconf->punct_bitmap;
2627*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2628*a90b9d01SCy Schubert 
2629206b73d0SCy Schubert 	if (cmode &&
26305b9c547cSRui Paulo 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
2631c1d255d3SCy Schubert 				    iconf->channel, iconf->enable_edmg,
2632c1d255d3SCy Schubert 				    iconf->edmg_channel, iconf->ieee80211n,
2633206b73d0SCy Schubert 				    iconf->ieee80211ac, iconf->ieee80211ax,
2634*a90b9d01SCy Schubert 				    iconf->ieee80211be,
26355b9c547cSRui Paulo 				    iconf->secondary_channel,
2636206b73d0SCy Schubert 				    hostapd_get_oper_chwidth(iconf),
2637206b73d0SCy Schubert 				    hostapd_get_oper_centr_freq_seg0_idx(iconf),
2638206b73d0SCy Schubert 				    hostapd_get_oper_centr_freq_seg1_idx(iconf),
2639206b73d0SCy Schubert 				    cmode->vht_capab,
2640*a90b9d01SCy Schubert 				    &cmode->he_capab[IEEE80211_MODE_AP],
2641*a90b9d01SCy Schubert 				    &cmode->eht_capab[IEEE80211_MODE_AP],
2642*a90b9d01SCy Schubert 				    hostapd_get_punct_bitmap(hapd)) == 0) {
2643*a90b9d01SCy Schubert 		freq.link_id = -1;
2644*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2645*a90b9d01SCy Schubert 		if (hapd->conf->mld_ap)
2646*a90b9d01SCy Schubert 			freq.link_id = hapd->mld_link_id;
2647*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
26485b9c547cSRui Paulo 		params.freq = &freq;
2649*a90b9d01SCy Schubert 	}
2650*a90b9d01SCy Schubert 
2651*a90b9d01SCy Schubert 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
2652*a90b9d01SCy Schubert 		mode = &hapd->iface->hw_features[i];
2653*a90b9d01SCy Schubert 
2654*a90b9d01SCy Schubert 		if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
2655*a90b9d01SCy Schubert 		    iconf->hw_mode != mode->mode)
2656*a90b9d01SCy Schubert 			continue;
2657*a90b9d01SCy Schubert 
2658*a90b9d01SCy Schubert 		hostapd_get_hw_mode_any_channels(hapd, mode,
2659*a90b9d01SCy Schubert 						 !(iconf->acs_freq_list.num ||
2660*a90b9d01SCy Schubert 						   iconf->acs_ch_list.num),
2661*a90b9d01SCy Schubert 						 true, &params.allowed_freqs);
2662*a90b9d01SCy Schubert 	}
26635b9c547cSRui Paulo 
26645b9c547cSRui Paulo 	res = hostapd_drv_set_ap(hapd, &params);
26655b9c547cSRui Paulo 	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
26665b9c547cSRui Paulo 	if (res)
26675b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
26685b9c547cSRui Paulo 	else
26695b9c547cSRui Paulo 		ret = 0;
26705b9c547cSRui Paulo fail:
26715b9c547cSRui Paulo 	ieee802_11_free_ap_params(&params);
26725b9c547cSRui Paulo 	return ret;
26735b9c547cSRui Paulo }
26745b9c547cSRui Paulo 
26755b9c547cSRui Paulo 
ieee802_11_set_beacon_per_bss_only(struct hostapd_data * hapd)2676*a90b9d01SCy Schubert void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
2677*a90b9d01SCy Schubert {
2678*a90b9d01SCy Schubert 	__ieee802_11_set_beacon(hapd);
2679*a90b9d01SCy Schubert }
2680*a90b9d01SCy Schubert 
2681*a90b9d01SCy Schubert 
ieee802_11_set_beacon(struct hostapd_data * hapd)26824b72b91aSCy Schubert int ieee802_11_set_beacon(struct hostapd_data *hapd)
26834b72b91aSCy Schubert {
26844b72b91aSCy Schubert 	struct hostapd_iface *iface = hapd->iface;
26854b72b91aSCy Schubert 	int ret;
26864b72b91aSCy Schubert 	size_t i, j;
2687*a90b9d01SCy Schubert 	bool is_6g, hapd_mld = false;
26884b72b91aSCy Schubert 
26894b72b91aSCy Schubert 	ret = __ieee802_11_set_beacon(hapd);
26904b72b91aSCy Schubert 	if (ret != 0)
26914b72b91aSCy Schubert 		return ret;
26924b72b91aSCy Schubert 
26934b72b91aSCy Schubert 	if (!iface->interfaces || iface->interfaces->count <= 1)
26944b72b91aSCy Schubert 		return 0;
26954b72b91aSCy Schubert 
2696*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2697*a90b9d01SCy Schubert 	hapd_mld = hapd->conf->mld_ap;
2698*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2699*a90b9d01SCy Schubert 
2700*a90b9d01SCy Schubert 	/* Update Beacon frames in case of 6 GHz colocation or AP MLD */
27014b72b91aSCy Schubert 	is_6g = is_6ghz_op_class(iface->conf->op_class);
27024b72b91aSCy Schubert 	for (j = 0; j < iface->interfaces->count; j++) {
2703*a90b9d01SCy Schubert 		struct hostapd_iface *other;
2704*a90b9d01SCy Schubert 		bool other_iface_6g;
27054b72b91aSCy Schubert 
2706*a90b9d01SCy Schubert 		other = iface->interfaces->iface[j];
2707*a90b9d01SCy Schubert 		if (other == iface || !other || !other->conf)
27084b72b91aSCy Schubert 			continue;
27094b72b91aSCy Schubert 
2710*a90b9d01SCy Schubert 		other_iface_6g = is_6ghz_op_class(other->conf->op_class);
2711*a90b9d01SCy Schubert 
2712*a90b9d01SCy Schubert 		if (is_6g == other_iface_6g && !hapd_mld)
27134b72b91aSCy Schubert 			continue;
27144b72b91aSCy Schubert 
2715*a90b9d01SCy Schubert 		for (i = 0; i < other->num_bss; i++) {
2716*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2717*a90b9d01SCy Schubert 			if (is_6g == other_iface_6g &&
2718*a90b9d01SCy Schubert 			    !(hapd_mld && other->bss[i]->conf->mld_ap &&
2719*a90b9d01SCy Schubert 			      hostapd_is_ml_partner(hapd, other->bss[i])))
2720*a90b9d01SCy Schubert 				continue;
2721*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2722*a90b9d01SCy Schubert 
2723*a90b9d01SCy Schubert 			if (other->bss[i] && other->bss[i]->started)
2724*a90b9d01SCy Schubert 				__ieee802_11_set_beacon(other->bss[i]);
27254b72b91aSCy Schubert 		}
27264b72b91aSCy Schubert 	}
27274b72b91aSCy Schubert 
27284b72b91aSCy Schubert 	return 0;
27294b72b91aSCy Schubert }
27304b72b91aSCy Schubert 
27314b72b91aSCy Schubert 
ieee802_11_set_beacons(struct hostapd_iface * iface)27325b9c547cSRui Paulo int ieee802_11_set_beacons(struct hostapd_iface *iface)
2733e28a4053SRui Paulo {
2734e28a4053SRui Paulo 	size_t i;
27355b9c547cSRui Paulo 	int ret = 0;
27365b9c547cSRui Paulo 
27375b9c547cSRui Paulo 	for (i = 0; i < iface->num_bss; i++) {
27385b9c547cSRui Paulo 		if (iface->bss[i]->started &&
27395b9c547cSRui Paulo 		    ieee802_11_set_beacon(iface->bss[i]) < 0)
27405b9c547cSRui Paulo 			ret = -1;
27415b9c547cSRui Paulo 	}
27425b9c547cSRui Paulo 
27435b9c547cSRui Paulo 	return ret;
2744e28a4053SRui Paulo }
2745e28a4053SRui Paulo 
2746f05cddf9SRui Paulo 
2747f05cddf9SRui Paulo /* only update beacons if started */
ieee802_11_update_beacons(struct hostapd_iface * iface)27485b9c547cSRui Paulo int ieee802_11_update_beacons(struct hostapd_iface *iface)
2749f05cddf9SRui Paulo {
2750f05cddf9SRui Paulo 	size_t i;
27515b9c547cSRui Paulo 	int ret = 0;
27525b9c547cSRui Paulo 
27535b9c547cSRui Paulo 	for (i = 0; i < iface->num_bss; i++) {
27545b9c547cSRui Paulo 		if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
27555b9c547cSRui Paulo 		    ieee802_11_set_beacon(iface->bss[i]) < 0)
27565b9c547cSRui Paulo 			ret = -1;
27575b9c547cSRui Paulo 	}
27585b9c547cSRui Paulo 
27595b9c547cSRui Paulo 	return ret;
2760f05cddf9SRui Paulo }
2761f05cddf9SRui Paulo 
2762e28a4053SRui Paulo #endif /* CONFIG_NATIVE_WINDOWS */
2763