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