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