Merge branch 'feature/mbo_support' into 'master'

esp_wifi: Add support for MBO certification

Closes WIFI-3777

See merge request espressif/esp-idf!12650
This commit is contained in:
Jiang Jiang Jian 2021-10-09 16:31:31 +00:00
commit f05351497c
28 changed files with 1605 additions and 193 deletions

View File

@ -1,16 +1,8 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ESP_WIFI_TYPES_H__
@ -80,6 +72,7 @@ typedef enum {
WIFI_REASON_ASSOC_NOT_AUTHED = 9,
WIFI_REASON_DISASSOC_PWRCAP_BAD = 10,
WIFI_REASON_DISASSOC_SUPCHAN_BAD = 11,
WIFI_REASON_BSS_TRANSITION_DISASSOC = 12,
WIFI_REASON_IE_INVALID = 13,
WIFI_REASON_MIC_FAILURE = 14,
WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
@ -250,7 +243,8 @@ typedef struct {
wifi_pmf_config_t pmf_cfg; /**< Configuration for Protected Management Frame. Will be advertized in RSN Capabilities in RSN IE. */
uint32_t rm_enabled:1; /**< Whether Radio Measurements are enabled for the connection */
uint32_t btm_enabled:1; /**< Whether BSS Transition Management is enabled for the connection */
uint32_t reserved:30; /**< Reserved for future feature set */
uint32_t mbo_enabled:1; /**< Whether MBO is enabled for the connection */
uint32_t reserved:29; /**< Reserved for future feature set */
} wifi_sta_config_t;
/** @brief Configuration data for ESP32 AP or STA.

@ -1 +1 @@
Subproject commit 4e1c7a8114fd03b9841f78a218f579799cb61a0a
Subproject commit 6f9d2a11850b9fc0d4fd4ce89c492129e06fd77c

View File

@ -154,8 +154,13 @@ if(CONFIG_WPA_11KV_SUPPORT)
else()
set(roaming_src "")
endif()
if(CONFIG_WPA_MBO_SUPPORT)
set(mbo_src "src/common/mbo.c")
else()
set(mbo_src "")
endif()
idf_component_register(SRCS "${srcs}" ${esp_srcs} "${tls_src}" "${roaming_src}" "${crypto_src}"
idf_component_register(SRCS "${srcs}" ${esp_srcs} "${tls_src}" "${roaming_src}" "${crypto_src}" "${mbo_src}"
INCLUDE_DIRS include port/include esp_supplicant/include
PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src
PRIV_REQUIRES mbedtls esp_timer)
@ -190,4 +195,7 @@ if(CONFIG_WPA_WPS_STRICT)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_WPS_STRICT)
endif()
if(CONFIG_WPA_MBO_SUPPORT)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_MBO)
endif()
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 3)

View File

@ -59,12 +59,18 @@ menu "Supplicant"
and on the radio environment. Current implementation adds beacon report,
link measurement, neighbor report.
if WPA_11KV_SUPPORT
config WPA_SCAN_CACHE
bool "Keep scan results in cache"
default n
help
Keep scan results in cache, if not enabled, those
will be flushed immediately.
endif
menuconfig WPA_SCAN_CACHE
bool "Keep scan results in cache"
depends on WPA_11KV_SUPPORT
default n
help
Keep scan results in cache, if not enabled, those
will be flushed immediately.
menuconfig WPA_MBO_SUPPORT
bool "Enable MBO support in supplicant"
depends on WPA_11KV_SUPPORT
default n
help
Select this option to enable WiFi Multiband operation certification support.
endmenu

View File

@ -70,6 +70,9 @@ ifneq ($(CONFIG_WPA_11KV_SUPPORT), y)
esp_supplicant/src/esp_common.o \
esp_supplicant/src/esp_scan.o
endif
ifneq ($(CONFIG_WPA_MBO_SUPPORT), y)
COMPONENT_OBJEXCLUDE += src/common/mbo.o
endif
CFLAGS += -DCONFIG_SHA256 -DCONFIG_DPP -DCONFIG_IEEE80211W -DESP_SUPPLICANT -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_TLS -DEAP_TTLS -DEAP_PEAP -DEAP_MSCHAPv2 -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DCONFIG_ECC -DCONFIG_WNM -D__ets__ -Wno-strict-aliasing
@ -79,3 +82,6 @@ endif
ifdef CONFIG_WPA_WPS_STRICT
CFLAGS += -DCONFIG_WPS_STRICT
endif
ifdef CONFIG_WPA_MBO_SUPPORT
CFLAGS += -DCONFIG_MBO
endif

View File

@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MBO_H
#define _ESP_MBO_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* enum non_pref_chan_reason: Reason for non preference of channel
*/
enum non_pref_chan_reason {
NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
NON_PREF_CHAN_REASON_RSSI = 1,
NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
};
/**
* @brief Channel structure for non preferred channel
*
* @param reason: enum non_pref_chan_reason
* @param oper_class: operating class for the channel
* @param chan: channel number
* @param preference: channel preference
*/
struct non_pref_chan {
enum non_pref_chan_reason reason;
uint8_t oper_class;
uint8_t chan;
uint8_t preference;
};
/**
* @brief Array structure for non preferred channel struct
*
* @param non_pref_chan_num: channel count
* @param chan: array of non_pref_chan type
*/
struct non_pref_chan_s {
size_t non_pref_chan_num;
struct non_pref_chan chan[];
};
/**
* @brief Update channel preference for MBO IE
*
* @param non_pref_chan: Non preference channel list
*
* @return
* - 0: success else failure
*/
int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,17 +1,7 @@
/**
* Copyright 2020 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_WNM_H
@ -29,11 +19,13 @@ enum btm_query_reason {
REASON_UNSPECIFIED = 0,
REASON_FRAME_LOSS = 1,
REASON_DELAY = 2,
REASON_QOS_CAPACITY = 3,
REASON_FIRST_ASSOC = 4,
REASON_LOAD_BALALNCE = 5,
REASON_BETTER_AP = 6,
REASON_CURRENT_DEAUTH = 7,
REASON_BANDWIDTH = 3,
REASON_LOAD_BALANCE = 4,
REASON_RSSI = 5,
REASON_RETRANSMISSIONS = 6,
REASON_INTERFERENCE = 7,
REASON_GRAY_ZONE = 8,
REASON_PREMIUM_AP = 9,
};
/**

View File

@ -1,17 +1,7 @@
/**
* Copyright 2020 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#include "utils/includes.h"
@ -37,8 +27,8 @@ static void *s_supplicant_task_hdl = NULL;
static void *s_supplicant_evt_queue = NULL;
static void *s_supplicant_api_lock = NULL;
static int esp_handle_action_frm(u8 *frame, size_t len,
u8 *sender, u32 rssi, u8 channel)
static int handle_action_frm(u8 *frame, size_t len,
u8 *sender, u32 rssi, u8 channel)
{
struct ieee_mgmt_frame *frm = os_malloc(sizeof(struct ieee_mgmt_frame) + len);
@ -61,7 +51,7 @@ static int esp_handle_action_frm(u8 *frame, size_t len,
return 0;
}
static void esp_rx_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
static void handle_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
u8 *payload, size_t len, u32 rssi)
{
if (payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
@ -78,7 +68,7 @@ static void esp_rx_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
}
}
static int esp_mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u32 rssi)
static int mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u32 rssi)
{
u8 category;
u8 bssid[ETH_ALEN];
@ -95,13 +85,13 @@ static int esp_mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u
if (category == WLAN_ACTION_WNM) {
ieee802_11_rx_wnm_action(wpa_s, sender, payload, len);
} else if (category == WLAN_ACTION_RADIO_MEASUREMENT) {
esp_rx_rrm_frame(wpa_s, sender, payload, len, rssi);
handle_rrm_frame(wpa_s, sender, payload, len, rssi);
}
return 0;
}
static void esp_btm_rrm_task(void *pvParameters)
static void btm_rrm_task(void *pvParameters)
{
supplicant_event_t *evt;
bool task_del = false;
@ -120,7 +110,7 @@ static void esp_btm_rrm_task(void *pvParameters)
case SIG_SUPPLICANT_RX_ACTION:
{
struct ieee_mgmt_frame *frm = (struct ieee_mgmt_frame *)evt->data;
esp_mgmt_rx_action(frm->sender, frm->payload, frm->len, frm->channel, frm->rssi);
mgmt_rx_action(frm->sender, frm->payload, frm->len, frm->channel, frm->rssi);
os_free(frm);
break;
}
@ -153,7 +143,7 @@ static void esp_btm_rrm_task(void *pvParameters)
vTaskDelete(NULL);
}
static void esp_clear_bssid_flag(struct wpa_supplicant *wpa_s)
static void clear_bssid_flag(struct wpa_supplicant *wpa_s)
{
wifi_config_t *config;
@ -175,7 +165,7 @@ static void esp_clear_bssid_flag(struct wpa_supplicant *wpa_s)
wpa_printf(MSG_DEBUG, "cleared bssid flag");
}
static void esp_register_action_frame(struct wpa_supplicant *wpa_s)
static void register_action_frame(struct wpa_supplicant *wpa_s)
{
wpa_s->type &= ~(1 << WLAN_FC_STYPE_ACTION);
/* subtype is defined only for action frame */
@ -193,8 +183,8 @@ static void esp_register_action_frame(struct wpa_supplicant *wpa_s)
esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype);
}
static void esp_supplicant_sta_conn_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
static void supplicant_sta_conn_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
u8 bssid[ETH_ALEN];
u8 *ie;
@ -215,13 +205,13 @@ static void esp_supplicant_sta_conn_handler(void* arg, esp_event_base_t event_ba
ieee802_11_parse_elems(wpa_s, ie, bss->ie_len);
wpa_bss_flush(wpa_s);
/* Register for action frames */
esp_register_action_frame(wpa_s);
register_action_frame(wpa_s);
/* clear set bssid flag */
esp_clear_bssid_flag(wpa_s);
clear_bssid_flag(wpa_s);
}
static void esp_supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
static void supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
wpas_rrm_reset(wpa_s);
@ -230,12 +220,49 @@ static void esp_supplicant_sta_disconn_handler(void* arg, esp_event_base_t event
}
}
static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf)
{
if (type == WLAN_FC_STYPE_BEACON || type == WLAN_FC_STYPE_PROBE_RESP) {
return esp_handle_beacon_probe(type, frame, len, sender, rssi, channel, current_tsf);
} else if (type == WLAN_FC_STYPE_ACTION) {
return handle_action_frm(frame, len, sender, rssi, channel);
}
return -1;
}
#ifdef CONFIG_MBO
static bool bss_profile_match(u8 *sender)
{
/* Incase supplicant wants drivers to skip this BSS, return false */
struct wpa_bss *bss = wpa_bss_get_bssid(&g_wpa_supp, sender);
if (!bss) {
return true;
}
const u8 *assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW);
if (assoc_disallow && assoc_disallow[1] >= 1) {
wpa_printf(MSG_DEBUG,
"skip - MBO association disallowed (reason %u)", assoc_disallow[2]);
return false;
}
if (wpa_is_bss_tmp_disallowed(&g_wpa_supp, bss)) {
wpa_printf(MSG_DEBUG,
"skip - BSS is temporary disallowed");
return false;
}
return true;
}
#endif
void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
s_supplicant_evt_queue = xQueueCreate(3, sizeof(supplicant_event_t));
xTaskCreate(esp_btm_rrm_task, "btm_rrm_t", SUPPLICANT_TASK_STACK_SIZE, NULL, 2, s_supplicant_task_hdl);
xTaskCreate(btm_rrm_task, "btm_rrm_t", SUPPLICANT_TASK_STACK_SIZE, NULL, 2, s_supplicant_task_hdl);
s_supplicant_api_lock = xSemaphoreCreateRecursiveMutex();
if (!s_supplicant_api_lock) {
@ -248,13 +275,22 @@ void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
wpas_clear_beacon_rep_data(wpa_s);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
&esp_supplicant_sta_conn_handler, NULL);
&supplicant_sta_conn_handler, NULL);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
&esp_supplicant_sta_disconn_handler, NULL);
&supplicant_sta_disconn_handler, NULL);
wpa_s->type = 0;
wpa_s->subtype = 0;
wpa_cb->wpa_sta_rx_mgmt = esp_ieee80211_handle_rx_frm;
wpa_s->type |= (1 << WLAN_FC_STYPE_BEACON) | (1 << WLAN_FC_STYPE_PROBE_RESP);
esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype);
wpa_cb->wpa_sta_rx_mgmt = ieee80211_handle_rx_frm;
/* Matching is done only for MBO at the moment, this can be extended for other features*/
#ifdef CONFIG_MBO
wpa_cb->wpa_sta_profile_match = bss_profile_match;
dl_list_init(&wpa_s->bss_tmp_disallowed);
#else
wpa_cb->wpa_sta_profile_match = NULL;
#endif
}
void esp_supplicant_common_deinit(void)
@ -268,28 +304,38 @@ void esp_supplicant_common_deinit(void)
wpas_rrm_reset(wpa_s);
wpas_clear_beacon_rep_data(wpa_s);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
&esp_supplicant_sta_conn_handler);
&supplicant_sta_conn_handler);
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
&esp_supplicant_sta_disconn_handler);
&supplicant_sta_disconn_handler);
}
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
struct wpa_ssid_value wpa_ssid = {0};
struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal();
os_memcpy(wpa_ssid.ssid, ssid->ssid, ssid->len);
wpa_ssid.ssid_len = ssid->len;
return wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, cb, cb_ctx);
return wpas_rrm_send_neighbor_rep_request(&g_wpa_supp, &wpa_ssid, 0, 0, cb, cb_ctx);
}
int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
const char *btm_candidates,
int cand_list)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, btm_candidates, cand_list);
return wnm_send_bss_transition_mgmt_query(&g_wpa_supp, query_reason, btm_candidates, cand_list);
}
int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan)
{
int ret = wpas_mbo_update_non_pref_chan(&g_wpa_supp, non_pref_chan);
if (ret == 0) {
esp_set_assoc_ie();
}
return ret;
}
void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
@ -306,18 +352,25 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
/* We only support roaming in same ESS, therefore only bssid setting is needed */
os_memcpy(config->sta.bssid, bss->bssid, ETH_ALEN);
config->sta.bssid_set = 1;
esp_wifi_internal_issue_disconnect(WIFI_REASON_ROAMING);
/* supplicant connect will only be called in case of bss transition(roaming) */
esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC);
esp_wifi_set_config(WIFI_IF_STA, config);
os_free(config);
esp_wifi_connect();
}
void esp_set_rm_enabled_ie(void)
static size_t get_rm_enabled_ie(uint8_t *ie, size_t len)
{
uint8_t rmm_ie[5] = {0};
uint8_t rrm_ie[7] = {0};
uint8_t rrm_ie_len = 5;
uint8_t *pos = rmm_ie;
uint8_t *pos = rrm_ie;
if (!esp_wifi_is_rm_enabled_internal(WIFI_IF_STA)) {
return 0;
}
*pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
*pos++ = rrm_ie_len;
*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
*pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
@ -326,10 +379,147 @@ void esp_set_rm_enabled_ie(void)
#endif
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE;
/* set rm enabled IE if enabled in driver */
if (esp_wifi_is_rm_enabled_internal(WIFI_IF_STA)) {
esp_wifi_set_appie_internal(WIFI_APPIE_RM_ENABLED_CAPS, rmm_ie, rrm_ie_len, 0);
os_memcpy(ie, rrm_ie, sizeof(rrm_ie));
return rrm_ie_len + 2;
}
#ifdef CONFIG_MBO
static size_t get_mbo_oce_scan_ie(uint8_t *ie, size_t len)
{
uint8_t mbo_ie[32] = {0};
uint8_t mbo_ie_len = 32;
/* Return if MBO IE is not enabled in driver */
if (!esp_wifi_is_mbo_enabled_internal(WIFI_IF_STA)) {
return 0;
}
struct wpabuf *default_ies = NULL;
if (wpabuf_resize(&default_ies, 18) == 0) {
wpas_mbo_scan_ie(&g_wpa_supp, default_ies);
os_memcpy(mbo_ie, wpabuf_head_u8(default_ies), wpabuf_len(default_ies));
mbo_ie_len = wpabuf_len(default_ies);
wpabuf_free(default_ies);
}
os_memcpy(ie, mbo_ie, mbo_ie_len);
return mbo_ie_len;
}
static size_t get_mbo_oce_assoc_ie(uint8_t *ie, size_t len)
{
uint8_t mbo_ie[32] = {0};
uint8_t mbo_ie_len = 32;
/* Return if MBO IE is not enabled in driver */
if (!esp_wifi_is_mbo_enabled_internal(WIFI_IF_STA)) {
return 0;
}
mbo_ie_len = wpas_mbo_ie(&g_wpa_supp, mbo_ie, mbo_ie_len, 0);
os_memcpy(ie, mbo_ie, mbo_ie_len);
return mbo_ie_len;
}
#endif
static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len)
{
uint8_t ext_caps_ie[5] = {0};
uint8_t ext_caps_ie_len = 3;
uint8_t *pos = ext_caps_ie;
if (!esp_wifi_is_btm_enabled_internal(WIFI_IF_STA)) {
return 0;
}
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = ext_caps_ie_len;
*pos++ = 0;
*pos++ = 0;
#define WLAN_EXT_CAPAB_BSS_TRANSITION BIT(3)
*pos |= WLAN_EXT_CAPAB_BSS_TRANSITION;
#undef WLAN_EXT_CAPAB_BSS_TRANSITION
os_memcpy(ie, ext_caps_ie, sizeof(ext_caps_ie));
return ext_caps_ie_len + 2;
}
static uint8_t get_operating_class_ie(uint8_t *ie, size_t len)
{
uint8_t op_class_ie[4] = {0};
uint8_t op_class_ie_len = 2;
uint8_t *pos = op_class_ie;
*pos++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
*pos++ = op_class_ie_len;
#define OPER_CLASS 0x51
/* Current Operating Class */
*pos++ = OPER_CLASS;
#undef OPER_CLASS
*pos = 0;
os_memcpy(ie, op_class_ie, sizeof(op_class_ie));
return op_class_ie_len + 2;
}
void esp_set_scan_ie(void)
{
#define SCAN_IE_LEN 64
uint8_t *ie, *pos;
size_t len = SCAN_IE_LEN, ie_len;
ie = os_malloc(SCAN_IE_LEN);
if (!ie) {
wpa_printf(MSG_ERROR, "failed to allocate ie");
return;
}
pos = ie;
ie_len = get_extended_caps_ie(pos, len);
pos += ie_len;
len -= ie_len;
#ifdef CONFIG_MBO
ie_len = get_mbo_oce_scan_ie(pos, len);
pos += ie_len;
len -= ie_len;
#endif
esp_wifi_unset_appie_internal(WIFI_APPIE_PROBEREQ);
esp_wifi_set_appie_internal(WIFI_APPIE_PROBEREQ, ie, SCAN_IE_LEN - len, 0);
os_free(ie);
#undef SCAN_IE_LEN
}
void esp_set_assoc_ie(void)
{
#define ASSOC_IE_LEN 128
uint8_t *ie, *pos;
size_t len = ASSOC_IE_LEN, ie_len;
ie = os_malloc(ASSOC_IE_LEN);
if (!ie) {
wpa_printf(MSG_ERROR, "failed to allocate ie");
return;
}
pos = ie;
ie_len = get_extended_caps_ie(pos, len);
pos += ie_len;
len -= ie_len;
ie_len = get_operating_class_ie(pos, len);
pos += ie_len;
len -= ie_len;
ie_len = get_rm_enabled_ie(pos, len);
pos += ie_len;
len -= ie_len;
#ifdef CONFIG_MBO
ie_len = get_mbo_oce_assoc_ie(pos, len);
pos += ie_len;
len -= ie_len;
#endif
esp_wifi_unset_appie_internal(WIFI_APPIE_ASSOC_REQ);
esp_wifi_set_appie_internal(WIFI_APPIE_ASSOC_REQ, ie, ASSOC_IE_LEN - len, 0);
os_free(ie);
#undef ASSOC_IE_LEN
}
void esp_get_tx_power(uint8_t *tx_power)
@ -399,15 +589,3 @@ int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data)
SUPPLICANT_API_UNLOCK();
return 0;
}
int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf)
{
if (type == WLAN_FC_STYPE_BEACON || type == WLAN_FC_STYPE_PROBE_RESP) {
return esp_handle_beacon_probe(type, frame, len, sender, rssi, channel, current_tsf);
} else if (type == WLAN_FC_STYPE_ACTION) {
return esp_handle_action_frm(frame, len, sender, rssi, channel);
}
return -1;
}

