Merge branch 'bugfix/concurrency_issue_roam_app_v5.4' into 'release/v5.4'

Add blacklist logic and some fixes in roaming app (v5.4)

See merge request espressif/esp-idf!44114
This commit is contained in:
Jiang Jiang Jian
2026-01-09 19:23:08 +08:00
13 changed files with 425 additions and 111 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES driver esptool_py esp_pm esp_timer nvs_flash PRIV_REQUIRES driver esptool_py esp_pm esp_timer nvs_flash
wpa_supplicant hal lwip esp_coex ${extra_priv_requires} wpa_supplicant hal lwip esp_coex ${extra_priv_requires}
PRIV_INCLUDE_DIRS ../wpa_supplicant/src/ ../wpa_supplicant/esp_supplicant/src/ PRIV_INCLUDE_DIRS ../wpa_supplicant/src/ ../wpa_supplicant/esp_supplicant/src/
wifi_apps/roaming_app/include wifi_apps/roaming_app/include wifi_apps/roaming_app/src
LDFRAGMENTS "${ldfragments}") LDFRAGMENTS "${ldfragments}")
if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED) if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED)
+3 -6
View File
@@ -181,8 +181,9 @@ static esp_err_t wifi_deinit_internal(void)
esp_supplicant_deinit(); esp_supplicant_deinit();
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP #if CONFIG_ESP_WIFI_SLP_SAMPLE_BEACON_FEATURE
roam_deinit_app(); wifi_beacon_offset_config_t offset_config = WIFI_BEACON_OFFSET_CONFIG_DEFAULT(false);
esp_wifi_beacon_offset_configure(&offset_config);
#endif #endif
err = esp_wifi_deinit_internal(); err = esp_wifi_deinit_internal();
@@ -446,10 +447,6 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config)
goto _deinit; goto _deinit;
} }
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
roam_init_app();
#endif
} else { } else {
goto _deinit; goto _deinit;
} }
@@ -16,9 +16,11 @@ struct roam_config {
int8_t low_rssi_threshold; int8_t low_rssi_threshold;
uint8_t rssi_threshold_reduction_offset; uint8_t rssi_threshold_reduction_offset;
bool scan_monitor; bool scan_monitor;
#if CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
uint8_t scan_interval; uint8_t scan_interval;
int8_t scan_rssi_threshold; int8_t scan_rssi_threshold;
uint8_t scan_rssi_diff; uint8_t scan_rssi_diff;
#endif
bool legacy_roam_enabled; bool legacy_roam_enabled;
uint8_t btm_retry_cnt; uint8_t btm_retry_cnt;
bool btm_roaming_enabled; bool btm_roaming_enabled;
@@ -37,6 +39,10 @@ void roam_sta_disconnected(void *disconn);
esp_err_t roam_get_config_params(struct roam_config *config); esp_err_t roam_get_config_params(struct roam_config *config);
esp_err_t roam_set_config_params(struct roam_config *config); esp_err_t roam_set_config_params(struct roam_config *config);
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
void esp_wifi_roaming_set_current_bssid(const uint8_t *bssid);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@@ -73,7 +73,7 @@ menu "Roaming Methods"
config ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM config ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM
bool "Support Network Assisted roaming using 802.11v" bool "Support Network Assisted roaming using 802.11v"
depends on ESP_WIFI_WNM_SUPPORT depends on ESP_WIFI_WNM_SUPPORT
default n default y
help help
Roaming between APs using network assisted Roaming. Roaming between APs using network assisted Roaming.
This involves BSS Transition Management mechanisms outlined in 802.11v. This involves BSS Transition Management mechanisms outlined in 802.11v.
@@ -139,13 +139,7 @@ menu "Scan Configuration"
Duration for which the results from the most recent scans can be used Duration for which the results from the most recent scans can be used
by the roaming app for determining the roaming candidates. by the roaming app for determining the roaming candidates.
config ESP_WIFI_ROAMING_MAX_CANDIDATES
int "Max Candidates in the network"
default 3
range 3 20
help
Max candidates that can be considered while scanning as a part of the
network at one time.
endmenu #"Scan Configuration" endmenu #"Scan Configuration"
@@ -160,7 +154,7 @@ config ESP_WIFI_ROAMING_BACKOFF_TIME
config ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING config ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING
bool "Send periodic neighbor report request to AP for internal list updation" bool "Send periodic neighbor report request to AP for internal list updation"
depends on ESP_WIFI_RRM_SUPPORT depends on ESP_WIFI_RRM_SUPPORT
default y default n
help help
This option will enable station to keep sending RRM neighbor list request to AP and This option will enable station to keep sending RRM neighbor list request to AP and
update its internal list. update its internal list.
@@ -182,3 +176,50 @@ config ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD
range -99 0 range -99 0
help help
The RSSI threshold beyond which we start sending periodic neighbor report requests. The RSSI threshold beyond which we start sending periodic neighbor report requests.
menu "Blacklist Configuration"
config ESP_WIFI_ROAMING_BSSID_BLACKLIST
bool "Enable BSSID blacklisting"
default n
help
Enable this to blacklist BSSIDs.
config ESP_WIFI_ROAMING_AUTO_BLACKLISTING
bool "Enable automatic BSSID blacklisting"
depends on ESP_WIFI_ROAMING_BSSID_BLACKLIST
help
Enable this to automatically blacklist BSSIDs after multiple failed connection attempts.
config ESP_WIFI_ROAMING_MAX_CONN_FAILURES
int "Maximum connection failures"
depends on ESP_WIFI_ROAMING_AUTO_BLACKLISTING
range 1 10
default 3
help
Maximum number of connection failures before a BSSID is blacklisted.
config ESP_WIFI_ROAMING_BLACKLIST_TIMEOUT
int "Blacklist timeout (in seconds)"
depends on ESP_WIFI_ROAMING_BSSID_BLACKLIST
range 10 3600
default 300
help
Time in seconds for which a BSSID remains in the blacklist.
This applies to both automatically and manually blacklisted BSSIDs.
config ESP_WIFI_ROAMING_MAX_CANDIDATES
int "Maximum number of roaming candidates"
range 1 10
default 5
help
Maximum number of roaming candidates to consider. This also defines the size of the blacklist.
config ESP_WIFI_ROAMING_PREVENT_DOWNGRADE
bool "Prevent security downgrade when roaming"
default n
help
If the currently connected AP sends a "transition disable" bit,
this option will make the roaming logic ignore less secure APs.
This helps prevent security downgrades when roaming in a mixed
security environment (e.g., WPA2/WPA3).
endmenu # "Blacklist Configuration"
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -22,12 +22,18 @@ extern "C" {
#define ROAMING_BACKOFF_TIME CONFIG_ESP_WIFI_ROAMING_BACKOFF_TIME #define ROAMING_BACKOFF_TIME CONFIG_ESP_WIFI_ROAMING_BACKOFF_TIME
/* Low RSSI based roaming configuration */ /* Low RSSI based roaming configuration */
#ifndef CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_ROAMING
#define CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_ROAMING 0
#endif
#define LOW_RSSI_ROAMING_ENABLED CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_ROAMING #define LOW_RSSI_ROAMING_ENABLED CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_ROAMING
#if LOW_RSSI_ROAMING_ENABLED #if LOW_RSSI_ROAMING_ENABLED
#define ROAMING_LOW_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD #define ROAMING_LOW_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD
#define RSSI_THRESHOLD_REDUCTION_OFFSET CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_OFFSET #define RSSI_THRESHOLD_REDUCTION_OFFSET CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_OFFSET
#endif /*LOW_RSSI_ROAMING_ENABLED*/ #endif /*LOW_RSSI_ROAMING_ENABLED*/
#ifndef CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
#define CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR 0
#endif
/* Periodic Scan based Roaming configuration */ /* Periodic Scan based Roaming configuration */
#define PERIODIC_SCAN_MONITORING CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR #define PERIODIC_SCAN_MONITORING CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
#if PERIODIC_SCAN_MONITORING #if PERIODIC_SCAN_MONITORING
@@ -134,6 +140,16 @@ struct roaming_app {
#endif #endif
#if PERIODIC_SCAN_MONITORING #if PERIODIC_SCAN_MONITORING
bool periodic_scan_active; bool periodic_scan_active;
#endif
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
struct blacklist_entry {
uint8_t bssid[ETH_ALEN];
#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING
uint8_t failures;
#endif
struct timeval timestamp;
} bssid_blacklist[CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES];
uint8_t bssid_blacklist_count;
#endif #endif
bool allow_reconnect; bool allow_reconnect;
}; };
@@ -28,22 +28,17 @@
#include "utils/eloop.h" #include "utils/eloop.h"
#include "rom/ets_sys.h" #include "rom/ets_sys.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
static struct roaming_app g_roaming_app; #include "common/wpa_common.h"
#include "esp_wpas_glue.h"
struct roaming_app g_roaming_app;
typedef void (* scan_done_cb_t)(void *arg, ETS_STATUS status); typedef void (* scan_done_cb_t)(void *arg, ETS_STATUS status);
extern int esp_wifi_promiscuous_scan_start(wifi_scan_config_t *config, scan_done_cb_t cb); extern int esp_wifi_promiscuous_scan_start(wifi_scan_config_t *config, scan_done_cb_t cb);
static void *scan_results_lock = NULL;
#define ROAM_SCAN_RESULTS_LOCK() os_mutex_lock(scan_results_lock)
#define ROAM_SCAN_RESULTS_UNLOCK() os_mutex_unlock(scan_results_lock)
#if PERIODIC_RRM_MONITORING
static void *neighbor_list_lock = NULL;
#define ROAM_NEIGHBOR_LIST_LOCK() os_mutex_lock(neighbor_list_lock)
#define ROAM_NEIGHBOR_LIST_UNLOCK() os_mutex_unlock(neighbor_list_lock)
#endif /*PERIODIC_RRM_MONITORING*/
static int wifi_post_roam_event(struct cand_bss *bss); static int wifi_post_roam_event(struct cand_bss *bss);
static void determine_best_ap(int8_t rssi_threshold); static void determine_best_ap(int8_t rssi_threshold);
static bool is_bssid_blacklisted(const uint8_t *bssid);
#if PERIODIC_RRM_MONITORING #if PERIODIC_RRM_MONITORING
static void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx); static void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx);
#endif #endif
@@ -53,6 +48,13 @@ static void roaming_app_periodic_scan_internal_handler(void *data, void *ctx);
static const char *ROAMING_TAG = "ROAM"; static const char *ROAMING_TAG = "ROAM";
void esp_wifi_roaming_set_current_bssid(const uint8_t *bssid)
{
if (bssid) {
memcpy(g_roaming_app.current_bss.ap.bssid, bssid, ETH_ALEN);
}
}
static inline long time_diff_sec(struct timeval *a, struct timeval *b) static inline long time_diff_sec(struct timeval *a, struct timeval *b)
{ {
return (a->tv_sec - b->tv_sec); return (a->tv_sec - b->tv_sec);
@@ -139,12 +141,6 @@ static void init_periodic_rrm_event(void)
ESP_LOGI(ROAMING_TAG, "RRM monitor is disabled in config"); ESP_LOGI(ROAMING_TAG, "RRM monitor is disabled in config");
return; return;
} }
if (!neighbor_list_lock) {
neighbor_list_lock = os_recursive_mutex_create();
if (!neighbor_list_lock) {
ESP_LOGE(ROAMING_TAG, "%s: failed to create roaming neighbor list lock", __func__);
}
}
ESP_LOGV(ROAMING_TAG, "Initialised Periodic RRM Monitoring event!"); ESP_LOGV(ROAMING_TAG, "Initialised Periodic RRM Monitoring event!");
g_roaming_app.periodic_rrm_active = true; g_roaming_app.periodic_rrm_active = true;
if (eloop_register_timeout(g_roaming_app.config.rrm_monitor_time, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) { if (eloop_register_timeout(g_roaming_app.config.rrm_monitor_time, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) {
@@ -179,23 +175,77 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data)
#endif /*PERIODIC_SCAN_MONITORING*/ #endif /*PERIODIC_SCAN_MONITORING*/
wifi_event_sta_disconnected_t *disconn = data; wifi_event_sta_disconnected_t *disconn = data;
ESP_LOGD(ROAMING_TAG, "station got disconnected reason=%d", disconn->reason); #define RSSI_INVALID -128
g_roaming_app.current_bss.ap.rssi = RSSI_INVALID;
#undef RSSI_INVALID
ESP_LOGD(ROAMING_TAG, "station got disconnected reason=%d, rssi =%d", disconn->reason, disconn->rssi);
#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING
if (disconn->reason == WIFI_REASON_CONNECTION_FAIL || disconn->reason == WIFI_REASON_AUTH_FAIL) {
bool found = false;
for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) {
if (memcmp(g_roaming_app.bssid_blacklist[i].bssid, g_roaming_app.current_bss.ap.bssid, ETH_ALEN) == 0) {
g_roaming_app.bssid_blacklist[i].failures++;
gettimeofday(&g_roaming_app.bssid_blacklist[i].timestamp, NULL);
ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " connection failures: %d", MAC2STR(g_roaming_app.current_bss.ap.bssid), g_roaming_app.bssid_blacklist[i].failures);
if (g_roaming_app.bssid_blacklist[i].failures >= CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES) {
ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " blacklisted", MAC2STR(g_roaming_app.current_bss.ap.bssid));
/* The BSSID is now blacklisted. The logic later in this function
* will check the blacklist and trigger a scan for a new AP
* instead of trying to reconnect to this one. */
}
found = true;
break;
}
}
if (!found) {
if (g_roaming_app.bssid_blacklist_count < CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES) {
memcpy(g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].bssid, g_roaming_app.current_bss.ap.bssid, ETH_ALEN);
g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].failures = 1;
gettimeofday(&g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].timestamp, NULL);
g_roaming_app.bssid_blacklist_count++;
ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " added to blacklist tracking", MAC2STR(g_roaming_app.current_bss.ap.bssid));
} else {
int oldest_idx = 0;
for (int i = 1; i < g_roaming_app.bssid_blacklist_count; i++) {
if (g_roaming_app.bssid_blacklist[i].timestamp.tv_sec < g_roaming_app.bssid_blacklist[oldest_idx].timestamp.tv_sec) {
oldest_idx = i;
}
}
ESP_LOGD(ROAMING_TAG, "Blacklist is full. Replacing oldest entry for " MACSTR, MAC2STR(g_roaming_app.bssid_blacklist[oldest_idx].bssid));
memcpy(g_roaming_app.bssid_blacklist[oldest_idx].bssid, g_roaming_app.current_bss.ap.bssid, ETH_ALEN);
g_roaming_app.bssid_blacklist[oldest_idx].failures = 1;
gettimeofday(&g_roaming_app.bssid_blacklist[oldest_idx].timestamp, NULL);
ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " added to blacklist tracking", MAC2STR(g_roaming_app.current_bss.ap.bssid));
}
}
}
#endif
if (disconn->reason == WIFI_REASON_ROAMING) { if (disconn->reason == WIFI_REASON_ROAMING) {
ESP_LOGD(ROAMING_TAG, "station roaming, do nothing"); ESP_LOGD(ROAMING_TAG, "station roaming, do nothing");
} else if (g_roaming_app.allow_reconnect == false) { } else if (g_roaming_app.allow_reconnect == false) {
ESP_LOGD(ROAMING_TAG, "station initiated disconnect, do nothing"); ESP_LOGD(ROAMING_TAG, "station initiated disconnect, do nothing");
} else { } else {
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
if (is_bssid_blacklisted(g_roaming_app.current_bss.ap.bssid)) {
ESP_LOGD(ROAMING_TAG, "AP " MACSTR " is blacklisted, finding another AP", MAC2STR(g_roaming_app.current_bss.ap.bssid));
determine_best_ap(0);
} else {
#endif
#if LEGACY_ROAM_ENABLED #if LEGACY_ROAM_ENABLED
/* /*
* Resetting the Bssid param as it is possible that a previous force * Resetting the Bssid param as it is possible that a previous force
* roam has set config to connect to a specific bssid and now further * roam has set config to connect to a specific bssid and now further
* roaming attempts using BTM could lead to a spiral of connecting to * roaming attempts using BTM could lead to a spiral of connecting to
* the previous AP */ * the previous AP */
if (g_roaming_app.force_roam_ongoing) { if (g_roaming_app.force_roam_ongoing) {
legacy_roam_clear_bssid_flag(); legacy_roam_clear_bssid_flag();
} }
#endif /*LEGACY_ROAM_ENABLED*/ #endif /*LEGACY_ROAM_ENABLED*/
esp_wifi_connect(); esp_wifi_connect();
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
}
#endif
} }
os_free(disconn); os_free(disconn);
} }
@@ -357,26 +407,27 @@ cleanup:
} }
return buf; return buf;
} }
static void roaming_app_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
static void roaming_app_neighbor_report_recv_internal_handler(void *ctx, void *data)
{ {
wifi_event_neighbor_report_t *neighbor_report_event = data;
if (!g_roaming_app.rrm_request_active) { if (!g_roaming_app.rrm_request_active) {
ESP_LOGV(ROAMING_TAG, "Not the response for our Neighbor Report Request"); ESP_LOGV(ROAMING_TAG, "Not the response for our Neighbor Report Request");
return; goto cleanup;
} }
g_roaming_app.rrm_request_active = false; g_roaming_app.rrm_request_active = false;
if (!event_data) { if (!neighbor_report_event) {
ESP_LOGE(ROAMING_TAG, "No data received for neighbor report"); ESP_LOGE(ROAMING_TAG, "No data received for neighbor report");
return; goto cleanup;
} }
wifi_event_neighbor_report_t *neighbor_report_event = (wifi_event_neighbor_report_t*)event_data;
ESP_LOGD(ROAMING_TAG, "Received cb for Neighbor Report Request"); ESP_LOGD(ROAMING_TAG, "Received cb for Neighbor Report Request");
uint8_t *pos = (uint8_t *)neighbor_report_event->report; uint8_t *pos = (uint8_t *)neighbor_report_event->report;
if (!pos) { if (!pos) {
ESP_LOGE(ROAMING_TAG, "Neighbor report is empty"); ESP_LOGE(ROAMING_TAG, "Neighbor report is empty");
return; goto cleanup;
} }
uint8_t report_len = neighbor_report_event->report_len; uint8_t report_len = neighbor_report_event->report_len;
@@ -384,22 +435,42 @@ static void roaming_app_neighbor_report_recv_handler(void* arg, esp_event_base_t
ESP_LOGD(ROAMING_TAG, "rrm: neighbor report len=%d", report_len); ESP_LOGD(ROAMING_TAG, "rrm: neighbor report len=%d", report_len);
ESP_LOG_BUFFER_HEXDUMP(ROAMING_TAG, pos, report_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEXDUMP(ROAMING_TAG, pos, report_len, ESP_LOG_DEBUG);
ROAM_NEIGHBOR_LIST_LOCK();
if (g_roaming_app.btm_neighbor_list) { if (g_roaming_app.btm_neighbor_list) {
os_free(g_roaming_app.btm_neighbor_list); os_free(g_roaming_app.btm_neighbor_list);
g_roaming_app.btm_neighbor_list = NULL; g_roaming_app.btm_neighbor_list = NULL;
} }
/* create neighbor list */ /* create neighbor list */
g_roaming_app.btm_neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); g_roaming_app.btm_neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
ROAM_NEIGHBOR_LIST_UNLOCK();
cleanup:
if (neighbor_report_event) {
os_free(neighbor_report_event);
}
}
static void roaming_app_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
wifi_event_neighbor_report_t *event = (wifi_event_neighbor_report_t*)event_data;
if (!event) {
return;
}
wifi_event_neighbor_report_t *event_copy = os_malloc(sizeof(wifi_event_neighbor_report_t) + event->report_len);
if (!event_copy) {
ESP_LOGE(ROAMING_TAG, "Failed to allocate memory for neighbor report event");
return;
}
memcpy(event_copy, event, sizeof(wifi_event_neighbor_report_t) + event->report_len);
if (eloop_register_timeout(0, 0, roaming_app_neighbor_report_recv_internal_handler, NULL, event_copy) != 0) {
os_free(event_copy);
}
} }
#endif /*PERIODIC_RRM_MONITORING*/ #endif /*PERIODIC_RRM_MONITORING*/
#if LOW_RSSI_ROAMING_ENABLED #if LOW_RSSI_ROAMING_ENABLED
static void roaming_app_rssi_low_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) static void roaming_app_rssi_low_internal_handler(void *ctx, void *data)
{ {
wifi_event_bss_rssi_low_t *event = event_data; wifi_event_bss_rssi_low_t *event = data;
ESP_LOGI(ROAMING_TAG, "%s:bss rssi is=%ld", __func__, event->rssi); ESP_LOGI(ROAMING_TAG, "%s:bss rssi is=%ld", __func__, event->rssi);
roaming_app_get_ap_info(&g_roaming_app.current_bss.ap); roaming_app_get_ap_info(&g_roaming_app.current_bss.ap);
@@ -408,25 +479,34 @@ static void roaming_app_rssi_low_handler(void* arg, esp_event_base_t event_base,
ESP_LOGD(ROAMING_TAG, "Resetting RSSI Threshold to %d", g_roaming_app.current_low_rssi_threshold); ESP_LOGD(ROAMING_TAG, "Resetting RSSI Threshold to %d", g_roaming_app.current_low_rssi_threshold);
esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold); esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold);
os_free(event);
}
static void roaming_app_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;
if (!event) {
return;
}
wifi_event_bss_rssi_low_t *event_copy = os_malloc(sizeof(wifi_event_bss_rssi_low_t));
if (!event_copy) {
ESP_LOGE(ROAMING_TAG, "Failed to allocate memory for rssi low event");
return;
}
memcpy(event_copy, event, sizeof(wifi_event_bss_rssi_low_t));
if (eloop_register_timeout(0, 0, roaming_app_rssi_low_internal_handler, NULL, event_copy) != 0) {
os_free(event_copy);
}
} }
#endif #endif
#if NETWORK_ASSISTED_ROAMING_ENABLED #if NETWORK_ASSISTED_ROAMING_ENABLED
static void trigger_network_assisted_roam(void) static void trigger_network_assisted_roam(void)
{ {
#if PERIODIC_RRM_MONITORING
if (g_roaming_app.current_bss.rrm_support) {
ROAM_NEIGHBOR_LIST_LOCK();
}
#endif /*PERIODIC_RRM_MONITORING*/
if (esp_wnm_send_bss_transition_mgmt_query(REASON_RSSI, g_roaming_app.btm_neighbor_list, 1) < 0) { if (esp_wnm_send_bss_transition_mgmt_query(REASON_RSSI, g_roaming_app.btm_neighbor_list, 1) < 0) {
ESP_LOGD(ROAMING_TAG, "failed to send btm query"); ESP_LOGD(ROAMING_TAG, "failed to send btm query");
} }
#if PERIODIC_RRM_MONITORING
if (g_roaming_app.current_bss.rrm_support) {
ROAM_NEIGHBOR_LIST_UNLOCK();
}
#endif /*PERIODIC_RRM_MONITORING*/
ESP_LOGD(ROAMING_TAG, "Sent BTM Query"); ESP_LOGD(ROAMING_TAG, "Sent BTM Query");
gettimeofday(&g_roaming_app.last_roamed_time, NULL); gettimeofday(&g_roaming_app.last_roamed_time, NULL);
#if LEGACY_ROAM_ENABLED #if LEGACY_ROAM_ENABLED
@@ -444,6 +524,7 @@ static void trigger_legacy_roam(struct cand_bss *bss)
wifi_cfg.sta.bssid_set = true; wifi_cfg.sta.bssid_set = true;
os_memcpy(wifi_cfg.sta.bssid, bss->bssid, ETH_ALEN); os_memcpy(wifi_cfg.sta.bssid, bss->bssid, ETH_ALEN);
esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC); esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC);
esp_wifi_roaming_set_current_bssid(bss->bssid);
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg); esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
esp_wifi_connect(); esp_wifi_connect();
ESP_LOGI(ROAMING_TAG, "Disconnecting and connecting to "MACSTR" on account of better rssi",MAC2STR(bss->bssid)); ESP_LOGI(ROAMING_TAG, "Disconnecting and connecting to "MACSTR" on account of better rssi",MAC2STR(bss->bssid));
@@ -458,7 +539,7 @@ void roaming_app_trigger_roam(struct cand_bss *bss)
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
ESP_LOGD(ROAMING_TAG,"Processing trigger roaming request."); ESP_LOGD(ROAMING_TAG,"Processing trigger roaming request.");
if (time_diff_sec(&now, &g_roaming_app.last_roamed_time) < g_roaming_app.config.backoff_time ) { if (time_diff_sec(&now, &g_roaming_app.last_roamed_time) < g_roaming_app.config.backoff_time ) {
ESP_LOGD(ROAMING_TAG,"Ignoring request as time difference to last request is %ld",time_diff_sec(&now, &g_roaming_app.last_roamed_time)); ESP_LOGD(ROAMING_TAG,"Ignoring request as time difference to last request is %ld",time_diff_sec(&now, &g_roaming_app.last_roamed_time));
goto free_bss; goto free_bss;
} }
#if NETWORK_ASSISTED_ROAMING_ENABLED #if NETWORK_ASSISTED_ROAMING_ENABLED
@@ -492,7 +573,6 @@ void roaming_app_trigger_roam_internal_handler(void *ctx, void *data)
} else { } else {
roaming_app_trigger_roam((struct cand_bss *)data); roaming_app_trigger_roam((struct cand_bss *)data);
} }
} }
static int wifi_post_roam_event(struct cand_bss *bss) static int wifi_post_roam_event(struct cand_bss *bss)
@@ -529,7 +609,6 @@ void print_ap_records(struct scanned_ap_info *ap_info)
} }
#if PERIODIC_RRM_MONITORING #if PERIODIC_RRM_MONITORING
static void periodic_rrm_request(struct timeval *now) static void periodic_rrm_request(struct timeval *now)
{ {
@@ -546,6 +625,31 @@ static void periodic_rrm_request(struct timeval *now)
static bool candidate_security_match(wifi_ap_record_t candidate) static bool candidate_security_match(wifi_ap_record_t candidate)
{ {
#if CONFIG_ESP_WIFI_ROAMING_PREVENT_DOWNGRADE
u8 transition_disable = wpa_supplicant_get_transition_disable();
if (transition_disable & TRANSITION_DISABLE_WPA3_PERSONAL) {
if (candidate.authmode == WIFI_AUTH_WPA2_PSK) {
return false;
}
}
if (transition_disable & TRANSITION_DISABLE_ENHANCED_OPEN) {
if (candidate.authmode == WIFI_AUTH_OPEN) {
return false;
}
}
if (transition_disable & TRANSITION_DISABLE_WPA3_ENTERPRISE) {
if (candidate.authmode == WIFI_AUTH_WPA2_ENTERPRISE) {
return false;
}
}
#if TODO // application doesn't have a way to know SAE-PK enabled AP atm
if (transition_disable & TRANSITION_DISABLE_SAE_PK) {
if (candidate.authmode == WIFI_AUTH_WPA3_PSK) {
return false;
}
}
#endif
#endif
wifi_auth_mode_t curr_auth = g_roaming_app.current_bss.ap.authmode; wifi_auth_mode_t curr_auth = g_roaming_app.current_bss.ap.authmode;
wifi_auth_mode_t cand_auth = candidate.authmode; wifi_auth_mode_t cand_auth = candidate.authmode;
ESP_LOGV(ROAMING_TAG, "Cand authmode : %d, Current Authmode : %d", cand_auth, curr_auth); ESP_LOGV(ROAMING_TAG, "Cand authmode : %d, Current Authmode : %d", cand_auth, curr_auth);
@@ -556,9 +660,6 @@ static bool candidate_security_match(wifi_ap_record_t candidate)
wifi_config_t wifi_cfg = {0}; wifi_config_t wifi_cfg = {0};
esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg); esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg);
if (wifi_cfg.sta.owe_enabled && OWE_COMPATIBLE(curr_auth, cand_auth)) { if (wifi_cfg.sta.owe_enabled && OWE_COMPATIBLE(curr_auth, cand_auth)) {
/*
* OWE <--> Open allowed if threshold is Open
*/
if (wifi_cfg.sta.threshold.authmode == WIFI_AUTH_OPEN) { if (wifi_cfg.sta.threshold.authmode == WIFI_AUTH_OPEN) {
ESP_LOGV(ROAMING_TAG, "transition between OWE and open permitted"); ESP_LOGV(ROAMING_TAG, "transition between OWE and open permitted");
return true; return true;
@@ -568,7 +669,7 @@ static bool candidate_security_match(wifi_ap_record_t candidate)
} }
} else if (wifi_cfg.sta.threshold.authmode > cand_auth) { } else if (wifi_cfg.sta.threshold.authmode > cand_auth) {
/* If the authmode of the candidate AP is less than our threshold, it /* If the authmode of the candidate AP is less than our threshold, it
* will fail during connection*/ * will fail during connection */
ESP_LOGV(ROAMING_TAG, "Authmode threshold failure %d -> %d", wifi_cfg.sta.threshold.authmode, cand_auth); ESP_LOGV(ROAMING_TAG, "Authmode threshold failure %d -> %d", wifi_cfg.sta.threshold.authmode, cand_auth);
return false; return false;
} else if (PSK_COMPATIBLE(curr_auth, cand_auth)) { } else if (PSK_COMPATIBLE(curr_auth, cand_auth)) {
@@ -581,11 +682,45 @@ static bool candidate_security_match(wifi_ap_record_t candidate)
return false; return false;
} }
static bool candidate_profile_match(wifi_ap_record_t candidate) #if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
static void remove_expired_blacklist_entries(void)
{ {
return candidate_security_match(candidate); struct timeval now;
gettimeofday(&now, NULL);
int j = 0;
for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) {
if (time_diff_sec(&now, &g_roaming_app.bssid_blacklist[i].timestamp) <= CONFIG_ESP_WIFI_ROAMING_BLACKLIST_TIMEOUT) {
if (i != j) {
g_roaming_app.bssid_blacklist[j] = g_roaming_app.bssid_blacklist[i];
}
j++;
} else {
ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " removed from blacklist due to timeout", MAC2STR(g_roaming_app.bssid_blacklist[i].bssid));
}
}
g_roaming_app.bssid_blacklist_count = j;
} }
/* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */ #endif
static bool is_bssid_blacklisted(const uint8_t *bssid)
{
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
remove_expired_blacklist_entries();
for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) {
if (memcmp(g_roaming_app.bssid_blacklist[i].bssid, bssid, ETH_ALEN) == 0) {
#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING
if (g_roaming_app.bssid_blacklist[i].failures >= CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES) {
return true;
}
#else
return true;
#endif
}
}
#endif
return false;
}
static void parse_scan_results_and_roam(void) static void parse_scan_results_and_roam(void)
{ {
int8_t rssi_threshold = g_roaming_app.current_rssi_threshold; int8_t rssi_threshold = g_roaming_app.current_rssi_threshold;
@@ -594,16 +729,18 @@ static void parse_scan_results_and_roam(void)
int8_t rssi_diff = 0; int8_t rssi_diff = 0;
uint8_t i; uint8_t i;
int8_t best_ap_index = -1; int8_t best_ap_index = -1;
wifi_ap_record_t ap_info;
roaming_app_get_ap_info(&ap_info);
for (i = 0; i < g_roaming_app.scanned_aps.current_count; i++) { for (i = 0; i < g_roaming_app.scanned_aps.current_count; i++) {
rssi_diff = g_roaming_app.scanned_aps.ap_records[i].rssi - ap_info.rssi; if (is_bssid_blacklisted(g_roaming_app.scanned_aps.ap_records[i].bssid)) {
ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " is blacklisted, skipping", MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid));
continue;
}
rssi_diff = g_roaming_app.scanned_aps.ap_records[i].rssi - g_roaming_app.current_bss.ap.rssi;
ESP_LOGD(ROAMING_TAG, "The difference between ("MACSTR", "MACSTR") with rssi (%d,%d) is : %d while the threshold is %d and the best rssi diff yet is %d, thecand_auth is %d", ESP_LOGD(ROAMING_TAG, "The difference between ("MACSTR", "MACSTR") with rssi (%d,%d) is : %d while the threshold is %d and the best rssi diff yet is %d, thecand_auth is %d",
MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(ap_info.bssid), MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(g_roaming_app.current_bss.ap.bssid),
g_roaming_app.scanned_aps.ap_records[i].rssi, ap_info.rssi, g_roaming_app.scanned_aps.ap_records[i].rssi, g_roaming_app.current_bss.ap.rssi,
rssi_diff, rssi_threshold, best_rssi_diff, g_roaming_app.scanned_aps.ap_records[i].authmode); rssi_diff, rssi_threshold, best_rssi_diff, g_roaming_app.scanned_aps.ap_records[i].authmode);
if ((memcmp(g_roaming_app.scanned_aps.ap_records[i].bssid, ap_info.bssid, ETH_ALEN) != 0) && if ((memcmp(g_roaming_app.scanned_aps.ap_records[i].bssid, g_roaming_app.current_bss.ap.bssid, ETH_ALEN) != 0) &&
candidate_profile_match(g_roaming_app.scanned_aps.ap_records[i]) && rssi_diff > best_rssi_diff ) { candidate_security_match(g_roaming_app.scanned_aps.ap_records[i]) && rssi_diff > best_rssi_diff ) {
best_rssi_diff = rssi_diff; best_rssi_diff = rssi_diff;
best_ap_index = i; best_ap_index = i;
} }
@@ -632,44 +769,41 @@ static void parse_scan_results_and_roam(void)
static void scan_done_event_handler(void *arg, ETS_STATUS status) static void scan_done_event_handler(void *arg, ETS_STATUS status)
{ {
if (status == ETS_OK) { if (status == ETS_OK) {
ROAM_SCAN_RESULTS_LOCK();
ESP_LOGD(ROAMING_TAG, "Scan Done properly"); ESP_LOGD(ROAMING_TAG, "Scan Done properly");
g_roaming_app.scanned_aps.current_count = MAX_CANDIDATE_COUNT; g_roaming_app.scanned_aps.current_count = MAX_CANDIDATE_COUNT;
esp_wifi_scan_get_ap_records(&g_roaming_app.scanned_aps.current_count, g_roaming_app.scanned_aps.ap_records); esp_wifi_scan_get_ap_records(&g_roaming_app.scanned_aps.current_count, g_roaming_app.scanned_aps.ap_records);
print_ap_records(&g_roaming_app.scanned_aps); print_ap_records(&g_roaming_app.scanned_aps);
parse_scan_results_and_roam(); parse_scan_results_and_roam();
ROAM_SCAN_RESULTS_UNLOCK();
} else { } else {
ESP_LOGD(ROAMING_TAG, "Scan Done with error %d ", status); ESP_LOGD(ROAMING_TAG, "Scan Done with error %d ", status);
} }
ROAM_SCAN_RESULTS_LOCK();
g_roaming_app.scan_ongoing = false; g_roaming_app.scan_ongoing = false;
ROAM_SCAN_RESULTS_UNLOCK();
} }
static void conduct_scan(void) static bool conduct_scan(void)
{ {
/* Update scan time in global structure */
gettimeofday(&g_roaming_app.scanned_aps.time, NULL); gettimeofday(&g_roaming_app.scanned_aps.time, NULL);
/* Issue scan */
os_memset(&g_roaming_app.scanned_aps, 0, sizeof(struct scanned_ap_info)); os_memset(&g_roaming_app.scanned_aps, 0, sizeof(struct scanned_ap_info));
/* Issue scan */
if (esp_wifi_promiscuous_scan_start(&g_roaming_app.config.scan_config, scan_done_event_handler) < 0) { if (esp_wifi_promiscuous_scan_start(&g_roaming_app.config.scan_config, scan_done_event_handler) < 0) {
ESP_LOGE(ROAMING_TAG, "failed to issue scan"); ESP_LOGE(ROAMING_TAG, "failed to issue scan");
return; return false;
} }
ESP_LOGI(ROAMING_TAG, "Issued Scan"); ESP_LOGI(ROAMING_TAG, "Issued Scan");
return true;
} }
static void determine_best_ap(int8_t rssi_threshold) static void determine_best_ap(int8_t rssi_threshold)
{ {
struct timeval now; struct timeval now;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
ROAM_SCAN_RESULTS_LOCK();
/* If the scan results are recent enough or a scan is already ongoing we should not trigger a new scan */ /* If the scan results are recent enough or a scan is already ongoing we should not trigger a new scan */
if (!g_roaming_app.scan_ongoing) { if (!g_roaming_app.scan_ongoing) {
g_roaming_app.scan_ongoing = true; g_roaming_app.scan_ongoing = true;
g_roaming_app.current_rssi_threshold = rssi_threshold; g_roaming_app.current_rssi_threshold = rssi_threshold;
if (time_diff_sec(&now,&g_roaming_app.scanned_aps.time) > SCAN_RESULTS_USABILITY_WINDOW) { if (time_diff_sec(&now,&g_roaming_app.scanned_aps.time) > SCAN_RESULTS_USABILITY_WINDOW) {
conduct_scan(); if (!conduct_scan()) {
g_roaming_app.scan_ongoing = false;
}
} else { } else {
parse_scan_results_and_roam(); parse_scan_results_and_roam();
g_roaming_app.scan_ongoing = false; g_roaming_app.scan_ongoing = false;
@@ -677,7 +811,6 @@ static void determine_best_ap(int8_t rssi_threshold)
} else if(rssi_threshold < g_roaming_app.current_rssi_threshold) { } else if(rssi_threshold < g_roaming_app.current_rssi_threshold) {
g_roaming_app.current_rssi_threshold = rssi_threshold; g_roaming_app.current_rssi_threshold = rssi_threshold;
} }
ROAM_SCAN_RESULTS_UNLOCK();
} }
#if PERIODIC_SCAN_MONITORING #if PERIODIC_SCAN_MONITORING
static void periodic_scan_roam(struct timeval *now) static void periodic_scan_roam(struct timeval *now)
@@ -780,6 +913,7 @@ static int8_t parse_scan_chan_list(void)
{ {
int8_t ret = 0; int8_t ret = 0;
char *scan_chan_string = NULL; char *scan_chan_string = NULL;
char *scan_chan_string_p = NULL;
if (validate_scan_chan_list(SCAN_PREFERRED_CHAN_LIST) == false) { if (validate_scan_chan_list(SCAN_PREFERRED_CHAN_LIST) == false) {
ESP_LOGE(ROAMING_TAG, "scan chan list validation failed."); ESP_LOGE(ROAMING_TAG, "scan chan list validation failed.");
ret = -1; ret = -1;
@@ -793,22 +927,27 @@ static int8_t parse_scan_chan_list(void)
} }
strlcpy(scan_chan_string, SCAN_PREFERRED_CHAN_LIST, strlen(SCAN_PREFERRED_CHAN_LIST) + 1); strlcpy(scan_chan_string, SCAN_PREFERRED_CHAN_LIST, strlen(SCAN_PREFERRED_CHAN_LIST) + 1);
char* token; char* token;
token = strsep(&scan_chan_string, ","); scan_chan_string_p = scan_chan_string;
token = strsep(&scan_chan_string_p, ",");
g_roaming_app.config.scan_config.channel_bitmap.ghz_2_channels = 0; g_roaming_app.config.scan_config.channel_bitmap.ghz_2_channels = 0;
g_roaming_app.config.scan_config.channel_bitmap.ghz_5_channels = 0;
while (token != NULL) { while (token != NULL) {
uint8_t channel = atoi(token); uint8_t channel = atoi(token);
/* Check if the number is within the required range */ /* Check if the number is within the required range */
if (channel >= 1 && channel <= 14) { if (channel >= 1 && channel <= 14) {
/* Check if the number is already present in the array */
g_roaming_app.config.scan_config.channel_bitmap.ghz_2_channels |= (1 << channel); g_roaming_app.config.scan_config.channel_bitmap.ghz_2_channels |= (1 << channel);
#if CONFIG_SOC_WIFI_SUPPORT_5G
} else if (channel >= 36 && channel <= 177) {
g_roaming_app.config.scan_config.channel_bitmap.ghz_5_channels |= CHANNEL_TO_BIT(channel);
#endif
} else { } else {
ESP_LOGE(ROAMING_TAG, "Channel out of range: %d", channel); ESP_LOGE(ROAMING_TAG, "Channel out of range: %d", channel);
ret = -1; ret = -1;
goto cleanup; goto cleanup;
} }
token = strsep(&scan_chan_string, ","); token = strsep(&scan_chan_string_p, ",");
} }
cleanup: cleanup:
@@ -818,7 +957,6 @@ cleanup:
return ret; return ret;
} }
static esp_err_t init_config_params(void) static esp_err_t init_config_params(void)
{ {
g_roaming_app.config.backoff_time = ROAMING_BACKOFF_TIME; g_roaming_app.config.backoff_time = ROAMING_BACKOFF_TIME;
@@ -828,9 +966,11 @@ static esp_err_t init_config_params(void)
g_roaming_app.config.rssi_threshold_reduction_offset = RSSI_THRESHOLD_REDUCTION_OFFSET; g_roaming_app.config.rssi_threshold_reduction_offset = RSSI_THRESHOLD_REDUCTION_OFFSET;
g_roaming_app.config.scan_monitor = PERIODIC_SCAN_MONITORING; g_roaming_app.config.scan_monitor = PERIODIC_SCAN_MONITORING;
#if PERIODIC_SCAN_MONITORING
g_roaming_app.config.scan_interval = SCAN_MONITOR_INTERVAL; g_roaming_app.config.scan_interval = SCAN_MONITOR_INTERVAL;
g_roaming_app.config.scan_rssi_threshold = SCAN_MONITOR_RSSI_THRESHOLD; g_roaming_app.config.scan_rssi_threshold = SCAN_MONITOR_RSSI_THRESHOLD;
g_roaming_app.config.scan_rssi_diff = SCAN_ROAM_RSSI_DIFF; g_roaming_app.config.scan_rssi_diff = SCAN_ROAM_RSSI_DIFF;
#endif /* PERIODIC_SCAN_MONITORING */
g_roaming_app.config.legacy_roam_enabled = LEGACY_ROAM_ENABLED; g_roaming_app.config.legacy_roam_enabled = LEGACY_ROAM_ENABLED;
g_roaming_app.config.btm_retry_cnt = BSS_TM_RETRY_COUNT; g_roaming_app.config.btm_retry_cnt = BSS_TM_RETRY_COUNT;
@@ -846,9 +986,11 @@ static esp_err_t init_config_params(void)
g_roaming_app.config.backoff_time, g_roaming_app.config.low_rssi_roam_trigger, g_roaming_app.config.backoff_time, g_roaming_app.config.low_rssi_roam_trigger,
g_roaming_app.config.low_rssi_threshold, g_roaming_app.config.rssi_threshold_reduction_offset); g_roaming_app.config.low_rssi_threshold, g_roaming_app.config.rssi_threshold_reduction_offset);
#if PERIODIC_SCAN_MONITORING
ESP_LOGD(ROAMING_TAG, "scan_monitor=%d scan_interval=%d scan_rssi_threshold=%d scan_rssi_diff=%d", ESP_LOGD(ROAMING_TAG, "scan_monitor=%d scan_interval=%d scan_rssi_threshold=%d scan_rssi_diff=%d",
g_roaming_app.config.scan_monitor, g_roaming_app.config.scan_interval, g_roaming_app.config.scan_monitor, g_roaming_app.config.scan_interval,
g_roaming_app.config.scan_rssi_threshold, g_roaming_app.config.scan_rssi_diff); g_roaming_app.config.scan_rssi_threshold, g_roaming_app.config.scan_rssi_diff);
#endif /* PERIODIC_SCAN_MONITORING */
ESP_LOGD(ROAMING_TAG, "legacy_roam_enabled=%d, btm_retry_cnt=%d btm_roaming_enabled=%d", ESP_LOGD(ROAMING_TAG, "legacy_roam_enabled=%d, btm_retry_cnt=%d btm_roaming_enabled=%d",
g_roaming_app.config.legacy_roam_enabled, g_roaming_app.config.legacy_roam_enabled,
@@ -864,14 +1006,6 @@ static esp_err_t init_config_params(void)
static esp_err_t init_scan_config(void) static esp_err_t init_scan_config(void)
{ {
if (!scan_results_lock) {
scan_results_lock = os_recursive_mutex_create();
if (!scan_results_lock) {
ESP_LOGE(ROAMING_TAG, "%s: failed to create scan results lock", __func__);
return ESP_FAIL;
}
}
if (strcmp(DEFAULT_PREFERRED_SCAN_CHAN_LIST, SCAN_PREFERRED_CHAN_LIST)) { if (strcmp(DEFAULT_PREFERRED_SCAN_CHAN_LIST, SCAN_PREFERRED_CHAN_LIST)) {
ESP_ERROR_CHECK(parse_scan_chan_list()); ESP_ERROR_CHECK(parse_scan_chan_list());
} }
@@ -895,6 +1029,7 @@ void roam_init_app(void)
ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized"); ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized");
return; return;
#endif #endif
memset(&g_roaming_app, 0, sizeof(g_roaming_app));
#if LOW_RSSI_ROAMING_ENABLED #if LOW_RSSI_ROAMING_ENABLED
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
&roaming_app_rssi_low_handler, NULL)); &roaming_app_rssi_low_handler, NULL));
@@ -926,23 +1061,107 @@ void roam_deinit_app(void)
#if PERIODIC_SCAN_MONITORING #if PERIODIC_SCAN_MONITORING
g_roaming_app.periodic_scan_active = false; g_roaming_app.periodic_scan_active = false;
eloop_cancel_timeout(roaming_app_periodic_scan_internal_handler, NULL, NULL);
#endif /*PERIODIC_SCAN_MONITORING*/ #endif /*PERIODIC_SCAN_MONITORING*/
#if PERIODIC_RRM_MONITORING #if PERIODIC_RRM_MONITORING
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP,
&roaming_app_neighbor_report_recv_handler)); &roaming_app_neighbor_report_recv_handler));
/* Disabling the periodic scan and RRM events */ /* Disabling the periodic scan and RRM events */
g_roaming_app.periodic_rrm_active = false; g_roaming_app.periodic_rrm_active = false;
if (neighbor_list_lock) { eloop_cancel_timeout(roaming_app_periodic_rrm_internal_handler, NULL, NULL);
os_mutex_delete(neighbor_list_lock); if (g_roaming_app.btm_neighbor_list) {
neighbor_list_lock = NULL; os_free(g_roaming_app.btm_neighbor_list);
g_roaming_app.btm_neighbor_list = NULL;
} }
#endif /*PERIODIC_RRM_MONITORING*/ #endif /*PERIODIC_RRM_MONITORING*/
if (scan_results_lock) {
os_mutex_delete(scan_results_lock);
scan_results_lock = NULL;
}
} }
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
static void roaming_app_blacklist_add_handler(void *ctx, void *data)
{
uint8_t *bssid = data;
if (g_roaming_app.bssid_blacklist_count >= CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES) {
ESP_LOGE(ROAMING_TAG, "Blacklist is full");
os_free(bssid);
return;
}
for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) {
if (memcmp(g_roaming_app.bssid_blacklist[i].bssid, bssid, ETH_ALEN) == 0) {
ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " already in blacklist", MAC2STR(bssid));
os_free(bssid);
return; // Already blacklisted
}
}
memcpy(g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].bssid, bssid, ETH_ALEN);
#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING
g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].failures = CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES;
#endif
gettimeofday(&g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].timestamp, NULL);
g_roaming_app.bssid_blacklist_count++;
ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " added to blacklist", MAC2STR(bssid));
os_free(bssid);
}
esp_err_t esp_wifi_blacklist_add(const uint8_t *bssid)
{
if (!bssid) {
return ESP_ERR_INVALID_ARG;
}
uint8_t *bssid_copy = os_malloc(ETH_ALEN);
if (!bssid_copy) {
return ESP_ERR_NO_MEM;
}
memcpy(bssid_copy, bssid, ETH_ALEN);
if (eloop_register_timeout(0, 0, roaming_app_blacklist_add_handler, NULL, bssid_copy) != 0) {
os_free(bssid_copy);
return ESP_FAIL;
}
return ESP_OK;
}
static void roaming_app_blacklist_remove_handler(void *ctx, void *data)
{
uint8_t *bssid = data;
int found_index = -1;
for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) {
if (memcmp(g_roaming_app.bssid_blacklist[i].bssid, bssid, ETH_ALEN) == 0) {
found_index = i;
break;
}
}
if (found_index != -1) {
// Shift elements to fill the gap
int remaining_entries = g_roaming_app.bssid_blacklist_count - found_index - 1;
if (remaining_entries > 0) {
memmove(&g_roaming_app.bssid_blacklist[found_index], &g_roaming_app.bssid_blacklist[found_index + 1], remaining_entries * sizeof(struct blacklist_entry));
}
g_roaming_app.bssid_blacklist_count--;
ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " removed from blacklist", MAC2STR(bssid));
} else {
ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " not found in blacklist", MAC2STR(bssid));
}
os_free(bssid);
}
esp_err_t esp_wifi_blacklist_remove(const uint8_t *bssid)
{
if (!bssid) {
return ESP_ERR_INVALID_ARG;
}
uint8_t *bssid_copy = os_malloc(ETH_ALEN);
if (!bssid_copy) {
return ESP_ERR_NO_MEM;
}
memcpy(bssid_copy, bssid, ETH_ALEN);
if (eloop_register_timeout(0, 0, roaming_app_blacklist_remove_handler, NULL, bssid_copy) != 0) {
os_free(bssid_copy);
return ESP_FAIL;
}
return ESP_OK;
}
#endif
/* No need for this to be done in pptask ctx */ /* No need for this to be done in pptask ctx */
esp_err_t roam_get_config_params(struct roam_config *config) esp_err_t roam_get_config_params(struct roam_config *config)
{ {
@@ -962,9 +1181,11 @@ static int update_config_params(void *data)
g_roaming_app.config.backoff_time, g_roaming_app.config.low_rssi_roam_trigger, g_roaming_app.config.backoff_time, g_roaming_app.config.low_rssi_roam_trigger,
g_roaming_app.config.low_rssi_threshold, g_roaming_app.config.rssi_threshold_reduction_offset); g_roaming_app.config.low_rssi_threshold, g_roaming_app.config.rssi_threshold_reduction_offset);
#if PERIODIC_SCAN_MONITORING
ESP_LOGI(ROAMING_TAG, "scan_monitor=%d scan_interval=%d scan_rssi_threshold=%d scan_rssi_diff=%d", ESP_LOGI(ROAMING_TAG, "scan_monitor=%d scan_interval=%d scan_rssi_threshold=%d scan_rssi_diff=%d",
g_roaming_app.config.scan_monitor, g_roaming_app.config.scan_interval, g_roaming_app.config.scan_monitor, g_roaming_app.config.scan_interval,
g_roaming_app.config.scan_rssi_threshold, g_roaming_app.config.scan_rssi_diff); g_roaming_app.config.scan_rssi_threshold, g_roaming_app.config.scan_rssi_diff);
#endif
ESP_LOGI(ROAMING_TAG, "legacy_roam_enabled=%d, btm_retry_cnt=%d btm_roaming_enabled=%d", ESP_LOGI(ROAMING_TAG, "legacy_roam_enabled=%d, btm_retry_cnt=%d btm_roaming_enabled=%d",
g_roaming_app.config.legacy_roam_enabled, g_roaming_app.config.legacy_roam_enabled,
+1
View File
@@ -245,6 +245,7 @@ idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src}
INCLUDE_DIRS include port/include esp_supplicant/include INCLUDE_DIRS include port/include esp_supplicant/include
PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto
../esp_wifi/wifi_apps/roaming_app/include ../esp_wifi/wifi_apps/roaming_app/include
../esp_wifi/wifi_apps/roaming_app/src
LDFRAGMENTS ${linker_fragments} LDFRAGMENTS ${linker_fragments}
PRIV_REQUIRES mbedtls esp_timer esp_wifi) PRIV_REQUIRES mbedtls esp_timer esp_wifi)
@@ -23,6 +23,8 @@
#include "rsn_supp/wpa_i.h" #include "rsn_supp/wpa_i.h"
#include "rsn_supp/wpa.h" #include "rsn_supp/wpa.h"
#include "esp_private/wifi.h" #include "esp_private/wifi.h"
#include "esp_wifi_types_generic.h"
#include "esp_roaming.h"
/* Utility Functions */ /* Utility Functions */
esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6]) esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6])
@@ -628,6 +630,9 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
config->sta.channel = bss->channel; config->sta.channel = bss->channel;
/* supplicant connect will only be called in case of bss transition(roaming) */ /* supplicant connect will only be called in case of bss transition(roaming) */
esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC); esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC);
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
esp_wifi_roaming_set_current_bssid(bss->bssid);
#endif
esp_wifi_set_config(WIFI_IF_STA, config); esp_wifi_set_config(WIFI_IF_STA, config);
os_free(config); os_free(config);
esp_wifi_connect(); esp_wifi_connect();
@@ -31,6 +31,7 @@
#include "esp_eap_client.h" #include "esp_eap_client.h"
#include "esp_common_i.h" #include "esp_common_i.h"
#include "esp_owe_i.h" #include "esp_owe_i.h"
#include "esp_roaming.h"
#include "esp_wps.h" #include "esp_wps.h"
#include "esp_wps_i.h" #include "esp_wps_i.h"
@@ -140,6 +141,9 @@ bool wpa_attach(void)
ret = (esp_wifi_register_eapol_txdonecb_internal(eapol_txcb) == ESP_OK); ret = (esp_wifi_register_eapol_txdonecb_internal(eapol_txcb) == ESP_OK);
} }
esp_set_scan_ie(); esp_set_scan_ie();
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
roam_init_app();
#endif
return ret; return ret;
} }
@@ -192,6 +196,9 @@ void wpa_ap_get_peer_spp_msg(void *sm_data, bool *spp_cap, bool *spp_req)
bool wpa_deattach(void) bool wpa_deattach(void)
{ {
struct wpa_sm *sm = &gWpaSm; struct wpa_sm *sm = &gWpaSm;
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
roam_deinit_app();
#endif
esp_wpa3_free_sae_data(); esp_wpa3_free_sae_data();
#ifdef CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT #ifdef CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT
if (sm->wpa_sm_eap_disable) { if (sm->wpa_sm_eap_disable) {
@@ -189,4 +189,9 @@ void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code)
{ {
/*check if need clear internal state and data value*/ /*check if need clear internal state and data value*/
} }
u8 wpa_supplicant_get_transition_disable(void)
{
return wpa_sm_get_transition_disable(&gWpaSm);
}
#endif #endif
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -7,6 +7,8 @@
#ifndef WPAS_GLUE_H #ifndef WPAS_GLUE_H
#define WPAS_GLUE_H #define WPAS_GLUE_H
#include "rsn_supp/wpa_i.h"
u8 *wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, u8 *wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
const void *data, u16 data_len, const void *data, u16 data_len,
size_t *msg_len, void **data_pos); size_t *msg_len, void **data_pos);
@@ -35,4 +37,6 @@ void wpa_supplicant_transition_disable(struct wpa_sm *sm, u8 bitmap);
int hostapd_send_eapol(const u8 *source, const u8 *sta_addr, int hostapd_send_eapol(const u8 *source, const u8 *sta_addr,
const u8 *data, size_t data_len); const u8 *data, size_t data_len);
u8 wpa_supplicant_get_transition_disable(void);
#endif /* WPAS_GLUE_H */ #endif /* WPAS_GLUE_H */
@@ -1340,6 +1340,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
if (ie.transition_disable) { if (ie.transition_disable) {
wpa_supplicant_transition_disable(sm, ie.transition_disable[0]); wpa_supplicant_transition_disable(sm, ie.transition_disable[0]);
sm->transition_disable = ie.transition_disable[0];
} }
if (sm->key_install && sm->key_info & WPA_KEY_INFO_INSTALL && sm->use_ext_key_id) { if (sm->key_install && sm->key_info & WPA_KEY_INFO_INSTALL && sm->use_ext_key_id) {
@@ -1357,6 +1358,11 @@ failed:
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
} }
u8 wpa_sm_get_transition_disable(struct wpa_sm *sm)
{
return sm->transition_disable;
}
static int wpa_supplicant_activate_ptk(struct wpa_sm *sm) static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
{ {
int keylen; int keylen;
@@ -9,6 +9,8 @@
#ifndef WPA_I_H #ifndef WPA_I_H
#define WPA_I_H #define WPA_I_H
#include "common/defs.h"
extern struct wpa_sm gWpaSm; extern struct wpa_sm gWpaSm;
#define DEFAULT_EAPOL_VERSION 1 #define DEFAULT_EAPOL_VERSION 1
@@ -86,6 +88,7 @@ struct wpa_sm {
struct wpa_gtk_data gd; //used for calllback save param struct wpa_gtk_data gd; //used for calllback save param
u16 key_info; //used for txcallback param u16 key_info; //used for txcallback param
u16 txcb_flags; u16 txcb_flags;
u8 transition_disable;
bool ap_notify_completed_rsne; bool ap_notify_completed_rsne;
wifi_pmf_config_t pmf_cfg; wifi_pmf_config_t pmf_cfg;
u8 eapol1_count; u8 eapol1_count;
@@ -207,6 +210,8 @@ int wpa_set_bss(char *macddr, char * bssid, u8 pairwise_cipher, u8 group_cipher,
int wpa_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len); int wpa_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len);
u8 wpa_sm_get_transition_disable(struct wpa_sm *sm);
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk); const struct wpa_eapol_key *key, struct wpa_ptk *ptk);
#endif /* WPA_I_H */ #endif /* WPA_I_H */