Merge branch 'feature/11kv_roaming' into 'master'

esp_wifi: 11kv support for network assisted roaming

Closes WIFI-2471

See merge request espressif/esp-idf!9474
This commit is contained in:
Jiang Jiang Jian 2020-11-18 20:40:36 +08:00
commit a139bceb0a
48 changed files with 5553 additions and 144 deletions

View File

@ -52,6 +52,9 @@ static system_event_id_t esp_event_legacy_wifi_event_id(int32_t event_id)
case WIFI_EVENT_STA_AUTHMODE_CHANGE:
return SYSTEM_EVENT_STA_AUTHMODE_CHANGE;
case WIFI_EVENT_STA_BSS_RSSI_LOW:
return SYSTEM_EVENT_STA_BSS_RSSI_LOW;
case WIFI_EVENT_STA_WPS_ER_SUCCESS:
return SYSTEM_EVENT_STA_WPS_ER_SUCCESS;

View File

@ -36,6 +36,7 @@ typedef enum {
SYSTEM_EVENT_STA_AUTHMODE_CHANGE, /*!< the auth mode of AP connected by ESP32 station changed */
SYSTEM_EVENT_STA_GOT_IP, /*!< ESP32 station got IP from connected AP */
SYSTEM_EVENT_STA_LOST_IP, /*!< ESP32 station lost IP and the IP is reset to 0 */
SYSTEM_EVENT_STA_BSS_RSSI_LOW, /*!< ESP32 station connected BSS rssi goes below threshold */
SYSTEM_EVENT_STA_WPS_ER_SUCCESS, /*!< ESP32 station wps succeeds in enrollee mode */
SYSTEM_EVENT_STA_WPS_ER_FAILED, /*!< ESP32 station wps fails in enrollee mode */
SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, /*!< ESP32 station wps timeout in enrollee mode */

View File

@ -1153,6 +1153,20 @@ esp_err_t esp_wifi_get_inactive_time(wifi_interface_t ifx, uint16_t *sec);
*/
esp_err_t esp_wifi_statis_dump(uint32_t modules);
/**
* @brief Set RSSI threshold below which APP will get an event
*
* @attention This API needs to be called every time after WIFI_EVENT_STA_BSS_RSSI_LOW event is received.
*
* @param rssi threshold value in dbm between -100 to 0
*
* @return
* - ESP_OK: succeed
* - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init
* - ESP_ERR_WIFI_ARG: invalid argument
*/
esp_err_t esp_wifi_set_rssi_threshold(int32_t rssi);
#ifdef __cplusplus
}
#endif

View File

@ -95,6 +95,7 @@ typedef enum {
WIFI_REASON_HANDSHAKE_TIMEOUT = 204,
WIFI_REASON_CONNECTION_FAIL = 205,
WIFI_REASON_AP_TSF_RESET = 206,
WIFI_REASON_ROAMING = 207,
} wifi_err_reason_t;
typedef enum {
@ -235,6 +236,9 @@ typedef struct {
wifi_sort_method_t sort_method; /**< sort the connect AP in the list by rssi or security mode */
wifi_scan_threshold_t threshold; /**< When sort_method is set, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. */
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 */
} wifi_sta_config_t;
/** @brief Configuration data for ESP32 AP or STA.
@ -541,6 +545,9 @@ typedef enum {
WIFI_EVENT_FTM_REPORT, /**< Receive report of FTM procedure */
/* Add next events after this only */
WIFI_EVENT_STA_BSS_RSSI_LOW, /**< AP's RSSI crossed configured threshold */
WIFI_EVENT_MAX, /**< Invalid WiFi event ID */
} wifi_event_t;
@ -622,6 +629,11 @@ typedef struct {
uint8_t mac[6]; /**< MAC address of the station which send probe request */
} wifi_event_ap_probe_req_rx_t;
/** Argument structure for WIFI_EVENT_STA_BSS_RSSI_LOW event */
typedef struct {
int32_t rssi; /**< RSSI value of bss */
} wifi_event_bss_rssi_low_t;
#define WIFI_STATIS_BUFFER (1<<0)
#define WIFI_STATIS_RXTX (1<<1)
#define WIFI_STATIS_HW (1<<2)

@ -1 +1 @@
Subproject commit 57db56bfeae0423a446f4d72f166fe044ee8c3ab
Subproject commit 34f1262375795c34dca8520ffd042f0e104843b7

View File

@ -6,6 +6,7 @@ set(srcs "port/os_xtensa.c"
"src/common/sae.c"
"src/common/wpa_common.c"
"src/common/dpp.c"
"src/utils/bitfield.c"
"src/crypto/aes-ctr.c"
"src/crypto/aes-siv.c"
"src/crypto/sha256-kdf.c"
@ -106,7 +107,21 @@ else()
)
endif()
idf_component_register(SRCS "${srcs}" "${tls_src}"
if(CONFIG_WPA_11KV_SUPPORT)
set(roaming_src
"src/common/rrm.c"
"src/common/wnm_sta.c"
"src/common/bss.c"
"src/common/scan.c"
"src/common/ieee802_11_common.c"
"src/esp_supplicant/esp_common.c"
"src/esp_supplicant/esp_scan.c"
)
else()
set(roaming_src "")
endif()
idf_component_register(SRCS "${srcs}" "${tls_src}" "${roaming_src}"
INCLUDE_DIRS include port/include include/esp_supplicant
PRIV_INCLUDE_DIRS src
PRIV_REQUIRES mbedtls esp_timer)
@ -132,6 +147,7 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE
CONFIG_WPA3_SAE
CONFIG_SHA256
CONFIG_DPP
CONFIG_WNM
)
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 3)

View File

@ -42,4 +42,28 @@ menu "Supplicant"
button bit without setting virtual/phycial display/button bit which
will cause M2 validation fail, bypassing WPS-Config method validation.
menuconfig WPA_11KV_SUPPORT
bool "Enable 802.11k, 802.11v APIs handling in supplicant"
default n
help
Select this option to enable 802.11k 802.11v APIs(RRM and BTM support).
Only APIs which are helpful for network assisted roaming
are supported for now.
Enable this option with BTM and RRM enabled in sta config
to make device ready for network assisted roaming.
BTM: BSS transition management enables an AP to request a station to transition
to a specific AP, or to indicate to a station a set of preferred APs.
RRM: Radio measurements enable STAs to understand the radio environment,
it enables STAs to observe and gather data on radio link performance
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
endmenu

View File

@ -2,10 +2,10 @@
COMPONENT_PRIV_INCLUDEDIRS := src
COMPONENT_SRCDIRS := port src/ap src/common src/crypto src/eap_peer src/rsn_supp src/tls src/utils src/esp_supplicant src/wps
COMPONENT_ADD_INCLUDEDIRS := include port/include include/esp_supplicant
COMPONENT_ADD_INCLUDEDIRS := include port/include include/esp_supplicant src/utils
ifeq ($(CONFIG_WPA_MBEDTLS_CRYPTO), y)
COMPONENT_OBJEXCLUDE := src/tls/asn1.o \
COMPONENT_OBJEXCLUDE += src/tls/asn1.o \
src/tls/bignum.o \
src/tls/pkcs1.o \
src/tls/pkcs5.o \
@ -25,5 +25,14 @@ ifeq ($(CONFIG_WPA_MBEDTLS_CRYPTO), y)
else
COMPONENT_OBJEXCLUDE := src/crypto/tls_mbedtls.o
endif
ifneq ($(CONFIG_WPA_11KV_SUPPORT), y)
COMPONENT_OBJEXCLUDE += src/common/rrm.o \
src/common/wnm_sta.o \
src/common/bss.o \
src/common/scan.o \
src/common/ieee802_11_common.o \
src/esp_supplicant/esp_common.o \
src/esp_supplicant/esp_scan.o
endif
CFLAGS += -DCONFIG_DPP -DCONFIG_WPA3_SAE -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 -D__ets__ -Wno-strict-aliasing
CFLAGS += -DCONFIG_DPP -DCONFIG_WPA3_SAE -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

View File

@ -0,0 +1,52 @@
/**
* Copyright 2020 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.
*/
#ifndef _ESP_RRM_H
#define _ESP_RRM_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Callback function type to get neighbor report
*
* @param ctx: neighbor report context
* @param report: neighbor report
* @param report_len: neighbor report length
*
* @return
* - void
*/
typedef void (*neighbor_rep_request_cb)(void *ctx, const uint8_t *report, size_t report_len);
/**
* @brief Send Radio measurement neighbor report request to connected AP
*
* @param cb: callback function for neighbor report
* @param cb_ctx: callback context
*
* @return
* - 0: success else failure
*/
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,56 @@
/**
* Copyright 2020 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.
*/
#ifndef _ESP_WNM_H
#define _ESP_WNM_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* enum btm_query_reason: Reason code for sending btm query
*/
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,
};
/**
* @brief Send bss transition query to connected AP
*
* @param query_reason: reason for sending query
* @param btm_candidates: btm candidates list if available
* @param cand_list: whether candidate list to be included from scan results available in supplicant's cache.
*
* @return
* - 0: success else failure
*/
int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
const char *btm_candidates,
int cand_list);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -17,6 +17,7 @@
#include "wpabuf.h"
#include "esp_log.h"
#include "supplicant_opt.h"
#ifdef ESPRESSIF_USE
@ -60,6 +61,7 @@ void wpa_debug_print_timestamp(void);
* Note: New line '\n' is added to the end of the text when printing to stdout.
*/
#define wpa_printf(level,fmt, args...) ESP_LOG_LEVEL_LOCAL(level, TAG, fmt, ##args)
#define wpa_dbg(ctx, level, fmt, args...) wpa_printf(level, fmt, ##args)
void wpa_dump_mem(char* desc, uint8_t *addr, uint16_t len);
static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
@ -153,6 +155,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
#define wpa_hexdump_buf_key(...) do {} while(0)
#define wpa_hexdump_ascii(...) do {} while(0)
#define wpa_hexdump_ascii_key(...) do {} while(0)
#define wpa_dbg(...) do {} while(0)
#endif
#define wpa_auth_logger

View File

@ -34,6 +34,8 @@ struct os_time {
suseconds_t usec;
};
#define os_reltime os_time
struct os_tm {
int sec; /* 0..59 or 60 for leap seconds */
int min; /* 0..59 */
@ -49,7 +51,7 @@ struct os_tm {
* Returns: 0 on success, -1 on failure
*/
int os_get_time(struct os_time *t);
#define os_get_reltime os_get_time
/* Helper macros for handling struct os_time */
@ -57,6 +59,7 @@ int os_get_time(struct os_time *t);
((a)->sec < (b)->sec || \
((a)->sec == (b)->sec && (a)->usec < (b)->usec))
#define os_reltime_before os_time_before
#define os_time_sub(a, b, res) do { \
(res)->sec = (a)->sec - (b)->sec; \
(res)->usec = (a)->usec - (b)->usec; \
@ -65,6 +68,7 @@ int os_get_time(struct os_time *t);
(res)->usec += 1000000; \
} \
} while (0)
#define os_reltime_sub os_time_sub
/**
* os_mktime - Convert broken-down time into seconds since 1970-01-01
@ -207,6 +211,10 @@ char * os_readfile(const char *name, size_t *len);
#ifndef os_zalloc
#define os_zalloc(s) calloc(1, (s))
#endif
#ifndef os_calloc
#define os_calloc(p, s) calloc((p), (s))
#endif
#ifndef os_free
#define os_free(p) free((p))
#endif
@ -293,4 +301,11 @@ static inline int os_snprintf_error(size_t size, int res)
{
return res < 0 || (unsigned int) res >= size;
}
static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
{
if (size && nmemb > (~(size_t) 0) / size)
return NULL;
return os_realloc(ptr, nmemb * size);
}
#endif /* OS_H */

View File

@ -28,4 +28,12 @@
#define DEBUG_PRINT
#endif
#if CONFIG_WPA_11KV_SUPPORT
#define ROAMING_SUPPORT 1
#endif
#if CONFIG_WPA_SCAN_CACHE
#define SCAN_CACHE_SUPPORTED
#endif
#endif /* _SUPPLICANT_OPT_H */

View File

@ -25,6 +25,7 @@
#include "os.h"
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include "esp_system.h"
#include "utils/common.h"
@ -48,3 +49,13 @@ int os_get_random(unsigned char *buf, size_t len)
esp_fill_random(buf, len);
return 0;
}
void os_sleep(os_time_t sec, os_time_t usec)
{
if (sec) {
sleep(sec);
}
if (usec) {
usleep(usec);
}
}

View File

@ -2177,7 +2177,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
}
#ifdef CONFIG_WNM
#ifdef CONFIG_WNM_AP
/* update GTK when exiting WNM-Sleep Mode */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
{
@ -2254,7 +2254,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
return pos - start;
}
#endif /* CONFIG_IEEE80211W */
#endif /* CONFIG_WNM */
#endif /* CONFIG_WNM_AP */
static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,

View File