View File

@ -1,17 +1,7 @@
/**
* Copyright 2020 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_COMMON_I_H
@ -47,23 +37,20 @@ enum SIG_SUPPLICANT {
};
int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data);
int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf);
void esp_set_rm_enabled_ie(void);
void esp_get_tx_power(uint8_t *tx_power);
void esp_supplicant_common_init(struct wpa_funcs *wpa_cb);
void esp_supplicant_common_deinit(void);
void esp_set_scan_ie(void);
void esp_set_assoc_ie(void);
#else
#include "esp_rrm.h"
#include "esp_wnm.h"
#include "esp_mbo.h"
static inline void esp_set_scan_ie(void) { }
static inline void esp_set_assoc_ie(void) { }
static inline void esp_set_rm_enabled_ie(void) {}
static inline int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf)
{
return -1;
}
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx)
{
@ -77,5 +64,9 @@ int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
return -1;
}
int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan)
{
return -1;
}
#endif
#endif

View File

@ -1,17 +1,7 @@
/**
* Copyright 2020 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#include "utils/includes.h"
@ -32,8 +22,8 @@
extern struct wpa_supplicant g_wpa_supp;
static void esp_scan_done_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
static void scan_done_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
@ -46,7 +36,7 @@ static void esp_scan_done_event_handler(void* arg, esp_event_base_t event_base,
esp_supplicant_post_evt(SIG_SUPPLICANT_SCAN_DONE, 0);
}
static void esp_supp_handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
static void handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss = wpa_bss_get_next_bss(wpa_s, wpa_s->current_bss);
@ -64,7 +54,7 @@ static void esp_supp_handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
}
}
static void esp_supp_scan_done_cleanup(struct wpa_supplicant *wpa_s)
static void scan_done_cleanup(struct wpa_supplicant *wpa_s)
{
uint16_t number = 1;
wifi_ap_record_t ap_records;
@ -84,10 +74,10 @@ void esp_supplicant_handle_scan_done_evt(void)
if (wpa_s->scan_reason == REASON_RRM_BEACON_REPORT) {
wpas_beacon_rep_scan_process(wpa_s, wpa_s->scan_start_tsf);
} else if (wpa_s->scan_reason == REASON_WNM_BSS_TRANS_REQ) {
esp_supp_handle_wnm_scan_done(wpa_s);
handle_wnm_scan_done(wpa_s);
}
if (wpa_s->scanning) {
esp_supp_scan_done_cleanup(wpa_s);
scan_done_cleanup(wpa_s);
}
wpa_bss_update_end(wpa_s);
#ifndef SCAN_CACHE_SUPPORTED
@ -101,7 +91,7 @@ void esp_scan_init(struct wpa_supplicant *wpa_s)
wpa_bss_init(wpa_s);
wpa_s->last_scan_res = NULL;
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE,
&esp_scan_done_event_handler, NULL);
&scan_done_event_handler, NULL);
}
void esp_scan_deinit(struct wpa_supplicant *wpa_s)
@ -110,7 +100,7 @@ void esp_scan_deinit(struct wpa_supplicant *wpa_s)
os_free(wpa_s->last_scan_res);
wpa_s->last_scan_res = NULL;
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_SCAN_DONE,
&esp_scan_done_event_handler);
&scan_done_event_handler);
}
int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
@ -158,10 +148,7 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
res->level = rssi;
os_memcpy(res->tsf_bssid, wpa_s->tsf_bssid, ETH_ALEN);
res->parent_tsf = current_tsf - wpa_s->scan_start_tsf;
if (type == WLAN_FC_STYPE_PROBE_RESP)
res->ie_len = len;
else if (type == WLAN_FC_STYPE_BEACON)
res->beacon_ie_len = len;
res->ie_len = len;
ptr += sizeof(struct wpa_scan_res);
@ -173,8 +160,8 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
return 0;
}
static int esp_issue_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *scan_params)
static int issue_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *scan_params)
{
wifi_scan_config_t *params = NULL;
int ret = 0;
@ -203,9 +190,13 @@ static int esp_issue_scan(struct wpa_supplicant *wpa_s,
goto cleanup;
}
os_memcpy(params->ssid, scan_params->ssids[0].ssid, scan_params->ssids[0].ssid_len);
params->scan_type = WIFI_SCAN_TYPE_ACTIVE;
} else
if (scan_params->mode == BEACON_REPORT_MODE_PASSIVE) {
params->scan_type = WIFI_SCAN_TYPE_PASSIVE;
} else {
params->scan_type = WIFI_SCAN_TYPE_ACTIVE;
}
if (scan_params->bssid) {
params->bssid = os_zalloc(ETH_ALEN);
@ -260,7 +251,7 @@ cleanup:
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
return esp_issue_scan(wpa_s, params);
return issue_scan(wpa_s, params);
}
void wpa_scan_results_free(struct wpa_scan_results *res)

View File

@ -1,16 +1,8 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_WIFI_DRIVER_H_
#define _ESP_WIFI_DRIVER_H_
@ -57,8 +49,7 @@ typedef enum {
/* wifi_appie_t is in rom code and can't be changed anymore, use wifi_appie_ram_t for new app IEs */
typedef enum {
WIFI_APPIE_RM_ENABLED_CAPS = WIFI_APPIE_MAX,
WIFI_APPIE_RAM_MAX,
WIFI_APPIE_RAM_MAX = WIFI_APPIE_MAX,
} wifi_appie_ram_t;
enum {
@ -137,6 +128,7 @@ struct wpa_funcs {
int (*wpa3_parse_sae_msg)(uint8_t *buf, size_t len, uint32_t type, uint16_t status);
int (*wpa_sta_rx_mgmt)(u8 type, u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channel, u64 current_tsf);
void (*wpa_config_done)(void);
bool (*wpa_sta_profile_match)(u8 *bssid);
};
struct wpa2_funcs {
@ -266,5 +258,6 @@ esp_err_t esp_wifi_action_tx_req(uint8_t type, uint8_t channel,
uint32_t wait_time_ms, const wifi_action_tx_req_t *req);
esp_err_t esp_wifi_remain_on_channel(uint8_t ifx, uint8_t type, uint8_t channel,
uint32_t wait_time_ms, wifi_action_rx_cb_t rx_cb);
bool esp_wifi_is_mbo_enabled_internal(uint8_t if_index);
#endif /* _ESP_WIFI_DRIVER_H_ */

View File

@ -1,16 +1,8 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "utils/includes.h"
#include "utils/common.h"
@ -123,6 +115,8 @@ bool wpa_attach(void)
if(ret) {
ret = (esp_wifi_register_tx_cb_internal(eapol_txcb, WIFI_TXCB_EAPOL_ID) == ESP_OK);
}
esp_set_scan_ie();
esp_set_assoc_ie();
return ret;
}
@ -185,7 +179,7 @@ void wpa_sta_connect(uint8_t *bssid)
void wpa_config_done(void)
{
/* used in future for setting scan and assoc IEs */
esp_set_rm_enabled_ie();
esp_set_assoc_ie();
}
int wpa_parse_wpa_ie_wrapper(const u8 *wpa_ie, size_t wpa_ie_len, wifi_wpa_ie_t *data)
@ -230,6 +224,7 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code)
static inline void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
{
wpa_cb->wpa_sta_rx_mgmt = NULL;
wpa_cb->wpa_sta_profile_match = NULL;
}
static inline void esp_supplicant_common_deinit(void)
{

View File

@ -132,8 +132,9 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
return !is_zero_ether_addr(bss->bssid) && wpa_s->current_bss->bssid &&
(os_memcmp(bss->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0);
#endif
#else
return 0;
#endif
}
static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
@ -198,7 +199,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
bss->ssid_len = ssid_len;
bss->ie_len = res->ie_len;
bss->beacon_ie_len = res->beacon_ie_len;
os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
dl_list_add_tail(&wpa_s->bss, &bss->list);
dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
@ -238,7 +239,7 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
dl_list_del(&bss->list);
if (bss->ie_len + bss->beacon_ie_len >=
res->ie_len + res->beacon_ie_len) {
os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
bss->ie_len = res->ie_len;
bss->beacon_ie_len = res->beacon_ie_len;
} else {
@ -258,7 +259,7 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
if (wpa_s->current_bss == bss)
wpa_s->current_bss = nbss;
bss = nbss;
os_memcpy(bss + 1, res + 1,
os_memcpy(bss->ies, res + 1,
res->ie_len + res->beacon_ie_len);
bss->ie_len = res->ie_len;
bss->beacon_ie_len = res->beacon_ie_len;
@ -471,6 +472,31 @@ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
}
/**
* wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
* @bss: BSS table entry
* @vendor_type: Vendor type (four octets starting the IE payload)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the BSS
* entry.
*/
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
{
const u8 *ies;
const struct element *elem;
ies = wpa_bss_ie_ptr(bss);
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, bss->ie_len) {
if (elem->datalen >= 4 &&
vendor_type == WPA_GET_BE32(elem->data))
return &elem->id;
}
return NULL;
}
int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
{
return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),

View File

@ -58,8 +58,14 @@ struct wpa_bss {
size_t beacon_ie_len;
/* followed by ie_len octets of IEs */
/* followed by beacon_ie_len octets of IEs */
u8 ies[];
};
static inline const u8 * wpa_bss_ie_ptr(const struct wpa_bss *bss)
{
return bss->ies;
}
void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res,
@ -75,6 +81,7 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
const u8 *bssid);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab);
struct wpa_bss * wpa_bss_get_next_bss(struct wpa_supplicant *wpa_s,
struct wpa_bss *prev_bss);

