diff --git a/components/esp_event/event_send.c b/components/esp_event/event_send.c index 46ef4792c1..27e5731ca1 100644 --- a/components/esp_event/event_send.c +++ b/components/esp_event/event_send.c @@ -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; diff --git a/components/esp_event/include/esp_event_legacy.h b/components/esp_event/include/esp_event_legacy.h index a2af69121f..e4f579a0a1 100644 --- a/components/esp_event/include/esp_event_legacy.h +++ b/components/esp_event/include/esp_event_legacy.h @@ -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 */ diff --git a/components/esp_wifi/include/esp_wifi.h b/components/esp_wifi/include/esp_wifi.h index 4dad75b9a8..21a0c7301a 100644 --- a/components/esp_wifi/include/esp_wifi.h +++ b/components/esp_wifi/include/esp_wifi.h @@ -229,7 +229,7 @@ extern uint64_t g_wifi_feature_caps; * @attention 2. Always use WIFI_INIT_CONFIG_DEFAULT macro to init the config to default values, this can * guarantee all the fields got correct value when more fields are added into wifi_init_config_t * in future release. If you want to set your owner initial values, overwrite the default values - * which are set by WIFI_INIT_CONFIG_DEFAULT, please be notified that the field 'magic' of + * which are set by WIFI_INIT_CONFIG_DEFAULT, please be notified that the field 'magic' of * wifi_init_config_t should always be WIFI_INIT_CONFIG_MAGIC! * * @param config pointer to WiFi init configuration structure; can point to a temporary variable. @@ -247,7 +247,7 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config); * * @attention 1. This API should be called if you want to remove WiFi driver from the system * - * @return + * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init */ @@ -332,11 +332,11 @@ esp_err_t esp_wifi_restore(void); * @attention 3. The scanning triggered by esp_wifi_start_scan() will not be effective until connection between ESP32 and the AP is established. * If ESP32 is scanning and connecting at the same time, ESP32 will abort scanning and return a warning message and error * number ESP_ERR_WIFI_STATE. - * If you want to do reconnection after ESP32 received disconnect event, remember to add the maximum retry time, otherwise the called + * If you want to do reconnection after ESP32 received disconnect event, remember to add the maximum retry time, otherwise the called * scan will not work. This is especially true when the AP doesn't exist, and you still try reconnection after ESP32 received disconnect * event with the reason code WIFI_REASON_NO_AP_FOUND. - * - * @return + * + * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init * - ESP_ERR_WIFI_NOT_STARTED: WiFi is not started by esp_wifi_start @@ -431,7 +431,7 @@ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); /** * @brief Get AP list found in last scan * - * @param[inout] number As input param, it stores max AP number ap_records can hold. + * @param[inout] number As input param, it stores max AP number ap_records can hold. * As output param, it receives the actual AP number this API returns. * @param ap_records wifi_ap_record_t array to hold the found APs * @@ -456,7 +456,7 @@ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_re * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_CONN: The station interface don't initialized - * - ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status + * - ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status */ esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); @@ -554,7 +554,7 @@ esp_err_t esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw); * @attention 1. This API should be called after esp_wifi_start() * @attention 2. When ESP32 is in STA mode, this API should not be called when STA is scanning or connecting to an external AP * @attention 3. When ESP32 is in softAP mode, this API should not be called when softAP has connected to external STAs - * @attention 4. When ESP32 is in STA+softAP mode, this API should not be called when in the scenarios described above + * @attention 4. When ESP32 is in STA+softAP mode, this API should not be called when in the scenarios described above * * @param primary for HT20, primary is the channel number, for HT40, primary is the primary channel * @param second for HT20, second is ignored, for HT40, second is the second channel @@ -596,7 +596,7 @@ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); * @attention 4. When the country info is changed because of configuration or because the station connects to a different * external AP, the country IE in probe response/beacon of the soft-AP is changed also. * @attention 5. The country configuration is stored into flash. - * @attention 6. This API doesn't validate the per-country rules, it's up to the user to fill in all fields according to + * @attention 6. This API doesn't validate the per-country rules, it's up to the user to fill in all fields according to * local regulations. * @attention 7. When this API is called, the PHY init data will switch to the PHY init data type corresponding to the * country info. @@ -660,7 +660,7 @@ esp_err_t esp_wifi_set_mac(wifi_interface_t ifx, const uint8_t mac[6]); esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]); /** - * @brief The RX callback function in the promiscuous mode. + * @brief The RX callback function in the promiscuous mode. * Each time a packet is received, the callback function will be called. * * @param buf Data received. Type of data in buffer (wifi_promiscuous_pkt_t or wifi_pkt_rx_ctrl_t) indicated by 'type' parameter. @@ -972,14 +972,14 @@ esp_err_t esp_wifi_get_event_mask(uint32_t *mask); * * @attention Currently only support for sending beacon/probe request/probe response/action and non-QoS * data frame - * + * * @param ifx interface if the Wi-Fi mode is Station, the ifx should be WIFI_IF_STA. If the Wi-Fi - * mode is SoftAP, the ifx should be WIFI_IF_AP. If the Wi-Fi mode is Station+SoftAP, the + * mode is SoftAP, the ifx should be WIFI_IF_AP. If the Wi-Fi mode is Station+SoftAP, the * ifx should be WIFI_IF_STA or WIFI_IF_AP. If the ifx is wrong, the API returns ESP_ERR_WIFI_IF. * @param buffer raw ieee80211 buffer * @param len the length of raw buffer, the len must be <= 1500 Bytes and >= 24 Bytes - * @param en_sys_seq indicate whether use the internal sequence number. If en_sys_seq is false, the - * sequence in raw buffer is unchanged, otherwise it will be overwritten by WiFi driver with + * @param en_sys_seq indicate whether use the internal sequence number. If en_sys_seq is false, the + * sequence in raw buffer is unchanged, otherwise it will be overwritten by WiFi driver with * the system sequence number. * Generally, if esp_wifi_80211_tx is called before the Wi-Fi connection has been set up, both * en_sys_seq==true and en_sys_seq==false are fine. However, if the API is called after the Wi-Fi @@ -995,12 +995,12 @@ esp_err_t esp_wifi_get_event_mask(uint32_t *mask); esp_err_t esp_wifi_80211_tx(wifi_interface_t ifx, const void *buffer, int len, bool en_sys_seq); /** - * @brief The RX callback function of Channel State Information(CSI) data. + * @brief The RX callback function of Channel State Information(CSI) data. * * Each time a CSI data is received, the callback function will be called. * - * @param ctx context argument, passed to esp_wifi_set_csi_rx_cb() when registering callback function. - * @param data CSI data received. The memory that it points to will be deallocated after callback function returns. + * @param ctx context argument, passed to esp_wifi_set_csi_rx_cb() when registering callback function. + * @param data CSI data received. The memory that it points to will be deallocated after callback function returns. * */ typedef void (* wifi_csi_cb_t)(void *ctx, wifi_csi_info_t *data); @@ -1025,7 +1025,7 @@ esp_err_t esp_wifi_set_csi_rx_cb(wifi_csi_cb_t cb, void *ctx); * @brief Set CSI data configuration * * @param config configuration - * + * * return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init @@ -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 diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index 665cb3affc..a3ea1fa07b 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -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) diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 57db56bfea..34f1262375 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 57db56bfeae0423a446f4d72f166fe044ee8c3ab +Subproject commit 34f1262375795c34dca8520ffd042f0e104843b7 diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index e02604b33c..1dfdaa78c9 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -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) diff --git a/components/wpa_supplicant/Kconfig b/components/wpa_supplicant/Kconfig index 591325d844..0828603eaa 100644 --- a/components/wpa_supplicant/Kconfig +++ b/components/wpa_supplicant/Kconfig @@ -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 diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index 7a178d8791..e6b473c465 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -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 diff --git a/components/wpa_supplicant/include/esp_supplicant/esp_rrm.h b/components/wpa_supplicant/include/esp_supplicant/esp_rrm.h new file mode 100644 index 0000000000..1ffcf18047 --- /dev/null +++ b/components/wpa_supplicant/include/esp_supplicant/esp_rrm.h @@ -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 +#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 diff --git a/components/wpa_supplicant/include/esp_supplicant/esp_wnm.h b/components/wpa_supplicant/include/esp_supplicant/esp_wnm.h new file mode 100644 index 0000000000..a1dcfb655c --- /dev/null +++ b/components/wpa_supplicant/include/esp_supplicant/esp_wnm.h @@ -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 +#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 diff --git a/components/wpa_supplicant/include/utils/wpa_debug.h b/components/wpa_supplicant/include/utils/wpa_debug.h index ea19c4d319..124786923a 100644 --- a/components/wpa_supplicant/include/utils/wpa_debug.h +++ b/components/wpa_supplicant/include/utils/wpa_debug.h @@ -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 diff --git a/components/wpa_supplicant/port/include/os.h b/components/wpa_supplicant/port/include/os.h index f39fc59984..66eea39d85 100644 --- a/components/wpa_supplicant/port/include/os.h +++ b/components/wpa_supplicant/port/include/os.h @@ -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 */ diff --git a/components/wpa_supplicant/port/include/supplicant_opt.h b/components/wpa_supplicant/port/include/supplicant_opt.h index a3d4c66620..b70ffd4c6d 100644 --- a/components/wpa_supplicant/port/include/supplicant_opt.h +++ b/components/wpa_supplicant/port/include/supplicant_opt.h @@ -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 */ diff --git a/components/wpa_supplicant/port/os_xtensa.c b/components/wpa_supplicant/port/os_xtensa.c index 55db624f91..48df398c4e 100644 --- a/components/wpa_supplicant/port/os_xtensa.c +++ b/components/wpa_supplicant/port/os_xtensa.c @@ -25,6 +25,7 @@ #include "os.h" #include #include +#include #include #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); + } +} diff --git a/components/wpa_supplicant/src/ap/wpa_auth.c b/components/wpa_supplicant/src/ap/wpa_auth.c index 01bd7c7606..c05db59efe 100644 --- a/components/wpa_supplicant/src/ap/wpa_auth.c +++ b/components/wpa_supplicant/src/ap/wpa_auth.c @@ -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, diff --git a/components/wpa_supplicant/src/common/bss.c b/components/wpa_supplicant/src/common/bss.c new file mode 100644 index 0000000000..7891d08c71 --- /dev/null +++ b/components/wpa_supplicant/src/common/bss.c @@ -0,0 +1,476 @@ +/* + * BSS table + * Copyright (c) 2009-2019, Jouni Malinen + * + * 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); +} diff --git a/components/wpa_supplicant/src/common/bss.h b/components/wpa_supplicant/src/common/bss.h new file mode 100644 index 0000000000..c34941a7b0 --- /dev/null +++ b/components/wpa_supplicant/src/common/bss.h @@ -0,0 +1,86 @@ +/* + * BSS table + * Copyright (c) 2009-2019, Jouni Malinen + * + * 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 */ diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.c b/components/wpa_supplicant/src/common/ieee802_11_common.c new file mode 100644 index 0000000000..ca159c1bed --- /dev/null +++ b/components/wpa_supplicant/src/common/ieee802_11_common.c @@ -0,0 +1,198 @@ +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2019, Jouni Malinen + * + * 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=,,, + * ,[,] + */ + 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; +} diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.h b/components/wpa_supplicant/src/common/ieee802_11_common.h new file mode 100644 index 0000000000..80dd04cb41 --- /dev/null +++ b/components/wpa_supplicant/src/common/ieee802_11_common.h @@ -0,0 +1,42 @@ +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2019, Jouni Malinen + * + * 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 */ diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index 9260d22f0b..32e2ca92f7 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_defs.h +++ b/components/wpa_supplicant/src/common/ieee802_11_defs.h @@ -1,21 +1,17 @@ /* * IEEE 802.11 Frame type definitions - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * 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 + /* 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 */ diff --git a/components/wpa_supplicant/src/common/rrm.c b/components/wpa_supplicant/src/common/rrm.c new file mode 100644 index 0000000000..ca1c21c17a --- /dev/null +++ b/components/wpa_supplicant/src/common/rrm.c @@ -0,0 +1,1162 @@ +/* + * wpa_supplicant - Radio Measurements + * Copyright (c) 2003-2016, Jouni Malinen + * + * 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 "utils/bitfield.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "rrm.h" +#include "scan.h" +#include +#include "esp_supplicant/esp_common_i.h" + +static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) +{ + struct rrm_data *rrm = data; + + if (!rrm->notify_neighbor_rep) { + wpa_printf(MSG_ERROR, + "RRM: Unexpected neighbor report timeout"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); + rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL, 0); + + rrm->notify_neighbor_rep = NULL; + rrm->neighbor_rep_cb_ctx = NULL; +} + + +/* + * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant + * @wpa_s: Pointer to wpa_supplicant + */ +void wpas_rrm_reset(struct wpa_supplicant *wpa_s) +{ + wpa_s->rrm.rrm_used = 0; + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + if (wpa_s->rrm.notify_neighbor_rep) + wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL); + wpa_s->rrm.next_neighbor_rep_token = 1; + wpas_clear_beacon_rep_data(wpa_s); +} + + +/* + * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report + * @wpa_s: Pointer to wpa_supplicant + * @report: Neighbor report buffer, prefixed by a 1-byte dialog token + * @report_len: Length of neighbor report buffer + */ +void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *report, size_t report_len) +{ + + wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); + if (report_len < 1) + return; + + if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) { + wpa_printf(MSG_DEBUG, + "RRM: Discarding neighbor report with token %d (expected %d)", + report[0], wpa_s->rrm.next_neighbor_rep_token - 1); + return; + } + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + + if (!wpa_s->rrm.notify_neighbor_rep) { + wpa_msg(wpa_s, MSG_INFO, "RRM: Unexpected neighbor report"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", + report[0]); + wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx, + report, report_len); + wpa_s->rrm.notify_neighbor_rep = NULL; + wpa_s->rrm.neighbor_rep_cb_ctx = NULL; +} + + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) +/* Workaround different, undefined for Windows, error codes used here */ +#ifndef ENOTCONN +#define ENOTCONN -1 +#endif +#ifndef EOPNOTSUPP +#define EOPNOTSUPP -1 +#endif +#ifndef ECANCELED +#define ECANCELED -1 +#endif +#endif + +/* Measurement Request element + Location Subject + Maximum Age subelement */ +#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) +/* Measurement Request element + Location Civic Request */ +#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) + + +/** + * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP + * @wpa_s: Pointer to wpa_supplicant + * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE + * is sent in the request. + * @lci: if set, neighbor request will include LCI request + * @civic: if set, neighbor request will include civic location request + * @cb: Callback function to be called once the requested report arrives, or + * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. + * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's + * the requester's responsibility to free it. + * In the latter case NULL will be sent in 'neighbor_rep'. + * @cb_ctx: Context value to send the callback function + * Returns: 0 in case of success, negative error code otherwise + * + * In case there is a previous request which has not been answered yet, the + * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT. + * Request must contain a callback function. + */ +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) +{ + struct wpabuf *buf; + + if (!(wpa_s->rrm_ie[0] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "RRM: No network support for Neighbor Report."); + return -EOPNOTSUPP; + } + + /* Refuse if there's a live request */ + if (wpa_s->rrm.notify_neighbor_rep) { + wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL); +#if 0 + wpa_dbg(wpa_s, MSG_DEBUG, + "RRM: Currently handling previous Neighbor Report."); + return -EBUSY; +#endif + } + + /* 3 = action category + action code + dialog token */ + buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) + + (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + + (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0)); + if (buf == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, + "RRM: Failed to allocate Neighbor Report Request"); + return -ENOMEM; + } + + wpa_dbg(wpa_s, MSG_DEBUG, + "RRM: Neighbor report request (for %s), token=%d", + (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), + wpa_s->rrm.next_neighbor_rep_token); + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); + wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); + if (ssid) { + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, ssid->ssid_len); + wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); + } + + if (lci) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 1); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + /* Optional Subelements */ + /* + * IEEE P802.11-REVmc/D5.0 Figure 9-170 + * The Maximum Age subelement is required, otherwise the AP can + * send only data that was determined after receiving the + * request. Setting it here to unlimited age. + */ + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + } + + if (civic) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 2); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + /* Measurement Type */ + wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: + * Location Civic request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ + /* Location Service Interval Units: Seconds */ + wpabuf_put_u8(buf, 0); + /* Location Service Interval: 0 - Only one report is requested + */ + wpabuf_put_le16(buf, 0); + /* No optional subelements */ + } + + wpa_s->rrm.next_neighbor_rep_token++; + + if (wpa_drv_send_action(wpa_s, 0, 0, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "RRM: Failed to send Neighbor Report Request"); + wpabuf_free(buf); + return -ECANCELED; + } + + wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx; + wpa_s->rrm.notify_neighbor_rep = cb; + eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0, + wpas_rrm_neighbor_rep_timeout_handler, + &wpa_s->rrm, NULL); + + wpabuf_free(buf); + return 0; +} + + +static int wpas_rrm_report_elem(struct wpabuf **buf, u8 token, u8 mode, u8 type, + const u8 *data, size_t data_len) +{ + if (wpabuf_resize(buf, 5 + data_len)) + return -1; + + wpabuf_put_u8(*buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(*buf, 3 + data_len); + wpabuf_put_u8(*buf, token); + wpabuf_put_u8(*buf, mode); + wpabuf_put_u8(*buf, type); + + if (data_len) + wpabuf_put_data(*buf, data, data_len); + + return 0; +} + +static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s, + const u8 *data, size_t len) +{ + struct wpabuf *report = wpabuf_alloc(len + 3); + + if (!report) + return; + + wpabuf_put_u8(report, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(report, WLAN_RRM_RADIO_MEASUREMENT_REPORT); + wpabuf_put_u8(report, wpa_s->rrm.token); + + wpabuf_put_data(report, data, len); + + if (wpa_drv_send_action(wpa_s, 0, 0, + wpabuf_head(report), wpabuf_len(report), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Radio measurement report failed: Sending Action frame failed"); + } + + wpabuf_free(report); +} + + +static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len) +{ + struct rrm_measurement_report_element *msr_rep; + u8 *end = pos + len; + u8 *msr_rep_end; + struct rrm_measurement_beacon_report *rep = NULL; + u8 *subelem; + + /* Find the last beacon report element */ + while (end - pos >= (int) sizeof(*msr_rep)) { + msr_rep = (struct rrm_measurement_report_element *) pos; + msr_rep_end = pos + msr_rep->len + 2; + + if (msr_rep->eid != WLAN_EID_MEASURE_REPORT || + msr_rep_end > end) { + /* Should not happen. This indicates a bug. */ + wpa_printf(MSG_ERROR, + "RRM: non-measurement report element in measurement report frame"); + return -1; + } + + if (msr_rep->type == MEASURE_TYPE_BEACON) + rep = (struct rrm_measurement_beacon_report *) + msr_rep->variable; + + pos += pos[1] + 2; + } + + if (!rep) + return 0; + + subelem = rep->variable; + while (subelem + 2 < msr_rep_end && + subelem[0] != WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION) + subelem += 2 + subelem[1]; + + if (subelem + 2 < msr_rep_end && + subelem[0] == WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION && + subelem[1] == 1 && + subelem + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end) + subelem[2] = 1; + + return 0; +} + + +static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s, + struct wpabuf *buf) +{ + int len = wpabuf_len(buf); + u8 *pos = wpabuf_mhead_u8(buf), *next = pos; + +#define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3) + + while (len) { + int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len; + + if (send_len == len) + wpas_rrm_beacon_rep_update_last_frame(pos, len); + + if (send_len == len || + (send_len + next[1] + 2) > MPDU_REPORT_LEN) { + wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len); + len -= send_len; + pos = next; + } + + if (len) + next += next[1] + 2; + } +#undef MPDU_REPORT_LEN +} + +static int wpas_get_op_chan_phy(int channel, const u8 *ies, size_t ies_len, + u8 *op_class, u8 *chan, u8 *phy_type) +{ + const u8 *ie; + int sec_chan = 0; + struct ieee80211_ht_operation *ht_oper = NULL; + + ie = get_ie(ies, ies_len, 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(channel, sec_chan); + + *phy_type = (sec_chan != 0) ? PHY_TYPE_HT : PHY_TYPE_ERP; + + return 0; +} + + +static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, + enum beacon_report_detail detail, + struct wpa_bss *bss, u8 *buf, + size_t buf_len, u8 **ies_buf, + size_t *ie_len, int add_fixed) +{ + u8 *ies = *ies_buf; + size_t ies_len = *ie_len; + u8 *pos = buf; + int rem_len; + + rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) - + sizeof(struct rrm_measurement_report_element) - 2 - + REPORTED_FRAME_BODY_SUBELEM_LEN; + + if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Invalid reporting detail: %d", + detail); + return -1; + } + + if (detail == BEACON_REPORT_DETAIL_NONE) + return 0; + + /* + * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) + + * beacon interval(2) + capabilities(2) = 14 bytes + */ + if (add_fixed && buf_len < 14) + return -1; + + *pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY; + /* The length will be filled later */ + pos++; + + if (add_fixed) { + WPA_PUT_LE64(pos, bss->tsf); + pos += sizeof(bss->tsf); + WPA_PUT_LE16(pos, bss->beacon_int); + pos += 2; + WPA_PUT_LE16(pos, bss->caps); + pos += 2; + } + + rem_len -= pos - buf; + + /* + * According to IEEE Std 802.11-2016, 9.4.2.22.7, if the reported frame + * body subelement causes the element to exceed the maximum element + * size, the subelement is truncated so that the last IE is a complete + * IE. So even when required to report all IEs, add elements one after + * the other and stop once there is no more room in the measurement + * element. + */ + while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) { + if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS || + (eids && bitfield_is_set(eids, ies[0]))) { + u8 elen = ies[1]; + + if (2 + elen > buf + buf_len - pos || + 2 + elen > rem_len) + break; + + *pos++ = ies[0]; + *pos++ = elen; + os_memcpy(pos, ies + 2, elen); + pos += elen; + rem_len -= 2 + elen; + } + + ies_len -= 2 + ies[1]; + ies += 2 + ies[1]; + } + + *ie_len = ies_len; + *ies_buf = ies; + + /* Now the length is known */ + buf[1] = pos - buf - 2; + return pos - buf; +} + + +static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data, + struct wpa_bss *bss, + struct wpabuf **wpa_buf, + struct rrm_measurement_beacon_report *rep, + u8 **ie, size_t *ie_len, u8 idx) +{ + int ret; + u8 *buf, *pos; + u32 subelems_len = REPORTED_FRAME_BODY_SUBELEM_LEN + + (data->last_indication ? + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN : 0); + + /* Maximum element length: Beacon Report element + Reported Frame Body + * subelement + all IEs of the reported Beacon frame + Reported Frame + * Body Fragment ID subelement */ + buf = os_malloc(sizeof(*rep) + 14 + *ie_len + subelems_len); + if (!buf) + return -1; + + os_memcpy(buf, rep, sizeof(*rep)); + + ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, + bss, buf + sizeof(*rep), + 14 + *ie_len, ie, ie_len, + idx == 0); + if (ret < 0) + goto out; + + pos = buf + ret + sizeof(*rep); + pos[0] = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID; + pos[1] = 2; + + /* + * Only one Beacon Report Measurement is supported at a time, so + * the Beacon Report ID can always be set to 1. + */ + pos[2] = 1; + + /* Fragment ID Number (bits 0..6) and More Frame Body Fragments (bit 7) + */ + pos[3] = idx; + if (data->report_detail != BEACON_REPORT_DETAIL_NONE && *ie_len) + pos[3] |= REPORTED_FRAME_BODY_MORE_FRAGMENTS; + else + pos[3] &= ~REPORTED_FRAME_BODY_MORE_FRAGMENTS; + + pos += REPORTED_FRAME_BODY_SUBELEM_LEN; + + if (data->last_indication) { + pos[0] = WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION; + pos[1] = 1; + + /* This field will be updated later if this is the last frame */ + pos[2] = 0; + } + + ret = wpas_rrm_report_elem(wpa_buf, data->token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, buf, + ret + sizeof(*rep) + subelems_len); +out: + os_free(buf); + return ret; +} + + +static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s, + struct wpabuf **wpa_buf, struct wpa_bss *bss, + u64 start, u64 parent_tsf) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + u8 *ies = (u8 *) (bss + 1); + u8 *pos = ies; + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + struct rrm_measurement_beacon_report rep; + u8 idx = 0; + + if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 && + os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0) + return 0; + + if (data->ssid_len && + (data->ssid_len != bss->ssid_len || + os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0)) + return 0; + + if (wpas_get_op_chan_phy(bss->channel, ies, ies_len, &rep.op_class, + &rep.channel, &rep.report_info) < 0) + return 0; + + rep.channel = bss->channel; + rep.start_time = host_to_le64(start); + rep.duration = host_to_le16(data->scan_params.duration); + rep.rcpi = rssi_to_rcpi(bss->level); + rep.rsni = 255; /* 255 indicates that RSNI is not available */ + os_memcpy(rep.bssid, bss->bssid, ETH_ALEN); + rep.antenna_id = 0; /* unknown */ + rep.parent_tsf = host_to_le32(parent_tsf); + + do { + int ret; + + ret = wpas_add_beacon_rep_elem(data, bss, wpa_buf, &rep, + &pos, &ies_len, idx++); + if (ret) + return ret; + } while (data->report_detail != BEACON_REPORT_DETAIL_NONE && + ies_len >= 2); + + return 0; +} + + +static int wpas_beacon_rep_no_results(struct wpa_supplicant *wpa_s, + struct wpabuf **buf) +{ + return wpas_rrm_report_elem(buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, NULL, 0); +} + + +static void wpas_beacon_rep_table(struct wpa_supplicant *wpa_s, + struct wpabuf **buf) +{ + size_t i; + + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpas_add_beacon_rep(wpa_s, buf, wpa_s->last_scan_res[i], + 0, 0) < 0) + break; + } + + if (!(*buf)) + wpas_beacon_rep_no_results(wpa_s, buf); + + wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", *buf); +} + + +static void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s) +{ + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr)) { + struct wpabuf *buf = NULL; + + if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_REJECT_REFUSED, + MEASURE_TYPE_BEACON, NULL, 0)) { + wpa_printf(MSG_ERROR, "RRM: Memory allocation failed"); + wpabuf_free(buf); + return; + } + + wpas_rrm_send_msr_report(wpa_s, buf); + wpabuf_free(buf); + } + + wpas_clear_beacon_rep_data(wpa_s); +} + + +static void wpas_rrm_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_driver_scan_params *params = + &wpa_s->beacon_rep_data.scan_params; + u16 prev_duration = params->duration; + + if (!wpa_s->current_bss) + return; + + os_get_reltime(&wpa_s->beacon_rep_scan); + wpa_s->scan_reason = REASON_RRM_BEACON_REPORT; + if (wpa_supplicant_trigger_scan(wpa_s, params) < 0) + wpas_rrm_refuse_request(wpa_s); + params->duration = prev_duration; +} + + +static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s, + struct beacon_rep_data *data, + u8 sid, u8 slen, const u8 *subelem) +{ + u8 report_info, i; + + switch (sid) { + case WLAN_BEACON_REQUEST_SUBELEM_SSID: + if (!slen) { + wpa_printf(MSG_DEBUG, + "SSID subelement with zero length - wildcard SSID"); + break; + } + + if (slen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Invalid SSID subelement length: %u", slen); + return -1; + } + + data->ssid_len = slen; + os_memcpy(data->ssid, subelem, data->ssid_len); + break; + case WLAN_BEACON_REQUEST_SUBELEM_INFO: + if (slen != 2) { + wpa_printf(MSG_DEBUG, + "Invalid reporting information subelement length: %u", + slen); + return -1; + } + + report_info = subelem[0]; + if (report_info != 0) { + wpa_printf(MSG_DEBUG, + "reporting information=%u is not supported", + report_info); + return 0; + } + break; + case WLAN_BEACON_REQUEST_SUBELEM_DETAIL: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Invalid reporting detail subelement length: %u", + slen); + return -1; + } + + data->report_detail = subelem[0]; + if (data->report_detail > + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u", + subelem[0]); + return -1; + } + + break; + case WLAN_BEACON_REQUEST_SUBELEM_REQUEST: + if (data->report_detail != + BEACON_REPORT_DETAIL_REQUESTED_ONLY) { + wpa_printf(MSG_DEBUG, + "Beacon request: request subelement is present but report detail is %u", + data->report_detail); + return -1; + } + + if (!slen) { + wpa_printf(MSG_DEBUG, + "Invalid request subelement length: %u", + slen); + return -1; + } + + if (data->eids) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Request subelement appears more than once"); + return -1; + } + + data->eids = bitfield_alloc(255); + if (!data->eids) { + wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap"); + return -1; + } + + for (i = 0; i < slen; i++) + bitfield_set(data->eids, subelem[i]); + break; + case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL: + /* Skip - it will be processed when freqs are added */ + break; + case WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Beacon request: Invalid last indication request subelement length: %u", + slen); + return -1; + } + + data->last_indication = subelem[0]; + break; + default: + wpa_printf(MSG_DEBUG, + "Beacon request: Unknown subelement id %u", sid); + break; + } + + return 1; +} + + +/** + * Returns 0 if the next element can be processed, 1 if some operation was + * triggered, and -1 if processing failed (i.e., the element is in invalid + * format or an internal error occurred). + */ +static int +wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s, + u8 elem_token, int duration_mandatory, + const struct rrm_measurement_beacon_request *req, + size_t len, struct wpabuf **buf) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + struct wpa_driver_scan_params *params = &data->scan_params; + const u8 *subelems; + size_t elems_len; + u16 rand_interval; + u32 interval_usec; + u32 _rand; + int ret = 0, res; + u8 reject_mode; + + if (len < sizeof(*req)) + return -1; + + if (req->mode != BEACON_REPORT_MODE_PASSIVE && + req->mode != BEACON_REPORT_MODE_ACTIVE && + req->mode != BEACON_REPORT_MODE_TABLE) + return 0; + + subelems = req->variable; + elems_len = len - sizeof(*req); + rand_interval = le_to_host16(req->rand_interval); + + os_memset(data, 0, sizeof(*data)); + + data->token = elem_token; + + /* default reporting detail is all fixed length fields and all + * elements */ + data->report_detail = BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS; + os_memcpy(data->bssid, req->bssid, ETH_ALEN); + + while (elems_len >= 2) { + if (subelems[1] > elems_len - 2) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Truncated subelement"); + ret = -1; + goto out; + } + + res = wpas_rm_handle_beacon_req_subelem( + wpa_s, data, subelems[0], subelems[1], &subelems[2]); + if (res < 0) { + ret = res; + goto out; + } else if (!res) { + reject_mode = MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE; + goto out_reject; + } + + elems_len -= 2 + subelems[1]; + subelems += 2 + subelems[1]; + } + + if (req->mode == BEACON_REPORT_MODE_TABLE) { + wpas_beacon_rep_table(wpa_s, buf); + goto out; + } + params->channel = req->channel; + params->duration = le_to_host16(req->duration); + params->duration_mandatory = duration_mandatory; + if (!params->duration) { + wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0"); + ret = -1; + goto out; + } + + if (data->ssid_len) { + params->ssids[params->num_ssids].ssid = data->ssid; + params->ssids[params->num_ssids++].ssid_len = data->ssid_len; + } + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); + interval_usec = (_rand % (rand_interval + 1)) * 1024; + os_sleep(0, interval_usec); + wpas_rrm_scan_timeout(wpa_s, NULL); + return 1; +out_reject: + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) && + wpas_rrm_report_elem(buf, elem_token, reject_mode, + MEASURE_TYPE_BEACON, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "RRM: Failed to add report element"); + ret = -1; + } +out: + wpas_clear_beacon_rep_data(wpa_s); + return ret; +} + + +static int +wpas_rrm_handle_msr_req_element( + struct wpa_supplicant *wpa_s, + const struct rrm_measurement_request_element *req, + struct wpabuf **buf) +{ + int duration_mandatory; + + wpa_printf(MSG_DEBUG, "Measurement request type %d token %d", + req->type, req->token); + + if (req->mode & MEASUREMENT_REQUEST_MODE_ENABLE) { + /* Enable bit is not supported for now */ + wpa_printf(MSG_DEBUG, "RRM: Enable bit not supported, ignore"); + return 0; + } + + if ((req->mode & MEASUREMENT_REQUEST_MODE_PARALLEL) && + req->type > MEASURE_TYPE_RPI_HIST) { + /* Parallel measurements are not supported for now */ + wpa_printf(MSG_DEBUG, + "RRM: Parallel measurements are not supported, reject"); + goto reject; + } + + duration_mandatory = + !!(req->mode & MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY); + + switch (req->type) { + case MEASURE_TYPE_BEACON: + return wpas_rm_handle_beacon_req(wpa_s, req->token, + duration_mandatory, + (const void *) req->variable, + req->len - 3, buf); + default: + wpa_printf(MSG_INFO, + "RRM: Unsupported radio measurement type %u", + req->type); + break; + } + +reject: + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) && + wpas_rrm_report_elem(buf, req->token, + MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE, + req->type, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "RRM: Failed to add report element"); + return -1; + } + + return 0; +} + + +static struct wpabuf * +wpas_rrm_process_msr_req_elems(struct wpa_supplicant *wpa_s, const u8 *pos, + size_t len) +{ + struct wpabuf *buf = NULL; + + while (len) { + const struct rrm_measurement_request_element *req; + int res; + + if (len < 2) { + wpa_printf(MSG_DEBUG, "RRM: Truncated element"); + goto out; + } + + req = (const struct rrm_measurement_request_element *) pos; + if (req->eid != WLAN_EID_MEASURE_REQUEST) { + wpa_printf(MSG_DEBUG, + "RRM: Expected Measurement Request element, but EID is %u", + req->eid); + printf("len is %d", len); + goto out; + } + + if (req->len < 3) { + wpa_printf(MSG_DEBUG, "RRM: Element length too short"); + goto out; + } + + if (req->len > len - 2) { + wpa_printf(MSG_DEBUG, "RRM: Element length too long"); + goto out; + } + + res = wpas_rrm_handle_msr_req_element(wpa_s, req, &buf); + if (res < 0) + goto out; + + pos += req->len + 2; + len -= req->len + 2; + } + + return buf; + +out: + wpabuf_free(buf); + return NULL; +} + + +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *dst, + const u8 *frame, size_t len) +{ + struct wpabuf *report; + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not RRM network"); + return; + } + + if (len < 3) { + wpa_printf(MSG_INFO, + "RRM: Ignoring too short radio measurement request"); + return; + } + + wpa_s->rrm.token = *frame; + os_memcpy(wpa_s->rrm.dst_addr, dst, ETH_ALEN); + + /* Number of repetitions is not supported */ + + report = wpas_rrm_process_msr_req_elems(wpa_s, frame + 3, len - 3); + if (!report) + return; + + wpas_rrm_send_msr_report(wpa_s, report); + wpabuf_free(report); +} + + +void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len, + int rssi) +{ + struct wpabuf *buf; + const struct rrm_link_measurement_request *req; + struct rrm_link_measurement_report report; + + req = (const struct rrm_link_measurement_request *) frame; + if (len < sizeof(*req)) { + wpa_printf(MSG_INFO, + "RRM: Link measurement report failed. Request too short"); + return; + } + + os_memset(&report, 0, sizeof(report)); + report.dialog_token = req->dialog_token; + report.tpc.eid = WLAN_EID_TPC_REPORT; + report.tpc.len = 2; + /* Note: The driver is expected to update report.tpc.tx_power and + * report.tpc.link_margin subfields when sending out this frame. + * Similarly, the driver would need to update report.rx_ant_id and + * report.tx_ant_id subfields. */ + +#ifdef ESP_SUPPLICANT + esp_get_tx_power(&report.tpc.tx_power); + /* Minimum RSSI = -96 dbm */ + report.tpc.link_margin = rssi + 96; +#endif + report.rsni = 255; /* 255 indicates that RSNI is not available */ + report.rcpi = rssi_to_rcpi(rssi); + + /* action_category + action_code */ + buf = wpabuf_alloc(2 + sizeof(report)); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Buffer allocation failed"); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT); + wpabuf_put_data(buf, &report, sizeof(report)); + wpa_hexdump_buf(MSG_DEBUG, "RRM: Link measurement report", buf); + + if (wpa_drv_send_action(wpa_s, 0, 0, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Send action failed"); + } + wpabuf_free(buf); +} + + +int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, + u64 scan_start_tsf) +{ + size_t i = 0; + struct wpabuf *buf = NULL; + struct wpa_bss *bss = NULL; + + if (!wpa_s->beacon_rep_data.token) + return 0; + + if (!wpa_s->current_bss) + goto out; + + wpa_printf(MSG_DEBUG, "RRM: TSF current BSS: " MACSTR, + MAC2STR(wpa_s->current_bss->bssid)); + + for (i = 0; i < wpa_s->num_bss; i++) { + bss = wpa_bss_get_next_bss(wpa_s, bss); + + if (!bss) + continue; + + if (wpa_s->beacon_rep_data.scan_params.channel && + bss->channel != wpa_s->beacon_rep_data.scan_params.channel) + continue; + /* We flush scan results before issuing scan again */ +#ifndef ESP_SUPPLICANT + /* + * Don't report results that were not received during the + * current measurement. + */ + if (scan_start_tsf < + scan_res->res[i]->parent_tsf) { + struct os_reltime update_time, diff; + + /* For now, allow 7 ms older results due to some + * unknown issue with cfg80211 BSS table updates during + * a scan with the current BSS. + * TODO: Fix this more properly to avoid having to have + * this type of hacks in place. */ + calculate_update_time(&scan_res->fetch_time, + scan_res->res[i]->age, + &update_time); + os_reltime_sub(&wpa_s->beacon_rep_scan, + &update_time, &diff); + if (os_reltime_before(&update_time, + &wpa_s->beacon_rep_scan) && + (diff.sec || diff.usec >= 8000)) { + wpa_printf(MSG_ERROR, + "RRM: Ignore scan result for " MACSTR + " due to old update (age(ms) %u, calculated age %u.%06u seconds)", + MAC2STR(scan_res->res[i]->bssid), + scan_res->res[i]->age, + (unsigned int) diff.sec, + (unsigned int) diff.usec); + continue; + } + } else { + continue; + } +#endif + if (wpas_add_beacon_rep(wpa_s, &buf, bss, scan_start_tsf, + bss->parent_tsf) < 0) + break; + } + + if (!buf && wpas_beacon_rep_no_results(wpa_s, &buf)) + goto out; + + wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", buf); + + wpas_rrm_send_msr_report(wpa_s, buf); + wpabuf_free(buf); + +out: + wpas_clear_beacon_rep_data(wpa_s); + return 1; +} + + +void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + + eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL); + bitfield_free(data->eids); + os_memset(data, 0, sizeof(*data)); +} diff --git a/components/wpa_supplicant/src/common/rrm.h b/components/wpa_supplicant/src/common/rrm.h new file mode 100644 index 0000000000..a981d7c04e --- /dev/null +++ b/components/wpa_supplicant/src/common/rrm.h @@ -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 diff --git a/components/wpa_supplicant/src/common/scan.c b/components/wpa_supplicant/src/common/scan.c new file mode 100644 index 0000000000..3e41749416 --- /dev/null +++ b/components/wpa_supplicant/src/common/scan.c @@ -0,0 +1,95 @@ +/* + * WPA Supplicant - Scanning + * Copyright (c) 2003-2019, Jouni Malinen + * + * 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); +} diff --git a/components/wpa_supplicant/src/common/scan.h b/components/wpa_supplicant/src/common/scan.h new file mode 100644 index 0000000000..c5f9e254f1 --- /dev/null +++ b/components/wpa_supplicant/src/common/scan.h @@ -0,0 +1,25 @@ +/* + * WPA Supplicant - Scanning + * Copyright (c) 2003-2014, Jouni Malinen + * + * 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 */ diff --git a/components/wpa_supplicant/src/common/wnm_sta.c b/components/wpa_supplicant/src/common/wnm_sta.c new file mode 100644 index 0000000000..f238c206fe --- /dev/null +++ b/components/wpa_supplicant/src/common/wnm_sta.c @@ -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; + } +} diff --git a/components/wpa_supplicant/src/common/wnm_sta.h b/components/wpa_supplicant/src/common/wnm_sta.h new file mode 100644 index 0000000000..e513e1a586 --- /dev/null +++ b/components/wpa_supplicant/src/common/wnm_sta.h @@ -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 */ diff --git a/components/wpa_supplicant/src/common/wpa_supplicant_i.h b/components/wpa_supplicant/src/common/wpa_supplicant_i.h new file mode 100644 index 0000000000..f3c07a9a41 --- /dev/null +++ b/components/wpa_supplicant/src/common/wpa_supplicant_i.h @@ -0,0 +1,110 @@ +/* + * wpa_supplicant - Internal definitions + * Copyright (c) 2003-2014, Jouni Malinen + * + * 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 diff --git a/components/wpa_supplicant/src/drivers/driver.h b/components/wpa_supplicant/src/drivers/driver.h new file mode 100644 index 0000000000..759e5b629d --- /dev/null +++ b/components/wpa_supplicant/src/drivers/driver.h @@ -0,0 +1,187 @@ +/* + * Driver interface definition + * Copyright (c) 2003-2017, Jouni Malinen + * + * 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 */ diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_common.c b/components/wpa_supplicant/src/esp_supplicant/esp_common.c new file mode 100644 index 0000000000..04cd3596bc --- /dev/null +++ b/components/wpa_supplicant/src/esp_supplicant/esp_common.c @@ -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; +} diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h b/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h new file mode 100644 index 0000000000..8d87d0774a --- /dev/null +++ b/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h @@ -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 diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_scan.c b/components/wpa_supplicant/src/esp_supplicant/esp_scan.c new file mode 100644 index 0000000000..70510e756d --- /dev/null +++ b/components/wpa_supplicant/src/esp_supplicant/esp_scan.c @@ -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); +} diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_scan_i.h b/components/wpa_supplicant/src/esp_supplicant/esp_scan_i.h new file mode 100644 index 0000000000..25442a6ac0 --- /dev/null +++ b/components/wpa_supplicant/src/esp_supplicant/esp_scan_i.h @@ -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 diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h b/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h index 2db4d93d7b..7320647f0d 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h @@ -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_ */ diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c b/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c index 02b87565bb..2f130abe3b 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c @@ -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); diff --git a/components/wpa_supplicant/src/utils/bitfield.c b/components/wpa_supplicant/src/utils/bitfield.c new file mode 100644 index 0000000000..8dcec3907e --- /dev/null +++ b/components/wpa_supplicant/src/utils/bitfield.c @@ -0,0 +1,89 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * 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; +} diff --git a/components/wpa_supplicant/src/utils/bitfield.h b/components/wpa_supplicant/src/utils/bitfield.h new file mode 100644 index 0000000000..7050a208c8 --- /dev/null +++ b/components/wpa_supplicant/src/utils/bitfield.h @@ -0,0 +1,21 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * 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 */ diff --git a/components/wpa_supplicant/src/utils/common.c b/components/wpa_supplicant/src/utils/common.c index b57feee3b2..47305b0cdb 100644 --- a/components/wpa_supplicant/src/utils/common.c +++ b/components/wpa_supplicant/src/utils/common.c @@ -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; +} diff --git a/components/wpa_supplicant/src/utils/common.h b/components/wpa_supplicant/src/utils/common.h index 70119d0b56..144487b7da 100644 --- a/components/wpa_supplicant/src/utils/common.h +++ b/components/wpa_supplicant/src/utils/common.h @@ -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); diff --git a/components/wpa_supplicant/src/utils/list.h b/components/wpa_supplicant/src/utils/list.h index a67a04966c..2b026054fe 100644 --- a/components/wpa_supplicant/src/utils/list.h +++ b/components/wpa_supplicant/src/utils/list.h @@ -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 @@ -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 */ diff --git a/components/wpa_supplicant/src/utils/wpa_debug.c b/components/wpa_supplicant/src/utils/wpa_debug.c index 231a7e3233..6436938cd7 100644 --- a/components/wpa_supplicant/src/utils/wpa_debug.c +++ b/components/wpa_supplicant/src/utils/wpa_debug.c @@ -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 diff --git a/examples/wifi/roaming/CMakeLists.txt b/examples/wifi/roaming/CMakeLists.txt new file mode 100644 index 0000000000..a1db8df427 --- /dev/null +++ b/examples/wifi/roaming/CMakeLists.txt @@ -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) diff --git a/examples/wifi/roaming/Makefile b/examples/wifi/roaming/Makefile new file mode 100644 index 0000000000..db88366ce7 --- /dev/null +++ b/examples/wifi/roaming/Makefile @@ -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 diff --git a/examples/wifi/roaming/README.md b/examples/wifi/roaming/README.md new file mode 100644 index 0000000000..66bf33180a --- /dev/null +++ b/examples/wifi/roaming/README.md @@ -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 diff --git a/examples/wifi/roaming/main/CMakeLists.txt b/examples/wifi/roaming/main/CMakeLists.txt new file mode 100644 index 0000000000..c3c5d418da --- /dev/null +++ b/examples/wifi/roaming/main/CMakeLists.txt @@ -0,0 +1,3 @@ +# Embed CA, certificate & key directly into binary +idf_component_register(SRCS "roaming_example.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/roaming/main/Kconfig.projbuild b/examples/wifi/roaming/main/Kconfig.projbuild new file mode 100644 index 0000000000..6561bff700 --- /dev/null +++ b/examples/wifi/roaming/main/Kconfig.projbuild @@ -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 diff --git a/examples/wifi/roaming/main/component.mk b/examples/wifi/roaming/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/wifi/roaming/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/wifi/roaming/main/roaming_example.c b/examples/wifi/roaming/main/roaming_example.c new file mode 100644 index 0000000000..4d48453e1b --- /dev/null +++ b/examples/wifi/roaming/main/roaming_example.c @@ -0,0 +1,364 @@ +#include +#include +#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(¶ms, 0, sizeof(wifi_scan_config_t)); + if (esp_wifi_scan_start(¶ms, 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(); +} diff --git a/examples/wifi/roaming/sdkconfig.defaults b/examples/wifi/roaming/sdkconfig.defaults new file mode 100644 index 0000000000..8e1ae374a1 --- /dev/null +++ b/examples/wifi/roaming/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_WPA_11KV_SUPPORT=y +CONFIG_WPA_SCAN_CACHE=y