xref: /freebsd/contrib/wpa/src/ap/wnm_ap.c (revision 85732ac8bccbc0adcf5a261ea1ffec8ca7b3a92d)
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"
15f05cddf9SRui Paulo #include "ap/hostapd.h"
16f05cddf9SRui Paulo #include "ap/sta_info.h"
17f05cddf9SRui Paulo #include "ap/ap_config.h"
18f05cddf9SRui Paulo #include "ap/ap_drv_ops.h"
19f05cddf9SRui Paulo #include "ap/wpa_auth.h"
20780fb4a2SCy Schubert #include "mbo_ap.h"
21f05cddf9SRui Paulo #include "wnm_ap.h"
22f05cddf9SRui Paulo 
23f05cddf9SRui Paulo #define MAX_TFS_IE_LEN  1024
24f05cddf9SRui Paulo 
25f05cddf9SRui Paulo 
26f05cddf9SRui Paulo /* get the TFS IE from driver */
27f05cddf9SRui Paulo static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
28f05cddf9SRui Paulo 				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
29f05cddf9SRui Paulo {
30f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
31f05cddf9SRui Paulo 
32f05cddf9SRui Paulo 	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
33f05cddf9SRui Paulo }
34f05cddf9SRui Paulo 
35f05cddf9SRui Paulo 
36f05cddf9SRui Paulo /* set the TFS IE to driver */
37f05cddf9SRui Paulo static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
38f05cddf9SRui Paulo 				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
39f05cddf9SRui Paulo {
40f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
41f05cddf9SRui Paulo 
42f05cddf9SRui Paulo 	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
43f05cddf9SRui Paulo }
44f05cddf9SRui Paulo 
45f05cddf9SRui Paulo 
46f05cddf9SRui Paulo /* MLME-SLEEPMODE.response */
47f05cddf9SRui Paulo static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
48f05cddf9SRui Paulo 					 const u8 *addr, u8 dialog_token,
49f05cddf9SRui Paulo 					 u8 action_type, u16 intval)
50f05cddf9SRui Paulo {
51f05cddf9SRui Paulo 	struct ieee80211_mgmt *mgmt;
52f05cddf9SRui Paulo 	int res;
53f05cddf9SRui Paulo 	size_t len;
54f05cddf9SRui Paulo 	size_t gtk_elem_len = 0;
55f05cddf9SRui Paulo 	size_t igtk_elem_len = 0;
56f05cddf9SRui Paulo 	struct wnm_sleep_element wnmsleep_ie;
57f05cddf9SRui Paulo 	u8 *wnmtfs_ie;
58f05cddf9SRui Paulo 	u8 wnmsleep_ie_len;
59f05cddf9SRui Paulo 	u16 wnmtfs_ie_len;
60f05cddf9SRui Paulo 	u8 *pos;
61f05cddf9SRui Paulo 	struct sta_info *sta;
62f05cddf9SRui Paulo 	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
63f05cddf9SRui Paulo 		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
64f05cddf9SRui Paulo 
65f05cddf9SRui Paulo 	sta = ap_get_sta(hapd, addr);
66f05cddf9SRui Paulo 	if (sta == NULL) {
67f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
68f05cddf9SRui Paulo 		return -EINVAL;
69f05cddf9SRui Paulo 	}
70f05cddf9SRui Paulo 
71f05cddf9SRui Paulo 	/* WNM-Sleep Mode IE */
72f05cddf9SRui Paulo 	os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
73f05cddf9SRui Paulo 	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
74f05cddf9SRui Paulo 	wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
75f05cddf9SRui Paulo 	wnmsleep_ie.len = wnmsleep_ie_len - 2;
76f05cddf9SRui Paulo 	wnmsleep_ie.action_type = action_type;
77f05cddf9SRui Paulo 	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
785b9c547cSRui Paulo 	wnmsleep_ie.intval = host_to_le16(intval);
79f05cddf9SRui Paulo 
80f05cddf9SRui Paulo 	/* TFS IE(s) */
81f05cddf9SRui Paulo 	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
82f05cddf9SRui Paulo 	if (wnmtfs_ie == NULL)
83f05cddf9SRui Paulo 		return -1;
84f05cddf9SRui Paulo 	if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
85f05cddf9SRui Paulo 				    tfs_oper)) {
86f05cddf9SRui Paulo 		wnmtfs_ie_len = 0;
87f05cddf9SRui Paulo 		os_free(wnmtfs_ie);
88f05cddf9SRui Paulo 		wnmtfs_ie = NULL;
89f05cddf9SRui Paulo 	}
90f05cddf9SRui Paulo 
91f05cddf9SRui Paulo #define MAX_GTK_SUBELEM_LEN 45
92f05cddf9SRui Paulo #define MAX_IGTK_SUBELEM_LEN 26
93f05cddf9SRui Paulo 	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
94f05cddf9SRui Paulo 			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
95f05cddf9SRui Paulo 	if (mgmt == NULL) {
96f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
97f05cddf9SRui Paulo 			   "WNM-Sleep Response action frame");
98*85732ac8SCy Schubert 		res = -1;
99*85732ac8SCy Schubert 		goto fail;
100f05cddf9SRui Paulo 	}
101f05cddf9SRui Paulo 	os_memcpy(mgmt->da, addr, ETH_ALEN);
102f05cddf9SRui Paulo 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
103f05cddf9SRui Paulo 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
104f05cddf9SRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
105f05cddf9SRui Paulo 					   WLAN_FC_STYPE_ACTION);
106f05cddf9SRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
107f05cddf9SRui Paulo 	mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
108f05cddf9SRui Paulo 	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
109f05cddf9SRui Paulo 	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
110f05cddf9SRui Paulo 	/* add key data if MFP is enabled */
111f05cddf9SRui Paulo 	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
112*85732ac8SCy Schubert 	    hapd->conf->wnm_sleep_mode_no_keys ||
113f05cddf9SRui Paulo 	    action_type != WNM_SLEEP_MODE_EXIT) {
114f05cddf9SRui Paulo 		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
115f05cddf9SRui Paulo 	} else {
116f05cddf9SRui Paulo 		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
117f05cddf9SRui Paulo 		pos += gtk_elem_len;
118f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
119f05cddf9SRui Paulo 			   (int) gtk_elem_len);
120f05cddf9SRui Paulo #ifdef CONFIG_IEEE80211W
121f05cddf9SRui Paulo 		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
122*85732ac8SCy Schubert 		if (res < 0)
123*85732ac8SCy Schubert 			goto fail;
124f05cddf9SRui Paulo 		igtk_elem_len = res;
125f05cddf9SRui Paulo 		pos += igtk_elem_len;
126f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
127f05cddf9SRui Paulo 			   (int) igtk_elem_len);
128f05cddf9SRui Paulo #endif /* CONFIG_IEEE80211W */
129f05cddf9SRui Paulo 
130f05cddf9SRui Paulo 		WPA_PUT_LE16((u8 *)
131f05cddf9SRui Paulo 			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
132f05cddf9SRui Paulo 			     gtk_elem_len + igtk_elem_len);
133f05cddf9SRui Paulo 	}
134f05cddf9SRui Paulo 	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
135f05cddf9SRui Paulo 	/* copy TFS IE here */
136f05cddf9SRui Paulo 	pos += wnmsleep_ie_len;
137f05cddf9SRui Paulo 	if (wnmtfs_ie)
138f05cddf9SRui Paulo 		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
139f05cddf9SRui Paulo 
140f05cddf9SRui Paulo 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
141f05cddf9SRui Paulo 		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
142f05cddf9SRui Paulo 
143f05cddf9SRui Paulo 	/* In driver, response frame should be forced to sent when STA is in
144f05cddf9SRui Paulo 	 * PS mode */
145f05cddf9SRui Paulo 	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
146f05cddf9SRui Paulo 				      mgmt->da, &mgmt->u.action.category, len);
147f05cddf9SRui Paulo 
148f05cddf9SRui Paulo 	if (!res) {
149f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
150f05cddf9SRui Paulo 			   "frame");
151f05cddf9SRui Paulo 
152f05cddf9SRui Paulo 		/* when entering wnmsleep
153f05cddf9SRui Paulo 		 * 1. pause the node in driver
154f05cddf9SRui Paulo 		 * 2. mark the node so that AP won't update GTK/IGTK during
155f05cddf9SRui Paulo 		 * WNM Sleep
156f05cddf9SRui Paulo 		 */
157f05cddf9SRui Paulo 		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
158f05cddf9SRui Paulo 		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
1595b9c547cSRui Paulo 			sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
160f05cddf9SRui Paulo 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
161f05cddf9SRui Paulo 					     addr, NULL, NULL);
162f05cddf9SRui Paulo 			wpa_set_wnmsleep(sta->wpa_sm, 1);
163f05cddf9SRui Paulo 		}
164f05cddf9SRui Paulo 		/* when exiting wnmsleep
165f05cddf9SRui Paulo 		 * 1. unmark the node
166f05cddf9SRui Paulo 		 * 2. start GTK/IGTK update if MFP is not used
167f05cddf9SRui Paulo 		 * 3. unpause the node in driver
168f05cddf9SRui Paulo 		 */
169f05cddf9SRui Paulo 		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
170f05cddf9SRui Paulo 		     wnmsleep_ie.status ==
171f05cddf9SRui Paulo 		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
172f05cddf9SRui Paulo 		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
1735b9c547cSRui Paulo 			sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
174f05cddf9SRui Paulo 			wpa_set_wnmsleep(sta->wpa_sm, 0);
175f05cddf9SRui Paulo 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
176f05cddf9SRui Paulo 					     addr, NULL, NULL);
177*85732ac8SCy Schubert 			if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
178*85732ac8SCy Schubert 			    hapd->conf->wnm_sleep_mode_no_keys)
179f05cddf9SRui Paulo 				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
180f05cddf9SRui Paulo 		}
181f05cddf9SRui Paulo 	} else
182f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
183f05cddf9SRui Paulo 
184f05cddf9SRui Paulo #undef MAX_GTK_SUBELEM_LEN
185f05cddf9SRui Paulo #undef MAX_IGTK_SUBELEM_LEN
186*85732ac8SCy Schubert fail:
187f05cddf9SRui Paulo 	os_free(wnmtfs_ie);
188f05cddf9SRui Paulo 	os_free(mgmt);
189f05cddf9SRui Paulo 	return res;
190f05cddf9SRui Paulo }
191f05cddf9SRui Paulo 
192f05cddf9SRui Paulo 
193f05cddf9SRui Paulo static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
194f05cddf9SRui Paulo 				       const u8 *addr, const u8 *frm, int len)
195f05cddf9SRui Paulo {
196f05cddf9SRui Paulo 	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
197f05cddf9SRui Paulo 	const u8 *pos = frm;
198f05cddf9SRui Paulo 	u8 dialog_token;
199f05cddf9SRui Paulo 	struct wnm_sleep_element *wnmsleep_ie = NULL;
200f05cddf9SRui Paulo 	/* multiple TFS Req IE (assuming consecutive) */
201f05cddf9SRui Paulo 	u8 *tfsreq_ie_start = NULL;
202f05cddf9SRui Paulo 	u8 *tfsreq_ie_end = NULL;
203f05cddf9SRui Paulo 	u16 tfsreq_ie_len = 0;
204f05cddf9SRui Paulo 
205*85732ac8SCy Schubert 	if (!hapd->conf->wnm_sleep_mode) {
206*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
207*85732ac8SCy Schubert 			   MACSTR " since WNM-Sleep Mode is disabled",
208*85732ac8SCy Schubert 			   MAC2STR(addr));
209*85732ac8SCy Schubert 		return;
210*85732ac8SCy Schubert 	}
211*85732ac8SCy Schubert 
212f05cddf9SRui Paulo 	dialog_token = *pos++;
213f05cddf9SRui Paulo 	while (pos + 1 < frm + len) {
214f05cddf9SRui Paulo 		u8 ie_len = pos[1];
215f05cddf9SRui Paulo 		if (pos + 2 + ie_len > frm + len)
216f05cddf9SRui Paulo 			break;
217*85732ac8SCy Schubert 		if (*pos == WLAN_EID_WNMSLEEP &&
218*85732ac8SCy Schubert 		    ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
219f05cddf9SRui Paulo 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
220f05cddf9SRui Paulo 		else if (*pos == WLAN_EID_TFS_REQ) {
221f05cddf9SRui Paulo 			if (!tfsreq_ie_start)
222f05cddf9SRui Paulo 				tfsreq_ie_start = (u8 *) pos;
223f05cddf9SRui Paulo 			tfsreq_ie_end = (u8 *) pos;
224f05cddf9SRui Paulo 		} else
225f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
226f05cddf9SRui Paulo 				   *pos);
227f05cddf9SRui Paulo 		pos += ie_len + 2;
228f05cddf9SRui Paulo 	}
229f05cddf9SRui Paulo 
230f05cddf9SRui Paulo 	if (!wnmsleep_ie) {
231f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
232f05cddf9SRui Paulo 		return;
233f05cddf9SRui Paulo 	}
234f05cddf9SRui Paulo 
235f05cddf9SRui Paulo 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
236f05cddf9SRui Paulo 	    tfsreq_ie_start && tfsreq_ie_end &&
237f05cddf9SRui Paulo 	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
238f05cddf9SRui Paulo 		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
239f05cddf9SRui Paulo 			tfsreq_ie_start;
240f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
241f05cddf9SRui Paulo 		/* pass the TFS Req IE(s) to driver for processing */
242f05cddf9SRui Paulo 		if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
243f05cddf9SRui Paulo 					    &tfsreq_ie_len,
244f05cddf9SRui Paulo 					    WNM_SLEEP_TFS_REQ_IE_SET))
245f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
246f05cddf9SRui Paulo 	}
247f05cddf9SRui Paulo 
248f05cddf9SRui Paulo 	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
249f05cddf9SRui Paulo 				      wnmsleep_ie->action_type,
2505b9c547cSRui Paulo 				      le_to_host16(wnmsleep_ie->intval));
251f05cddf9SRui Paulo 
252f05cddf9SRui Paulo 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
253f05cddf9SRui Paulo 		/* clear the tfs after sending the resp frame */
254f05cddf9SRui Paulo 		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
255f05cddf9SRui Paulo 					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
256f05cddf9SRui Paulo 	}
257f05cddf9SRui Paulo }
258f05cddf9SRui Paulo 
259f05cddf9SRui Paulo 
2605b9c547cSRui Paulo static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
2615b9c547cSRui Paulo 						  const u8 *addr,
262*85732ac8SCy Schubert 						  u8 dialog_token)
263f05cddf9SRui Paulo {
2645b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
265*85732ac8SCy Schubert 	size_t len;
2665b9c547cSRui Paulo 	u8 *pos;
2675b9c547cSRui Paulo 	int res;
2685b9c547cSRui Paulo 
269*85732ac8SCy Schubert 	mgmt = os_zalloc(sizeof(*mgmt));
2705b9c547cSRui Paulo 	if (mgmt == NULL)
2715b9c547cSRui Paulo 		return -1;
2725b9c547cSRui Paulo 	os_memcpy(mgmt->da, addr, ETH_ALEN);
2735b9c547cSRui Paulo 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
2745b9c547cSRui Paulo 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
2755b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
2765b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
2775b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
2785b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
2795b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
2805b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode = 0;
2815b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
2825b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
2835b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
2845b9c547cSRui Paulo 
2855b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
2865b9c547cSRui Paulo 		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
2875b9c547cSRui Paulo 		   "validity_interval=%u",
2885b9c547cSRui Paulo 		   MAC2STR(addr), dialog_token,
2895b9c547cSRui Paulo 		   mgmt->u.action.u.bss_tm_req.req_mode,
2905b9c547cSRui Paulo 		   le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
2915b9c547cSRui Paulo 		   mgmt->u.action.u.bss_tm_req.validity_interval);
2925b9c547cSRui Paulo 
2935b9c547cSRui Paulo 	len = pos - &mgmt->u.action.category;
2945b9c547cSRui Paulo 	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
2955b9c547cSRui Paulo 				      mgmt->da, &mgmt->u.action.category, len);
2965b9c547cSRui Paulo 	os_free(mgmt);
2975b9c547cSRui Paulo 	return res;
2985b9c547cSRui Paulo }
2995b9c547cSRui Paulo 
3005b9c547cSRui Paulo 
3015b9c547cSRui Paulo static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
3025b9c547cSRui Paulo 					       const u8 *addr, const u8 *frm,
3035b9c547cSRui Paulo 					       size_t len)
3045b9c547cSRui Paulo {
3055b9c547cSRui Paulo 	u8 dialog_token, reason;
3065b9c547cSRui Paulo 	const u8 *pos, *end;
307*85732ac8SCy Schubert 	int enabled = hapd->conf->bss_transition;
308*85732ac8SCy Schubert 
309*85732ac8SCy Schubert #ifdef CONFIG_MBO
310*85732ac8SCy Schubert 	if (hapd->conf->mbo_enabled)
311*85732ac8SCy Schubert 		enabled = 1;
312*85732ac8SCy Schubert #endif /* CONFIG_MBO */
313*85732ac8SCy Schubert 	if (!enabled) {
314*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
315*85732ac8SCy Schubert 			   "Ignore BSS Transition Management Query from "
316*85732ac8SCy Schubert 			   MACSTR
317*85732ac8SCy Schubert 			   " since BSS Transition Management is disabled",
318*85732ac8SCy Schubert 			   MAC2STR(addr));
319*85732ac8SCy Schubert 		return;
320*85732ac8SCy Schubert 	}
3215b9c547cSRui Paulo 
3225b9c547cSRui Paulo 	if (len < 2) {
3235b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
3245b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
3255b9c547cSRui Paulo 		return;
3265b9c547cSRui Paulo 	}
3275b9c547cSRui Paulo 
3285b9c547cSRui Paulo 	pos = frm;
3295b9c547cSRui Paulo 	end = pos + len;
3305b9c547cSRui Paulo 	dialog_token = *pos++;
3315b9c547cSRui Paulo 	reason = *pos++;
3325b9c547cSRui Paulo 
3335b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
3345b9c547cSRui Paulo 		   MACSTR " dialog_token=%u reason=%u",
3355b9c547cSRui Paulo 		   MAC2STR(addr), dialog_token, reason);
3365b9c547cSRui Paulo 
3375b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
3385b9c547cSRui Paulo 		    pos, end - pos);
3395b9c547cSRui Paulo 
340*85732ac8SCy Schubert 	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
341*85732ac8SCy Schubert }
342*85732ac8SCy Schubert 
343*85732ac8SCy Schubert 
344*85732ac8SCy Schubert void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
345*85732ac8SCy Schubert {
346*85732ac8SCy Schubert 	struct hostapd_data *hapd = eloop_ctx;
347*85732ac8SCy Schubert 	struct sta_info *sta = timeout_ctx;
348*85732ac8SCy Schubert 
349*85732ac8SCy Schubert 	if (sta->agreed_to_steer) {
350*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
351*85732ac8SCy Schubert 			   hapd->conf->iface, MAC2STR(sta->addr));
352*85732ac8SCy Schubert 		sta->agreed_to_steer = 0;
353*85732ac8SCy Schubert 	}
3545b9c547cSRui Paulo }
3555b9c547cSRui Paulo 
3565b9c547cSRui Paulo 
3575b9c547cSRui Paulo static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
3585b9c547cSRui Paulo 					      const u8 *addr, const u8 *frm,
3595b9c547cSRui Paulo 					      size_t len)
3605b9c547cSRui Paulo {
3615b9c547cSRui Paulo 	u8 dialog_token, status_code, bss_termination_delay;
3625b9c547cSRui Paulo 	const u8 *pos, *end;
363*85732ac8SCy Schubert 	int enabled = hapd->conf->bss_transition;
364*85732ac8SCy Schubert 	struct sta_info *sta;
365*85732ac8SCy Schubert 
366*85732ac8SCy Schubert #ifdef CONFIG_MBO
367*85732ac8SCy Schubert 	if (hapd->conf->mbo_enabled)
368*85732ac8SCy Schubert 		enabled = 1;
369*85732ac8SCy Schubert #endif /* CONFIG_MBO */
370*85732ac8SCy Schubert 	if (!enabled) {
371*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
372*85732ac8SCy Schubert 			   "Ignore BSS Transition Management Response from "
373*85732ac8SCy Schubert 			   MACSTR
374*85732ac8SCy Schubert 			   " since BSS Transition Management is disabled",
375*85732ac8SCy Schubert 			   MAC2STR(addr));
376*85732ac8SCy Schubert 		return;
377*85732ac8SCy Schubert 	}
3785b9c547cSRui Paulo 
3795b9c547cSRui Paulo 	if (len < 3) {
3805b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
3815b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
3825b9c547cSRui Paulo 		return;
3835b9c547cSRui Paulo 	}
3845b9c547cSRui Paulo 
3855b9c547cSRui Paulo 	pos = frm;
3865b9c547cSRui Paulo 	end = pos + len;
3875b9c547cSRui Paulo 	dialog_token = *pos++;
3885b9c547cSRui Paulo 	status_code = *pos++;
3895b9c547cSRui Paulo 	bss_termination_delay = *pos++;
3905b9c547cSRui Paulo 
3915b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
3925b9c547cSRui Paulo 		   MACSTR " dialog_token=%u status_code=%u "
3935b9c547cSRui Paulo 		   "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
3945b9c547cSRui Paulo 		   status_code, bss_termination_delay);
3955b9c547cSRui Paulo 
396*85732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
397*85732ac8SCy Schubert 	if (!sta) {
398*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Station " MACSTR
399*85732ac8SCy Schubert 			   " not found for received BSS TM Response",
400*85732ac8SCy Schubert 			   MAC2STR(addr));
401*85732ac8SCy Schubert 		return;
402*85732ac8SCy Schubert 	}
403*85732ac8SCy Schubert 
4045b9c547cSRui Paulo 	if (status_code == WNM_BSS_TM_ACCEPT) {
4055b9c547cSRui Paulo 		if (end - pos < ETH_ALEN) {
4065b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
4075b9c547cSRui Paulo 			return;
4085b9c547cSRui Paulo 		}
409*85732ac8SCy Schubert 		sta->agreed_to_steer = 1;
410*85732ac8SCy Schubert 		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
411*85732ac8SCy Schubert 		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
412*85732ac8SCy Schubert 				       hapd, sta);
4135b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
4145b9c547cSRui Paulo 			   MAC2STR(pos));
4155b9c547cSRui Paulo 		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
4165b9c547cSRui Paulo 			" status_code=%u bss_termination_delay=%u target_bssid="
4175b9c547cSRui Paulo 			MACSTR,
4185b9c547cSRui Paulo 			MAC2STR(addr), status_code, bss_termination_delay,
4195b9c547cSRui Paulo 			MAC2STR(pos));
4205b9c547cSRui Paulo 		pos += ETH_ALEN;
4215b9c547cSRui Paulo 	} else {
422*85732ac8SCy Schubert 		sta->agreed_to_steer = 0;
4235b9c547cSRui Paulo 		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
4245b9c547cSRui Paulo 			" status_code=%u bss_termination_delay=%u",
4255b9c547cSRui Paulo 			MAC2STR(addr), status_code, bss_termination_delay);
4265b9c547cSRui Paulo 	}
4275b9c547cSRui Paulo 
4285b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
4295b9c547cSRui Paulo 		    pos, end - pos);
4305b9c547cSRui Paulo }
4315b9c547cSRui Paulo 
4325b9c547cSRui Paulo 
433780fb4a2SCy Schubert static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
434780fb4a2SCy Schubert 					       const u8 *addr, const u8 *buf,
435780fb4a2SCy Schubert 					       size_t len)
436780fb4a2SCy Schubert {
437780fb4a2SCy Schubert 	u8 dialog_token, type;
438780fb4a2SCy Schubert 
439780fb4a2SCy Schubert 	if (len < 2)
440780fb4a2SCy Schubert 		return;
441780fb4a2SCy Schubert 	dialog_token = *buf++;
442780fb4a2SCy Schubert 	type = *buf++;
443780fb4a2SCy Schubert 	len -= 2;
444780fb4a2SCy Schubert 
445780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG,
446780fb4a2SCy Schubert 		   "WNM: Received WNM Notification Request frame from "
447780fb4a2SCy Schubert 		   MACSTR " (dialog_token=%u type=%u)",
448780fb4a2SCy Schubert 		   MAC2STR(addr), dialog_token, type);
449780fb4a2SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
450780fb4a2SCy Schubert 		    buf, len);
451780fb4a2SCy Schubert 	if (type == WLAN_EID_VENDOR_SPECIFIC)
452780fb4a2SCy Schubert 		mbo_ap_wnm_notification_req(hapd, addr, buf, len);
453780fb4a2SCy Schubert }
454780fb4a2SCy Schubert 
455780fb4a2SCy Schubert 
456*85732ac8SCy Schubert static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
457*85732ac8SCy Schubert 						const u8 *addr, const u8 *buf,
458*85732ac8SCy Schubert 						size_t len)
459*85732ac8SCy Schubert {
460*85732ac8SCy Schubert 	u8 dialog_token;
461*85732ac8SCy Schubert 	char *hex;
462*85732ac8SCy Schubert 	size_t hex_len;
463*85732ac8SCy Schubert 
464*85732ac8SCy Schubert 	if (!hapd->conf->coloc_intf_reporting) {
465*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
466*85732ac8SCy Schubert 			   "WNM: Ignore unexpected Collocated Interference Report from "
467*85732ac8SCy Schubert 			   MACSTR, MAC2STR(addr));
468*85732ac8SCy Schubert 		return;
469*85732ac8SCy Schubert 	}
470*85732ac8SCy Schubert 
471*85732ac8SCy Schubert 	if (len < 1) {
472*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
473*85732ac8SCy Schubert 			   "WNM: Ignore too short Collocated Interference Report from "
474*85732ac8SCy Schubert 			   MACSTR, MAC2STR(addr));
475*85732ac8SCy Schubert 		return;
476*85732ac8SCy Schubert 	}
477*85732ac8SCy Schubert 	dialog_token = *buf++;
478*85732ac8SCy Schubert 	len--;
479*85732ac8SCy Schubert 
480*85732ac8SCy Schubert 	wpa_printf(MSG_DEBUG,
481*85732ac8SCy Schubert 		   "WNM: Received Collocated Interference Report frame from "
482*85732ac8SCy Schubert 		   MACSTR " (dialog_token=%u)",
483*85732ac8SCy Schubert 		   MAC2STR(addr), dialog_token);
484*85732ac8SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
485*85732ac8SCy Schubert 		    buf, len);
486*85732ac8SCy Schubert 
487*85732ac8SCy Schubert 	hex_len = 2 * len + 1;
488*85732ac8SCy Schubert 	hex = os_malloc(hex_len);
489*85732ac8SCy Schubert 	if (!hex)
490*85732ac8SCy Schubert 		return;
491*85732ac8SCy Schubert 	wpa_snprintf_hex(hex, hex_len, buf, len);
492*85732ac8SCy Schubert 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
493*85732ac8SCy Schubert 		     MAC2STR(addr), dialog_token, hex);
494*85732ac8SCy Schubert 	os_free(hex);
495*85732ac8SCy Schubert }
496*85732ac8SCy Schubert 
497*85732ac8SCy Schubert 
4985b9c547cSRui Paulo int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
4995b9c547cSRui Paulo 				const struct ieee80211_mgmt *mgmt, size_t len)
5005b9c547cSRui Paulo {
5015b9c547cSRui Paulo 	u8 action;
5025b9c547cSRui Paulo 	const u8 *payload;
5035b9c547cSRui Paulo 	size_t plen;
5045b9c547cSRui Paulo 
5055b9c547cSRui Paulo 	if (len < IEEE80211_HDRLEN + 2)
506f05cddf9SRui Paulo 		return -1;
507f05cddf9SRui Paulo 
5085b9c547cSRui Paulo 	payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
5095b9c547cSRui Paulo 	action = *payload++;
5105b9c547cSRui Paulo 	plen = len - IEEE80211_HDRLEN - 2;
5115b9c547cSRui Paulo 
5125b9c547cSRui Paulo 	switch (action) {
513f05cddf9SRui Paulo 	case WNM_BSS_TRANS_MGMT_QUERY:
5145b9c547cSRui Paulo 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
5155b9c547cSRui Paulo 						   plen);
5165b9c547cSRui Paulo 		return 0;
517f05cddf9SRui Paulo 	case WNM_BSS_TRANS_MGMT_RESP:
5185b9c547cSRui Paulo 		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
5195b9c547cSRui Paulo 						  plen);
5205b9c547cSRui Paulo 		return 0;
521f05cddf9SRui Paulo 	case WNM_SLEEP_MODE_REQ:
5225b9c547cSRui Paulo 		ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
523f05cddf9SRui Paulo 		return 0;
524780fb4a2SCy Schubert 	case WNM_NOTIFICATION_REQ:
525780fb4a2SCy Schubert 		ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
526780fb4a2SCy Schubert 						   plen);
527780fb4a2SCy Schubert 		return 0;
528*85732ac8SCy Schubert 	case WNM_COLLOCATED_INTERFERENCE_REPORT:
529*85732ac8SCy Schubert 		ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
530*85732ac8SCy Schubert 						    plen);
531*85732ac8SCy Schubert 		return 0;
532f05cddf9SRui Paulo 	}
533f05cddf9SRui Paulo 
534f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
5355b9c547cSRui Paulo 		   action, MAC2STR(mgmt->sa));
536f05cddf9SRui Paulo 	return -1;
537f05cddf9SRui Paulo }
5385b9c547cSRui Paulo 
5395b9c547cSRui Paulo 
5405b9c547cSRui Paulo int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
5415b9c547cSRui Paulo 			       struct sta_info *sta, int disassoc_timer)
5425b9c547cSRui Paulo {
5435b9c547cSRui Paulo 	u8 buf[1000], *pos;
5445b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
5455b9c547cSRui Paulo 
5465b9c547cSRui Paulo 	os_memset(buf, 0, sizeof(buf));
5475b9c547cSRui Paulo 	mgmt = (struct ieee80211_mgmt *) buf;
5485b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
5495b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
5505b9c547cSRui Paulo 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
5515b9c547cSRui Paulo 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
5525b9c547cSRui Paulo 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
5535b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
5545b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
5555b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
5565b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode =
5575b9c547cSRui Paulo 		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
5585b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer =
5595b9c547cSRui Paulo 		host_to_le16(disassoc_timer);
5605b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
5615b9c547cSRui Paulo 
5625b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
5635b9c547cSRui Paulo 
5645b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
5655b9c547cSRui Paulo 		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
5665b9c547cSRui Paulo 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
5675b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
5685b9c547cSRui Paulo 			   "Management Request frame");
5695b9c547cSRui Paulo 		return -1;
5705b9c547cSRui Paulo 	}
5715b9c547cSRui Paulo 
5725b9c547cSRui Paulo 	return 0;
5735b9c547cSRui Paulo }
5745b9c547cSRui Paulo 
5755b9c547cSRui Paulo 
5765b9c547cSRui Paulo static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
5775b9c547cSRui Paulo 			       int disassoc_timer)
5785b9c547cSRui Paulo {
5795b9c547cSRui Paulo 	int timeout, beacon_int;
5805b9c547cSRui Paulo 
5815b9c547cSRui Paulo 	/*
5825b9c547cSRui Paulo 	 * Prevent STA from reconnecting using cached PMKSA to force
5835b9c547cSRui Paulo 	 * full authentication with the authentication server (which may
5845b9c547cSRui Paulo 	 * decide to reject the connection),
5855b9c547cSRui Paulo 	 */
5865b9c547cSRui Paulo 	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
5875b9c547cSRui Paulo 
5885b9c547cSRui Paulo 	beacon_int = hapd->iconf->beacon_int;
5895b9c547cSRui Paulo 	if (beacon_int < 1)
5905b9c547cSRui Paulo 		beacon_int = 100; /* best guess */
5915b9c547cSRui Paulo 	/* Calculate timeout in ms based on beacon_int in TU */
5925b9c547cSRui Paulo 	timeout = disassoc_timer * beacon_int * 128 / 125;
5935b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
5945b9c547cSRui Paulo 		   " set to %d ms", MAC2STR(sta->addr), timeout);
5955b9c547cSRui Paulo 
5965b9c547cSRui Paulo 	sta->timeout_next = STA_DISASSOC_FROM_CLI;
5975b9c547cSRui Paulo 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
5985b9c547cSRui Paulo 	eloop_register_timeout(timeout / 1000,
5995b9c547cSRui Paulo 			       timeout % 1000 * 1000,
6005b9c547cSRui Paulo 			       ap_handle_timer, hapd, sta);
6015b9c547cSRui Paulo }
6025b9c547cSRui Paulo 
6035b9c547cSRui Paulo 
6045b9c547cSRui Paulo int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
6055b9c547cSRui Paulo 				   struct sta_info *sta, const char *url,
6065b9c547cSRui Paulo 				   int disassoc_timer)
6075b9c547cSRui Paulo {
6085b9c547cSRui Paulo 	u8 buf[1000], *pos;
6095b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
6105b9c547cSRui Paulo 	size_t url_len;
6115b9c547cSRui Paulo 
6125b9c547cSRui Paulo 	os_memset(buf, 0, sizeof(buf));
6135b9c547cSRui Paulo 	mgmt = (struct ieee80211_mgmt *) buf;
6145b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
6155b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
6165b9c547cSRui Paulo 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
6175b9c547cSRui Paulo 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
6185b9c547cSRui Paulo 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
6195b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
6205b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
6215b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
6225b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode =
6235b9c547cSRui Paulo 		WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
6245b9c547cSRui Paulo 		WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
6255b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer =
6265b9c547cSRui Paulo 		host_to_le16(disassoc_timer);
6275b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
6285b9c547cSRui Paulo 
6295b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
6305b9c547cSRui Paulo 
6315b9c547cSRui Paulo 	/* Session Information URL */
6325b9c547cSRui Paulo 	url_len = os_strlen(url);
6335b9c547cSRui Paulo 	if (url_len > 255)
6345b9c547cSRui Paulo 		return -1;
6355b9c547cSRui Paulo 	*pos++ = url_len;
6365b9c547cSRui Paulo 	os_memcpy(pos, url, url_len);
6375b9c547cSRui Paulo 	pos += url_len;
6385b9c547cSRui Paulo 
6395b9c547cSRui Paulo 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
6405b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
6415b9c547cSRui Paulo 			   "Management Request frame");
6425b9c547cSRui Paulo 		return -1;
6435b9c547cSRui Paulo 	}
6445b9c547cSRui Paulo 
6455b9c547cSRui Paulo 	if (disassoc_timer) {
6465b9c547cSRui Paulo 		/* send disassociation frame after time-out */
6475b9c547cSRui Paulo 		set_disassoc_timer(hapd, sta, disassoc_timer);
6485b9c547cSRui Paulo 	}
6495b9c547cSRui Paulo 
6505b9c547cSRui Paulo 	return 0;
6515b9c547cSRui Paulo }
6525b9c547cSRui Paulo 
6535b9c547cSRui Paulo 
6545b9c547cSRui Paulo int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
6555b9c547cSRui Paulo 			u8 req_mode, int disassoc_timer, u8 valid_int,
6565b9c547cSRui Paulo 			const u8 *bss_term_dur, const char *url,
657780fb4a2SCy Schubert 			const u8 *nei_rep, size_t nei_rep_len,
658780fb4a2SCy Schubert 			const u8 *mbo_attrs, size_t mbo_len)
6595b9c547cSRui Paulo {
6605b9c547cSRui Paulo 	u8 *buf, *pos;
6615b9c547cSRui Paulo 	struct ieee80211_mgmt *mgmt;
6625b9c547cSRui Paulo 	size_t url_len;
6635b9c547cSRui Paulo 
6645b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
6655b9c547cSRui Paulo 		   MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
6665b9c547cSRui Paulo 		   MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
667780fb4a2SCy Schubert 	buf = os_zalloc(1000 + nei_rep_len + mbo_len);
6685b9c547cSRui Paulo 	if (buf == NULL)
6695b9c547cSRui Paulo 		return -1;
6705b9c547cSRui Paulo 	mgmt = (struct ieee80211_mgmt *) buf;
6715b9c547cSRui Paulo 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
6725b9c547cSRui Paulo 					   WLAN_FC_STYPE_ACTION);
6735b9c547cSRui Paulo 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
6745b9c547cSRui Paulo 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
6755b9c547cSRui Paulo 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
6765b9c547cSRui Paulo 	mgmt->u.action.category = WLAN_ACTION_WNM;
6775b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
6785b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
6795b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
6805b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.disassoc_timer =
6815b9c547cSRui Paulo 		host_to_le16(disassoc_timer);
6825b9c547cSRui Paulo 	mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
6835b9c547cSRui Paulo 
6845b9c547cSRui Paulo 	pos = mgmt->u.action.u.bss_tm_req.variable;
6855b9c547cSRui Paulo 
6865b9c547cSRui Paulo 	if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
6875b9c547cSRui Paulo 	    bss_term_dur) {
6885b9c547cSRui Paulo 		os_memcpy(pos, bss_term_dur, 12);
6895b9c547cSRui Paulo 		pos += 12;
6905b9c547cSRui Paulo 	}
6915b9c547cSRui Paulo 
6925b9c547cSRui Paulo 	if (url) {
6935b9c547cSRui Paulo 		/* Session Information URL */
6945b9c547cSRui Paulo 		url_len = os_strlen(url);
6955b9c547cSRui Paulo 		if (url_len > 255) {
6965b9c547cSRui Paulo 			os_free(buf);
6975b9c547cSRui Paulo 			return -1;
6985b9c547cSRui Paulo 		}
6995b9c547cSRui Paulo 
7005b9c547cSRui Paulo 		*pos++ = url_len;
7015b9c547cSRui Paulo 		os_memcpy(pos, url, url_len);
7025b9c547cSRui Paulo 		pos += url_len;
7035b9c547cSRui Paulo 	}
7045b9c547cSRui Paulo 
7055b9c547cSRui Paulo 	if (nei_rep) {
7065b9c547cSRui Paulo 		os_memcpy(pos, nei_rep, nei_rep_len);
7075b9c547cSRui Paulo 		pos += nei_rep_len;
7085b9c547cSRui Paulo 	}
7095b9c547cSRui Paulo 
710780fb4a2SCy Schubert 	if (mbo_len > 0) {
711780fb4a2SCy Schubert 		pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
712780fb4a2SCy Schubert 				  mbo_len);
713780fb4a2SCy Schubert 	}
714780fb4a2SCy Schubert 
7155b9c547cSRui Paulo 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
7165b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
7175b9c547cSRui Paulo 			   "Failed to send BSS Transition Management Request frame");
7185b9c547cSRui Paulo 		os_free(buf);
7195b9c547cSRui Paulo 		return -1;
7205b9c547cSRui Paulo 	}
7215b9c547cSRui Paulo 	os_free(buf);
7225b9c547cSRui Paulo 
7235b9c547cSRui Paulo 	if (disassoc_timer) {
7245b9c547cSRui Paulo 		/* send disassociation frame after time-out */
7255b9c547cSRui Paulo 		set_disassoc_timer(hapd, sta, disassoc_timer);
7265b9c547cSRui Paulo 	}
7275b9c547cSRui Paulo 
7285b9c547cSRui Paulo 	return 0;
7295b9c547cSRui Paulo }
730*85732ac8SCy Schubert 
731*85732ac8SCy Schubert 
732*85732ac8SCy Schubert int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
733*85732ac8SCy Schubert 			    unsigned int auto_report, unsigned int timeout)
734*85732ac8SCy Schubert {
735*85732ac8SCy Schubert 	u8 buf[100], *pos;
736*85732ac8SCy Schubert 	struct ieee80211_mgmt *mgmt;
737*85732ac8SCy Schubert 	u8 dialog_token = 1;
738*85732ac8SCy Schubert 
739*85732ac8SCy Schubert 	if (auto_report > 3 || timeout > 63)
740*85732ac8SCy Schubert 		return -1;
741*85732ac8SCy Schubert 	os_memset(buf, 0, sizeof(buf));
742*85732ac8SCy Schubert 	mgmt = (struct ieee80211_mgmt *) buf;
743*85732ac8SCy Schubert 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
744*85732ac8SCy Schubert 					   WLAN_FC_STYPE_ACTION);
745*85732ac8SCy Schubert 	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
746*85732ac8SCy Schubert 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
747*85732ac8SCy Schubert 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
748*85732ac8SCy Schubert 	mgmt->u.action.category = WLAN_ACTION_WNM;
749*85732ac8SCy Schubert 	mgmt->u.action.u.coloc_intf_req.action =
750*85732ac8SCy Schubert 		WNM_COLLOCATED_INTERFERENCE_REQ;
751*85732ac8SCy Schubert 	mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
752*85732ac8SCy Schubert 	mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
753*85732ac8SCy Schubert 	pos = &mgmt->u.action.u.coloc_intf_req.req_info;
754*85732ac8SCy Schubert 	pos++;
755*85732ac8SCy Schubert 
756*85732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
757*85732ac8SCy Schubert 		   MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
758*85732ac8SCy Schubert 		   MAC2STR(sta->addr), dialog_token, auto_report, timeout);
759*85732ac8SCy Schubert 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
760*85732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
761*85732ac8SCy Schubert 			   "WNM: Failed to send Collocated Interference Request frame");
762*85732ac8SCy Schubert 		return -1;
763*85732ac8SCy Schubert 	}
764*85732ac8SCy Schubert 
765*85732ac8SCy Schubert 	return 0;
766*85732ac8SCy Schubert }
767