View File

@ -37,6 +37,42 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
return NULL;
}
const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
{
const struct element *elem;
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
if (elem->datalen >= 4 &&
vendor_type == WPA_GET_BE32(elem->data))
return &elem->id;
}
return NULL;
}
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
{
/*
* MBO IE requires 6 bytes without the attributes: EID (1), length (1),
* OUI (3), OUI type (1).
*/
if (len < 6 + attr_len) {
wpa_printf(MSG_DEBUG,
"MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
len, attr_len);
return 0;
}
*buf++ = WLAN_EID_VENDOR_SPECIFIC;
*buf++ = attr_len + 4;
WPA_PUT_BE24(buf, OUI_WFA);
buf += 3;
*buf++ = MBO_OUI_TYPE;
os_memcpy(buf, attr, attr_len);
return 6 + attr_len;
}
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len)
{

View File

@ -39,5 +39,7 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t len);
int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
u8 get_operating_class(u8 chan, int sec_channel);
#endif /* IEEE802_11_COMMON_H */

View File

@ -212,6 +212,7 @@
#define WLAN_EID_FAST_BSS_TRANSITION 55
#define WLAN_EID_TIMEOUT_INTERVAL 56
#define WLAN_EID_RIC_DATA 57
#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
#define WLAN_EID_WAPI 68
@ -536,6 +537,13 @@ struct ieee80211_ht_operation {
/* 2 - Reserved */
#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
#define MBO_IE_VENDOR_TYPE 0x506f9a16
#define OSEN_IE_VENDOR_TYPE 0x506f9a12
#define MBO_OUI_TYPE 22
#define OCE_STA BIT(0)
#define OCE_STA_CFON BIT(1)
#define OCE_AP BIT(2)
/*
* WMM Information Element (used in (Re)Association Request frames; may also be
* used in Beacon frames)
@ -622,6 +630,45 @@ enum wmm_ac {
WMM_AC_NUM = 4
};
/* MBO v0.0_r19, 4.2: MBO Attributes */
/* Table 4-5: MBO Attributes */
/* OCE v0.0.10, Table 4-3: OCE Attributes */
enum mbo_attr_id {
MBO_ATTR_ID_AP_CAPA_IND = 1,
MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
MBO_ATTR_ID_CELL_DATA_CAPA = 3,
MBO_ATTR_ID_ASSOC_DISALLOW = 4,
MBO_ATTR_ID_CELL_DATA_PREF = 5,
MBO_ATTR_ID_TRANSITION_REASON = 6,
MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
OCE_ATTR_ID_CAPA_IND = 101,
OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
OCE_ATTR_ID_RNR_COMPLETENESS = 104,
};
/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
/* Table 4-7: MBO AP Capability Indication Field Values */
#define MBO_AP_CAPA_CELL_AWARE BIT(6)
/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */
/* Table 4-10: Reason Code Field Values */
enum mbo_non_pref_chan_reason {
MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
MBO_NON_PREF_CHAN_REASON_RSSI = 1,
MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
};
/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */
/* Table 4-13: Cellular Data Connectivity Field */
enum mbo_cellular_capa {
MBO_CELL_CAPA_AVAILABLE = 1,
MBO_CELL_CAPA_NOT_AVAILABLE = 2,
MBO_CELL_CAPA_NOT_SUPPORTED = 3,
};
/* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */
/* Table 4-21: Transition Rejection Reason Code Field Values */
enum mbo_transition_reject_reason {
@ -634,6 +681,12 @@ enum mbo_transition_reject_reason {
MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
};
/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
#define OCE_RELEASE 1
#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
#define OCE_IS_STA_CFON BIT(3)
#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
/* IEEE 802.11v - WNM Action field values */
enum wnm_action {
WNM_EVENT_REQ = 0,

View File

@ -0,0 +1,799 @@
/*
* wpa_supplicant - MBO
*
* Copyright(c) 2015 Intel Deutschland GmbH
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* 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"
#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "bss.h"
#include "scan.h"
#include "ieee802_11_common.h"
#ifdef ESP_SUPPLICANT
#include "esp_timer.h"
#include "esp_mbo.h"
extern struct wpa_supplicant g_wpa_supp;
#endif
/* type + length + oui + oui type */
#define MBO_IE_HEADER 6
#ifndef ESP_SUPPLICANT
static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
{
if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
return -1;
/* Only checking the validity of the channel and oper_class */
if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
return -1;
return 0;
}
#endif
const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr)
{
const u8 *mbo;
u8 ie_len = mbo_ie[1];
if (ie_len < MBO_IE_HEADER - 2)
return NULL;
mbo = mbo_ie + MBO_IE_HEADER;
return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr);
}
const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
enum mbo_attr_id attr)
{
const u8 *mbo_ie;
mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE);
if (!mbo_ie)
return NULL;
return mbo_attr_from_mbo_ie(mbo_ie, attr);
}
const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
{
const u8 *mbo, *end;
if (!bss)
return NULL;
mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
if (!mbo)
return NULL;
end = mbo + 2 + mbo[1];
mbo += MBO_IE_HEADER;
return get_ie(mbo, end - mbo, attr);
}
void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss
#ifndef ESP_SUPPLICANT
, struct wpa_ssid *ssid
#endif
)
{
const u8 *rsne, *mbo, *oce;
struct wpa_ie_data ie;
wpa_s->disable_mbo_oce = 0;
if (!bss)
return;
mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND);
oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND);
if (!mbo && !oce)
return;
if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON))
return; /* STA-CFON is not required to enable PMF */
rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (!rsne || wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ie) < 0)
return; /* AP is not using RSN */
if (!(ie.capabilities & WPA_CAPABILITY_MFPC))
wpa_s->disable_mbo_oce = 1; /* AP uses RSN without PMF */
#ifdef ESP_SUPPLICANT
if (!esp_wifi_sta_pmf_enabled())
#else
if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
#endif
wpa_s->disable_mbo_oce = 1; /* STA uses RSN without PMF */
if (wpa_s->disable_mbo_oce)
wpa_printf(MSG_INFO,
"MBO: Disable MBO/OCE due to misbehaving AP not having enabled PMF");
}
static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
struct wpabuf *mbo,
u8 start, u8 end)
{
u8 i;
wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
for (i = start; i < end; i++)
wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
}
static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size)
{
wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
wpabuf_put_u8(mbo, size); /* Length */
}
static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
struct wpabuf *mbo, u8 start, u8 end)
{
size_t size = end - start + 3;
if (size + 2 > wpabuf_tailroom(mbo))
return;
wpas_mbo_non_pref_chan_attr_hdr(mbo, size);
wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
}
static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
{
wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
wpabuf_put_u8(mbo, len); /* Length */
wpabuf_put_be24(mbo, OUI_WFA);
wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
}
static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
struct wpabuf *mbo, u8 start,
u8 end)
{
size_t size = end - start + 7;
if (size + 2 > wpabuf_tailroom(mbo))
return;
wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
}
static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
struct wpabuf *mbo, int subelement)
{
u8 i, start = 0;
struct wpa_mbo_non_pref_channel *start_pref;
if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
if (subelement)
wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
else
wpas_mbo_non_pref_chan_attr_hdr(mbo, 0);
return;
}
start_pref = &wpa_s->non_pref_chan[0];
for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
struct wpa_mbo_non_pref_channel *non_pref = NULL;
if (i < wpa_s->non_pref_chan_num)
non_pref = &wpa_s->non_pref_chan[i];
if (!non_pref ||
non_pref->oper_class != start_pref->oper_class ||
non_pref->reason != start_pref->reason ||
non_pref->preference != start_pref->preference) {
if (subelement)
wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
start, i);
else
wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
i);
if (!non_pref)
return;
start = i;
start_pref = non_pref;
}
}
}
int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
int add_oce_capa)
{
struct wpabuf *mbo;
int res;
if (len < MBO_IE_HEADER + 3 + 7 +
((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
return 0;
/* Leave room for the MBO IE header */
mbo = wpabuf_alloc(len - MBO_IE_HEADER);
if (!mbo)
return 0;
/* Add non-preferred channels attribute */
wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
/*
* Send cellular capabilities attribute even if AP does not advertise
* cellular capabilities.
*/
wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
wpabuf_put_u8(mbo, 1);
#ifdef ESP_SUPPLICANT
wpabuf_put_u8(mbo, MBO_CELL_CAPA_NOT_SUPPORTED);
#else
wpabuf_put_u8(mbo, wpa_s->mbo_cell_capa);
#endif
/* Add OCE capability indication attribute if OCE is enabled */
if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) {
wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
wpabuf_put_u8(mbo, 1);
wpabuf_put_u8(mbo, OCE_RELEASE);
}
res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
if (!res)
wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
wpabuf_free(mbo);
return res;
}
static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
const u8 *data, size_t len)
{
struct wpabuf *buf;
int res;
/*
* Send WNM-Notification Request frame only in case of a change in
* non-preferred channels list during association, if the AP supports
* MBO.
*/
if (!wpa_s->current_bss ||
!wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
return;
buf = wpabuf_alloc(4 + len);
if (!buf)
return;
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
wpa_s->mbo_wnm_token++;
if (wpa_s->mbo_wnm_token == 0)
wpa_s->mbo_wnm_token++;
wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
wpabuf_put_data(buf, data, len);
res = wpa_drv_send_action(wpa_s, 0, 0,
wpabuf_head(buf), wpabuf_len(buf), 0);
if (res < 0)
wpa_printf(MSG_DEBUG,
"Failed to send WNM-Notification Request frame with non-preferred channel list");
wpabuf_free(buf);
}
static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
{
struct wpabuf *buf;
buf = wpabuf_alloc(512);
if (!buf)
return;
wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
wpabuf_len(buf));
wpabuf_free(buf);
}
#ifndef ESP_SUPPLICANT
static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
struct wpa_mbo_non_pref_channel *b)
{
return a->oper_class == b->oper_class && a->chan == b->chan;
}
/*
* wpa_non_pref_chan_cmp - Compare two channels for sorting
*
* In MBO IE non-preferred channel subelement we can put many channels in an
* attribute if they are in the same operating class and have the same
* preference and reason. To make it easy for the functions that build
* the IE attributes and WNM Request subelements, save the channels sorted
* by their oper_class and reason.
*/
static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
{
const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
if (a->oper_class != b->oper_class)
return (int) a->oper_class - (int) b->oper_class;
if (a->reason != b->reason)
return (int) a->reason - (int) b->reason;
return (int) a->preference - (int) b->preference;
}
int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
const char *non_pref_chan)
{
char *cmd, *token, *context = NULL;
struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
size_t num = 0, size = 0;
unsigned i;
wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
non_pref_chan ? non_pref_chan : "N/A");
/*
* The shortest channel configuration is 7 characters - 3 colons and
* 4 values.
*/
if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
goto update;
cmd = os_strdup(non_pref_chan);
if (!cmd)
return -1;
while ((token = str_token(cmd, " ", &context))) {
struct wpa_mbo_non_pref_channel *chan;
int ret;
unsigned int _oper_class;
unsigned int _chan;
unsigned int _preference;
unsigned int _reason;
if (num == size) {
size = size ? size * 2 : 1;
tmp_chans = os_realloc_array(chans, size,
sizeof(*chans));
if (!tmp_chans) {
wpa_printf(MSG_ERROR,
"Couldn't reallocate non_pref_chan");
goto fail;
}
chans = tmp_chans;
}
chan = &chans[num];
ret = sscanf(token, "%u:%u:%u:%u", &_oper_class,
&_chan, &_preference, &_reason);
if (ret != 4 ||
_oper_class > 255 || _chan > 255 ||
_preference > 255 || _reason > 65535 ) {
wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
token);
goto fail;
}
chan->oper_class = _oper_class;
chan->chan = _chan;
chan->preference = _preference;
chan->reason = _reason;
if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
chan->chan, chan->reason)) {
wpa_printf(MSG_ERROR,
"Invalid non_pref_chan: oper class %d chan %d reason %d",
chan->oper_class, chan->chan, chan->reason);
goto fail;
}
for (i = 0; i < num; i++)
if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
break;
if (i != num) {
wpa_printf(MSG_ERROR,
"oper class %d chan %d is duplicated",
chan->oper_class, chan->chan);
goto fail;
}
num++;
}
os_free(cmd);
if (chans) {
qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
wpa_non_pref_chan_cmp);
}
update:
os_free(wpa_s->non_pref_chan);
wpa_s->non_pref_chan = chans;
wpa_s->non_pref_chan_num = num;
wpas_mbo_non_pref_chan_changed(wpa_s);
return 0;
fail:
os_free(chans);
os_free(cmd);
return -1;
}
#else
int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
struct non_pref_chan_s *non_pref_chan)
{
struct wpa_mbo_non_pref_channel *chans = NULL;
/*
* The shortest channel configuration is 7 characters - 3 colons and
* 4 values.
*/
if (!non_pref_chan)
goto update;
chans = os_malloc(sizeof(struct wpa_mbo_non_pref_channel) * non_pref_chan->non_pref_chan_num);
os_memcpy(chans, non_pref_chan->chan, sizeof(struct wpa_mbo_non_pref_channel) * non_pref_chan->non_pref_chan_num);
update:
os_free(wpa_s->non_pref_chan);
wpa_s->non_pref_chan = chans;
if (non_pref_chan)
wpa_s->non_pref_chan_num = non_pref_chan->non_pref_chan_num;
else
wpa_s->non_pref_chan_num = 0;
wpas_mbo_non_pref_chan_changed(wpa_s);
return 0;
}
#endif
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
{
u8 *len;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(ie, 1);
wpabuf_put_be24(ie, OUI_WFA);
wpabuf_put_u8(ie, MBO_OUI_TYPE);
wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
wpabuf_put_u8(ie, 1);
#ifdef ESP_SUPPLICANT
wpabuf_put_u8(ie, MBO_CELL_CAPA_NOT_SUPPORTED);
#else
wpabuf_put_u8(ie, wpa_s->mbo_cell_capa);
#endif
if (wpa_s->enable_oce & OCE_STA) {
wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
wpabuf_put_u8(ie, 1);
wpabuf_put_u8(ie, OCE_RELEASE);
}
*len = (u8 *) wpabuf_put(ie, 0) - len - 1;
}
#ifdef ESP_SUPPLICANT
static struct
wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{
struct wpa_bss_tmp_disallowed *bss;
dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
return bss;
}
return NULL;
}
static void wpa_bss_tmp_disallow_timeout(void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx;
/* Make sure the bss is not already freed */
dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (bss == tmp) {
dl_list_del(&tmp->list);
esp_timer_stop(bss->blacklist_timer);
esp_timer_delete(bss->blacklist_timer);
os_free(tmp);
break;
}
}
}
void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
unsigned int sec, int rssi_threshold)
{
struct wpa_bss_tmp_disallowed *bss;
bss = wpas_get_disallowed_bss(wpa_s, bssid);
if (bss) {
esp_timer_stop(bss->blacklist_timer);
goto finish;
}
bss = os_malloc(sizeof(*bss));
esp_timer_create_args_t blacklist_timer_create = {
.callback = &wpa_bss_tmp_disallow_timeout,
.arg = bss,
.dispatch_method = ESP_TIMER_TASK,
.name = "blacklist_timeout_timer"
};
esp_timer_create(&blacklist_timer_create, &(bss->blacklist_timer));
if (!bss) {
wpa_printf(MSG_DEBUG,
"Failed to allocate memory for temp disallow BSS");
return;
}
os_memcpy(bss->bssid, bssid, ETH_ALEN);
dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
finish:
esp_timer_start_once(bss->blacklist_timer, (sec + 1) * 1e6);
}
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss)
{
struct wpa_bss_tmp_disallowed *disallowed = NULL, *tmp, *prev;
dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (os_memcmp(bss->bssid, tmp->bssid, ETH_ALEN) == 0) {
disallowed = tmp;
break;
}
}
if (!disallowed)
return 0;
return 1;
}
#endif
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
size_t len)
{
const u8 *pos, *cell_pref = NULL;
u8 id, elen;
u16 disallowed_sec = 0;
if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
mbo_ie[3] != MBO_OUI_TYPE)
return;
pos = mbo_ie + 4;
len -= 4;
while (len >= 2) {
id = *pos++;
elen = *pos++;
len -= 2;
if (elen > len)
goto fail;
switch (id) {
case MBO_ATTR_ID_CELL_DATA_PREF:
if (elen != 1)
goto fail;
#ifndef ESP_SUPPLICANT
if (wpa_s->mbo_cell_capa ==
MBO_CELL_CAPA_AVAILABLE)
cell_pref = pos;
else
#endif
wpa_printf(MSG_DEBUG,
"MBO: Station does not support Cellular data connection");
break;
case MBO_ATTR_ID_TRANSITION_REASON:
if (elen != 1)
goto fail;
wpa_s->wnm_mbo_trans_reason_present = 1;
wpa_s->wnm_mbo_transition_reason = *pos;
break;
case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
if (elen != 2)
goto fail;
if (wpa_s->wnm_mode &
WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
wpa_printf(MSG_DEBUG,
"MBO: Unexpected association retry delay, BSS is terminating");
goto fail;
} else if (wpa_s->wnm_mode &
WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
disallowed_sec = WPA_GET_LE16(pos);
wpa_printf(MSG_DEBUG,
"MBO: Association retry delay: %u",
disallowed_sec);
} else {
wpa_printf(MSG_DEBUG,
"MBO: Association retry delay attribute not in disassoc imminent mode");
}
break;
case MBO_ATTR_ID_AP_CAPA_IND:
case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
case MBO_ATTR_ID_CELL_DATA_CAPA:
case MBO_ATTR_ID_ASSOC_DISALLOW:
case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
wpa_printf(MSG_DEBUG,
"MBO: Attribute %d should not be included in BTM Request frame",
id);
break;
default:
wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
id);
return;
}
pos += elen;
len -= elen;
}
if (cell_pref)
wpa_printf(MSG_INFO, "preference=%u",
*cell_pref);
if (wpa_s->wnm_mbo_trans_reason_present)
wpa_printf(MSG_INFO, "reason=%u",
wpa_s->wnm_mbo_transition_reason);
if (disallowed_sec && wpa_s->current_bss)
wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
disallowed_sec, 0);
return;
fail:
wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
id, elen, len);
}
size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
size_t len,
enum mbo_transition_reject_reason reason)
{
u8 reject_attr[3];
reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
reject_attr[1] = 1;
reject_attr[2] = reason;
return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
}
#ifndef ESP_SUPPLICANT
void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
{
u8 cell_capa[7];
if (wpa_s->mbo_cell_capa == mbo_cell_capa) {
wpa_printf(MSG_DEBUG,
"MBO: Cellular capability already set to %u",
mbo_cell_capa);
return;
}
wpa_s->mbo_cell_capa = mbo_cell_capa;
cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
cell_capa[1] = 5; /* Length */
WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
cell_capa[6] = mbo_cell_capa;
wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
wpa_supplicant_set_default_scan_ies(wpa_s);
}
struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, u32 mbo_subtypes)
{
struct wpabuf *anqp_buf;
u8 *len_pos;
u8 i;
if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
wpa_printf(MSG_INFO, "MBO: " MACSTR
" does not support MBO - cannot request MBO ANQP elements from it",
MAC2STR(bss->bssid));
return NULL;
}
/* Allocate size for the maximum case - all MBO subtypes are set */
anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
if (!anqp_buf)
return NULL;
len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
wpabuf_put_be24(anqp_buf, OUI_WFA);
wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
/* The first valid MBO subtype is 1 */
for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
if (mbo_subtypes & BIT(i))
wpabuf_put_u8(anqp_buf, i);
}
gas_anqp_set_element_len(anqp_buf, len_pos);
return anqp_buf;
}
void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, const u8 *sa,
const u8 *data, size_t slen)
{
const u8 *pos = data;
u8 subtype;
if (slen < 1)
return;
subtype = *pos++;
slen--;
switch (subtype) {
case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
if (slen < 1)
break;
wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
" cell_conn_pref=%u", MAC2STR(sa), *pos);
break;
default:
wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
subtype);
break;
}
}
#endif

