From a7d20669136ab260f7ebd5acd29f4abdabf43254 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Fri, 27 Jun 2025 23:05:37 +0530 Subject: [PATCH] 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) {