1f05cddf9SRui Paulo /* 2f05cddf9SRui Paulo * hostapd - WNM 3*5b9c547cSRui 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" 12*5b9c547cSRui Paulo #include "utils/eloop.h" 13f05cddf9SRui Paulo #include "common/ieee802_11_defs.h" 14*5b9c547cSRui Paulo #include "common/wpa_ctrl.h" 15f05cddf9SRui Paulo #include "ap/hostapd.h" 16f05cddf9SRui Paulo #include "ap/sta_info.h" 17f05cddf9SRui Paulo #include "ap/ap_config.h" 18f05cddf9SRui Paulo #include "ap/ap_drv_ops.h" 19f05cddf9SRui Paulo #include "ap/wpa_auth.h" 20f05cddf9SRui Paulo #include "wnm_ap.h" 21f05cddf9SRui Paulo 22f05cddf9SRui Paulo #define MAX_TFS_IE_LEN 1024 23f05cddf9SRui Paulo 24f05cddf9SRui Paulo 25f05cddf9SRui Paulo /* get the TFS IE from driver */ 26f05cddf9SRui Paulo static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, 27f05cddf9SRui Paulo u8 *buf, u16 *buf_len, enum wnm_oper oper) 28f05cddf9SRui Paulo { 29f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); 30f05cddf9SRui Paulo 31f05cddf9SRui Paulo return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); 32f05cddf9SRui Paulo } 33f05cddf9SRui Paulo 34f05cddf9SRui Paulo 35f05cddf9SRui Paulo /* set the TFS IE to driver */ 36f05cddf9SRui Paulo static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, 37f05cddf9SRui Paulo u8 *buf, u16 *buf_len, enum wnm_oper oper) 38f05cddf9SRui Paulo { 39f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); 40f05cddf9SRui Paulo 41f05cddf9SRui Paulo return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); 42f05cddf9SRui Paulo } 43f05cddf9SRui Paulo 44f05cddf9SRui Paulo 45f05cddf9SRui Paulo /* MLME-SLEEPMODE.response */ 46f05cddf9SRui Paulo static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, 47f05cddf9SRui Paulo const u8 *addr, u8 dialog_token, 48f05cddf9SRui Paulo u8 action_type, u16 intval) 49f05cddf9SRui Paulo { 50f05cddf9SRui Paulo struct ieee80211_mgmt *mgmt; 51f05cddf9SRui Paulo int res; 52f05cddf9SRui Paulo size_t len; 53f05cddf9SRui Paulo size_t gtk_elem_len = 0; 54f05cddf9SRui Paulo size_t igtk_elem_len = 0; 55f05cddf9SRui Paulo struct wnm_sleep_element wnmsleep_ie; 56f05cddf9SRui Paulo u8 *wnmtfs_ie; 57f05cddf9SRui Paulo u8 wnmsleep_ie_len; 58f05cddf9SRui Paulo u16 wnmtfs_ie_len; 59f05cddf9SRui Paulo u8 *pos; 60f05cddf9SRui Paulo struct sta_info *sta; 61f05cddf9SRui Paulo enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? 62f05cddf9SRui Paulo WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; 63f05cddf9SRui Paulo 64f05cddf9SRui Paulo sta = ap_get_sta(hapd, addr); 65f05cddf9SRui Paulo if (sta == NULL) { 66f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: station not found", __func__); 67f05cddf9SRui Paulo return -EINVAL; 68f05cddf9SRui Paulo } 69f05cddf9SRui Paulo 70f05cddf9SRui Paulo /* WNM-Sleep Mode IE */ 71f05cddf9SRui Paulo os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); 72f05cddf9SRui Paulo wnmsleep_ie_len = sizeof(struct wnm_sleep_element); 73f05cddf9SRui Paulo wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; 74f05cddf9SRui Paulo wnmsleep_ie.len = wnmsleep_ie_len - 2; 75f05cddf9SRui Paulo wnmsleep_ie.action_type = action_type; 76f05cddf9SRui Paulo wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; 77*5b9c547cSRui Paulo wnmsleep_ie.intval = host_to_le16(intval); 78f05cddf9SRui Paulo 79f05cddf9SRui Paulo /* TFS IE(s) */ 80f05cddf9SRui Paulo wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); 81f05cddf9SRui Paulo if (wnmtfs_ie == NULL) 82f05cddf9SRui Paulo return -1; 83f05cddf9SRui Paulo if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, 84f05cddf9SRui Paulo tfs_oper)) { 85f05cddf9SRui Paulo wnmtfs_ie_len = 0; 86f05cddf9SRui Paulo os_free(wnmtfs_ie); 87f05cddf9SRui Paulo wnmtfs_ie = NULL; 88f05cddf9SRui Paulo } 89f05cddf9SRui Paulo 90f05cddf9SRui Paulo #define MAX_GTK_SUBELEM_LEN 45 91f05cddf9SRui Paulo #define MAX_IGTK_SUBELEM_LEN 26 92f05cddf9SRui Paulo mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + 93f05cddf9SRui Paulo MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); 94f05cddf9SRui Paulo if (mgmt == NULL) { 95f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 96f05cddf9SRui Paulo "WNM-Sleep Response action frame"); 97f05cddf9SRui Paulo return -1; 98f05cddf9SRui Paulo } 99f05cddf9SRui Paulo os_memcpy(mgmt->da, addr, ETH_ALEN); 100f05cddf9SRui Paulo os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 101f05cddf9SRui Paulo os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 102f05cddf9SRui Paulo mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 103f05cddf9SRui Paulo WLAN_FC_STYPE_ACTION); 104f05cddf9SRui Paulo mgmt->u.action.category = WLAN_ACTION_WNM; 105f05cddf9SRui Paulo mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; 106f05cddf9SRui Paulo mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; 107f05cddf9SRui Paulo pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; 108f05cddf9SRui Paulo /* add key data if MFP is enabled */ 109f05cddf9SRui Paulo if (!wpa_auth_uses_mfp(sta->wpa_sm) || 110f05cddf9SRui Paulo action_type != WNM_SLEEP_MODE_EXIT) { 111f05cddf9SRui Paulo mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; 112f05cddf9SRui Paulo } else { 113f05cddf9SRui Paulo gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); 114f05cddf9SRui Paulo pos += gtk_elem_len; 115f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", 116f05cddf9SRui Paulo (int) gtk_elem_len); 117f05cddf9SRui Paulo #ifdef CONFIG_IEEE80211W 118f05cddf9SRui Paulo res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); 119f05cddf9SRui Paulo if (res < 0) { 120f05cddf9SRui Paulo os_free(wnmtfs_ie); 121f05cddf9SRui Paulo os_free(mgmt); 122f05cddf9SRui Paulo return -1; 123f05cddf9SRui Paulo } 124f05cddf9SRui Paulo igtk_elem_len = res; 125f05cddf9SRui Paulo pos += igtk_elem_len; 126f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", 127f05cddf9SRui Paulo (int) igtk_elem_len); 128f05cddf9SRui Paulo #endif /* CONFIG_IEEE80211W */ 129f05cddf9SRui Paulo 130f05cddf9SRui Paulo WPA_PUT_LE16((u8 *) 131f05cddf9SRui Paulo &mgmt->u.action.u.wnm_sleep_resp.keydata_len, 132f05cddf9SRui Paulo gtk_elem_len + igtk_elem_len); 133f05cddf9SRui Paulo } 134f05cddf9SRui Paulo os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); 135f05cddf9SRui Paulo /* copy TFS IE here */ 136f05cddf9SRui Paulo pos += wnmsleep_ie_len; 137f05cddf9SRui Paulo if (wnmtfs_ie) 138f05cddf9SRui Paulo os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); 139f05cddf9SRui Paulo 140f05cddf9SRui Paulo len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + 141f05cddf9SRui Paulo igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; 142f05cddf9SRui Paulo 143f05cddf9SRui Paulo /* In driver, response frame should be forced to sent when STA is in 144f05cddf9SRui Paulo * PS mode */ 145f05cddf9SRui Paulo res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, 146f05cddf9SRui Paulo mgmt->da, &mgmt->u.action.category, len); 147f05cddf9SRui Paulo 148f05cddf9SRui Paulo if (!res) { 149f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " 150f05cddf9SRui Paulo "frame"); 151f05cddf9SRui Paulo 152f05cddf9SRui Paulo /* when entering wnmsleep 153f05cddf9SRui Paulo * 1. pause the node in driver 154f05cddf9SRui Paulo * 2. mark the node so that AP won't update GTK/IGTK during 155f05cddf9SRui Paulo * WNM Sleep 156f05cddf9SRui Paulo */ 157f05cddf9SRui Paulo if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && 158f05cddf9SRui Paulo wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { 159*5b9c547cSRui Paulo sta->flags |= WLAN_STA_WNM_SLEEP_MODE; 160f05cddf9SRui Paulo hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, 161f05cddf9SRui Paulo addr, NULL, NULL); 162f05cddf9SRui Paulo wpa_set_wnmsleep(sta->wpa_sm, 1); 163f05cddf9SRui Paulo } 164f05cddf9SRui Paulo /* when exiting wnmsleep 165f05cddf9SRui Paulo * 1. unmark the node 166f05cddf9SRui Paulo * 2. start GTK/IGTK update if MFP is not used 167f05cddf9SRui Paulo * 3. unpause the node in driver 168f05cddf9SRui Paulo */ 169f05cddf9SRui Paulo if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || 170f05cddf9SRui Paulo wnmsleep_ie.status == 171f05cddf9SRui Paulo WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && 172f05cddf9SRui Paulo wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { 173*5b9c547cSRui Paulo sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; 174f05cddf9SRui Paulo wpa_set_wnmsleep(sta->wpa_sm, 0); 175f05cddf9SRui Paulo hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, 176f05cddf9SRui Paulo addr, NULL, NULL); 177f05cddf9SRui Paulo if (!wpa_auth_uses_mfp(sta->wpa_sm)) 178f05cddf9SRui Paulo wpa_wnmsleep_rekey_gtk(sta->wpa_sm); 179f05cddf9SRui Paulo } 180f05cddf9SRui Paulo } else 181f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); 182f05cddf9SRui Paulo 183f05cddf9SRui Paulo #undef MAX_GTK_SUBELEM_LEN 184f05cddf9SRui Paulo #undef MAX_IGTK_SUBELEM_LEN 185f05cddf9SRui Paulo os_free(wnmtfs_ie); 186f05cddf9SRui Paulo os_free(mgmt); 187f05cddf9SRui Paulo return res; 188f05cddf9SRui Paulo } 189f05cddf9SRui Paulo 190f05cddf9SRui Paulo 191f05cddf9SRui Paulo static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, 192f05cddf9SRui Paulo const u8 *addr, const u8 *frm, int len) 193f05cddf9SRui Paulo { 194f05cddf9SRui Paulo /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ 195f05cddf9SRui Paulo const u8 *pos = frm; 196f05cddf9SRui Paulo u8 dialog_token; 197f05cddf9SRui Paulo struct wnm_sleep_element *wnmsleep_ie = NULL; 198f05cddf9SRui Paulo /* multiple TFS Req IE (assuming consecutive) */ 199f05cddf9SRui Paulo u8 *tfsreq_ie_start = NULL; 200f05cddf9SRui Paulo u8 *tfsreq_ie_end = NULL; 201f05cddf9SRui Paulo u16 tfsreq_ie_len = 0; 202f05cddf9SRui Paulo 203f05cddf9SRui Paulo dialog_token = *pos++; 204f05cddf9SRui Paulo while (pos + 1 < frm + len) { 205f05cddf9SRui Paulo u8 ie_len = pos[1]; 206f05cddf9SRui Paulo if (pos + 2 + ie_len > frm + len) 207f05cddf9SRui Paulo break; 208f05cddf9SRui Paulo if (*pos == WLAN_EID_WNMSLEEP) 209f05cddf9SRui Paulo wnmsleep_ie = (struct wnm_sleep_element *) pos; 210f05cddf9SRui Paulo else if (*pos == WLAN_EID_TFS_REQ) { 211f05cddf9SRui Paulo if (!tfsreq_ie_start) 212f05cddf9SRui Paulo tfsreq_ie_start = (u8 *) pos; 213f05cddf9SRui Paulo tfsreq_ie_end = (u8 *) pos; 214f05cddf9SRui Paulo } else 215f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", 216f05cddf9SRui Paulo *pos); 217f05cddf9SRui Paulo pos += ie_len + 2; 218f05cddf9SRui Paulo } 219f05cddf9SRui Paulo 220f05cddf9SRui Paulo if (!wnmsleep_ie) { 221f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 222f05cddf9SRui Paulo return; 223f05cddf9SRui Paulo } 224f05cddf9SRui Paulo 225f05cddf9SRui Paulo if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && 226f05cddf9SRui Paulo tfsreq_ie_start && tfsreq_ie_end && 227f05cddf9SRui Paulo tfsreq_ie_end - tfsreq_ie_start >= 0) { 228f05cddf9SRui Paulo tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - 229f05cddf9SRui Paulo tfsreq_ie_start; 230f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); 231f05cddf9SRui Paulo /* pass the TFS Req IE(s) to driver for processing */ 232f05cddf9SRui Paulo if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, 233f05cddf9SRui Paulo &tfsreq_ie_len, 234f05cddf9SRui Paulo WNM_SLEEP_TFS_REQ_IE_SET)) 235f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); 236f05cddf9SRui Paulo } 237f05cddf9SRui Paulo 238f05cddf9SRui Paulo ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, 239f05cddf9SRui Paulo wnmsleep_ie->action_type, 240*5b9c547cSRui Paulo le_to_host16(wnmsleep_ie->intval)); 241f05cddf9SRui Paulo 242f05cddf9SRui Paulo if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 243f05cddf9SRui Paulo /* clear the tfs after sending the resp frame */ 244f05cddf9SRui Paulo ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, 245f05cddf9SRui Paulo &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); 246f05cddf9SRui Paulo } 247f05cddf9SRui Paulo } 248f05cddf9SRui Paulo 249f05cddf9SRui Paulo 250*5b9c547cSRui Paulo static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, 251*5b9c547cSRui Paulo const u8 *addr, 252*5b9c547cSRui Paulo u8 dialog_token, 253*5b9c547cSRui Paulo const char *url) 254f05cddf9SRui Paulo { 255*5b9c547cSRui Paulo struct ieee80211_mgmt *mgmt; 256*5b9c547cSRui Paulo size_t url_len, len; 257*5b9c547cSRui Paulo u8 *pos; 258*5b9c547cSRui Paulo int res; 259*5b9c547cSRui Paulo 260*5b9c547cSRui Paulo if (url) 261*5b9c547cSRui Paulo url_len = os_strlen(url); 262*5b9c547cSRui Paulo else 263*5b9c547cSRui Paulo url_len = 0; 264*5b9c547cSRui Paulo 265*5b9c547cSRui Paulo mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); 266*5b9c547cSRui Paulo if (mgmt == NULL) 267*5b9c547cSRui Paulo return -1; 268*5b9c547cSRui Paulo os_memcpy(mgmt->da, addr, ETH_ALEN); 269*5b9c547cSRui Paulo os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 270*5b9c547cSRui Paulo os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 271*5b9c547cSRui Paulo mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 272*5b9c547cSRui Paulo WLAN_FC_STYPE_ACTION); 273*5b9c547cSRui Paulo mgmt->u.action.category = WLAN_ACTION_WNM; 274*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 275*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; 276*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.req_mode = 0; 277*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); 278*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.validity_interval = 1; 279*5b9c547cSRui Paulo pos = mgmt->u.action.u.bss_tm_req.variable; 280*5b9c547cSRui Paulo if (url) { 281*5b9c547cSRui Paulo *pos++ += url_len; 282*5b9c547cSRui Paulo os_memcpy(pos, url, url_len); 283*5b9c547cSRui Paulo pos += url_len; 284*5b9c547cSRui Paulo } 285*5b9c547cSRui Paulo 286*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " 287*5b9c547cSRui Paulo MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " 288*5b9c547cSRui Paulo "validity_interval=%u", 289*5b9c547cSRui Paulo MAC2STR(addr), dialog_token, 290*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.req_mode, 291*5b9c547cSRui Paulo le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), 292*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.validity_interval); 293*5b9c547cSRui Paulo 294*5b9c547cSRui Paulo len = pos - &mgmt->u.action.category; 295*5b9c547cSRui Paulo res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, 296*5b9c547cSRui Paulo mgmt->da, &mgmt->u.action.category, len); 297*5b9c547cSRui Paulo os_free(mgmt); 298*5b9c547cSRui Paulo return res; 299*5b9c547cSRui Paulo } 300*5b9c547cSRui Paulo 301*5b9c547cSRui Paulo 302*5b9c547cSRui Paulo static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, 303*5b9c547cSRui Paulo const u8 *addr, const u8 *frm, 304*5b9c547cSRui Paulo size_t len) 305*5b9c547cSRui Paulo { 306*5b9c547cSRui Paulo u8 dialog_token, reason; 307*5b9c547cSRui Paulo const u8 *pos, *end; 308*5b9c547cSRui Paulo 309*5b9c547cSRui Paulo if (len < 2) { 310*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " 311*5b9c547cSRui Paulo MACSTR, MAC2STR(addr)); 312*5b9c547cSRui Paulo return; 313*5b9c547cSRui Paulo } 314*5b9c547cSRui Paulo 315*5b9c547cSRui Paulo pos = frm; 316*5b9c547cSRui Paulo end = pos + len; 317*5b9c547cSRui Paulo dialog_token = *pos++; 318*5b9c547cSRui Paulo reason = *pos++; 319*5b9c547cSRui Paulo 320*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from " 321*5b9c547cSRui Paulo MACSTR " dialog_token=%u reason=%u", 322*5b9c547cSRui Paulo MAC2STR(addr), dialog_token, reason); 323*5b9c547cSRui Paulo 324*5b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", 325*5b9c547cSRui Paulo pos, end - pos); 326*5b9c547cSRui Paulo 327*5b9c547cSRui Paulo ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); 328*5b9c547cSRui Paulo } 329*5b9c547cSRui Paulo 330*5b9c547cSRui Paulo 331*5b9c547cSRui Paulo static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, 332*5b9c547cSRui Paulo const u8 *addr, const u8 *frm, 333*5b9c547cSRui Paulo size_t len) 334*5b9c547cSRui Paulo { 335*5b9c547cSRui Paulo u8 dialog_token, status_code, bss_termination_delay; 336*5b9c547cSRui Paulo const u8 *pos, *end; 337*5b9c547cSRui Paulo 338*5b9c547cSRui Paulo if (len < 3) { 339*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " 340*5b9c547cSRui Paulo MACSTR, MAC2STR(addr)); 341*5b9c547cSRui Paulo return; 342*5b9c547cSRui Paulo } 343*5b9c547cSRui Paulo 344*5b9c547cSRui Paulo pos = frm; 345*5b9c547cSRui Paulo end = pos + len; 346*5b9c547cSRui Paulo dialog_token = *pos++; 347*5b9c547cSRui Paulo status_code = *pos++; 348*5b9c547cSRui Paulo bss_termination_delay = *pos++; 349*5b9c547cSRui Paulo 350*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from " 351*5b9c547cSRui Paulo MACSTR " dialog_token=%u status_code=%u " 352*5b9c547cSRui Paulo "bss_termination_delay=%u", MAC2STR(addr), dialog_token, 353*5b9c547cSRui Paulo status_code, bss_termination_delay); 354*5b9c547cSRui Paulo 355*5b9c547cSRui Paulo if (status_code == WNM_BSS_TM_ACCEPT) { 356*5b9c547cSRui Paulo if (end - pos < ETH_ALEN) { 357*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); 358*5b9c547cSRui Paulo return; 359*5b9c547cSRui Paulo } 360*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, 361*5b9c547cSRui Paulo MAC2STR(pos)); 362*5b9c547cSRui Paulo wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR 363*5b9c547cSRui Paulo " status_code=%u bss_termination_delay=%u target_bssid=" 364*5b9c547cSRui Paulo MACSTR, 365*5b9c547cSRui Paulo MAC2STR(addr), status_code, bss_termination_delay, 366*5b9c547cSRui Paulo MAC2STR(pos)); 367*5b9c547cSRui Paulo pos += ETH_ALEN; 368*5b9c547cSRui Paulo } else { 369*5b9c547cSRui Paulo wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR 370*5b9c547cSRui Paulo " status_code=%u bss_termination_delay=%u", 371*5b9c547cSRui Paulo MAC2STR(addr), status_code, bss_termination_delay); 372*5b9c547cSRui Paulo } 373*5b9c547cSRui Paulo 374*5b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", 375*5b9c547cSRui Paulo pos, end - pos); 376*5b9c547cSRui Paulo } 377*5b9c547cSRui Paulo 378*5b9c547cSRui Paulo 379*5b9c547cSRui Paulo int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, 380*5b9c547cSRui Paulo const struct ieee80211_mgmt *mgmt, size_t len) 381*5b9c547cSRui Paulo { 382*5b9c547cSRui Paulo u8 action; 383*5b9c547cSRui Paulo const u8 *payload; 384*5b9c547cSRui Paulo size_t plen; 385*5b9c547cSRui Paulo 386*5b9c547cSRui Paulo if (len < IEEE80211_HDRLEN + 2) 387f05cddf9SRui Paulo return -1; 388f05cddf9SRui Paulo 389*5b9c547cSRui Paulo payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; 390*5b9c547cSRui Paulo action = *payload++; 391*5b9c547cSRui Paulo plen = len - IEEE80211_HDRLEN - 2; 392*5b9c547cSRui Paulo 393*5b9c547cSRui Paulo switch (action) { 394f05cddf9SRui Paulo case WNM_BSS_TRANS_MGMT_QUERY: 395*5b9c547cSRui Paulo ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload, 396*5b9c547cSRui Paulo plen); 397*5b9c547cSRui Paulo return 0; 398f05cddf9SRui Paulo case WNM_BSS_TRANS_MGMT_RESP: 399*5b9c547cSRui Paulo ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, 400*5b9c547cSRui Paulo plen); 401*5b9c547cSRui Paulo return 0; 402f05cddf9SRui Paulo case WNM_SLEEP_MODE_REQ: 403*5b9c547cSRui Paulo ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); 404f05cddf9SRui Paulo return 0; 405f05cddf9SRui Paulo } 406f05cddf9SRui Paulo 407f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, 408*5b9c547cSRui Paulo action, MAC2STR(mgmt->sa)); 409f05cddf9SRui Paulo return -1; 410f05cddf9SRui Paulo } 411*5b9c547cSRui Paulo 412*5b9c547cSRui Paulo 413*5b9c547cSRui Paulo int wnm_send_disassoc_imminent(struct hostapd_data *hapd, 414*5b9c547cSRui Paulo struct sta_info *sta, int disassoc_timer) 415*5b9c547cSRui Paulo { 416*5b9c547cSRui Paulo u8 buf[1000], *pos; 417*5b9c547cSRui Paulo struct ieee80211_mgmt *mgmt; 418*5b9c547cSRui Paulo 419*5b9c547cSRui Paulo os_memset(buf, 0, sizeof(buf)); 420*5b9c547cSRui Paulo mgmt = (struct ieee80211_mgmt *) buf; 421*5b9c547cSRui Paulo mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 422*5b9c547cSRui Paulo WLAN_FC_STYPE_ACTION); 423*5b9c547cSRui Paulo os_memcpy(mgmt->da, sta->addr, ETH_ALEN); 424*5b9c547cSRui Paulo os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 425*5b9c547cSRui Paulo os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 426*5b9c547cSRui Paulo mgmt->u.action.category = WLAN_ACTION_WNM; 427*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 428*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.dialog_token = 1; 429*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.req_mode = 430*5b9c547cSRui Paulo WNM_BSS_TM_REQ_DISASSOC_IMMINENT; 431*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.disassoc_timer = 432*5b9c547cSRui Paulo host_to_le16(disassoc_timer); 433*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.validity_interval = 0; 434*5b9c547cSRui Paulo 435*5b9c547cSRui Paulo pos = mgmt->u.action.u.bss_tm_req.variable; 436*5b9c547cSRui Paulo 437*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " 438*5b9c547cSRui Paulo MACSTR, disassoc_timer, MAC2STR(sta->addr)); 439*5b9c547cSRui Paulo if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { 440*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " 441*5b9c547cSRui Paulo "Management Request frame"); 442*5b9c547cSRui Paulo return -1; 443*5b9c547cSRui Paulo } 444*5b9c547cSRui Paulo 445*5b9c547cSRui Paulo return 0; 446*5b9c547cSRui Paulo } 447*5b9c547cSRui Paulo 448*5b9c547cSRui Paulo 449*5b9c547cSRui Paulo static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta, 450*5b9c547cSRui Paulo int disassoc_timer) 451*5b9c547cSRui Paulo { 452*5b9c547cSRui Paulo int timeout, beacon_int; 453*5b9c547cSRui Paulo 454*5b9c547cSRui Paulo /* 455*5b9c547cSRui Paulo * Prevent STA from reconnecting using cached PMKSA to force 456*5b9c547cSRui Paulo * full authentication with the authentication server (which may 457*5b9c547cSRui Paulo * decide to reject the connection), 458*5b9c547cSRui Paulo */ 459*5b9c547cSRui Paulo wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); 460*5b9c547cSRui Paulo 461*5b9c547cSRui Paulo beacon_int = hapd->iconf->beacon_int; 462*5b9c547cSRui Paulo if (beacon_int < 1) 463*5b9c547cSRui Paulo beacon_int = 100; /* best guess */ 464*5b9c547cSRui Paulo /* Calculate timeout in ms based on beacon_int in TU */ 465*5b9c547cSRui Paulo timeout = disassoc_timer * beacon_int * 128 / 125; 466*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR 467*5b9c547cSRui Paulo " set to %d ms", MAC2STR(sta->addr), timeout); 468*5b9c547cSRui Paulo 469*5b9c547cSRui Paulo sta->timeout_next = STA_DISASSOC_FROM_CLI; 470*5b9c547cSRui Paulo eloop_cancel_timeout(ap_handle_timer, hapd, sta); 471*5b9c547cSRui Paulo eloop_register_timeout(timeout / 1000, 472*5b9c547cSRui Paulo timeout % 1000 * 1000, 473*5b9c547cSRui Paulo ap_handle_timer, hapd, sta); 474*5b9c547cSRui Paulo } 475*5b9c547cSRui Paulo 476*5b9c547cSRui Paulo 477*5b9c547cSRui Paulo int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, 478*5b9c547cSRui Paulo struct sta_info *sta, const char *url, 479*5b9c547cSRui Paulo int disassoc_timer) 480*5b9c547cSRui Paulo { 481*5b9c547cSRui Paulo u8 buf[1000], *pos; 482*5b9c547cSRui Paulo struct ieee80211_mgmt *mgmt; 483*5b9c547cSRui Paulo size_t url_len; 484*5b9c547cSRui Paulo 485*5b9c547cSRui Paulo os_memset(buf, 0, sizeof(buf)); 486*5b9c547cSRui Paulo mgmt = (struct ieee80211_mgmt *) buf; 487*5b9c547cSRui Paulo mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 488*5b9c547cSRui Paulo WLAN_FC_STYPE_ACTION); 489*5b9c547cSRui Paulo os_memcpy(mgmt->da, sta->addr, ETH_ALEN); 490*5b9c547cSRui Paulo os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 491*5b9c547cSRui Paulo os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 492*5b9c547cSRui Paulo mgmt->u.action.category = WLAN_ACTION_WNM; 493*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 494*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.dialog_token = 1; 495*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.req_mode = 496*5b9c547cSRui Paulo WNM_BSS_TM_REQ_DISASSOC_IMMINENT | 497*5b9c547cSRui Paulo WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; 498*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.disassoc_timer = 499*5b9c547cSRui Paulo host_to_le16(disassoc_timer); 500*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.validity_interval = 0x01; 501*5b9c547cSRui Paulo 502*5b9c547cSRui Paulo pos = mgmt->u.action.u.bss_tm_req.variable; 503*5b9c547cSRui Paulo 504*5b9c547cSRui Paulo /* Session Information URL */ 505*5b9c547cSRui Paulo url_len = os_strlen(url); 506*5b9c547cSRui Paulo if (url_len > 255) 507*5b9c547cSRui Paulo return -1; 508*5b9c547cSRui Paulo *pos++ = url_len; 509*5b9c547cSRui Paulo os_memcpy(pos, url, url_len); 510*5b9c547cSRui Paulo pos += url_len; 511*5b9c547cSRui Paulo 512*5b9c547cSRui Paulo if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { 513*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " 514*5b9c547cSRui Paulo "Management Request frame"); 515*5b9c547cSRui Paulo return -1; 516*5b9c547cSRui Paulo } 517*5b9c547cSRui Paulo 518*5b9c547cSRui Paulo if (disassoc_timer) { 519*5b9c547cSRui Paulo /* send disassociation frame after time-out */ 520*5b9c547cSRui Paulo set_disassoc_timer(hapd, sta, disassoc_timer); 521*5b9c547cSRui Paulo } 522*5b9c547cSRui Paulo 523*5b9c547cSRui Paulo return 0; 524*5b9c547cSRui Paulo } 525*5b9c547cSRui Paulo 526*5b9c547cSRui Paulo 527*5b9c547cSRui Paulo int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, 528*5b9c547cSRui Paulo u8 req_mode, int disassoc_timer, u8 valid_int, 529*5b9c547cSRui Paulo const u8 *bss_term_dur, const char *url, 530*5b9c547cSRui Paulo const u8 *nei_rep, size_t nei_rep_len) 531*5b9c547cSRui Paulo { 532*5b9c547cSRui Paulo u8 *buf, *pos; 533*5b9c547cSRui Paulo struct ieee80211_mgmt *mgmt; 534*5b9c547cSRui Paulo size_t url_len; 535*5b9c547cSRui Paulo 536*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " 537*5b9c547cSRui Paulo MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", 538*5b9c547cSRui Paulo MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); 539*5b9c547cSRui Paulo buf = os_zalloc(1000 + nei_rep_len); 540*5b9c547cSRui Paulo if (buf == NULL) 541*5b9c547cSRui Paulo return -1; 542*5b9c547cSRui Paulo mgmt = (struct ieee80211_mgmt *) buf; 543*5b9c547cSRui Paulo mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 544*5b9c547cSRui Paulo WLAN_FC_STYPE_ACTION); 545*5b9c547cSRui Paulo os_memcpy(mgmt->da, sta->addr, ETH_ALEN); 546*5b9c547cSRui Paulo os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 547*5b9c547cSRui Paulo os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 548*5b9c547cSRui Paulo mgmt->u.action.category = WLAN_ACTION_WNM; 549*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 550*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.dialog_token = 1; 551*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.req_mode = req_mode; 552*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.disassoc_timer = 553*5b9c547cSRui Paulo host_to_le16(disassoc_timer); 554*5b9c547cSRui Paulo mgmt->u.action.u.bss_tm_req.validity_interval = valid_int; 555*5b9c547cSRui Paulo 556*5b9c547cSRui Paulo pos = mgmt->u.action.u.bss_tm_req.variable; 557*5b9c547cSRui Paulo 558*5b9c547cSRui Paulo if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) && 559*5b9c547cSRui Paulo bss_term_dur) { 560*5b9c547cSRui Paulo os_memcpy(pos, bss_term_dur, 12); 561*5b9c547cSRui Paulo pos += 12; 562*5b9c547cSRui Paulo } 563*5b9c547cSRui Paulo 564*5b9c547cSRui Paulo if (url) { 565*5b9c547cSRui Paulo /* Session Information URL */ 566*5b9c547cSRui Paulo url_len = os_strlen(url); 567*5b9c547cSRui Paulo if (url_len > 255) { 568*5b9c547cSRui Paulo os_free(buf); 569*5b9c547cSRui Paulo return -1; 570*5b9c547cSRui Paulo } 571*5b9c547cSRui Paulo 572*5b9c547cSRui Paulo *pos++ = url_len; 573*5b9c547cSRui Paulo os_memcpy(pos, url, url_len); 574*5b9c547cSRui Paulo pos += url_len; 575*5b9c547cSRui Paulo } 576*5b9c547cSRui Paulo 577*5b9c547cSRui Paulo if (nei_rep) { 578*5b9c547cSRui Paulo os_memcpy(pos, nei_rep, nei_rep_len); 579*5b9c547cSRui Paulo pos += nei_rep_len; 580*5b9c547cSRui Paulo } 581*5b9c547cSRui Paulo 582*5b9c547cSRui Paulo if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { 583*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 584*5b9c547cSRui Paulo "Failed to send BSS Transition Management Request frame"); 585*5b9c547cSRui Paulo os_free(buf); 586*5b9c547cSRui Paulo return -1; 587*5b9c547cSRui Paulo } 588*5b9c547cSRui Paulo os_free(buf); 589*5b9c547cSRui Paulo 590*5b9c547cSRui Paulo if (disassoc_timer) { 591*5b9c547cSRui Paulo /* send disassociation frame after time-out */ 592*5b9c547cSRui Paulo set_disassoc_timer(hapd, sta, disassoc_timer); 593*5b9c547cSRui Paulo } 594*5b9c547cSRui Paulo 595*5b9c547cSRui Paulo return 0; 596*5b9c547cSRui Paulo } 597