View File

@ -851,8 +851,15 @@ wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s,
goto out;
}
params->channel = req->channel;
#ifdef ESP_SUPPLICANT
if (params->channel == 0xff) {
/* set it to zero */
params->channel = 0;
}
#endif
params->duration = le_to_host16(req->duration);
params->duration_mandatory = duration_mandatory;
params->mode = req->mode;
if (!params->duration) {
wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0");
ret = -1;

View File

@ -191,6 +191,109 @@ static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
return NULL;
}
#ifdef CONFIG_MBO
static struct wpa_bss *
get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
enum mbo_transition_reject_reason *reason)
{
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;
#ifndef ESP_SUPPLICANT
info = wpa_drv_get_bss_trans_status(wpa_s, &params);
#endif
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);
#ifndef ESP_SUPPLICANT
if (target &&
(target->level <
wpa_s->conf->disassoc_imminent_rssi_threshold))
continue;
#else
if (target)
continue;
#endif
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 &&
os_memcmp(first_candidate_bssid,
info->candidates[i].bssid, ETH_ALEN) == 0)
{
*reason = info->candidates[i].reject_reason;
break;
}
}
}
target = NULL;
end:
os_free(params.bssid);
if (info) {
os_free(info->candidates);
os_free(info);
}
return target;
}
#endif /* CONFIG_MBO */
/* basic function to match candidate profile with current bss */
bool wpa_scan_res_match(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
@ -294,7 +397,14 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
nei->acceptable = 1;
}
#ifdef CONFIG_MBO
if (wpa_s->wnm_mbo_trans_reason_present)
target = get_mbo_transition_candidate(wpa_s, reason);
else
target = get_first_acceptable(wpa_s);
#else /* CONFIG_MBO */
target = get_first_acceptable(wpa_s);
#endif /* CONFIG_MBO */
if (target) {
wpa_printf(MSG_DEBUG,
@ -506,6 +616,27 @@ static void wnm_send_bss_transition_mgmt_resp(
if (status == WNM_BSS_TM_ACCEPT)
wnm_add_cand_list(wpa_s, &buf);
#ifdef CONFIG_MBO
if (status != WNM_BSS_TM_ACCEPT &&
wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
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");
return;
}
wpabuf_put_data(buf, mbo, ret);
}
}
#endif /* CONFIG_MBO */
res = wpa_drv_send_action(wpa_s, 0, 0,
wpabuf_head_u8(buf), wpabuf_len(buf), 0);
if (res < 0) {
@ -692,8 +823,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
{
unsigned int beacon_int;
u8 valid_int;
#ifdef CONFIG_MBO
const u8 *vendor;
#endif /* CONFIG_MBO */
if (wpa_s->disable_btm)
if (wpa_s->disable_mbo_oce || wpa_s->disable_btm)
return;
if (end - pos < 5)
@ -721,6 +855,19 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
wpa_s->wnm_dissoc_timer, valid_int);
#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);
wnm_send_bss_transition_mgmt_resp(
wpa_s, wpa_s->wnm_dialog_token,
wpa_s->reject_btm_req_reason,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
return;
}
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
pos += 5;
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {

View File

@ -44,6 +44,9 @@ struct neighbor_report {
unsigned int bearing_present:1;
unsigned int bss_term_present:1;
unsigned int acceptable:1;
#ifdef CONFIG_MBO
unsigned int is_first:1;
#endif /* CONFIG_MBO */
struct measurement_pilot *meas_pilot;
struct multiple_bssid *mul_bssid;
int freq;
@ -56,6 +59,13 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
u8 *sender, u8 *payload, size_t len);
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
size_t len);
size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
size_t len,
enum mbo_transition_reject_reason reason);
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
u8 query_reason,
const char *btm_candidates,
@ -66,5 +76,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, char *ssid,
int after_new_scan);
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss);
#endif /* WNM_STA_H */

