2012-02-26 16:27:19 +01:00
|
|
|
/*
|
|
|
|
* wpa_supplicant - WNM
|
2013-04-21 01:48:10 +02:00
|
|
|
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
|
2012-02-26 16:27:19 +01:00
|
|
|
*
|
|
|
|
* This software may be distributed under the terms of the BSD license.
|
|
|
|
* See README for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "utils/includes.h"
|
|
|
|
|
|
|
|
#include "utils/common.h"
|
|
|
|
#include "common/ieee802_11_defs.h"
|
2014-11-22 18:50:16 +01:00
|
|
|
#include "common/ieee802_11_common.h"
|
2013-04-21 01:48:10 +02:00
|
|
|
#include "common/wpa_ctrl.h"
|
2018-08-06 21:46:33 +02:00
|
|
|
#include "common/ocv.h"
|
2012-02-26 16:27:19 +01:00
|
|
|
#include "rsn_supp/wpa.h"
|
2017-03-06 12:46:00 +01:00
|
|
|
#include "config.h"
|
2012-12-16 11:29:44 +01:00
|
|
|
#include "wpa_supplicant_i.h"
|
|
|
|
#include "driver_i.h"
|
2012-12-22 19:27:30 +01:00
|
|
|
#include "scan.h"
|
2013-05-16 16:48:59 +02:00
|
|
|
#include "ctrl_iface.h"
|
|
|
|
#include "bss.h"
|
|
|
|
#include "wnm_sta.h"
|
2018-05-04 20:16:18 +02:00
|
|
|
#include "notify.h"
|
2012-11-02 12:05:57 +01:00
|
|
|
#include "hs20_supplicant.h"
|
2012-02-26 16:27:19 +01:00
|
|
|
|
|
|
|
#define MAX_TFS_IE_LEN 1024
|
2013-05-16 16:48:59 +02:00
|
|
|
#define WNM_MAX_NEIGHBOR_REPORT 10
|
2012-02-26 16:27:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* get the TFS IE from driver */
|
|
|
|
static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
|
|
|
|
u16 *buf_len, enum wnm_oper oper)
|
|
|
|
{
|
|
|
|
wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
|
|
|
|
|
|
|
|
return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* set the TFS IE to driver */
|
|
|
|
static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
|
2015-10-25 13:40:35 +01:00
|
|
|
const u8 *addr, const u8 *buf, u16 buf_len,
|
2012-02-26 16:27:19 +01:00
|
|
|
enum wnm_oper oper)
|
|
|
|
{
|
2015-10-25 13:40:35 +01:00
|
|
|
u16 len = buf_len;
|
|
|
|
|
2012-02-26 16:27:19 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
|
|
|
|
|
2015-10-25 13:40:35 +01:00
|
|
|
return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
|
2012-02-26 16:27:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* MLME-SLEEPMODE.request */
|
|
|
|
int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
|
2012-12-16 11:31:16 +01:00
|
|
|
u8 action, u16 intval, struct wpabuf *tfs_req)
|
2012-02-26 16:27:19 +01:00
|
|
|
{
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
|
int res;
|
|
|
|
size_t len;
|
|
|
|
struct wnm_sleep_element *wnmsleep_ie;
|
2018-08-06 21:46:33 +02:00
|
|
|
u8 *wnmtfs_ie, *oci_ie;
|
|
|
|
u8 wnmsleep_ie_len, oci_ie_len;
|
2012-02-26 16:27:19 +01:00
|
|
|
u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
|
|
|
|
enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
|
|
|
|
WNM_SLEEP_TFS_REQ_IE_NONE;
|
|
|
|
|
2012-12-16 11:31:16 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
|
|
|
|
"action=%s to " MACSTR,
|
|
|
|
action == 0 ? "enter" : "exit",
|
|
|
|
MAC2STR(wpa_s->bssid));
|
|
|
|
|
2012-02-26 16:27:19 +01:00
|
|
|
/* WNM-Sleep Mode IE */
|
|
|
|
wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
|
|
|
|
wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
|
|
|
|
if (wnmsleep_ie == NULL)
|
|
|
|
return -1;
|
|
|
|
wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
|
|
|
|
wnmsleep_ie->len = wnmsleep_ie_len - 2;
|
|
|
|
wnmsleep_ie->action_type = action;
|
|
|
|
wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
|
2012-12-16 11:29:10 +01:00
|
|
|
wnmsleep_ie->intval = host_to_le16(intval);
|
2012-12-16 11:31:16 +01:00
|
|
|
wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
|
|
|
|
(u8 *) wnmsleep_ie, wnmsleep_ie_len);
|
2012-02-26 16:27:19 +01:00
|
|
|
|
|
|
|
/* TFS IE(s) */
|
2012-12-16 11:31:16 +01:00
|
|
|
if (tfs_req) {
|
|
|
|
wnmtfs_ie_len = wpabuf_len(tfs_req);
|
2017-03-07 10:17:23 +01:00
|
|
|
wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len);
|
2012-12-16 11:31:16 +01:00
|
|
|
if (wnmtfs_ie == NULL) {
|
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
|
|
|
|
if (wnmtfs_ie == NULL) {
|
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
|
|
|
|
tfs_oper)) {
|
|
|
|
wnmtfs_ie_len = 0;
|
|
|
|
os_free(wnmtfs_ie);
|
|
|
|
wnmtfs_ie = NULL;
|
|
|
|
}
|
2012-02-26 16:27:19 +01:00
|
|
|
}
|
2012-12-16 11:31:16 +01:00
|
|
|
wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
|
|
|
|
(u8 *) wnmtfs_ie, wnmtfs_ie_len);
|
2012-02-26 16:27:19 +01:00
|
|
|
|
2018-08-06 21:46:33 +02:00
|
|
|
oci_ie = NULL;
|
|
|
|
oci_ie_len = 0;
|
|
|
|
#ifdef CONFIG_OCV
|
|
|
|
if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) {
|
|
|
|
struct wpa_channel_info ci;
|
|
|
|
|
|
|
|
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
|
|
|
|
wpa_printf(MSG_WARNING,
|
|
|
|
"Failed to get channel info for OCI element in WNM-Sleep Mode frame");
|
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
os_free(wnmtfs_ie);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-06-26 10:19:14 +02:00
|
|
|
#ifdef CONFIG_TESTING_OPTIONS
|
|
|
|
if (wpa_s->oci_freq_override_wnm_sleep) {
|
|
|
|
wpa_printf(MSG_INFO,
|
|
|
|
"TEST: Override OCI KDE frequency %d -> %d MHz",
|
|
|
|
ci.frequency,
|
|
|
|
wpa_s->oci_freq_override_wnm_sleep);
|
|
|
|
ci.frequency = wpa_s->oci_freq_override_wnm_sleep;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_TESTING_OPTIONS */
|
2018-08-06 21:46:33 +02:00
|
|
|
|
|
|
|
oci_ie_len = OCV_OCI_EXTENDED_LEN;
|
|
|
|
oci_ie = os_zalloc(oci_ie_len);
|
|
|
|
if (!oci_ie) {
|
|
|
|
wpa_printf(MSG_WARNING,
|
|
|
|
"Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame");
|
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
os_free(wnmtfs_ie);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
|
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
os_free(wnmtfs_ie);
|
|
|
|
os_free(oci_ie);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_OCV */
|
|
|
|
|
|
|
|
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len +
|
|
|
|
oci_ie_len);
|
2012-02-26 16:27:19 +01:00
|
|
|
if (mgmt == NULL) {
|
|
|
|
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
|
|
|
|
"WNM-Sleep Request action frame");
|
2012-12-16 18:31:25 +01:00
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
os_free(wnmtfs_ie);
|
2012-02-26 16:27:19 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
|
|
|
|
os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
|
|
|
|
os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
|
|
|
|
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
|
|
|
|
WLAN_FC_STYPE_ACTION);
|
|
|
|
mgmt->u.action.category = WLAN_ACTION_WNM;
|
|
|
|
mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
|
2012-12-16 11:32:53 +01:00
|
|
|
mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
|
2012-02-26 16:27:19 +01:00
|
|
|
os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
|
|
|
|
wnmsleep_ie_len);
|
|
|
|
/* copy TFS IE here */
|
|
|
|
if (wnmtfs_ie_len > 0) {
|
|
|
|
os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
|
|
|
|
wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:46:33 +02:00
|
|
|
#ifdef CONFIG_OCV
|
|
|
|
/* copy OCV OCI here */
|
|
|
|
if (oci_ie_len > 0) {
|
|
|
|
os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
|
|
|
|
wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_OCV */
|
|
|
|
|
2012-02-26 16:27:19 +01:00
|
|
|
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
|
2018-08-06 21:46:33 +02:00
|
|
|
wnmtfs_ie_len + oci_ie_len;
|
2012-02-26 16:27:19 +01:00
|
|
|
|
|
|
|
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
|
|
|
|
wpa_s->own_addr, wpa_s->bssid,
|
|
|
|
&mgmt->u.action.category, len, 0);
|
|
|
|
if (res < 0)
|
|
|
|
wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
|
|
|
|
"(action=%d, intval=%d)", action, intval);
|
2015-10-25 22:02:14 +01:00
|
|
|
else
|
|
|
|
wpa_s->wnmsleep_used = 1;
|
2012-02-26 16:27:19 +01:00
|
|
|
|
|
|
|
os_free(wnmsleep_ie);
|
|
|
|
os_free(wnmtfs_ie);
|
2018-08-06 21:46:33 +02:00
|
|
|
os_free(oci_ie);
|
2012-02-26 16:27:19 +01:00
|
|
|
os_free(mgmt);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-16 12:05:19 +01:00
|
|
|
static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
|
2015-10-25 13:40:35 +01:00
|
|
|
const u8 *tfsresp_ie_start,
|
|
|
|
const u8 *tfsresp_ie_end)
|
2012-12-16 12:05:19 +01:00
|
|
|
{
|
|
|
|
wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
|
|
|
|
wpa_s->bssid, NULL, NULL);
|
|
|
|
/* remove GTK/IGTK ?? */
|
|
|
|
|
|
|
|
/* set the TFS Resp IE(s) */
|
|
|
|
if (tfsresp_ie_start && tfsresp_ie_end &&
|
|
|
|
tfsresp_ie_end - tfsresp_ie_start >= 0) {
|
|
|
|
u16 tfsresp_ie_len;
|
|
|
|
tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
|
|
|
|
tfsresp_ie_start;
|
|
|
|
wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
|
|
|
|
/* pass the TFS Resp IE(s) to driver for processing */
|
|
|
|
if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
|
|
|
|
tfsresp_ie_start,
|
2015-10-25 13:40:35 +01:00
|
|
|
tfsresp_ie_len,
|
2012-12-16 12:05:19 +01:00
|
|
|
WNM_SLEEP_TFS_RESP_IE_SET))
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *frm, u16 key_len_total)
|
|
|
|
{
|
|
|
|
u8 *ptr, *end;
|
|
|
|
u8 gtk_len;
|
|
|
|
|
|
|
|
wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid,
|
|
|
|
NULL, NULL);
|
|
|
|
|
|
|
|
/* Install GTK/IGTK */
|
|
|
|
|
|
|
|
/* point to key data field */
|
2013-12-29 10:22:23 +01:00
|
|
|
ptr = (u8 *) frm + 1 + 2;
|
2012-12-16 12:05:19 +01:00
|
|
|
end = ptr + key_len_total;
|
|
|
|
wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
|
|
|
|
|
2015-10-25 14:45:50 +01:00
|
|
|
if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) {
|
|
|
|
wpa_msg(wpa_s, MSG_INFO,
|
|
|
|
"WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-18 10:50:07 +02:00
|
|
|
while (end - ptr > 1) {
|
|
|
|
if (2 + ptr[1] > end - ptr) {
|
2012-12-16 12:05:19 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
|
|
|
|
"length");
|
|
|
|
if (end > ptr) {
|
|
|
|
wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
|
|
|
|
ptr, end - ptr);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
|
|
|
|
if (ptr[1] < 11 + 5) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
|
|
|
|
"subelem");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gtk_len = *(ptr + 4);
|
|
|
|
if (ptr[1] < 11 + gtk_len ||
|
|
|
|
gtk_len < 5 || gtk_len > 32) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
|
|
|
|
"subelem");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wpa_wnmsleep_install_key(
|
|
|
|
wpa_s->wpa,
|
|
|
|
WNM_SLEEP_SUBELEM_GTK,
|
|
|
|
ptr);
|
|
|
|
ptr += 13 + gtk_len;
|
|
|
|
} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
|
|
|
|
if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
|
|
|
|
"subelem");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wpa_wnmsleep_install_key(wpa_s->wpa,
|
|
|
|
WNM_SLEEP_SUBELEM_IGTK, ptr);
|
|
|
|
ptr += 10 + WPA_IGTK_LEN;
|
2020-02-17 23:06:26 +01:00
|
|
|
} else if (*ptr == WNM_SLEEP_SUBELEM_BIGTK) {
|
|
|
|
if (ptr[1] < 2 + 6 + WPA_BIGTK_LEN) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Too short BIGTK subelem");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wpa_wnmsleep_install_key(wpa_s->wpa,
|
|
|
|
WNM_SLEEP_SUBELEM_BIGTK, ptr);
|
|
|
|
ptr += 10 + WPA_BIGTK_LEN;
|
2012-12-16 12:05:19 +01:00
|
|
|
} else
|
|
|
|
break; /* skip the loop */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-26 16:27:19 +01:00
|
|
|
static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *frm, int len)
|
|
|
|
{
|
|
|
|
/*
|
2014-04-05 18:52:18 +02:00
|
|
|
* Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
|
2012-02-26 16:27:19 +01:00
|
|
|
* WNM-Sleep Mode IE | TFS Response IE
|
|
|
|
*/
|
2015-10-25 13:40:35 +01:00
|
|
|
const u8 *pos = frm; /* point to payload after the action field */
|
2014-04-05 18:52:18 +02:00
|
|
|
u16 key_len_total;
|
2012-02-26 16:27:19 +01:00
|
|
|
struct wnm_sleep_element *wnmsleep_ie = NULL;
|
|
|
|
/* multiple TFS Resp IE (assuming consecutive) */
|
2015-10-25 13:40:35 +01:00
|
|
|
const u8 *tfsresp_ie_start = NULL;
|
|
|
|
const u8 *tfsresp_ie_end = NULL;
|
2018-08-06 21:46:33 +02:00
|
|
|
#ifdef CONFIG_OCV
|
|
|
|
const u8 *oci_ie = NULL;
|
|
|
|
u8 oci_ie_len = 0;
|
|
|
|
#endif /* CONFIG_OCV */
|
2014-11-23 17:04:02 +01:00
|
|
|
size_t left;
|
2012-02-26 16:27:19 +01:00
|
|
|
|
2015-10-25 22:02:14 +01:00
|
|
|
if (!wpa_s->wnmsleep_used) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
2017-09-22 10:25:02 +02:00
|
|
|
"WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested");
|
2015-10-25 22:02:14 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-05 18:52:18 +02:00
|
|
|
if (len < 3)
|
|
|
|
return;
|
|
|
|
key_len_total = WPA_GET_LE16(frm + 1);
|
|
|
|
|
2013-12-29 10:22:23 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
|
|
|
|
frm[0], key_len_total);
|
2014-11-23 17:04:02 +01:00
|
|
|
left = len - 3;
|
|
|
|
if (key_len_total > left) {
|
2012-12-16 11:48:34 +01:00
|
|
|
wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
|
|
|
|
return;
|
|
|
|
}
|
2014-11-23 17:04:02 +01:00
|
|
|
pos += 3 + key_len_total;
|
2015-10-18 10:50:07 +02:00
|
|
|
while (pos - frm + 1 < len) {
|
2012-02-26 16:27:19 +01:00
|
|
|
u8 ie_len = *(pos + 1);
|
2015-10-18 10:50:07 +02:00
|
|
|
if (2 + ie_len > frm + len - pos) {
|
2012-12-16 11:48:34 +01:00
|
|
|
wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
|
2015-10-25 13:45:09 +01:00
|
|
|
if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
|
2012-02-26 16:27:19 +01:00
|
|
|
wnmsleep_ie = (struct wnm_sleep_element *) pos;
|
|
|
|
else if (*pos == WLAN_EID_TFS_RESP) {
|
|
|
|
if (!tfsresp_ie_start)
|
|
|
|
tfsresp_ie_start = pos;
|
|
|
|
tfsresp_ie_end = pos;
|
2018-08-06 21:46:33 +02:00
|
|
|
#ifdef CONFIG_OCV
|
|
|
|
} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
|
|
|
|
pos[2] == WLAN_EID_EXT_OCV_OCI) {
|
|
|
|
oci_ie = pos + 3;
|
|
|
|
oci_ie_len = ie_len - 1;
|
|
|
|
#endif /* CONFIG_OCV */
|
2012-02-26 16:27:19 +01:00
|
|
|
} else
|
|
|
|
wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
|
|
|
|
pos += ie_len + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wnmsleep_ie) {
|
|
|
|
wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:46:33 +02:00
|
|
|
#ifdef CONFIG_OCV
|
|
|
|
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
|
|
|
|
wpa_sm_ocv_enabled(wpa_s->wpa)) {
|
|
|
|
struct wpa_channel_info ci;
|
|
|
|
|
|
|
|
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
|
|
|
|
wpa_msg(wpa_s, MSG_WARNING,
|
|
|
|
"Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
|
|
|
|
channel_width_to_int(ci.chanwidth),
|
2020-09-02 20:55:28 +02:00
|
|
|
ci.seg1_idx) != OCI_SUCCESS) {
|
2020-05-25 17:33:00 +02:00
|
|
|
wpa_msg(wpa_s, MSG_WARNING, "WNM: OCV failed: %s",
|
|
|
|
ocv_errorstr);
|
2018-08-06 21:46:33 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_OCV */
|
|
|
|
|
2017-09-22 10:25:02 +02:00
|
|
|
wpa_s->wnmsleep_used = 0;
|
|
|
|
|
2012-12-16 11:33:55 +01:00
|
|
|
if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
|
|
|
|
wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
|
2012-02-26 16:27:19 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
|
|
|
|
"frame (action=%d, intval=%d)",
|
|
|
|
wnmsleep_ie->action_type, wnmsleep_ie->intval);
|
2012-12-16 11:57:38 +01:00
|
|
|
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
|
2012-12-16 12:05:19 +01:00
|
|
|
wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
|
|
|
|
tfsresp_ie_end);
|
2012-12-16 11:57:38 +01:00
|
|
|
} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
|
2012-12-16 12:05:19 +01:00
|
|
|
wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
|
2012-02-26 16:27:19 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
|
|
|
|
"(action=%d, intval=%d)",
|
|
|
|
wnmsleep_ie->action_type, wnmsleep_ie->intval);
|
2012-12-16 11:57:38 +01:00
|
|
|
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
|
2012-02-26 16:27:19 +01:00
|
|
|
wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
|
|
|
|
wpa_s->bssid, NULL, NULL);
|
2012-12-16 11:57:38 +01:00
|
|
|
else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
|
2012-02-26 16:27:19 +01:00
|
|
|
wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
|
|
|
|
wpa_s->bssid, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-02-20 14:18:27 +01:00
|
|
|
void wnm_btm_reset(struct wpa_supplicant *wpa_s)
|
2013-05-16 16:48:59 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
|
|
|
|
os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
|
|
|
|
os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
|
|
|
|
}
|
|
|
|
|
2014-04-07 12:35:18 +02:00
|
|
|
wpa_s->wnm_num_neighbor_report = 0;
|
2013-05-16 16:48:59 +02:00
|
|
|
os_free(wpa_s->wnm_neighbor_report_elements);
|
|
|
|
wpa_s->wnm_neighbor_report_elements = NULL;
|
2024-02-20 14:18:27 +01:00
|
|
|
|
|
|
|
wpa_s->wnm_cand_valid_until.sec = 0;
|
|
|
|
wpa_s->wnm_cand_valid_until.usec = 0;
|
|
|
|
|
|
|
|
wpa_s->wnm_mode = 0;
|
|
|
|
wpa_s->wnm_dialog_token = 0;
|
|
|
|
wpa_s->wnm_reply = 0;
|
|
|
|
|
|
|
|
#ifdef CONFIG_MBO
|
|
|
|
wpa_s->wnm_mbo_trans_reason_present = 0;
|
|
|
|
wpa_s->wnm_mbo_transition_reason = 0;
|
|
|
|
#endif /* CONFIG_MBO */
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
|
|
|
|
u8 id, u8 elen, const u8 *pos)
|
|
|
|
{
|
|
|
|
switch (id) {
|
|
|
|
case WNM_NEIGHBOR_TSF:
|
|
|
|
if (elen < 2 + 2) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
|
|
|
|
break;
|
|
|
|
}
|
2014-11-22 14:37:27 +01:00
|
|
|
rep->tsf_offset = WPA_GET_LE16(pos);
|
|
|
|
rep->beacon_int = WPA_GET_LE16(pos + 2);
|
|
|
|
rep->tsf_present = 1;
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
|
|
|
|
if (elen < 2) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
|
|
|
|
"country string");
|
|
|
|
break;
|
|
|
|
}
|
2014-11-22 14:37:27 +01:00
|
|
|
os_memcpy(rep->country, pos, 2);
|
|
|
|
rep->country_present = 1;
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
|
|
|
|
if (elen < 1) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
|
|
|
|
"candidate");
|
|
|
|
break;
|
|
|
|
}
|
2014-11-22 14:37:27 +01:00
|
|
|
rep->preference = pos[0];
|
|
|
|
rep->preference_present = 1;
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
|
2019-05-24 15:59:25 +02:00
|
|
|
if (elen < 10) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Too short BSS termination duration");
|
|
|
|
break;
|
|
|
|
}
|
2014-11-22 14:37:27 +01:00
|
|
|
rep->bss_term_tsf = WPA_GET_LE64(pos);
|
|
|
|
rep->bss_term_dur = WPA_GET_LE16(pos + 8);
|
|
|
|
rep->bss_term_present = 1;
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_BEARING:
|
|
|
|
if (elen < 8) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
|
|
|
|
"bearing");
|
|
|
|
break;
|
|
|
|
}
|
2014-11-22 14:37:27 +01:00
|
|
|
rep->bearing = WPA_GET_LE16(pos);
|
|
|
|
rep->distance = WPA_GET_LE32(pos + 2);
|
|
|
|
rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
|
|
|
|
rep->bearing_present = 1;
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_MEASUREMENT_PILOT:
|
2014-04-08 00:32:28 +02:00
|
|
|
if (elen < 1) {
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
|
|
|
|
"pilot");
|
|
|
|
break;
|
|
|
|
}
|
2014-04-08 00:20:24 +02:00
|
|
|
os_free(rep->meas_pilot);
|
2013-05-16 16:48:59 +02:00
|
|
|
rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
|
|
|
|
if (rep->meas_pilot == NULL)
|
|
|
|
break;
|
|
|
|
rep->meas_pilot->measurement_pilot = pos[0];
|
2014-04-08 00:32:28 +02:00
|
|
|
rep->meas_pilot->subelem_len = elen - 1;
|
|
|
|
os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
|
2014-04-08 00:32:28 +02:00
|
|
|
if (elen < 5) {
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
|
|
|
|
"capabilities");
|
|
|
|
break;
|
|
|
|
}
|
2014-11-22 14:37:27 +01:00
|
|
|
os_memcpy(rep->rm_capab, pos, 5);
|
|
|
|
rep->rm_capab_present = 1;
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
|
|
|
case WNM_NEIGHBOR_MULTIPLE_BSSID:
|
2014-04-08 00:32:28 +02:00
|
|
|
if (elen < 1) {
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
|
|
|
|
break;
|
|
|
|
}
|
2014-04-08 00:20:24 +02:00
|
|
|
os_free(rep->mul_bssid);
|
2013-05-16 16:48:59 +02:00
|
|
|
rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
|
|
|
|
if (rep->mul_bssid == NULL)
|
|
|
|
break;
|
|
|
|
rep->mul_bssid->max_bssid_indicator = pos[0];
|
2014-04-08 00:32:28 +02:00
|
|
|
rep->mul_bssid->subelem_len = elen - 1;
|
|
|
|
os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
|
2013-05-16 16:48:59 +02:00
|
|
|
break;
|
2022-10-11 12:57:44 +02:00
|
|
|
default:
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Unsupported neighbor report subelement id %u",
|
|
|
|
id);
|
|
|
|
break;
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-22 18:50:16 +01:00
|
|
|
static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
|
|
|
|
{
|
2014-11-24 09:57:38 +01:00
|
|
|
struct wpa_bss *bss = wpa_s->current_bss;
|
|
|
|
const char *country = NULL;
|
2016-02-05 16:06:06 +01:00
|
|
|
int freq;
|
2014-11-24 09:57:38 +01:00
|
|
|
|
|
|
|
if (bss) {
|
|
|
|
const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
|
|
|
|
|
|
|
|
if (elem && elem[1] >= 2)
|
|
|
|
country = (const char *) (elem + 2);
|
|
|
|
}
|
|
|
|
|
2016-02-05 16:06:06 +01:00
|
|
|
freq = ieee80211_chan_to_freq(country, op_class, chan);
|
|
|
|
if (freq <= 0 && op_class == 0) {
|
|
|
|
/*
|
|
|
|
* Some APs do not advertise correct operating class
|
|
|
|
* information. Try to determine the most likely operating
|
|
|
|
* frequency based on the channel number.
|
|
|
|
*/
|
|
|
|
if (chan >= 1 && chan <= 13)
|
|
|
|
freq = 2407 + chan * 5;
|
|
|
|
else if (chan == 14)
|
|
|
|
freq = 2484;
|
2020-11-23 09:00:02 +01:00
|
|
|
else if (chan >= 36 && chan <= 177)
|
2016-02-05 16:06:06 +01:00
|
|
|
freq = 5000 + chan * 5;
|
|
|
|
}
|
|
|
|
return freq;
|
2014-11-22 18:50:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-16 16:48:59 +02:00
|
|
|
static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *pos, u8 len,
|
|
|
|
struct neighbor_report *rep)
|
|
|
|
{
|
|
|
|
u8 left = len;
|
|
|
|
|
|
|
|
if (left < 13) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memcpy(rep->bssid, pos, ETH_ALEN);
|
2014-11-22 12:43:17 +01:00
|
|
|
rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
|
2013-05-16 16:48:59 +02:00
|
|
|
rep->regulatory_class = *(pos + 10);
|
|
|
|
rep->channel_number = *(pos + 11);
|
|
|
|
rep->phy_type = *(pos + 12);
|
|
|
|
|
|
|
|
pos += 13;
|
|
|
|
left -= 13;
|
|
|
|
|
|
|
|
while (left >= 2) {
|
|
|
|
u8 id, elen;
|
|
|
|
|
|
|
|
id = *pos++;
|
|
|
|
elen = *pos++;
|
2014-04-07 23:53:55 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
|
|
|
|
left -= 2;
|
|
|
|
if (elen > left) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Truncated neighbor report subelement");
|
|
|
|
break;
|
|
|
|
}
|
2013-05-16 16:48:59 +02:00
|
|
|
wnm_parse_neighbor_report_elem(rep, id, elen, pos);
|
2014-04-07 23:53:55 +02:00
|
|
|
left -= elen;
|
2013-05-16 16:48:59 +02:00
|
|
|
pos += elen;
|
|
|
|
}
|
2014-11-22 18:50:16 +01:00
|
|
|
|
|
|
|
rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
|
|
|
|
rep->channel_number);
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-06 12:46:00 +01:00
|
|
|
static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
|
|
|
|
wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MBO
|
2014-11-22 17:04:21 +01:00
|
|
|
static struct wpa_bss *
|
2017-03-06 12:46:00 +01:00
|
|
|
get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
|
|
|
|
enum mbo_transition_reject_reason *reason)
|
2013-05-16 16:48:59 +02:00
|
|
|
{
|
2017-03-06 12:46:00 +01:00
|
|
|
struct wpa_bss *target = NULL;
|
|
|
|
struct wpa_bss_trans_info params;
|
|
|
|
struct wpa_bss_candidate_info *info = NULL;
|
|
|
|
struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
|
|
|
|
u8 *first_candidate_bssid = NULL, *pos;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
|
|
|
|
params.n_candidates = 0;
|
|
|
|
params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
|
|
|
|
if (!params.bssid)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pos = params.bssid;
|
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
|
|
|
|
if (nei->is_first)
|
|
|
|
first_candidate_bssid = nei->bssid;
|
|
|
|
if (!nei->acceptable)
|
|
|
|
continue;
|
|
|
|
os_memcpy(pos, nei->bssid, ETH_ALEN);
|
|
|
|
pos += ETH_ALEN;
|
|
|
|
params.n_candidates++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!params.n_candidates)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms);
|
|
|
|
if (!info) {
|
|
|
|
/* If failed to get candidate BSS transition status from driver,
|
|
|
|
* get the first acceptable candidate from wpa_supplicant.
|
|
|
|
*/
|
|
|
|
target = wpa_bss_get_bssid(wpa_s, params.bssid);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the first acceptable candidate from driver */
|
|
|
|
for (i = 0; i < info->num; i++) {
|
|
|
|
if (info->candidates[i].is_accept) {
|
|
|
|
target = wpa_bss_get_bssid(wpa_s,
|
|
|
|
info->candidates[i].bssid);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If Disassociation Imminent is set and driver rejects all the
|
|
|
|
* candidate select first acceptable candidate which has
|
|
|
|
* rssi > disassoc_imminent_rssi_threshold
|
|
|
|
*/
|
|
|
|
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
|
|
|
|
for (i = 0; i < info->num; i++) {
|
|
|
|
target = wpa_bss_get_bssid(wpa_s,
|
|
|
|
info->candidates[i].bssid);
|
2017-04-28 12:52:08 +02:00
|
|
|
if (target &&
|
|
|
|
(target->level <
|
|
|
|
wpa_s->conf->disassoc_imminent_rssi_threshold))
|
2017-03-06 12:46:00 +01:00
|
|
|
continue;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* While sending BTM reject use reason code of the first candidate
|
|
|
|
* received in BTM request frame
|
|
|
|
*/
|
|
|
|
if (reason) {
|
|
|
|
for (i = 0; i < info->num; i++) {
|
|
|
|
if (first_candidate_bssid &&
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
ether_addr_equal(first_candidate_bssid,
|
|
|
|
info->candidates[i].bssid)) {
|
2017-03-06 12:46:00 +01:00
|
|
|
*reason = info->candidates[i].reject_reason;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
target = NULL;
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2017-03-06 12:46:00 +01:00
|
|
|
end:
|
|
|
|
os_free(params.bssid);
|
|
|
|
if (info) {
|
|
|
|
os_free(info->candidates);
|
|
|
|
os_free(info);
|
|
|
|
}
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_MBO */
|
|
|
|
|
|
|
|
|
2023-07-27 18:02:11 +02:00
|
|
|
static struct wpa_bss * find_better_target(struct wpa_bss *a,
|
|
|
|
struct wpa_bss *b)
|
|
|
|
{
|
|
|
|
if (!a)
|
|
|
|
return b;
|
|
|
|
if (!b)
|
|
|
|
return a;
|
|
|
|
|
|
|
|
if (a->est_throughput > b->est_throughput) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: A is better: " MACSTR
|
|
|
|
" est-tput: %d B: " MACSTR " est-tput: %d",
|
|
|
|
MAC2STR(a->bssid), a->est_throughput,
|
|
|
|
MAC2STR(b->bssid), b->est_throughput);
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: B is better, A: " MACSTR
|
|
|
|
" est-tput: %d B: " MACSTR " est-tput: %d",
|
|
|
|
MAC2STR(a->bssid), a->est_throughput,
|
|
|
|
MAC2STR(b->bssid), b->est_throughput);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2017-03-06 12:46:00 +01:00
|
|
|
static struct wpa_bss *
|
2024-04-29 13:51:46 +02:00
|
|
|
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
|
2017-03-06 12:46:00 +01:00
|
|
|
enum mbo_transition_reject_reason *reason)
|
|
|
|
{
|
2014-11-22 17:04:21 +01:00
|
|
|
u8 i;
|
2014-11-22 16:01:26 +01:00
|
|
|
struct wpa_bss *bss = wpa_s->current_bss;
|
2014-11-22 17:04:21 +01:00
|
|
|
struct wpa_bss *target;
|
2023-07-27 18:02:11 +02:00
|
|
|
struct wpa_bss *best_target = NULL;
|
|
|
|
struct wpa_bss *bss_in_list = NULL;
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
if (!bss)
|
2016-06-23 12:11:01 +02:00
|
|
|
return NULL;
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2013-12-18 07:32:44 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
|
2014-11-22 16:01:26 +01:00
|
|
|
MAC2STR(wpa_s->bssid), bss->level);
|
2013-12-18 07:32:44 +01:00
|
|
|
|
2017-03-06 12:46:00 +01:00
|
|
|
wnm_clear_acceptable(wpa_s);
|
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
|
|
|
|
struct neighbor_report *nei;
|
2014-11-22 16:01:26 +01:00
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
nei = &wpa_s->wnm_neighbor_report_elements[i];
|
2014-11-22 16:01:26 +01:00
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
target = wpa_bss_get_bssid(wpa_s, nei->bssid);
|
|
|
|
if (!target) {
|
2014-11-22 16:01:26 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
|
|
|
|
" (pref %d) not found in scan results",
|
|
|
|
MAC2STR(nei->bssid),
|
|
|
|
nei->preference_present ? nei->preference :
|
|
|
|
-1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-02-20 14:18:21 +01:00
|
|
|
/*
|
|
|
|
* TODO: Could consider allowing transition to another ESS if
|
|
|
|
* PMF was enabled for the association.
|
|
|
|
*/
|
2024-02-20 14:18:20 +01:00
|
|
|
if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
|
2016-11-13 17:22:38 +01:00
|
|
|
1, 0)) {
|
2016-03-24 11:11:55 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
|
|
|
|
" (pref %d) does not match the current network profile",
|
|
|
|
MAC2STR(nei->bssid),
|
|
|
|
nei->preference_present ? nei->preference :
|
|
|
|
-1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
if (target->level < bss->level && target->level < -80) {
|
2014-11-22 16:01:26 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
|
|
|
|
" (pref %d) does not have sufficient signal level (%d)",
|
|
|
|
MAC2STR(nei->bssid),
|
|
|
|
nei->preference_present ? nei->preference :
|
|
|
|
-1,
|
2014-11-22 17:04:21 +01:00
|
|
|
target->level);
|
2014-11-22 16:01:26 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-03-06 12:46:00 +01:00
|
|
|
nei->acceptable = 1;
|
2023-07-27 18:02:11 +02:00
|
|
|
|
|
|
|
best_target = find_better_target(target, best_target);
|
|
|
|
if (target == bss)
|
|
|
|
bss_in_list = bss;
|
2017-03-06 12:46:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MBO
|
|
|
|
if (wpa_s->wnm_mbo_trans_reason_present)
|
|
|
|
target = get_mbo_transition_candidate(wpa_s, reason);
|
|
|
|
else
|
2023-07-27 18:02:11 +02:00
|
|
|
target = best_target;
|
2017-03-06 12:46:00 +01:00
|
|
|
#else /* CONFIG_MBO */
|
2023-07-27 18:02:11 +02:00
|
|
|
target = best_target;
|
2017-03-06 12:46:00 +01:00
|
|
|
#endif /* CONFIG_MBO */
|
|
|
|
|
2023-07-27 18:02:11 +02:00
|
|
|
if (!target)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Found an acceptable preferred transition candidate BSS "
|
|
|
|
MACSTR " (RSSI %d, tput: %d bss-tput: %d)",
|
|
|
|
MAC2STR(target->bssid), target->level,
|
|
|
|
target->est_throughput, bss->est_throughput);
|
|
|
|
|
|
|
|
if (!bss_in_list)
|
|
|
|
return target;
|
|
|
|
|
|
|
|
if ((!target->est_throughput && !bss_in_list->est_throughput) ||
|
|
|
|
(target->est_throughput > bss_in_list->est_throughput &&
|
|
|
|
target->est_throughput - bss_in_list->est_throughput >
|
|
|
|
bss_in_list->est_throughput >> 4)) {
|
|
|
|
/* It is more than 100/16 percent better, so switch. */
|
|
|
|
return target;
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
2023-07-27 18:02:11 +02:00
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Stay with our current BSS, not enough change in estimated throughput to switch");
|
|
|
|
return bss_in_list;
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-15 15:53:41 +01:00
|
|
|
static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
|
|
|
|
{
|
|
|
|
const u8 *ie_a, *ie_b;
|
|
|
|
|
|
|
|
if (!a || !b)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ie_a = wpa_bss_get_ie(a, eid);
|
|
|
|
ie_b = wpa_bss_get_ie(b, eid);
|
|
|
|
|
|
|
|
if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
|
|
|
|
{
|
|
|
|
u32 info = 0;
|
|
|
|
|
|
|
|
info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Leave the security and key scope bits unset to indicate that the
|
|
|
|
* security information is not available.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
|
|
|
|
info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
|
|
|
|
if (bss->caps & WLAN_CAPABILITY_QOS)
|
|
|
|
info |= NEI_REP_BSSID_INFO_QOS;
|
|
|
|
if (bss->caps & WLAN_CAPABILITY_APSD)
|
|
|
|
info |= NEI_REP_BSSID_INFO_APSD;
|
|
|
|
if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
|
|
|
|
info |= NEI_REP_BSSID_INFO_RM;
|
|
|
|
if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
|
|
|
|
info |= NEI_REP_BSSID_INFO_DELAYED_BA;
|
|
|
|
if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
|
|
|
|
info |= NEI_REP_BSSID_INFO_IMM_BA;
|
|
|
|
if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
|
|
|
|
info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
|
|
|
|
if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
|
|
|
|
info |= NEI_REP_BSSID_INFO_HT;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
|
|
|
|
u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
|
|
|
|
u8 pref)
|
2016-02-15 15:53:41 +01:00
|
|
|
{
|
2017-03-08 13:37:38 +01:00
|
|
|
if (wpabuf_len(*buf) + 18 >
|
|
|
|
IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: No room in frame for Neighbor Report element");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-15 15:53:41 +01:00
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
if (wpabuf_resize(buf, 18) < 0) {
|
2016-02-15 15:53:41 +01:00
|
|
|
wpa_printf(MSG_DEBUG,
|
2017-03-08 13:37:38 +01:00
|
|
|
"WNM: Failed to allocate memory for Neighbor Report element");
|
2016-02-15 15:53:41 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
|
2016-02-15 15:53:41 +01:00
|
|
|
/* length: 13 for basic neighbor report + 3 for preference subelement */
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_u8(*buf, 16);
|
|
|
|
wpabuf_put_data(*buf, bssid, ETH_ALEN);
|
|
|
|
wpabuf_put_le32(*buf, bss_info);
|
|
|
|
wpabuf_put_u8(*buf, op_class);
|
|
|
|
wpabuf_put_u8(*buf, chan);
|
|
|
|
wpabuf_put_u8(*buf, phy_type);
|
|
|
|
wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
|
|
|
|
wpabuf_put_u8(*buf, 1);
|
|
|
|
wpabuf_put_u8(*buf, pref);
|
|
|
|
return 0;
|
2016-02-15 15:53:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
|
2017-03-08 13:37:38 +01:00
|
|
|
struct wpa_bss *bss, struct wpabuf **buf,
|
2016-02-15 15:53:41 +01:00
|
|
|
u8 pref)
|
|
|
|
{
|
|
|
|
const u8 *ie;
|
|
|
|
u8 op_class, chan;
|
2022-05-13 18:17:40 +02:00
|
|
|
int sec_chan = 0;
|
|
|
|
enum oper_chan_width vht = CONF_OPER_CHWIDTH_USE_HT;
|
2016-02-15 15:53:41 +01:00
|
|
|
enum phy_type phy_type;
|
|
|
|
u32 info;
|
|
|
|
struct ieee80211_ht_operation *ht_oper = NULL;
|
|
|
|
struct ieee80211_vht_operation *vht_oper = NULL;
|
|
|
|
|
|
|
|
ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
|
|
|
|
if (ie && ie[1] >= 2) {
|
|
|
|
ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
|
|
|
|
|
|
|
|
if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
|
|
|
|
sec_chan = 1;
|
|
|
|
else if (ht_oper->ht_param &
|
|
|
|
HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
|
|
|
|
sec_chan = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION);
|
|
|
|
if (ie && ie[1] >= 1) {
|
|
|
|
vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
|
|
|
|
|
2019-05-20 09:55:06 +02:00
|
|
|
if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ ||
|
|
|
|
vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ ||
|
|
|
|
vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ)
|
2016-02-15 15:53:41 +01:00
|
|
|
vht = vht_oper->vht_op_info_chwidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class,
|
|
|
|
&chan) == NUM_HOSTAPD_MODES) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Cannot determine operating class and channel");
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL),
|
|
|
|
(vht_oper != NULL));
|
|
|
|
if (phy_type == PHY_TYPE_UNSPECIFIED) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Cannot determine BSS phy type for Neighbor Report");
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
info = wnm_get_bss_info(wpa_s, bss);
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
|
|
|
|
pref);
|
2016-02-15 15:53:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
|
2016-02-15 15:53:41 +01:00
|
|
|
{
|
|
|
|
unsigned int i, pref = 255;
|
|
|
|
struct os_reltime now;
|
|
|
|
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
|
|
|
|
|
|
|
if (!ssid)
|
2017-03-08 13:37:38 +01:00
|
|
|
return;
|
2016-02-15 15:53:41 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: Define when scan results are no longer valid for the candidate
|
|
|
|
* list.
|
|
|
|
*/
|
|
|
|
os_get_reltime(&now);
|
|
|
|
if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
|
2017-03-08 13:37:38 +01:00
|
|
|
return;
|
2016-02-15 15:53:41 +01:00
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Add candidate list to BSS Transition Management Response frame");
|
|
|
|
for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
|
|
|
|
struct wpa_bss *bss = wpa_s->last_scan_res[i];
|
|
|
|
int res;
|
|
|
|
|
2016-11-13 17:22:38 +01:00
|
|
|
if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
|
2017-03-08 13:37:38 +01:00
|
|
|
res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
|
2016-02-15 15:53:41 +01:00
|
|
|
if (res == -2)
|
|
|
|
continue; /* could not build entry for BSS */
|
|
|
|
if (res < 0)
|
|
|
|
break; /* no more room for candidates */
|
|
|
|
if (pref == 1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
wpa_hexdump_buf(MSG_DEBUG,
|
|
|
|
"WNM: BSS Transition Management Response candidate list",
|
|
|
|
*buf);
|
2016-02-15 15:53:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
|
|
|
|
|
2024-02-20 14:18:18 +01:00
|
|
|
static int wnm_send_bss_transition_mgmt_resp(
|
2024-02-20 14:18:25 +01:00
|
|
|
struct wpa_supplicant *wpa_s,
|
2017-03-06 12:46:00 +01:00
|
|
|
enum bss_trans_mgmt_status_code status,
|
|
|
|
enum mbo_transition_reject_reason reason,
|
|
|
|
u8 delay, const u8 *target_bssid)
|
2012-12-22 19:27:30 +01:00
|
|
|
{
|
2017-03-08 13:37:38 +01:00
|
|
|
struct wpabuf *buf;
|
2014-11-23 15:13:50 +01:00
|
|
|
int res;
|
2012-12-22 19:27:30 +01:00
|
|
|
|
2024-02-20 14:18:24 +01:00
|
|
|
wpa_s->wnm_reply = 0;
|
|
|
|
|
2017-03-06 12:46:00 +01:00
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Send BSS Transition Management Response to " MACSTR
|
|
|
|
" dialog_token=%u status=%u reason=%u delay=%d",
|
2024-02-20 14:18:25 +01:00
|
|
|
MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
|
|
|
|
reason, delay);
|
2014-11-22 17:04:21 +01:00
|
|
|
if (!wpa_s->current_bss) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Current BSS not known - drop response");
|
2024-02-20 14:18:18 +01:00
|
|
|
return -1;
|
2014-11-22 17:04:21 +01:00
|
|
|
}
|
2012-12-22 19:27:30 +01:00
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
|
|
|
|
if (!buf) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Failed to allocate memory for BTM response");
|
2024-02-20 14:18:18 +01:00
|
|
|
return -1;
|
2017-03-08 13:37:38 +01:00
|
|
|
}
|
|
|
|
|
2018-05-04 20:16:18 +02:00
|
|
|
wpa_s->bss_tm_status = status;
|
|
|
|
wpas_notify_bss_tm_status(wpa_s);
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
|
|
|
wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
|
2024-02-20 14:18:25 +01:00
|
|
|
wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_u8(buf, status);
|
|
|
|
wpabuf_put_u8(buf, delay);
|
2012-12-22 19:27:30 +01:00
|
|
|
if (target_bssid) {
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_data(buf, target_bssid, ETH_ALEN);
|
2013-12-27 17:05:27 +01:00
|
|
|
} else if (status == WNM_BSS_TM_ACCEPT) {
|
|
|
|
/*
|
|
|
|
* P802.11-REVmc clarifies that the Target BSSID field is always
|
|
|
|
* present when status code is zero, so use a fake value here if
|
|
|
|
* no BSSID is yet known.
|
|
|
|
*/
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
|
2012-12-22 19:27:30 +01:00
|
|
|
}
|
|
|
|
|
2024-02-20 14:18:26 +01:00
|
|
|
if (status == WNM_BSS_TM_ACCEPT && target_bssid)
|
2017-03-08 13:37:38 +01:00
|
|
|
wnm_add_cand_list(wpa_s, &buf);
|
2016-02-15 15:53:41 +01:00
|
|
|
|
2016-02-15 15:53:37 +01:00
|
|
|
#ifdef CONFIG_MBO
|
2017-03-06 12:46:00 +01:00
|
|
|
if (status != WNM_BSS_TM_ACCEPT &&
|
|
|
|
wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
|
2017-03-08 13:37:38 +01:00
|
|
|
u8 mbo[10];
|
|
|
|
size_t ret;
|
|
|
|
|
|
|
|
ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
|
|
|
|
reason);
|
|
|
|
if (ret) {
|
|
|
|
if (wpabuf_resize(&buf, ret) < 0) {
|
|
|
|
wpabuf_free(buf);
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Failed to allocate memory for MBO IE");
|
2024-02-20 14:18:18 +01:00
|
|
|
return -1;
|
2017-03-08 13:37:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
wpabuf_put_data(buf, mbo, ret);
|
|
|
|
}
|
2016-02-15 15:53:37 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_MBO */
|
|
|
|
|
2014-11-23 15:13:50 +01:00
|
|
|
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
|
|
|
|
wpa_s->own_addr, wpa_s->bssid,
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_head_u8(buf), wpabuf_len(buf), 0);
|
2014-11-23 15:13:50 +01:00
|
|
|
if (res < 0) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Failed to send BSS Transition Management Response");
|
|
|
|
}
|
2017-03-08 13:37:38 +01:00
|
|
|
|
|
|
|
wpabuf_free(buf);
|
2024-02-20 14:18:18 +01:00
|
|
|
|
|
|
|
return res;
|
2012-12-22 19:27:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-21 13:29:56 +02:00
|
|
|
static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
|
|
|
|
struct wpa_bss *bss, struct wpa_ssid *ssid,
|
|
|
|
int after_new_scan)
|
|
|
|
{
|
2022-01-05 02:35:13 +01:00
|
|
|
struct wpa_radio_work *already_connecting;
|
|
|
|
|
2016-04-21 13:29:56 +02:00
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
|
|
"WNM: Transition to BSS " MACSTR
|
|
|
|
" based on BSS Transition Management Request (old BSSID "
|
|
|
|
MACSTR " after_new_scan=%d)",
|
|
|
|
MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
|
|
|
|
|
|
|
|
/* Send the BSS Management Response - Accept */
|
|
|
|
if (wpa_s->wnm_reply) {
|
2024-02-20 14:18:18 +01:00
|
|
|
wpa_s->wnm_target_bss = bss;
|
2016-04-21 13:29:56 +02:00
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Sending successful BSS Transition Management Response");
|
2024-02-20 14:18:18 +01:00
|
|
|
|
|
|
|
/* This function will be called again from the TX handler to
|
|
|
|
* start the actual reassociation after this response has been
|
|
|
|
* delivered to the current AP. */
|
|
|
|
if (wnm_send_bss_transition_mgmt_resp(
|
2024-02-20 14:18:25 +01:00
|
|
|
wpa_s, WNM_BSS_TM_ACCEPT,
|
2024-02-20 14:18:18 +01:00
|
|
|
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
|
|
|
|
bss->bssid) >= 0)
|
|
|
|
return;
|
2016-04-21 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bss == wpa_s->current_bss) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Already associated with the preferred candidate");
|
2024-02-20 14:18:27 +01:00
|
|
|
wnm_btm_reset(wpa_s);
|
2016-04-21 13:29:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-05 02:35:13 +01:00
|
|
|
already_connecting = radio_work_pending(wpa_s, "sme-connect");
|
2016-04-21 13:29:56 +02:00
|
|
|
wpa_s->reassociate = 1;
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
|
|
|
|
wpa_supplicant_connect(wpa_s, bss, ssid);
|
2022-01-05 02:35:13 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Indicate that a BSS transition is in progress so scan results that
|
|
|
|
* come in before the 'sme-connect' radio work gets executed do not
|
|
|
|
* override the original connection attempt.
|
|
|
|
*/
|
|
|
|
if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
|
|
|
|
wpa_s->bss_trans_mgmt_in_progress = true;
|
2016-04-21 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-02-20 14:18:16 +01:00
|
|
|
int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
|
2013-05-16 16:48:59 +02:00
|
|
|
{
|
2014-11-22 17:04:21 +01:00
|
|
|
struct wpa_bss *bss;
|
|
|
|
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
|
|
|
enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
|
2017-03-06 12:46:00 +01:00
|
|
|
enum mbo_transition_reject_reason reason =
|
|
|
|
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2024-02-20 14:18:27 +01:00
|
|
|
if (!wpa_s->wnm_dialog_token)
|
2014-11-22 17:04:21 +01:00
|
|
|
return 0;
|
|
|
|
|
2016-04-21 13:29:56 +02:00
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
|
|
"WNM: Process scan results for BSS Transition Management");
|
2024-02-20 14:18:16 +01:00
|
|
|
if (!pre_scan_check &&
|
2024-02-20 14:18:27 +01:00
|
|
|
os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
|
2024-02-20 14:18:16 +01:00
|
|
|
os_reltime_before(&wpa_s->wnm_cand_valid_until,
|
2014-11-22 17:04:21 +01:00
|
|
|
&wpa_s->scan_trigger_time)) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
|
2024-02-20 14:18:27 +01:00
|
|
|
goto send_bss_resp_fail;
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
2024-04-29 13:51:42 +02:00
|
|
|
if (!pre_scan_check && !wpa_s->wnm_transition_scan)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
wpa_s->wnm_transition_scan = false;
|
|
|
|
|
2013-05-16 16:48:59 +02:00
|
|
|
/* Compare the Neighbor Report and scan results */
|
2024-04-29 13:51:46 +02:00
|
|
|
bss = compare_scan_neighbor_results(wpa_s, &reason);
|
2024-02-20 14:18:16 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a pre-scan check, returning 0 will trigger a scan and
|
|
|
|
* another call. In that case, reject "bad" candidates in the hope of
|
|
|
|
* finding a better candidate after scanning.
|
|
|
|
*
|
|
|
|
* Use a simple heuristic to check whether the selection is reasonable
|
|
|
|
* or a scan is a good idea. For that, we need to have found a
|
|
|
|
* candidate BSS (which might be the current one), it is up-to-date,
|
|
|
|
* and we don't want to immediately roam back again.
|
|
|
|
*/
|
|
|
|
if (pre_scan_check) {
|
|
|
|
struct os_reltime age;
|
|
|
|
|
|
|
|
if (!bss)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
os_reltime_age(&bss->last_update, &age);
|
|
|
|
if (age.sec >= 10)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifndef CONFIG_NO_ROAMING
|
2024-02-20 14:18:27 +01:00
|
|
|
if (wpa_s->current_bss && bss != wpa_s->current_bss &&
|
2024-02-20 14:18:16 +01:00
|
|
|
wpa_supplicant_need_to_roam_within_ess(wpa_s,
|
|
|
|
wpa_s->current_bss,
|
|
|
|
bss))
|
|
|
|
return 0;
|
|
|
|
#endif /* CONFIG_NO_ROAMING */
|
|
|
|
}
|
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
if (!bss) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
|
|
|
|
status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
|
|
|
|
goto send_bss_resp_fail;
|
|
|
|
}
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2014-11-22 17:04:21 +01:00
|
|
|
/* Associate to the network */
|
2016-04-21 13:29:56 +02:00
|
|
|
wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
|
2014-11-22 17:04:21 +01:00
|
|
|
return 1;
|
|
|
|
|
2013-05-16 16:48:59 +02:00
|
|
|
send_bss_resp_fail:
|
2024-04-29 13:51:52 +02:00
|
|
|
if (wpa_s->wnm_reply) {
|
|
|
|
/* If disassoc imminent is set, we must not reject */
|
|
|
|
if (wpa_s->wnm_mode &
|
|
|
|
(WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
|
|
|
|
WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Accept BTM request because disassociation imminent bit is set");
|
|
|
|
status = WNM_BSS_TM_ACCEPT;
|
|
|
|
}
|
2014-11-22 19:12:12 +01:00
|
|
|
|
2024-02-20 14:18:25 +01:00
|
|
|
wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
|
|
|
|
0, NULL);
|
2024-04-29 13:51:52 +02:00
|
|
|
}
|
2024-02-20 14:18:24 +01:00
|
|
|
|
2024-02-20 14:18:27 +01:00
|
|
|
wnm_btm_reset(wpa_s);
|
2014-11-22 17:04:21 +01:00
|
|
|
|
|
|
|
return 0;
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-22 15:33:57 +01:00
|
|
|
static int cand_pref_compar(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct neighbor_report *aa = a;
|
|
|
|
const struct neighbor_report *bb = b;
|
|
|
|
|
2023-06-09 11:26:43 +02:00
|
|
|
if (aa->disassoc_imminent && !bb->disassoc_imminent)
|
|
|
|
return 1;
|
|
|
|
if (bb->disassoc_imminent && !aa->disassoc_imminent)
|
|
|
|
return -1;
|
|
|
|
|
2014-11-22 15:33:57 +01:00
|
|
|
if (!aa->preference_present && !bb->preference_present)
|
|
|
|
return 0;
|
|
|
|
if (!aa->preference_present)
|
|
|
|
return 1;
|
|
|
|
if (!bb->preference_present)
|
|
|
|
return -1;
|
|
|
|
if (bb->preference > aa->preference)
|
|
|
|
return 1;
|
|
|
|
if (bb->preference < aa->preference)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
|
|
|
|
{
|
|
|
|
if (!wpa_s->wnm_neighbor_report_elements)
|
|
|
|
return;
|
|
|
|
qsort(wpa_s->wnm_neighbor_report_elements,
|
|
|
|
wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
|
|
|
|
cand_pref_compar);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-22 12:38:11 +01:00
|
|
|
static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
|
|
|
|
if (!wpa_s->wnm_neighbor_report_elements)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
|
|
|
|
struct neighbor_report *nei;
|
|
|
|
|
|
|
|
nei = &wpa_s->wnm_neighbor_report_elements[i];
|
|
|
|
wpa_printf(MSG_DEBUG, "%u: " MACSTR
|
2014-11-22 18:50:16 +01:00
|
|
|
" info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
|
2014-11-22 12:43:17 +01:00
|
|
|
i, MAC2STR(nei->bssid), nei->bssid_info,
|
|
|
|
nei->regulatory_class,
|
2014-11-22 12:38:11 +01:00
|
|
|
nei->channel_number, nei->phy_type,
|
2014-11-22 18:50:16 +01:00
|
|
|
nei->preference_present ? nei->preference : -1,
|
|
|
|
nei->freq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < wpa_s->hw.num_modes; i++) {
|
|
|
|
struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (j = 0; j < mode->num_channels; j++) {
|
|
|
|
struct hostapd_channel_data *chan;
|
|
|
|
|
|
|
|
chan = &mode->channels[j];
|
|
|
|
if (chan->freq == freq &&
|
|
|
|
!(chan->flag & HOSTAPD_CHAN_DISABLED))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
|
|
|
|
{
|
|
|
|
int *freqs;
|
|
|
|
int num_freqs = 0;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!wpa_s->wnm_neighbor_report_elements)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (wpa_s->hw.modes == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
os_free(wpa_s->next_scan_freqs);
|
|
|
|
wpa_s->next_scan_freqs = NULL;
|
|
|
|
|
|
|
|
freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
|
|
|
|
if (freqs == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
|
|
|
|
struct neighbor_report *nei;
|
|
|
|
|
|
|
|
nei = &wpa_s->wnm_neighbor_report_elements[i];
|
2024-02-20 14:18:17 +01:00
|
|
|
|
|
|
|
if (nei->preference_present && nei->preference == 0)
|
|
|
|
continue;
|
|
|
|
|
2014-11-22 18:50:16 +01:00
|
|
|
if (nei->freq <= 0) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Unknown neighbor operating frequency for "
|
|
|
|
MACSTR " - scan all channels",
|
|
|
|
MAC2STR(nei->bssid));
|
|
|
|
os_free(freqs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (chan_supported(wpa_s, nei->freq))
|
|
|
|
add_freq(freqs, &num_freqs, nei->freq);
|
2014-11-22 12:38:11 +01:00
|
|
|
}
|
2014-11-22 18:50:16 +01:00
|
|
|
|
|
|
|
if (num_freqs == 0) {
|
|
|
|
os_free(freqs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Scan %d frequencies based on transition candidate list",
|
|
|
|
num_freqs);
|
|
|
|
wpa_s->next_scan_freqs = freqs;
|
2014-11-22 12:38:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-04-29 13:51:45 +02:00
|
|
|
static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *pos, const u8 *end)
|
|
|
|
{
|
|
|
|
while (end - pos >= 2 &&
|
|
|
|
wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) {
|
|
|
|
u8 tag = *pos++;
|
|
|
|
u8 len = *pos++;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag);
|
|
|
|
if (len > end - pos) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Truncated request");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (tag == WLAN_EID_NEIGHBOR_REPORT) {
|
|
|
|
struct neighbor_report *rep;
|
|
|
|
|
2024-04-29 13:51:55 +02:00
|
|
|
if (!wpa_s->wnm_num_neighbor_report) {
|
|
|
|
wpa_s->wnm_neighbor_report_elements = os_calloc(
|
|
|
|
WNM_MAX_NEIGHBOR_REPORT,
|
|
|
|
sizeof(struct neighbor_report));
|
|
|
|
if (!wpa_s->wnm_neighbor_report_elements)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2024-04-29 13:51:45 +02:00
|
|
|
rep = &wpa_s->wnm_neighbor_report_elements[
|
|
|
|
wpa_s->wnm_num_neighbor_report];
|
|
|
|
wnm_parse_neighbor_report(wpa_s, pos, len, rep);
|
|
|
|
if ((wpa_s->wnm_mode &
|
|
|
|
WNM_BSS_TM_REQ_DISASSOC_IMMINENT) &&
|
|
|
|
ether_addr_equal(rep->bssid, wpa_s->bssid))
|
|
|
|
rep->disassoc_imminent = 1;
|
|
|
|
|
|
|
|
wpa_s->wnm_num_neighbor_report++;
|
|
|
|
#ifdef CONFIG_MBO
|
|
|
|
if (wpa_s->wnm_mbo_trans_reason_present &&
|
|
|
|
wpa_s->wnm_num_neighbor_report == 1) {
|
|
|
|
rep->is_first = 1;
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: First transition candidate is "
|
|
|
|
MACSTR, MAC2STR(rep->bssid));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_MBO */
|
|
|
|
}
|
|
|
|
|
|
|
|
pos += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-22 19:27:30 +01:00
|
|
|
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *pos, const u8 *end,
|
|
|
|
int reply)
|
|
|
|
{
|
2014-11-22 12:54:08 +01:00
|
|
|
unsigned int beacon_int;
|
|
|
|
u8 valid_int;
|
2016-02-15 15:53:36 +01:00
|
|
|
#ifdef CONFIG_MBO
|
|
|
|
const u8 *vendor;
|
|
|
|
#endif /* CONFIG_MBO */
|
2023-12-13 15:04:10 +01:00
|
|
|
bool disassoc_imminent;
|
2014-11-22 12:54:08 +01:00
|
|
|
|
2019-09-11 12:35:22 +02:00
|
|
|
if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm)
|
2019-05-30 16:14:55 +02:00
|
|
|
return;
|
|
|
|
|
2015-10-18 10:50:07 +02:00
|
|
|
if (end - pos < 5)
|
2012-12-22 19:27:30 +01:00
|
|
|
return;
|
|
|
|
|
2014-11-22 12:54:08 +01:00
|
|
|
if (wpa_s->current_bss)
|
|
|
|
beacon_int = wpa_s->current_bss->beacon_int;
|
|
|
|
else
|
|
|
|
beacon_int = 100; /* best guess */
|
|
|
|
|
2024-02-20 14:18:27 +01:00
|
|
|
wnm_btm_reset(wpa_s);
|
|
|
|
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_s->wnm_dialog_token = pos[0];
|
|
|
|
wpa_s->wnm_mode = pos[1];
|
2024-08-02 11:44:48 +02:00
|
|
|
wpa_s->wnm_disassoc_timer = WPA_GET_LE16(pos + 2);
|
2023-12-16 18:45:33 +01:00
|
|
|
wpa_s->wnm_link_removal = false;
|
2014-11-22 12:54:08 +01:00
|
|
|
valid_int = pos[4];
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_s->wnm_reply = reply;
|
2012-12-22 19:27:30 +01:00
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
|
|
|
|
"dialog_token=%u request_mode=0x%x "
|
|
|
|
"disassoc_timer=%u validity_interval=%u",
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
|
2024-08-02 11:44:48 +02:00
|
|
|
wpa_s->wnm_disassoc_timer, valid_int);
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2024-04-29 13:51:53 +02:00
|
|
|
if (!wpa_s->wnm_dialog_token) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Invalid dialog token");
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2024-04-29 13:51:53 +02:00
|
|
|
}
|
|
|
|
|
2016-09-15 10:35:53 +02:00
|
|
|
#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
|
|
|
|
if (wpa_s->reject_btm_req_reason) {
|
|
|
|
wpa_printf(MSG_INFO,
|
|
|
|
"WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
|
|
|
|
wpa_s->reject_btm_req_reason);
|
2017-03-06 12:46:00 +01:00
|
|
|
wnm_send_bss_transition_mgmt_resp(
|
2024-02-20 14:18:25 +01:00
|
|
|
wpa_s, wpa_s->reject_btm_req_reason,
|
2017-03-23 11:41:22 +01:00
|
|
|
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2016-09-15 10:35:53 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
|
|
|
|
|
2012-12-22 19:27:30 +01:00
|
|
|
pos += 5;
|
2013-05-16 16:48:59 +02:00
|
|
|
|
2013-05-23 15:07:10 +02:00
|
|
|
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
|
2015-10-18 10:50:07 +02:00
|
|
|
if (end - pos < 12) {
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
|
2012-12-22 19:27:30 +01:00
|
|
|
pos += 12; /* BSS Termination Duration */
|
2013-05-16 16:48:59 +02:00
|
|
|
}
|
|
|
|
|
2013-05-23 15:07:10 +02:00
|
|
|
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
|
2012-12-22 19:27:30 +01:00
|
|
|
char url[256];
|
2022-05-08 11:19:42 +02:00
|
|
|
u8 url_len;
|
2013-04-21 01:48:10 +02:00
|
|
|
|
2022-05-08 11:19:42 +02:00
|
|
|
if (end - pos < 1) {
|
2012-12-22 19:27:30 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
|
|
|
|
"Management Request (URL)");
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2012-12-22 19:27:30 +01:00
|
|
|
}
|
2022-05-08 11:19:42 +02:00
|
|
|
url_len = *pos++;
|
|
|
|
if (url_len > end - pos) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Invalid BSS Transition Management Request (URL truncated)");
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2022-05-08 11:19:42 +02:00
|
|
|
}
|
|
|
|
os_memcpy(url, pos, url_len);
|
|
|
|
url[url_len] = '\0';
|
|
|
|
pos += url_len;
|
2013-04-21 01:48:10 +02:00
|
|
|
|
|
|
|
wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
|
|
|
|
wpa_sm_pmf_enabled(wpa_s->wpa),
|
2024-08-02 11:44:48 +02:00
|
|
|
wpa_s->wnm_disassoc_timer * beacon_int * 128 / 125,
|
|
|
|
url);
|
2012-12-22 19:27:30 +01:00
|
|
|
}
|
|
|
|
|
2023-12-13 15:04:10 +01:00
|
|
|
disassoc_imminent = wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Based on IEEE P802.11be/D5.0, when a station is a non-AP MLD with
|
|
|
|
* more than one affiliated link, the Link Removal Imminent field is
|
|
|
|
* set to 1, and the BSS Termination Included field is set to 1, only
|
|
|
|
* one of the links is removed and the other links remain associated.
|
|
|
|
* Ignore the Disassociation Imminent field in such a case.
|
2024-02-20 14:18:26 +01:00
|
|
|
*
|
|
|
|
* TODO: We should check if the AP has more than one link.
|
|
|
|
* TODO: We should pass the RX link and use that
|
2023-12-13 15:04:10 +01:00
|
|
|
*/
|
2024-02-20 14:18:26 +01:00
|
|
|
if (disassoc_imminent && wpa_s->valid_links &&
|
2023-12-13 15:04:10 +01:00
|
|
|
(wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
|
|
|
|
(wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
|
2024-02-20 14:18:26 +01:00
|
|
|
/* If we still have a link, then just accept the request */
|
|
|
|
if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
|
|
|
|
wpa_printf(MSG_INFO,
|
|
|
|
"WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
|
|
|
|
disassoc_imminent = false;
|
|
|
|
|
|
|
|
wnm_send_bss_transition_mgmt_resp(
|
|
|
|
wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
|
|
|
|
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2024-02-20 14:18:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The last link is being removed (which must be the assoc link)
|
|
|
|
*/
|
2023-12-16 18:45:33 +01:00
|
|
|
wpa_s->wnm_link_removal = true;
|
2024-04-29 13:51:43 +02:00
|
|
|
wpa_s->wnm_disassoc_mld = false;
|
2024-04-29 13:51:43 +02:00
|
|
|
os_memcpy(wpa_s->wnm_disassoc_addr,
|
2024-02-20 14:18:26 +01:00
|
|
|
wpa_s->links[wpa_s->mlo_assoc_link_id].bssid,
|
|
|
|
ETH_ALEN);
|
2024-04-29 13:51:43 +02:00
|
|
|
} else if (wpa_s->valid_links) {
|
|
|
|
wpa_s->wnm_disassoc_mld = true;
|
2024-04-29 13:51:43 +02:00
|
|
|
os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->ap_mld_addr,
|
2024-04-29 13:51:43 +02:00
|
|
|
ETH_ALEN);
|
2024-02-20 14:18:26 +01:00
|
|
|
} else {
|
2024-04-29 13:51:43 +02:00
|
|
|
wpa_s->wnm_disassoc_mld = false;
|
2024-04-29 13:51:43 +02:00
|
|
|
os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->bssid, ETH_ALEN);
|
2023-12-13 15:04:10 +01:00
|
|
|
}
|
|
|
|
|
2024-04-29 13:51:54 +02:00
|
|
|
if (disassoc_imminent)
|
2012-12-22 19:27:30 +01:00
|
|
|
wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
|
2024-08-02 11:44:48 +02:00
|
|
|
"Disassociation Timer %u", wpa_s->wnm_disassoc_timer);
|
2012-12-22 19:27:30 +01:00
|
|
|
|
2016-02-15 15:53:36 +01:00
|
|
|
#ifdef CONFIG_MBO
|
|
|
|
vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
|
|
|
|
if (vendor)
|
|
|
|
wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
|
|
|
|
#endif /* CONFIG_MBO */
|
|
|
|
|
2024-04-29 13:51:55 +02:00
|
|
|
if (wnm_parse_candidate_list(wpa_s, pos, end) < 0)
|
|
|
|
goto reset;
|
2016-02-29 13:30:00 +01:00
|
|
|
|
2024-04-29 13:51:55 +02:00
|
|
|
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
|
2016-02-29 13:30:00 +01:00
|
|
|
if (!wpa_s->wnm_num_neighbor_report) {
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Candidate list included bit is set, but no candidates found");
|
|
|
|
wnm_send_bss_transition_mgmt_resp(
|
2024-02-20 14:18:25 +01:00
|
|
|
wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
|
2017-03-23 11:41:22 +01:00
|
|
|
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
|
|
|
|
NULL);
|
2024-04-29 13:51:54 +02:00
|
|
|
goto reset;
|
2023-11-11 22:20:32 +01:00
|
|
|
}
|
2024-04-29 13:51:55 +02:00
|
|
|
wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpa_s->wnm_num_neighbor_report) {
|
|
|
|
unsigned int valid_ms;
|
2023-11-11 22:20:32 +01:00
|
|
|
|
2014-11-22 15:33:57 +01:00
|
|
|
wnm_sort_cand_list(wpa_s);
|
2014-11-22 12:38:11 +01:00
|
|
|
wnm_dump_cand_list(wpa_s);
|
2014-11-22 12:54:08 +01:00
|
|
|
valid_ms = valid_int * beacon_int * 128 / 125;
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
|
|
|
|
valid_ms);
|
|
|
|
os_get_reltime(&wpa_s->wnm_cand_valid_until);
|
2024-04-29 13:51:44 +02:00
|
|
|
os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms);
|
2024-04-29 13:51:54 +02:00
|
|
|
} else if (!disassoc_imminent) {
|
2013-05-23 15:10:29 +02:00
|
|
|
enum bss_trans_mgmt_status_code status;
|
2023-12-16 18:45:33 +01:00
|
|
|
|
2024-04-29 13:51:54 +02:00
|
|
|
/* No candidate list and disassociation is not imminent */
|
|
|
|
|
2023-12-16 18:45:33 +01:00
|
|
|
if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) ||
|
|
|
|
wpa_s->wnm_link_removal)
|
2013-05-23 15:10:29 +02:00
|
|
|
status = WNM_BSS_TM_ACCEPT;
|
|
|
|
else {
|
|
|
|
wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
|
|
|
|
status = WNM_BSS_TM_REJECT_UNSPECIFIED;
|
|
|
|
}
|
2024-04-29 13:51:54 +02:00
|
|
|
|
|
|
|
if (reply)
|
|
|
|
wnm_send_bss_transition_mgmt_resp(
|
|
|
|
wpa_s, status,
|
|
|
|
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
goto reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try fetching the latest scan results from the kernel.
|
|
|
|
* This can help in finding more up-to-date information should
|
|
|
|
* the driver have done some internal scanning operations after
|
|
|
|
* the last scan result update in wpa_supplicant.
|
|
|
|
*
|
|
|
|
* It is not a new scan, this does not update the last_scan
|
|
|
|
* timestamp nor will it expire old BSSs.
|
|
|
|
*/
|
|
|
|
wpa_supplicant_update_scan_results(wpa_s, NULL);
|
|
|
|
if (wnm_scan_process(wpa_s, true) > 0)
|
|
|
|
return;
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: No valid match in previous scan results - try a new scan");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have a fixed BSSID configured, just reject at this point.
|
|
|
|
* NOTE: We could actually check if we are allowed to stay (and we do
|
|
|
|
* above if we have scan results available).
|
|
|
|
*/
|
|
|
|
if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Fixed BSSID, rejecting request");
|
|
|
|
|
|
|
|
if (reply)
|
|
|
|
wnm_send_bss_transition_mgmt_resp(
|
|
|
|
wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
|
|
|
|
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
goto reset;
|
2012-12-22 19:27:30 +01:00
|
|
|
}
|
2024-04-29 13:51:54 +02:00
|
|
|
|
|
|
|
wnm_set_scan_freqs(wpa_s);
|
|
|
|
if (wpa_s->wnm_num_neighbor_report == 1) {
|
|
|
|
os_memcpy(wpa_s->next_scan_bssid,
|
|
|
|
wpa_s->wnm_neighbor_report_elements[0].bssid,
|
|
|
|
ETH_ALEN);
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: Scan only for a specific BSSID since there is only a single candidate "
|
|
|
|
MACSTR, MAC2STR(wpa_s->next_scan_bssid));
|
|
|
|
}
|
|
|
|
wpa_s->wnm_transition_scan = true;
|
|
|
|
wpa_supplicant_req_scan(wpa_s, 0, 0);
|
|
|
|
|
|
|
|
/* Continue from scan handler */
|
|
|
|
return;
|
|
|
|
|
|
|
|
reset:
|
|
|
|
wnm_btm_reset(wpa_s);
|
2012-12-22 19:27:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-02-20 14:18:18 +01:00
|
|
|
int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
|
|
|
|
size_t data_len)
|
|
|
|
{
|
|
|
|
const struct ieee80211_mgmt *frame =
|
|
|
|
(const struct ieee80211_mgmt *) data;
|
|
|
|
|
|
|
|
if (data_len <
|
|
|
|
IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
|
|
|
|
frame->u.action.category != WLAN_ACTION_WNM ||
|
|
|
|
frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
|
|
|
|
frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If disassoc imminent bit was set in the request, the response may
|
|
|
|
* indicate accept even if no candidate was found, so bail out here.
|
|
|
|
*/
|
|
|
|
if (!wpa_s->wnm_target_bss) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wpa_s->current_ssid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
|
|
|
|
0);
|
|
|
|
|
|
|
|
wpa_s->wnm_target_bss = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
#define BTM_QUERY_MIN_SIZE 4
|
|
|
|
|
2013-05-16 16:50:31 +02:00
|
|
|
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
|
2017-03-08 13:37:40 +01:00
|
|
|
u8 query_reason,
|
|
|
|
const char *btm_candidates,
|
|
|
|
int cand_list)
|
2013-05-16 16:50:31 +02:00
|
|
|
{
|
2017-03-08 13:37:38 +01:00
|
|
|
struct wpabuf *buf;
|
2013-05-16 16:50:31 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
|
2016-02-15 15:53:43 +01:00
|
|
|
MACSTR " query_reason=%u%s",
|
|
|
|
MAC2STR(wpa_s->bssid), query_reason,
|
|
|
|
cand_list ? " candidate list" : "");
|
2013-05-16 16:50:31 +02:00
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
|
|
|
|
if (!buf)
|
|
|
|
return -1;
|
2013-05-16 16:50:31 +02:00
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
|
|
|
wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
|
|
|
|
wpabuf_put_u8(buf, 1);
|
|
|
|
wpabuf_put_u8(buf, query_reason);
|
2016-02-15 15:53:43 +01:00
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
if (cand_list)
|
|
|
|
wnm_add_cand_list(wpa_s, &buf);
|
2013-05-16 16:50:31 +02:00
|
|
|
|
2017-03-08 13:37:40 +01:00
|
|
|
if (btm_candidates) {
|
|
|
|
const size_t max_len = 1000;
|
|
|
|
|
|
|
|
ret = wpabuf_resize(&buf, max_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
wpabuf_free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ieee802_11_parse_candidate_list(btm_candidates,
|
|
|
|
wpabuf_put(buf, 0),
|
|
|
|
max_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
wpabuf_free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpabuf_put(buf, ret);
|
|
|
|
}
|
|
|
|
|
2013-05-16 16:50:31 +02:00
|
|
|
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
|
|
|
|
wpa_s->own_addr, wpa_s->bssid,
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_head_u8(buf), wpabuf_len(buf), 0);
|
2013-05-16 16:50:31 +02:00
|
|
|
|
2017-03-08 13:37:38 +01:00
|
|
|
wpabuf_free(buf);
|
2013-05-16 16:50:31 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-02 12:05:57 +01:00
|
|
|
static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *sa, const u8 *data,
|
|
|
|
int len)
|
|
|
|
{
|
2013-07-26 21:12:37 +02:00
|
|
|
const u8 *pos, *end, *next;
|
2012-11-02 12:05:57 +01:00
|
|
|
u8 ie, ie_len;
|
|
|
|
|
|
|
|
pos = data;
|
|
|
|
end = data + len;
|
|
|
|
|
2015-10-18 10:50:07 +02:00
|
|
|
while (end - pos > 1) {
|
2012-11-02 12:05:57 +01:00
|
|
|
ie = *pos++;
|
|
|
|
ie_len = *pos++;
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
|
|
|
|
ie, ie_len);
|
|
|
|
if (ie_len > end - pos) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
|
|
|
|
"subelement");
|
|
|
|
break;
|
|
|
|
}
|
2013-07-26 21:12:37 +02:00
|
|
|
next = pos + ie_len;
|
|
|
|
if (ie_len < 4) {
|
|
|
|
pos = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
|
|
|
|
WPA_GET_BE24(pos), pos[3]);
|
2012-11-02 12:05:57 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_HS20
|
|
|
|
if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
|
|
|
|
WPA_GET_BE24(pos) == OUI_WFA &&
|
|
|
|
pos[3] == HS20_WNM_SUB_REM_NEEDED) {
|
|
|
|
/* Subscription Remediation subelement */
|
|
|
|
const u8 *ie_end;
|
|
|
|
u8 url_len;
|
|
|
|
char *url;
|
|
|
|
u8 osu_method;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
|
|
|
|
"subelement");
|
|
|
|
ie_end = pos + ie_len;
|
|
|
|
pos += 4;
|
|
|
|
url_len = *pos++;
|
|
|
|
if (url_len == 0) {
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
|
|
|
|
url = NULL;
|
|
|
|
osu_method = 1;
|
|
|
|
} else {
|
2015-10-18 10:50:07 +02:00
|
|
|
if (url_len + 1 > ie_end - pos) {
|
2012-11-02 12:05:57 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
|
|
|
|
url_len,
|
|
|
|
(int) (ie_end - pos));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
url = os_malloc(url_len + 1);
|
|
|
|
if (url == NULL)
|
|
|
|
break;
|
|
|
|
os_memcpy(url, pos, url_len);
|
|
|
|
url[url_len] = '\0';
|
|
|
|
osu_method = pos[url_len];
|
|
|
|
}
|
|
|
|
hs20_rx_subscription_remediation(wpa_s, url,
|
|
|
|
osu_method);
|
|
|
|
os_free(url);
|
2013-07-26 21:12:37 +02:00
|
|
|
pos = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
|
|
|
|
WPA_GET_BE24(pos) == OUI_WFA &&
|
|
|
|
pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
|
|
|
|
const u8 *ie_end;
|
|
|
|
u8 url_len;
|
|
|
|
char *url;
|
|
|
|
u8 code;
|
|
|
|
u16 reauth_delay;
|
|
|
|
|
|
|
|
ie_end = pos + ie_len;
|
|
|
|
pos += 4;
|
|
|
|
code = *pos++;
|
|
|
|
reauth_delay = WPA_GET_LE16(pos);
|
|
|
|
pos += 2;
|
|
|
|
url_len = *pos++;
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
|
|
|
|
"Imminent - Reason Code %u "
|
|
|
|
"Re-Auth Delay %u URL Length %u",
|
|
|
|
code, reauth_delay, url_len);
|
2015-10-18 10:50:07 +02:00
|
|
|
if (url_len > ie_end - pos)
|
2013-07-26 21:12:37 +02:00
|
|
|
break;
|
|
|
|
url = os_malloc(url_len + 1);
|
|
|
|
if (url == NULL)
|
|
|
|
break;
|
|
|
|
os_memcpy(url, pos, url_len);
|
|
|
|
url[url_len] = '\0';
|
|
|
|
hs20_rx_deauth_imminent_notice(wpa_s, code,
|
|
|
|
reauth_delay, url);
|
2018-04-23 23:17:47 +02:00
|
|
|
os_free(url);
|
|
|
|
pos = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
|
|
|
|
WPA_GET_BE24(pos) == OUI_WFA &&
|
|
|
|
pos[3] == HS20_WNM_T_C_ACCEPTANCE) {
|
|
|
|
const u8 *ie_end;
|
|
|
|
u8 url_len;
|
|
|
|
char *url;
|
|
|
|
|
|
|
|
ie_end = pos + ie_len;
|
|
|
|
pos += 4;
|
|
|
|
url_len = *pos++;
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)",
|
|
|
|
url_len);
|
|
|
|
if (url_len > ie_end - pos)
|
|
|
|
break;
|
|
|
|
url = os_malloc(url_len + 1);
|
|
|
|
if (!url)
|
|
|
|
break;
|
|
|
|
os_memcpy(url, pos, url_len);
|
|
|
|
url[url_len] = '\0';
|
|
|
|
hs20_rx_t_c_acceptance(wpa_s, url);
|
2013-07-26 21:12:37 +02:00
|
|
|
os_free(url);
|
|
|
|
pos = next;
|
|
|
|
continue;
|
2012-11-02 12:05:57 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_HS20 */
|
|
|
|
|
2013-07-26 21:12:37 +02:00
|
|
|
pos = next;
|
2012-11-02 12:05:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *sa, const u8 *frm, int len)
|
|
|
|
{
|
|
|
|
const u8 *pos, *end;
|
|
|
|
u8 dialog_token, type;
|
|
|
|
|
|
|
|
/* Dialog Token [1] | Type [1] | Subelements */
|
|
|
|
|
|
|
|
if (len < 2 || sa == NULL)
|
|
|
|
return;
|
|
|
|
end = frm + len;
|
|
|
|
pos = frm;
|
|
|
|
dialog_token = *pos++;
|
|
|
|
type = *pos++;
|
|
|
|
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
|
|
|
|
"(dialog_token %u type %u sa " MACSTR ")",
|
|
|
|
dialog_token, type, MAC2STR(sa));
|
|
|
|
wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
|
|
|
|
pos, end - pos);
|
|
|
|
|
|
|
|
if (wpa_s->wpa_state != WPA_COMPLETED ||
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
(!ether_addr_equal(sa, wpa_s->bssid) &&
|
2023-12-13 15:04:06 +01:00
|
|
|
(!wpa_s->valid_links ||
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
!ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
|
2012-11-02 12:05:57 +01:00
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
|
|
|
|
"from our AP - ignore it");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 1:
|
|
|
|
ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
|
|
|
|
"WNM-Notification type %u", type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-30 13:00:00 +01:00
|
|
|
static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s,
|
|
|
|
const u8 *sa, const u8 *frm,
|
|
|
|
int len)
|
|
|
|
{
|
|
|
|
u8 dialog_token, req_info, auto_report, timeout;
|
|
|
|
|
|
|
|
if (!wpa_s->conf->coloc_intf_reporting)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Dialog Token [1] | Request Info [1] */
|
|
|
|
|
|
|
|
if (len < 2)
|
|
|
|
return;
|
|
|
|
dialog_token = frm[0];
|
|
|
|
req_info = frm[1];
|
|
|
|
auto_report = req_info & 0x03;
|
|
|
|
timeout = req_info >> 2;
|
|
|
|
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
|
|
"WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")",
|
|
|
|
dialog_token, auto_report, timeout, MAC2STR(sa));
|
|
|
|
|
|
|
|
if (dialog_token == 0)
|
|
|
|
return; /* only nonzero values are used for request */
|
|
|
|
|
|
|
|
if (wpa_s->wpa_state != WPA_COMPLETED ||
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
(!ether_addr_equal(sa, wpa_s->bssid) &&
|
2023-12-13 15:04:06 +01:00
|
|
|
(!wpa_s->valid_links ||
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
!ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
|
2018-10-30 13:00:00 +01:00
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
|
|
"WNM: Collocated Interference Request frame not from current AP - ignore it");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u",
|
|
|
|
dialog_token, auto_report, timeout);
|
|
|
|
wpa_s->coloc_intf_dialog_token = dialog_token;
|
|
|
|
wpa_s->coloc_intf_auto_report = auto_report;
|
|
|
|
wpa_s->coloc_intf_timeout = timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-26 16:27:19 +01:00
|
|
|
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
|
2013-12-29 10:22:23 +01:00
|
|
|
const struct ieee80211_mgmt *mgmt, size_t len)
|
2012-02-26 16:27:19 +01:00
|
|
|
{
|
2012-12-22 11:02:15 +01:00
|
|
|
const u8 *pos, *end;
|
2012-12-22 19:27:30 +01:00
|
|
|
u8 act;
|
2012-12-22 11:02:15 +01:00
|
|
|
|
2013-12-29 10:22:23 +01:00
|
|
|
if (len < IEEE80211_HDRLEN + 2)
|
2012-12-22 11:02:15 +01:00
|
|
|
return;
|
|
|
|
|
2014-06-12 22:33:33 +02:00
|
|
|
pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
|
2012-12-22 11:02:15 +01:00
|
|
|
act = *pos++;
|
2013-12-29 10:22:23 +01:00
|
|
|
end = ((const u8 *) mgmt) + len;
|
2012-12-22 11:02:15 +01:00
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
|
2013-12-29 10:22:23 +01:00
|
|
|
act, MAC2STR(mgmt->sa));
|
2012-12-22 19:27:30 +01:00
|
|
|
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
(!ether_addr_equal(mgmt->sa, wpa_s->bssid) &&
|
2023-12-13 15:04:06 +01:00
|
|
|
(!wpa_s->valid_links ||
|
Use ether_addr_equal() to compare whether two MAC addresses are equal
This was done with spatch using the following semantic patch and minor
manual edits to clean up coding style and avoid compiler warnings in
driver_wext.c:
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- os_memcmp(a, b, ETH_ALEN) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !os_memcmp(a, b, ETH_ALEN)
+ ether_addr_equal(a, b)
Signed-off-by: Jouni Malinen <j@w1.fi>
2024-01-13 22:15:36 +01:00
|
|
|
!ether_addr_equal(mgmt->sa, wpa_s->ap_mld_addr)))) {
|
2012-12-22 19:27:30 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
|
|
|
|
"frame");
|
|
|
|
return;
|
|
|
|
}
|
2012-02-26 16:27:19 +01:00
|
|
|
|
|
|
|
switch (act) {
|
2012-12-22 11:02:15 +01:00
|
|
|
case WNM_BSS_TRANS_MGMT_REQ:
|
2012-12-22 19:27:30 +01:00
|
|
|
ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
|
2013-12-29 10:22:23 +01:00
|
|
|
!(mgmt->da[0] & 0x01));
|
2012-12-22 11:02:15 +01:00
|
|
|
break;
|
2012-02-26 16:27:19 +01:00
|
|
|
case WNM_SLEEP_MODE_RESP:
|
2013-12-29 10:22:23 +01:00
|
|
|
ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
|
2012-02-26 16:27:19 +01:00
|
|
|
break;
|
2012-11-02 12:05:57 +01:00
|
|
|
case WNM_NOTIFICATION_REQ:
|
|
|
|
ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
|
|
|
|
break;
|
2018-10-30 13:00:00 +01:00
|
|
|
case WNM_COLLOCATED_INTERFERENCE_REQ:
|
|
|
|
ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos,
|
|
|
|
end - pos);
|
|
|
|
break;
|
2012-02-26 16:27:19 +01:00
|
|
|
default:
|
2013-05-16 16:48:59 +02:00
|
|
|
wpa_printf(MSG_ERROR, "WNM: Unknown request");
|
2012-02-26 16:27:19 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-10-30 13:00:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
|
|
|
|
const struct wpabuf *elems)
|
|
|
|
{
|
|
|
|
struct wpabuf *buf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to "
|
|
|
|
MACSTR " (dialog token %u)",
|
|
|
|
MAC2STR(wpa_s->bssid), dialog_token);
|
|
|
|
|
|
|
|
buf = wpabuf_alloc(3 + wpabuf_len(elems));
|
|
|
|
if (!buf)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
|
|
|
|
wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT);
|
|
|
|
wpabuf_put_u8(buf, dialog_token);
|
|
|
|
wpabuf_put_buf(buf, elems);
|
|
|
|
|
|
|
|
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
|
|
|
|
wpa_s->own_addr, wpa_s->bssid,
|
|
|
|
wpabuf_head_u8(buf), wpabuf_len(buf), 0);
|
|
|
|
wpabuf_free(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
|
|
|
|
struct wpabuf *elems)
|
|
|
|
{
|
|
|
|
if (elems && wpabuf_len(elems) == 0) {
|
|
|
|
wpabuf_free(elems);
|
|
|
|
elems = NULL;
|
|
|
|
}
|
|
|
|
|
2024-02-20 14:18:23 +01:00
|
|
|
/* NOTE: The elements are not stored as they are only send out once */
|
|
|
|
|
|
|
|
if (wpa_s->conf->coloc_intf_reporting && elems &&
|
2018-10-30 13:00:00 +01:00
|
|
|
wpa_s->coloc_intf_dialog_token &&
|
|
|
|
(wpa_s->coloc_intf_auto_report == 1 ||
|
|
|
|
wpa_s->coloc_intf_auto_report == 3)) {
|
|
|
|
/* TODO: Check that there has not been less than
|
|
|
|
* wpa_s->coloc_intf_timeout * 200 TU from the last report.
|
|
|
|
*/
|
|
|
|
wnm_send_coloc_intf_report(wpa_s,
|
|
|
|
wpa_s->coloc_intf_dialog_token,
|
2024-02-20 14:18:23 +01:00
|
|
|
elems);
|
2018-10-30 13:00:00 +01:00
|
|
|
}
|
2024-02-20 14:18:23 +01:00
|
|
|
|
|
|
|
wpabuf_free(elems);
|
2018-10-30 13:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
|
|
|
|
{
|
|
|
|
wpa_s->coloc_intf_dialog_token = 0;
|
|
|
|
wpa_s->coloc_intf_auto_report = 0;
|
|
|
|
}
|
2023-12-13 15:04:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
|
|
|
|
{
|
2024-04-29 13:51:48 +02:00
|
|
|
int i;
|
|
|
|
|
2023-12-13 15:04:12 +01:00
|
|
|
/*
|
|
|
|
* In case disassociation imminent is set, do no try to use a BSS to
|
|
|
|
* which we are connected.
|
|
|
|
*/
|
2024-04-29 13:51:47 +02:00
|
|
|
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
|
|
|
|
if (!wpa_s->wnm_disassoc_mld) {
|
|
|
|
if (ether_addr_equal(bss->bssid,
|
|
|
|
wpa_s->wnm_disassoc_addr))
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
if (ether_addr_equal(bss->mld_addr,
|
|
|
|
wpa_s->wnm_disassoc_addr))
|
|
|
|
return true;
|
|
|
|
}
|
2023-12-13 15:04:12 +01:00
|
|
|
}
|
|
|
|
|
2024-04-29 13:51:48 +02:00
|
|
|
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
|
|
|
|
struct neighbor_report *nei;
|
|
|
|
|
|
|
|
nei = &wpa_s->wnm_neighbor_report_elements[i];
|
|
|
|
if (!ether_addr_equal(nei->bssid, bss->bssid))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (nei->preference_present && nei->preference == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the abridged bit is set, the BSS must be a known neighbor. */
|
|
|
|
if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED) &&
|
|
|
|
wpa_s->wnm_num_neighbor_report == i)
|
|
|
|
return true;
|
|
|
|
|
2023-12-13 15:04:12 +01:00
|
|
|
return false;
|
|
|
|
}
|