@ -0,0 +1,476 @@
/*
* BSS table
* Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
*
* 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 "common/ieee802_11_common.h"
#include "drivers/driver.h"
#include "eap_peer/eap.h"
#include "wpa_supplicant_i.h"
#include "config.h"
#include "scan.h"
#include "bss.h"
#ifdef ESP_SUPPLICANT
#include "esp_supplicant/esp_wifi_driver.h"
#endif
#define MAX_BSS_COUNT 20
void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
const char *reason)
{
if (wpa_s->last_scan_res) {
unsigned int i;
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
if (wpa_s->last_scan_res[i] == bss) {
os_memmove(&wpa_s->last_scan_res[i],
&wpa_s->last_scan_res[i + 1],
(wpa_s->last_scan_res_used - i - 1)
* sizeof(struct wpa_bss *));
wpa_s->last_scan_res_used--;
break;
}
}
}
dl_list_del(&bss->list);
dl_list_del(&bss->list_id);
wpa_s->num_bss--;
wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
" SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
os_free(bss);
}
/**
* wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
* @wpa_s: Pointer to wpa_supplicant data
* @bssid: BSSID
* @ssid: SSID
* @ssid_len: Length of @ssid
* Returns: Pointer to the BSS entry or %NULL if not found
*/
struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
struct wpa_bss *bss;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
bss->ssid_len == ssid_len &&
os_memcmp(bss->ssid, ssid, ssid_len) == 0)
return bss;
}
return NULL;
}
void calculate_update_time(const struct os_reltime *fetch_time,
unsigned int age_ms,
struct os_reltime *update_time)
{
os_time_t usec;
update_time->sec = fetch_time->sec;
update_time->usec = fetch_time->usec;
update_time->sec -= age_ms / 1000;
usec = (age_ms % 1000) * 1000;
if (update_time->usec < usec) {
update_time->sec--;
update_time->usec += 1000000;
}
update_time->usec -= usec;
}
static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
struct os_reltime *fetch_time)
{
dst->flags = src->flags;
os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
dst->channel = src->chan;
dst->beacon_int = src->beacon_int;
dst->caps = src->caps;
dst->noise = src->noise;
dst->level = src->level;
dst->tsf = src->tsf;
dst->parent_tsf = src->parent_tsf;
calculate_update_time(fetch_time, src->age, &dst->last_update);
}
#ifdef ESP_SUPPLICANT
static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal();
if (ssid->ssid == NULL || ssid->len == 0)
return 0;
if (ssid->len == bss->ssid_len &&
os_memcmp(ssid->ssid, bss->ssid, ssid->len) == 0)
return 1;
return 0;
}
#endif
static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
if (bss == wpa_s->current_bss)
return 1;
if (wpa_s->current_bss &&
(bss->ssid_len != wpa_s->current_bss->ssid_len ||
os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
bss->ssid_len) != 0))
return 0; /* SSID has changed */
return !is_zero_ether_addr(bss->bssid) && wpa_s->current_bss->bssid &&
(os_memcmp(bss->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0);
}
static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (!wpa_bss_known(wpa_s, bss)) {
wpa_bss_remove(wpa_s, bss, __func__);
return 0;
}
}
return -1;
}
static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss;
/*
* Remove the oldest entry that does not match with any configured
* network.
*/
if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
return 0;
/*
* Remove the oldest entry that isn't currently in use.
*/
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (!wpa_bss_in_use(wpa_s, bss)) {
wpa_bss_remove(wpa_s, bss, __func__);
return 0;
}
}
return -1;
}
static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
const u8 *ssid, size_t ssid_len,
struct wpa_scan_res *res,
struct os_reltime *fetch_time)
{
struct wpa_bss *bss;
if ((wpa_s->num_bss + 1 > MAX_BSS_COUNT) &&
(wpa_bss_remove_oldest(wpa_s) < 0)) {
wpa_printf(MSG_ERROR,
"Failed to clean older entries, rejecting scan result");
return NULL;
}
bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
if (bss == NULL)
return NULL;
bss->id = wpa_s->bss_next_id++;
bss->last_update_idx = wpa_s->bss_update_idx;
wpa_bss_copy_res(bss, res, fetch_time);
os_memcpy(bss->ssid, ssid, ssid_len);
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);
dl_list_add_tail(&wpa_s->bss, &bss->list);
dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
wpa_s->num_bss++;
wpa_dbg(wpa_s, MSG_INFO, "BSS: Add new id %u BSSID " MACSTR
" SSID '%s' chan %d",
bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
bss->channel);
return bss;
}
static struct wpa_bss *
wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
struct wpa_scan_res *res, struct os_reltime *fetch_time)
{
if (bss->last_update_idx == wpa_s->bss_update_idx) {
struct os_reltime update_time;
/*
* Some drivers (e.g., cfg80211) include multiple BSS entries
* for the same BSS if that BSS's channel changes. The BSS list
* implementation in wpa_supplicant does not do that and we need
* to filter out the obsolete results here to make sure only the
* most current BSS information remains in the table.
*/
wpa_printf(MSG_DEBUG, "BSS: " MACSTR
" has multiple entries in the scan results - select the most current one",
MAC2STR(bss->bssid));
calculate_update_time(fetch_time, res->age, &update_time);
wpa_printf(MSG_DEBUG,
"Accept this BSS entry since it looks more current than the previous update");
}
bss->last_update_idx = wpa_s->bss_update_idx;
wpa_bss_copy_res(bss, res, fetch_time);
/* Move the entry to the end of the list */
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);
bss->ie_len = res->ie_len;
bss->beacon_ie_len = res->beacon_ie_len;
} else {
struct wpa_bss *nbss;
struct dl_list *prev = bss->list_id.prev;
dl_list_del(&bss->list_id);
nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
res->beacon_ie_len);
if (nbss) {
unsigned int i;
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
if (wpa_s->last_scan_res[i] == bss) {
wpa_s->last_scan_res[i] = nbss;
break;
}
}
if (wpa_s->current_bss == bss)
wpa_s->current_bss = nbss;
bss = nbss;
os_memcpy(bss + 1, res + 1,
res->ie_len + res->beacon_ie_len);
bss->ie_len = res->ie_len;
bss->beacon_ie_len = res->beacon_ie_len;
}
dl_list_add(prev, &bss->list_id);
}
dl_list_add_tail(&wpa_s->bss, &bss->list);
return bss;
}
/**
* wpa_bss_update_start - Start a BSS table update from scan results
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is called at the start of each BSS table update round for new
* scan results. The actual scan result entries are indicated with calls to
* wpa_bss_update_scan_res() and the update round is finished with a call to
* wpa_bss_update_end().
*/
void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
{
wpa_s->bss_update_idx++;
wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
wpa_s->bss_update_idx);
wpa_s->last_scan_res_used = 0;
}
/**
* wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
* @wpa_s: Pointer to wpa_supplicant data
* @res: Scan result
* @fetch_time: Time when the result was fetched from the driver
*
* This function updates a BSS table entry (or adds one) based on a scan result.
* This is called separately for each scan result between the calls to
* wpa_bss_update_start() and wpa_bss_update_end().
*/
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res,
struct os_reltime *fetch_time)
{
const u8 *ssid;
struct wpa_bss *bss;
ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
if (ssid == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
MACSTR, MAC2STR(res->bssid));
return;
}
if (ssid[1] > SSID_MAX_LEN) {
wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
MACSTR, MAC2STR(res->bssid));
return;
}
/* TODO: add option for ignoring BSSes we are not interested in
* (to save memory) */
bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
if (bss == NULL)
bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
else {
bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
if (wpa_s->last_scan_res) {
unsigned int i;
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
if (bss == wpa_s->last_scan_res[i]) {
/* Already in the list */
return;
}
}
}
}
if (bss == NULL)
return;
if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
struct wpa_bss **n;
unsigned int siz;
if (wpa_s->last_scan_res_size == 0)
siz = 32;
else
siz = wpa_s->last_scan_res_size * 2;
n = os_realloc_array(wpa_s->last_scan_res, siz,
sizeof(struct wpa_bss *));
if (n == NULL)
return;
wpa_s->last_scan_res = n;
wpa_s->last_scan_res_size = siz;
}
if (wpa_s->last_scan_res)
wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
}
/**
* wpa_bss_update_end - End a BSS table update from scan results
* @wpa_s: Pointer to wpa_supplicant data
* @info: Information about scan parameters
* @new_scan: Whether this update round was based on a new scan
*
* This function is called at the end of each BSS table update round for new
* scan results. The start of the update was indicated with a call to
* wpa_bss_update_start().
*/
void wpa_bss_update_end(struct wpa_supplicant *wpa_s)
{
os_get_reltime(&wpa_s->last_scan);
}
/**
* wpa_bss_init - Initialize BSS table
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 on success, -1 on failure
*
* This prepares BSS table lists and timer for periodic updates. The BSS table
* is deinitialized with wpa_bss_deinit() once not needed anymore.
*/
int wpa_bss_init(struct wpa_supplicant *wpa_s)
{
dl_list_init(&wpa_s->bss);
dl_list_init(&wpa_s->bss_id);
return 0;
}
/**
* wpa_bss_flush - Flush all unused BSS entries
* @wpa_s: Pointer to wpa_supplicant data
*/
void wpa_bss_flush(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss, *n;
if (wpa_s->bss.next == NULL)
return; /* BSS table not yet initialized */
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
if (wpa_bss_in_use(wpa_s, bss))
continue;
wpa_bss_remove(wpa_s, bss, __func__);
}
}
/**
* wpa_bss_deinit - Deinitialize BSS table
* @wpa_s: Pointer to wpa_supplicant data
*/
void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
{
wpa_bss_flush(wpa_s);
}
/**
* wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
* @wpa_s: Pointer to wpa_supplicant data
* @bssid: BSSID
* Returns: Pointer to the BSS entry or %NULL if not found
*/
struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{
struct wpa_bss *bss;
dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
return bss;
}
return NULL;
}
/**
* wpa_bss_get_next_bss - Fetch a next BSS table entry from the list
* @wpa_s: Pointer to wpa_supplicant data
* @bss: BSS
* Returns: Pointer to the BSS entry or %NULL if not found
*/
struct wpa_bss * wpa_bss_get_next_bss(struct wpa_supplicant *wpa_s,
struct wpa_bss *prev_bss)
{
struct wpa_bss *bss;
if (!prev_bss)
return dl_list_first(&wpa_s->bss, struct wpa_bss, list);
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, prev_bss->bssid, ETH_ALEN) == 0)
return dl_list_entry(bss->list.next, struct wpa_bss, list);
}
return NULL;
}
/**
* wpa_bss_get_ie - Fetch a specified information element from a BSS entry
* @bss: BSS table entry
* @ie: Information element identitifier (WLAN_EID_*)
* 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_ie(const struct wpa_bss *bss, u8 ie)
{
return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
}
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),
capab);
}

View File

@ -0,0 +1,86 @@
/*
* BSS table
* Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef BSS_H
#define BSS_H
struct wpa_scan_res;
/**
* struct wpa_bss - BSS table
*
* This structure is used to store information about neighboring BSSes in
* generic format. It is mainly updated based on scan results from the driver.
*/
struct wpa_bss {
/** List entry for struct wpa_supplicant::bss */
struct dl_list list;
/** List entry for struct wpa_supplicant::bss_id */
struct dl_list list_id;
/** Unique identifier for this BSS entry */
unsigned int id;
/** Index of the last scan update */
unsigned int last_update_idx;
/** Information flags about the BSS/IBSS (WPA_BSS_*) */
unsigned int flags;
/** BSSID */
u8 bssid[ETH_ALEN];
/** SSID */
u8 ssid[SSID_MAX_LEN];
/** Length of SSID */
size_t ssid_len;
/** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
int channel;
/** Beacon interval in TUs (host byte order) */
u16 beacon_int;
/** Capability information field in host byte order */
u16 caps;
/** Signal quality */
int qual;
/** Noise level */
int noise;
/** Signal level */
int level;
/** Timestamp of last Beacon/Probe Response frame */
u64 tsf;
/** Timestamp of parent aganist which it was taken */
u64 parent_tsf;
/** Time of the last update (i.e., Beacon or Probe Response RX) */
struct os_reltime last_update;
/** Length of the following IE field in octets (from Probe Response) */
size_t ie_len;
/** Length of the following Beacon IE field in octets */
size_t beacon_ie_len;
/* followed by ie_len octets of IEs */
/* followed by beacon_ie_len octets of 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,
struct os_reltime *fetch_time);
void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
const char *reason);
void wpa_bss_update_end(struct wpa_supplicant *wpa_s);
int wpa_bss_init(struct wpa_supplicant *wpa_s);
void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
void wpa_bss_flush(struct wpa_supplicant *wpa_s);
struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ssid, size_t ssid_len);
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);
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);
void calculate_update_time(const struct os_reltime *fetch_time,
unsigned int age_ms,
struct os_reltime *update_time);
#endif /* BSS_H */

View File

@ -0,0 +1,198 @@
/*
* IEEE 802.11 Common routines
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* 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 "defs.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
#include "common/wpa_supplicant_i.h"
/**
* get_ie - Fetch a specified information element from IEs buffer
* @ies: Information elements buffer
* @len: Information elements buffer length
* @eid: Information element identifier (WLAN_EID_*)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the IEs
* buffer or %NULL in case the element is not found.
*/
const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
{
const struct element *elem;
if (!ies)
return NULL;
for_each_element_id(elem, eid, ies, len)
return &elem->id;
return NULL;
}
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len)
{
u8 *nei_pos = nei_rep;
const char *end;
/*
* BSS Transition Candidate List Entries - Neighbor Report elements
* neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
* <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
*/
while (pos) {
u8 *nei_start;
long int val;
char *endptr, *tmp;
pos = os_strstr(pos, " neighbor=");
if (!pos)
break;
if (nei_pos + 15 > nei_rep + nei_rep_len) {
wpa_printf(MSG_DEBUG,
"Not enough room for additional neighbor");
return -1;
}
pos += 10;
nei_start = nei_pos;
*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
nei_pos++; /* length to be filled in */
if (hwaddr_aton2(pos, nei_pos) < 0) {
wpa_printf(MSG_DEBUG, "Invalid BSSID");
return -1;
}
nei_pos += ETH_ALEN;
pos += 17;
if (*pos != ',') {
wpa_printf(MSG_DEBUG, "Missing BSSID Information");
return -1;
}
pos++;
val = strtol(pos, &endptr, 0);
WPA_PUT_LE32(nei_pos, val);
nei_pos += 4;
if (*endptr != ',') {
wpa_printf(MSG_DEBUG, "Missing Operating Class");
return -1;
}
pos = endptr + 1;
*nei_pos++ = atoi(pos); /* Operating Class */
pos = os_strchr(pos, ',');
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "Missing Channel Number");
return -1;
}
pos++;
*nei_pos++ = atoi(pos); /* Channel Number */
pos = os_strchr(pos, ',');
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "Missing PHY Type");
return -1;
}
pos++;
*nei_pos++ = atoi(pos); /* PHY Type */
end = os_strchr(pos, ' ');
tmp = os_strchr(pos, ',');
if (tmp && (!end || tmp < end)) {
/* Optional Subelements (hexdump) */
size_t len;
pos = tmp + 1;
end = os_strchr(pos, ' ');
if (end)
len = end - pos;
else
len = os_strlen(pos);
if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
wpa_printf(MSG_DEBUG,
"Not enough room for neighbor subelements");
return -1;
}
if (len & 0x01 ||
hexstr2bin(pos, nei_pos, len / 2) < 0) {
wpa_printf(MSG_DEBUG,
"Invalid neighbor subelement info");
return -1;
}
nei_pos += len / 2;
pos = end;
}
nei_start[1] = nei_pos - nei_start - 2;
}
return nei_pos - nei_rep;
}
/**
* ieee802_11_parse_elems - Parse information elements in management frames
* @start: Pointer to the start of IEs
* @len: Length of IE buffer in octets
* @elems: Data structure for parsed elements
* @show_errors: Whether to show parsing errors in debug log
* Returns: Parsing result
*/
int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t len)
{
const struct element *elem;
if (!start)
return 0;
for_each_element(elem, start, len) {
u8 id = elem->id;
const u8 *pos = elem->data;
switch (id) {
case WLAN_EID_RRM_ENABLED_CAPABILITIES:
os_memcpy(wpa_s->rrm_ie, pos, 5);
wpa_s->rrm.rrm_used = true;
break;
case WLAN_EID_EXT_CAPAB:
/* extended caps can go beyond 8 octacts but we aren't using them now */
os_memcpy(wpa_s->extend_caps, pos, 5);
break;
default:
break;
}
}
return 0;
}
int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
{
if (!ie || ie[1] <= capab / 8)
return 0;
return !!(ie[2 + capab / 8] & BIT(capab % 8));
}
u8 get_operating_class(u8 chan, int sec_channel)
{
u8 op_class;
if (chan < 1 || chan > 14)
return 0;
if (sec_channel == 1)
op_class = 83;
else if (sec_channel == -1)
op_class = 84;
else
op_class = 81;
return op_class;
}

