1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * hostapd - WNM
3e5b75505Sopenharmony_ci * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci */
8e5b75505Sopenharmony_ci
9e5b75505Sopenharmony_ci#include "utils/includes.h"
10e5b75505Sopenharmony_ci
11e5b75505Sopenharmony_ci#include "utils/common.h"
12e5b75505Sopenharmony_ci#include "utils/eloop.h"
13e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h"
14e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h"
15e5b75505Sopenharmony_ci#include "common/ocv.h"
16e5b75505Sopenharmony_ci#include "ap/hostapd.h"
17e5b75505Sopenharmony_ci#include "ap/sta_info.h"
18e5b75505Sopenharmony_ci#include "ap/ap_config.h"
19e5b75505Sopenharmony_ci#include "ap/ap_drv_ops.h"
20e5b75505Sopenharmony_ci#include "ap/wpa_auth.h"
21e5b75505Sopenharmony_ci#include "mbo_ap.h"
22e5b75505Sopenharmony_ci#include "wnm_ap.h"
23e5b75505Sopenharmony_ci
24e5b75505Sopenharmony_ci#define MAX_TFS_IE_LEN  1024
25e5b75505Sopenharmony_ci
26e5b75505Sopenharmony_ci
27e5b75505Sopenharmony_ci/* get the TFS IE from driver */
28e5b75505Sopenharmony_cistatic int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
29e5b75505Sopenharmony_ci				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
30e5b75505Sopenharmony_ci{
31e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
32e5b75505Sopenharmony_ci
33e5b75505Sopenharmony_ci	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
34e5b75505Sopenharmony_ci}
35e5b75505Sopenharmony_ci
36e5b75505Sopenharmony_ci
37e5b75505Sopenharmony_ci/* set the TFS IE to driver */
38e5b75505Sopenharmony_cistatic int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
39e5b75505Sopenharmony_ci				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
40e5b75505Sopenharmony_ci{
41e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
42e5b75505Sopenharmony_ci
43e5b75505Sopenharmony_ci	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
44e5b75505Sopenharmony_ci}
45e5b75505Sopenharmony_ci
46e5b75505Sopenharmony_ci
47e5b75505Sopenharmony_ci/* MLME-SLEEPMODE.response */
48e5b75505Sopenharmony_cistatic int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
49e5b75505Sopenharmony_ci					 const u8 *addr, u8 dialog_token,
50e5b75505Sopenharmony_ci					 u8 action_type, u16 intval)
51e5b75505Sopenharmony_ci{
52e5b75505Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
53e5b75505Sopenharmony_ci	int res;
54e5b75505Sopenharmony_ci	size_t len;
55e5b75505Sopenharmony_ci	size_t gtk_elem_len = 0;
56e5b75505Sopenharmony_ci	size_t igtk_elem_len = 0;
57e5b75505Sopenharmony_ci	struct wnm_sleep_element wnmsleep_ie;
58e5b75505Sopenharmony_ci	u8 *wnmtfs_ie, *oci_ie;
59e5b75505Sopenharmony_ci	u8 wnmsleep_ie_len, oci_ie_len;
60e5b75505Sopenharmony_ci	u16 wnmtfs_ie_len;
61e5b75505Sopenharmony_ci	u8 *pos;
62e5b75505Sopenharmony_ci	struct sta_info *sta;
63e5b75505Sopenharmony_ci	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
64e5b75505Sopenharmony_ci		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
65e5b75505Sopenharmony_ci
66e5b75505Sopenharmony_ci	sta = ap_get_sta(hapd, addr);
67e5b75505Sopenharmony_ci	if (sta == NULL) {
68e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
69e5b75505Sopenharmony_ci		return -EINVAL;
70e5b75505Sopenharmony_ci	}
71e5b75505Sopenharmony_ci
72e5b75505Sopenharmony_ci	/* WNM-Sleep Mode IE */
73e5b75505Sopenharmony_ci	os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
74e5b75505Sopenharmony_ci	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
75e5b75505Sopenharmony_ci	wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
76e5b75505Sopenharmony_ci	wnmsleep_ie.len = wnmsleep_ie_len - 2;
77e5b75505Sopenharmony_ci	wnmsleep_ie.action_type = action_type;
78e5b75505Sopenharmony_ci	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
79e5b75505Sopenharmony_ci	wnmsleep_ie.intval = host_to_le16(intval);
80e5b75505Sopenharmony_ci
81e5b75505Sopenharmony_ci	/* TFS IE(s) */
82e5b75505Sopenharmony_ci	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
83e5b75505Sopenharmony_ci	if (wnmtfs_ie == NULL)
84e5b75505Sopenharmony_ci		return -1;
85e5b75505Sopenharmony_ci	if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
86e5b75505Sopenharmony_ci				    tfs_oper)) {
87e5b75505Sopenharmony_ci		wnmtfs_ie_len = 0;
88e5b75505Sopenharmony_ci		os_free(wnmtfs_ie);
89e5b75505Sopenharmony_ci		wnmtfs_ie = NULL;
90e5b75505Sopenharmony_ci	}
91e5b75505Sopenharmony_ci
92e5b75505Sopenharmony_ci	oci_ie = NULL;
93e5b75505Sopenharmony_ci	oci_ie_len = 0;
94e5b75505Sopenharmony_ci#ifdef CONFIG_OCV
95e5b75505Sopenharmony_ci	if (action_type == WNM_SLEEP_MODE_EXIT &&
96e5b75505Sopenharmony_ci	    wpa_auth_uses_ocv(sta->wpa_sm)) {
97e5b75505Sopenharmony_ci		struct wpa_channel_info ci;
98e5b75505Sopenharmony_ci
99e5b75505Sopenharmony_ci		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
100e5b75505Sopenharmony_ci			wpa_printf(MSG_WARNING,
101e5b75505Sopenharmony_ci				   "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
102e5b75505Sopenharmony_ci			os_free(wnmtfs_ie);
103e5b75505Sopenharmony_ci			return -1;
104e5b75505Sopenharmony_ci		}
105e5b75505Sopenharmony_ci
106e5b75505Sopenharmony_ci		oci_ie_len = OCV_OCI_EXTENDED_LEN;
107e5b75505Sopenharmony_ci		oci_ie = os_zalloc(oci_ie_len);
108e5b75505Sopenharmony_ci		if (!oci_ie) {
109e5b75505Sopenharmony_ci			wpa_printf(MSG_WARNING,
110e5b75505Sopenharmony_ci				   "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
111e5b75505Sopenharmony_ci			os_free(wnmtfs_ie);
112e5b75505Sopenharmony_ci			return -1;
113e5b75505Sopenharmony_ci		}
114e5b75505Sopenharmony_ci
115e5b75505Sopenharmony_ci		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
116e5b75505Sopenharmony_ci			os_free(wnmtfs_ie);
117e5b75505Sopenharmony_ci			os_free(oci_ie);
118e5b75505Sopenharmony_ci			return -1;
119e5b75505Sopenharmony_ci		}
120e5b75505Sopenharmony_ci	}
121e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */
122e5b75505Sopenharmony_ci
123e5b75505Sopenharmony_ci#define MAX_GTK_SUBELEM_LEN 45
124e5b75505Sopenharmony_ci#define MAX_IGTK_SUBELEM_LEN 26
125e5b75505Sopenharmony_ci	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
126e5b75505Sopenharmony_ci			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
127e5b75505Sopenharmony_ci			 oci_ie_len);
128e5b75505Sopenharmony_ci	if (mgmt == NULL) {
129e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
130e5b75505Sopenharmony_ci			   "WNM-Sleep Response action frame");
131e5b75505Sopenharmony_ci		res = -1;
132e5b75505Sopenharmony_ci		goto fail;
133e5b75505Sopenharmony_ci	}
134e5b75505Sopenharmony_ci	os_memcpy(mgmt->da, addr, ETH_ALEN);
135e5b75505Sopenharmony_ci	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
136e5b75505Sopenharmony_ci	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
137e5b75505Sopenharmony_ci	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
138e5b75505Sopenharmony_ci					   WLAN_FC_STYPE_ACTION);
139e5b75505Sopenharmony_ci	mgmt->u.action.category = WLAN_ACTION_WNM;
140e5b75505Sopenharmony_ci	mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
141e5b75505Sopenharmony_ci	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
142e5b75505Sopenharmony_ci	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
143e5b75505Sopenharmony_ci	/* add key data if MFP is enabled */
144e5b75505Sopenharmony_ci	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
145e5b75505Sopenharmony_ci	    hapd->conf->wnm_sleep_mode_no_keys ||
146e5b75505Sopenharmony_ci	    action_type != WNM_SLEEP_MODE_EXIT) {
147e5b75505Sopenharmony_ci		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
148e5b75505Sopenharmony_ci	} else {
149e5b75505Sopenharmony_ci		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
150e5b75505Sopenharmony_ci		pos += gtk_elem_len;
151e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
152e5b75505Sopenharmony_ci			   (int) gtk_elem_len);
153e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211W
154e5b75505Sopenharmony_ci		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
155e5b75505Sopenharmony_ci		if (res < 0)
156e5b75505Sopenharmony_ci			goto fail;
157e5b75505Sopenharmony_ci		igtk_elem_len = res;
158e5b75505Sopenharmony_ci		pos += igtk_elem_len;
159e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
160e5b75505Sopenharmony_ci			   (int) igtk_elem_len);
161e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211W */
162e5b75505Sopenharmony_ci
163e5b75505Sopenharmony_ci		WPA_PUT_LE16((u8 *)
164e5b75505Sopenharmony_ci			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
165e5b75505Sopenharmony_ci			     gtk_elem_len + igtk_elem_len);
166e5b75505Sopenharmony_ci	}
167e5b75505Sopenharmony_ci	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
168e5b75505Sopenharmony_ci	/* copy TFS IE here */
169e5b75505Sopenharmony_ci	pos += wnmsleep_ie_len;
170e5b75505Sopenharmony_ci	if (wnmtfs_ie) {
171e5b75505Sopenharmony_ci		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
172e5b75505Sopenharmony_ci		pos += wnmtfs_ie_len;
173e5b75505Sopenharmony_ci	}
174e5b75505Sopenharmony_ci#ifdef CONFIG_OCV
175e5b75505Sopenharmony_ci	/* copy OCV OCI here */
176e5b75505Sopenharmony_ci	if (oci_ie_len > 0)
177e5b75505Sopenharmony_ci		os_memcpy(pos, oci_ie, oci_ie_len);
178e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */
179e5b75505Sopenharmony_ci
180e5b75505Sopenharmony_ci	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
181e5b75505Sopenharmony_ci		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
182e5b75505Sopenharmony_ci
183e5b75505Sopenharmony_ci	/* In driver, response frame should be forced to sent when STA is in
184e5b75505Sopenharmony_ci	 * PS mode */
185e5b75505Sopenharmony_ci	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
186e5b75505Sopenharmony_ci				      mgmt->da, &mgmt->u.action.category, len);
187e5b75505Sopenharmony_ci
188e5b75505Sopenharmony_ci	if (!res) {
189e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
190e5b75505Sopenharmony_ci			   "frame");
191e5b75505Sopenharmony_ci
192e5b75505Sopenharmony_ci		/* when entering wnmsleep
193e5b75505Sopenharmony_ci		 * 1. pause the node in driver
194e5b75505Sopenharmony_ci		 * 2. mark the node so that AP won't update GTK/IGTK during
195e5b75505Sopenharmony_ci		 * WNM Sleep
196e5b75505Sopenharmony_ci		 */
197e5b75505Sopenharmony_ci		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
198e5b75505Sopenharmony_ci		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
199e5b75505Sopenharmony_ci			sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
200e5b75505Sopenharmony_ci			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
201e5b75505Sopenharmony_ci					     addr, NULL, NULL);
202e5b75505Sopenharmony_ci			wpa_set_wnmsleep(sta->wpa_sm, 1);
203e5b75505Sopenharmony_ci		}
204e5b75505Sopenharmony_ci		/* when exiting wnmsleep
205e5b75505Sopenharmony_ci		 * 1. unmark the node
206e5b75505Sopenharmony_ci		 * 2. start GTK/IGTK update if MFP is not used
207e5b75505Sopenharmony_ci		 * 3. unpause the node in driver
208e5b75505Sopenharmony_ci		 */
209e5b75505Sopenharmony_ci		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
210e5b75505Sopenharmony_ci		     wnmsleep_ie.status ==
211e5b75505Sopenharmony_ci		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
212e5b75505Sopenharmony_ci		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
213e5b75505Sopenharmony_ci			sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
214e5b75505Sopenharmony_ci			wpa_set_wnmsleep(sta->wpa_sm, 0);
215e5b75505Sopenharmony_ci			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
216e5b75505Sopenharmony_ci					     addr, NULL, NULL);
217e5b75505Sopenharmony_ci			if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
218e5b75505Sopenharmony_ci			    hapd->conf->wnm_sleep_mode_no_keys)
219e5b75505Sopenharmony_ci				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
220e5b75505Sopenharmony_ci		}
221e5b75505Sopenharmony_ci	} else
222e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
223e5b75505Sopenharmony_ci
224e5b75505Sopenharmony_ci#undef MAX_GTK_SUBELEM_LEN
225e5b75505Sopenharmony_ci#undef MAX_IGTK_SUBELEM_LEN
226e5b75505Sopenharmony_cifail:
227e5b75505Sopenharmony_ci	os_free(wnmtfs_ie);
228e5b75505Sopenharmony_ci	os_free(oci_ie);
229e5b75505Sopenharmony_ci	os_free(mgmt);
230e5b75505Sopenharmony_ci	return res;
231e5b75505Sopenharmony_ci}
232e5b75505Sopenharmony_ci
233e5b75505Sopenharmony_ci
234e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
235e5b75505Sopenharmony_ci				       const u8 *addr, const u8 *frm, int len)
236e5b75505Sopenharmony_ci{
237e5b75505Sopenharmony_ci	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
238e5b75505Sopenharmony_ci	const u8 *pos = frm;
239e5b75505Sopenharmony_ci	u8 dialog_token;
240e5b75505Sopenharmony_ci	struct wnm_sleep_element *wnmsleep_ie = NULL;
241e5b75505Sopenharmony_ci	/* multiple TFS Req IE (assuming consecutive) */
242e5b75505Sopenharmony_ci	u8 *tfsreq_ie_start = NULL;
243e5b75505Sopenharmony_ci	u8 *tfsreq_ie_end = NULL;
244e5b75505Sopenharmony_ci	u16 tfsreq_ie_len = 0;
245e5b75505Sopenharmony_ci#ifdef CONFIG_OCV
246e5b75505Sopenharmony_ci	struct sta_info *sta;
247e5b75505Sopenharmony_ci	const u8 *oci_ie = NULL;
248e5b75505Sopenharmony_ci	u8 oci_ie_len = 0;
249e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */
250e5b75505Sopenharmony_ci
251e5b75505Sopenharmony_ci	if (!hapd->conf->wnm_sleep_mode) {
252e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
253e5b75505Sopenharmony_ci			   MACSTR " since WNM-Sleep Mode is disabled",
254e5b75505Sopenharmony_ci			   MAC2STR(addr));
255e5b75505Sopenharmony_ci		return;
256e5b75505Sopenharmony_ci	}
257e5b75505Sopenharmony_ci
258e5b75505Sopenharmony_ci	if (len < 1) {
259e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
260e5b75505Sopenharmony_ci			   "WNM: Ignore too short WNM-Sleep Mode Request from "
261e5b75505Sopenharmony_ci			   MACSTR, MAC2STR(addr));
262e5b75505Sopenharmony_ci		return;
263e5b75505Sopenharmony_ci	}
264e5b75505Sopenharmony_ci
265e5b75505Sopenharmony_ci	dialog_token = *pos++;
266e5b75505Sopenharmony_ci	while (pos + 1 < frm + len) {
267e5b75505Sopenharmony_ci		u8 ie_len = pos[1];
268e5b75505Sopenharmony_ci		if (pos + 2 + ie_len > frm + len)
269e5b75505Sopenharmony_ci			break;
270e5b75505Sopenharmony_ci		if (*pos == WLAN_EID_WNMSLEEP &&
271e5b75505Sopenharmony_ci		    ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
272e5b75505Sopenharmony_ci			wnmsleep_ie = (struct wnm_sleep_element *) pos;
273e5b75505Sopenharmony_ci		else if (*pos == WLAN_EID_TFS_REQ) {
274e5b75505Sopenharmony_ci			if (!tfsreq_ie_start)
275e5b75505Sopenharmony_ci				tfsreq_ie_start = (u8 *) pos;
276e5b75505Sopenharmony_ci			tfsreq_ie_end = (u8 *) pos;
277e5b75505Sopenharmony_ci#ifdef CONFIG_OCV
278e5b75505Sopenharmony_ci		} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
279e5b75505Sopenharmony_ci			   pos[2] == WLAN_EID_EXT_OCV_OCI) {
280e5b75505Sopenharmony_ci			oci_ie = pos + 3;
281e5b75505Sopenharmony_ci			oci_ie_len = ie_len - 1;
282e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */
283e5b75505Sopenharmony_ci		} else
284e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
285e5b75505Sopenharmony_ci				   *pos);
286e5b75505Sopenharmony_ci		pos += ie_len + 2;
287e5b75505Sopenharmony_ci	}
288e5b75505Sopenharmony_ci
289e5b75505Sopenharmony_ci	if (!wnmsleep_ie) {
290e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
291e5b75505Sopenharmony_ci		return;
292e5b75505Sopenharmony_ci	}
293e5b75505Sopenharmony_ci
294e5b75505Sopenharmony_ci#ifdef CONFIG_OCV
295e5b75505Sopenharmony_ci	sta = ap_get_sta(hapd, addr);
296e5b75505Sopenharmony_ci	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
297e5b75505Sopenharmony_ci	    sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
298e5b75505Sopenharmony_ci		struct wpa_channel_info ci;
299e5b75505Sopenharmony_ci
300e5b75505Sopenharmony_ci		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
301e5b75505Sopenharmony_ci			wpa_printf(MSG_WARNING,
302e5b75505Sopenharmony_ci				   "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
303e5b75505Sopenharmony_ci			return;
304e5b75505Sopenharmony_ci		}
305e5b75505Sopenharmony_ci
306e5b75505Sopenharmony_ci		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
307e5b75505Sopenharmony_ci					 channel_width_to_int(ci.chanwidth),
308e5b75505Sopenharmony_ci					 ci.seg1_idx) != 0) {
309e5b75505Sopenharmony_ci			wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
310e5b75505Sopenharmony_ci			return;
311e5b75505Sopenharmony_ci		}
312e5b75505Sopenharmony_ci	}
313e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */
314e5b75505Sopenharmony_ci
315e5b75505Sopenharmony_ci	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
316e5b75505Sopenharmony_ci	    tfsreq_ie_start && tfsreq_ie_end &&
317e5b75505Sopenharmony_ci	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
318e5b75505Sopenharmony_ci		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
319e5b75505Sopenharmony_ci			tfsreq_ie_start;
320e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
321e5b75505Sopenharmony_ci		/* pass the TFS Req IE(s) to driver for processing */
322e5b75505Sopenharmony_ci		if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
323e5b75505Sopenharmony_ci					    &tfsreq_ie_len,
324e5b75505Sopenharmony_ci					    WNM_SLEEP_TFS_REQ_IE_SET))
325e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
326e5b75505Sopenharmony_ci	}
327e5b75505Sopenharmony_ci
328e5b75505Sopenharmony_ci	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
329e5b75505Sopenharmony_ci				      wnmsleep_ie->action_type,
330e5b75505Sopenharmony_ci				      le_to_host16(wnmsleep_ie->intval));
331e5b75505Sopenharmony_ci
332e5b75505Sopenharmony_ci	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
333e5b75505Sopenharmony_ci		/* clear the tfs after sending the resp frame */
334e5b75505Sopenharmony_ci		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
335e5b75505Sopenharmony_ci					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
336e5b75505Sopenharmony_ci	}
337e5b75505Sopenharmony_ci}
338e5b75505Sopenharmony_ci
339e5b75505Sopenharmony_ci
340e5b75505Sopenharmony_cistatic int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
341e5b75505Sopenharmony_ci						  const u8 *addr,
342e5b75505Sopenharmony_ci						  u8 dialog_token)
343e5b75505Sopenharmony_ci{
344e5b75505Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
345e5b75505Sopenharmony_ci	size_t len;
346e5b75505Sopenharmony_ci	u8 *pos;
347e5b75505Sopenharmony_ci	int res;
348e5b75505Sopenharmony_ci
349e5b75505Sopenharmony_ci	mgmt = os_zalloc(sizeof(*mgmt));
350e5b75505Sopenharmony_ci	if (mgmt == NULL)
351e5b75505Sopenharmony_ci		return -1;
352e5b75505Sopenharmony_ci	os_memcpy(mgmt->da, addr, ETH_ALEN);
353e5b75505Sopenharmony_ci	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
354e5b75505Sopenharmony_ci	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
355e5b75505Sopenharmony_ci	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
356e5b75505Sopenharmony_ci					   WLAN_FC_STYPE_ACTION);
357e5b75505Sopenharmony_ci	mgmt->u.action.category = WLAN_ACTION_WNM;
358e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
359e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
360e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.req_mode = 0;
361e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
362e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
363e5b75505Sopenharmony_ci	pos = mgmt->u.action.u.bss_tm_req.variable;
364e5b75505Sopenharmony_ci
365e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
366e5b75505Sopenharmony_ci		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
367e5b75505Sopenharmony_ci		   "validity_interval=%u",
368e5b75505Sopenharmony_ci		   MAC2STR(addr), dialog_token,
369e5b75505Sopenharmony_ci		   mgmt->u.action.u.bss_tm_req.req_mode,
370e5b75505Sopenharmony_ci		   le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
371e5b75505Sopenharmony_ci		   mgmt->u.action.u.bss_tm_req.validity_interval);
372e5b75505Sopenharmony_ci
373e5b75505Sopenharmony_ci	len = pos - &mgmt->u.action.category;
374e5b75505Sopenharmony_ci	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
375e5b75505Sopenharmony_ci				      mgmt->da, &mgmt->u.action.category, len);
376e5b75505Sopenharmony_ci	os_free(mgmt);
377e5b75505Sopenharmony_ci	return res;
378e5b75505Sopenharmony_ci}
379e5b75505Sopenharmony_ci
380e5b75505Sopenharmony_ci
381e5b75505Sopenharmony_cistatic void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
382e5b75505Sopenharmony_ci					       const u8 *addr, const u8 *frm,
383e5b75505Sopenharmony_ci					       size_t len)
384e5b75505Sopenharmony_ci{
385e5b75505Sopenharmony_ci	u8 dialog_token, reason;
386e5b75505Sopenharmony_ci	const u8 *pos, *end;
387e5b75505Sopenharmony_ci	int enabled = hapd->conf->bss_transition;
388e5b75505Sopenharmony_ci
389e5b75505Sopenharmony_ci#ifdef CONFIG_MBO
390e5b75505Sopenharmony_ci	if (hapd->conf->mbo_enabled)
391e5b75505Sopenharmony_ci		enabled = 1;
392e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */
393e5b75505Sopenharmony_ci	if (!enabled) {
394e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
395e5b75505Sopenharmony_ci			   "Ignore BSS Transition Management Query from "
396e5b75505Sopenharmony_ci			   MACSTR
397e5b75505Sopenharmony_ci			   " since BSS Transition Management is disabled",
398e5b75505Sopenharmony_ci			   MAC2STR(addr));
399e5b75505Sopenharmony_ci		return;
400e5b75505Sopenharmony_ci	}
401e5b75505Sopenharmony_ci
402e5b75505Sopenharmony_ci	if (len < 2) {
403e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
404e5b75505Sopenharmony_ci			   MACSTR, MAC2STR(addr));
405e5b75505Sopenharmony_ci		return;
406e5b75505Sopenharmony_ci	}
407e5b75505Sopenharmony_ci
408e5b75505Sopenharmony_ci	pos = frm;
409e5b75505Sopenharmony_ci	end = pos + len;
410e5b75505Sopenharmony_ci	dialog_token = *pos++;
411e5b75505Sopenharmony_ci	reason = *pos++;
412e5b75505Sopenharmony_ci
413e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
414e5b75505Sopenharmony_ci		   MACSTR " dialog_token=%u reason=%u",
415e5b75505Sopenharmony_ci		   MAC2STR(addr), dialog_token, reason);
416e5b75505Sopenharmony_ci
417e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
418e5b75505Sopenharmony_ci		    pos, end - pos);
419e5b75505Sopenharmony_ci
420e5b75505Sopenharmony_ci	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
421e5b75505Sopenharmony_ci}
422e5b75505Sopenharmony_ci
423e5b75505Sopenharmony_ci
424e5b75505Sopenharmony_civoid ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
425e5b75505Sopenharmony_ci{
426e5b75505Sopenharmony_ci	struct hostapd_data *hapd = eloop_ctx;
427e5b75505Sopenharmony_ci	struct sta_info *sta = timeout_ctx;
428e5b75505Sopenharmony_ci
429e5b75505Sopenharmony_ci	if (sta->agreed_to_steer) {
430e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
431e5b75505Sopenharmony_ci			   hapd->conf->iface, MAC2STR(sta->addr));
432e5b75505Sopenharmony_ci		sta->agreed_to_steer = 0;
433e5b75505Sopenharmony_ci	}
434e5b75505Sopenharmony_ci}
435e5b75505Sopenharmony_ci
436e5b75505Sopenharmony_ci
437e5b75505Sopenharmony_cistatic void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
438e5b75505Sopenharmony_ci					      const u8 *addr, const u8 *frm,
439e5b75505Sopenharmony_ci					      size_t len)
440e5b75505Sopenharmony_ci{
441e5b75505Sopenharmony_ci	u8 dialog_token, status_code, bss_termination_delay;
442e5b75505Sopenharmony_ci	const u8 *pos, *end;
443e5b75505Sopenharmony_ci	int enabled = hapd->conf->bss_transition;
444e5b75505Sopenharmony_ci	struct sta_info *sta;
445e5b75505Sopenharmony_ci
446e5b75505Sopenharmony_ci#ifdef CONFIG_MBO
447e5b75505Sopenharmony_ci	if (hapd->conf->mbo_enabled)
448e5b75505Sopenharmony_ci		enabled = 1;
449e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */
450e5b75505Sopenharmony_ci	if (!enabled) {
451e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
452e5b75505Sopenharmony_ci			   "Ignore BSS Transition Management Response from "
453e5b75505Sopenharmony_ci			   MACSTR
454e5b75505Sopenharmony_ci			   " since BSS Transition Management is disabled",
455e5b75505Sopenharmony_ci			   MAC2STR(addr));
456e5b75505Sopenharmony_ci		return;
457e5b75505Sopenharmony_ci	}
458e5b75505Sopenharmony_ci
459e5b75505Sopenharmony_ci	if (len < 3) {
460e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
461e5b75505Sopenharmony_ci			   MACSTR, MAC2STR(addr));
462e5b75505Sopenharmony_ci		return;
463e5b75505Sopenharmony_ci	}
464e5b75505Sopenharmony_ci
465e5b75505Sopenharmony_ci	pos = frm;
466e5b75505Sopenharmony_ci	end = pos + len;
467e5b75505Sopenharmony_ci	dialog_token = *pos++;
468e5b75505Sopenharmony_ci	status_code = *pos++;
469e5b75505Sopenharmony_ci	bss_termination_delay = *pos++;
470e5b75505Sopenharmony_ci
471e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
472e5b75505Sopenharmony_ci		   MACSTR " dialog_token=%u status_code=%u "
473e5b75505Sopenharmony_ci		   "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
474e5b75505Sopenharmony_ci		   status_code, bss_termination_delay);
475e5b75505Sopenharmony_ci
476e5b75505Sopenharmony_ci	sta = ap_get_sta(hapd, addr);
477e5b75505Sopenharmony_ci	if (!sta) {
478e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Station " MACSTR
479e5b75505Sopenharmony_ci			   " not found for received BSS TM Response",
480e5b75505Sopenharmony_ci			   MAC2STR(addr));
481e5b75505Sopenharmony_ci		return;
482e5b75505Sopenharmony_ci	}
483e5b75505Sopenharmony_ci
484e5b75505Sopenharmony_ci	if (status_code == WNM_BSS_TM_ACCEPT) {
485e5b75505Sopenharmony_ci		if (end - pos < ETH_ALEN) {
486e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
487e5b75505Sopenharmony_ci			return;
488e5b75505Sopenharmony_ci		}
489e5b75505Sopenharmony_ci		sta->agreed_to_steer = 1;
490e5b75505Sopenharmony_ci		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
491e5b75505Sopenharmony_ci		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
492e5b75505Sopenharmony_ci				       hapd, sta);
493e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
494e5b75505Sopenharmony_ci			   MAC2STR(pos));
495e5b75505Sopenharmony_ci		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
496e5b75505Sopenharmony_ci			" status_code=%u bss_termination_delay=%u target_bssid="
497e5b75505Sopenharmony_ci			MACSTR,
498e5b75505Sopenharmony_ci			MAC2STR(addr), status_code, bss_termination_delay,
499e5b75505Sopenharmony_ci			MAC2STR(pos));
500e5b75505Sopenharmony_ci		pos += ETH_ALEN;
501e5b75505Sopenharmony_ci	} else {
502e5b75505Sopenharmony_ci		sta->agreed_to_steer = 0;
503e5b75505Sopenharmony_ci		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
504e5b75505Sopenharmony_ci			" status_code=%u bss_termination_delay=%u",
505e5b75505Sopenharmony_ci			MAC2STR(addr), status_code, bss_termination_delay);
506e5b75505Sopenharmony_ci	}
507e5b75505Sopenharmony_ci
508e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
509e5b75505Sopenharmony_ci		    pos, end - pos);
510e5b75505Sopenharmony_ci}
511e5b75505Sopenharmony_ci
512e5b75505Sopenharmony_ci
513e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
514e5b75505Sopenharmony_ci					       const u8 *addr, const u8 *buf,
515e5b75505Sopenharmony_ci					       size_t len)
516e5b75505Sopenharmony_ci{
517e5b75505Sopenharmony_ci	u8 dialog_token, type;
518e5b75505Sopenharmony_ci
519e5b75505Sopenharmony_ci	if (len < 2)
520e5b75505Sopenharmony_ci		return;
521e5b75505Sopenharmony_ci	dialog_token = *buf++;
522e5b75505Sopenharmony_ci	type = *buf++;
523e5b75505Sopenharmony_ci	len -= 2;
524e5b75505Sopenharmony_ci
525e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG,
526e5b75505Sopenharmony_ci		   "WNM: Received WNM Notification Request frame from "
527e5b75505Sopenharmony_ci		   MACSTR " (dialog_token=%u type=%u)",
528e5b75505Sopenharmony_ci		   MAC2STR(addr), dialog_token, type);
529e5b75505Sopenharmony_ci	wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
530e5b75505Sopenharmony_ci		    buf, len);
531e5b75505Sopenharmony_ci	if (type == WLAN_EID_VENDOR_SPECIFIC)
532e5b75505Sopenharmony_ci		mbo_ap_wnm_notification_req(hapd, addr, buf, len);
533e5b75505Sopenharmony_ci}
534e5b75505Sopenharmony_ci
535e5b75505Sopenharmony_ci
536e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
537e5b75505Sopenharmony_ci						const u8 *addr, const u8 *buf,
538e5b75505Sopenharmony_ci						size_t len)
539e5b75505Sopenharmony_ci{
540e5b75505Sopenharmony_ci	u8 dialog_token;
541e5b75505Sopenharmony_ci	char *hex;
542e5b75505Sopenharmony_ci	size_t hex_len;
543e5b75505Sopenharmony_ci
544e5b75505Sopenharmony_ci	if (!hapd->conf->coloc_intf_reporting) {
545e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
546e5b75505Sopenharmony_ci			   "WNM: Ignore unexpected Collocated Interference Report from "
547e5b75505Sopenharmony_ci			   MACSTR, MAC2STR(addr));
548e5b75505Sopenharmony_ci		return;
549e5b75505Sopenharmony_ci	}
550e5b75505Sopenharmony_ci
551e5b75505Sopenharmony_ci	if (len < 1) {
552e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
553e5b75505Sopenharmony_ci			   "WNM: Ignore too short Collocated Interference Report from "
554e5b75505Sopenharmony_ci			   MACSTR, MAC2STR(addr));
555e5b75505Sopenharmony_ci		return;
556e5b75505Sopenharmony_ci	}
557e5b75505Sopenharmony_ci	dialog_token = *buf++;
558e5b75505Sopenharmony_ci	len--;
559e5b75505Sopenharmony_ci
560e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG,
561e5b75505Sopenharmony_ci		   "WNM: Received Collocated Interference Report frame from "
562e5b75505Sopenharmony_ci		   MACSTR " (dialog_token=%u)",
563e5b75505Sopenharmony_ci		   MAC2STR(addr), dialog_token);
564e5b75505Sopenharmony_ci	wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
565e5b75505Sopenharmony_ci		    buf, len);
566e5b75505Sopenharmony_ci
567e5b75505Sopenharmony_ci	hex_len = 2 * len + 1;
568e5b75505Sopenharmony_ci	hex = os_malloc(hex_len);
569e5b75505Sopenharmony_ci	if (!hex)
570e5b75505Sopenharmony_ci		return;
571e5b75505Sopenharmony_ci	wpa_snprintf_hex(hex, hex_len, buf, len);
572e5b75505Sopenharmony_ci	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
573e5b75505Sopenharmony_ci		     MAC2STR(addr), dialog_token, hex);
574e5b75505Sopenharmony_ci	os_free(hex);
575e5b75505Sopenharmony_ci}
576e5b75505Sopenharmony_ci
577e5b75505Sopenharmony_ci
578e5b75505Sopenharmony_ciint ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
579e5b75505Sopenharmony_ci				const struct ieee80211_mgmt *mgmt, size_t len)
580e5b75505Sopenharmony_ci{
581e5b75505Sopenharmony_ci	u8 action;
582e5b75505Sopenharmony_ci	const u8 *payload;
583e5b75505Sopenharmony_ci	size_t plen;
584e5b75505Sopenharmony_ci
585e5b75505Sopenharmony_ci	if (len < IEEE80211_HDRLEN + 2)
586e5b75505Sopenharmony_ci		return -1;
587e5b75505Sopenharmony_ci
588e5b75505Sopenharmony_ci	payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
589e5b75505Sopenharmony_ci	action = *payload++;
590e5b75505Sopenharmony_ci	plen = len - IEEE80211_HDRLEN - 2;
591e5b75505Sopenharmony_ci
592e5b75505Sopenharmony_ci	switch (action) {
593e5b75505Sopenharmony_ci	case WNM_BSS_TRANS_MGMT_QUERY:
594e5b75505Sopenharmony_ci		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
595e5b75505Sopenharmony_ci						   plen);
596e5b75505Sopenharmony_ci		return 0;
597e5b75505Sopenharmony_ci	case WNM_BSS_TRANS_MGMT_RESP:
598e5b75505Sopenharmony_ci		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
599e5b75505Sopenharmony_ci						  plen);
600e5b75505Sopenharmony_ci		return 0;
601e5b75505Sopenharmony_ci	case WNM_SLEEP_MODE_REQ:
602e5b75505Sopenharmony_ci		ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
603e5b75505Sopenharmony_ci		return 0;
604e5b75505Sopenharmony_ci	case WNM_NOTIFICATION_REQ:
605e5b75505Sopenharmony_ci		ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
606e5b75505Sopenharmony_ci						   plen);
607e5b75505Sopenharmony_ci		return 0;
608e5b75505Sopenharmony_ci	case WNM_COLLOCATED_INTERFERENCE_REPORT:
609e5b75505Sopenharmony_ci		ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
610e5b75505Sopenharmony_ci						    plen);
611e5b75505Sopenharmony_ci		return 0;
612e5b75505Sopenharmony_ci	}
613e5b75505Sopenharmony_ci
614e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
615e5b75505Sopenharmony_ci		   action, MAC2STR(mgmt->sa));
616e5b75505Sopenharmony_ci	return -1;
617e5b75505Sopenharmony_ci}
618e5b75505Sopenharmony_ci
619e5b75505Sopenharmony_ci
620e5b75505Sopenharmony_ciint wnm_send_disassoc_imminent(struct hostapd_data *hapd,
621e5b75505Sopenharmony_ci			       struct sta_info *sta, int disassoc_timer)
622e5b75505Sopenharmony_ci{
623e5b75505Sopenharmony_ci	u8 buf[1000], *pos;
624e5b75505Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
625e5b75505Sopenharmony_ci
626e5b75505Sopenharmony_ci	os_memset(buf, 0, sizeof(buf));
627e5b75505Sopenharmony_ci	mgmt = (struct ieee80211_mgmt *) buf;
628e5b75505Sopenharmony_ci	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
629e5b75505Sopenharmony_ci					   WLAN_FC_STYPE_ACTION);
630e5b75505Sopenharmony_ci	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
631e5b75505Sopenharmony_ci	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
632e5b75505Sopenharmony_ci	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
633e5b75505Sopenharmony_ci	mgmt->u.action.category = WLAN_ACTION_WNM;
634e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
635e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
636e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.req_mode =
637e5b75505Sopenharmony_ci		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
638e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.disassoc_timer =
639e5b75505Sopenharmony_ci		host_to_le16(disassoc_timer);
640e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
641e5b75505Sopenharmony_ci
642e5b75505Sopenharmony_ci	pos = mgmt->u.action.u.bss_tm_req.variable;
643e5b75505Sopenharmony_ci
644e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
645e5b75505Sopenharmony_ci		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
646e5b75505Sopenharmony_ci	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
647e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
648e5b75505Sopenharmony_ci			   "Management Request frame");
649e5b75505Sopenharmony_ci		return -1;
650e5b75505Sopenharmony_ci	}
651e5b75505Sopenharmony_ci
652e5b75505Sopenharmony_ci	return 0;
653e5b75505Sopenharmony_ci}
654e5b75505Sopenharmony_ci
655e5b75505Sopenharmony_ci
656e5b75505Sopenharmony_cistatic void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
657e5b75505Sopenharmony_ci			       int disassoc_timer)
658e5b75505Sopenharmony_ci{
659e5b75505Sopenharmony_ci	int timeout, beacon_int;
660e5b75505Sopenharmony_ci
661e5b75505Sopenharmony_ci	/*
662e5b75505Sopenharmony_ci	 * Prevent STA from reconnecting using cached PMKSA to force
663e5b75505Sopenharmony_ci	 * full authentication with the authentication server (which may
664e5b75505Sopenharmony_ci	 * decide to reject the connection),
665e5b75505Sopenharmony_ci	 */
666e5b75505Sopenharmony_ci	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
667e5b75505Sopenharmony_ci
668e5b75505Sopenharmony_ci	beacon_int = hapd->iconf->beacon_int;
669e5b75505Sopenharmony_ci	if (beacon_int < 1)
670e5b75505Sopenharmony_ci		beacon_int = 100; /* best guess */
671e5b75505Sopenharmony_ci	/* Calculate timeout in ms based on beacon_int in TU */
672e5b75505Sopenharmony_ci	timeout = disassoc_timer * beacon_int * 128 / 125;
673e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
674e5b75505Sopenharmony_ci		   " set to %d ms", MAC2STR(sta->addr), timeout);
675e5b75505Sopenharmony_ci
676e5b75505Sopenharmony_ci	sta->timeout_next = STA_DISASSOC_FROM_CLI;
677e5b75505Sopenharmony_ci	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
678e5b75505Sopenharmony_ci	eloop_register_timeout(timeout / 1000,
679e5b75505Sopenharmony_ci			       timeout % 1000 * 1000,
680e5b75505Sopenharmony_ci			       ap_handle_timer, hapd, sta);
681e5b75505Sopenharmony_ci}
682e5b75505Sopenharmony_ci
683e5b75505Sopenharmony_ci
684e5b75505Sopenharmony_ciint wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
685e5b75505Sopenharmony_ci				   struct sta_info *sta, const char *url,
686e5b75505Sopenharmony_ci				   int disassoc_timer)
687e5b75505Sopenharmony_ci{
688e5b75505Sopenharmony_ci	u8 buf[1000], *pos;
689e5b75505Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
690e5b75505Sopenharmony_ci	size_t url_len;
691e5b75505Sopenharmony_ci
692e5b75505Sopenharmony_ci	os_memset(buf, 0, sizeof(buf));
693e5b75505Sopenharmony_ci	mgmt = (struct ieee80211_mgmt *) buf;
694e5b75505Sopenharmony_ci	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
695e5b75505Sopenharmony_ci					   WLAN_FC_STYPE_ACTION);
696e5b75505Sopenharmony_ci	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
697e5b75505Sopenharmony_ci	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
698e5b75505Sopenharmony_ci	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
699e5b75505Sopenharmony_ci	mgmt->u.action.category = WLAN_ACTION_WNM;
700e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
701e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
702e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.req_mode =
703e5b75505Sopenharmony_ci		WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
704e5b75505Sopenharmony_ci		WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
705e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.disassoc_timer =
706e5b75505Sopenharmony_ci		host_to_le16(disassoc_timer);
707e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
708e5b75505Sopenharmony_ci
709e5b75505Sopenharmony_ci	pos = mgmt->u.action.u.bss_tm_req.variable;
710e5b75505Sopenharmony_ci
711e5b75505Sopenharmony_ci	/* Session Information URL */
712e5b75505Sopenharmony_ci	url_len = os_strlen(url);
713e5b75505Sopenharmony_ci	if (url_len > 255)
714e5b75505Sopenharmony_ci		return -1;
715e5b75505Sopenharmony_ci	*pos++ = url_len;
716e5b75505Sopenharmony_ci	os_memcpy(pos, url, url_len);
717e5b75505Sopenharmony_ci	pos += url_len;
718e5b75505Sopenharmony_ci
719e5b75505Sopenharmony_ci	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
720e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
721e5b75505Sopenharmony_ci			   "Management Request frame");
722e5b75505Sopenharmony_ci		return -1;
723e5b75505Sopenharmony_ci	}
724e5b75505Sopenharmony_ci
725e5b75505Sopenharmony_ci	if (disassoc_timer) {
726e5b75505Sopenharmony_ci		/* send disassociation frame after time-out */
727e5b75505Sopenharmony_ci		set_disassoc_timer(hapd, sta, disassoc_timer);
728e5b75505Sopenharmony_ci	}
729e5b75505Sopenharmony_ci
730e5b75505Sopenharmony_ci	return 0;
731e5b75505Sopenharmony_ci}
732e5b75505Sopenharmony_ci
733e5b75505Sopenharmony_ci
734e5b75505Sopenharmony_ciint wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
735e5b75505Sopenharmony_ci			u8 req_mode, int disassoc_timer, u8 valid_int,
736e5b75505Sopenharmony_ci			const u8 *bss_term_dur, const char *url,
737e5b75505Sopenharmony_ci			const u8 *nei_rep, size_t nei_rep_len,
738e5b75505Sopenharmony_ci			const u8 *mbo_attrs, size_t mbo_len)
739e5b75505Sopenharmony_ci{
740e5b75505Sopenharmony_ci	u8 *buf, *pos;
741e5b75505Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
742e5b75505Sopenharmony_ci	size_t url_len;
743e5b75505Sopenharmony_ci
744e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
745e5b75505Sopenharmony_ci		   MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
746e5b75505Sopenharmony_ci		   MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
747e5b75505Sopenharmony_ci	buf = os_zalloc(1000 + nei_rep_len + mbo_len);
748e5b75505Sopenharmony_ci	if (buf == NULL)
749e5b75505Sopenharmony_ci		return -1;
750e5b75505Sopenharmony_ci	mgmt = (struct ieee80211_mgmt *) buf;
751e5b75505Sopenharmony_ci	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
752e5b75505Sopenharmony_ci					   WLAN_FC_STYPE_ACTION);
753e5b75505Sopenharmony_ci	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
754e5b75505Sopenharmony_ci	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
755e5b75505Sopenharmony_ci	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
756e5b75505Sopenharmony_ci	mgmt->u.action.category = WLAN_ACTION_WNM;
757e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
758e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
759e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
760e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.disassoc_timer =
761e5b75505Sopenharmony_ci		host_to_le16(disassoc_timer);
762e5b75505Sopenharmony_ci	mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
763e5b75505Sopenharmony_ci
764e5b75505Sopenharmony_ci	pos = mgmt->u.action.u.bss_tm_req.variable;
765e5b75505Sopenharmony_ci
766e5b75505Sopenharmony_ci	if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
767e5b75505Sopenharmony_ci	    bss_term_dur) {
768e5b75505Sopenharmony_ci		os_memcpy(pos, bss_term_dur, 12);
769e5b75505Sopenharmony_ci		pos += 12;
770e5b75505Sopenharmony_ci	}
771e5b75505Sopenharmony_ci
772e5b75505Sopenharmony_ci	if (url) {
773e5b75505Sopenharmony_ci		/* Session Information URL */
774e5b75505Sopenharmony_ci		url_len = os_strlen(url);
775e5b75505Sopenharmony_ci		if (url_len > 255) {
776e5b75505Sopenharmony_ci			os_free(buf);
777e5b75505Sopenharmony_ci			return -1;
778e5b75505Sopenharmony_ci		}
779e5b75505Sopenharmony_ci
780e5b75505Sopenharmony_ci		*pos++ = url_len;
781e5b75505Sopenharmony_ci		os_memcpy(pos, url, url_len);
782e5b75505Sopenharmony_ci		pos += url_len;
783e5b75505Sopenharmony_ci	}
784e5b75505Sopenharmony_ci
785e5b75505Sopenharmony_ci	if (nei_rep) {
786e5b75505Sopenharmony_ci		os_memcpy(pos, nei_rep, nei_rep_len);
787e5b75505Sopenharmony_ci		pos += nei_rep_len;
788e5b75505Sopenharmony_ci	}
789e5b75505Sopenharmony_ci
790e5b75505Sopenharmony_ci	if (mbo_len > 0) {
791e5b75505Sopenharmony_ci		pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
792e5b75505Sopenharmony_ci				  mbo_len);
793e5b75505Sopenharmony_ci	}
794e5b75505Sopenharmony_ci
795e5b75505Sopenharmony_ci	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
796e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
797e5b75505Sopenharmony_ci			   "Failed to send BSS Transition Management Request frame");
798e5b75505Sopenharmony_ci		os_free(buf);
799e5b75505Sopenharmony_ci		return -1;
800e5b75505Sopenharmony_ci	}
801e5b75505Sopenharmony_ci	os_free(buf);
802e5b75505Sopenharmony_ci
803e5b75505Sopenharmony_ci	if (disassoc_timer) {
804e5b75505Sopenharmony_ci		/* send disassociation frame after time-out */
805e5b75505Sopenharmony_ci		set_disassoc_timer(hapd, sta, disassoc_timer);
806e5b75505Sopenharmony_ci	}
807e5b75505Sopenharmony_ci
808e5b75505Sopenharmony_ci	return 0;
809e5b75505Sopenharmony_ci}
810e5b75505Sopenharmony_ci
811e5b75505Sopenharmony_ci
812e5b75505Sopenharmony_ciint wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
813e5b75505Sopenharmony_ci			    unsigned int auto_report, unsigned int timeout)
814e5b75505Sopenharmony_ci{
815e5b75505Sopenharmony_ci	u8 buf[100], *pos;
816e5b75505Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
817e5b75505Sopenharmony_ci	u8 dialog_token = 1;
818e5b75505Sopenharmony_ci
819e5b75505Sopenharmony_ci	if (auto_report > 3 || timeout > 63)
820e5b75505Sopenharmony_ci		return -1;
821e5b75505Sopenharmony_ci	os_memset(buf, 0, sizeof(buf));
822e5b75505Sopenharmony_ci	mgmt = (struct ieee80211_mgmt *) buf;
823e5b75505Sopenharmony_ci	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
824e5b75505Sopenharmony_ci					   WLAN_FC_STYPE_ACTION);
825e5b75505Sopenharmony_ci	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
826e5b75505Sopenharmony_ci	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
827e5b75505Sopenharmony_ci	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
828e5b75505Sopenharmony_ci	mgmt->u.action.category = WLAN_ACTION_WNM;
829e5b75505Sopenharmony_ci	mgmt->u.action.u.coloc_intf_req.action =
830e5b75505Sopenharmony_ci		WNM_COLLOCATED_INTERFERENCE_REQ;
831e5b75505Sopenharmony_ci	mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
832e5b75505Sopenharmony_ci	mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
833e5b75505Sopenharmony_ci	pos = &mgmt->u.action.u.coloc_intf_req.req_info;
834e5b75505Sopenharmony_ci	pos++;
835e5b75505Sopenharmony_ci
836e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
837e5b75505Sopenharmony_ci		   MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
838e5b75505Sopenharmony_ci		   MAC2STR(sta->addr), dialog_token, auto_report, timeout);
839e5b75505Sopenharmony_ci	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
840e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
841e5b75505Sopenharmony_ci			   "WNM: Failed to send Collocated Interference Request frame");
842e5b75505Sopenharmony_ci		return -1;
843e5b75505Sopenharmony_ci	}
844e5b75505Sopenharmony_ci
845e5b75505Sopenharmony_ci	return 0;
846e5b75505Sopenharmony_ci}
847