xref: /freebsd/contrib/wpa/src/ap/wnm_ap.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo  * hostapd - WNM
35b9c547cSRui Paulo  * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
4f05cddf9SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7f05cddf9SRui Paulo  */
8f05cddf9SRui Paulo 
9f05cddf9SRui Paulo #include "utils/includes.h"
10f05cddf9SRui Paulo 
11f05cddf9SRui Paulo #include "utils/common.h"
125b9c547cSRui Paulo #include "utils/eloop.h"
13f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
145b9c547cSRui Paulo #include "common/wpa_ctrl.h"
154bc52338SCy Schubert #include "common/ocv.h"
16f05cddf9SRui Paulo #include "ap/hostapd.h"
17f05cddf9SRui Paulo #include "ap/sta_info.h"
18f05cddf9SRui Paulo #include "ap/ap_config.h"
19f05cddf9SRui Paulo #include "ap/ap_drv_ops.h"
20f05cddf9SRui Paulo #include "ap/wpa_auth.h"
21780fb4a2SCy Schubert #include "mbo_ap.h"
22f05cddf9SRui Paulo #include "wnm_ap.h"
23f05cddf9SRui Paulo 
24f05cddf9SRui Paulo #define MAX_TFS_IE_LEN  1024
25f05cddf9SRui Paulo 
26f05cddf9SRui Paulo 
27f05cddf9SRui Paulo /* get the TFS IE from driver */
ieee80211_11_get_tfs_ie(struct hostapd_data * hapd,const u8 * addr,u8 * buf,u16 * buf_len,enum wnm_oper oper)28f05cddf9SRui Paulo static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
29f05cddf9SRui Paulo 				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
30f05cddf9SRui Paulo {
31f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
32f05cddf9SRui Paulo 
33f05cddf9SRui Paulo 	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
34f05cddf9SRui Paulo }
35f05cddf9SRui Paulo 
36f05cddf9SRui Paulo 
37f05cddf9SRui Paulo /* set the TFS IE to driver */
ieee80211_11_set_tfs_ie(struct hostapd_data * hapd,const u8 * addr,u8 * buf,u16 * buf_len,enum wnm_oper oper)38f05cddf9SRui Paulo static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
39f05cddf9SRui Paulo 				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
40f05cddf9SRui Paulo {
41f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
42f05cddf9SRui Paulo 
43f05cddf9SRui Paulo 	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
44f05cddf9SRui Paulo }
45f05cddf9SRui Paulo 
46f05cddf9SRui Paulo 
wnm_ap_get_own_addr(struct hostapd_data * hapd,struct sta_info * sta)47*a90b9d01SCy Schubert static const u8 * wnm_ap_get_own_addr(struct hostapd_data *hapd,
48*a90b9d01SCy Schubert 				      struct sta_info *sta)
49*a90b9d01SCy Schubert {
50*a90b9d01SCy Schubert 	const u8 *own_addr = hapd->own_addr;
51*a90b9d01SCy Schubert 
52*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
53*a90b9d01SCy Schubert 	if (hapd->conf->mld_ap && (!sta || ap_sta_is_mld(hapd, sta)))
54*a90b9d01SCy Schubert 		own_addr = hapd->mld->mld_addr;
55*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
56*a90b9d01SCy Schubert 
57*a90b9d01SCy Schubert 	return own_addr;
58*a90b9d01SCy Schubert }
59*a90b9d01SCy Schubert 
60*a90b9d01SCy Schubert 
61f05cddf9SRui Paulo /* MLME-SLEEPMODE.response */
ieee802_11_send_wnmsleep_resp(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token,u8 action_type,u16 intval)62f05cddf9SRui Paulo static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
63f05cddf9SRui Paulo 					 const u8 *addr, u8 dialog_token,
64f05cddf9SRui Paulo 					 u8 action_type, u16 intval)
65f05cddf9SRui Paulo {
66f05cddf9SRui Paulo 	struct ieee80211_mgmt *mgmt;
67f05cddf9SRui Paulo 	int res;
68f05cddf9SRui Paulo 	size_t len;
69f05cddf9SRui Paulo 	size_t gtk_elem_len = 0;
70f05cddf9SRui Paulo 	size_t igtk_elem_len = 0;
71c1d255d3SCy Schubert 	size_t bigtk_elem_len = 0;
72f05cddf9SRui Paulo 	struct wnm_sleep_element wnmsleep_ie;
734bc52338SCy Schubert 	u8 *wnmtfs_ie, *oci_ie;
744bc52338SCy Schubert 	u8 wnmsleep_ie_len, oci_ie_len;
75f05cddf9SRui Paulo 	u16 wnmtfs_ie_len;
76f05cddf9SRui Paulo 	u8 *pos;
77f05cddf9SRui Paulo 	struct sta_info *sta;
78f05cddf9SRui Paulo 	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
79f05cddf9SRui Paulo 		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
80*a90b9d01SCy Schubert 	const u8 *own_addr;
81f05cddf9SRui Paulo 
82f05cddf9SRui Paulo 	sta = ap_get_sta(hapd, addr);
83f05cddf9SRui Paulo 	if (sta == NULL) {
84f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
85f05cddf9SRui Paulo 		return -EINVAL;
86f05cddf9SRui Paulo 	}
87f05cddf9SRui Paulo 
88f05cddf9SRui Paulo 	/* WNM-Sleep Mode IE */
89f05cddf9SRui Paulo 	os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
90f05cddf9SRui Paulo 	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
91f05cddf9SRui Paulo 	wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
92f05cddf9SRui Paulo 	wnmsleep_ie.len = wnmsleep_ie_len - 2;
93f05cddf9SRui Paulo 	wnmsleep_ie.action_type = action_type;
94f05cddf9SRui Paulo 	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
955b9c547cSRui Paulo 	wnmsleep_ie.intval = host_to_le16(intval);
96f05cddf9SRui Paulo 
97f05cddf9SRui Paulo 	/* TFS IE(s) */
98f05cddf9SRui Paulo 	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
99f05cddf9SRui Paulo 	if (wnmtfs_ie == NULL)
100f05cddf9SRui Paulo 		return -1;
101f05cddf9SRui Paulo 	if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
102f05cddf9SRui Paulo 				    tfs_oper)) {
103f05cddf9SRui Paulo 		wnmtfs_ie_len = 0;
104f05cddf9SRui Paulo 		os_free(wnmtfs_ie);
105f05cddf9SRui Paulo 		wnmtfs_ie = NULL;
106f05cddf9SRui Paulo 	}
107f05cddf9SRui Paulo 
1084bc52338SCy Schubert 	oci_ie = NULL;
1094bc52338SCy Schubert 	oci_ie_len = 0;
1104bc52338SCy Schubert #ifdef CONFIG_OCV
1114bc52338SCy Schubert 	if (action_type == WNM_SLEEP_MODE_EXIT &&
1124bc52338SCy Schubert 	    wpa_auth_uses_ocv(sta->wpa_sm)) {
1134bc52338SCy Schubert 		struct wpa_channel_info ci;
1144bc52338SCy Schubert 
1154bc52338SCy Schubert 		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
1164bc52338SCy Schubert 			wpa_printf(MSG_WARNING,
1174bc52338SCy Schubert 				   "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
1184bc52338SCy Schubert 			os_free(wnmtfs_ie);
1194bc52338SCy Schubert 			return -1;
1204bc52338SCy Schubert 		}
121c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
122c1d255d3SCy Schubert 		if (hapd->conf->oci_freq_override_wnm_sleep) {
123c1d255d3SCy Schubert 			wpa_printf(MSG_INFO,
124c1d255d3SCy Schubert 				   "TEST: Override OCI frequency %d -> %u MHz",
125c1d255d3SCy Schubert 				   ci.frequency,
126c1d255d3SCy Schubert 				   hapd->conf->oci_freq_override_wnm_sleep);
127c1d255d3SCy Schubert 			ci.frequency = hapd->conf->oci_freq_override_wnm_sleep;
128c1d255d3SCy Schubert 		}
129c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
1304bc52338SCy Schubert 
1314bc52338SCy Schubert 		oci_ie_len = OCV_OCI_EXTENDED_LEN;
1324bc52338SCy Schubert 		oci_ie = os_zalloc(oci_ie_len);
1334bc52338SCy Schubert 		if (!oci_ie) {
1344bc52338SCy Schubert 			wpa_printf(MSG_WARNING,
1354bc52338SCy Schubert 				   "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
1364bc52338SCy Schubert 			os_free(wnmtfs_ie);
1374bc52338SCy Schubert 			return -1;
1384bc52338SCy Schubert 		}
1394bc52338SCy Schubert 
1404bc52338SCy Schubert 		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
1414bc52338SCy Schubert 			os_free(wnmtfs_ie);
1424bc52338SCy Schubert 			os_free(oci_ie);
1434bc52338SCy Schubert 			return -1;
1444bc52338SCy Schubert 		}
1454bc52338SCy Schubert 	}
1464bc52338SCy Schubert #endif /* CONFIG_OCV */
1474bc52338SCy Schubert 
148f05cddf9SRui Paulo #define MAX_GTK_SUBELEM_LEN 45
149f05cddf9SRui Paulo #define MAX_IGTK_SUBELEM_LEN 26
150c1d255d3SCy Schubert #define MAX_BIGTK_SUBELEM_LEN 26
151f05cddf9SRui Paulo 	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
1524bc52338SCy Schubert 			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
153c1d255d3SCy Schubert 			 MAX_BIGTK_SUBELEM_LEN +
1544bc52338SCy Schubert 			 oci_ie_len);
155f05cddf9SRui Paulo 	if (mgmt == NULL) {
156f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
157f05cddf9SRui Paulo 			   "WNM-Sleep Response action frame");
15885732ac8SCy Schubert 		res = -1;
15985732ac8SCy Schubert 		goto fail;
160f05cddf9SRui Paulo 	}
161*a90b9d01SCy Schubert 
162*a90b9d01SCy Schubert 	own_addr = wnm_ap_get_own_addr(hapd, sta);
163*a90b9d01SCy Schubert 
164f05cddf9SRui Paulo 	os_memcpy(mgmt->da, addr, ETH_ALEN);
165*a90b9d01SCy Schubert 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
166*a90b9d01SCy Schubert 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
167f05cddf9SRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
168f05cddf9SRui Paulo 					   WLAN_FC_STYPE_ACTION);
169f05cddf9SRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
170f05cddf9SRui Paulo 	mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
171f05cddf9SRui Paulo 	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
172f05cddf9SRui Paulo 	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
173f05cddf9SRui Paulo 	/* add key data if MFP is enabled */
174f05cddf9SRui Paulo 	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
17585732ac8SCy Schubert 	    hapd->conf->wnm_sleep_mode_no_keys ||
176f05cddf9SRui Paulo 	    action_type != WNM_SLEEP_MODE_EXIT) {
177f05cddf9SRui Paulo 		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
178f05cddf9SRui Paulo 	} else {
179f05cddf9SRui Paulo 		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
180f05cddf9SRui Paulo 		pos += gtk_elem_len;
181f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
182f05cddf9SRui Paulo 			   (int) gtk_elem_len);
183f05cddf9SRui Paulo 		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
18485732ac8SCy Schubert 		if (res < 0)
18585732ac8SCy Schubert 			goto fail;
186f05cddf9SRui Paulo 		igtk_elem_len = res;
187f05cddf9SRui Paulo 		pos += igtk_elem_len;
188f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
189f05cddf9SRui Paulo 			   (int) igtk_elem_len);
190c1d255d3SCy Schubert 		if (hapd->conf->beacon_prot &&
191c1d255d3SCy Schubert 		    (hapd->iface->drv_flags &
192c1d255d3SCy Schubert 		     WPA_DRIVER_FLAGS_BEACON_PROTECTION)) {
193c1d255d3SCy Schubert 			res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos);
194c1d255d3SCy Schubert 			if (res < 0)
195c1d255d3SCy Schubert 				goto fail;
196c1d255d3SCy Schubert 			bigtk_elem_len = res;
197c1d255d3SCy Schubert 			pos += bigtk_elem_len;
198c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG, "Pass 4 bigtk_len = %d",
199c1d255d3SCy Schubert 				   (int) bigtk_elem_len);
200c1d255d3SCy Schubert 		}
201f05cddf9SRui Paulo 
202f05cddf9SRui Paulo 		WPA_PUT_LE16((u8 *)
203f05cddf9SRui Paulo 			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
204c1d255d3SCy Schubert 			     gtk_elem_len + igtk_elem_len + bigtk_elem_len);
205f05cddf9SRui Paulo 	}
206f05cddf9SRui Paulo 	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
207f05cddf9SRui Paulo 	/* copy TFS IE here */
208f05cddf9SRui Paulo 	pos += wnmsleep_ie_len;
2094bc52338SCy Schubert 	if (wnmtfs_ie) {
210f05cddf9SRui Paulo 		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
2114bc52338SCy Schubert 		pos += wnmtfs_ie_len;
2124bc52338SCy Schubert 	}
2134bc52338SCy Schubert #ifdef CONFIG_OCV
2144bc52338SCy Schubert 	/* copy OCV OCI here */
2154bc52338SCy Schubert 	if (oci_ie_len > 0)
2164bc52338SCy Schubert 		os_memcpy(pos, oci_ie, oci_ie_len);
2174bc52338SCy Schubert #endif /* CONFIG_OCV */
218f05cddf9SRui Paulo 
219f05cddf9SRui Paulo 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
220c1d255d3SCy Schubert 		igtk_elem_len + bigtk_elem_len +
221c1d255d3SCy Schubert 		wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
222f05cddf9SRui Paulo 
223f05cddf9SRui Paulo 	/* In driver, response frame should be forced to sent when STA is in
224f05cddf9SRui Paulo 	 * PS mode */
225f05cddf9SRui Paulo 	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
226f05cddf9SRui Paulo 				      mgmt->da, &mgmt->u.action.category, len);
227f05cddf9SRui Paulo 
228f05cddf9SRui Paulo 	if (!res) {
229f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
230f05cddf9SRui Paulo 			   "frame");
231f05cddf9SRui Paulo 
232f05cddf9SRui Paulo 		/* when entering wnmsleep
233f05cddf9SRui Paulo 		 * 1. pause the node in driver
234c1d255d3SCy Schubert 		 * 2. mark the node so that AP won't update GTK/IGTK/BIGTK
235c1d255d3SCy Schubert 		 * during WNM Sleep
236f05cddf9SRui Paulo 		 */
237f05cddf9SRui Paulo 		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
238f05cddf9SRui Paulo 		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
2395b9c547cSRui Paulo 			sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
240f05cddf9SRui Paulo 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
241f05cddf9SRui Paulo 					     addr, NULL, NULL);
242f05cddf9SRui Paulo 			wpa_set_wnmsleep(sta->wpa_sm, 1);
243f05cddf9SRui Paulo 		}
244f05cddf9SRui Paulo 		/* when exiting wnmsleep
245f05cddf9SRui Paulo 		 * 1. unmark the node
246c1d255d3SCy Schubert 		 * 2. start GTK/IGTK/BIGTK update if MFP is not used
247f05cddf9SRui Paulo 		 * 3. unpause the node in driver
248f05cddf9SRui Paulo 		 */
249f05cddf9SRui Paulo 		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
250f05cddf9SRui Paulo 		     wnmsleep_ie.status ==
251f05cddf9SRui Paulo 		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
252f05cddf9SRui Paulo 		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
2535b9c547cSRui Paulo 			sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
254f05cddf9SRui Paulo 			wpa_set_wnmsleep(sta->wpa_sm, 0);
255f05cddf9SRui Paulo 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
256f05cddf9SRui Paulo 					     addr, NULL, NULL);
25785732ac8SCy Schubert 			if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
25885732ac8SCy Schubert 			    hapd->conf->wnm_sleep_mode_no_keys)
259f05cddf9SRui Paulo 				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
260f05cddf9SRui Paulo 		}
261f05cddf9SRui Paulo 	} else
262f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
263f05cddf9SRui Paulo 
264f05cddf9SRui Paulo #undef MAX_GTK_SUBELEM_LEN
265f05cddf9SRui Paulo #undef MAX_IGTK_SUBELEM_LEN
266c1d255d3SCy Schubert #undef MAX_BIGTK_SUBELEM_LEN
26785732ac8SCy Schubert fail:
268f05cddf9SRui Paulo 	os_free(wnmtfs_ie);
2694bc52338SCy Schubert 	os_free(oci_ie);
270f05cddf9SRui Paulo 	os_free(mgmt);
271f05cddf9SRui Paulo 	return res;
272f05cddf9SRui Paulo }
273f05cddf9SRui Paulo 
274f05cddf9SRui Paulo 
ieee802_11_rx_wnmsleep_req(struct hostapd_data * hapd,const u8 * addr,const u8 * frm,int len)275f05cddf9SRui Paulo static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
276f05cddf9SRui Paulo 				       const u8 *addr, const u8 *frm, int len)
277f05cddf9SRui Paulo {
278f05cddf9SRui Paulo 	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
279f05cddf9SRui Paulo 	const u8 *pos = frm;
280f05cddf9SRui Paulo 	u8 dialog_token;
281f05cddf9SRui Paulo 	struct wnm_sleep_element *wnmsleep_ie = NULL;
282f05cddf9SRui Paulo 	/* multiple TFS Req IE (assuming consecutive) */
283f05cddf9SRui Paulo 	u8 *tfsreq_ie_start = NULL;
284f05cddf9SRui Paulo 	u8 *tfsreq_ie_end = NULL;
285f05cddf9SRui Paulo 	u16 tfsreq_ie_len = 0;
2864bc52338SCy Schubert #ifdef CONFIG_OCV
2874bc52338SCy Schubert 	struct sta_info *sta;
2884bc52338SCy Schubert 	const u8 *oci_ie = NULL;
2894bc52338SCy Schubert 	u8 oci_ie_len = 0;
2904bc52338SCy Schubert #endif /* CONFIG_OCV */
291f05cddf9SRui Paulo 
29285732ac8SCy Schubert 	if (!hapd->conf->wnm_sleep_mode) {
29385732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
29485732ac8SCy Schubert 			   MACSTR " since WNM-Sleep Mode is disabled",
29585732ac8SCy Schubert 			   MAC2STR(addr));
29685732ac8SCy Schubert 		return;
29785732ac8SCy Schubert 	}
29885732ac8SCy Schubert 
2994bc52338SCy Schubert 	if (len < 1) {
3004bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
3014bc52338SCy Schubert 			   "WNM: Ignore too short WNM-Sleep Mode Request from "
3024bc52338SCy Schubert 			   MACSTR, MAC2STR(addr));
3034bc52338SCy Schubert 		return;
3044bc52338SCy Schubert 	}
3054bc52338SCy Schubert 
306f05cddf9SRui Paulo 	dialog_token = *pos++;
307f05cddf9SRui Paulo 	while (pos + 1 < frm + len) {
308f05cddf9SRui Paulo 		u8 ie_len = pos[1];
309f05cddf9SRui Paulo 		if (pos + 2 + ie_len > frm + len)
310f05cddf9SRui Paulo 			break;
31185732ac8SCy Schubert 		if (*pos == WLAN_EID_WNMSLEEP &&
31285732ac8SCy Schubert 		    ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
313f05cddf9SRui Paulo 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
314f05cddf9SRui Paulo 		else if (*pos == WLAN_EID_TFS_REQ) {
315f05cddf9SRui Paulo 			if (!tfsreq_ie_start)
316f05cddf9SRui Paulo 				tfsreq_ie_start = (u8 *) pos;
317f05cddf9SRui Paulo 			tfsreq_ie_end = (u8 *) pos;
3184bc52338SCy Schubert #ifdef CONFIG_OCV
3194bc52338SCy Schubert 		} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
3204bc52338SCy Schubert 			   pos[2] == WLAN_EID_EXT_OCV_OCI) {
3214bc52338SCy Schubert 			oci_ie = pos + 3;
3224bc52338SCy Schubert 			oci_ie_len = ie_len - 1;
3234bc52338SCy Schubert #endif /* CONFIG_OCV */
324f05cddf9SRui Paulo 		} else
325f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
326f05cddf9SRui Paulo 				   *pos);
327f05cddf9SRui Paulo 		pos += ie_len + 2;
328f05cddf9SRui Paulo 	}
329f05cddf9SRui Paulo 
330f05cddf9SRui Paulo 	if (!wnmsleep_ie) {
331f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
332f05cddf9SRui Paulo 		return;
333f05cddf9SRui Paulo 	}
334f05cddf9SRui Paulo 
3354bc52338SCy Schubert #ifdef CONFIG_OCV
3364bc52338SCy Schubert 	sta = ap_get_sta(hapd, addr);
3374bc52338SCy Schubert 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
3384bc52338SCy Schubert 	    sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
3394bc52338SCy Schubert 		struct wpa_channel_info ci;
3404bc52338SCy Schubert 
3414bc52338SCy Schubert 		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
3424bc52338SCy Schubert 			wpa_printf(MSG_WARNING,
3434bc52338SCy Schubert 				   "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
3444bc52338SCy Schubert 			return;
3454bc52338SCy Schubert 		}
3464bc52338SCy Schubert 
3474bc52338SCy Schubert 		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
3484bc52338SCy Schubert 					 channel_width_to_int(ci.chanwidth),
349c1d255d3SCy Schubert 					 ci.seg1_idx) != OCI_SUCCESS) {
350c1d255d3SCy Schubert 			wpa_msg(hapd, MSG_WARNING, "WNM: OCV failed: %s",
351c1d255d3SCy Schubert 				ocv_errorstr);
3524bc52338SCy Schubert 			return;
3534bc52338SCy Schubert 		}
3544bc52338SCy Schubert 	}
3554bc52338SCy Schubert #endif /* CONFIG_OCV */
3564bc52338SCy Schubert 
357f05cddf9SRui Paulo 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
358f05cddf9SRui Paulo 	    tfsreq_ie_start && tfsreq_ie_end &&
359f05cddf9SRui Paulo 	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
360f05cddf9SRui Paulo 		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
361f05cddf9SRui Paulo 			tfsreq_ie_start;
362f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
363f05cddf9SRui Paulo 		/* pass the TFS Req IE(s) to driver for processing */
364f05cddf9SRui Paulo 		if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
365f05cddf9SRui Paulo 					    &tfsreq_ie_len,
366f05cddf9SRui Paulo 					    WNM_SLEEP_TFS_REQ_IE_SET))
367f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
368f05cddf9SRui Paulo 	}
369f05cddf9SRui Paulo 
370f05cddf9SRui Paulo 	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
371f05cddf9SRui Paulo 				      wnmsleep_ie->action_type,
3725b9c547cSRui Paulo 				      le_to_host16(wnmsleep_ie->intval));
373f05cddf9SRui Paulo 
374f05cddf9SRui Paulo 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
375f05cddf9SRui Paulo 		/* clear the tfs after sending the resp frame */
376f05cddf9SRui Paulo 		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
377f05cddf9SRui Paulo 					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
378f05cddf9SRui Paulo 	}
379f05cddf9SRui Paulo }
380f05cddf9SRui Paulo 
381f05cddf9SRui Paulo 
ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token)3825b9c547cSRui Paulo static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
3835b9c547cSRui Paulo 						  const u8 *addr,
38485732ac8SCy Schubert 						  u8 dialog_token)
385f05cddf9SRui Paulo {
3865b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
387*a90b9d01SCy Schubert 	const u8 *own_addr;
388*a90b9d01SCy Schubert 	struct sta_info *sta;
38985732ac8SCy Schubert 	size_t len;
3905b9c547cSRui Paulo 	u8 *pos;
3915b9c547cSRui Paulo 	int res;
3925b9c547cSRui Paulo 
39385732ac8SCy Schubert 	mgmt = os_zalloc(sizeof(*mgmt));
3945b9c547cSRui Paulo 	if (mgmt == NULL)
3955b9c547cSRui Paulo 		return -1;
396*a90b9d01SCy Schubert 
397*a90b9d01SCy Schubert 	sta = ap_get_sta(hapd, addr);
398*a90b9d01SCy Schubert 	own_addr = wnm_ap_get_own_addr(hapd, sta);
399*a90b9d01SCy Schubert 
4005b9c547cSRui Paulo 	os_memcpy(mgmt->da, addr, ETH_ALEN);
401*a90b9d01SCy Schubert 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
402*a90b9d01SCy Schubert 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
4035b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
4045b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
4055b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
4065b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
4075b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
4085b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode = 0;
4095b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
4105b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
4115b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
4125b9c547cSRui Paulo 
4135b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
4145b9c547cSRui Paulo 		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
4155b9c547cSRui Paulo 		   "validity_interval=%u",
4165b9c547cSRui Paulo 		   MAC2STR(addr), dialog_token,
4175b9c547cSRui Paulo 		   mgmt->u.action.u.bss_tm_req.req_mode,
4185b9c547cSRui Paulo 		   le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
4195b9c547cSRui Paulo 		   mgmt->u.action.u.bss_tm_req.validity_interval);
4205b9c547cSRui Paulo 
4215b9c547cSRui Paulo 	len = pos - &mgmt->u.action.category;
4225b9c547cSRui Paulo 	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
4235b9c547cSRui Paulo 				      mgmt->da, &mgmt->u.action.category, len);
4245b9c547cSRui Paulo 	os_free(mgmt);
4255b9c547cSRui Paulo 	return res;
4265b9c547cSRui Paulo }
4275b9c547cSRui Paulo 
4285b9c547cSRui Paulo 
ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data * hapd,const u8 * addr,const u8 * frm,size_t len)4295b9c547cSRui Paulo static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
4305b9c547cSRui Paulo 					       const u8 *addr, const u8 *frm,
4315b9c547cSRui Paulo 					       size_t len)
4325b9c547cSRui Paulo {
4335b9c547cSRui Paulo 	u8 dialog_token, reason;
4345b9c547cSRui Paulo 	const u8 *pos, *end;
43585732ac8SCy Schubert 	int enabled = hapd->conf->bss_transition;
436*a90b9d01SCy Schubert 	char *hex = NULL;
437*a90b9d01SCy Schubert 	size_t hex_len;
43885732ac8SCy Schubert 
43985732ac8SCy Schubert #ifdef CONFIG_MBO
44085732ac8SCy Schubert 	if (hapd->conf->mbo_enabled)
44185732ac8SCy Schubert 		enabled = 1;
44285732ac8SCy Schubert #endif /* CONFIG_MBO */
44385732ac8SCy Schubert 	if (!enabled) {
44485732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
44585732ac8SCy Schubert 			   "Ignore BSS Transition Management Query from "
44685732ac8SCy Schubert 			   MACSTR
44785732ac8SCy Schubert 			   " since BSS Transition Management is disabled",
44885732ac8SCy Schubert 			   MAC2STR(addr));
44985732ac8SCy Schubert 		return;
45085732ac8SCy Schubert 	}
4515b9c547cSRui Paulo 
4525b9c547cSRui Paulo 	if (len < 2) {
4535b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
4545b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
4555b9c547cSRui Paulo 		return;
4565b9c547cSRui Paulo 	}
4575b9c547cSRui Paulo 
4585b9c547cSRui Paulo 	pos = frm;
4595b9c547cSRui Paulo 	end = pos + len;
4605b9c547cSRui Paulo 	dialog_token = *pos++;
4615b9c547cSRui Paulo 	reason = *pos++;
4625b9c547cSRui Paulo 
4635b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
4645b9c547cSRui Paulo 		   MACSTR " dialog_token=%u reason=%u",
4655b9c547cSRui Paulo 		   MAC2STR(addr), dialog_token, reason);
4665b9c547cSRui Paulo 
4675b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
4685b9c547cSRui Paulo 		    pos, end - pos);
4695b9c547cSRui Paulo 
470*a90b9d01SCy Schubert 	hex_len = 2 * (end - pos) + 1;
471*a90b9d01SCy Schubert 	if (hex_len > 1) {
472*a90b9d01SCy Schubert 		hex = os_malloc(hex_len);
473*a90b9d01SCy Schubert 		if (hex)
474*a90b9d01SCy Schubert 			wpa_snprintf_hex(hex, hex_len, pos, end - pos);
475*a90b9d01SCy Schubert 	}
476*a90b9d01SCy Schubert 	wpa_msg(hapd->msg_ctx, MSG_INFO,
477*a90b9d01SCy Schubert 		BSS_TM_QUERY MACSTR " reason=%u%s%s",
478*a90b9d01SCy Schubert 		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
479*a90b9d01SCy Schubert 	os_free(hex);
480*a90b9d01SCy Schubert 
48185732ac8SCy Schubert 	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
48285732ac8SCy Schubert }
48385732ac8SCy Schubert 
48485732ac8SCy Schubert 
ap_sta_reset_steer_flag_timer(void * eloop_ctx,void * timeout_ctx)48585732ac8SCy Schubert void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
48685732ac8SCy Schubert {
48785732ac8SCy Schubert 	struct hostapd_data *hapd = eloop_ctx;
48885732ac8SCy Schubert 	struct sta_info *sta = timeout_ctx;
48985732ac8SCy Schubert 
49085732ac8SCy Schubert 	if (sta->agreed_to_steer) {
49185732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
49285732ac8SCy Schubert 			   hapd->conf->iface, MAC2STR(sta->addr));
49385732ac8SCy Schubert 		sta->agreed_to_steer = 0;
49485732ac8SCy Schubert 	}
4955b9c547cSRui Paulo }
4965b9c547cSRui Paulo 
4975b9c547cSRui Paulo 
ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data * hapd,const u8 * addr,const u8 * frm,size_t len)4985b9c547cSRui Paulo static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
4995b9c547cSRui Paulo 					      const u8 *addr, const u8 *frm,
5005b9c547cSRui Paulo 					      size_t len)
5015b9c547cSRui Paulo {
5025b9c547cSRui Paulo 	u8 dialog_token, status_code, bss_termination_delay;
5035b9c547cSRui Paulo 	const u8 *pos, *end;
50485732ac8SCy Schubert 	int enabled = hapd->conf->bss_transition;
50585732ac8SCy Schubert 	struct sta_info *sta;
50685732ac8SCy Schubert 
50785732ac8SCy Schubert #ifdef CONFIG_MBO
50885732ac8SCy Schubert 	if (hapd->conf->mbo_enabled)
50985732ac8SCy Schubert 		enabled = 1;
51085732ac8SCy Schubert #endif /* CONFIG_MBO */
51185732ac8SCy Schubert 	if (!enabled) {
51285732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
51385732ac8SCy Schubert 			   "Ignore BSS Transition Management Response from "
51485732ac8SCy Schubert 			   MACSTR
51585732ac8SCy Schubert 			   " since BSS Transition Management is disabled",
51685732ac8SCy Schubert 			   MAC2STR(addr));
51785732ac8SCy Schubert 		return;
51885732ac8SCy Schubert 	}
5195b9c547cSRui Paulo 
5205b9c547cSRui Paulo 	if (len < 3) {
5215b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
5225b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
5235b9c547cSRui Paulo 		return;
5245b9c547cSRui Paulo 	}
5255b9c547cSRui Paulo 
5265b9c547cSRui Paulo 	pos = frm;
5275b9c547cSRui Paulo 	end = pos + len;
5285b9c547cSRui Paulo 	dialog_token = *pos++;
5295b9c547cSRui Paulo 	status_code = *pos++;
5305b9c547cSRui Paulo 	bss_termination_delay = *pos++;
5315b9c547cSRui Paulo 
5325b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
5335b9c547cSRui Paulo 		   MACSTR " dialog_token=%u status_code=%u "
5345b9c547cSRui Paulo 		   "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
5355b9c547cSRui Paulo 		   status_code, bss_termination_delay);
5365b9c547cSRui Paulo 
53785732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
53885732ac8SCy Schubert 	if (!sta) {
53985732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Station " MACSTR
54085732ac8SCy Schubert 			   " not found for received BSS TM Response",
54185732ac8SCy Schubert 			   MAC2STR(addr));
54285732ac8SCy Schubert 		return;
54385732ac8SCy Schubert 	}
54485732ac8SCy Schubert 
5455b9c547cSRui Paulo 	if (status_code == WNM_BSS_TM_ACCEPT) {
5465b9c547cSRui Paulo 		if (end - pos < ETH_ALEN) {
5475b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
5485b9c547cSRui Paulo 			return;
5495b9c547cSRui Paulo 		}
55085732ac8SCy Schubert 		sta->agreed_to_steer = 1;
55185732ac8SCy Schubert 		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
55285732ac8SCy Schubert 		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
55385732ac8SCy Schubert 				       hapd, sta);
5545b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
5555b9c547cSRui Paulo 			   MAC2STR(pos));
5565b9c547cSRui Paulo 		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
5575b9c547cSRui Paulo 			" status_code=%u bss_termination_delay=%u target_bssid="
5585b9c547cSRui Paulo 			MACSTR,
5595b9c547cSRui Paulo 			MAC2STR(addr), status_code, bss_termination_delay,
5605b9c547cSRui Paulo 			MAC2STR(pos));
5615b9c547cSRui Paulo 		pos += ETH_ALEN;
5625b9c547cSRui Paulo 	} else {
56385732ac8SCy Schubert 		sta->agreed_to_steer = 0;
5645b9c547cSRui Paulo 		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
5655b9c547cSRui Paulo 			" status_code=%u bss_termination_delay=%u",
5665b9c547cSRui Paulo 			MAC2STR(addr), status_code, bss_termination_delay);
5675b9c547cSRui Paulo 	}
5685b9c547cSRui Paulo 
5695b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
5705b9c547cSRui Paulo 		    pos, end - pos);
5715b9c547cSRui Paulo }
5725b9c547cSRui Paulo 
5735b9c547cSRui Paulo 
wnm_beacon_protection_failure(struct hostapd_data * hapd,const u8 * addr)574c1d255d3SCy Schubert static void wnm_beacon_protection_failure(struct hostapd_data *hapd,
575c1d255d3SCy Schubert 					  const u8 *addr)
576c1d255d3SCy Schubert {
577c1d255d3SCy Schubert 	struct sta_info *sta;
578c1d255d3SCy Schubert 
579c1d255d3SCy Schubert 	if (!hapd->conf->beacon_prot ||
580c1d255d3SCy Schubert 	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
581c1d255d3SCy Schubert 		return;
582c1d255d3SCy Schubert 
583c1d255d3SCy Schubert 	sta = ap_get_sta(hapd, addr);
584c1d255d3SCy Schubert 	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
585c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "Station " MACSTR
586c1d255d3SCy Schubert 			   " not found for received WNM-Notification Request",
587c1d255d3SCy Schubert 			   MAC2STR(addr));
588c1d255d3SCy Schubert 		return;
589c1d255d3SCy Schubert 	}
590c1d255d3SCy Schubert 
591c1d255d3SCy Schubert 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
592c1d255d3SCy Schubert 		       HOSTAPD_LEVEL_INFO,
593c1d255d3SCy Schubert 		       "Beacon protection failure reported");
594c1d255d3SCy Schubert 	wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_UNPROT_BEACON "reporter="
595c1d255d3SCy Schubert 		MACSTR, MAC2STR(addr));
596c1d255d3SCy Schubert }
597c1d255d3SCy Schubert 
598c1d255d3SCy Schubert 
ieee802_11_rx_wnm_notification_req(struct hostapd_data * hapd,const u8 * addr,const u8 * buf,size_t len)599780fb4a2SCy Schubert static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
600780fb4a2SCy Schubert 					       const u8 *addr, const u8 *buf,
601780fb4a2SCy Schubert 					       size_t len)
602780fb4a2SCy Schubert {
603780fb4a2SCy Schubert 	u8 dialog_token, type;
604780fb4a2SCy Schubert 
605780fb4a2SCy Schubert 	if (len < 2)
606780fb4a2SCy Schubert 		return;
607780fb4a2SCy Schubert 	dialog_token = *buf++;
608780fb4a2SCy Schubert 	type = *buf++;
609780fb4a2SCy Schubert 	len -= 2;
610780fb4a2SCy Schubert 
611780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG,
612780fb4a2SCy Schubert 		   "WNM: Received WNM Notification Request frame from "
613780fb4a2SCy Schubert 		   MACSTR " (dialog_token=%u type=%u)",
614780fb4a2SCy Schubert 		   MAC2STR(addr), dialog_token, type);
615780fb4a2SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
616780fb4a2SCy Schubert 		    buf, len);
617c1d255d3SCy Schubert 	switch (type) {
618c1d255d3SCy Schubert 	case WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE:
619c1d255d3SCy Schubert 		wnm_beacon_protection_failure(hapd, addr);
620c1d255d3SCy Schubert 		break;
621c1d255d3SCy Schubert 	case WNM_NOTIF_TYPE_VENDOR_SPECIFIC:
622780fb4a2SCy Schubert 		mbo_ap_wnm_notification_req(hapd, addr, buf, len);
623c1d255d3SCy Schubert 		break;
624c1d255d3SCy Schubert 	}
625780fb4a2SCy Schubert }
626780fb4a2SCy Schubert 
627780fb4a2SCy Schubert 
ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data * hapd,const u8 * addr,const u8 * buf,size_t len)62885732ac8SCy Schubert static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
62985732ac8SCy Schubert 						const u8 *addr, const u8 *buf,
63085732ac8SCy Schubert 						size_t len)
63185732ac8SCy Schubert {
63285732ac8SCy Schubert 	u8 dialog_token;
63385732ac8SCy Schubert 	char *hex;
63485732ac8SCy Schubert 	size_t hex_len;
63585732ac8SCy Schubert 
63685732ac8SCy Schubert 	if (!hapd->conf->coloc_intf_reporting) {
63785732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
63885732ac8SCy Schubert 			   "WNM: Ignore unexpected Collocated Interference Report from "
63985732ac8SCy Schubert 			   MACSTR, MAC2STR(addr));
64085732ac8SCy Schubert 		return;
64185732ac8SCy Schubert 	}
64285732ac8SCy Schubert 
64385732ac8SCy Schubert 	if (len < 1) {
64485732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
64585732ac8SCy Schubert 			   "WNM: Ignore too short Collocated Interference Report from "
64685732ac8SCy Schubert 			   MACSTR, MAC2STR(addr));
64785732ac8SCy Schubert 		return;
64885732ac8SCy Schubert 	}
64985732ac8SCy Schubert 	dialog_token = *buf++;
65085732ac8SCy Schubert 	len--;
65185732ac8SCy Schubert 
65285732ac8SCy Schubert 	wpa_printf(MSG_DEBUG,
65385732ac8SCy Schubert 		   "WNM: Received Collocated Interference Report frame from "
65485732ac8SCy Schubert 		   MACSTR " (dialog_token=%u)",
65585732ac8SCy Schubert 		   MAC2STR(addr), dialog_token);
65685732ac8SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
65785732ac8SCy Schubert 		    buf, len);
65885732ac8SCy Schubert 
65985732ac8SCy Schubert 	hex_len = 2 * len + 1;
66085732ac8SCy Schubert 	hex = os_malloc(hex_len);
66185732ac8SCy Schubert 	if (!hex)
66285732ac8SCy Schubert 		return;
66385732ac8SCy Schubert 	wpa_snprintf_hex(hex, hex_len, buf, len);
66485732ac8SCy Schubert 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
66585732ac8SCy Schubert 		     MAC2STR(addr), dialog_token, hex);
66685732ac8SCy Schubert 	os_free(hex);
66785732ac8SCy Schubert }
66885732ac8SCy Schubert 
66985732ac8SCy Schubert 
670*a90b9d01SCy Schubert 
wnm_event_type2str(enum wnm_event_report_type wtype)671*a90b9d01SCy Schubert static const char * wnm_event_type2str(enum wnm_event_report_type wtype)
672*a90b9d01SCy Schubert {
673*a90b9d01SCy Schubert #define W2S(wtype) case WNM_EVENT_TYPE_ ## wtype: return #wtype;
674*a90b9d01SCy Schubert 	switch (wtype) {
675*a90b9d01SCy Schubert 	W2S(TRANSITION)
676*a90b9d01SCy Schubert 	W2S(RSNA)
677*a90b9d01SCy Schubert 	W2S(P2P_LINK)
678*a90b9d01SCy Schubert 	W2S(WNM_LOG)
679*a90b9d01SCy Schubert 	W2S(BSS_COLOR_COLLISION)
680*a90b9d01SCy Schubert 	W2S(BSS_COLOR_IN_USE)
681*a90b9d01SCy Schubert 	}
682*a90b9d01SCy Schubert 	return "UNKNOWN";
683*a90b9d01SCy Schubert #undef W2S
684*a90b9d01SCy Schubert }
685*a90b9d01SCy Schubert 
686*a90b9d01SCy Schubert 
ieee802_11_rx_wnm_event_report(struct hostapd_data * hapd,const u8 * addr,const u8 * buf,size_t len)687*a90b9d01SCy Schubert static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd,
688*a90b9d01SCy Schubert 					   const u8 *addr, const u8 *buf,
689*a90b9d01SCy Schubert 					   size_t len)
690*a90b9d01SCy Schubert {
691*a90b9d01SCy Schubert 	struct sta_info *sta;
692*a90b9d01SCy Schubert 	u8 dialog_token;
693*a90b9d01SCy Schubert 	struct wnm_event_report_element *report_ie;
694*a90b9d01SCy Schubert 	const u8 *pos = buf, *end = buf + len;
695*a90b9d01SCy Schubert 	const size_t fixed_field_len = 3; /* Event Token/Type/Report Status */
696*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AX
697*a90b9d01SCy Schubert 	const size_t tsf_len = 8;
698*a90b9d01SCy Schubert 	u8 color;
699*a90b9d01SCy Schubert 	u64 bitmap;
700*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AX */
701*a90b9d01SCy Schubert 
702*a90b9d01SCy Schubert 	if (end - pos < 1 + 2) {
703*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
704*a90b9d01SCy Schubert 			   "WNM: Ignore too short WNM Event Report frame from "
705*a90b9d01SCy Schubert 			   MACSTR, MAC2STR(addr));
706*a90b9d01SCy Schubert 		return;
707*a90b9d01SCy Schubert 	}
708*a90b9d01SCy Schubert 
709*a90b9d01SCy Schubert 	dialog_token = *pos++;
710*a90b9d01SCy Schubert 	report_ie = (struct wnm_event_report_element *) pos;
711*a90b9d01SCy Schubert 
712*a90b9d01SCy Schubert 	if (end - pos < 2 + report_ie->len ||
713*a90b9d01SCy Schubert 	    report_ie->len < fixed_field_len) {
714*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
715*a90b9d01SCy Schubert 			   "WNM: Ignore truncated WNM Event Report frame from "
716*a90b9d01SCy Schubert 			   MACSTR, MAC2STR(addr));
717*a90b9d01SCy Schubert 		return;
718*a90b9d01SCy Schubert 	}
719*a90b9d01SCy Schubert 
720*a90b9d01SCy Schubert 	if (report_ie->eid != WLAN_EID_EVENT_REPORT ||
721*a90b9d01SCy Schubert 	    report_ie->status != WNM_STATUS_SUCCESSFUL)
722*a90b9d01SCy Schubert 		return;
723*a90b9d01SCy Schubert 
724*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "WNM: Received WNM Event Report frame from "
725*a90b9d01SCy Schubert 		   MACSTR " dialog_token=%u event_token=%u type=%d (%s)",
726*a90b9d01SCy Schubert 		   MAC2STR(addr), dialog_token, report_ie->token,
727*a90b9d01SCy Schubert 		   report_ie->type, wnm_event_type2str(report_ie->type));
728*a90b9d01SCy Schubert 
729*a90b9d01SCy Schubert 	pos += 2 + fixed_field_len;
730*a90b9d01SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "WNM: Event Report", pos, end - pos);
731*a90b9d01SCy Schubert 
732*a90b9d01SCy Schubert 	sta = ap_get_sta(hapd, addr);
733*a90b9d01SCy Schubert 	if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
734*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "Station " MACSTR
735*a90b9d01SCy Schubert 			   " not found for received WNM Event Report",
736*a90b9d01SCy Schubert 			   MAC2STR(addr));
737*a90b9d01SCy Schubert 		return;
738*a90b9d01SCy Schubert 	}
739*a90b9d01SCy Schubert 
740*a90b9d01SCy Schubert 	switch (report_ie->type) {
741*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211AX
742*a90b9d01SCy Schubert 	case WNM_EVENT_TYPE_BSS_COLOR_COLLISION:
743*a90b9d01SCy Schubert 		if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
744*a90b9d01SCy Schubert 			return;
745*a90b9d01SCy Schubert 		if (report_ie->len <
746*a90b9d01SCy Schubert 		    fixed_field_len + tsf_len + 8) {
747*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
748*a90b9d01SCy Schubert 				   "WNM: Too short BSS color collision event report from "
749*a90b9d01SCy Schubert 				   MACSTR, MAC2STR(addr));
750*a90b9d01SCy Schubert 			return;
751*a90b9d01SCy Schubert 		}
752*a90b9d01SCy Schubert 		bitmap = WPA_GET_LE64(report_ie->u.bss_color_collision.color_bitmap);
753*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
754*a90b9d01SCy Schubert 			   "WNM: BSS color collision bitmap 0x%llx reported by "
755*a90b9d01SCy Schubert 			   MACSTR, (unsigned long long) bitmap, MAC2STR(addr));
756*a90b9d01SCy Schubert 		hostapd_switch_color(hapd->iface->bss[0], bitmap);
757*a90b9d01SCy Schubert 		break;
758*a90b9d01SCy Schubert 	case WNM_EVENT_TYPE_BSS_COLOR_IN_USE:
759*a90b9d01SCy Schubert 		if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
760*a90b9d01SCy Schubert 			return;
761*a90b9d01SCy Schubert 		if (report_ie->len < fixed_field_len + tsf_len + 1) {
762*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
763*a90b9d01SCy Schubert 				   "WNM: Too short BSS color in use event report from "
764*a90b9d01SCy Schubert 				   MACSTR, MAC2STR(addr));
765*a90b9d01SCy Schubert 			return;
766*a90b9d01SCy Schubert 		}
767*a90b9d01SCy Schubert 		color = report_ie->u.bss_color_in_use.color;
768*a90b9d01SCy Schubert 		if (color > 63) {
769*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
770*a90b9d01SCy Schubert 				   "WNM: Invalid BSS color %u report from "
771*a90b9d01SCy Schubert 				   MACSTR, color, MAC2STR(addr));
772*a90b9d01SCy Schubert 			return;
773*a90b9d01SCy Schubert 		}
774*a90b9d01SCy Schubert 		if (color == 0) {
775*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
776*a90b9d01SCy Schubert 				   "WNM: BSS color use report canceled by "
777*a90b9d01SCy Schubert 				   MACSTR, MAC2STR(addr));
778*a90b9d01SCy Schubert 			/* TODO: Clear stored color from the collision bitmap
779*a90b9d01SCy Schubert 			 * if there are no other users for it. */
780*a90b9d01SCy Schubert 			return;
781*a90b9d01SCy Schubert 		}
782*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "WNM: BSS color %u use report by "
783*a90b9d01SCy Schubert 			   MACSTR, color, MAC2STR(addr));
784*a90b9d01SCy Schubert 		hapd->color_collision_bitmap |= 1ULL << color;
785*a90b9d01SCy Schubert 		break;
786*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211AX */
787*a90b9d01SCy Schubert 	default:
788*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
789*a90b9d01SCy Schubert 			   "WNM Event Report type=%d (%s) not supported",
790*a90b9d01SCy Schubert 			   report_ie->type,
791*a90b9d01SCy Schubert 			   wnm_event_type2str(report_ie->type));
792*a90b9d01SCy Schubert 		break;
793*a90b9d01SCy Schubert 	}
794*a90b9d01SCy Schubert }
795*a90b9d01SCy Schubert 
796*a90b9d01SCy Schubert 
ieee802_11_rx_wnm_action_ap(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)7975b9c547cSRui Paulo int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
7985b9c547cSRui Paulo 				const struct ieee80211_mgmt *mgmt, size_t len)
7995b9c547cSRui Paulo {
8005b9c547cSRui Paulo 	u8 action;
8015b9c547cSRui Paulo 	const u8 *payload;
8025b9c547cSRui Paulo 	size_t plen;
8035b9c547cSRui Paulo 
8045b9c547cSRui Paulo 	if (len < IEEE80211_HDRLEN + 2)
805f05cddf9SRui Paulo 		return -1;
806f05cddf9SRui Paulo 
8075b9c547cSRui Paulo 	payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
8085b9c547cSRui Paulo 	action = *payload++;
8095b9c547cSRui Paulo 	plen = len - IEEE80211_HDRLEN - 2;
8105b9c547cSRui Paulo 
8115b9c547cSRui Paulo 	switch (action) {
812*a90b9d01SCy Schubert 	case WNM_EVENT_REPORT:
813*a90b9d01SCy Schubert 		ieee802_11_rx_wnm_event_report(hapd, mgmt->sa, payload,
814*a90b9d01SCy Schubert 					       plen);
815*a90b9d01SCy Schubert 		return 0;
816f05cddf9SRui Paulo 	case WNM_BSS_TRANS_MGMT_QUERY:
8175b9c547cSRui Paulo 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
8185b9c547cSRui Paulo 						   plen);
8195b9c547cSRui Paulo 		return 0;
820f05cddf9SRui Paulo 	case WNM_BSS_TRANS_MGMT_RESP:
8215b9c547cSRui Paulo 		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
8225b9c547cSRui Paulo 						  plen);
8235b9c547cSRui Paulo 		return 0;
824f05cddf9SRui Paulo 	case WNM_SLEEP_MODE_REQ:
8255b9c547cSRui Paulo 		ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
826f05cddf9SRui Paulo 		return 0;
827780fb4a2SCy Schubert 	case WNM_NOTIFICATION_REQ:
828780fb4a2SCy Schubert 		ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
829780fb4a2SCy Schubert 						   plen);
830780fb4a2SCy Schubert 		return 0;
83185732ac8SCy Schubert 	case WNM_COLLOCATED_INTERFERENCE_REPORT:
83285732ac8SCy Schubert 		ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
83385732ac8SCy Schubert 						    plen);
83485732ac8SCy Schubert 		return 0;
835f05cddf9SRui Paulo 	}
836f05cddf9SRui Paulo 
837f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
8385b9c547cSRui Paulo 		   action, MAC2STR(mgmt->sa));
839f05cddf9SRui Paulo 	return -1;
840f05cddf9SRui Paulo }
8415b9c547cSRui Paulo 
8425b9c547cSRui Paulo 
wnm_send_disassoc_imminent(struct hostapd_data * hapd,struct sta_info * sta,int disassoc_timer)8435b9c547cSRui Paulo int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
8445b9c547cSRui Paulo 			       struct sta_info *sta, int disassoc_timer)
8455b9c547cSRui Paulo {
8465b9c547cSRui Paulo 	u8 buf[1000], *pos;
8475b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
848*a90b9d01SCy Schubert 	const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
8495b9c547cSRui Paulo 
8505b9c547cSRui Paulo 	os_memset(buf, 0, sizeof(buf));
8515b9c547cSRui Paulo 	mgmt = (struct ieee80211_mgmt *) buf;
8525b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
8535b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
8545b9c547cSRui Paulo 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
855*a90b9d01SCy Schubert 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
856*a90b9d01SCy Schubert 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
8575b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
8585b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
8595b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
8605b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode =
8615b9c547cSRui Paulo 		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
8625b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer =
8635b9c547cSRui Paulo 		host_to_le16(disassoc_timer);
8645b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
8655b9c547cSRui Paulo 
8665b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
8675b9c547cSRui Paulo 
8685b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
8695b9c547cSRui Paulo 		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
870c1d255d3SCy Schubert 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
8715b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
8725b9c547cSRui Paulo 			   "Management Request frame");
8735b9c547cSRui Paulo 		return -1;
8745b9c547cSRui Paulo 	}
8755b9c547cSRui Paulo 
8765b9c547cSRui Paulo 	return 0;
8775b9c547cSRui Paulo }
8785b9c547cSRui Paulo 
8795b9c547cSRui Paulo 
set_disassoc_timer(struct hostapd_data * hapd,struct sta_info * sta,int disassoc_timer)8805b9c547cSRui Paulo static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
8815b9c547cSRui Paulo 			       int disassoc_timer)
8825b9c547cSRui Paulo {
8835b9c547cSRui Paulo 	int timeout, beacon_int;
8845b9c547cSRui Paulo 
8855b9c547cSRui Paulo 	/*
8865b9c547cSRui Paulo 	 * Prevent STA from reconnecting using cached PMKSA to force
8875b9c547cSRui Paulo 	 * full authentication with the authentication server (which may
8885b9c547cSRui Paulo 	 * decide to reject the connection),
8895b9c547cSRui Paulo 	 */
8905b9c547cSRui Paulo 	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
8915b9c547cSRui Paulo 
8925b9c547cSRui Paulo 	beacon_int = hapd->iconf->beacon_int;
8935b9c547cSRui Paulo 	if (beacon_int < 1)
8945b9c547cSRui Paulo 		beacon_int = 100; /* best guess */
8955b9c547cSRui Paulo 	/* Calculate timeout in ms based on beacon_int in TU */
8965b9c547cSRui Paulo 	timeout = disassoc_timer * beacon_int * 128 / 125;
8975b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
8985b9c547cSRui Paulo 		   " set to %d ms", MAC2STR(sta->addr), timeout);
8995b9c547cSRui Paulo 
9005b9c547cSRui Paulo 	sta->timeout_next = STA_DISASSOC_FROM_CLI;
9015b9c547cSRui Paulo 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
9025b9c547cSRui Paulo 	eloop_register_timeout(timeout / 1000,
9035b9c547cSRui Paulo 			       timeout % 1000 * 1000,
9045b9c547cSRui Paulo 			       ap_handle_timer, hapd, sta);
9055b9c547cSRui Paulo }
9065b9c547cSRui Paulo 
9075b9c547cSRui Paulo 
wnm_send_ess_disassoc_imminent(struct hostapd_data * hapd,struct sta_info * sta,const char * url,int disassoc_timer)9085b9c547cSRui Paulo int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
9095b9c547cSRui Paulo 				   struct sta_info *sta, const char *url,
9105b9c547cSRui Paulo 				   int disassoc_timer)
9115b9c547cSRui Paulo {
9125b9c547cSRui Paulo 	u8 buf[1000], *pos;
9135b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
9145b9c547cSRui Paulo 	size_t url_len;
915*a90b9d01SCy Schubert 	const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
9165b9c547cSRui Paulo 
9175b9c547cSRui Paulo 	os_memset(buf, 0, sizeof(buf));
9185b9c547cSRui Paulo 	mgmt = (struct ieee80211_mgmt *) buf;
9195b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
9205b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
9215b9c547cSRui Paulo 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
922*a90b9d01SCy Schubert 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
923*a90b9d01SCy Schubert 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
9245b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
9255b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
9265b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
9275b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode =
9285b9c547cSRui Paulo 		WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
9295b9c547cSRui Paulo 		WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
9305b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer =
9315b9c547cSRui Paulo 		host_to_le16(disassoc_timer);
9325b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
9335b9c547cSRui Paulo 
9345b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
9355b9c547cSRui Paulo 
9365b9c547cSRui Paulo 	/* Session Information URL */
9375b9c547cSRui Paulo 	url_len = os_strlen(url);
9385b9c547cSRui Paulo 	if (url_len > 255)
9395b9c547cSRui Paulo 		return -1;
9405b9c547cSRui Paulo 	*pos++ = url_len;
9415b9c547cSRui Paulo 	os_memcpy(pos, url, url_len);
9425b9c547cSRui Paulo 	pos += url_len;
9435b9c547cSRui Paulo 
944c1d255d3SCy Schubert 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
9455b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
9465b9c547cSRui Paulo 			   "Management Request frame");
9475b9c547cSRui Paulo 		return -1;
9485b9c547cSRui Paulo 	}
9495b9c547cSRui Paulo 
9505b9c547cSRui Paulo 	if (disassoc_timer) {
9515b9c547cSRui Paulo 		/* send disassociation frame after time-out */
9525b9c547cSRui Paulo 		set_disassoc_timer(hapd, sta, disassoc_timer);
9535b9c547cSRui Paulo 	}
9545b9c547cSRui Paulo 
9555b9c547cSRui Paulo 	return 0;
9565b9c547cSRui Paulo }
9575b9c547cSRui Paulo 
9585b9c547cSRui Paulo 
wnm_send_bss_tm_req(struct hostapd_data * hapd,struct sta_info * sta,u8 req_mode,int disassoc_timer,u8 valid_int,const u8 * bss_term_dur,u8 dialog_token,const char * url,const u8 * nei_rep,size_t nei_rep_len,const u8 * mbo_attrs,size_t mbo_len)9595b9c547cSRui Paulo int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
9605b9c547cSRui Paulo 			u8 req_mode, int disassoc_timer, u8 valid_int,
9614b72b91aSCy Schubert 			const u8 *bss_term_dur, u8 dialog_token,
9624b72b91aSCy Schubert 			const char *url, const u8 *nei_rep, size_t nei_rep_len,
963780fb4a2SCy Schubert 			const u8 *mbo_attrs, size_t mbo_len)
9645b9c547cSRui Paulo {
9655b9c547cSRui Paulo 	u8 *buf, *pos;
9665b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
9675b9c547cSRui Paulo 	size_t url_len;
968*a90b9d01SCy Schubert 	const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
9695b9c547cSRui Paulo 
9705b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
9714b72b91aSCy Schubert 		   MACSTR
9724b72b91aSCy Schubert 		   " req_mode=0x%x disassoc_timer=%d valid_int=0x%x dialog_token=%u",
9734b72b91aSCy Schubert 		   MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int,
9744b72b91aSCy Schubert 		   dialog_token);
975780fb4a2SCy Schubert 	buf = os_zalloc(1000 + nei_rep_len + mbo_len);
9765b9c547cSRui Paulo 	if (buf == NULL)
9775b9c547cSRui Paulo 		return -1;
9785b9c547cSRui Paulo 	mgmt = (struct ieee80211_mgmt *) buf;
9795b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
9805b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
9815b9c547cSRui Paulo 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
982*a90b9d01SCy Schubert 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
983*a90b9d01SCy Schubert 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
9845b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
9855b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
9864b72b91aSCy Schubert 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
9875b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
9885b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer =
9895b9c547cSRui Paulo 		host_to_le16(disassoc_timer);
9905b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
9915b9c547cSRui Paulo 
9925b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
9935b9c547cSRui Paulo 
9945b9c547cSRui Paulo 	if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
9955b9c547cSRui Paulo 	    bss_term_dur) {
9965b9c547cSRui Paulo 		os_memcpy(pos, bss_term_dur, 12);
9975b9c547cSRui Paulo 		pos += 12;
9985b9c547cSRui Paulo 	}
9995b9c547cSRui Paulo 
10005b9c547cSRui Paulo 	if (url) {
10015b9c547cSRui Paulo 		/* Session Information URL */
10025b9c547cSRui Paulo 		url_len = os_strlen(url);
10035b9c547cSRui Paulo 		if (url_len > 255) {
10045b9c547cSRui Paulo 			os_free(buf);
10055b9c547cSRui Paulo 			return -1;
10065b9c547cSRui Paulo 		}
10075b9c547cSRui Paulo 
10085b9c547cSRui Paulo 		*pos++ = url_len;
10095b9c547cSRui Paulo 		os_memcpy(pos, url, url_len);
10105b9c547cSRui Paulo 		pos += url_len;
10115b9c547cSRui Paulo 	}
10125b9c547cSRui Paulo 
10135b9c547cSRui Paulo 	if (nei_rep) {
10145b9c547cSRui Paulo 		os_memcpy(pos, nei_rep, nei_rep_len);
10155b9c547cSRui Paulo 		pos += nei_rep_len;
10165b9c547cSRui Paulo 	}
10175b9c547cSRui Paulo 
1018780fb4a2SCy Schubert 	if (mbo_len > 0) {
1019780fb4a2SCy Schubert 		pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
1020780fb4a2SCy Schubert 				  mbo_len);
1021780fb4a2SCy Schubert 	}
1022780fb4a2SCy Schubert 
1023c1d255d3SCy Schubert 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
10245b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
10255b9c547cSRui Paulo 			   "Failed to send BSS Transition Management Request frame");
10265b9c547cSRui Paulo 		os_free(buf);
10275b9c547cSRui Paulo 		return -1;
10285b9c547cSRui Paulo 	}
10295b9c547cSRui Paulo 	os_free(buf);
10305b9c547cSRui Paulo 
10315b9c547cSRui Paulo 	if (disassoc_timer) {
1032*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
1033*a90b9d01SCy Schubert 		if (ap_sta_is_mld(hapd, sta)) {
1034*a90b9d01SCy Schubert 			int i;
1035*a90b9d01SCy Schubert 			unsigned int links = 0;
1036*a90b9d01SCy Schubert 
1037*a90b9d01SCy Schubert 			for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
1038*a90b9d01SCy Schubert 				if (sta->mld_info.links[i].valid)
1039*a90b9d01SCy Schubert 					links++;
1040*a90b9d01SCy Schubert 			}
1041*a90b9d01SCy Schubert 
1042*a90b9d01SCy Schubert 			if (links > 1) {
1043*a90b9d01SCy Schubert 				wpa_printf(MSG_DEBUG,
1044*a90b9d01SCy Schubert 					   "WNM: Only terminating one link - other links remains associated for "
1045*a90b9d01SCy Schubert 					   MACSTR,
1046*a90b9d01SCy Schubert 					   MAC2STR(sta->mld_info.common_info.mld_addr));
1047*a90b9d01SCy Schubert 				return 0;
1048*a90b9d01SCy Schubert 			}
1049*a90b9d01SCy Schubert 		}
1050*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
1051*a90b9d01SCy Schubert 
10525b9c547cSRui Paulo 		/* send disassociation frame after time-out */
10535b9c547cSRui Paulo 		set_disassoc_timer(hapd, sta, disassoc_timer);
10545b9c547cSRui Paulo 	}
10555b9c547cSRui Paulo 
10565b9c547cSRui Paulo 	return 0;
10575b9c547cSRui Paulo }
105885732ac8SCy Schubert 
105985732ac8SCy Schubert 
wnm_send_coloc_intf_req(struct hostapd_data * hapd,struct sta_info * sta,unsigned int auto_report,unsigned int timeout)106085732ac8SCy Schubert int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
106185732ac8SCy Schubert 			    unsigned int auto_report, unsigned int timeout)
106285732ac8SCy Schubert {
106385732ac8SCy Schubert 	u8 buf[100], *pos;
106485732ac8SCy Schubert 	struct ieee80211_mgmt *mgmt;
106585732ac8SCy Schubert 	u8 dialog_token = 1;
1066*a90b9d01SCy Schubert 	const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
106785732ac8SCy Schubert 
106885732ac8SCy Schubert 	if (auto_report > 3 || timeout > 63)
106985732ac8SCy Schubert 		return -1;
107085732ac8SCy Schubert 	os_memset(buf, 0, sizeof(buf));
107185732ac8SCy Schubert 	mgmt = (struct ieee80211_mgmt *) buf;
107285732ac8SCy Schubert 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
107385732ac8SCy Schubert 					   WLAN_FC_STYPE_ACTION);
107485732ac8SCy Schubert 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
1075*a90b9d01SCy Schubert 	os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
1076*a90b9d01SCy Schubert 	os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
107785732ac8SCy Schubert 	mgmt->u.action.category = WLAN_ACTION_WNM;
107885732ac8SCy Schubert 	mgmt->u.action.u.coloc_intf_req.action =
107985732ac8SCy Schubert 		WNM_COLLOCATED_INTERFERENCE_REQ;
108085732ac8SCy Schubert 	mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
108185732ac8SCy Schubert 	mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
108285732ac8SCy Schubert 	pos = &mgmt->u.action.u.coloc_intf_req.req_info;
108385732ac8SCy Schubert 	pos++;
108485732ac8SCy Schubert 
108585732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
108685732ac8SCy Schubert 		   MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
108785732ac8SCy Schubert 		   MAC2STR(sta->addr), dialog_token, auto_report, timeout);
1088c1d255d3SCy Schubert 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
108985732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
109085732ac8SCy Schubert 			   "WNM: Failed to send Collocated Interference Request frame");
109185732ac8SCy Schubert 		return -1;
109285732ac8SCy Schubert 	}
109385732ac8SCy Schubert 
109485732ac8SCy Schubert 	return 0;
109585732ac8SCy Schubert }
1096