View File

@ -0,0 +1,42 @@
/*
* IEEE 802.11 Common routines
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_COMMON_H
#define IEEE802_11_COMMON_H
#include "defs.h"
#include "ieee802_11_defs.h"
struct element {
u8 id;
u8 datalen;
u8 data[];
} STRUCT_PACKED;
/* element iteration helpers */
#define for_each_element(_elem, _data, _datalen) \
for (_elem = (const struct element *) (_data); \
(const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
(int) sizeof(*_elem) && \
(const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
(int) sizeof(*_elem) + _elem->datalen; \
_elem = (const struct element *) (_elem->data + _elem->datalen))
#define for_each_element_id(element, _id, data, datalen) \
for_each_element(element, data, datalen) \
if (element->id == (_id))
struct wpa_supplicant;
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len);
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);
u8 get_operating_class(u8 chan, int sec_channel);
#endif /* IEEE802_11_COMMON_H */

View File

@ -1,21 +1,17 @@
/*
* IEEE 802.11 Frame type definitions
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_DEFS_H
#define IEEE802_11_DEFS_H
#include <utils/common.h>
/* IEEE 802.11 defines */
#define WLAN_FC_PVER 0x0003
@ -89,10 +85,15 @@
#define WLAN_CAPABILITY_PBCC BIT(6)
#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
#define WLAN_CAPABILITY_QOS BIT(9)
#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
#define WLAN_CAPABILITY_APSD BIT(11)
#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12)
#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
@ -104,26 +105,23 @@
#define WLAN_STATUS_AUTH_TIMEOUT 16
#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
#define WLAN_STATUS_ASSOC_DENIED_RATES 18
/* IEEE 802.11b */
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
/* IEEE 802.11h */
#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
/* IEEE 802.11g */
#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26
#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27
#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
#define WLAN_STATUS_R0KH_UNREACHABLE 28
/* IEEE 802.11w */
#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33
#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34
#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35
#define WLAN_STATUS_REQUEST_DECLINED 37
#define WLAN_STATUS_INVALID_PARAMETERS 38
/* IEEE 802.11i */
#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39
#define WLAN_STATUS_INVALID_IE 40
#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
@ -136,7 +134,6 @@
#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
/* IEEE 802.11r */
#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
#define WLAN_STATUS_INVALID_PMKID 53
#define WLAN_STATUS_INVALID_MDIE 54
@ -161,7 +158,7 @@
#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
#define WLAN_REASON_DEAUTH_LEAVING 3
@ -171,10 +168,9 @@
#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
/* IEEE 802.11h */
#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
/* IEEE 802.11i */
#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12
#define WLAN_REASON_INVALID_IE 13
#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
@ -188,18 +184,15 @@
#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
/* Information Element IDs */
/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */
#define WLAN_EID_SSID 0
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_FH_PARAMS 2
#define WLAN_EID_DS_PARAMS 3
#define WLAN_EID_CF_PARAMS 4
#define WLAN_EID_TIM 5
#define WLAN_EID_IBSS_PARAMS 6
#define WLAN_EID_COUNTRY 7
#define WLAN_EID_CHALLENGE 16
/* EIDs defined by IEEE 802.11h - START */
#define WLAN_EID_PWR_CONSTRAINT 32
#define WLAN_EID_PWR_CAPABILITY 33
#define WLAN_EID_TPC_REQUEST 34
@ -208,23 +201,25 @@
#define WLAN_EID_CHANNEL_SWITCH 37
#define WLAN_EID_MEASURE_REQUEST 38
#define WLAN_EID_MEASURE_REPORT 39
#define WLAN_EID_QUITE 40
#define WLAN_EID_QUIET 40
#define WLAN_EID_IBSS_DFS 41
/* EIDs defined by IEEE 802.11h - END */
#define WLAN_EID_ERP_INFO 42
#define WLAN_EID_HT_CAP 45
#define WLAN_EID_RSN 48
#define WLAN_EID_EXT_SUPP_RATES 50
#define WLAN_EID_NEIGHBOR_REPORT 52
#define WLAN_EID_MOBILITY_DOMAIN 54
#define WLAN_EID_FAST_BSS_TRANSITION 55
#define WLAN_EID_TIMEOUT_INTERVAL 56
#define WLAN_EID_RIC_DATA 57
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
#define WLAN_EID_20_40_BSS_COEXISTENCE 72
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
#define WLAN_EID_MMIE 76
#define WLAN_EID_EXT_CAPAB 127
#define WLAN_EID_VENDOR_SPECIFIC 221
#define WLAN_EID_CAG_NUMBER 237
#define WLAN_EID_AP_CSN 239
@ -241,7 +236,7 @@
#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5
#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6
#define WLAN_EID_EXT_KEY_DELIVERY 7
#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8
#define WLAN_EID_EXT_WRAPPED_DATA 8
#define WLAN_EID_EXT_FTM_SYNC_INFO 9
#define WLAN_EID_EXT_EXTENDED_REQUEST 10
#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11
@ -253,8 +248,7 @@
#define WLAN_EID_EXT_HE_CAPABILITIES 35
#define WLAN_EID_EXT_HE_OPERATION 36
/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
#define WLAN_ACTION_QOS 1
#define WLAN_ACTION_DLS 2
@ -264,6 +258,8 @@
#define WLAN_ACTION_FT 6
#define WLAN_ACTION_HT 7
#define WLAN_ACTION_SA_QUERY 8
#define WLAN_ACTION_WNM 10
#define WLAN_ACTION_UNPROTECTED_WNM 11
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
@ -300,6 +296,51 @@ struct ieee80211_hdr {
#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
/* Radio Measurement Action codes */
#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
/* Radio Measurement capabilities (from RM Enabled Capabilities element)
* IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */
/* byte 1 (out of 5) */
#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4)
#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5)
#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6)
/*
* IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for
* measurement requests
*/
enum measure_type {
MEASURE_TYPE_RPI_HIST = 2,
MEASURE_TYPE_BEACON = 5,
MEASURE_TYPE_LCI = 8,
MEASURE_TYPE_LOCATION_CIVIC = 11,
MEASURE_TYPE_MEASURE_PAUSE = 255,
};
/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */
enum location_subject {
LOCATION_SUBJECT_LOCAL = 0,
LOCATION_SUBJECT_REMOTE = 1,
LOCATION_SUBJECT_3RD_PARTY = 2,
};
/*
* IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request
*/
enum lci_req_subelem {
LCI_REQ_SUBELEM_AZIMUTH_REQ = 1,
LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2,
LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3,
LCI_REQ_SUBELEM_MAX_AGE = 4,
};
struct ieee80211_mgmt {
le16 frame_control;
@ -406,6 +447,7 @@ struct ieee80211_mgmt {
} STRUCT_PACKED;
#define IEEE80211_MAX_MMPDU_SIZE 2304
struct ieee80211_ht_capabilities {
le16 ht_capabilities_info;
u8 a_mpdu_params;
@ -416,12 +458,14 @@ struct ieee80211_ht_capabilities {
} STRUCT_PACKED;
/* HT Operation element */
struct ieee80211_ht_operation {
u8 control_chan;
u8 ht_param;
le16 operation_mode;
le16 stbc_param;
u8 basic_set[16];
u8 primary_chan;
/* Five octets of HT Operation Information */
u8 ht_param; /* B0..B7 */
le16 operation_mode; /* B8..B23 */
le16 param; /* B24..B39 */
u8 basic_mcs_set[16];
} STRUCT_PACKED;
#ifdef _MSC_VER
@ -432,7 +476,9 @@ struct ieee80211_ht_operation {
#define ERP_INFO_USE_PROTECTION BIT(1)
#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
/* HT Capabilities Info field within HT Capabilities element */
#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0))
#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1))
#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3)))
@ -450,79 +496,86 @@ struct ieee80211_ht_operation {
#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10))
#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11))
#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12))
#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13))
/* B13 - Reserved (was PSMP support during P802.11n development) */
#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14))
#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15))
/* HT Extended Capabilities field within HT Capabilities element */
#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0))
#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK ((u16) (BIT(1) | BIT(2)))
#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1
/* B3..B7 - Reserved */
#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK ((u16) (BIT(8) | BIT(9)))
#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8
#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10))
#define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10))
#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11))
/* B12..B15 - Reserved */
/* Transmit Beanforming Capabilities within HT Capabilities element */
#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0))
#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3))
#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4))
#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5))
#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7))
#define TX_BF_CAP_CALIB_OFFSET 6
#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9))
#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10))
#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11)))
#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17
#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28)))
#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27
/* B29..B31 - Reserved */
#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3))
#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4))
#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5))
#define TX_BEAMFORM_CAP_CALIB_OFFSET 6
#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9))
#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10))
#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17
#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0))
#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5))
#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6))
/* ASEL Capability field within HT Capabilities element */
#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0))
#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5))
#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6))
/* B7 - Reserved */
/* First octet of HT Operation Information within HT Operation element */
#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1))
#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0))
#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1))
#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2))
#define HT_INFO_HT_PARAM_STA_CHNL_WIDTH ((u8) BIT(2))
#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3))
#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4))
#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5))
/* B4..B7 - Reserved */
/* HT Protection (B8..B9 of HT Operation Information) */
#define HT_PROT_NO_PROTECTION 0
#define HT_PROT_NONMEMBER_PROTECTION 1
#define HT_PROT_20MHZ_PROTECTION 2
#define HT_PROT_NON_HT_MIXED 3
/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in
* HT Operation Information) */
#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */
#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */
/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */
#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT ((u16) BIT(4)) /* B12 */
/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */
#define OP_MODE_PURE 0
#define OP_MODE_MAY_BE_LEGACY_STAS 1
#define OP_MODE_20MHZ_HT_STA_ASSOCED 2
#define OP_MODE_MIXED 3
#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \
((le16) (0x0001 | 0x0002))
#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0
#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2))
#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3))
#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4))
#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6))
#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7))
#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8))
#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9))
#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10))
#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11))
#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
* 00:50:F2 */
#define WPA_IE_VENDOR_TYPE 0x0050f201
#define WPS_IE_VENDOR_TYPE 0x0050f204
/* Last two octets of HT Operation Information (BIT(0) = B24) */
/* B24..B29 - Reserved */
#define HT_OPER_PARAM_DUAL_BEACON ((u16) BIT(6))
#define HT_OPER_PARAM_DUAL_CTS_PROTECTION ((u16) BIT(7))
#define HT_OPER_PARAM_STBC_BEACON ((u16) BIT(8))
#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP ((u16) BIT(9))
#define HT_OPER_PARAM_PCO_ACTIVE ((u16) BIT(10))
#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
/* B36..B39 - Reserved */
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@ -589,7 +642,7 @@ struct wmm_parameter_element {
u8 oui_type; /* 2 */
u8 oui_subtype; /* 1 */
u8 version; /* 1 for WMM version 1.0 */
u8 qos_info; /* AP/STA specif QoS info */
u8 qos_info; /* AP/STA specific QoS info */
u8 reserved; /* 0 */
struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
@ -624,29 +677,260 @@ struct wmm_tspec_element {
/* Access Categories / ACI to AC coding */
enum {
enum wmm_ac {
WMM_AC_BE = 0 /* Best Effort */,
WMM_AC_BK = 1 /* Background */,
WMM_AC_VI = 2 /* Video */,
WMM_AC_VO = 3 /* Voice */
WMM_AC_VO = 3 /* Voice */,
WMM_AC_NUM = 4
};
/* 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 {
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0,
MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1,
MBO_TRANSITION_REJECT_REASON_DELAY = 2,
MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3,
MBO_TRANSITION_REJECT_REASON_RSSI = 4,
MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5,
MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
};
#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
/* IEEE 802.11v - WNM Action field values */
enum wnm_action {
WNM_EVENT_REQ = 0,
WNM_EVENT_REPORT = 1,
WNM_DIAGNOSTIC_REQ = 2,
WNM_DIAGNOSTIC_REPORT = 3,
WNM_LOCATION_CFG_REQ = 4,
WNM_LOCATION_CFG_RESP = 5,
WNM_BSS_TRANS_MGMT_QUERY = 6,
WNM_BSS_TRANS_MGMT_REQ = 7,
WNM_BSS_TRANS_MGMT_RESP = 8,
WNM_FMS_REQ = 9,
WNM_FMS_RESP = 10,
WNM_COLLOCATED_INTERFERENCE_REQ = 11,
WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
WNM_TFS_REQ = 13,
WNM_TFS_RESP = 14,
WNM_TFS_NOTIFY = 15,
WNM_SLEEP_MODE_REQ = 16,
WNM_SLEEP_MODE_RESP = 17,
WNM_TIM_BROADCAST_REQ = 18,
WNM_TIM_BROADCAST_RESP = 19,
WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
WNM_CHANNEL_USAGE_REQ = 21,
WNM_CHANNEL_USAGE_RESP = 22,
WNM_DMS_REQ = 23,
WNM_DMS_RESP = 24,
WNM_TIMING_MEASUREMENT_REQ = 25,
WNM_NOTIFICATION_REQ = 26,
WNM_NOTIFICATION_RESP = 27
};
#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
/* cipher suite selectors */
#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01
#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02
/* reserved: 0x000FAC03 */
#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
/* IEEE Std 802.11-2012 - Table 8-253 */
enum bss_trans_mgmt_status_code {
WNM_BSS_TM_ACCEPT = 0,
WNM_BSS_TM_REJECT_UNSPECIFIED = 1,
WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2,
WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3,
WNM_BSS_TM_REJECT_UNDESIRED = 4,
WNM_BSS_TM_REJECT_DELAY_REQUEST = 5,
WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6,
WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7,
WNM_BSS_TM_REJECT_LEAVING_ESS = 8
};
/* AKM suite selectors */
#define WLAN_AKM_SUITE_8021X 0x000FAC01
#define WLAN_AKM_SUITE_PSK 0x000FAC02
/*
* IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for
* neighbor report
*/
#define WNM_NEIGHBOR_TSF 1
#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3
#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4
#define WNM_NEIGHBOR_BEARING 5
#define WNM_NEIGHBOR_MEASUREMENT_REPORT 39
#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66
#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
struct tpc_report {
u8 eid;
u8 len;
u8 tx_power;
u8 link_margin;
} STRUCT_PACKED;
#define RRM_CAPABILITIES_IE_LEN 5
/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
struct rrm_link_measurement_request {
u8 dialog_token;
s8 tx_power;
s8 max_tp;
u8 variable[0];
} STRUCT_PACKED;
/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
struct rrm_link_measurement_report {
u8 dialog_token;
struct tpc_report tpc;
u8 rx_ant_id;
u8 tx_ant_id;
u8 rcpi;
u8 rsni;
u8 variable[0];
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */
struct rrm_measurement_request_element {
u8 eid; /* Element ID */
u8 len; /* Length */
u8 token; /* Measurement Token */
u8 mode; /* Measurement Request Mode */
u8 type; /* Measurement Type */
u8 variable[0]; /* Measurement Request */
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */
#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0)
#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1)
#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2)
#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3)
#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4)
/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */
struct rrm_measurement_beacon_request {
u8 oper_class; /* Operating Class */
u8 channel; /* Channel Number */
le16 rand_interval; /* Randomization Interval (in TUs) */
le16 duration; /* Measurement Duration (in TUs) */
u8 mode; /* Measurement Mode */
u8 bssid[ETH_ALEN]; /* BSSID */
u8 variable[0]; /* Optional Subelements */
} STRUCT_PACKED;
/*
* IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon
* request
*/
enum beacon_report_mode {
BEACON_REPORT_MODE_PASSIVE = 0,
BEACON_REPORT_MODE_ACTIVE = 1,
BEACON_REPORT_MODE_TABLE = 2,
};
/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for
* Beacon request */
#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0
#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */
#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */
#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10
#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */
#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221
/*
* IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values
*/
enum beacon_report_detail {
/* No fixed-length fields or elements */
BEACON_REPORT_DETAIL_NONE = 0,
/* All fixed-length fields and any requested elements in the Request
* element if present */
BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1,
/* All fixed-length fields and elements (default, used when Reporting
* Detail subelement is not included in a Beacon request) */
BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2,
};
/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */
struct rrm_measurement_report_element {
u8 eid; /* Element ID */
u8 len; /* Length */
u8 token; /* Measurement Token */
u8 mode; /* Measurement Report Mode */
u8 type; /* Measurement Type */
u8 variable[0]; /* Measurement Report */
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */
#define MEASUREMENT_REPORT_MODE_ACCEPT 0
#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0)
#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1)
#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2)
/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */
struct rrm_measurement_beacon_report {
u8 op_class; /* Operating Class */
u8 channel; /* Channel Number */
le64 start_time; /* Actual Measurement Start Time
* (in TSF of the BSS requesting the measurement) */
le16 duration; /* in TUs */
u8 report_info; /* Reported Frame Information */
u8 rcpi; /* RCPI */
u8 rsni; /* RSNI */
u8 bssid[ETH_ALEN]; /* BSSID */
u8 antenna_id; /* Antenna ID */
le32 parent_tsf; /* Parent TSF */
u8 variable[0]; /* Optional Subelements */
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for
* Beacon report */
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2
#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164
/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the
* Reported Frame Body Fragment ID subelement */
#define REPORTED_FRAME_BODY_SUBELEM_LEN 4
#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7)
/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */
#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3
/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */
enum phy_type {
PHY_TYPE_UNSPECIFIED = 0,
PHY_TYPE_FHSS = 1,
PHY_TYPE_DSSS = 2,
PHY_TYPE_IRBASEBAND = 3,
PHY_TYPE_OFDM = 4,
PHY_TYPE_HRDSSS = 5,
PHY_TYPE_ERP = 6,
PHY_TYPE_HT = 7,
PHY_TYPE_DMG = 8,
PHY_TYPE_VHT = 9,
};
/* IEEE P802.11-REVmc/D5.0, 9.4.2.37 - Neighbor Report element */
/* BSSID Information Field */
#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0)
#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1)
#define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1))
#define NEI_REP_BSSID_INFO_SECURITY BIT(2)
#define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3)
#define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4)
#define NEI_REP_BSSID_INFO_QOS BIT(5)
#define NEI_REP_BSSID_INFO_APSD BIT(6)
#define NEI_REP_BSSID_INFO_RM BIT(7)
#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8)
#define NEI_REP_BSSID_INFO_IMM_BA BIT(9)
#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10)
#define NEI_REP_BSSID_INFO_HT BIT(11)
#define NEI_REP_BSSID_INFO_VHT BIT(12)
#define NEI_REP_BSSID_INFO_FTM BIT(13)
#endif /* IEEE802_11_DEFS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
#ifndef RRM_H
#define RRM_H
#include "common/defs.h"
#include "utils/list.h"
#include "esp_wifi_types.h"
#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
const u8 *report, size_t report_len);
int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
const struct wpa_ssid_value *ssid,
int lci, int civic,
void (*cb)(void *ctx,
const u8 *neighbor_rep, size_t len),
void *cb_ctx);
void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *dst,
const u8 *frame, size_t len);
void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
const u8 *src,
const u8 *frame, size_t len,
int rssi);
int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
u64 scan_start_tsf);
void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s);
#endif

View File

@ -0,0 +1,95 @@
/*
* WPA Supplicant - Scanning
* Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* 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 "config.h"
#include "wpa_supplicant_i.h"
#include "drivers/driver.h"
#include "common/ieee802_11_common.h"
#include "bss.h"
#include "scan.h"
/**
* wpa_supplicant_req_scan - Schedule a scan for neighboring access points
* @wpa_s: Pointer to wpa_supplicant data
* @sec: Number of seconds after which to scan
* @usec: Number of microseconds after which to scan
*
* This function is used to schedule a scan for neighboring access points after
* the specified time.
*/
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
{
int ret;
struct wpa_driver_scan_params *params;
os_sleep(sec, usec);
if (wpa_s->scanning) {
wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Return");
return;
}
params = os_zalloc(sizeof(*params));
if (wpa_s->wnm_mode) {
/* Use the same memory */
params->ssids[0].ssid = wpa_s->current_bss->ssid;
params->ssids[0].ssid_len = wpa_s->current_bss->ssid_len;
params->num_ssids = 1;
}
if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
/* Use the same memory */
params->bssid = wpa_s->next_scan_bssid;
}
if (wpa_s->next_scan_chan)
params->channel = wpa_s->next_scan_chan;
wpa_s->scan_reason = REASON_WNM_BSS_TRANS_REQ;
ret = wpa_supplicant_trigger_scan(wpa_s, params);
os_free(params);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Failed to issue scan - Return");
return;
}
}
/**
* wpa_scan_get_ie - Fetch a specified information element from a scan result
* @res: Scan result entry
* @ie: Information element identitifier (WLAN_EID_*)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the scan
* result.
*/
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
{
size_t ie_len = res->ie_len;
/* Use the Beacon frame IEs if res->ie_len is not available */
if (!ie_len)
ie_len = res->beacon_ie_len;
return get_ie((const u8 *) (res + 1), ie_len, ie);
}
void wpa_scan_free_params(struct wpa_driver_scan_params *params)
{
if (params == NULL)
return;
os_free((u8 *) params->bssid);
os_free(params);
}

View File

@ -0,0 +1,25 @@
/*
* WPA Supplicant - Scanning
* Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef SCAN_H
#define SCAN_H
/*
* Noise floor values to use when we have signal strength
* measurements, but no noise floor measurements. These values were
* measured in an office environment with many APs.
*/
#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
struct wpa_driver_scan_params;
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params);
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
void wpa_scan_free_params(struct wpa_driver_scan_params *params);
#endif /* SCAN_H */

View File

@ -0,0 +1,966 @@
/*
* wpa_supplicant - WNM
* Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* 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 "common/ieee802_11_common.h"
#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "drivers/driver.h"
#include "scan.h"
#include "bss.h"
#include "wnm_sta.h"
#define MAX_TFS_IE_LEN 1024
#define WNM_MAX_NEIGHBOR_REPORT 10
#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
{
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);
}
wpa_s->wnm_num_neighbor_report = 0;
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
}
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;
}
rep->tsf_offset = WPA_GET_LE16(pos);
rep->beacon_int = WPA_GET_LE16(pos + 2);
rep->tsf_present = 1;
break;
case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
if (elen < 2) {
wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
"country string");
break;
}
os_memcpy(rep->country, pos, 2);
rep->country_present = 1;
break;
case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
if (elen < 1) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
"candidate");
break;
}
rep->preference = pos[0];
rep->preference_present = 1;
break;
case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
if (elen < 10) {
wpa_printf(MSG_DEBUG,
"WNM: Too short BSS termination duration");
break;
}
rep->bss_term_tsf = WPA_GET_LE64(pos);
rep->bss_term_dur = WPA_GET_LE16(pos + 8);
rep->bss_term_present = 1;
break;
case WNM_NEIGHBOR_BEARING:
if (elen < 8) {
wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
"bearing");
break;
}
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;
break;
case WNM_NEIGHBOR_MEASUREMENT_PILOT:
if (elen < 1) {
wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
"pilot");
break;
}
os_free(rep->meas_pilot);
rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
if (rep->meas_pilot == NULL)
break;
rep->meas_pilot->measurement_pilot = pos[0];
rep->meas_pilot->subelem_len = elen - 1;
os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
break;
case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
if (elen < 5) {
wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
"capabilities");
break;
}
os_memcpy(rep->rm_capab, pos, 5);
rep->rm_capab_present = 1;
break;
case WNM_NEIGHBOR_MULTIPLE_BSSID:
if (elen < 1) {
wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
break;
}
os_free(rep->mul_bssid);
rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
if (rep->mul_bssid == NULL)
break;
rep->mul_bssid->max_bssid_indicator = pos[0];
rep->mul_bssid->subelem_len = elen - 1;
os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
break;
}
}
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);
rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
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++;
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;
}
wnm_parse_neighbor_report_elem(rep, id, elen, pos);
left -= elen;
pos += elen;
}
}
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;
}
static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
{
unsigned int i;
struct neighbor_report *nei;
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
nei = &wpa_s->wnm_neighbor_report_elements[i];
if (nei->acceptable)
return wpa_bss_get_bssid(wpa_s, nei->bssid);
}
return NULL;
}
/* basic function to match candidate profile with current bss */
bool wpa_scan_res_match(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
struct wpa_bss *target_bss)
{
if (current_bss->ssid_len != target_bss->ssid_len) {
wpa_printf(MSG_DEBUG, "WNM: ssid didn't match");
return false;
}
if (os_memcmp(current_bss->ssid, target_bss->ssid, current_bss->ssid_len) != 0) {
wpa_printf(MSG_DEBUG, "WNM: ssid didn't match");
return false;
}
/* TODO security Match */
return true;
}
static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
enum mbo_transition_reject_reason *reason)
{
u8 i;
struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_bss *target;
if (!bss)
return NULL;
wnm_clear_acceptable(wpa_s);
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
struct neighbor_report *nei;
nei = &wpa_s->wnm_neighbor_report_elements[i];
if (nei->preference_present && nei->preference == 0) {
wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
MAC2STR(nei->bssid));
continue;
}
target = wpa_bss_get_bssid(wpa_s, nei->bssid);
if (!target) {
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) not found in scan results",
MAC2STR(nei->bssid),
nei->preference_present ? nei->preference :
-1);
continue;
}
if (age_secs) {
struct os_reltime now;
if (os_get_reltime(&now) == 0 &&
os_time_expired(&now, &target->last_update,
age_secs)) {
wpa_printf(MSG_DEBUG,
"Candidate BSS is more than %ld seconds old",
age_secs);
continue;
}
}
if (bss->ssid_len != target->ssid_len ||
os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
/*
* TODO: Could consider allowing transition to another
* ESS if PMF was enabled for the association.
*/
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) in different ESS",
MAC2STR(nei->bssid),
nei->preference_present ? nei->preference :
-1);
continue;
}
if (wpa_s->current_bss &&
!wpa_scan_res_match(wpa_s, wpa_s->current_bss, target)) {
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;
}
if (target->level < bss->level && target->level < -80) {
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,
target->level);
continue;
}
nei->acceptable = 1;
}
target = get_first_acceptable(wpa_s);
if (target) {
wpa_printf(MSG_DEBUG,
"WNM: Found an acceptable preferred transition candidate BSS "
MACSTR " (RSSI %d)",
MAC2STR(target->bssid), target->level);
}
return target;
}
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;
}
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)
{
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;
}
if (wpabuf_resize(buf, 18) < 0) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for Neighbor Report element");
return -1;
}
wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
/* length: 13 for basic neighbor report + 3 for preference subelement */
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;
}
static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpabuf **buf,
u8 pref)
{
const u8 *ie;
u8 op_class;
int sec_chan = 0;
enum phy_type phy_type;
u32 info;
struct ieee80211_ht_operation *ht_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;
}
op_class = get_operating_class(bss->channel, sec_chan);
phy_type = (sec_chan != 0) ? PHY_TYPE_HT : PHY_TYPE_ERP;
info = wnm_get_bss_info(wpa_s, bss);
return wnm_add_nei_rep(buf, bss->bssid, info, op_class, bss->channel, phy_type,
pref);
}
static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
{
unsigned int i, pref = 255;
struct os_reltime now;
if (!wpa_s->current_bss)
return;
/*
* TODO: Define when scan results are no longer valid for the candidate
* list.
*/
os_get_reltime(&now);
if (os_time_expired(&now, &wpa_s->last_scan, 10))
return;
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;
if (wpa_scan_res_match(wpa_s, wpa_s->current_bss, bss)) {
res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
if (res == -2)
continue; /* could not build entry for BSS */
if (res < 0)
break; /* no more room for candidates */
if (pref == 1)
break;
}
}
wpa_hexdump_buf(MSG_DEBUG,
"WNM: BSS Transition Management Response candidate list",
*buf);
}
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
static void wnm_send_bss_transition_mgmt_resp(
struct wpa_supplicant *wpa_s, u8 dialog_token,
enum bss_trans_mgmt_status_code status,
enum mbo_transition_reject_reason reason,
u8 delay, const u8 *target_bssid)
{
struct wpabuf *buf;
int res;
wpa_printf(MSG_DEBUG,
"WNM: Send BSS Transition Management Response to " MACSTR
" dialog_token=%u status=%u reason=%u delay=%d",
MAC2STR(wpa_s->current_bss->bssid), dialog_token, status, reason, delay);
if (!wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Current BSS not known - drop response");
return;
}
buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
if (!buf) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for BTM response");
return;
}
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
wpabuf_put_u8(buf, dialog_token);
wpabuf_put_u8(buf, status);
wpabuf_put_u8(buf, delay);
if (target_bssid) {
wpabuf_put_data(buf, target_bssid, ETH_ALEN);
} 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.
*/
wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
}
if (status == WNM_BSS_TM_ACCEPT)
wnm_add_cand_list(wpa_s, &buf);
res = wpa_drv_send_action(wpa_s, 0, 0,
wpabuf_head_u8(buf), wpabuf_len(buf), 0);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to send BSS Transition Management Response");
}
wpabuf_free(buf);
}
void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, char *ssid,
int after_new_scan)
{
wpa_printf(MSG_DEBUG,
"WNM: Transition to BSS " MACSTR
" based on BSS Transition Management Request after_new_scan=%d)",
MAC2STR(bss->bssid), after_new_scan);
/* Send the BSS Management Response - Accept */
if (wpa_s->wnm_reply) {
wpa_s->wnm_reply = 0;
wpa_printf(MSG_DEBUG,
"WNM: Sending successful BSS Transition Management Response");
wnm_send_bss_transition_mgmt_resp(
wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
bss->bssid);
}
if (bss == wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Already associated with the preferred candidate");
wnm_deallocate_memory(wpa_s);
return;
}
wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
wpa_supplicant_connect(wpa_s, bss, ssid);
wnm_deallocate_memory(wpa_s);
}
int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
{
struct wpa_bss *bss;
enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
enum mbo_transition_reject_reason reason =
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
if (!wpa_s->wnm_neighbor_report_elements)
return 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Process scan results for BSS Transition Management");
if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
&wpa_s->scan_trigger_time)) {
wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
wnm_deallocate_memory(wpa_s);
return 0;
}
/* Compare the Neighbor Report and scan results */
bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
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;
}
/* Associate to the network */
wnm_bss_tm_connect(wpa_s, bss, NULL, 1);
return 1;
send_bss_resp_fail:
if (!reply_on_fail)
return 0;
/* Send reject response for all the failures */
if (wpa_s->wnm_reply) {
wpa_s->wnm_reply = 0;
wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token,
status, reason, 0, NULL);
}
wnm_deallocate_memory(wpa_s);
return 0;
}
static int cand_pref_compar(const void *a, const void *b)
{
const struct neighbor_report *aa = a;
const struct neighbor_report *bb = b;
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);
}
static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
{
#ifdef DEBUG_PRINT
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
" info=0x%x op_class=%u chan=%u phy=%u pref=%d",
i, MAC2STR(nei->bssid), nei->bssid_info,
nei->regulatory_class,
nei->channel_number, nei->phy_type,
nei->preference_present ? nei->preference : -1);
}
#endif
}
static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
{
unsigned int i;
int num_chan;
u8 chan = 0;
wpa_s->next_scan_chan = 0;
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];
if (nei->channel_number <= 0) {
wpa_printf(MSG_DEBUG,
"WNM: Unknown neighbor operating channel_number for "
MACSTR " - scan all channels",
MAC2STR(nei->bssid));
return;
}
if (nei->channel_number != chan)
num_chan++;
}
if (num_chan == 1) {
wpa_s->next_scan_chan = chan;
}
}
static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
{
/* ESP doesn't support this */
return 0;
}
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
const u8 *pos, const u8 *end,
int reply)
{
unsigned int beacon_int;
u8 valid_int;
if (wpa_s->disable_btm)
return;
if (end - pos < 5)
return;
#ifdef CONFIG_MBO
wpa_s->wnm_mbo_trans_reason_present = 0;
wpa_s->wnm_mbo_transition_reason = 0;
#endif /* CONFIG_MBO */
if (wpa_s->current_bss)
beacon_int = wpa_s->current_bss->beacon_int;
else
beacon_int = 100; /* best guess */
wpa_s->wnm_dialog_token = pos[0];
wpa_s->wnm_mode = pos[1];
wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
valid_int = pos[4];
wpa_s->wnm_reply = reply;
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
"dialog_token=%u request_mode=0x%x "
"disassoc_timer=%u validity_interval=%u",
wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
wpa_s->wnm_dissoc_timer, valid_int);
pos += 5;
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
if (end - pos < 12) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
return;
}
os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
pos += 12; /* BSS Termination Duration */
}
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
char url[256];
if (end - pos < 1 || 1 + pos[0] > end - pos) {
wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
"Management Request (URL)");
return;
}
os_memcpy(url, pos + 1, pos[0]);
url[pos[0]] = '\0';
pos += 1 + pos[0];
wpa_msg(wpa_s, MSG_DEBUG, "ESS_DISASSOC_IMMINENT %u %s",
wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
}
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
wpa_msg(wpa_s, MSG_DEBUG, "WNM: Disassociation Imminent - "
"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
if (wpa_s->wnm_dissoc_timer) {
/* TODO: mark current BSS less preferred for
* selection */
wpa_printf(MSG_DEBUG, "Trying to find another BSS");
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
#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 */
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
unsigned int valid_ms;
wpa_msg(wpa_s, MSG_DEBUG, "WNM: Preferred List Available");
wnm_deallocate_memory(wpa_s);
wpa_s->wnm_neighbor_report_elements = os_calloc(
WNM_MAX_NEIGHBOR_REPORT,
sizeof(struct neighbor_report));
if (wpa_s->wnm_neighbor_report_elements == NULL)
return;
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;
}
if (tag == WLAN_EID_NEIGHBOR_REPORT) {
struct neighbor_report *rep;
rep = &wpa_s->wnm_neighbor_report_elements[
wpa_s->wnm_num_neighbor_report];
wnm_parse_neighbor_report(wpa_s, pos, len, rep);
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;
}
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(
wpa_s, wpa_s->wnm_dialog_token,
WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
NULL);
return;
}
wnm_sort_cand_list(wpa_s);
wnm_dump_cand_list(wpa_s);
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);
wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
wpa_s->wnm_cand_valid_until.sec +=
wpa_s->wnm_cand_valid_until.usec / 1000000;
wpa_s->wnm_cand_valid_until.usec %= 1000000;
/*
* Fetch the latest scan results from the kernel and check for
* candidates based on those results first. This can help in
* finding more up-to-date information should the driver has
* done some internal scanning operations after the last scan
* result update in wpa_supplicant.
*/
if (wnm_fetch_scan_results(wpa_s) > 0)
return;
/*
* Try to use previously received scan results, if they are
* recent enough to use for a connection.
*/
if (wpa_s->last_scan_res_used > 0) {
struct os_reltime now;
os_get_reltime(&now);
if (!os_time_expired(&now, &wpa_s->last_scan, 10)) {
wpa_printf(MSG_DEBUG,
"WNM: Try to use recent scan results");
if (wnm_scan_process(wpa_s, 0) > 0)
return;
wpa_printf(MSG_DEBUG,
"WNM: No match in previous scan results - try a new scan");
}
}
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_supplicant_req_scan(wpa_s, 0, 0);
} else if (reply) {
enum bss_trans_mgmt_status_code status;
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
status = WNM_BSS_TM_ACCEPT;
else {
wpa_msg(wpa_s, MSG_DEBUG, "WNM: BSS Transition Management Request did not include candidates");
status = WNM_BSS_TM_REJECT_UNSPECIFIED;
}
wpa_s->wnm_reply = 0;
wnm_send_bss_transition_mgmt_resp(
wpa_s, wpa_s->wnm_dialog_token, status,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
}
}
#define BTM_QUERY_MIN_SIZE 4
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
u8 query_reason,
const char *btm_candidates,
int cand_list)
{
struct wpabuf *buf;
int ret;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
MACSTR " query_reason=%u%s",
MAC2STR(wpa_s->current_bss->bssid), query_reason,
cand_list ? " candidate list" : "");
buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
if (!buf)
return -1;
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);
if (cand_list)
wnm_add_cand_list(wpa_s, &buf);
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);
}
ret = wpa_drv_send_action(wpa_s, 0, 0,
wpabuf_head_u8(buf), wpabuf_len(buf), 0);
wpabuf_free(buf);
return ret;
}
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
u8 *sender, u8 *payload, size_t len)
{
const u8 *pos, *end;
u8 act;
if (len < 2)
return;
pos = payload;
act = *pos++;
end = payload + len;
wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
act, MAC2STR(sender));
switch (act) {
case WNM_BSS_TRANS_MGMT_REQ:
ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
!(sender[0] & 0x01));
break;
default:
wpa_printf(MSG_ERROR, "WNM: Unknown request");
break;
}
}

