From a7d20669136ab260f7ebd5acd29f4abdabf43254 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Fri, 27 Jun 2025 23:05:37 +0530 Subject: [PATCH 1/5] fix(esp_wifi): Add blacklist logic in roaming app --- .../wifi_apps/roaming_app/src/Kconfig.roaming | 24 ++++ .../wifi_apps/roaming_app/src/esp_roaming_i.h | 9 ++ .../wifi_apps/roaming_app/src/roaming_app.c | 125 ++++++++++++++++++ 3 files changed, 158 insertions(+) diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming index 22f641377d..3b19d8d568 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming +++ b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming @@ -182,3 +182,27 @@ config ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD range -99 0 help The RSSI threshold beyond which we start sending periodic neighbor report requests. + +menu "Blacklist Configuration" + config ESP_WIFI_ROAMING_AUTO_BLACKLISTING + bool "Enable automatic BSSID blacklisting" + default y + 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_AUTO_BLACKLISTING + range 10 3600 + default 300 + help + Time in seconds for which a BSSID remains in the blacklist. +endmenu # "Blacklist Configuration" diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h b/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h index 9ca515e6af..73c7d0f48f 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h +++ b/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h @@ -134,6 +134,15 @@ struct roaming_app { #endif #if PERIODIC_SCAN_MONITORING bool periodic_scan_active; +#endif +#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING + struct blacklist_entry { + uint8_t bssid[ETH_ALEN]; + uint8_t failures; + struct timeval timestamp; + }; + struct blacklist_entry bssid_blacklist[CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES]; + uint8_t bssid_blacklist_count; #endif bool allow_reconnect; }; diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c index 9f2d37261b..e0a77244aa 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c +++ b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c @@ -180,6 +180,30 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data) wifi_event_sta_disconnected_t *disconn = data; ESP_LOGD(ROAMING_TAG, "station got disconnected reason=%d", disconn->reason); +#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, disconn->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(disconn->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(disconn->bssid)); + } + found = true; + break; + } + } + if (!found && 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, disconn->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(disconn->bssid)); + } + } +#endif if (disconn->reason == WIFI_REASON_ROAMING) { ESP_LOGD(ROAMING_TAG, "station roaming, do nothing"); } else if (g_roaming_app.allow_reconnect == false) { @@ -585,6 +609,50 @@ static bool candidate_profile_match(wifi_ap_record_t candidate) { return candidate_security_match(candidate); } +#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST +static bool is_bssid_blacklisted(const uint8_t *bssid) +{ + for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) { + if (memcmp(g_roaming_app.bssid_blacklist[i], bssid, ETH_ALEN) == 0) { + return true; + } + } + return false; +} +#endif + +#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING +static void remove_expired_blacklist_entries(void) +{ + struct timeval now; + gettimeofday(&now, NULL); + 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) { + ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " removed from blacklist due to timeout", MAC2STR(g_roaming_app.bssid_blacklist[i].bssid)); + for (int j = i; j < g_roaming_app.bssid_blacklist_count - 1; j++) { + g_roaming_app.bssid_blacklist[j] = g_roaming_app.bssid_blacklist[j + 1]; + } + g_roaming_app.bssid_blacklist_count--; + i--; // Decrement i to recheck the current index + } + } +} +#endif + +static bool is_bssid_blacklisted(const uint8_t *bssid) +{ +#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING + 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 && + g_roaming_app.bssid_blacklist[i].failures >= CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES) { + return true; + } + } +#endif + return false; +} + /* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */ static void parse_scan_results_and_roam(void) { @@ -597,6 +665,10 @@ static void parse_scan_results_and_roam(void) 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++) { + 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 - ap_info.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", MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(ap_info.bssid), @@ -905,6 +977,10 @@ void roam_init_app(void) ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, &roaming_app_neighbor_report_recv_handler, NULL)); #endif /*PERIODIC_RRM_MONITORING*/ +#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING + memset(g_roaming_app.bssid_blacklist, 0, sizeof(g_roaming_app.bssid_blacklist)); + g_roaming_app.bssid_blacklist_count = 0; +#endif ESP_LOGI(ROAMING_TAG, "Roaming app initialization done"); } @@ -943,6 +1019,55 @@ void roam_deinit_app(void) } } +#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST +esp_err_t esp_wifi_blacklist_add(const uint8_t *bssid) +{ + if (!bssid) { + return ESP_ERR_INVALID_ARG; + } + if (g_roaming_app.bssid_blacklist_count >= CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST_COUNT) { + ESP_LOGE(ROAMING_TAG, "Blacklist is full"); + return ESP_ERR_NO_MEM; + } + for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) { + if (memcmp(g_roaming_app.bssid_blacklist[i], bssid, ETH_ALEN) == 0) { + ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " already in blacklist", MAC2STR(bssid)); + return ESP_OK; // Already blacklisted + } + } + memcpy(g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count], bssid, ETH_ALEN); + g_roaming_app.bssid_blacklist_count++; + ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " added to blacklist", MAC2STR(bssid)); + return ESP_OK; +} + +esp_err_t esp_wifi_blacklist_remove(const uint8_t *bssid) +{ + if (!bssid) { + return ESP_ERR_INVALID_ARG; + } + 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, ETH_ALEN) == 0) { + found_index = i; + break; + } + } + + if (found_index != -1) { + // Shift elements to fill the gap + for (int i = found_index; i < g_roaming_app.bssid_blacklist_count - 1; i++) { + memcpy(g_roaming_app.bssid_blacklist[i], g_roaming_app.bssid_blacklist[i + 1], ETH_ALEN); + } + 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)); + } + return ESP_OK; +} +#endif + /* No need for this to be done in pptask ctx */ esp_err_t roam_get_config_params(struct roam_config *config) { From 074b2d0f93d6be82eda07b56d4fa1338dd4a2749 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Fri, 25 Jul 2025 12:12:41 +0530 Subject: [PATCH 2/5] fix(roaming_app): resolve issues in blacklisting logic This commit addresses several issues in the BSSID blacklisting feature of the roaming application: - Merged duplicate functions into a single, unified function, resolving a compilation error. - Corrected and to properly access the member of the struct, fixing invalid memory access. - Introduced in Kconfig to enable the manual blacklisting feature and made auto-blacklisting dependent on it. - Updated to use the correct BSSID from . - Optimized the removal of expired blacklist entries by using for better efficiency. --- .../wifi_apps/roaming_app/src/Kconfig.roaming | 14 ++++++ .../wifi_apps/roaming_app/src/esp_roaming_i.h | 3 +- .../wifi_apps/roaming_app/src/roaming_app.c | 48 +++++++++---------- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming index 3b19d8d568..dd95bee9e4 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming +++ b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming @@ -184,8 +184,15 @@ config ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD 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 default y help Enable this to automatically blacklist BSSIDs after multiple failed connection attempts. @@ -205,4 +212,11 @@ menu "Blacklist Configuration" default 300 help Time in seconds for which a BSSID remains in the blacklist. + + 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. endmenu # "Blacklist Configuration" diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h b/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h index 73c7d0f48f..8c8efbc2cf 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h +++ b/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h @@ -140,8 +140,7 @@ struct roaming_app { uint8_t bssid[ETH_ALEN]; uint8_t failures; struct timeval timestamp; - }; - struct blacklist_entry bssid_blacklist[CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES]; + } bssid_blacklist[CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES]; uint8_t bssid_blacklist_count; #endif bool allow_reconnect; diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c index e0a77244aa..3bff2d2ae5 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c +++ b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c @@ -184,23 +184,23 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data) 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, disconn->bssid, ETH_ALEN) == 0) { + 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(disconn->bssid), g_roaming_app.bssid_blacklist[i].failures); + 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(disconn->bssid)); + ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " blacklisted", MAC2STR(g_roaming_app.current_bss.ap.bssid)); } found = true; break; } } if (!found && 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, disconn->bssid, ETH_ALEN); + 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(disconn->bssid)); + ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " added to blacklist tracking", MAC2STR(g_roaming_app.current_bss.ap.bssid)); } } #endif @@ -609,17 +609,6 @@ static bool candidate_profile_match(wifi_ap_record_t candidate) { return candidate_security_match(candidate); } -#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST -static bool is_bssid_blacklisted(const uint8_t *bssid) -{ - for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) { - if (memcmp(g_roaming_app.bssid_blacklist[i], bssid, ETH_ALEN) == 0) { - return true; - } - } - return false; -} -#endif #if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING static void remove_expired_blacklist_entries(void) @@ -629,8 +618,9 @@ static void remove_expired_blacklist_entries(void) 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) { ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " removed from blacklist due to timeout", MAC2STR(g_roaming_app.bssid_blacklist[i].bssid)); - for (int j = i; j < g_roaming_app.bssid_blacklist_count - 1; j++) { - g_roaming_app.bssid_blacklist[j] = g_roaming_app.bssid_blacklist[j + 1]; + int remaining_entries = g_roaming_app.bssid_blacklist_count - i - 1; + if (remaining_entries > 0) { + memmove(&g_roaming_app.bssid_blacklist[i], &g_roaming_app.bssid_blacklist[i + 1], remaining_entries * sizeof(struct blacklist_entry)); } g_roaming_app.bssid_blacklist_count--; i--; // Decrement i to recheck the current index @@ -649,6 +639,13 @@ static bool is_bssid_blacklisted(const uint8_t *bssid) return true; } } +#endif +#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST + 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) { + return true; + } + } #endif return false; } @@ -1025,17 +1022,19 @@ esp_err_t esp_wifi_blacklist_add(const uint8_t *bssid) if (!bssid) { return ESP_ERR_INVALID_ARG; } - if (g_roaming_app.bssid_blacklist_count >= CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST_COUNT) { + if (g_roaming_app.bssid_blacklist_count >= CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES) { ESP_LOGE(ROAMING_TAG, "Blacklist is full"); return ESP_ERR_NO_MEM; } for (int i = 0; i < g_roaming_app.bssid_blacklist_count; i++) { - if (memcmp(g_roaming_app.bssid_blacklist[i], bssid, ETH_ALEN) == 0) { + if (memcmp(g_roaming_app.bssid_blacklist[i].bssid, bssid, ETH_ALEN) == 0) { ESP_LOGD(ROAMING_TAG, "BSSID " MACSTR " already in blacklist", MAC2STR(bssid)); return ESP_OK; // Already blacklisted } } - memcpy(g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count], bssid, ETH_ALEN); + memcpy(g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].bssid, bssid, ETH_ALEN); + g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].failures = CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES; + 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)); return ESP_OK; @@ -1048,7 +1047,7 @@ esp_err_t esp_wifi_blacklist_remove(const uint8_t *bssid) } 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, ETH_ALEN) == 0) { + if (memcmp(g_roaming_app.bssid_blacklist[i].bssid, bssid, ETH_ALEN) == 0) { found_index = i; break; } @@ -1056,8 +1055,9 @@ esp_err_t esp_wifi_blacklist_remove(const uint8_t *bssid) if (found_index != -1) { // Shift elements to fill the gap - for (int i = found_index; i < g_roaming_app.bssid_blacklist_count - 1; i++) { - memcpy(g_roaming_app.bssid_blacklist[i], g_roaming_app.bssid_blacklist[i + 1], ETH_ALEN); + 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)); From 7d18b82575df9ebd814b65dd3bb8d4c6e13c1467 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Fri, 25 Jul 2025 14:06:51 +0530 Subject: [PATCH 3/5] feat(roaming): ignore WPA2-only APs on transition disable This commit introduces a new feature to the roaming logic. If the currently connected AP has the 'transition disable' bit set in its RSN IE, the roaming logic will now ignore any scanned APs that only support WPA2-PSK. This prevents a security downgrade when roaming in a mixed WPA2/WPA3 environment. A new Kconfig option, CONFIG_ESP_WIFI_IGNORE_WPA2_ONLY_ON_TRANSITION_DISABLE, has been added to control this feature. It is disabled by default. --- .../wifi_apps/roaming_app/src/Kconfig.roaming | 9 ++++++ .../wifi_apps/roaming_app/src/roaming_app.c | 32 +++++++++++++++++++ .../esp_supplicant/src/esp_wpas_glue.c | 5 +++ .../esp_supplicant/src/esp_wpas_glue.h | 4 ++- components/wpa_supplicant/src/rsn_supp/wpa.c | 6 ++++ .../wpa_supplicant/src/rsn_supp/wpa_i.h | 3 ++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming index dd95bee9e4..221dff7355 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming +++ b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming @@ -219,4 +219,13 @@ menu "Blacklist Configuration" 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" diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c index 3bff2d2ae5..4d4265ddbf 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c +++ b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c @@ -605,8 +605,40 @@ static bool candidate_security_match(wifi_ap_record_t candidate) return false; } +#include "esp_wpas_glue.h" + static bool candidate_profile_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 + return candidate_security_match(candidate); } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c index 948f271397..42cbbe92d8 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c @@ -189,4 +189,9 @@ void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) { /*check if need clear internal state and data value*/ } + +u8 wpa_supplicant_get_transition_disable(void) +{ + return wpa_sm_get_transition_disable(&gWpaSm); +} #endif diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h index 48ff0a6379..775c5b29f0 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h @@ -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 */ @@ -35,4 +35,6 @@ void wpa_supplicant_transition_disable(struct wpa_sm *sm, u8 bitmap); int hostapd_send_eapol(const u8 *source, const u8 *sta_addr, const u8 *data, size_t data_len); + +u8 wpa_supplicant_get_transition_disable(void); #endif /* WPAS_GLUE_H */ diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index 75fe1f5094..4dfcd00a41 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -1340,6 +1340,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (ie.transition_disable) { 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) { @@ -1357,6 +1358,11 @@ failed: 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) { int keylen; diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_i.h b/components/wpa_supplicant/src/rsn_supp/wpa_i.h index b31aeb1e1a..b07015eede 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_i.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_i.h @@ -86,6 +86,7 @@ struct wpa_sm { struct wpa_gtk_data gd; //used for calllback save param u16 key_info; //used for txcallback param u16 txcb_flags; + u8 transition_disable; bool ap_notify_completed_rsne; wifi_pmf_config_t pmf_cfg; u8 eapol1_count; @@ -208,6 +209,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); +u8 wpa_sm_get_transition_disable(struct wpa_sm *sm); + int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk); #endif /* WPA_I_H */ From 0f3034586bd45d7acf936a56f453a6e1be76bc7d Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Tue, 9 Sep 2025 14:58:03 +0530 Subject: [PATCH 4/5] fix(esp_wifi): Address some review comments and issues --- .../wifi_apps/roaming_app/src/Kconfig.roaming | 12 +- .../wifi_apps/roaming_app/src/esp_roaming_i.h | 4 +- .../wifi_apps/roaming_app/src/roaming_app.c | 252 +++++++++++------- .../esp_supplicant/src/esp_wpas_glue.h | 2 + .../wpa_supplicant/src/rsn_supp/wpa_i.h | 2 + 5 files changed, 164 insertions(+), 108 deletions(-) diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming index 221dff7355..7406d9b12f 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming +++ b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming @@ -139,13 +139,7 @@ menu "Scan Configuration" Duration for which the results from the most recent scans can be used 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" @@ -193,7 +187,6 @@ menu "Blacklist Configuration" config ESP_WIFI_ROAMING_AUTO_BLACKLISTING bool "Enable automatic BSSID blacklisting" depends on ESP_WIFI_ROAMING_BSSID_BLACKLIST - default y help Enable this to automatically blacklist BSSIDs after multiple failed connection attempts. @@ -207,11 +200,12 @@ menu "Blacklist Configuration" config ESP_WIFI_ROAMING_BLACKLIST_TIMEOUT int "Blacklist timeout (in seconds)" - depends on ESP_WIFI_ROAMING_AUTO_BLACKLISTING + 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" diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h b/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h index 8c8efbc2cf..4afda4c0aa 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h +++ b/components/esp_wifi/wifi_apps/roaming_app/src/esp_roaming_i.h @@ -135,10 +135,12 @@ struct roaming_app { #if PERIODIC_SCAN_MONITORING bool periodic_scan_active; #endif -#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING +#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; diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c index 4d4265ddbf..9d59c91a90 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c +++ b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c @@ -28,7 +28,10 @@ #include "utils/eloop.h" #include "rom/ets_sys.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); 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 void determine_best_ap(int8_t rssi_threshold); +static bool is_bssid_blacklisted(const uint8_t *bssid); #if PERIODIC_RRM_MONITORING static void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx); #endif @@ -190,17 +194,34 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data) 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 && 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)); + 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 @@ -209,17 +230,26 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data) } else if (g_roaming_app.allow_reconnect == false) { ESP_LOGD(ROAMING_TAG, "station initiated disconnect, do nothing"); } 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 - /* - * 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 - * roaming attempts using BTM could lead to a spiral of connecting to - * the previous AP */ - if (g_roaming_app.force_roam_ongoing) { - legacy_roam_clear_bssid_flag(); - } + /* + * 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 + * roaming attempts using BTM could lead to a spiral of connecting to + * the previous AP */ + if (g_roaming_app.force_roam_ongoing) { + legacy_roam_clear_bssid_flag(); + } #endif /*LEGACY_ROAM_ENABLED*/ - esp_wifi_connect(); + esp_wifi_connect(); +#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST + } +#endif } os_free(disconn); } @@ -482,7 +512,7 @@ void roaming_app_trigger_roam(struct cand_bss *bss) gettimeofday(&now, NULL); 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 ) { - 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; } #if NETWORK_ASSISTED_ROAMING_ENABLED @@ -516,7 +546,6 @@ void roaming_app_trigger_roam_internal_handler(void *ctx, void *data) } else { roaming_app_trigger_roam((struct cand_bss *)data); } - } static int wifi_post_roam_event(struct cand_bss *bss) @@ -553,7 +582,6 @@ void print_ap_records(struct scanned_ap_info *ap_info) } - #if PERIODIC_RRM_MONITORING static void periodic_rrm_request(struct timeval *now) { @@ -570,6 +598,35 @@ static void periodic_rrm_request(struct timeval *now) 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 cand_auth = candidate.authmode; ESP_LOGV(ROAMING_TAG, "Cand authmode : %d, Current Authmode : %d", cand_auth, curr_auth); @@ -605,77 +662,39 @@ static bool candidate_security_match(wifi_ap_record_t candidate) return false; } -#include "esp_wpas_glue.h" - -static bool candidate_profile_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 - - return candidate_security_match(candidate); -} - -#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING +#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST static void remove_expired_blacklist_entries(void) { 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) { - ESP_LOGI(ROAMING_TAG, "BSSID " MACSTR " removed from blacklist due to timeout", MAC2STR(g_roaming_app.bssid_blacklist[i].bssid)); - int remaining_entries = g_roaming_app.bssid_blacklist_count - i - 1; - if (remaining_entries > 0) { - memmove(&g_roaming_app.bssid_blacklist[i], &g_roaming_app.bssid_blacklist[i + 1], remaining_entries * sizeof(struct blacklist_entry)); + 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]; } - g_roaming_app.bssid_blacklist_count--; - i--; // Decrement i to recheck the current index + 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_AUTO_BLACKLISTING +#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 && - g_roaming_app.bssid_blacklist[i].failures >= CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES) { - return true; - } - } -#endif -#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST 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 @@ -704,7 +723,7 @@ static void parse_scan_results_and_roam(void) g_roaming_app.scanned_aps.ap_records[i].rssi, ap_info.rssi, 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) && - 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_ap_index = i; } @@ -747,7 +766,7 @@ static void scan_done_event_handler(void *arg, ETS_STATUS status) 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); @@ -755,9 +774,10 @@ static void conduct_scan(void) 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) { ESP_LOGE(ROAMING_TAG, "failed to issue scan"); - return; + return false; } ESP_LOGI(ROAMING_TAG, "Issued Scan"); + return true; } static void determine_best_ap(int8_t rssi_threshold) @@ -770,7 +790,9 @@ static void determine_best_ap(int8_t rssi_threshold) g_roaming_app.scan_ongoing = true; g_roaming_app.current_rssi_threshold = rssi_threshold; 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 { parse_scan_results_and_roam(); g_roaming_app.scan_ongoing = false; @@ -919,7 +941,6 @@ cleanup: return ret; } - static esp_err_t init_config_params(void) { g_roaming_app.config.backoff_time = ROAMING_BACKOFF_TIME; @@ -996,6 +1017,7 @@ void roam_init_app(void) ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized"); return; #endif + memset(&g_roaming_app, 0, sizeof(g_roaming_app)); #if LOW_RSSI_ROAMING_ENABLED ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, &roaming_app_rssi_low_handler, NULL)); @@ -1006,10 +1028,6 @@ void roam_init_app(void) ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, &roaming_app_neighbor_report_recv_handler, NULL)); #endif /*PERIODIC_RRM_MONITORING*/ -#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING - memset(g_roaming_app.bssid_blacklist, 0, sizeof(g_roaming_app.bssid_blacklist)); - g_roaming_app.bssid_blacklist_count = 0; -#endif ESP_LOGI(ROAMING_TAG, "Roaming app initialization done"); } @@ -1037,6 +1055,10 @@ void roam_deinit_app(void) &roaming_app_neighbor_report_recv_handler)); /* Disabling the periodic scan and RRM events */ 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) { os_mutex_delete(neighbor_list_lock); neighbor_list_lock = NULL; @@ -1049,34 +1071,51 @@ 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; } - if (g_roaming_app.bssid_blacklist_count >= CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES) { - ESP_LOGE(ROAMING_TAG, "Blacklist is full"); + uint8_t *bssid_copy = os_malloc(ETH_ALEN); + if (!bssid_copy) { return ESP_ERR_NO_MEM; } - 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)); - return ESP_OK; // Already blacklisted - } + 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; } - memcpy(g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].bssid, bssid, ETH_ALEN); - g_roaming_app.bssid_blacklist[g_roaming_app.bssid_blacklist_count].failures = CONFIG_ESP_WIFI_ROAMING_MAX_CONN_FAILURES; - 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)); return ESP_OK; } -esp_err_t esp_wifi_blacklist_remove(const uint8_t *bssid) +static void roaming_app_blacklist_remove_handler(void *ctx, void *data) { - if (!bssid) { - return ESP_ERR_INVALID_ARG; - } + 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) { @@ -1096,6 +1135,23 @@ esp_err_t esp_wifi_blacklist_remove(const uint8_t *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 diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h index 775c5b29f0..1a1d57993b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h @@ -7,6 +7,8 @@ #ifndef WPAS_GLUE_H #define WPAS_GLUE_H +#include "rsn_supp/wpa_i.h" + u8 *wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos); diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_i.h b/components/wpa_supplicant/src/rsn_supp/wpa_i.h index b07015eede..909073e610 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_i.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_i.h @@ -9,6 +9,8 @@ #ifndef WPA_I_H #define WPA_I_H +#include "common/defs.h" + extern struct wpa_sm gWpaSm; #define DEFAULT_EAPOL_VERSION 1 From fdda62267e7ad3e7ea68f0ca81f4b80aa31a447c Mon Sep 17 00:00:00 2001 From: "tarun.kumar" Date: Tue, 16 Sep 2025 21:38:38 +0530 Subject: [PATCH 5/5] fix(wifi) : Resetting current bssid for every roaming connection intiated - Bssid reset after btm or legacy roam - Making few changes in CMake for wpa_supplicant and esp_wifi --- components/esp_wifi/CMakeLists.txt | 2 +- .../roaming_app/include/esp_roaming.h | 4 ++++ .../wifi_apps/roaming_app/src/roaming_app.c | 24 +++++++++++++------ components/wpa_supplicant/CMakeLists.txt | 1 + .../esp_supplicant/src/esp_common.c | 4 ++++ 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index b6b5ec890c..569bc2efb7 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -63,7 +63,7 @@ idf_component_register(SRCS "${srcs}" PRIV_REQUIRES esp_pm esp_timer nvs_flash wpa_supplicant hal lwip esp_coex 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}") if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED) diff --git a/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h b/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h index 6ed20ce949..dff094c3b8 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h +++ b/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h @@ -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_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 } #endif diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c index 9d59c91a90..771d9e6bdc 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c +++ b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c @@ -57,6 +57,13 @@ static void roaming_app_periodic_scan_internal_handler(void *data, void *ctx); 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) { return (a->tv_sec - b->tv_sec); @@ -183,7 +190,11 @@ static void roaming_app_disconnected_event_handler(void *ctx, void *data) #endif /*PERIODIC_SCAN_MONITORING*/ 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; @@ -498,6 +509,7 @@ static void trigger_legacy_roam(struct cand_bss *bss) wifi_cfg.sta.bssid_set = true; os_memcpy(wifi_cfg.sta.bssid, bss->bssid, ETH_ALEN); 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_connect(); ESP_LOGI(ROAMING_TAG, "Disconnecting and connecting to "MACSTR" on account of better rssi",MAC2STR(bss->bssid)); @@ -710,19 +722,17 @@ static void parse_scan_results_and_roam(void) int8_t rssi_diff = 0; uint8_t i; 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++) { 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 - ap_info.rssi; + 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", - MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(ap_info.bssid), - g_roaming_app.scanned_aps.ap_records[i].rssi, ap_info.rssi, + 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, g_roaming_app.current_bss.ap.rssi, 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_security_match(g_roaming_app.scanned_aps.ap_records[i]) && rssi_diff > best_rssi_diff ) { best_rssi_diff = rssi_diff; best_ap_index = i; diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index cafc14a27b..4039ab4345 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -245,6 +245,7 @@ idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src} INCLUDE_DIRS include port/include esp_supplicant/include PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto ../esp_wifi/wifi_apps/roaming_app/include + ../esp_wifi/wifi_apps/roaming_app/src LDFRAGMENTS ${linker_fragments} PRIV_REQUIRES mbedtls esp_timer esp_wifi) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 9e7f3656fc..814a9fd506 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -24,6 +24,7 @@ #include "rsn_supp/wpa.h" #include "esp_private/wifi.h" #include "esp_wifi_types_generic.h" +#include "esp_roaming.h" /* Utility Functions */ esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6]) @@ -633,6 +634,9 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, config->sta.channel = bss->channel; /* supplicant connect will only be called in case of bss transition(roaming) */ 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); os_free(config); esp_wifi_connect();