View File

@ -362,7 +362,6 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
return 0;
}
/**
* wpa_eapol_key_mic - Calculate EAPOL-Key MIC
* @key: EAPOL-Key Key Confirmation Key (KCK)

View File

@ -324,6 +324,8 @@ const char * wpa_cipher_txt(int cipher);
int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
struct wpa_ie_data *data);
int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data);
int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
u8 *mic);

View File

@ -41,6 +41,16 @@ struct rrm_data {
u8 dst_addr[ETH_ALEN];
};
struct wpa_bss_tmp_disallowed {
struct dl_list list;
u8 bssid[ETH_ALEN];
#ifndef ESP_SUPPLICANT
int rssi_threshold;
#else
esp_timer_handle_t blacklist_timer;
#endif
};
#define SSID_MAX_LEN 32
struct beacon_rep_data {
u8 token;
@ -61,6 +71,7 @@ enum scan_trigger_reason {
struct wpa_supplicant {
int disable_btm;
unsigned int disable_mbo_oce;
/* rrm ie */
uint8_t rrm_ie[5];
u8 extend_caps[8];
@ -101,10 +112,58 @@ struct wpa_supplicant {
u8 wnm_bss_termination_duration[12];
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
#ifdef CONFIG_MBO
unsigned int wnm_mbo_trans_reason_present:1;
u8 wnm_mbo_transition_reason;
/* Multiband operation non-preferred channel */
struct wpa_mbo_non_pref_channel {
enum mbo_non_pref_chan_reason reason;
u8 oper_class;
u8 chan;
u8 preference;
} *non_pref_chan;
size_t non_pref_chan_num;
u8 mbo_wnm_token;
/**
* enable_oce - Enable OCE if it is enabled by user and device also
* supports OCE.
* User can enable OCE with wpa_config's 'oce' parameter as follows -
* - Set BIT(0) to enable OCE in non-AP STA mode.
* - Set BIT(1) to enable OCE in STA-CFON mode.
*/
u8 enable_oce;
struct dl_list bss_tmp_disallowed;
#endif /* CONFIG_MBO */
#endif /* CONFIG_WNM */
struct rrm_data rrm;
struct beacon_rep_data beacon_rep_data;
struct os_reltime beacon_rep_scan;
};
#endif
struct non_pref_chan_s;
/* MBO functions */
int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
int add_oce_capa);
const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr);
const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
enum mbo_attr_id attr);
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
size_t len);
size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
size_t len,
enum mbo_transition_reject_reason reason);
void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, u32 mbo_subtypes);
void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, const u8 *sa,
const u8 *data, size_t slen);
void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s);
int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
struct non_pref_chan_s *non_pref_chan);
#endif /* WPA_SUPPLICANT_I_H */