View File

@ -0,0 +1,70 @@
/*
* IEEE 802.11v WNM related functions and structures
* Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WNM_STA_H
#define WNM_STA_H
struct measurement_pilot {
u8 measurement_pilot;
u8 subelem_len;
u8 subelems[255];
};
struct multiple_bssid {
u8 max_bssid_indicator;
u8 subelem_len;
u8 subelems[255];
};
struct neighbor_report {
u8 bssid[ETH_ALEN];
u32 bssid_info;
u8 regulatory_class;
u8 channel_number;
u8 phy_type;
u8 preference; /* valid if preference_present=1 */
u16 tsf_offset; /* valid if tsf_present=1 */
u16 beacon_int; /* valid if tsf_present=1 */
char country[2]; /* valid if country_present=1 */
u8 rm_capab[5]; /* valid if rm_capab_present=1 */
u16 bearing; /* valid if bearing_present=1 */
u16 rel_height; /* valid if bearing_present=1 */
u32 distance; /* valid if bearing_present=1 */
u64 bss_term_tsf; /* valid if bss_term_present=1 */
u16 bss_term_dur; /* valid if bss_term_present=1 */
unsigned int preference_present:1;
unsigned int tsf_present:1;
unsigned int country_present:1;
unsigned int rm_capab_present:1;
unsigned int bearing_present:1;
unsigned int bss_term_present:1;
unsigned int acceptable:1;
struct measurement_pilot *meas_pilot;
struct multiple_bssid *mul_bssid;
int freq;
};
int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
u8 action, u16 intval, struct wpabuf *tfs_req);
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
u8 *sender, u8 *payload, size_t len);
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
u8 query_reason,
const char *btm_candidates,
int cand_list);
void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
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);
#endif /* WNM_STA_H */

