1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * wpa_supplicant - WNM 3e5b75505Sopenharmony_ci * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci */ 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ci#include "utils/includes.h" 10e5b75505Sopenharmony_ci 11e5b75505Sopenharmony_ci#include "utils/common.h" 12e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h" 13e5b75505Sopenharmony_ci#include "common/ieee802_11_common.h" 14e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h" 15e5b75505Sopenharmony_ci#include "common/ocv.h" 16e5b75505Sopenharmony_ci#include "rsn_supp/wpa.h" 17e5b75505Sopenharmony_ci#include "config.h" 18e5b75505Sopenharmony_ci#include "wpa_supplicant_i.h" 19e5b75505Sopenharmony_ci#include "driver_i.h" 20e5b75505Sopenharmony_ci#include "scan.h" 21e5b75505Sopenharmony_ci#include "ctrl_iface.h" 22e5b75505Sopenharmony_ci#include "bss.h" 23e5b75505Sopenharmony_ci#include "wnm_sta.h" 24e5b75505Sopenharmony_ci#include "notify.h" 25e5b75505Sopenharmony_ci#include "hs20_supplicant.h" 26e5b75505Sopenharmony_ci 27e5b75505Sopenharmony_ci#define MAX_TFS_IE_LEN 1024 28e5b75505Sopenharmony_ci#define WNM_MAX_NEIGHBOR_REPORT 10 29e5b75505Sopenharmony_ci 30e5b75505Sopenharmony_ci#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */ 31e5b75505Sopenharmony_ci 32e5b75505Sopenharmony_ci/* get the TFS IE from driver */ 33e5b75505Sopenharmony_cistatic int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, 34e5b75505Sopenharmony_ci u16 *buf_len, enum wnm_oper oper) 35e5b75505Sopenharmony_ci{ 36e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); 37e5b75505Sopenharmony_ci 38e5b75505Sopenharmony_ci return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); 39e5b75505Sopenharmony_ci} 40e5b75505Sopenharmony_ci 41e5b75505Sopenharmony_ci 42e5b75505Sopenharmony_ci/* set the TFS IE to driver */ 43e5b75505Sopenharmony_cistatic int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, 44e5b75505Sopenharmony_ci const u8 *addr, const u8 *buf, u16 buf_len, 45e5b75505Sopenharmony_ci enum wnm_oper oper) 46e5b75505Sopenharmony_ci{ 47e5b75505Sopenharmony_ci u16 len = buf_len; 48e5b75505Sopenharmony_ci 49e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); 50e5b75505Sopenharmony_ci 51e5b75505Sopenharmony_ci return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len); 52e5b75505Sopenharmony_ci} 53e5b75505Sopenharmony_ci 54e5b75505Sopenharmony_ci 55e5b75505Sopenharmony_ci/* MLME-SLEEPMODE.request */ 56e5b75505Sopenharmony_ciint ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, 57e5b75505Sopenharmony_ci u8 action, u16 intval, struct wpabuf *tfs_req) 58e5b75505Sopenharmony_ci{ 59e5b75505Sopenharmony_ci struct ieee80211_mgmt *mgmt; 60e5b75505Sopenharmony_ci int res; 61e5b75505Sopenharmony_ci size_t len; 62e5b75505Sopenharmony_ci struct wnm_sleep_element *wnmsleep_ie; 63e5b75505Sopenharmony_ci u8 *wnmtfs_ie, *oci_ie; 64e5b75505Sopenharmony_ci u8 wnmsleep_ie_len, oci_ie_len; 65e5b75505Sopenharmony_ci u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ 66e5b75505Sopenharmony_ci enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : 67e5b75505Sopenharmony_ci WNM_SLEEP_TFS_REQ_IE_NONE; 68e5b75505Sopenharmony_ci 69e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " 70e5b75505Sopenharmony_ci "action=%s to " MACSTR, 71e5b75505Sopenharmony_ci action == 0 ? "enter" : "exit", 72e5b75505Sopenharmony_ci MAC2STR(wpa_s->bssid)); 73e5b75505Sopenharmony_ci 74e5b75505Sopenharmony_ci /* WNM-Sleep Mode IE */ 75e5b75505Sopenharmony_ci wnmsleep_ie_len = sizeof(struct wnm_sleep_element); 76e5b75505Sopenharmony_ci wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); 77e5b75505Sopenharmony_ci if (wnmsleep_ie == NULL) 78e5b75505Sopenharmony_ci return -1; 79e5b75505Sopenharmony_ci wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; 80e5b75505Sopenharmony_ci wnmsleep_ie->len = wnmsleep_ie_len - 2; 81e5b75505Sopenharmony_ci wnmsleep_ie->action_type = action; 82e5b75505Sopenharmony_ci wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; 83e5b75505Sopenharmony_ci wnmsleep_ie->intval = host_to_le16(intval); 84e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", 85e5b75505Sopenharmony_ci (u8 *) wnmsleep_ie, wnmsleep_ie_len); 86e5b75505Sopenharmony_ci 87e5b75505Sopenharmony_ci /* TFS IE(s) */ 88e5b75505Sopenharmony_ci if (tfs_req) { 89e5b75505Sopenharmony_ci wnmtfs_ie_len = wpabuf_len(tfs_req); 90e5b75505Sopenharmony_ci wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len); 91e5b75505Sopenharmony_ci if (wnmtfs_ie == NULL) { 92e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 93e5b75505Sopenharmony_ci return -1; 94e5b75505Sopenharmony_ci } 95e5b75505Sopenharmony_ci } else { 96e5b75505Sopenharmony_ci wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); 97e5b75505Sopenharmony_ci if (wnmtfs_ie == NULL) { 98e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 99e5b75505Sopenharmony_ci return -1; 100e5b75505Sopenharmony_ci } 101e5b75505Sopenharmony_ci if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, 102e5b75505Sopenharmony_ci tfs_oper)) { 103e5b75505Sopenharmony_ci wnmtfs_ie_len = 0; 104e5b75505Sopenharmony_ci os_free(wnmtfs_ie); 105e5b75505Sopenharmony_ci wnmtfs_ie = NULL; 106e5b75505Sopenharmony_ci } 107e5b75505Sopenharmony_ci } 108e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", 109e5b75505Sopenharmony_ci (u8 *) wnmtfs_ie, wnmtfs_ie_len); 110e5b75505Sopenharmony_ci 111e5b75505Sopenharmony_ci oci_ie = NULL; 112e5b75505Sopenharmony_ci oci_ie_len = 0; 113e5b75505Sopenharmony_ci#ifdef CONFIG_OCV 114e5b75505Sopenharmony_ci if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) { 115e5b75505Sopenharmony_ci struct wpa_channel_info ci; 116e5b75505Sopenharmony_ci 117e5b75505Sopenharmony_ci if (wpa_drv_channel_info(wpa_s, &ci) != 0) { 118e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, 119e5b75505Sopenharmony_ci "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); 120e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 121e5b75505Sopenharmony_ci os_free(wnmtfs_ie); 122e5b75505Sopenharmony_ci return -1; 123e5b75505Sopenharmony_ci } 124e5b75505Sopenharmony_ci 125e5b75505Sopenharmony_ci oci_ie_len = OCV_OCI_EXTENDED_LEN; 126e5b75505Sopenharmony_ci oci_ie = os_zalloc(oci_ie_len); 127e5b75505Sopenharmony_ci if (!oci_ie) { 128e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, 129e5b75505Sopenharmony_ci "Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame"); 130e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 131e5b75505Sopenharmony_ci os_free(wnmtfs_ie); 132e5b75505Sopenharmony_ci return -1; 133e5b75505Sopenharmony_ci } 134e5b75505Sopenharmony_ci 135e5b75505Sopenharmony_ci if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { 136e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 137e5b75505Sopenharmony_ci os_free(wnmtfs_ie); 138e5b75505Sopenharmony_ci os_free(oci_ie); 139e5b75505Sopenharmony_ci return -1; 140e5b75505Sopenharmony_ci } 141e5b75505Sopenharmony_ci } 142e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */ 143e5b75505Sopenharmony_ci 144e5b75505Sopenharmony_ci mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len + 145e5b75505Sopenharmony_ci oci_ie_len); 146e5b75505Sopenharmony_ci if (mgmt == NULL) { 147e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 148e5b75505Sopenharmony_ci "WNM-Sleep Request action frame"); 149e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 150e5b75505Sopenharmony_ci os_free(wnmtfs_ie); 151e5b75505Sopenharmony_ci return -1; 152e5b75505Sopenharmony_ci } 153e5b75505Sopenharmony_ci 154e5b75505Sopenharmony_ci os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 155e5b75505Sopenharmony_ci os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 156e5b75505Sopenharmony_ci os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 157e5b75505Sopenharmony_ci mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 158e5b75505Sopenharmony_ci WLAN_FC_STYPE_ACTION); 159e5b75505Sopenharmony_ci mgmt->u.action.category = WLAN_ACTION_WNM; 160e5b75505Sopenharmony_ci mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; 161e5b75505Sopenharmony_ci mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; 162e5b75505Sopenharmony_ci os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, 163e5b75505Sopenharmony_ci wnmsleep_ie_len); 164e5b75505Sopenharmony_ci /* copy TFS IE here */ 165e5b75505Sopenharmony_ci if (wnmtfs_ie_len > 0) { 166e5b75505Sopenharmony_ci os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + 167e5b75505Sopenharmony_ci wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); 168e5b75505Sopenharmony_ci } 169e5b75505Sopenharmony_ci 170e5b75505Sopenharmony_ci#ifdef CONFIG_OCV 171e5b75505Sopenharmony_ci /* copy OCV OCI here */ 172e5b75505Sopenharmony_ci if (oci_ie_len > 0) { 173e5b75505Sopenharmony_ci os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + 174e5b75505Sopenharmony_ci wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len); 175e5b75505Sopenharmony_ci } 176e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */ 177e5b75505Sopenharmony_ci 178e5b75505Sopenharmony_ci len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + 179e5b75505Sopenharmony_ci wnmtfs_ie_len + oci_ie_len; 180e5b75505Sopenharmony_ci 181e5b75505Sopenharmony_ci res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 182e5b75505Sopenharmony_ci wpa_s->own_addr, wpa_s->bssid, 183e5b75505Sopenharmony_ci &mgmt->u.action.category, len, 0); 184e5b75505Sopenharmony_ci if (res < 0) 185e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " 186e5b75505Sopenharmony_ci "(action=%d, intval=%d)", action, intval); 187e5b75505Sopenharmony_ci else 188e5b75505Sopenharmony_ci wpa_s->wnmsleep_used = 1; 189e5b75505Sopenharmony_ci 190e5b75505Sopenharmony_ci os_free(wnmsleep_ie); 191e5b75505Sopenharmony_ci os_free(wnmtfs_ie); 192e5b75505Sopenharmony_ci os_free(oci_ie); 193e5b75505Sopenharmony_ci os_free(mgmt); 194e5b75505Sopenharmony_ci 195e5b75505Sopenharmony_ci return res; 196e5b75505Sopenharmony_ci} 197e5b75505Sopenharmony_ci 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_cistatic void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, 200e5b75505Sopenharmony_ci const u8 *tfsresp_ie_start, 201e5b75505Sopenharmony_ci const u8 *tfsresp_ie_end) 202e5b75505Sopenharmony_ci{ 203e5b75505Sopenharmony_ci wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, 204e5b75505Sopenharmony_ci wpa_s->bssid, NULL, NULL); 205e5b75505Sopenharmony_ci /* remove GTK/IGTK ?? */ 206e5b75505Sopenharmony_ci 207e5b75505Sopenharmony_ci /* set the TFS Resp IE(s) */ 208e5b75505Sopenharmony_ci if (tfsresp_ie_start && tfsresp_ie_end && 209e5b75505Sopenharmony_ci tfsresp_ie_end - tfsresp_ie_start >= 0) { 210e5b75505Sopenharmony_ci u16 tfsresp_ie_len; 211e5b75505Sopenharmony_ci tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - 212e5b75505Sopenharmony_ci tfsresp_ie_start; 213e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); 214e5b75505Sopenharmony_ci /* pass the TFS Resp IE(s) to driver for processing */ 215e5b75505Sopenharmony_ci if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, 216e5b75505Sopenharmony_ci tfsresp_ie_start, 217e5b75505Sopenharmony_ci tfsresp_ie_len, 218e5b75505Sopenharmony_ci WNM_SLEEP_TFS_RESP_IE_SET)) 219e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); 220e5b75505Sopenharmony_ci } 221e5b75505Sopenharmony_ci} 222e5b75505Sopenharmony_ci 223e5b75505Sopenharmony_ci 224e5b75505Sopenharmony_cistatic void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, 225e5b75505Sopenharmony_ci const u8 *frm, u16 key_len_total) 226e5b75505Sopenharmony_ci{ 227e5b75505Sopenharmony_ci u8 *ptr, *end; 228e5b75505Sopenharmony_ci u8 gtk_len; 229e5b75505Sopenharmony_ci 230e5b75505Sopenharmony_ci wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, 231e5b75505Sopenharmony_ci NULL, NULL); 232e5b75505Sopenharmony_ci 233e5b75505Sopenharmony_ci /* Install GTK/IGTK */ 234e5b75505Sopenharmony_ci 235e5b75505Sopenharmony_ci /* point to key data field */ 236e5b75505Sopenharmony_ci ptr = (u8 *) frm + 1 + 2; 237e5b75505Sopenharmony_ci end = ptr + key_len_total; 238e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); 239e5b75505Sopenharmony_ci 240e5b75505Sopenharmony_ci if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { 241e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_INFO, 242e5b75505Sopenharmony_ci "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); 243e5b75505Sopenharmony_ci return; 244e5b75505Sopenharmony_ci } 245e5b75505Sopenharmony_ci 246e5b75505Sopenharmony_ci while (end - ptr > 1) { 247e5b75505Sopenharmony_ci if (2 + ptr[1] > end - ptr) { 248e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " 249e5b75505Sopenharmony_ci "length"); 250e5b75505Sopenharmony_ci if (end > ptr) { 251e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", 252e5b75505Sopenharmony_ci ptr, end - ptr); 253e5b75505Sopenharmony_ci } 254e5b75505Sopenharmony_ci break; 255e5b75505Sopenharmony_ci } 256e5b75505Sopenharmony_ci if (*ptr == WNM_SLEEP_SUBELEM_GTK) { 257e5b75505Sopenharmony_ci if (ptr[1] < 11 + 5) { 258e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short GTK " 259e5b75505Sopenharmony_ci "subelem"); 260e5b75505Sopenharmony_ci break; 261e5b75505Sopenharmony_ci } 262e5b75505Sopenharmony_ci gtk_len = *(ptr + 4); 263e5b75505Sopenharmony_ci if (ptr[1] < 11 + gtk_len || 264e5b75505Sopenharmony_ci gtk_len < 5 || gtk_len > 32) { 265e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " 266e5b75505Sopenharmony_ci "subelem"); 267e5b75505Sopenharmony_ci break; 268e5b75505Sopenharmony_ci } 269e5b75505Sopenharmony_ci wpa_wnmsleep_install_key( 270e5b75505Sopenharmony_ci wpa_s->wpa, 271e5b75505Sopenharmony_ci WNM_SLEEP_SUBELEM_GTK, 272e5b75505Sopenharmony_ci ptr); 273e5b75505Sopenharmony_ci ptr += 13 + gtk_len; 274e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211W 275e5b75505Sopenharmony_ci } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { 276e5b75505Sopenharmony_ci if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { 277e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " 278e5b75505Sopenharmony_ci "subelem"); 279e5b75505Sopenharmony_ci break; 280e5b75505Sopenharmony_ci } 281e5b75505Sopenharmony_ci wpa_wnmsleep_install_key(wpa_s->wpa, 282e5b75505Sopenharmony_ci WNM_SLEEP_SUBELEM_IGTK, ptr); 283e5b75505Sopenharmony_ci ptr += 10 + WPA_IGTK_LEN; 284e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211W */ 285e5b75505Sopenharmony_ci } else 286e5b75505Sopenharmony_ci break; /* skip the loop */ 287e5b75505Sopenharmony_ci } 288e5b75505Sopenharmony_ci} 289e5b75505Sopenharmony_ci 290e5b75505Sopenharmony_ci 291e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, 292e5b75505Sopenharmony_ci const u8 *frm, int len) 293e5b75505Sopenharmony_ci{ 294e5b75505Sopenharmony_ci /* 295e5b75505Sopenharmony_ci * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | 296e5b75505Sopenharmony_ci * WNM-Sleep Mode IE | TFS Response IE 297e5b75505Sopenharmony_ci */ 298e5b75505Sopenharmony_ci const u8 *pos = frm; /* point to payload after the action field */ 299e5b75505Sopenharmony_ci u16 key_len_total; 300e5b75505Sopenharmony_ci struct wnm_sleep_element *wnmsleep_ie = NULL; 301e5b75505Sopenharmony_ci /* multiple TFS Resp IE (assuming consecutive) */ 302e5b75505Sopenharmony_ci const u8 *tfsresp_ie_start = NULL; 303e5b75505Sopenharmony_ci const u8 *tfsresp_ie_end = NULL; 304e5b75505Sopenharmony_ci#ifdef CONFIG_OCV 305e5b75505Sopenharmony_ci const u8 *oci_ie = NULL; 306e5b75505Sopenharmony_ci u8 oci_ie_len = 0; 307e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */ 308e5b75505Sopenharmony_ci size_t left; 309e5b75505Sopenharmony_ci 310e5b75505Sopenharmony_ci if (!wpa_s->wnmsleep_used) { 311e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 312e5b75505Sopenharmony_ci "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); 313e5b75505Sopenharmony_ci return; 314e5b75505Sopenharmony_ci } 315e5b75505Sopenharmony_ci 316e5b75505Sopenharmony_ci if (len < 3) 317e5b75505Sopenharmony_ci return; 318e5b75505Sopenharmony_ci key_len_total = WPA_GET_LE16(frm + 1); 319e5b75505Sopenharmony_ci 320e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", 321e5b75505Sopenharmony_ci frm[0], key_len_total); 322e5b75505Sopenharmony_ci left = len - 3; 323e5b75505Sopenharmony_ci if (key_len_total > left) { 324e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); 325e5b75505Sopenharmony_ci return; 326e5b75505Sopenharmony_ci } 327e5b75505Sopenharmony_ci pos += 3 + key_len_total; 328e5b75505Sopenharmony_ci while (pos - frm + 1 < len) { 329e5b75505Sopenharmony_ci u8 ie_len = *(pos + 1); 330e5b75505Sopenharmony_ci if (2 + ie_len > frm + len - pos) { 331e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); 332e5b75505Sopenharmony_ci break; 333e5b75505Sopenharmony_ci } 334e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); 335e5b75505Sopenharmony_ci if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4) 336e5b75505Sopenharmony_ci wnmsleep_ie = (struct wnm_sleep_element *) pos; 337e5b75505Sopenharmony_ci else if (*pos == WLAN_EID_TFS_RESP) { 338e5b75505Sopenharmony_ci if (!tfsresp_ie_start) 339e5b75505Sopenharmony_ci tfsresp_ie_start = pos; 340e5b75505Sopenharmony_ci tfsresp_ie_end = pos; 341e5b75505Sopenharmony_ci#ifdef CONFIG_OCV 342e5b75505Sopenharmony_ci } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && 343e5b75505Sopenharmony_ci pos[2] == WLAN_EID_EXT_OCV_OCI) { 344e5b75505Sopenharmony_ci oci_ie = pos + 3; 345e5b75505Sopenharmony_ci oci_ie_len = ie_len - 1; 346e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */ 347e5b75505Sopenharmony_ci } else 348e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); 349e5b75505Sopenharmony_ci pos += ie_len + 2; 350e5b75505Sopenharmony_ci } 351e5b75505Sopenharmony_ci 352e5b75505Sopenharmony_ci if (!wnmsleep_ie) { 353e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 354e5b75505Sopenharmony_ci return; 355e5b75505Sopenharmony_ci } 356e5b75505Sopenharmony_ci 357e5b75505Sopenharmony_ci#ifdef CONFIG_OCV 358e5b75505Sopenharmony_ci if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && 359e5b75505Sopenharmony_ci wpa_sm_ocv_enabled(wpa_s->wpa)) { 360e5b75505Sopenharmony_ci struct wpa_channel_info ci; 361e5b75505Sopenharmony_ci 362e5b75505Sopenharmony_ci if (wpa_drv_channel_info(wpa_s, &ci) != 0) { 363e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_WARNING, 364e5b75505Sopenharmony_ci "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); 365e5b75505Sopenharmony_ci return; 366e5b75505Sopenharmony_ci } 367e5b75505Sopenharmony_ci 368e5b75505Sopenharmony_ci if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, 369e5b75505Sopenharmony_ci channel_width_to_int(ci.chanwidth), 370e5b75505Sopenharmony_ci ci.seg1_idx) != 0) { 371e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_WARNING, "WNM: %s", ocv_errorstr); 372e5b75505Sopenharmony_ci return; 373e5b75505Sopenharmony_ci } 374e5b75505Sopenharmony_ci } 375e5b75505Sopenharmony_ci#endif /* CONFIG_OCV */ 376e5b75505Sopenharmony_ci 377e5b75505Sopenharmony_ci wpa_s->wnmsleep_used = 0; 378e5b75505Sopenharmony_ci 379e5b75505Sopenharmony_ci if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || 380e5b75505Sopenharmony_ci wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { 381e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " 382e5b75505Sopenharmony_ci "frame (action=%d, intval=%d)", 383e5b75505Sopenharmony_ci wnmsleep_ie->action_type, wnmsleep_ie->intval); 384e5b75505Sopenharmony_ci if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { 385e5b75505Sopenharmony_ci wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, 386e5b75505Sopenharmony_ci tfsresp_ie_end); 387e5b75505Sopenharmony_ci } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 388e5b75505Sopenharmony_ci wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); 389e5b75505Sopenharmony_ci } 390e5b75505Sopenharmony_ci } else { 391e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " 392e5b75505Sopenharmony_ci "(action=%d, intval=%d)", 393e5b75505Sopenharmony_ci wnmsleep_ie->action_type, wnmsleep_ie->intval); 394e5b75505Sopenharmony_ci if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) 395e5b75505Sopenharmony_ci wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, 396e5b75505Sopenharmony_ci wpa_s->bssid, NULL, NULL); 397e5b75505Sopenharmony_ci else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) 398e5b75505Sopenharmony_ci wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, 399e5b75505Sopenharmony_ci wpa_s->bssid, NULL, NULL); 400e5b75505Sopenharmony_ci } 401e5b75505Sopenharmony_ci} 402e5b75505Sopenharmony_ci 403e5b75505Sopenharmony_ci 404e5b75505Sopenharmony_civoid wnm_deallocate_memory(struct wpa_supplicant *wpa_s) 405e5b75505Sopenharmony_ci{ 406e5b75505Sopenharmony_ci int i; 407e5b75505Sopenharmony_ci 408e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 409e5b75505Sopenharmony_ci os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); 410e5b75505Sopenharmony_ci os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); 411e5b75505Sopenharmony_ci } 412e5b75505Sopenharmony_ci 413e5b75505Sopenharmony_ci wpa_s->wnm_num_neighbor_report = 0; 414e5b75505Sopenharmony_ci os_free(wpa_s->wnm_neighbor_report_elements); 415e5b75505Sopenharmony_ci wpa_s->wnm_neighbor_report_elements = NULL; 416e5b75505Sopenharmony_ci 417e5b75505Sopenharmony_ci wpabuf_free(wpa_s->coloc_intf_elems); 418e5b75505Sopenharmony_ci wpa_s->coloc_intf_elems = NULL; 419e5b75505Sopenharmony_ci} 420e5b75505Sopenharmony_ci 421e5b75505Sopenharmony_ci 422e5b75505Sopenharmony_cistatic void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, 423e5b75505Sopenharmony_ci u8 id, u8 elen, const u8 *pos) 424e5b75505Sopenharmony_ci{ 425e5b75505Sopenharmony_ci switch (id) { 426e5b75505Sopenharmony_ci case WNM_NEIGHBOR_TSF: 427e5b75505Sopenharmony_ci if (elen < 2 + 2) { 428e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); 429e5b75505Sopenharmony_ci break; 430e5b75505Sopenharmony_ci } 431e5b75505Sopenharmony_ci rep->tsf_offset = WPA_GET_LE16(pos); 432e5b75505Sopenharmony_ci rep->beacon_int = WPA_GET_LE16(pos + 2); 433e5b75505Sopenharmony_ci rep->tsf_present = 1; 434e5b75505Sopenharmony_ci break; 435e5b75505Sopenharmony_ci case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: 436e5b75505Sopenharmony_ci if (elen < 2) { 437e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short condensed " 438e5b75505Sopenharmony_ci "country string"); 439e5b75505Sopenharmony_ci break; 440e5b75505Sopenharmony_ci } 441e5b75505Sopenharmony_ci os_memcpy(rep->country, pos, 2); 442e5b75505Sopenharmony_ci rep->country_present = 1; 443e5b75505Sopenharmony_ci break; 444e5b75505Sopenharmony_ci case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: 445e5b75505Sopenharmony_ci if (elen < 1) { 446e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " 447e5b75505Sopenharmony_ci "candidate"); 448e5b75505Sopenharmony_ci break; 449e5b75505Sopenharmony_ci } 450e5b75505Sopenharmony_ci rep->preference = pos[0]; 451e5b75505Sopenharmony_ci rep->preference_present = 1; 452e5b75505Sopenharmony_ci break; 453e5b75505Sopenharmony_ci case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: 454e5b75505Sopenharmony_ci if (elen < 10) { 455e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 456e5b75505Sopenharmony_ci "WNM: Too short BSS termination duration"); 457e5b75505Sopenharmony_ci break; 458e5b75505Sopenharmony_ci } 459e5b75505Sopenharmony_ci rep->bss_term_tsf = WPA_GET_LE64(pos); 460e5b75505Sopenharmony_ci rep->bss_term_dur = WPA_GET_LE16(pos + 8); 461e5b75505Sopenharmony_ci rep->bss_term_present = 1; 462e5b75505Sopenharmony_ci break; 463e5b75505Sopenharmony_ci case WNM_NEIGHBOR_BEARING: 464e5b75505Sopenharmony_ci if (elen < 8) { 465e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " 466e5b75505Sopenharmony_ci "bearing"); 467e5b75505Sopenharmony_ci break; 468e5b75505Sopenharmony_ci } 469e5b75505Sopenharmony_ci rep->bearing = WPA_GET_LE16(pos); 470e5b75505Sopenharmony_ci rep->distance = WPA_GET_LE32(pos + 2); 471e5b75505Sopenharmony_ci rep->rel_height = WPA_GET_LE16(pos + 2 + 4); 472e5b75505Sopenharmony_ci rep->bearing_present = 1; 473e5b75505Sopenharmony_ci break; 474e5b75505Sopenharmony_ci case WNM_NEIGHBOR_MEASUREMENT_PILOT: 475e5b75505Sopenharmony_ci if (elen < 1) { 476e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short measurement " 477e5b75505Sopenharmony_ci "pilot"); 478e5b75505Sopenharmony_ci break; 479e5b75505Sopenharmony_ci } 480e5b75505Sopenharmony_ci os_free(rep->meas_pilot); 481e5b75505Sopenharmony_ci rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); 482e5b75505Sopenharmony_ci if (rep->meas_pilot == NULL) 483e5b75505Sopenharmony_ci break; 484e5b75505Sopenharmony_ci rep->meas_pilot->measurement_pilot = pos[0]; 485e5b75505Sopenharmony_ci rep->meas_pilot->subelem_len = elen - 1; 486e5b75505Sopenharmony_ci os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); 487e5b75505Sopenharmony_ci break; 488e5b75505Sopenharmony_ci case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: 489e5b75505Sopenharmony_ci if (elen < 5) { 490e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " 491e5b75505Sopenharmony_ci "capabilities"); 492e5b75505Sopenharmony_ci break; 493e5b75505Sopenharmony_ci } 494e5b75505Sopenharmony_ci os_memcpy(rep->rm_capab, pos, 5); 495e5b75505Sopenharmony_ci rep->rm_capab_present = 1; 496e5b75505Sopenharmony_ci break; 497e5b75505Sopenharmony_ci case WNM_NEIGHBOR_MULTIPLE_BSSID: 498e5b75505Sopenharmony_ci if (elen < 1) { 499e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); 500e5b75505Sopenharmony_ci break; 501e5b75505Sopenharmony_ci } 502e5b75505Sopenharmony_ci os_free(rep->mul_bssid); 503e5b75505Sopenharmony_ci rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); 504e5b75505Sopenharmony_ci if (rep->mul_bssid == NULL) 505e5b75505Sopenharmony_ci break; 506e5b75505Sopenharmony_ci rep->mul_bssid->max_bssid_indicator = pos[0]; 507e5b75505Sopenharmony_ci rep->mul_bssid->subelem_len = elen - 1; 508e5b75505Sopenharmony_ci os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); 509e5b75505Sopenharmony_ci break; 510e5b75505Sopenharmony_ci } 511e5b75505Sopenharmony_ci} 512e5b75505Sopenharmony_ci 513e5b75505Sopenharmony_ci 514e5b75505Sopenharmony_cistatic int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) 515e5b75505Sopenharmony_ci{ 516e5b75505Sopenharmony_ci struct wpa_bss *bss = wpa_s->current_bss; 517e5b75505Sopenharmony_ci const char *country = NULL; 518e5b75505Sopenharmony_ci int freq; 519e5b75505Sopenharmony_ci 520e5b75505Sopenharmony_ci if (bss) { 521e5b75505Sopenharmony_ci const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); 522e5b75505Sopenharmony_ci 523e5b75505Sopenharmony_ci if (elem && elem[1] >= 2) 524e5b75505Sopenharmony_ci country = (const char *) (elem + 2); 525e5b75505Sopenharmony_ci } 526e5b75505Sopenharmony_ci 527e5b75505Sopenharmony_ci freq = ieee80211_chan_to_freq(country, op_class, chan); 528e5b75505Sopenharmony_ci if (freq <= 0 && op_class == 0) { 529e5b75505Sopenharmony_ci /* 530e5b75505Sopenharmony_ci * Some APs do not advertise correct operating class 531e5b75505Sopenharmony_ci * information. Try to determine the most likely operating 532e5b75505Sopenharmony_ci * frequency based on the channel number. 533e5b75505Sopenharmony_ci */ 534e5b75505Sopenharmony_ci if (chan >= 1 && chan <= 13) 535e5b75505Sopenharmony_ci freq = 2407 + chan * 5; 536e5b75505Sopenharmony_ci else if (chan == 14) 537e5b75505Sopenharmony_ci freq = 2484; 538e5b75505Sopenharmony_ci else if (chan >= 36 && chan <= 169) 539e5b75505Sopenharmony_ci freq = 5000 + chan * 5; 540e5b75505Sopenharmony_ci } 541e5b75505Sopenharmony_ci return freq; 542e5b75505Sopenharmony_ci} 543e5b75505Sopenharmony_ci 544e5b75505Sopenharmony_ci 545e5b75505Sopenharmony_cistatic void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, 546e5b75505Sopenharmony_ci const u8 *pos, u8 len, 547e5b75505Sopenharmony_ci struct neighbor_report *rep) 548e5b75505Sopenharmony_ci{ 549e5b75505Sopenharmony_ci u8 left = len; 550e5b75505Sopenharmony_ci 551e5b75505Sopenharmony_ci if (left < 13) { 552e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); 553e5b75505Sopenharmony_ci return; 554e5b75505Sopenharmony_ci } 555e5b75505Sopenharmony_ci 556e5b75505Sopenharmony_ci os_memcpy(rep->bssid, pos, ETH_ALEN); 557e5b75505Sopenharmony_ci rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); 558e5b75505Sopenharmony_ci rep->regulatory_class = *(pos + 10); 559e5b75505Sopenharmony_ci rep->channel_number = *(pos + 11); 560e5b75505Sopenharmony_ci rep->phy_type = *(pos + 12); 561e5b75505Sopenharmony_ci 562e5b75505Sopenharmony_ci pos += 13; 563e5b75505Sopenharmony_ci left -= 13; 564e5b75505Sopenharmony_ci 565e5b75505Sopenharmony_ci while (left >= 2) { 566e5b75505Sopenharmony_ci u8 id, elen; 567e5b75505Sopenharmony_ci 568e5b75505Sopenharmony_ci id = *pos++; 569e5b75505Sopenharmony_ci elen = *pos++; 570e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); 571e5b75505Sopenharmony_ci left -= 2; 572e5b75505Sopenharmony_ci if (elen > left) { 573e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 574e5b75505Sopenharmony_ci "WNM: Truncated neighbor report subelement"); 575e5b75505Sopenharmony_ci break; 576e5b75505Sopenharmony_ci } 577e5b75505Sopenharmony_ci wnm_parse_neighbor_report_elem(rep, id, elen, pos); 578e5b75505Sopenharmony_ci left -= elen; 579e5b75505Sopenharmony_ci pos += elen; 580e5b75505Sopenharmony_ci } 581e5b75505Sopenharmony_ci 582e5b75505Sopenharmony_ci rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, 583e5b75505Sopenharmony_ci rep->channel_number); 584e5b75505Sopenharmony_ci} 585e5b75505Sopenharmony_ci 586e5b75505Sopenharmony_ci 587e5b75505Sopenharmony_cistatic void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) 588e5b75505Sopenharmony_ci{ 589e5b75505Sopenharmony_ci unsigned int i; 590e5b75505Sopenharmony_ci 591e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) 592e5b75505Sopenharmony_ci wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; 593e5b75505Sopenharmony_ci} 594e5b75505Sopenharmony_ci 595e5b75505Sopenharmony_ci 596e5b75505Sopenharmony_cistatic struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s) 597e5b75505Sopenharmony_ci{ 598e5b75505Sopenharmony_ci unsigned int i; 599e5b75505Sopenharmony_ci struct neighbor_report *nei; 600e5b75505Sopenharmony_ci 601e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 602e5b75505Sopenharmony_ci nei = &wpa_s->wnm_neighbor_report_elements[i]; 603e5b75505Sopenharmony_ci if (nei->acceptable) 604e5b75505Sopenharmony_ci return wpa_bss_get_bssid(wpa_s, nei->bssid); 605e5b75505Sopenharmony_ci } 606e5b75505Sopenharmony_ci 607e5b75505Sopenharmony_ci return NULL; 608e5b75505Sopenharmony_ci} 609e5b75505Sopenharmony_ci 610e5b75505Sopenharmony_ci 611e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 612e5b75505Sopenharmony_cistatic struct wpa_bss * 613e5b75505Sopenharmony_ciget_mbo_transition_candidate(struct wpa_supplicant *wpa_s, 614e5b75505Sopenharmony_ci enum mbo_transition_reject_reason *reason) 615e5b75505Sopenharmony_ci{ 616e5b75505Sopenharmony_ci struct wpa_bss *target = NULL; 617e5b75505Sopenharmony_ci struct wpa_bss_trans_info params; 618e5b75505Sopenharmony_ci struct wpa_bss_candidate_info *info = NULL; 619e5b75505Sopenharmony_ci struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; 620e5b75505Sopenharmony_ci u8 *first_candidate_bssid = NULL, *pos; 621e5b75505Sopenharmony_ci unsigned int i; 622e5b75505Sopenharmony_ci 623e5b75505Sopenharmony_ci params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; 624e5b75505Sopenharmony_ci params.n_candidates = 0; 625e5b75505Sopenharmony_ci params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); 626e5b75505Sopenharmony_ci if (!params.bssid) 627e5b75505Sopenharmony_ci return NULL; 628e5b75505Sopenharmony_ci 629e5b75505Sopenharmony_ci pos = params.bssid; 630e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { 631e5b75505Sopenharmony_ci if (nei->is_first) 632e5b75505Sopenharmony_ci first_candidate_bssid = nei->bssid; 633e5b75505Sopenharmony_ci if (!nei->acceptable) 634e5b75505Sopenharmony_ci continue; 635e5b75505Sopenharmony_ci os_memcpy(pos, nei->bssid, ETH_ALEN); 636e5b75505Sopenharmony_ci pos += ETH_ALEN; 637e5b75505Sopenharmony_ci params.n_candidates++; 638e5b75505Sopenharmony_ci } 639e5b75505Sopenharmony_ci 640e5b75505Sopenharmony_ci if (!params.n_candidates) 641e5b75505Sopenharmony_ci goto end; 642e5b75505Sopenharmony_ci 643e5b75505Sopenharmony_ci info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); 644e5b75505Sopenharmony_ci if (!info) { 645e5b75505Sopenharmony_ci /* If failed to get candidate BSS transition status from driver, 646e5b75505Sopenharmony_ci * get the first acceptable candidate from wpa_supplicant. 647e5b75505Sopenharmony_ci */ 648e5b75505Sopenharmony_ci target = wpa_bss_get_bssid(wpa_s, params.bssid); 649e5b75505Sopenharmony_ci goto end; 650e5b75505Sopenharmony_ci } 651e5b75505Sopenharmony_ci 652e5b75505Sopenharmony_ci /* Get the first acceptable candidate from driver */ 653e5b75505Sopenharmony_ci for (i = 0; i < info->num; i++) { 654e5b75505Sopenharmony_ci if (info->candidates[i].is_accept) { 655e5b75505Sopenharmony_ci target = wpa_bss_get_bssid(wpa_s, 656e5b75505Sopenharmony_ci info->candidates[i].bssid); 657e5b75505Sopenharmony_ci goto end; 658e5b75505Sopenharmony_ci } 659e5b75505Sopenharmony_ci } 660e5b75505Sopenharmony_ci 661e5b75505Sopenharmony_ci /* If Disassociation Imminent is set and driver rejects all the 662e5b75505Sopenharmony_ci * candidate select first acceptable candidate which has 663e5b75505Sopenharmony_ci * rssi > disassoc_imminent_rssi_threshold 664e5b75505Sopenharmony_ci */ 665e5b75505Sopenharmony_ci if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 666e5b75505Sopenharmony_ci for (i = 0; i < info->num; i++) { 667e5b75505Sopenharmony_ci target = wpa_bss_get_bssid(wpa_s, 668e5b75505Sopenharmony_ci info->candidates[i].bssid); 669e5b75505Sopenharmony_ci if (target && 670e5b75505Sopenharmony_ci (target->level < 671e5b75505Sopenharmony_ci wpa_s->conf->disassoc_imminent_rssi_threshold)) 672e5b75505Sopenharmony_ci continue; 673e5b75505Sopenharmony_ci goto end; 674e5b75505Sopenharmony_ci } 675e5b75505Sopenharmony_ci } 676e5b75505Sopenharmony_ci 677e5b75505Sopenharmony_ci /* While sending BTM reject use reason code of the first candidate 678e5b75505Sopenharmony_ci * received in BTM request frame 679e5b75505Sopenharmony_ci */ 680e5b75505Sopenharmony_ci if (reason) { 681e5b75505Sopenharmony_ci for (i = 0; i < info->num; i++) { 682e5b75505Sopenharmony_ci if (first_candidate_bssid && 683e5b75505Sopenharmony_ci os_memcmp(first_candidate_bssid, 684e5b75505Sopenharmony_ci info->candidates[i].bssid, ETH_ALEN) == 0) 685e5b75505Sopenharmony_ci { 686e5b75505Sopenharmony_ci *reason = info->candidates[i].reject_reason; 687e5b75505Sopenharmony_ci break; 688e5b75505Sopenharmony_ci } 689e5b75505Sopenharmony_ci } 690e5b75505Sopenharmony_ci } 691e5b75505Sopenharmony_ci 692e5b75505Sopenharmony_ci target = NULL; 693e5b75505Sopenharmony_ci 694e5b75505Sopenharmony_ciend: 695e5b75505Sopenharmony_ci os_free(params.bssid); 696e5b75505Sopenharmony_ci if (info) { 697e5b75505Sopenharmony_ci os_free(info->candidates); 698e5b75505Sopenharmony_ci os_free(info); 699e5b75505Sopenharmony_ci } 700e5b75505Sopenharmony_ci return target; 701e5b75505Sopenharmony_ci} 702e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 703e5b75505Sopenharmony_ci 704e5b75505Sopenharmony_ci 705e5b75505Sopenharmony_cistatic struct wpa_bss * 706e5b75505Sopenharmony_cicompare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, 707e5b75505Sopenharmony_ci enum mbo_transition_reject_reason *reason) 708e5b75505Sopenharmony_ci{ 709e5b75505Sopenharmony_ci u8 i; 710e5b75505Sopenharmony_ci struct wpa_bss *bss = wpa_s->current_bss; 711e5b75505Sopenharmony_ci struct wpa_bss *target; 712e5b75505Sopenharmony_ci 713e5b75505Sopenharmony_ci if (!bss) 714e5b75505Sopenharmony_ci return NULL; 715e5b75505Sopenharmony_ci 716e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", 717e5b75505Sopenharmony_ci MAC2STR(wpa_s->bssid), bss->level); 718e5b75505Sopenharmony_ci 719e5b75505Sopenharmony_ci wnm_clear_acceptable(wpa_s); 720e5b75505Sopenharmony_ci 721e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 722e5b75505Sopenharmony_ci struct neighbor_report *nei; 723e5b75505Sopenharmony_ci 724e5b75505Sopenharmony_ci nei = &wpa_s->wnm_neighbor_report_elements[i]; 725e5b75505Sopenharmony_ci if (nei->preference_present && nei->preference == 0) { 726e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, 727e5b75505Sopenharmony_ci MAC2STR(nei->bssid)); 728e5b75505Sopenharmony_ci continue; 729e5b75505Sopenharmony_ci } 730e5b75505Sopenharmony_ci 731e5b75505Sopenharmony_ci target = wpa_bss_get_bssid(wpa_s, nei->bssid); 732e5b75505Sopenharmony_ci if (!target) { 733e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 734e5b75505Sopenharmony_ci " (pref %d) not found in scan results", 735e5b75505Sopenharmony_ci MAC2STR(nei->bssid), 736e5b75505Sopenharmony_ci nei->preference_present ? nei->preference : 737e5b75505Sopenharmony_ci -1); 738e5b75505Sopenharmony_ci continue; 739e5b75505Sopenharmony_ci } 740e5b75505Sopenharmony_ci 741e5b75505Sopenharmony_ci if (age_secs) { 742e5b75505Sopenharmony_ci struct os_reltime now; 743e5b75505Sopenharmony_ci 744e5b75505Sopenharmony_ci if (os_get_reltime(&now) == 0 && 745e5b75505Sopenharmony_ci os_reltime_expired(&now, &target->last_update, 746e5b75505Sopenharmony_ci age_secs)) { 747e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 748e5b75505Sopenharmony_ci "Candidate BSS is more than %ld seconds old", 749e5b75505Sopenharmony_ci age_secs); 750e5b75505Sopenharmony_ci continue; 751e5b75505Sopenharmony_ci } 752e5b75505Sopenharmony_ci } 753e5b75505Sopenharmony_ci 754e5b75505Sopenharmony_ci if (bss->ssid_len != target->ssid_len || 755e5b75505Sopenharmony_ci os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { 756e5b75505Sopenharmony_ci /* 757e5b75505Sopenharmony_ci * TODO: Could consider allowing transition to another 758e5b75505Sopenharmony_ci * ESS if PMF was enabled for the association. 759e5b75505Sopenharmony_ci */ 760e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 761e5b75505Sopenharmony_ci " (pref %d) in different ESS", 762e5b75505Sopenharmony_ci MAC2STR(nei->bssid), 763e5b75505Sopenharmony_ci nei->preference_present ? nei->preference : 764e5b75505Sopenharmony_ci -1); 765e5b75505Sopenharmony_ci continue; 766e5b75505Sopenharmony_ci } 767e5b75505Sopenharmony_ci 768e5b75505Sopenharmony_ci if (wpa_s->current_ssid && 769e5b75505Sopenharmony_ci !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, 770e5b75505Sopenharmony_ci 1, 0)) { 771e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 772e5b75505Sopenharmony_ci " (pref %d) does not match the current network profile", 773e5b75505Sopenharmony_ci MAC2STR(nei->bssid), 774e5b75505Sopenharmony_ci nei->preference_present ? nei->preference : 775e5b75505Sopenharmony_ci -1); 776e5b75505Sopenharmony_ci continue; 777e5b75505Sopenharmony_ci } 778e5b75505Sopenharmony_ci 779e5b75505Sopenharmony_ci if (wpa_is_bss_tmp_disallowed(wpa_s, target)) { 780e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 781e5b75505Sopenharmony_ci "MBO: Candidate BSS " MACSTR 782e5b75505Sopenharmony_ci " retry delay is not over yet", 783e5b75505Sopenharmony_ci MAC2STR(nei->bssid)); 784e5b75505Sopenharmony_ci continue; 785e5b75505Sopenharmony_ci } 786e5b75505Sopenharmony_ci 787e5b75505Sopenharmony_ci if (target->level < bss->level && target->level < -80) { 788e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 789e5b75505Sopenharmony_ci " (pref %d) does not have sufficient signal level (%d)", 790e5b75505Sopenharmony_ci MAC2STR(nei->bssid), 791e5b75505Sopenharmony_ci nei->preference_present ? nei->preference : 792e5b75505Sopenharmony_ci -1, 793e5b75505Sopenharmony_ci target->level); 794e5b75505Sopenharmony_ci continue; 795e5b75505Sopenharmony_ci } 796e5b75505Sopenharmony_ci 797e5b75505Sopenharmony_ci nei->acceptable = 1; 798e5b75505Sopenharmony_ci } 799e5b75505Sopenharmony_ci 800e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 801e5b75505Sopenharmony_ci if (wpa_s->wnm_mbo_trans_reason_present) 802e5b75505Sopenharmony_ci target = get_mbo_transition_candidate(wpa_s, reason); 803e5b75505Sopenharmony_ci else 804e5b75505Sopenharmony_ci target = get_first_acceptable(wpa_s); 805e5b75505Sopenharmony_ci#else /* CONFIG_MBO */ 806e5b75505Sopenharmony_ci target = get_first_acceptable(wpa_s); 807e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 808e5b75505Sopenharmony_ci 809e5b75505Sopenharmony_ci if (target) { 810e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 811e5b75505Sopenharmony_ci "WNM: Found an acceptable preferred transition candidate BSS " 812e5b75505Sopenharmony_ci MACSTR " (RSSI %d)", 813e5b75505Sopenharmony_ci MAC2STR(target->bssid), target->level); 814e5b75505Sopenharmony_ci } 815e5b75505Sopenharmony_ci 816e5b75505Sopenharmony_ci return target; 817e5b75505Sopenharmony_ci} 818e5b75505Sopenharmony_ci 819e5b75505Sopenharmony_ci 820e5b75505Sopenharmony_cistatic int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid) 821e5b75505Sopenharmony_ci{ 822e5b75505Sopenharmony_ci const u8 *ie_a, *ie_b; 823e5b75505Sopenharmony_ci 824e5b75505Sopenharmony_ci if (!a || !b) 825e5b75505Sopenharmony_ci return 0; 826e5b75505Sopenharmony_ci 827e5b75505Sopenharmony_ci ie_a = wpa_bss_get_ie(a, eid); 828e5b75505Sopenharmony_ci ie_b = wpa_bss_get_ie(b, eid); 829e5b75505Sopenharmony_ci 830e5b75505Sopenharmony_ci if (!ie_a || !ie_b || ie_a[1] != ie_b[1]) 831e5b75505Sopenharmony_ci return 0; 832e5b75505Sopenharmony_ci 833e5b75505Sopenharmony_ci return os_memcmp(ie_a, ie_b, ie_a[1]) == 0; 834e5b75505Sopenharmony_ci} 835e5b75505Sopenharmony_ci 836e5b75505Sopenharmony_ci 837e5b75505Sopenharmony_cistatic u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 838e5b75505Sopenharmony_ci{ 839e5b75505Sopenharmony_ci u32 info = 0; 840e5b75505Sopenharmony_ci 841e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH; 842e5b75505Sopenharmony_ci 843e5b75505Sopenharmony_ci /* 844e5b75505Sopenharmony_ci * Leave the security and key scope bits unset to indicate that the 845e5b75505Sopenharmony_ci * security information is not available. 846e5b75505Sopenharmony_ci */ 847e5b75505Sopenharmony_ci 848e5b75505Sopenharmony_ci if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT) 849e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; 850e5b75505Sopenharmony_ci if (bss->caps & WLAN_CAPABILITY_QOS) 851e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_QOS; 852e5b75505Sopenharmony_ci if (bss->caps & WLAN_CAPABILITY_APSD) 853e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_APSD; 854e5b75505Sopenharmony_ci if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT) 855e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_RM; 856e5b75505Sopenharmony_ci if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK) 857e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_DELAYED_BA; 858e5b75505Sopenharmony_ci if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK) 859e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_IMM_BA; 860e5b75505Sopenharmony_ci if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN)) 861e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN; 862e5b75505Sopenharmony_ci if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP)) 863e5b75505Sopenharmony_ci info |= NEI_REP_BSSID_INFO_HT; 864e5b75505Sopenharmony_ci 865e5b75505Sopenharmony_ci return info; 866e5b75505Sopenharmony_ci} 867e5b75505Sopenharmony_ci 868e5b75505Sopenharmony_ci 869e5b75505Sopenharmony_cistatic int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid, 870e5b75505Sopenharmony_ci u32 bss_info, u8 op_class, u8 chan, u8 phy_type, 871e5b75505Sopenharmony_ci u8 pref) 872e5b75505Sopenharmony_ci{ 873e5b75505Sopenharmony_ci if (wpabuf_len(*buf) + 18 > 874e5b75505Sopenharmony_ci IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) { 875e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 876e5b75505Sopenharmony_ci "WNM: No room in frame for Neighbor Report element"); 877e5b75505Sopenharmony_ci return -1; 878e5b75505Sopenharmony_ci } 879e5b75505Sopenharmony_ci 880e5b75505Sopenharmony_ci if (wpabuf_resize(buf, 18) < 0) { 881e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 882e5b75505Sopenharmony_ci "WNM: Failed to allocate memory for Neighbor Report element"); 883e5b75505Sopenharmony_ci return -1; 884e5b75505Sopenharmony_ci } 885e5b75505Sopenharmony_ci 886e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT); 887e5b75505Sopenharmony_ci /* length: 13 for basic neighbor report + 3 for preference subelement */ 888e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, 16); 889e5b75505Sopenharmony_ci wpabuf_put_data(*buf, bssid, ETH_ALEN); 890e5b75505Sopenharmony_ci wpabuf_put_le32(*buf, bss_info); 891e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, op_class); 892e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, chan); 893e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, phy_type); 894e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE); 895e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, 1); 896e5b75505Sopenharmony_ci wpabuf_put_u8(*buf, pref); 897e5b75505Sopenharmony_ci return 0; 898e5b75505Sopenharmony_ci} 899e5b75505Sopenharmony_ci 900e5b75505Sopenharmony_ci 901e5b75505Sopenharmony_cistatic int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, 902e5b75505Sopenharmony_ci struct wpa_bss *bss, struct wpabuf **buf, 903e5b75505Sopenharmony_ci u8 pref) 904e5b75505Sopenharmony_ci{ 905e5b75505Sopenharmony_ci const u8 *ie; 906e5b75505Sopenharmony_ci u8 op_class, chan; 907e5b75505Sopenharmony_ci int sec_chan = 0, vht = 0; 908e5b75505Sopenharmony_ci enum phy_type phy_type; 909e5b75505Sopenharmony_ci u32 info; 910e5b75505Sopenharmony_ci struct ieee80211_ht_operation *ht_oper = NULL; 911e5b75505Sopenharmony_ci struct ieee80211_vht_operation *vht_oper = NULL; 912e5b75505Sopenharmony_ci 913e5b75505Sopenharmony_ci ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); 914e5b75505Sopenharmony_ci if (ie && ie[1] >= 2) { 915e5b75505Sopenharmony_ci ht_oper = (struct ieee80211_ht_operation *) (ie + 2); 916e5b75505Sopenharmony_ci 917e5b75505Sopenharmony_ci if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 918e5b75505Sopenharmony_ci sec_chan = 1; 919e5b75505Sopenharmony_ci else if (ht_oper->ht_param & 920e5b75505Sopenharmony_ci HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 921e5b75505Sopenharmony_ci sec_chan = -1; 922e5b75505Sopenharmony_ci } 923e5b75505Sopenharmony_ci 924e5b75505Sopenharmony_ci ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); 925e5b75505Sopenharmony_ci if (ie && ie[1] >= 1) { 926e5b75505Sopenharmony_ci vht_oper = (struct ieee80211_vht_operation *) (ie + 2); 927e5b75505Sopenharmony_ci 928e5b75505Sopenharmony_ci if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ || 929e5b75505Sopenharmony_ci vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ || 930e5b75505Sopenharmony_ci vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ) 931e5b75505Sopenharmony_ci vht = vht_oper->vht_op_info_chwidth; 932e5b75505Sopenharmony_ci } 933e5b75505Sopenharmony_ci 934e5b75505Sopenharmony_ci if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, 935e5b75505Sopenharmony_ci &chan) == NUM_HOSTAPD_MODES) { 936e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 937e5b75505Sopenharmony_ci "WNM: Cannot determine operating class and channel"); 938e5b75505Sopenharmony_ci return -2; 939e5b75505Sopenharmony_ci } 940e5b75505Sopenharmony_ci 941e5b75505Sopenharmony_ci phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), 942e5b75505Sopenharmony_ci (vht_oper != NULL)); 943e5b75505Sopenharmony_ci if (phy_type == PHY_TYPE_UNSPECIFIED) { 944e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 945e5b75505Sopenharmony_ci "WNM: Cannot determine BSS phy type for Neighbor Report"); 946e5b75505Sopenharmony_ci return -2; 947e5b75505Sopenharmony_ci } 948e5b75505Sopenharmony_ci 949e5b75505Sopenharmony_ci info = wnm_get_bss_info(wpa_s, bss); 950e5b75505Sopenharmony_ci 951e5b75505Sopenharmony_ci return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type, 952e5b75505Sopenharmony_ci pref); 953e5b75505Sopenharmony_ci} 954e5b75505Sopenharmony_ci 955e5b75505Sopenharmony_ci 956e5b75505Sopenharmony_cistatic void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf) 957e5b75505Sopenharmony_ci{ 958e5b75505Sopenharmony_ci unsigned int i, pref = 255; 959e5b75505Sopenharmony_ci struct os_reltime now; 960e5b75505Sopenharmony_ci struct wpa_ssid *ssid = wpa_s->current_ssid; 961e5b75505Sopenharmony_ci 962e5b75505Sopenharmony_ci if (!ssid) 963e5b75505Sopenharmony_ci return; 964e5b75505Sopenharmony_ci 965e5b75505Sopenharmony_ci /* 966e5b75505Sopenharmony_ci * TODO: Define when scan results are no longer valid for the candidate 967e5b75505Sopenharmony_ci * list. 968e5b75505Sopenharmony_ci */ 969e5b75505Sopenharmony_ci os_get_reltime(&now); 970e5b75505Sopenharmony_ci if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) 971e5b75505Sopenharmony_ci return; 972e5b75505Sopenharmony_ci 973e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 974e5b75505Sopenharmony_ci "WNM: Add candidate list to BSS Transition Management Response frame"); 975e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) { 976e5b75505Sopenharmony_ci struct wpa_bss *bss = wpa_s->last_scan_res[i]; 977e5b75505Sopenharmony_ci int res; 978e5b75505Sopenharmony_ci 979e5b75505Sopenharmony_ci if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { 980e5b75505Sopenharmony_ci res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--); 981e5b75505Sopenharmony_ci if (res == -2) 982e5b75505Sopenharmony_ci continue; /* could not build entry for BSS */ 983e5b75505Sopenharmony_ci if (res < 0) 984e5b75505Sopenharmony_ci break; /* no more room for candidates */ 985e5b75505Sopenharmony_ci if (pref == 1) 986e5b75505Sopenharmony_ci break; 987e5b75505Sopenharmony_ci } 988e5b75505Sopenharmony_ci } 989e5b75505Sopenharmony_ci 990e5b75505Sopenharmony_ci wpa_hexdump_buf(MSG_DEBUG, 991e5b75505Sopenharmony_ci "WNM: BSS Transition Management Response candidate list", 992e5b75505Sopenharmony_ci *buf); 993e5b75505Sopenharmony_ci} 994e5b75505Sopenharmony_ci 995e5b75505Sopenharmony_ci 996e5b75505Sopenharmony_ci#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN 997e5b75505Sopenharmony_ci 998e5b75505Sopenharmony_cistatic void wnm_send_bss_transition_mgmt_resp( 999e5b75505Sopenharmony_ci struct wpa_supplicant *wpa_s, u8 dialog_token, 1000e5b75505Sopenharmony_ci enum bss_trans_mgmt_status_code status, 1001e5b75505Sopenharmony_ci enum mbo_transition_reject_reason reason, 1002e5b75505Sopenharmony_ci u8 delay, const u8 *target_bssid) 1003e5b75505Sopenharmony_ci{ 1004e5b75505Sopenharmony_ci struct wpabuf *buf; 1005e5b75505Sopenharmony_ci int res; 1006e5b75505Sopenharmony_ci 1007e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1008e5b75505Sopenharmony_ci "WNM: Send BSS Transition Management Response to " MACSTR 1009e5b75505Sopenharmony_ci " dialog_token=%u status=%u reason=%u delay=%d", 1010e5b75505Sopenharmony_ci MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay); 1011e5b75505Sopenharmony_ci if (!wpa_s->current_bss) { 1012e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1013e5b75505Sopenharmony_ci "WNM: Current BSS not known - drop response"); 1014e5b75505Sopenharmony_ci return; 1015e5b75505Sopenharmony_ci } 1016e5b75505Sopenharmony_ci 1017e5b75505Sopenharmony_ci buf = wpabuf_alloc(BTM_RESP_MIN_SIZE); 1018e5b75505Sopenharmony_ci if (!buf) { 1019e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1020e5b75505Sopenharmony_ci "WNM: Failed to allocate memory for BTM response"); 1021e5b75505Sopenharmony_ci return; 1022e5b75505Sopenharmony_ci } 1023e5b75505Sopenharmony_ci 1024e5b75505Sopenharmony_ci wpa_s->bss_tm_status = status; 1025e5b75505Sopenharmony_ci wpas_notify_bss_tm_status(wpa_s); 1026e5b75505Sopenharmony_ci 1027e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_WNM); 1028e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP); 1029e5b75505Sopenharmony_ci wpabuf_put_u8(buf, dialog_token); 1030e5b75505Sopenharmony_ci wpabuf_put_u8(buf, status); 1031e5b75505Sopenharmony_ci wpabuf_put_u8(buf, delay); 1032e5b75505Sopenharmony_ci if (target_bssid) { 1033e5b75505Sopenharmony_ci wpabuf_put_data(buf, target_bssid, ETH_ALEN); 1034e5b75505Sopenharmony_ci } else if (status == WNM_BSS_TM_ACCEPT) { 1035e5b75505Sopenharmony_ci /* 1036e5b75505Sopenharmony_ci * P802.11-REVmc clarifies that the Target BSSID field is always 1037e5b75505Sopenharmony_ci * present when status code is zero, so use a fake value here if 1038e5b75505Sopenharmony_ci * no BSSID is yet known. 1039e5b75505Sopenharmony_ci */ 1040e5b75505Sopenharmony_ci wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN); 1041e5b75505Sopenharmony_ci } 1042e5b75505Sopenharmony_ci 1043e5b75505Sopenharmony_ci if (status == WNM_BSS_TM_ACCEPT) 1044e5b75505Sopenharmony_ci wnm_add_cand_list(wpa_s, &buf); 1045e5b75505Sopenharmony_ci 1046e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 1047e5b75505Sopenharmony_ci if (status != WNM_BSS_TM_ACCEPT && 1048e5b75505Sopenharmony_ci wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) { 1049e5b75505Sopenharmony_ci u8 mbo[10]; 1050e5b75505Sopenharmony_ci size_t ret; 1051e5b75505Sopenharmony_ci 1052e5b75505Sopenharmony_ci ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo), 1053e5b75505Sopenharmony_ci reason); 1054e5b75505Sopenharmony_ci if (ret) { 1055e5b75505Sopenharmony_ci if (wpabuf_resize(&buf, ret) < 0) { 1056e5b75505Sopenharmony_ci wpabuf_free(buf); 1057e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1058e5b75505Sopenharmony_ci "WNM: Failed to allocate memory for MBO IE"); 1059e5b75505Sopenharmony_ci return; 1060e5b75505Sopenharmony_ci } 1061e5b75505Sopenharmony_ci 1062e5b75505Sopenharmony_ci wpabuf_put_data(buf, mbo, ret); 1063e5b75505Sopenharmony_ci } 1064e5b75505Sopenharmony_ci } 1065e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 1066e5b75505Sopenharmony_ci 1067e5b75505Sopenharmony_ci res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 1068e5b75505Sopenharmony_ci wpa_s->own_addr, wpa_s->bssid, 1069e5b75505Sopenharmony_ci wpabuf_head_u8(buf), wpabuf_len(buf), 0); 1070e5b75505Sopenharmony_ci if (res < 0) { 1071e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1072e5b75505Sopenharmony_ci "WNM: Failed to send BSS Transition Management Response"); 1073e5b75505Sopenharmony_ci } 1074e5b75505Sopenharmony_ci 1075e5b75505Sopenharmony_ci wpabuf_free(buf); 1076e5b75505Sopenharmony_ci} 1077e5b75505Sopenharmony_ci 1078e5b75505Sopenharmony_ci 1079e5b75505Sopenharmony_cistatic void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, 1080e5b75505Sopenharmony_ci struct wpa_bss *bss, struct wpa_ssid *ssid, 1081e5b75505Sopenharmony_ci int after_new_scan) 1082e5b75505Sopenharmony_ci{ 1083e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1084e5b75505Sopenharmony_ci "WNM: Transition to BSS " MACSTR 1085e5b75505Sopenharmony_ci " based on BSS Transition Management Request (old BSSID " 1086e5b75505Sopenharmony_ci MACSTR " after_new_scan=%d)", 1087e5b75505Sopenharmony_ci MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan); 1088e5b75505Sopenharmony_ci 1089e5b75505Sopenharmony_ci /* Send the BSS Management Response - Accept */ 1090e5b75505Sopenharmony_ci if (wpa_s->wnm_reply) { 1091e5b75505Sopenharmony_ci wpa_s->wnm_reply = 0; 1092e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1093e5b75505Sopenharmony_ci "WNM: Sending successful BSS Transition Management Response"); 1094e5b75505Sopenharmony_ci wnm_send_bss_transition_mgmt_resp( 1095e5b75505Sopenharmony_ci wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, 1096e5b75505Sopenharmony_ci MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, 1097e5b75505Sopenharmony_ci bss->bssid); 1098e5b75505Sopenharmony_ci } 1099e5b75505Sopenharmony_ci 1100e5b75505Sopenharmony_ci if (bss == wpa_s->current_bss) { 1101e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1102e5b75505Sopenharmony_ci "WNM: Already associated with the preferred candidate"); 1103e5b75505Sopenharmony_ci wnm_deallocate_memory(wpa_s); 1104e5b75505Sopenharmony_ci return; 1105e5b75505Sopenharmony_ci } 1106e5b75505Sopenharmony_ci 1107e5b75505Sopenharmony_ci wpa_s->reassociate = 1; 1108e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Issuing connect"); 1109e5b75505Sopenharmony_ci wpa_supplicant_connect(wpa_s, bss, ssid); 1110e5b75505Sopenharmony_ci wnm_deallocate_memory(wpa_s); 1111e5b75505Sopenharmony_ci} 1112e5b75505Sopenharmony_ci 1113e5b75505Sopenharmony_ci 1114e5b75505Sopenharmony_ciint wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) 1115e5b75505Sopenharmony_ci{ 1116e5b75505Sopenharmony_ci struct wpa_bss *bss; 1117e5b75505Sopenharmony_ci struct wpa_ssid *ssid = wpa_s->current_ssid; 1118e5b75505Sopenharmony_ci enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; 1119e5b75505Sopenharmony_ci enum mbo_transition_reject_reason reason = 1120e5b75505Sopenharmony_ci MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; 1121e5b75505Sopenharmony_ci 1122e5b75505Sopenharmony_ci if (!wpa_s->wnm_neighbor_report_elements) 1123e5b75505Sopenharmony_ci return 0; 1124e5b75505Sopenharmony_ci 1125e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1126e5b75505Sopenharmony_ci "WNM: Process scan results for BSS Transition Management"); 1127e5b75505Sopenharmony_ci if (os_reltime_before(&wpa_s->wnm_cand_valid_until, 1128e5b75505Sopenharmony_ci &wpa_s->scan_trigger_time)) { 1129e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); 1130e5b75505Sopenharmony_ci wnm_deallocate_memory(wpa_s); 1131e5b75505Sopenharmony_ci return 0; 1132e5b75505Sopenharmony_ci } 1133e5b75505Sopenharmony_ci 1134e5b75505Sopenharmony_ci if (!wpa_s->current_bss || 1135e5b75505Sopenharmony_ci os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, 1136e5b75505Sopenharmony_ci ETH_ALEN) != 0) { 1137e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); 1138e5b75505Sopenharmony_ci return 0; 1139e5b75505Sopenharmony_ci } 1140e5b75505Sopenharmony_ci 1141e5b75505Sopenharmony_ci /* Compare the Neighbor Report and scan results */ 1142e5b75505Sopenharmony_ci bss = compare_scan_neighbor_results(wpa_s, 0, &reason); 1143e5b75505Sopenharmony_ci if (!bss) { 1144e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); 1145e5b75505Sopenharmony_ci status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; 1146e5b75505Sopenharmony_ci goto send_bss_resp_fail; 1147e5b75505Sopenharmony_ci } 1148e5b75505Sopenharmony_ci 1149e5b75505Sopenharmony_ci /* Associate to the network */ 1150e5b75505Sopenharmony_ci wnm_bss_tm_connect(wpa_s, bss, ssid, 1); 1151e5b75505Sopenharmony_ci return 1; 1152e5b75505Sopenharmony_ci 1153e5b75505Sopenharmony_cisend_bss_resp_fail: 1154e5b75505Sopenharmony_ci if (!reply_on_fail) 1155e5b75505Sopenharmony_ci return 0; 1156e5b75505Sopenharmony_ci 1157e5b75505Sopenharmony_ci /* Send reject response for all the failures */ 1158e5b75505Sopenharmony_ci 1159e5b75505Sopenharmony_ci if (wpa_s->wnm_reply) { 1160e5b75505Sopenharmony_ci wpa_s->wnm_reply = 0; 1161e5b75505Sopenharmony_ci wnm_send_bss_transition_mgmt_resp(wpa_s, 1162e5b75505Sopenharmony_ci wpa_s->wnm_dialog_token, 1163e5b75505Sopenharmony_ci status, reason, 0, NULL); 1164e5b75505Sopenharmony_ci } 1165e5b75505Sopenharmony_ci wnm_deallocate_memory(wpa_s); 1166e5b75505Sopenharmony_ci 1167e5b75505Sopenharmony_ci return 0; 1168e5b75505Sopenharmony_ci} 1169e5b75505Sopenharmony_ci 1170e5b75505Sopenharmony_ci 1171e5b75505Sopenharmony_cistatic int cand_pref_compar(const void *a, const void *b) 1172e5b75505Sopenharmony_ci{ 1173e5b75505Sopenharmony_ci const struct neighbor_report *aa = a; 1174e5b75505Sopenharmony_ci const struct neighbor_report *bb = b; 1175e5b75505Sopenharmony_ci 1176e5b75505Sopenharmony_ci if (!aa->preference_present && !bb->preference_present) 1177e5b75505Sopenharmony_ci return 0; 1178e5b75505Sopenharmony_ci if (!aa->preference_present) 1179e5b75505Sopenharmony_ci return 1; 1180e5b75505Sopenharmony_ci if (!bb->preference_present) 1181e5b75505Sopenharmony_ci return -1; 1182e5b75505Sopenharmony_ci if (bb->preference > aa->preference) 1183e5b75505Sopenharmony_ci return 1; 1184e5b75505Sopenharmony_ci if (bb->preference < aa->preference) 1185e5b75505Sopenharmony_ci return -1; 1186e5b75505Sopenharmony_ci return 0; 1187e5b75505Sopenharmony_ci} 1188e5b75505Sopenharmony_ci 1189e5b75505Sopenharmony_ci 1190e5b75505Sopenharmony_cistatic void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) 1191e5b75505Sopenharmony_ci{ 1192e5b75505Sopenharmony_ci if (!wpa_s->wnm_neighbor_report_elements) 1193e5b75505Sopenharmony_ci return; 1194e5b75505Sopenharmony_ci qsort(wpa_s->wnm_neighbor_report_elements, 1195e5b75505Sopenharmony_ci wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), 1196e5b75505Sopenharmony_ci cand_pref_compar); 1197e5b75505Sopenharmony_ci} 1198e5b75505Sopenharmony_ci 1199e5b75505Sopenharmony_ci 1200e5b75505Sopenharmony_cistatic void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) 1201e5b75505Sopenharmony_ci{ 1202e5b75505Sopenharmony_ci unsigned int i; 1203e5b75505Sopenharmony_ci 1204e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); 1205e5b75505Sopenharmony_ci if (!wpa_s->wnm_neighbor_report_elements) 1206e5b75505Sopenharmony_ci return; 1207e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 1208e5b75505Sopenharmony_ci struct neighbor_report *nei; 1209e5b75505Sopenharmony_ci 1210e5b75505Sopenharmony_ci nei = &wpa_s->wnm_neighbor_report_elements[i]; 1211e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "%u: " MACSTR 1212e5b75505Sopenharmony_ci " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", 1213e5b75505Sopenharmony_ci i, MAC2STR(nei->bssid), nei->bssid_info, 1214e5b75505Sopenharmony_ci nei->regulatory_class, 1215e5b75505Sopenharmony_ci nei->channel_number, nei->phy_type, 1216e5b75505Sopenharmony_ci nei->preference_present ? nei->preference : -1, 1217e5b75505Sopenharmony_ci nei->freq); 1218e5b75505Sopenharmony_ci } 1219e5b75505Sopenharmony_ci} 1220e5b75505Sopenharmony_ci 1221e5b75505Sopenharmony_ci 1222e5b75505Sopenharmony_cistatic int chan_supported(struct wpa_supplicant *wpa_s, int freq) 1223e5b75505Sopenharmony_ci{ 1224e5b75505Sopenharmony_ci unsigned int i; 1225e5b75505Sopenharmony_ci 1226e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->hw.num_modes; i++) { 1227e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; 1228e5b75505Sopenharmony_ci int j; 1229e5b75505Sopenharmony_ci 1230e5b75505Sopenharmony_ci for (j = 0; j < mode->num_channels; j++) { 1231e5b75505Sopenharmony_ci struct hostapd_channel_data *chan; 1232e5b75505Sopenharmony_ci 1233e5b75505Sopenharmony_ci chan = &mode->channels[j]; 1234e5b75505Sopenharmony_ci if (chan->freq == freq && 1235e5b75505Sopenharmony_ci !(chan->flag & HOSTAPD_CHAN_DISABLED)) 1236e5b75505Sopenharmony_ci return 1; 1237e5b75505Sopenharmony_ci } 1238e5b75505Sopenharmony_ci } 1239e5b75505Sopenharmony_ci 1240e5b75505Sopenharmony_ci return 0; 1241e5b75505Sopenharmony_ci} 1242e5b75505Sopenharmony_ci 1243e5b75505Sopenharmony_ci 1244e5b75505Sopenharmony_cistatic void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) 1245e5b75505Sopenharmony_ci{ 1246e5b75505Sopenharmony_ci int *freqs; 1247e5b75505Sopenharmony_ci int num_freqs = 0; 1248e5b75505Sopenharmony_ci unsigned int i; 1249e5b75505Sopenharmony_ci 1250e5b75505Sopenharmony_ci if (!wpa_s->wnm_neighbor_report_elements) 1251e5b75505Sopenharmony_ci return; 1252e5b75505Sopenharmony_ci 1253e5b75505Sopenharmony_ci if (wpa_s->hw.modes == NULL) 1254e5b75505Sopenharmony_ci return; 1255e5b75505Sopenharmony_ci 1256e5b75505Sopenharmony_ci os_free(wpa_s->next_scan_freqs); 1257e5b75505Sopenharmony_ci wpa_s->next_scan_freqs = NULL; 1258e5b75505Sopenharmony_ci 1259e5b75505Sopenharmony_ci freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); 1260e5b75505Sopenharmony_ci if (freqs == NULL) 1261e5b75505Sopenharmony_ci return; 1262e5b75505Sopenharmony_ci 1263e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 1264e5b75505Sopenharmony_ci struct neighbor_report *nei; 1265e5b75505Sopenharmony_ci 1266e5b75505Sopenharmony_ci nei = &wpa_s->wnm_neighbor_report_elements[i]; 1267e5b75505Sopenharmony_ci if (nei->freq <= 0) { 1268e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1269e5b75505Sopenharmony_ci "WNM: Unknown neighbor operating frequency for " 1270e5b75505Sopenharmony_ci MACSTR " - scan all channels", 1271e5b75505Sopenharmony_ci MAC2STR(nei->bssid)); 1272e5b75505Sopenharmony_ci os_free(freqs); 1273e5b75505Sopenharmony_ci return; 1274e5b75505Sopenharmony_ci } 1275e5b75505Sopenharmony_ci if (chan_supported(wpa_s, nei->freq)) 1276e5b75505Sopenharmony_ci add_freq(freqs, &num_freqs, nei->freq); 1277e5b75505Sopenharmony_ci } 1278e5b75505Sopenharmony_ci 1279e5b75505Sopenharmony_ci if (num_freqs == 0) { 1280e5b75505Sopenharmony_ci os_free(freqs); 1281e5b75505Sopenharmony_ci return; 1282e5b75505Sopenharmony_ci } 1283e5b75505Sopenharmony_ci 1284e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1285e5b75505Sopenharmony_ci "WNM: Scan %d frequencies based on transition candidate list", 1286e5b75505Sopenharmony_ci num_freqs); 1287e5b75505Sopenharmony_ci wpa_s->next_scan_freqs = freqs; 1288e5b75505Sopenharmony_ci} 1289e5b75505Sopenharmony_ci 1290e5b75505Sopenharmony_ci 1291e5b75505Sopenharmony_cistatic int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) 1292e5b75505Sopenharmony_ci{ 1293e5b75505Sopenharmony_ci struct wpa_scan_results *scan_res; 1294e5b75505Sopenharmony_ci struct wpa_bss *bss; 1295e5b75505Sopenharmony_ci struct wpa_ssid *ssid = wpa_s->current_ssid; 1296e5b75505Sopenharmony_ci u8 i, found = 0; 1297e5b75505Sopenharmony_ci size_t j; 1298e5b75505Sopenharmony_ci 1299e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1300e5b75505Sopenharmony_ci "WNM: Fetch current scan results from the driver for checking transition candidates"); 1301e5b75505Sopenharmony_ci scan_res = wpa_drv_get_scan_results2(wpa_s); 1302e5b75505Sopenharmony_ci if (!scan_res) { 1303e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results"); 1304e5b75505Sopenharmony_ci return 0; 1305e5b75505Sopenharmony_ci } 1306e5b75505Sopenharmony_ci 1307e5b75505Sopenharmony_ci if (scan_res->fetch_time.sec == 0) 1308e5b75505Sopenharmony_ci os_get_reltime(&scan_res->fetch_time); 1309e5b75505Sopenharmony_ci 1310e5b75505Sopenharmony_ci filter_scan_res(wpa_s, scan_res); 1311e5b75505Sopenharmony_ci 1312e5b75505Sopenharmony_ci for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 1313e5b75505Sopenharmony_ci struct neighbor_report *nei; 1314e5b75505Sopenharmony_ci 1315e5b75505Sopenharmony_ci nei = &wpa_s->wnm_neighbor_report_elements[i]; 1316e5b75505Sopenharmony_ci if (nei->preference_present && nei->preference == 0) 1317e5b75505Sopenharmony_ci continue; 1318e5b75505Sopenharmony_ci 1319e5b75505Sopenharmony_ci for (j = 0; j < scan_res->num; j++) { 1320e5b75505Sopenharmony_ci struct wpa_scan_res *res; 1321e5b75505Sopenharmony_ci const u8 *ssid_ie; 1322e5b75505Sopenharmony_ci 1323e5b75505Sopenharmony_ci res = scan_res->res[j]; 1324e5b75505Sopenharmony_ci if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 || 1325e5b75505Sopenharmony_ci res->age > WNM_SCAN_RESULT_AGE * 1000) 1326e5b75505Sopenharmony_ci continue; 1327e5b75505Sopenharmony_ci bss = wpa_s->current_bss; 1328e5b75505Sopenharmony_ci ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID); 1329e5b75505Sopenharmony_ci if (bss && ssid_ie && 1330e5b75505Sopenharmony_ci (bss->ssid_len != ssid_ie[1] || 1331e5b75505Sopenharmony_ci os_memcmp(bss->ssid, ssid_ie + 2, 1332e5b75505Sopenharmony_ci bss->ssid_len) != 0)) 1333e5b75505Sopenharmony_ci continue; 1334e5b75505Sopenharmony_ci 1335e5b75505Sopenharmony_ci /* Potential candidate found */ 1336e5b75505Sopenharmony_ci found = 1; 1337e5b75505Sopenharmony_ci scan_snr(res); 1338e5b75505Sopenharmony_ci scan_est_throughput(wpa_s, res); 1339e5b75505Sopenharmony_ci wpa_bss_update_scan_res(wpa_s, res, 1340e5b75505Sopenharmony_ci &scan_res->fetch_time); 1341e5b75505Sopenharmony_ci } 1342e5b75505Sopenharmony_ci } 1343e5b75505Sopenharmony_ci 1344e5b75505Sopenharmony_ci wpa_scan_results_free(scan_res); 1345e5b75505Sopenharmony_ci if (!found) { 1346e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1347e5b75505Sopenharmony_ci "WNM: No transition candidate matches existing scan results"); 1348e5b75505Sopenharmony_ci return 0; 1349e5b75505Sopenharmony_ci } 1350e5b75505Sopenharmony_ci 1351e5b75505Sopenharmony_ci bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL); 1352e5b75505Sopenharmony_ci if (!bss) { 1353e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1354e5b75505Sopenharmony_ci "WNM: Comparison of scan results against transition candidates did not find matches"); 1355e5b75505Sopenharmony_ci return 0; 1356e5b75505Sopenharmony_ci } 1357e5b75505Sopenharmony_ci 1358e5b75505Sopenharmony_ci /* Associate to the network */ 1359e5b75505Sopenharmony_ci wnm_bss_tm_connect(wpa_s, bss, ssid, 0); 1360e5b75505Sopenharmony_ci return 1; 1361e5b75505Sopenharmony_ci} 1362e5b75505Sopenharmony_ci 1363e5b75505Sopenharmony_ci 1364e5b75505Sopenharmony_cistatic void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, 1365e5b75505Sopenharmony_ci const u8 *pos, const u8 *end, 1366e5b75505Sopenharmony_ci int reply) 1367e5b75505Sopenharmony_ci{ 1368e5b75505Sopenharmony_ci unsigned int beacon_int; 1369e5b75505Sopenharmony_ci u8 valid_int; 1370e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 1371e5b75505Sopenharmony_ci const u8 *vendor; 1372e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 1373e5b75505Sopenharmony_ci 1374e5b75505Sopenharmony_ci if (wpa_s->conf->disable_btm) 1375e5b75505Sopenharmony_ci return; 1376e5b75505Sopenharmony_ci 1377e5b75505Sopenharmony_ci if (end - pos < 5) 1378e5b75505Sopenharmony_ci return; 1379e5b75505Sopenharmony_ci 1380e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 1381e5b75505Sopenharmony_ci wpa_s->wnm_mbo_trans_reason_present = 0; 1382e5b75505Sopenharmony_ci wpa_s->wnm_mbo_transition_reason = 0; 1383e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 1384e5b75505Sopenharmony_ci 1385e5b75505Sopenharmony_ci if (wpa_s->current_bss) 1386e5b75505Sopenharmony_ci beacon_int = wpa_s->current_bss->beacon_int; 1387e5b75505Sopenharmony_ci else 1388e5b75505Sopenharmony_ci beacon_int = 100; /* best guess */ 1389e5b75505Sopenharmony_ci 1390e5b75505Sopenharmony_ci wpa_s->wnm_dialog_token = pos[0]; 1391e5b75505Sopenharmony_ci wpa_s->wnm_mode = pos[1]; 1392e5b75505Sopenharmony_ci wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); 1393e5b75505Sopenharmony_ci valid_int = pos[4]; 1394e5b75505Sopenharmony_ci wpa_s->wnm_reply = reply; 1395e5b75505Sopenharmony_ci 1396e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " 1397e5b75505Sopenharmony_ci "dialog_token=%u request_mode=0x%x " 1398e5b75505Sopenharmony_ci "disassoc_timer=%u validity_interval=%u", 1399e5b75505Sopenharmony_ci wpa_s->wnm_dialog_token, wpa_s->wnm_mode, 1400e5b75505Sopenharmony_ci wpa_s->wnm_dissoc_timer, valid_int); 1401e5b75505Sopenharmony_ci 1402e5b75505Sopenharmony_ci#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) 1403e5b75505Sopenharmony_ci if (wpa_s->reject_btm_req_reason) { 1404e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 1405e5b75505Sopenharmony_ci "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", 1406e5b75505Sopenharmony_ci wpa_s->reject_btm_req_reason); 1407e5b75505Sopenharmony_ci wnm_send_bss_transition_mgmt_resp( 1408e5b75505Sopenharmony_ci wpa_s, wpa_s->wnm_dialog_token, 1409e5b75505Sopenharmony_ci wpa_s->reject_btm_req_reason, 1410e5b75505Sopenharmony_ci MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); 1411e5b75505Sopenharmony_ci return; 1412e5b75505Sopenharmony_ci } 1413e5b75505Sopenharmony_ci#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ 1414e5b75505Sopenharmony_ci 1415e5b75505Sopenharmony_ci pos += 5; 1416e5b75505Sopenharmony_ci 1417e5b75505Sopenharmony_ci if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { 1418e5b75505Sopenharmony_ci if (end - pos < 12) { 1419e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); 1420e5b75505Sopenharmony_ci return; 1421e5b75505Sopenharmony_ci } 1422e5b75505Sopenharmony_ci os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); 1423e5b75505Sopenharmony_ci pos += 12; /* BSS Termination Duration */ 1424e5b75505Sopenharmony_ci } 1425e5b75505Sopenharmony_ci 1426e5b75505Sopenharmony_ci if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { 1427e5b75505Sopenharmony_ci char url[256]; 1428e5b75505Sopenharmony_ci 1429e5b75505Sopenharmony_ci if (end - pos < 1 || 1 + pos[0] > end - pos) { 1430e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " 1431e5b75505Sopenharmony_ci "Management Request (URL)"); 1432e5b75505Sopenharmony_ci return; 1433e5b75505Sopenharmony_ci } 1434e5b75505Sopenharmony_ci os_memcpy(url, pos + 1, pos[0]); 1435e5b75505Sopenharmony_ci url[pos[0]] = '\0'; 1436e5b75505Sopenharmony_ci pos += 1 + pos[0]; 1437e5b75505Sopenharmony_ci 1438e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", 1439e5b75505Sopenharmony_ci wpa_sm_pmf_enabled(wpa_s->wpa), 1440e5b75505Sopenharmony_ci wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); 1441e5b75505Sopenharmony_ci } 1442e5b75505Sopenharmony_ci 1443e5b75505Sopenharmony_ci if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 1444e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " 1445e5b75505Sopenharmony_ci "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); 1446e5b75505Sopenharmony_ci if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { 1447e5b75505Sopenharmony_ci /* TODO: mark current BSS less preferred for 1448e5b75505Sopenharmony_ci * selection */ 1449e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Trying to find another BSS"); 1450e5b75505Sopenharmony_ci wpa_supplicant_req_scan(wpa_s, 0, 0); 1451e5b75505Sopenharmony_ci } 1452e5b75505Sopenharmony_ci } 1453e5b75505Sopenharmony_ci 1454e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 1455e5b75505Sopenharmony_ci vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); 1456e5b75505Sopenharmony_ci if (vendor) 1457e5b75505Sopenharmony_ci wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); 1458e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 1459e5b75505Sopenharmony_ci 1460e5b75505Sopenharmony_ci if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { 1461e5b75505Sopenharmony_ci unsigned int valid_ms; 1462e5b75505Sopenharmony_ci 1463e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); 1464e5b75505Sopenharmony_ci wnm_deallocate_memory(wpa_s); 1465e5b75505Sopenharmony_ci wpa_s->wnm_neighbor_report_elements = os_calloc( 1466e5b75505Sopenharmony_ci WNM_MAX_NEIGHBOR_REPORT, 1467e5b75505Sopenharmony_ci sizeof(struct neighbor_report)); 1468e5b75505Sopenharmony_ci if (wpa_s->wnm_neighbor_report_elements == NULL) 1469e5b75505Sopenharmony_ci return; 1470e5b75505Sopenharmony_ci 1471e5b75505Sopenharmony_ci while (end - pos >= 2 && 1472e5b75505Sopenharmony_ci wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) 1473e5b75505Sopenharmony_ci { 1474e5b75505Sopenharmony_ci u8 tag = *pos++; 1475e5b75505Sopenharmony_ci u8 len = *pos++; 1476e5b75505Sopenharmony_ci 1477e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", 1478e5b75505Sopenharmony_ci tag); 1479e5b75505Sopenharmony_ci if (len > end - pos) { 1480e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Truncated request"); 1481e5b75505Sopenharmony_ci return; 1482e5b75505Sopenharmony_ci } 1483e5b75505Sopenharmony_ci if (tag == WLAN_EID_NEIGHBOR_REPORT) { 1484e5b75505Sopenharmony_ci struct neighbor_report *rep; 1485e5b75505Sopenharmony_ci rep = &wpa_s->wnm_neighbor_report_elements[ 1486e5b75505Sopenharmony_ci wpa_s->wnm_num_neighbor_report]; 1487e5b75505Sopenharmony_ci wnm_parse_neighbor_report(wpa_s, pos, len, rep); 1488e5b75505Sopenharmony_ci wpa_s->wnm_num_neighbor_report++; 1489e5b75505Sopenharmony_ci#ifdef CONFIG_MBO 1490e5b75505Sopenharmony_ci if (wpa_s->wnm_mbo_trans_reason_present && 1491e5b75505Sopenharmony_ci wpa_s->wnm_num_neighbor_report == 1) { 1492e5b75505Sopenharmony_ci rep->is_first = 1; 1493e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1494e5b75505Sopenharmony_ci "WNM: First transition candidate is " 1495e5b75505Sopenharmony_ci MACSTR, MAC2STR(rep->bssid)); 1496e5b75505Sopenharmony_ci } 1497e5b75505Sopenharmony_ci#endif /* CONFIG_MBO */ 1498e5b75505Sopenharmony_ci } 1499e5b75505Sopenharmony_ci 1500e5b75505Sopenharmony_ci pos += len; 1501e5b75505Sopenharmony_ci } 1502e5b75505Sopenharmony_ci 1503e5b75505Sopenharmony_ci if (!wpa_s->wnm_num_neighbor_report) { 1504e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1505e5b75505Sopenharmony_ci "WNM: Candidate list included bit is set, but no candidates found"); 1506e5b75505Sopenharmony_ci wnm_send_bss_transition_mgmt_resp( 1507e5b75505Sopenharmony_ci wpa_s, wpa_s->wnm_dialog_token, 1508e5b75505Sopenharmony_ci WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, 1509e5b75505Sopenharmony_ci MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, 1510e5b75505Sopenharmony_ci NULL); 1511e5b75505Sopenharmony_ci return; 1512e5b75505Sopenharmony_ci } 1513e5b75505Sopenharmony_ci 1514e5b75505Sopenharmony_ci wnm_sort_cand_list(wpa_s); 1515e5b75505Sopenharmony_ci wnm_dump_cand_list(wpa_s); 1516e5b75505Sopenharmony_ci valid_ms = valid_int * beacon_int * 128 / 125; 1517e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", 1518e5b75505Sopenharmony_ci valid_ms); 1519e5b75505Sopenharmony_ci os_get_reltime(&wpa_s->wnm_cand_valid_until); 1520e5b75505Sopenharmony_ci wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; 1521e5b75505Sopenharmony_ci wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; 1522e5b75505Sopenharmony_ci wpa_s->wnm_cand_valid_until.sec += 1523e5b75505Sopenharmony_ci wpa_s->wnm_cand_valid_until.usec / 1000000; 1524e5b75505Sopenharmony_ci wpa_s->wnm_cand_valid_until.usec %= 1000000; 1525e5b75505Sopenharmony_ci os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); 1526e5b75505Sopenharmony_ci 1527e5b75505Sopenharmony_ci /* 1528e5b75505Sopenharmony_ci * Fetch the latest scan results from the kernel and check for 1529e5b75505Sopenharmony_ci * candidates based on those results first. This can help in 1530e5b75505Sopenharmony_ci * finding more up-to-date information should the driver has 1531e5b75505Sopenharmony_ci * done some internal scanning operations after the last scan 1532e5b75505Sopenharmony_ci * result update in wpa_supplicant. 1533e5b75505Sopenharmony_ci */ 1534e5b75505Sopenharmony_ci if (wnm_fetch_scan_results(wpa_s) > 0) 1535e5b75505Sopenharmony_ci return; 1536e5b75505Sopenharmony_ci 1537e5b75505Sopenharmony_ci /* 1538e5b75505Sopenharmony_ci * Try to use previously received scan results, if they are 1539e5b75505Sopenharmony_ci * recent enough to use for a connection. 1540e5b75505Sopenharmony_ci */ 1541e5b75505Sopenharmony_ci if (wpa_s->last_scan_res_used > 0) { 1542e5b75505Sopenharmony_ci struct os_reltime now; 1543e5b75505Sopenharmony_ci 1544e5b75505Sopenharmony_ci os_get_reltime(&now); 1545e5b75505Sopenharmony_ci if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { 1546e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1547e5b75505Sopenharmony_ci "WNM: Try to use recent scan results"); 1548e5b75505Sopenharmony_ci if (wnm_scan_process(wpa_s, 0) > 0) 1549e5b75505Sopenharmony_ci return; 1550e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1551e5b75505Sopenharmony_ci "WNM: No match in previous scan results - try a new scan"); 1552e5b75505Sopenharmony_ci } 1553e5b75505Sopenharmony_ci } 1554e5b75505Sopenharmony_ci 1555e5b75505Sopenharmony_ci wnm_set_scan_freqs(wpa_s); 1556e5b75505Sopenharmony_ci if (wpa_s->wnm_num_neighbor_report == 1) { 1557e5b75505Sopenharmony_ci os_memcpy(wpa_s->next_scan_bssid, 1558e5b75505Sopenharmony_ci wpa_s->wnm_neighbor_report_elements[0].bssid, 1559e5b75505Sopenharmony_ci ETH_ALEN); 1560e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1561e5b75505Sopenharmony_ci "WNM: Scan only for a specific BSSID since there is only a single candidate " 1562e5b75505Sopenharmony_ci MACSTR, MAC2STR(wpa_s->next_scan_bssid)); 1563e5b75505Sopenharmony_ci } 1564e5b75505Sopenharmony_ci wpa_supplicant_req_scan(wpa_s, 0, 0); 1565e5b75505Sopenharmony_ci } else if (reply) { 1566e5b75505Sopenharmony_ci enum bss_trans_mgmt_status_code status; 1567e5b75505Sopenharmony_ci if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) 1568e5b75505Sopenharmony_ci status = WNM_BSS_TM_ACCEPT; 1569e5b75505Sopenharmony_ci else { 1570e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); 1571e5b75505Sopenharmony_ci status = WNM_BSS_TM_REJECT_UNSPECIFIED; 1572e5b75505Sopenharmony_ci } 1573e5b75505Sopenharmony_ci wnm_send_bss_transition_mgmt_resp( 1574e5b75505Sopenharmony_ci wpa_s, wpa_s->wnm_dialog_token, status, 1575e5b75505Sopenharmony_ci MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); 1576e5b75505Sopenharmony_ci } 1577e5b75505Sopenharmony_ci} 1578e5b75505Sopenharmony_ci 1579e5b75505Sopenharmony_ci 1580e5b75505Sopenharmony_ci#define BTM_QUERY_MIN_SIZE 4 1581e5b75505Sopenharmony_ci 1582e5b75505Sopenharmony_ciint wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, 1583e5b75505Sopenharmony_ci u8 query_reason, 1584e5b75505Sopenharmony_ci const char *btm_candidates, 1585e5b75505Sopenharmony_ci int cand_list) 1586e5b75505Sopenharmony_ci{ 1587e5b75505Sopenharmony_ci struct wpabuf *buf; 1588e5b75505Sopenharmony_ci int ret; 1589e5b75505Sopenharmony_ci 1590e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " 1591e5b75505Sopenharmony_ci MACSTR " query_reason=%u%s", 1592e5b75505Sopenharmony_ci MAC2STR(wpa_s->bssid), query_reason, 1593e5b75505Sopenharmony_ci cand_list ? " candidate list" : ""); 1594e5b75505Sopenharmony_ci 1595e5b75505Sopenharmony_ci buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE); 1596e5b75505Sopenharmony_ci if (!buf) 1597e5b75505Sopenharmony_ci return -1; 1598e5b75505Sopenharmony_ci 1599e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_WNM); 1600e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY); 1601e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 1); 1602e5b75505Sopenharmony_ci wpabuf_put_u8(buf, query_reason); 1603e5b75505Sopenharmony_ci 1604e5b75505Sopenharmony_ci if (cand_list) 1605e5b75505Sopenharmony_ci wnm_add_cand_list(wpa_s, &buf); 1606e5b75505Sopenharmony_ci 1607e5b75505Sopenharmony_ci if (btm_candidates) { 1608e5b75505Sopenharmony_ci const size_t max_len = 1000; 1609e5b75505Sopenharmony_ci 1610e5b75505Sopenharmony_ci ret = wpabuf_resize(&buf, max_len); 1611e5b75505Sopenharmony_ci if (ret < 0) { 1612e5b75505Sopenharmony_ci wpabuf_free(buf); 1613e5b75505Sopenharmony_ci return ret; 1614e5b75505Sopenharmony_ci } 1615e5b75505Sopenharmony_ci 1616e5b75505Sopenharmony_ci ret = ieee802_11_parse_candidate_list(btm_candidates, 1617e5b75505Sopenharmony_ci wpabuf_put(buf, 0), 1618e5b75505Sopenharmony_ci max_len); 1619e5b75505Sopenharmony_ci if (ret < 0) { 1620e5b75505Sopenharmony_ci wpabuf_free(buf); 1621e5b75505Sopenharmony_ci return ret; 1622e5b75505Sopenharmony_ci } 1623e5b75505Sopenharmony_ci 1624e5b75505Sopenharmony_ci wpabuf_put(buf, ret); 1625e5b75505Sopenharmony_ci } 1626e5b75505Sopenharmony_ci 1627e5b75505Sopenharmony_ci ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 1628e5b75505Sopenharmony_ci wpa_s->own_addr, wpa_s->bssid, 1629e5b75505Sopenharmony_ci wpabuf_head_u8(buf), wpabuf_len(buf), 0); 1630e5b75505Sopenharmony_ci 1631e5b75505Sopenharmony_ci wpabuf_free(buf); 1632e5b75505Sopenharmony_ci return ret; 1633e5b75505Sopenharmony_ci} 1634e5b75505Sopenharmony_ci 1635e5b75505Sopenharmony_ci 1636e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, 1637e5b75505Sopenharmony_ci const u8 *sa, const u8 *data, 1638e5b75505Sopenharmony_ci int len) 1639e5b75505Sopenharmony_ci{ 1640e5b75505Sopenharmony_ci const u8 *pos, *end, *next; 1641e5b75505Sopenharmony_ci u8 ie, ie_len; 1642e5b75505Sopenharmony_ci 1643e5b75505Sopenharmony_ci pos = data; 1644e5b75505Sopenharmony_ci end = data + len; 1645e5b75505Sopenharmony_ci 1646e5b75505Sopenharmony_ci while (end - pos > 1) { 1647e5b75505Sopenharmony_ci ie = *pos++; 1648e5b75505Sopenharmony_ci ie_len = *pos++; 1649e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", 1650e5b75505Sopenharmony_ci ie, ie_len); 1651e5b75505Sopenharmony_ci if (ie_len > end - pos) { 1652e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Not enough room for " 1653e5b75505Sopenharmony_ci "subelement"); 1654e5b75505Sopenharmony_ci break; 1655e5b75505Sopenharmony_ci } 1656e5b75505Sopenharmony_ci next = pos + ie_len; 1657e5b75505Sopenharmony_ci if (ie_len < 4) { 1658e5b75505Sopenharmony_ci pos = next; 1659e5b75505Sopenharmony_ci continue; 1660e5b75505Sopenharmony_ci } 1661e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", 1662e5b75505Sopenharmony_ci WPA_GET_BE24(pos), pos[3]); 1663e5b75505Sopenharmony_ci 1664e5b75505Sopenharmony_ci#ifdef CONFIG_HS20 1665e5b75505Sopenharmony_ci if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && 1666e5b75505Sopenharmony_ci WPA_GET_BE24(pos) == OUI_WFA && 1667e5b75505Sopenharmony_ci pos[3] == HS20_WNM_SUB_REM_NEEDED) { 1668e5b75505Sopenharmony_ci /* Subscription Remediation subelement */ 1669e5b75505Sopenharmony_ci const u8 *ie_end; 1670e5b75505Sopenharmony_ci u8 url_len; 1671e5b75505Sopenharmony_ci char *url; 1672e5b75505Sopenharmony_ci u8 osu_method; 1673e5b75505Sopenharmony_ci 1674e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " 1675e5b75505Sopenharmony_ci "subelement"); 1676e5b75505Sopenharmony_ci ie_end = pos + ie_len; 1677e5b75505Sopenharmony_ci pos += 4; 1678e5b75505Sopenharmony_ci url_len = *pos++; 1679e5b75505Sopenharmony_ci if (url_len == 0) { 1680e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); 1681e5b75505Sopenharmony_ci url = NULL; 1682e5b75505Sopenharmony_ci osu_method = 1; 1683e5b75505Sopenharmony_ci } else { 1684e5b75505Sopenharmony_ci if (url_len + 1 > ie_end - pos) { 1685e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", 1686e5b75505Sopenharmony_ci url_len, 1687e5b75505Sopenharmony_ci (int) (ie_end - pos)); 1688e5b75505Sopenharmony_ci break; 1689e5b75505Sopenharmony_ci } 1690e5b75505Sopenharmony_ci url = os_malloc(url_len + 1); 1691e5b75505Sopenharmony_ci if (url == NULL) 1692e5b75505Sopenharmony_ci break; 1693e5b75505Sopenharmony_ci os_memcpy(url, pos, url_len); 1694e5b75505Sopenharmony_ci url[url_len] = '\0'; 1695e5b75505Sopenharmony_ci osu_method = pos[url_len]; 1696e5b75505Sopenharmony_ci } 1697e5b75505Sopenharmony_ci hs20_rx_subscription_remediation(wpa_s, url, 1698e5b75505Sopenharmony_ci osu_method); 1699e5b75505Sopenharmony_ci os_free(url); 1700e5b75505Sopenharmony_ci pos = next; 1701e5b75505Sopenharmony_ci continue; 1702e5b75505Sopenharmony_ci } 1703e5b75505Sopenharmony_ci 1704e5b75505Sopenharmony_ci if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && 1705e5b75505Sopenharmony_ci WPA_GET_BE24(pos) == OUI_WFA && 1706e5b75505Sopenharmony_ci pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { 1707e5b75505Sopenharmony_ci const u8 *ie_end; 1708e5b75505Sopenharmony_ci u8 url_len; 1709e5b75505Sopenharmony_ci char *url; 1710e5b75505Sopenharmony_ci u8 code; 1711e5b75505Sopenharmony_ci u16 reauth_delay; 1712e5b75505Sopenharmony_ci 1713e5b75505Sopenharmony_ci ie_end = pos + ie_len; 1714e5b75505Sopenharmony_ci pos += 4; 1715e5b75505Sopenharmony_ci code = *pos++; 1716e5b75505Sopenharmony_ci reauth_delay = WPA_GET_LE16(pos); 1717e5b75505Sopenharmony_ci pos += 2; 1718e5b75505Sopenharmony_ci url_len = *pos++; 1719e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " 1720e5b75505Sopenharmony_ci "Imminent - Reason Code %u " 1721e5b75505Sopenharmony_ci "Re-Auth Delay %u URL Length %u", 1722e5b75505Sopenharmony_ci code, reauth_delay, url_len); 1723e5b75505Sopenharmony_ci if (url_len > ie_end - pos) 1724e5b75505Sopenharmony_ci break; 1725e5b75505Sopenharmony_ci url = os_malloc(url_len + 1); 1726e5b75505Sopenharmony_ci if (url == NULL) 1727e5b75505Sopenharmony_ci break; 1728e5b75505Sopenharmony_ci os_memcpy(url, pos, url_len); 1729e5b75505Sopenharmony_ci url[url_len] = '\0'; 1730e5b75505Sopenharmony_ci hs20_rx_deauth_imminent_notice(wpa_s, code, 1731e5b75505Sopenharmony_ci reauth_delay, url); 1732e5b75505Sopenharmony_ci os_free(url); 1733e5b75505Sopenharmony_ci pos = next; 1734e5b75505Sopenharmony_ci continue; 1735e5b75505Sopenharmony_ci } 1736e5b75505Sopenharmony_ci 1737e5b75505Sopenharmony_ci if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && 1738e5b75505Sopenharmony_ci WPA_GET_BE24(pos) == OUI_WFA && 1739e5b75505Sopenharmony_ci pos[3] == HS20_WNM_T_C_ACCEPTANCE) { 1740e5b75505Sopenharmony_ci const u8 *ie_end; 1741e5b75505Sopenharmony_ci u8 url_len; 1742e5b75505Sopenharmony_ci char *url; 1743e5b75505Sopenharmony_ci 1744e5b75505Sopenharmony_ci ie_end = pos + ie_len; 1745e5b75505Sopenharmony_ci pos += 4; 1746e5b75505Sopenharmony_ci url_len = *pos++; 1747e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1748e5b75505Sopenharmony_ci "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)", 1749e5b75505Sopenharmony_ci url_len); 1750e5b75505Sopenharmony_ci if (url_len > ie_end - pos) 1751e5b75505Sopenharmony_ci break; 1752e5b75505Sopenharmony_ci url = os_malloc(url_len + 1); 1753e5b75505Sopenharmony_ci if (!url) 1754e5b75505Sopenharmony_ci break; 1755e5b75505Sopenharmony_ci os_memcpy(url, pos, url_len); 1756e5b75505Sopenharmony_ci url[url_len] = '\0'; 1757e5b75505Sopenharmony_ci hs20_rx_t_c_acceptance(wpa_s, url); 1758e5b75505Sopenharmony_ci os_free(url); 1759e5b75505Sopenharmony_ci pos = next; 1760e5b75505Sopenharmony_ci continue; 1761e5b75505Sopenharmony_ci } 1762e5b75505Sopenharmony_ci#endif /* CONFIG_HS20 */ 1763e5b75505Sopenharmony_ci 1764e5b75505Sopenharmony_ci pos = next; 1765e5b75505Sopenharmony_ci } 1766e5b75505Sopenharmony_ci} 1767e5b75505Sopenharmony_ci 1768e5b75505Sopenharmony_ci 1769e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, 1770e5b75505Sopenharmony_ci const u8 *sa, const u8 *frm, int len) 1771e5b75505Sopenharmony_ci{ 1772e5b75505Sopenharmony_ci const u8 *pos, *end; 1773e5b75505Sopenharmony_ci u8 dialog_token, type; 1774e5b75505Sopenharmony_ci 1775e5b75505Sopenharmony_ci /* Dialog Token [1] | Type [1] | Subelements */ 1776e5b75505Sopenharmony_ci 1777e5b75505Sopenharmony_ci if (len < 2 || sa == NULL) 1778e5b75505Sopenharmony_ci return; 1779e5b75505Sopenharmony_ci end = frm + len; 1780e5b75505Sopenharmony_ci pos = frm; 1781e5b75505Sopenharmony_ci dialog_token = *pos++; 1782e5b75505Sopenharmony_ci type = *pos++; 1783e5b75505Sopenharmony_ci 1784e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " 1785e5b75505Sopenharmony_ci "(dialog_token %u type %u sa " MACSTR ")", 1786e5b75505Sopenharmony_ci dialog_token, type, MAC2STR(sa)); 1787e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", 1788e5b75505Sopenharmony_ci pos, end - pos); 1789e5b75505Sopenharmony_ci 1790e5b75505Sopenharmony_ci if (wpa_s->wpa_state != WPA_COMPLETED || 1791e5b75505Sopenharmony_ci os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { 1792e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " 1793e5b75505Sopenharmony_ci "from our AP - ignore it"); 1794e5b75505Sopenharmony_ci return; 1795e5b75505Sopenharmony_ci } 1796e5b75505Sopenharmony_ci 1797e5b75505Sopenharmony_ci switch (type) { 1798e5b75505Sopenharmony_ci case 1: 1799e5b75505Sopenharmony_ci ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); 1800e5b75505Sopenharmony_ci break; 1801e5b75505Sopenharmony_ci default: 1802e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " 1803e5b75505Sopenharmony_ci "WNM-Notification type %u", type); 1804e5b75505Sopenharmony_ci break; 1805e5b75505Sopenharmony_ci } 1806e5b75505Sopenharmony_ci} 1807e5b75505Sopenharmony_ci 1808e5b75505Sopenharmony_ci 1809e5b75505Sopenharmony_cistatic void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s, 1810e5b75505Sopenharmony_ci const u8 *sa, const u8 *frm, 1811e5b75505Sopenharmony_ci int len) 1812e5b75505Sopenharmony_ci{ 1813e5b75505Sopenharmony_ci u8 dialog_token, req_info, auto_report, timeout; 1814e5b75505Sopenharmony_ci 1815e5b75505Sopenharmony_ci if (!wpa_s->conf->coloc_intf_reporting) 1816e5b75505Sopenharmony_ci return; 1817e5b75505Sopenharmony_ci 1818e5b75505Sopenharmony_ci /* Dialog Token [1] | Request Info [1] */ 1819e5b75505Sopenharmony_ci 1820e5b75505Sopenharmony_ci if (len < 2) 1821e5b75505Sopenharmony_ci return; 1822e5b75505Sopenharmony_ci dialog_token = frm[0]; 1823e5b75505Sopenharmony_ci req_info = frm[1]; 1824e5b75505Sopenharmony_ci auto_report = req_info & 0x03; 1825e5b75505Sopenharmony_ci timeout = req_info >> 2; 1826e5b75505Sopenharmony_ci 1827e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1828e5b75505Sopenharmony_ci "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")", 1829e5b75505Sopenharmony_ci dialog_token, auto_report, timeout, MAC2STR(sa)); 1830e5b75505Sopenharmony_ci 1831e5b75505Sopenharmony_ci if (dialog_token == 0) 1832e5b75505Sopenharmony_ci return; /* only nonzero values are used for request */ 1833e5b75505Sopenharmony_ci 1834e5b75505Sopenharmony_ci if (wpa_s->wpa_state != WPA_COMPLETED || 1835e5b75505Sopenharmony_ci os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { 1836e5b75505Sopenharmony_ci wpa_dbg(wpa_s, MSG_DEBUG, 1837e5b75505Sopenharmony_ci "WNM: Collocated Interference Request frame not from current AP - ignore it"); 1838e5b75505Sopenharmony_ci return; 1839e5b75505Sopenharmony_ci } 1840e5b75505Sopenharmony_ci 1841e5b75505Sopenharmony_ci wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u", 1842e5b75505Sopenharmony_ci dialog_token, auto_report, timeout); 1843e5b75505Sopenharmony_ci wpa_s->coloc_intf_dialog_token = dialog_token; 1844e5b75505Sopenharmony_ci wpa_s->coloc_intf_auto_report = auto_report; 1845e5b75505Sopenharmony_ci wpa_s->coloc_intf_timeout = timeout; 1846e5b75505Sopenharmony_ci} 1847e5b75505Sopenharmony_ci 1848e5b75505Sopenharmony_ci 1849e5b75505Sopenharmony_civoid ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, 1850e5b75505Sopenharmony_ci const struct ieee80211_mgmt *mgmt, size_t len) 1851e5b75505Sopenharmony_ci{ 1852e5b75505Sopenharmony_ci const u8 *pos, *end; 1853e5b75505Sopenharmony_ci u8 act; 1854e5b75505Sopenharmony_ci 1855e5b75505Sopenharmony_ci if (len < IEEE80211_HDRLEN + 2) 1856e5b75505Sopenharmony_ci return; 1857e5b75505Sopenharmony_ci 1858e5b75505Sopenharmony_ci pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; 1859e5b75505Sopenharmony_ci act = *pos++; 1860e5b75505Sopenharmony_ci end = ((const u8 *) mgmt) + len; 1861e5b75505Sopenharmony_ci 1862e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, 1863e5b75505Sopenharmony_ci act, MAC2STR(mgmt->sa)); 1864e5b75505Sopenharmony_ci if (wpa_s->wpa_state < WPA_ASSOCIATED || 1865e5b75505Sopenharmony_ci os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { 1866e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " 1867e5b75505Sopenharmony_ci "frame"); 1868e5b75505Sopenharmony_ci return; 1869e5b75505Sopenharmony_ci } 1870e5b75505Sopenharmony_ci 1871e5b75505Sopenharmony_ci switch (act) { 1872e5b75505Sopenharmony_ci case WNM_BSS_TRANS_MGMT_REQ: 1873e5b75505Sopenharmony_ci ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, 1874e5b75505Sopenharmony_ci !(mgmt->da[0] & 0x01)); 1875e5b75505Sopenharmony_ci break; 1876e5b75505Sopenharmony_ci case WNM_SLEEP_MODE_RESP: 1877e5b75505Sopenharmony_ci ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); 1878e5b75505Sopenharmony_ci break; 1879e5b75505Sopenharmony_ci case WNM_NOTIFICATION_REQ: 1880e5b75505Sopenharmony_ci ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); 1881e5b75505Sopenharmony_ci break; 1882e5b75505Sopenharmony_ci case WNM_COLLOCATED_INTERFERENCE_REQ: 1883e5b75505Sopenharmony_ci ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, 1884e5b75505Sopenharmony_ci end - pos); 1885e5b75505Sopenharmony_ci break; 1886e5b75505Sopenharmony_ci default: 1887e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "WNM: Unknown request"); 1888e5b75505Sopenharmony_ci break; 1889e5b75505Sopenharmony_ci } 1890e5b75505Sopenharmony_ci} 1891e5b75505Sopenharmony_ci 1892e5b75505Sopenharmony_ci 1893e5b75505Sopenharmony_ciint wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, 1894e5b75505Sopenharmony_ci const struct wpabuf *elems) 1895e5b75505Sopenharmony_ci{ 1896e5b75505Sopenharmony_ci struct wpabuf *buf; 1897e5b75505Sopenharmony_ci int ret; 1898e5b75505Sopenharmony_ci 1899e5b75505Sopenharmony_ci if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems) 1900e5b75505Sopenharmony_ci return -1; 1901e5b75505Sopenharmony_ci 1902e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to " 1903e5b75505Sopenharmony_ci MACSTR " (dialog token %u)", 1904e5b75505Sopenharmony_ci MAC2STR(wpa_s->bssid), dialog_token); 1905e5b75505Sopenharmony_ci 1906e5b75505Sopenharmony_ci buf = wpabuf_alloc(3 + wpabuf_len(elems)); 1907e5b75505Sopenharmony_ci if (!buf) 1908e5b75505Sopenharmony_ci return -1; 1909e5b75505Sopenharmony_ci 1910e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_WNM); 1911e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT); 1912e5b75505Sopenharmony_ci wpabuf_put_u8(buf, dialog_token); 1913e5b75505Sopenharmony_ci wpabuf_put_buf(buf, elems); 1914e5b75505Sopenharmony_ci 1915e5b75505Sopenharmony_ci ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 1916e5b75505Sopenharmony_ci wpa_s->own_addr, wpa_s->bssid, 1917e5b75505Sopenharmony_ci wpabuf_head_u8(buf), wpabuf_len(buf), 0); 1918e5b75505Sopenharmony_ci wpabuf_free(buf); 1919e5b75505Sopenharmony_ci return ret; 1920e5b75505Sopenharmony_ci} 1921e5b75505Sopenharmony_ci 1922e5b75505Sopenharmony_ci 1923e5b75505Sopenharmony_civoid wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, 1924e5b75505Sopenharmony_ci struct wpabuf *elems) 1925e5b75505Sopenharmony_ci{ 1926e5b75505Sopenharmony_ci wpabuf_free(wpa_s->coloc_intf_elems); 1927e5b75505Sopenharmony_ci if (elems && wpabuf_len(elems) == 0) { 1928e5b75505Sopenharmony_ci wpabuf_free(elems); 1929e5b75505Sopenharmony_ci elems = NULL; 1930e5b75505Sopenharmony_ci } 1931e5b75505Sopenharmony_ci wpa_s->coloc_intf_elems = elems; 1932e5b75505Sopenharmony_ci 1933e5b75505Sopenharmony_ci if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems && 1934e5b75505Sopenharmony_ci wpa_s->coloc_intf_dialog_token && 1935e5b75505Sopenharmony_ci (wpa_s->coloc_intf_auto_report == 1 || 1936e5b75505Sopenharmony_ci wpa_s->coloc_intf_auto_report == 3)) { 1937e5b75505Sopenharmony_ci /* TODO: Check that there has not been less than 1938e5b75505Sopenharmony_ci * wpa_s->coloc_intf_timeout * 200 TU from the last report. 1939e5b75505Sopenharmony_ci */ 1940e5b75505Sopenharmony_ci wnm_send_coloc_intf_report(wpa_s, 1941e5b75505Sopenharmony_ci wpa_s->coloc_intf_dialog_token, 1942e5b75505Sopenharmony_ci wpa_s->coloc_intf_elems); 1943e5b75505Sopenharmony_ci } 1944e5b75505Sopenharmony_ci} 1945e5b75505Sopenharmony_ci 1946e5b75505Sopenharmony_ci 1947e5b75505Sopenharmony_civoid wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) 1948e5b75505Sopenharmony_ci{ 1949e5b75505Sopenharmony_ci#ifdef CONFIG_WNM 1950e5b75505Sopenharmony_ci wpa_s->coloc_intf_dialog_token = 0; 1951e5b75505Sopenharmony_ci wpa_s->coloc_intf_auto_report = 0; 1952e5b75505Sopenharmony_ci#endif /* CONFIG_WNM */ 1953e5b75505Sopenharmony_ci} 1954