View File

@ -143,6 +143,7 @@ struct wpa_driver_scan_params {
u16 duration;
unsigned int duration_mandatory;
u8 mode;
};
/**
@ -174,6 +175,20 @@ struct scan_info {
u8 scan_start_tsf_bssid[ETH_ALEN];
} scan_info;
struct wpa_bss_trans_info {
u8 mbo_transition_reason;
u8 n_candidates;
u8 *bssid;
};
struct wpa_bss_candidate_info {
u8 num;
struct candidate_list {
u8 bssid[ETH_ALEN];
u8 is_accept;
u32 reject_reason;
} *candidates;
};
/* driver_common.c */
void wpa_scan_results_free(struct wpa_scan_results *res);

View File

@ -6,6 +6,7 @@
#include "esp_wifi.h"
#include "esp_wnm.h"
#include "esp_rrm.h"
#include "esp_mbo.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
@ -40,6 +41,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
wifi_event_sta_disconnected_t *disconn = event_data;
ESP_LOGI(TAG, "station got disconnected reason=%d", disconn->reason);
if (disconn->reason == WIFI_REASON_ROAMING) {
ESP_LOGI(TAG, "station roaming, do nothing");
} else {
@ -323,6 +325,37 @@ static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base,
}
#endif
#if 0
/* Example code to update channel preference in MBO */
static void clear_chan_preference()
{
esp_mbo_update_non_pref_chan(NULL);
}
static void update_chan_preference(void)
{
struct non_pref_chan_s *chans = malloc(sizeof(struct non_pref_chan_s) + 2 * sizeof(struct non_pref_chan));
chans->non_pref_chan_num = 2;
/* first */
struct non_pref_chan *chan = &chans->chan[0];
chan->reason = NON_PREF_CHAN_REASON_UNSPECIFIED;
chan->oper_class = 0x51;
chan->chan = 1;
chan->preference = 0;
/* second */
chan = &chans->chan[1];
chan->reason = NON_PREF_CHAN_REASON_UNSPECIFIED;
chan->oper_class = 0x51;
chan->chan = 11;
chan->preference = 1;
esp_mbo_update_non_pref_chan(chans);
free(chans);
}
#endif
static void initialise_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
@ -348,6 +381,8 @@ static void initialise_wifi(void)
.password = EXAMPLE_WIFI_PASSWORD,
.rm_enabled =1,
.btm_enabled =1,
.mbo_enabled =1,
.pmf_cfg.capable = 1,
},
};