View File

@ -0,0 +1,110 @@
/*
* wpa_supplicant - Internal definitions
* Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_SUPPLICANT_I_H
#define WPA_SUPPLICANT_I_H
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
/*
* struct rrm_data - Data used for managing RRM features
*/
struct rrm_data {
/* rrm_used - indication regarding the current connection */
unsigned int rrm_used:1;
/*
* notify_neighbor_rep - Callback for notifying report requester
*/
void (*notify_neighbor_rep)(void *ctx, const u8 *neighbor_rep, size_t len);
/*
* neighbor_rep_cb_ctx - Callback context
* Received in the callback registration, and sent to the callback
* function as a parameter.
*/
void *neighbor_rep_cb_ctx;
/* next_neighbor_rep_token - Next request's dialog token */
u8 next_neighbor_rep_token;
/* token - Dialog token of the current radio measurement */
u8 token;
/* destination address of the current radio measurement request */
u8 dst_addr[ETH_ALEN];
};
#define SSID_MAX_LEN 32
struct beacon_rep_data {
u8 token;
u8 last_indication;
struct wpa_driver_scan_params scan_params;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 bssid[ETH_ALEN];
enum beacon_report_detail report_detail;
struct bitfield *eids;
};
enum scan_trigger_reason {
REASON_INVALID,
REASON_RRM_BEACON_REPORT,
REASON_WNM_BSS_TRANS_REQ,
};
struct wpa_supplicant {
int disable_btm;
/* rrm ie */
uint8_t rrm_ie[5];
u8 extend_caps[8];
int scanning;
enum scan_trigger_reason scan_reason;
u64 scan_start_tsf;
u8 tsf_bssid[ETH_ALEN];
struct wpa_bss *current_bss;
struct dl_list bss; /* struct wpa_bss::list */
struct dl_list bss_id; /* struct wpa_bss::list_id */
size_t num_bss;
unsigned int bss_update_idx;
unsigned int bss_next_id;
/*
* Pointers to BSS entries in the order they were in the last scan
* results.
*/
struct wpa_bss **last_scan_res;
unsigned int last_scan_res_used;
unsigned int last_scan_res_size;
struct os_reltime last_scan;
struct os_reltime scan_trigger_time, scan_start_time;
u8 next_scan_bssid[ETH_ALEN];
/* type and subtype of frames for which supplicant has registered */
uint32_t type, subtype;
u8 next_scan_chan;
#ifdef CONFIG_WNM
u8 wnm_dialog_token;
u8 wnm_reply;
u8 wnm_num_neighbor_report;
u8 wnm_mode;
u16 wnm_dissoc_timer;
u8 wnm_bss_termination_duration[12];
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
#endif /* CONFIG_WNM */
struct rrm_data rrm;
struct beacon_rep_data beacon_rep_data;
struct os_reltime beacon_rep_scan;
};
#endif

View File

