Merge branch 'feat/roaming_app_blacklist' into 'master'

fix(esp_wifi): Add blacklist logic in roaming app

Closes WIFIBUG-1219, WIFIBUG-1208, WIFIBUG-1109, WIFIBUG-1203, and WIFIBUG-1428

See merge request espressif/esp-idf!40802
This commit is contained in:
Kapil Gupta
2025-09-28 21:44:31 +05:30
11 changed files with 339 additions and 36 deletions

View File

@@ -63,7 +63,7 @@ idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES esp_pm esp_timer nvs_flash PRIV_REQUIRES esp_pm esp_timer nvs_flash
wpa_supplicant hal lwip esp_coex wpa_supplicant hal lwip esp_coex
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)

View File

@@ -37,6 +37,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

View File

@@ -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"
@@ -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"

View File

@@ -134,6 +134,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;
}; };

View File

@@ -28,7 +28,10 @@
#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);
@@ -44,6 +47,7 @@ static void *neighbor_list_lock = NULL;
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 +57,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);
@@ -179,12 +190,63 @@ 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
@@ -196,6 +258,9 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data)
} }
#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);
} }
@@ -444,6 +509,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));
@@ -492,7 +558,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 +594,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 +610,35 @@ 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)
{ {
u8 transition_disable = wpa_supplicant_get_transition_disable();
#if CONFIG_ESP_WIFI_ROAMING_PREVENT_DOWNGRADE
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) {
/* This is a simplification. A more accurate check would involve
* parsing the candidate's RSN IE to see if it supports SAE-PK.
* For now, we reject all SAE APs if SAE-PK is enforced. */
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);
@@ -581,10 +674,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;
} }
#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;
}
/* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */ /* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */
static void parse_scan_results_and_roam(void) static void parse_scan_results_and_roam(void)
{ {
@@ -594,16 +722,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;
} }
@@ -646,7 +776,7 @@ static void scan_done_event_handler(void *arg, ETS_STATUS status)
g_roaming_app.scan_ongoing = false; g_roaming_app.scan_ongoing = false;
ROAM_SCAN_RESULTS_UNLOCK(); ROAM_SCAN_RESULTS_UNLOCK();
} }
static void conduct_scan(void) static bool conduct_scan(void)
{ {
/* Update scan time in global structure */ /* Update scan time in global structure */
gettimeofday(&g_roaming_app.scanned_aps.time, NULL); gettimeofday(&g_roaming_app.scanned_aps.time, NULL);
@@ -654,9 +784,10 @@ static void conduct_scan(void)
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));
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)
@@ -669,7 +800,9 @@ static void determine_best_ap(int8_t rssi_threshold)
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;
@@ -818,7 +951,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;
@@ -895,6 +1027,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));
@@ -932,6 +1065,10 @@ void roam_deinit_app(void)
&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 (g_roaming_app.btm_neighbor_list) {
os_free(g_roaming_app.btm_neighbor_list);
g_roaming_app.btm_neighbor_list = NULL;
}
if (neighbor_list_lock) { if (neighbor_list_lock) {
os_mutex_delete(neighbor_list_lock); os_mutex_delete(neighbor_list_lock);
neighbor_list_lock = NULL; neighbor_list_lock = NULL;
@@ -943,6 +1080,92 @@ void roam_deinit_app(void)
} }
} }
#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)
{ {

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)

View File

@@ -24,6 +24,7 @@
#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_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])
@@ -643,6 +644,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();

View File

@@ -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

View File

@@ -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 */

View File

@@ -1434,6 +1434,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) {
@@ -1451,6 +1452,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;

View File

@@ -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
@@ -97,6 +99,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;
@@ -223,6 +226,8 @@ int wpa_set_bss(uint8_t *macddr, uint8_t *bssid, uint8_t pairwise_cipher, uint8_
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);
#ifdef CONFIG_WPA3_COMPAT #ifdef CONFIG_WPA3_COMPAT