View File

@ -1,2 +1,3 @@
CONFIG_WPA_11KV_SUPPORT=y
CONFIG_WPA_SCAN_CACHE=y
CONFIG_WPA_MBO_SUPPORT=y

View File

@ -1229,7 +1229,6 @@ components/esp_wifi/include/esp_wifi.h
components/esp_wifi/include/esp_wifi_crypto_types.h
components/esp_wifi/include/esp_wifi_default.h
components/esp_wifi/include/esp_wifi_netif.h
components/esp_wifi/include/esp_wifi_types.h
components/esp_wifi/include/smartconfig_ack.h
components/esp_wifi/src/coexist.c
components/esp_wifi/src/lib_printf.c
@ -2979,24 +2978,18 @@ components/wifi_provisioning/src/wifi_provisioning_priv.h
components/wifi_provisioning/src/wifi_scan.c
components/wpa_supplicant/esp_supplicant/include/esp_dpp.h
components/wpa_supplicant/esp_supplicant/include/esp_rrm.h
components/wpa_supplicant/esp_supplicant/include/esp_wnm.h
components/wpa_supplicant/esp_supplicant/include/esp_wpa.h
components/wpa_supplicant/esp_supplicant/include/esp_wpa2.h
components/wpa_supplicant/esp_supplicant/include/esp_wps.h
components/wpa_supplicant/esp_supplicant/src/esp_common.c
components/wpa_supplicant/esp_supplicant/src/esp_common_i.h
components/wpa_supplicant/esp_supplicant/src/esp_dpp.c
components/wpa_supplicant/esp_supplicant/src/esp_dpp_i.h
components/wpa_supplicant/esp_supplicant/src/esp_hostap.c
components/wpa_supplicant/esp_supplicant/src/esp_hostap.h
components/wpa_supplicant/esp_supplicant/src/esp_scan.c
components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h
components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h
components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c
components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c
components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h
components/wpa_supplicant/esp_supplicant/src/esp_wpa_err.h
components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c
components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c
components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h
components/wpa_supplicant/esp_supplicant/src/esp_wps.c
@ -3027,6 +3020,7 @@ components/wpa_supplicant/src/common/eapol_common.h
components/wpa_supplicant/src/common/ieee802_11_common.c
components/wpa_supplicant/src/common/ieee802_11_common.h
components/wpa_supplicant/src/common/ieee802_11_defs.h
components/wpa_supplicant/src/common/mbo.c
components/wpa_supplicant/src/common/rrm.c
components/wpa_supplicant/src/common/rrm.h
components/wpa_supplicant/src/common/sae.c