@ -0,0 +1,187 @@
/*
* Driver interface definition
* Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file defines a driver interface used by both %wpa_supplicant and
* hostapd. The first part of the file defines data structures used in various
* driver operations. This is followed by the struct wpa_driver_ops that each
* driver wrapper will beed to define with callback functions for requesting
* driver operations. After this, there are definitions for driver event
* reporting with wpa_supplicant_event() and some convenience helper functions
* that can be used to report events.
*/
#ifndef DRIVER_H
#define DRIVER_H
#define WPA_SUPPLICANT_DRIVER_VERSION 4
#include "common/defs.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
#include "utils/list.h"
struct wpa_bss;
struct wpa_supplicant;
/**
* struct wpa_scan_res - Scan result for an BSS/IBSS
* @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
* @bssid: BSSID
* @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
* @beacon_int: beacon interval in TUs (host byte order)
* @caps: capability information field in host byte order
* @qual: signal quality
* @noise: noise level
* @level: signal level
* @tsf: Timestamp
* @age: Age of the information in milliseconds (i.e., how many milliseconds
* ago the last Beacon or Probe Response frame was received)
* @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
* @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
* of TSF of the BSS specified by %tsf_bssid.
* @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
* @ie_len: length of the following IE field in octets
* @beacon_ie_len: length of the following Beacon IE field in octets
*
* This structure is used as a generic format for scan results from the
* driver. Each driver interface implementation is responsible for converting
* the driver or OS specific scan results into this format.
*
* If the driver does not support reporting all IEs, the IE data structure is
* constructed of the IEs that are available. This field will also need to
* include SSID in IE format. All drivers are encouraged to be extended to
* report all IEs to make it easier to support future additions.
*
* This structure data is followed by ie_len octets of IEs from Probe Response
* frame (or if the driver does not indicate source of IEs, these may also be
* from Beacon frame). After the first set of IEs, another set of IEs may follow
* (with beacon_ie_len octets of data) if the driver provides both IE sets.
*/
struct wpa_scan_res {
unsigned int flags;
u8 bssid[ETH_ALEN];
int chan;
u16 beacon_int;
u16 caps;
int noise;
int level;
u64 tsf;
unsigned int age;
u64 parent_tsf;
u8 tsf_bssid[ETH_ALEN];
size_t ie_len;
size_t beacon_ie_len;
/* Followed by ie_len + beacon_ie_len octets of IE data */
};
/**
* struct wpa_scan_results - Scan results
* @res: Array of pointers to allocated variable length scan result entries
* @num: Number of entries in the scan result array
* @fetch_time: Time when the results were fetched from the driver
*/
struct wpa_scan_results {
struct wpa_scan_res **res;
size_t num;
struct os_reltime fetch_time;
};
#define WPAS_MAX_SCAN_SSIDS 1
/**
* struct wpa_driver_scan_ssid - SSIDs to scan for
* @ssid - specific SSID to scan for (ProbeReq)
* %NULL or zero-length SSID is used to indicate active scan
* with wildcard SSID.
* @ssid_len - Length of the SSID in octets
*/
struct wpa_driver_scan_ssid {
const u8 *ssid;
size_t ssid_len;
};
/**
* struct wpa_driver_scan_params - Scan parameters
* Data for struct wpa_driver_ops::scan2().
*/
struct wpa_driver_scan_params {
/**
* ssids - SSIDs to scan for
*/
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
/**
* num_ssids - Number of entries in ssids array
* Zero indicates a request for a passive scan.
*/
size_t num_ssids;
/**
* freqs - Array of frequencies to scan or %NULL for all frequencies
*
* The frequency is set in MHz. The array is zero-terminated.
*/
int channel;
/**
* bssid - Specific BSSID to scan for
*
* This optional parameter can be used to replace the default wildcard
* BSSID with a specific BSSID to scan for if results are needed from
* only a single BSS.
*/
const u8 *bssid;
/**
* duration - Dwell time on each channel
*
* This optional parameter can be used to set the dwell time on each
* channel. In TUs.
*/
u16 duration;
unsigned int duration_mandatory;
};
/**
* struct scan_info - Optional data for EVENT_SCAN_RESULTS events
* @aborted: Whether the scan was aborted
* @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
* @num_freqs: Number of entries in freqs array
* @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
* SSID)
* @num_ssids: Number of entries in ssids array
* @external_scan: Whether the scan info is for an external scan
* @nl_scan_event: 1 if the source of this scan event is a normal scan,
* 0 if the source of the scan event is a vendor scan
* @scan_start_tsf: Time when the scan started in terms of TSF of the
* BSS that the interface that requested the scan is connected to
* (if available).
* @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
* is set.
*/
struct scan_info {
int aborted;
const int *freqs;
size_t num_freqs;
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
size_t num_ssids;
int external_scan;
int nl_scan_event;
u64 scan_start_tsf;
u8 scan_start_tsf_bssid[ETH_ALEN];
} scan_info;
/* driver_common.c */
void wpa_scan_results_free(struct wpa_scan_results *res);
int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
unsigned int chan, unsigned int wait,
const u8 *data, size_t data_len, int no_cck);
void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, char *ssid);
#endif /* DRIVER_H */

View File

@ -0,0 +1,403 @@
/**
* Copyright 2020 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.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_wifi_driver.h"
#include "drivers/driver.h"
#include "common/bss.h"
#include "common/rrm.h"
#include "common/wnm_sta.h"
#include "common/wpa_supplicant_i.h"
#include "esp_supplicant/esp_scan_i.h"
#include "esp_supplicant/esp_common_i.h"
#include "common/ieee802_11_common.h"
#include "esp_rrm.h"
#include "esp_wnm.h"
struct wpa_supplicant g_wpa_supp;
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)
{
struct ieee_mgmt_frame *frm = os_malloc(sizeof(struct ieee_mgmt_frame) + len);
if (!frm) {
wpa_printf(MSG_ERROR, "memory allocation failed");
return -1;
}
os_memcpy(frm->sender, sender, ETH_ALEN);
frm->len = len;
frm->channel = channel;
frm->rssi = rssi;
os_memcpy(frm->payload, frame, len);
if (esp_supplicant_post_evt(SIG_SUPPLICANT_RX_ACTION, (u32)frm) != 0) {
os_free(frm);
return -1;
}
return 0;
}
static void esp_rx_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
u8 *payload, size_t len, u32 rssi)
{
if (payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
/* neighbor report parsing */
wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, len - 1);
} else if (payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
/* Beacon measurement */
wpas_rrm_handle_radio_measurement_request(wpa_s, NULL,
sender, payload + 1, len - 1);
} else if (payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
/* Link measurement */
wpas_rrm_handle_link_measurement_request(wpa_s, NULL,
payload + 1, len - 1, rssi);
}
}
static int esp_mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u32 rssi)
{
u8 category;
u8 bssid[ETH_ALEN];
struct wpa_supplicant *wpa_s = &g_wpa_supp;
int ret = esp_wifi_get_assoc_bssid_internal(bssid);
if (ret < 0) {
wpa_printf(MSG_INFO, "STA not associated");
return -1;
}
category = *payload++;
len--;
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);
}
return 0;
}
static void esp_btm_rrm_task(void *pvParameters)
{
supplicant_event_t *evt;
bool task_del = false;
while(1) {
if (xQueueReceive(s_supplicant_evt_queue, &evt, portMAX_DELAY) != pdTRUE)
continue;
/* event validation failed */
if (evt->id >= SIG_SUPPLICANT_MAX) {
os_free(evt);
continue;
}
/* get lock */
SUPPLICANT_API_LOCK();
switch (evt->id) {
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);
os_free(frm);
break;
}
case SIG_SUPPLICANT_SCAN_DONE:
esp_supplicant_handle_scan_done_evt();
break;
case SIG_SUPPLICANT_DEL_TASK:
task_del = true;
break;
default:
break;
}
os_free(evt);
SUPPLICANT_API_UNLOCK();
if (task_del)
break;
}
vQueueDelete(s_supplicant_evt_queue);
s_supplicant_evt_queue = NULL;
if (s_supplicant_api_lock) {
vSemaphoreDelete(s_supplicant_api_lock);
s_supplicant_api_lock = NULL;
}
/* At this point, we completed */
vTaskDelete(NULL);
}
static void esp_clear_bssid_flag(struct wpa_supplicant *wpa_s)
{
wifi_config_t *config;
/* Reset only if btm is enabled */
if (esp_wifi_is_btm_enabled_internal(ESP_IF_WIFI_STA) == false)
return;
config = os_zalloc(sizeof(wifi_config_t));
if (!config) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return;
}
esp_wifi_get_config(ESP_IF_WIFI_STA, config);
config->sta.bssid_set = 0;
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
os_free(config);
wpa_printf(MSG_DEBUG, "cleared bssid flag");
}
static void esp_register_action_frame(struct wpa_supplicant *wpa_s)
{
wpa_s->type &= ~WLAN_FC_STYPE_ACTION;
/* subtype is defined only for action frame */
wpa_s->subtype = 0;
/* current supported features in supplicant: rrm and btm */
if (esp_wifi_is_rm_enabled_internal(ESP_IF_WIFI_STA))
wpa_s->subtype = 1 << WLAN_ACTION_RADIO_MEASUREMENT;
if (esp_wifi_is_btm_enabled_internal(ESP_IF_WIFI_STA))
wpa_s->subtype |= 1 << WLAN_ACTION_WNM;
if (wpa_s->subtype)
wpa_s->type |= 1 << WLAN_FC_STYPE_ACTION;
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,
int event_id, void* event_data)
{
u8 bssid[ETH_ALEN];
u8 *ie;
struct wpa_supplicant *wpa_s = &g_wpa_supp;
int ret = esp_wifi_get_assoc_bssid_internal(bssid);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Not able to get connected bssid");
return;
}
struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!bss) {
wpa_printf(MSG_INFO, "connected bss entry not present in scan cache");
return;
}
wpa_s->current_bss = bss;
ie = (u8 *)bss;
ie += sizeof(struct wpa_bss);
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);
/* clear set bssid flag */
esp_clear_bssid_flag(wpa_s);
}
static void esp_supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
wpas_rrm_reset(wpa_s);
if (wpa_s->current_bss) {
wpa_s->current_bss = NULL;
}
}
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);
s_supplicant_api_lock = xSemaphoreCreateRecursiveMutex();
if (!s_supplicant_api_lock) {
wpa_printf(MSG_ERROR, "esp_supplicant_common_init: failed to create Supplicant API lock");
return;
}
esp_scan_init(wpa_s);
wpas_rrm_reset(wpa_s);
wpas_clear_beacon_rep_data(wpa_s);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
&esp_supplicant_sta_conn_handler, NULL);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
&esp_supplicant_sta_disconn_handler, NULL);
wpa_s->type = 0;
wpa_s->subtype = 0;
wpa_cb->wpa_sta_rx_mgmt = esp_ieee80211_handle_rx_frm;
}
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;
wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, cb, cb_ctx);
return 0;
}
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);
}
void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, char *ssid)
{
wifi_config_t *config = os_zalloc(sizeof(wifi_config_t));
if (!config) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return;
}
esp_wifi_get_config(ESP_IF_WIFI_STA, config);
/* 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);
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
os_free(config);
esp_wifi_connect();
}
void esp_set_rm_enabled_ie(void)
{
uint8_t rmm_ie[5] = {0};
uint8_t rrm_ie_len = 5;
uint8_t *pos = rmm_ie;
*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
*pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
#ifdef SCAN_CACHE_SUPPORTED
WLAN_RRM_CAPS_BEACON_REPORT_TABLE |
#endif
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE;
/* set rm enabled IE if enabled in driver */
if (esp_wifi_is_rm_enabled_internal(ESP_IF_WIFI_STA)) {
esp_wifi_set_appie_internal(WIFI_APPIE_RM_ENABLED_CAPS, rmm_ie, rrm_ie_len, 0);
}
}
void esp_get_tx_power(uint8_t *tx_power)
{
#define DEFAULT_MAX_TX_POWER 19 /* max tx power is 19.5 dbm */
s8 power;
/* esp sends management frames at max tx power configured */
int ret = esp_wifi_get_max_tx_power(&power);
if (ret != 0) {
wpa_printf(MSG_ERROR, "failed to get tx power");
*tx_power = DEFAULT_MAX_TX_POWER;
return;
}
*tx_power = power/4;
#undef DEFAULT_MAX_TX_POWER
}
int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
unsigned int channel,
unsigned int wait,
const u8 *data, size_t data_len,
int no_cck)
{
int ret = 0;
wifi_mgmt_frm_req_t *req = os_zalloc(sizeof(*req) + data_len);;
if (!req)
return -1;
if (!wpa_s->current_bss) {
wpa_printf(MSG_ERROR, "STA not associated, return");
ret = -1;
goto cleanup;
}
req->ifx = ESP_IF_WIFI_STA;
req->subtype = WLAN_FC_STYPE_ACTION;
req->data_len = data_len;
os_memcpy(req->data, data, req->data_len);
if (esp_wifi_send_mgmt_frm_internal(req) != 0) {
wpa_printf(MSG_ERROR, "action frame sending failed");
ret = -1;
goto cleanup;
}
wpa_printf(MSG_INFO, "action frame sent");
cleanup:
os_free(req);
return ret;
}
int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data)
{
supplicant_event_t *evt = os_zalloc(sizeof(supplicant_event_t));
if (evt == NULL) {
return -1;
}
evt->id = evt_id;
evt->data = data;
SUPPLICANT_API_LOCK();
if (xQueueSend(s_supplicant_evt_queue, &evt, 10 / portTICK_PERIOD_MS ) != pdPASS) {
SUPPLICANT_API_UNLOCK();
os_free(evt);
return -1;
}
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

@ -0,0 +1,80 @@
/**
* Copyright 2020 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.
*/
#ifndef ESP_COMMON_I_H
#define ESP_COMMON_I_H
#include "utils/includes.h"
struct wpa_funcs;
#ifdef ROAMING_SUPPORT
struct ieee_mgmt_frame {
u8 sender[ETH_ALEN];
u8 channel;
u32 rssi;
size_t len;
u8 payload[0];
};
typedef struct {
uint32_t id;
uint32_t data;
} supplicant_event_t;
#define SUPPLICANT_API_LOCK() xSemaphoreTakeRecursive(s_supplicant_api_lock, portMAX_DELAY)
#define SUPPLICANT_API_UNLOCK() xSemaphoreGiveRecursive(s_supplicant_api_lock)
#define SUPPLICANT_TASK_STACK_SIZE (6144 + TASK_STACK_SIZE_ADD)
enum SIG_SUPPLICANT {
SIG_SUPPLICANT_RX_ACTION,
SIG_SUPPLICANT_SCAN_DONE,
SIG_SUPPLICANT_DEL_TASK,
SIG_SUPPLICANT_MAX,
};
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);
#else
#include "esp_rrm.h"
#include "esp_wnm.h"
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)
{
return -1;
}
int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
const char *btm_candidates,
int cand_list)
{
return -1;
}
#endif
#endif

View File

@ -0,0 +1,271 @@
/**
* Copyright 2020 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.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_supplicant_i.h"
#include "utils/wpa_debug.h"
#include "esp_wifi_driver.h"
#include "esp_wifi_types.h"
#include "drivers/driver.h"
#include "common/scan.h"
#include "common/bss.h"
#include "common/rrm.h"
#include "common/ieee802_11_common.h"
#include "esp_supplicant/esp_common_i.h"
#include "common/wnm_sta.h"
extern struct wpa_supplicant g_wpa_supp;
static void esp_scan_done_event_handler(void* arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
if (!wpa_s->scanning) {
/* update last scan time */
wpa_s->scan_start_tsf = esp_wifi_get_tsf_time(ESP_IF_WIFI_STA);
wpa_printf(MSG_DEBUG, "scan not triggered by supplicant, ignore");
return;
}
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);
esp_supplicant_post_evt(SIG_SUPPLICANT_SCAN_DONE, 0);
}
static void esp_supp_handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss = wpa_bss_get_next_bss(wpa_s, wpa_s->current_bss);
if (wpa_s->wnm_neighbor_report_elements) {
wnm_scan_process(wpa_s, 1);
} else if (wpa_s->wnm_dissoc_timer) {
if (wpa_s->num_bss == 1) {
wpa_printf(MSG_INFO, "not able to find another candidate, do nothing");
return;
}
/* this is a already matched bss */
if (bss) {
wnm_bss_tm_connect(wpa_s, bss, NULL, 1);
}
}
}
static void esp_supp_scan_done_cleanup(struct wpa_supplicant *wpa_s)
{
uint16_t number = 1;
wifi_ap_record_t ap_records;
wpa_s->scanning = 0;
wpa_s->scan_reason = 0;
/* clean scan list from net80211 */
esp_wifi_scan_get_ap_records(&number, &ap_records);
}
void esp_supplicant_handle_scan_done_evt(void)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
wpa_printf(MSG_INFO, "scan done received");
/* Check which module started this, call the respective function */
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);
}
esp_supp_scan_done_cleanup(wpa_s);
wpa_bss_update_end(wpa_s);
#ifndef SCAN_CACHE_SUPPORTED
wpa_bss_flush(wpa_s);
#endif
}
void esp_scan_init(struct wpa_supplicant *wpa_s)
{
wpa_s->scanning = 0;
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);
}
void esp_scan_deinit(struct wpa_supplicant *wpa_s)
{
wpa_bss_deinit(wpa_s);
}
int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
struct os_reltime now;
struct wpa_scan_res *res;
u8 *ptr;
if (len < 12) {
wpa_printf(MSG_ERROR, "beacon/probe is having short len=%d\n", len);
return -1;
}
res = os_zalloc(sizeof(struct wpa_scan_res) + len - 12);
if (!res) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return -1;
}
ptr = (u8 *)res;
os_get_time(&now);
os_memcpy(res->bssid, sender, ETH_ALEN);
res->tsf = WPA_GET_LE64(frame);
frame += 8;
len -= 8;
if ((wpa_s->scan_start_tsf == 0) &&
wpa_s->current_bss &&
(os_memcmp(wpa_s->current_bss, sender, ETH_ALEN) == 0)) {
wpa_s->scan_start_tsf = res->tsf;
os_memcpy(wpa_s->tsf_bssid, sender, ETH_ALEN);
}
res->beacon_int = WPA_GET_LE16(frame);
frame += 2;
len -= 2;
res->caps = WPA_GET_LE16(frame);
frame += 2;
len -= 2;
res->chan = channel;
res->noise = 0;
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;
ptr += sizeof(struct wpa_scan_res);
/* update rest of the frame */
os_memcpy(ptr, frame, len);
wpa_bss_update_scan_res(wpa_s, res, &now);
os_free(res);
return 0;
}
static int esp_issue_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *scan_params)
{
wifi_scan_config_t *params = NULL;
int ret = 0;
u64 scan_start_tsf = esp_wifi_get_tsf_time(ESP_IF_WIFI_STA);
/* TODO: Directly try to connect if scan results are recent */
if ((scan_start_tsf - wpa_s->scan_start_tsf) > 100000) {
wpa_printf(MSG_DEBUG, "flushing old scan cache %llu",
(scan_start_tsf - wpa_s->scan_start_tsf));
wpa_bss_flush(wpa_s);
}
esp_wifi_get_macaddr_internal(ESP_IF_WIFI_STA, wpa_s->tsf_bssid);
if (scan_params) {
params = os_zalloc(sizeof(wifi_scan_config_t));
if (!params) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return -1;
}
if (scan_params->num_ssids) {
params->ssid = os_zalloc(scan_params->ssids[0].ssid_len + 1);
if (!params->ssid) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return -1;
}
os_memcpy(params->ssid, scan_params->ssids[0].ssid, scan_params->ssids[0].ssid_len);
params->scan_type = WIFI_SCAN_TYPE_ACTIVE;
} else
params->scan_type = WIFI_SCAN_TYPE_PASSIVE;
if (scan_params->bssid) {
params->bssid = os_zalloc(ETH_ALEN);
if (!params->bssid) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return -1;
}
os_memcpy(params->bssid, scan_params->bssid, ETH_ALEN);
}
if (scan_params->channel) {
params->channel = scan_params->channel;
}
if (scan_params->duration) {
params->scan_time.passive = scan_params->duration;
params->scan_time.active.min = scan_params->duration;
params->scan_time.active.max = scan_params->duration;
}
}
wpa_s->scan_start_tsf = scan_start_tsf;
/* Register frames to come to supplicant when we park on channel */
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);
/* issue scan */
if (esp_wifi_scan_start(params, false) < 0) {
ret = -1;
goto cleanup;
}
wpa_s->scanning = 1;
wpa_bss_update_start(wpa_s);
wpa_printf(MSG_INFO, "scan issued at time=%llu", wpa_s->scan_start_tsf);
cleanup:
if (params->ssid)
os_free(params->ssid);
if (params->bssid)
os_free(params->bssid);
os_free(params);
return ret;
}
/**
* wpa_supplicant_trigger_scan - Request driver to start a scan
* @wpa_s: Pointer to wpa_supplicant data
* @params: Scan parameters
* Returns: 0 on success, -1 on failure
*/
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
return esp_issue_scan(wpa_s, params);
}
void wpa_scan_results_free(struct wpa_scan_results *res)
{
size_t i;
if (res == NULL)
return;
for (i = 0; i < res->num; i++)
os_free(res->res[i]);
os_free(res->res);
os_free(res);
}

View File

@ -0,0 +1,25 @@
/**
* Copyright 2020 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.
*/
#ifndef ESP_SCAN_I_H
#define ESP_SCAN_I_H
void esp_scan_init(struct wpa_supplicant *wpa_s);
void esp_scan_deinit(struct wpa_supplicant *wpa_s);
int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf);
void esp_supplicant_handle_scan_done_evt(void);
#endif

View File

@ -55,6 +55,12 @@ typedef enum {
WIFI_APPIE_MAX,
} wifi_appie_t;
/* 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_t;
enum {
NONE_AUTH = 0x01,
WPA_AUTH_UNSPEC = 0x02,
@ -126,6 +132,7 @@ struct wpa_funcs {
int (*wpa_michael_mic_failure)(u16 is_unicast);
uint8_t *(*wpa3_build_sae_msg)(uint8_t *bssid, uint32_t type, size_t *len);
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);
};
struct wpa2_funcs {
@ -176,6 +183,13 @@ typedef struct {
uint8_t igtk[WPA_IGTK_LEN];
} wifi_wpa_igtk_t;
typedef struct {
wifi_interface_t ifx;
uint8_t subtype;
uint32_t data_len;
uint8_t data[0];
} wifi_mgmt_frm_req_t;
uint8_t *esp_wifi_ap_get_prof_pmk_internal(void);
struct wifi_ssid *esp_wifi_ap_get_prof_ap_ssid_internal(void);
uint8_t esp_wifi_ap_get_prof_authmode_internal(void);
@ -237,5 +251,9 @@ wifi_cipher_type_t esp_wifi_sta_get_mgmt_group_cipher(void);
int esp_wifi_set_igtk_internal(uint8_t if_index, const wifi_wpa_igtk_t *igtk);
esp_err_t esp_wifi_internal_issue_disconnect(uint8_t reason_code);
bool esp_wifi_skip_supp_pmkcaching(void);
bool esp_wifi_is_rm_enabled_internal(uint8_t if_index);
bool esp_wifi_is_btm_enabled_internal(uint8_t if_index);
esp_err_t esp_wifi_register_mgmt_frame_internal(uint32_t type, uint32_t subtype);
esp_err_t esp_wifi_send_mgmt_frm_internal(const wifi_mgmt_frm_req_t *req);
#endif /* _ESP_WIFI_DRIVER_H_ */

View File

@ -35,6 +35,7 @@
#include "esp_private/wifi.h"
#include "esp_wpa3_i.h"
#include "esp_wpa2.h"
#include "esp_common_i.h"
void wpa_install_key(enum wpa_alg alg, u8 *addr, int key_idx, int set_tx,
u8 *seq, size_t seq_len, u8 *key, size_t key_len, int key_entry_valid)
@ -103,6 +104,7 @@ void wpa_config_assoc_ie(u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len)
} else {
esp_wifi_set_appie_internal(WIFI_APPIE_RSN, assoc_buf, assoc_wpa_ie_len, 1);
}
esp_set_rm_enabled_ie();
}
void wpa_neg_complete(void)
@ -156,6 +158,7 @@ bool wpa_deattach(void)
void wpa_sta_connect(uint8_t *bssid)
{
int ret = 0;
wpa_config_profile();
ret = wpa_config_bss(bssid);
@ -200,6 +203,13 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code)
}
}
#ifndef ROAMING_SUPPORT
static inline void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
{
wpa_cb->wpa_sta_rx_mgmt = NULL;
}
#endif
int esp_supplicant_init(void)
{
struct wpa_funcs *wpa_cb;
@ -228,6 +238,7 @@ int esp_supplicant_init(void)
wpa_cb->wpa_config_bss = NULL;//wpa_config_bss;
wpa_cb->wpa_michael_mic_failure = wpa_michael_mic_failure;
esp_wifi_register_wpa3_cb(wpa_cb);
esp_supplicant_common_init(wpa_cb);
esp_wifi_register_wpa_cb_internal(wpa_cb);

View File

@ -0,0 +1,89 @@
/*
* Bitfield
* Copyright (c) 2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "bitfield.h"
struct bitfield {
u8 *bits;
size_t max_bits;
};
struct bitfield * bitfield_alloc(size_t max_bits)
{
struct bitfield *bf;
bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8);
if (bf == NULL)
return NULL;
bf->bits = (u8 *) (bf + 1);
bf->max_bits = max_bits;
return bf;
}
void bitfield_free(struct bitfield *bf)
{
os_free(bf);
}
void bitfield_set(struct bitfield *bf, size_t bit)
{
if (bit >= bf->max_bits)
return;
bf->bits[bit / 8] |= BIT(bit % 8);
}
void bitfield_clear(struct bitfield *bf, size_t bit)
{
if (bit >= bf->max_bits)
return;
bf->bits[bit / 8] &= ~BIT(bit % 8);
}
int bitfield_is_set(struct bitfield *bf, size_t bit)
{
if (bit >= bf->max_bits)
return 0;
return !!(bf->bits[bit / 8] & BIT(bit % 8));
}
static int first_zero(u8 val)
{
int i;
for (i = 0; i < 8; i++) {
if (!(val & 0x01))
return i;
val >>= 1;
}
return -1;
}
int bitfield_get_first_zero(struct bitfield *bf)
{
size_t i;
for (i = 0; i < (bf->max_bits + 7) / 8; i++) {
if (bf->bits[i] != 0xff)
break;
}
if (i == (bf->max_bits + 7) / 8)
return -1;
i = i * 8 + first_zero(bf->bits[i]);
if (i >= bf->max_bits)
return -1;
return i;
}

View File

@ -0,0 +1,21 @@
/*
* Bitfield
* Copyright (c) 2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef BITFIELD_H
#define BITFIELD_H
struct bitfield;
struct bitfield * bitfield_alloc(size_t max_bits);
void bitfield_free(struct bitfield *bf);
void bitfield_set(struct bitfield *bf, size_t bit);
void bitfield_clear(struct bitfield *bf, size_t bit);
int bitfield_is_set(struct bitfield *bf, size_t bit);
int bitfield_get_first_zero(struct bitfield *bf);
#endif /* BITFIELD_H */

View File

@ -491,3 +491,59 @@ int hwaddr_aton2(const char *txt, u8 *addr)
return pos - txt;
}
static inline int os_reltime_expired(struct os_time *now,
struct os_time *ts,
os_time_t timeout_secs)
{
struct os_time age;
os_time_sub(now, ts, &age);
return (age.sec > timeout_secs) ||
(age.sec == timeout_secs && age.usec > 0);
}
int os_time_expired(struct os_time *now,
struct os_time *ts,
os_time_t timeout_secs)
{
return os_reltime_expired(now, ts, timeout_secs);
}
u8 rssi_to_rcpi(int rssi)
{
if (!rssi)
return 255; /* not available */
if (rssi < -110)
return 0;
if (rssi > 0)
return 220;
return (rssi + 110) * 2;
}
/**
* wpa_ssid_txt - Convert SSID to a printable string
* @ssid: SSID (32-octet string)
* @ssid_len: Length of ssid in octets
* Returns: Pointer to a printable string
*
* This function can be used to convert SSIDs into printable form. In most
* cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
* does not limit the used character set, so anything could be used in an SSID.
*
* This function uses a static buffer, so only one call can be used at the
* time, i.e., this is not re-entrant and the returned buffer must be used
* before calling this again.
*/
const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
{
static char ssid_txt[SSID_MAX_LEN * 4 + 1];
if (ssid == NULL) {
ssid_txt[0] = '\0';
return ssid_txt;
}
printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
return ssid_txt;
}

View File

@ -13,6 +13,7 @@
#endif /* ets */
#include "os.h"
#include "esp_bit_defs.h"
#include "list.h"
/* Define platform specific variable type macros */
#if defined(ESP_PLATFORM)
@ -115,6 +116,12 @@ static inline unsigned int wpa_swap_32(unsigned int v)
#define WPA_BYTE_SWAP_DEFINED
#endif /* !WPA_BYTE_SWAP_DEFINED */
#define SSID_MAX_LEN 32
struct wpa_ssid_value {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
};
/* Macros for handling unaligned memory accesses */
@ -379,6 +386,10 @@ int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
size_t len);
int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
u8 rssi_to_rcpi(int rssi);
int os_time_expired(struct os_time *now,
struct os_time *ts,
os_time_t timeout_secs);
#ifdef CONFIG_NATIVE_WINDOWS
void wpa_unicode2ascii_inplace(TCHAR *str);

View File

@ -12,8 +12,8 @@
* See README and COPYING for more details.
*/
#ifndef LIST_H
#define LIST_H
#ifndef DL_LIST_H
#define DL_LIST_H
#include <stddef.h>
@ -100,4 +100,4 @@ static inline unsigned int dl_list_len(struct dl_list *list)
#define DEFINE_DL_LIST(name) \
struct dl_list name = { &(name), &(name) }
#endif /* LIST_H */
#endif /* DL_LIST_H */

View File

@ -91,22 +91,22 @@ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
size_t i;
char output[50];
if (level < MSG_MSGDUMP)
if (level < MSG_ERROR)
return;
wpa_printf(MSG_DEBUG, "%s - hexdump(len=%lu):", title, (unsigned long) len);
wpa_printf(level, "%s - hexdump(len=%lu):", title, (unsigned long) len);
if (buf == NULL) {
wpa_printf(MSG_DEBUG, " [NULL]");
wpa_printf(level, " [NULL]");
} else {
for (i = 0; i < len / 16; i++) {
_wpa_snprintf_hex(output, 50, buf + i * 16, 16, 0, 1);
wpa_printf(MSG_DEBUG, "%s", output);
wpa_printf(level, "%s", output);
}
if (len % 16) {
int bytes_printed = (len / 16) * 16;
_wpa_snprintf_hex(output, 50, buf + bytes_printed,
len - bytes_printed, 0, 1);
wpa_printf(MSG_DEBUG, "%s", output);
wpa_printf(level, "%s", output);
}
}
#endif

View File

@ -0,0 +1,9 @@
# (Automatically converted from project Makefile by convert_to_cmake.py.)
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(roaming)

View File

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := roaminng
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,58 @@
# Roaming Example
This example demonstrate a roaming example using 11k and 11v APIs.
All the logs are taken after debug enabled:
Beacon request:
I (11125) wpa: scan issued at time=7579442263
I (11125) wpa: BSS: Add new id 1 BSSID b6:fb:e4:4d:6e:22 SSID 'ESPTest' chan 1
I (11135) wpa: BSS: Add new id 2 BSSID d6:fb:e4:4d:6e:22 SSID 'ESPDedicated' chan 1
I (11165) wpa: BSS: Add new id 3 BSSID c6:fb:e4:4d:6e:22 SSID 'ESPGuestNetwork' chan 1
I (11185) wpa: BSS: Add new id 4 BSSID b4:fb:e4:4d:6e:22 SSID 'ESPIndia' chan 1
I (11245) wpa: BSS: Add new id 5 BSSID b8:27:eb:3b:4a:59 SSID 'wpa2_enterprise' chan 6
I (11365) wpa: BSS: Add new id 6 BSSID 38:94:ed:34:07:66 SSID 'Nighthawk' chan 6
I (11365) wpa: BSS: Add new id 7 BSSID 10:da:43:dc:99:20 SSID 'NETGEAR40' chan 6
I (12435) wpa: BSS: Add new id 8 BSSID c6:fb:e4:4d:7c:89 SSID 'ESPGuestNetwork' chan 11
I (12445) wpa: BSS: Add new id 9 BSSID d6:fb:e4:4d:7c:89 SSID 'ESPDedicated' chan 11
I (12455) wpa: BSS: Add new id 10 BSSID b6:fb:e4:4d:7c:89 SSID 'ESPTest' chan 11
I (12485) wpa: BSS: Add new id 11 BSSID b4:fb:e4:4d:7c:89 SSID 'ESPIndia' chan 11
I (12825) wpa: scan done received
I (12845) wpa: action frame sent
I (12845) wpa: action frame sent
Sta's moving from one AP to another on BTM request:
I (379479) wpa: WNM: RX action 7 from 50:3e:aa:26:35:42
I (379479) wpa: WNM: BSS Transition Management Request: dialog_token=1 request_mode=0x1 disassoc_timer=0 validity_interval=1
I (379489) wpa: WNM: Neighbor report tag 52
I (379499) wpa: WNM: Subelement id=3 len=1
I (379499) wpa: WNM: BSS Transition Candidate List
I (379509) wpa: 0: b4:e6:2d:eb:1d:76 info=0x0 op_class=81 chan=1 phy=7 pref=255
I (379519) wpa: WNM: Candidate list valid for 102 ms
I (379519) wpa: WNM: Scan only for a specific BSSID since there is only a single candidate b4:e6:2d:eb:1d:76
I (379539) wpa: scan issued at time=9979439869
I (379539) wpa: BSS: Add new id 12 BSSID b4:e6:2d:eb:1d:76 SSID 'roaming_test123' chan 6
I (381979) wpa: scan done received
I (381979) wpa: WNM: Process scan results for BSS Transition Management
I (381979) wpa: WNM: Found an acceptable preferred transition candidate BSS b4:e6:2d:eb:1d:76 (RSSI -7)
I (381989) wpa: WNM: Transition to BSS b4:e6:2d:eb:1d:76 based on BSS Transition Management Request after_new_scan=1)
I (381999) wpa: WNM: Sending successful BSS Transition Management Response
I (382009) wpa: WNM: Send BSS Transition Management Response to 50:3e:aa:26:35:42 dialog_token=1 status=0 reason=0 delay=0
I (382019) wpa: action frame sent
I (382029) wpa: WNM: Issuing connect
I (382029) wifi:state: run -> init (0)
I (382029) wifi:pm stop, total sleep time: 102136757 us / 109258805 us
I (382039) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (382769) wifi:new:<6,1>, old:<6,0>, ap:<255,255>, sta:<6,1>, prof:1
I (384689) wifi:state: init -> auth (b0)
I (384699) wifi:state: auth -> assoc (0)
I (384709) wifi:state: assoc -> run (10)
I (384729) wifi:connected with roaming_test123, aid = 1, channel 6, 40U, bssid = b4:e6:2d:eb:1d:76
I (384729) wifi:security: WPA2-PSK, phy: bgn, rssi: -7
I (384729) wifi:pm start, type: 1
I (384789) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (385609) esp_netif_handlers: sta ip: 192.168.4.2, mask: 255.255.255.0, gw: 192.168.4.1

View File

@ -0,0 +1,3 @@
# Embed CA, certificate & key directly into binary
idf_component_register(SRCS "roaming_example.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,21 @@
menu "Example Configuration"
config EXAMPLE_WIFI_SSID
string "WiFi SSID"
default "roaming_test"
help
SSID (network name) for the example to connect to.
config EXAMPLE_WIFI_PASSWORD
string "WiFi Password"
default "password"
help
WiFi password (WPA or WPA2) for the example to use.
config EXAMPLE_WIFI_RSSI_THRESHOLD
int "WiFi RSSI threshold to trigger roaming"
default 0
help
WiFi RSSI threshold to trigger roaming value in dbm (-100 to 0). 0 denotes feature is disabled.
endmenu

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,364 @@
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wnm.h"
#include "esp_rrm.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
/* Configuration */
#define EXAMPLE_WIFI_SSID CONFIG_EXAMPLE_WIFI_SSID
#define EXAMPLE_WIFI_PASSWORD CONFIG_EXAMPLE_WIFI_PASSWORD
#define EXAMPLE_WIFI_RSSI_THRESHOLD CONFIG_EXAMPLE_WIFI_RSSI_THRESHOLD
/* rrm ctx */
int rrm_ctx = 0;
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* esp netif object representing the WIFI station */
static esp_netif_t *sta_netif = NULL;
static const char *TAG = "roaming_example";
static inline uint32_t WPA_GET_LE32(const uint8_t *a)
{
return ((uint32_t) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
}
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
wifi_event_sta_disconnected_t *disconn = event_data;
if (disconn->reason == WIFI_REASON_ROAMING) {
ESP_LOGI(TAG, "station roaming, do nothing");
} else {
esp_wifi_connect();
}
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
#if EXAMPLE_WIFI_RSSI_THRESHOLD
if (EXAMPLE_WIFI_RSSI_THRESHOLD) {
ESP_LOGI(TAG, "setting rssi threshold as %d\n", EXAMPLE_WIFI_RSSI_THRESHOLD);
esp_wifi_set_rssi_threshold(EXAMPLE_WIFI_RSSI_THRESHOLD);
}
#endif
}
}
#ifndef WLAN_EID_MEASURE_REPORT
#define WLAN_EID_MEASURE_REPORT 39
#endif
#ifndef MEASURE_TYPE_LCI
#define MEASURE_TYPE_LCI 9
#endif
#ifndef MEASURE_TYPE_LOCATION_CIVIC
#define MEASURE_TYPE_LOCATION_CIVIC 11
#endif
#ifndef WLAN_EID_NEIGHBOR_REPORT
#define WLAN_EID_NEIGHBOR_REPORT 52
#endif
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
#define MAX_NEIGHBOR_LEN 512
#if 1
static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
{
size_t len = 0;
const uint8_t *data;
int ret = 0;
/*
* Neighbor Report element (IEEE P802.11-REVmc/D5.0)
* BSSID[6]
* BSSID Information[4]
* Operating Class[1]
* Channel Number[1]
* PHY Type[1]
* Optional Subelements[variable]
*/
#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
if (!report || report_len == 0) {
ESP_LOGI(TAG, "RRM neighbor report is not valid");
return NULL;
}
char *buf = calloc(1, MAX_NEIGHBOR_LEN);
data = report;
while (report_len >= 2 + NR_IE_MIN_LEN) {
const uint8_t *nr;
char lci[256 * 2 + 1];
char civic[256 * 2 + 1];
uint8_t nr_len = data[1];
const uint8_t *pos = data, *end;
if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
nr_len < NR_IE_MIN_LEN) {
ESP_LOGI(TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%u",
data[0], nr_len);
ret = -1;
goto cleanup;
}
if (2U + nr_len > report_len) {
ESP_LOGI(TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
data[0], report_len, nr_len);
ret = -1;
goto cleanup;
}
pos += 2;
end = pos + nr_len;
nr = pos;
pos += NR_IE_MIN_LEN;
lci[0] = '\0';
civic[0] = '\0';
while (end - pos > 2) {
uint8_t s_id, s_len;
s_id = *pos++;
s_len = *pos++;
if (s_len > end - pos) {
ret = -1;
goto cleanup;
}
if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) {
/* Measurement Token[1] */
/* Measurement Report Mode[1] */
/* Measurement Type[1] */
/* Measurement Report[variable] */
switch (pos[2]) {
case MEASURE_TYPE_LCI:
if (lci[0])
break;
memcpy(lci, pos, s_len);
break;
case MEASURE_TYPE_LOCATION_CIVIC:
if (civic[0])
break;
memcpy(civic, pos, s_len);
break;
}
}
pos += s_len;
}
ESP_LOGI(TAG, "RMM neigbor report bssid=" MACSTR
" info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s",
MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
nr[ETH_ALEN + 6],
lci[0] ? " lci=" : "", lci,
civic[0] ? " civic=" : "", civic);
/* neighbor start */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
/* bssid */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr));
/* , */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* bssid info */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04x", WPA_GET_LE32(nr + ETH_ALEN));
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* operating class */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]);
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* channel number */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]);
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* phy type */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]);
/* optional elements, skip */
data = end;
report_len -= 2 + nr_len;
}
cleanup:
if (ret < 0) {
free(buf);
buf = NULL;
}
return buf;
}
#else
/* Sample API to create neighbor list */
char * get_tmp_neighbor_list(uint8_t *report, size_t report_len)
{
#define MAC1 "00:01:02:03:04:05"
#define MAC2 "00:02:03:04:05:06"
char * buf = calloc(1, MAX_NEIGHBOR_LEN);
size_t len = 0;
char *pos;
if (!buf)
return NULL;
pos = buf;
/* create two temp neighbors */
/* format for neighbor list : neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff */
/* neighbor1 start */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
/* bssid */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, MAC1);
/* , */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* bssid info */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "0x0000");
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* operating class */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "81");
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* channel number */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "6");
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* phy type */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "7");
/* optional elements, skip */
/* neighbor2 start */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
/* bssid */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, MAC2);
/* , */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* bssid info */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "0x0000");
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* operating class */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "81");
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* channel number */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "6");
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
/* phy type */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "7");
/* optional elements, skip */
len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, " ");
#undef MAC1
#undef MAC2
return buf;
}
#endif
void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len)
{
int *val = ctx;
uint8_t *pos = (uint8_t *)report;
int cand_list = 0;
if (!report) {
ESP_LOGE(TAG, "report is null");
return;
}
if (*val != rrm_ctx) {
ESP_LOGE(TAG, "rrm_ctx value didn't match, not initiated by us");
return;
}
/* dump report info */
ESP_LOGI(TAG, "rrm: neighbor report len=%d", report_len);
ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_INFO);
/* create neighbor list */
char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
/* In case neighbor list is not present issue a scan and get the list from that */
if (!neighbor_list) {
/* issue scan */
wifi_scan_config_t params;
memset(&params, 0, sizeof(wifi_scan_config_t));
if (esp_wifi_scan_start(&params, true) < 0) {
goto cleanup;
}
/* cleanup from net802.11 */
uint16_t number = 1;
wifi_ap_record_t ap_records;
esp_wifi_scan_get_ap_records(&number, &ap_records);
cand_list = 1;
}
/* send AP btm query, this will cause STA to roam as well */
esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list);
cleanup:
if (neighbor_list)
free(neighbor_list);
}
#if EXAMPLE_WIFI_RSSI_THRESHOLD
static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
wifi_event_bss_rssi_low_t *event = event_data;
ESP_LOGI(TAG, "%s:bss rssi is=%d", __func__, event->rssi);
/* Lets check channel conditions */
rrm_ctx++;
if (esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx) < 0) {
/* failed to send neighbor report request */
ESP_LOGI(TAG, "failed to send neighbor report request");
if (esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0) < 0) {
ESP_LOGI(TAG, "failed to send btm query");
}
}
}
#endif
static void initialise_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
#if EXAMPLE_WIFI_RSSI_THRESHOLD
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
&esp_bss_rssi_low_handler, NULL));
#endif
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASSWORD,
.rm_enabled =1,
.btm_enabled =1,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
void app_main(void)
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
}

View File

@ -0,0 +1,2 @@
CONFIG_WPA_11KV_SUPPORT=y
CONFIG_WPA_SCAN_CACHE=y