Skip to content

WPA2-Enterprise STA client re-authenticates (IDFGH-16885) #17954

@IntelligentLumenM

Description

@IntelligentLumenM

Summary

I am facing a WPA2-Enterprise issue where some ESP-based luminaires repeatedly re-associate and re-authenticate approximately every 30 seconds on a customer network.

All devices run exactly the same firmware, using WPA2-Enterprise in STA mode only. However:

  • Some devices stay connected and stable.
  • Other devices on the same site, same SSID, and same firmware keep going through association + 802.1X authentication every ~30 seconds.

The customer’s Cisco WLAN controller keeps these devices in an "Authenticating" state and shows that the whole association/authentication sequence is repeated periodically. Their network team suspects this behavior is being triggered by the client module itself (Espressif), not by the AP or the controller.

I would like to confirm that our WPA2-Enterprise implementation is correct and understand whether there is any known issue or configuration detail on the ESP side that could cause this behavior.

Environment

  • Module / SoC: ESP32-WROOM-32UE
  • ESP-IDF version: 5.0
  • Wi-Fi mode: STA
  • Authentication: WPA2-Enterprise (EAP, username/password)

Behavior observed on the WLAN controller

On the Cisco controller, when the issue occurs they see the client:

  1. Associate to the AP (association success).
  2. Start 802.1X / DOT1X L2 authentication.
  3. Stay in an Authenticating state.
  4. After ~30 seconds, the same sequence repeats (association + 802.1X).

They shared the following log sequence, which repeats every 30 seconds while the client is in "Authenticating":

2025/11/05 10:23:12.940520677 {wncd_x_R0-0}{1}: [client-orch-sm] [15594]: (note): MAC: a4e5.7c54.6a78  Association received. BSSID e438.7e11.c963, WLAN BWSIA2, Slot 0 AP e438.7e11.c960, vtt-ap-166114, Site tag ES_Vtt, Policy tag APG_Vtt, Policy profile BWSIA2, Switching Central, old BSSID e438.7e11.c963, Socket delay 0ms
2025/11/05 10:23:12.940620698 {wncd_x_R0-0}{1}: [client-orch-state] [15594]: (note): MAC: a4e5.7c54.6a78  Client state transition: S_CO_L2_AUTH_IN_PROGRESS -> S_CO_L2_AUTH_IN_PROGRESS
2025/11/05 10:23:12.940997973 {wncd_x_R0-0}{1}: [dot11] [15594]: (note): MAC: a4e5.7c54.6a78  Association success. AID 2, Roaming = False, WGB = False, 11r = False, 11w = False Fast roam = False
2025/11/05 10:23:12.941302122 {wncd_x_R0-0}{1}: [client-orch-state] [15594]: (note): MAC: a4e5.7c54.6a78  Client state transition: S_CO_L2_AUTH_IN_PROGRESS -> S_CO_L2_AUTH_IN_PROGRESS
2025/11/05 10:23:12.941624216 {wncd_x_R0-0}{1}: [client-auth] [15594]: (note): MAC: a4e5.7c54.6a78  ADD MOBILE sent. Client state flags: 0x71  BSSID: MAC: e438.7e11.c963  capwap IFID: 0x9000002a, Add mobiles sent: 1
2025/11/05 10:23:12.964827607 {wncd_x_R0-0}{1}: [client-auth] [15594]: (note): MAC: a4e5.7c54.6a78  L2 Authentication initiated. method DOT1X, Policy VLAN 62, AAA override = 0 , NAC = 0
2025/11/05 10:23:12.971668901 {iosrp_R0-0}{1}: [pki] [4191]: (note): CRYPTO_PKI: Begin local cert chain retrieval.
2025/11/05 10:23:12.971688741 {iosrp_R0-0}{1}: [pki] [4191]: (note): CRYPTO_PKI: Done with local cert chain fetch 0.

According to them, this looks like a client-side reconnection loop, not something driven by the controller.

My WPA2-Enterprise implementation

This project is a pure STA configuration (point–multipoint) using WPA2-Enterprise.

Below is the main code that manages Wi-Fi connectivity and WPA2-Enterprise settings:

static void event_handler(void* arg, esp_event_base_t event_base,
            int32_t event_id, void* event_data)
{
    esp_netif_dns_info_t dns;
    char url[128];
    if (event_base == WIFI_EVENT) {
        switch (event_id) {
            case WIFI_EVENT_STA_START: {
                esp_wifi_connect();
            } break;
            case WIFI_EVENT_STA_DISCONNECTED: {
                wifi_event_sta_disconnected_t *disconn = (wifi_event_sta_disconnected_t*)event_data;
                if (disconn->reason == WIFI_REASON_ROAMING) {
                    ESP_LOGI(MAIN_TAG, "station roaming, do nothing");
                } else {
                    set_led(0);
                    mqtt_stop();
                    esp_wifi_connect();
                }
            } break;
            case WIFI_EVENT_STA_CONNECTED: {
                ESP_LOGI(MAIN_TAG, "WiFi conectado correctamente al AP");
                set_led(2);
                if (enableBT == 1) ble_start_scan();
            } break;
            case WIFI_EVENT_AP_START: {

            } break;
            case WIFI_EVENT_AP_STOP: {

            } break;
            case WIFI_EVENT_AP_STACONNECTED: {
                wifi_event_ap_staconnected_t *sta_conn = (wifi_event_ap_staconnected_t*)event_data;
                add_client(sta_conn->mac);
            } break;
            case WIFI_EVENT_AP_STADISCONNECTED: {
                wifi_event_ap_stadisconnected_t *sta_dis = (wifi_event_ap_stadisconnected_t*)event_data;
                remove_client(sta_dis->mac);
            } break;
        }
    } else if (event_base == IP_EVENT) {
        switch (event_id) {
            case IP_EVENT_STA_GOT_IP: {
                ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
                ESP_LOGI(MAIN_TAG, ">>>>>> IP_EVENT_STA_GOT_IP <<<<<<");
                sprintf(IPV4_ADDRESS_STR, IPSTR, IP2STR(&event->ip_info.ip));
                sprintf(IPV4_PARENT_ADDRESS_STR, IPSTR, IP2STR(&event->ip_info.gw));
                ESP_LOGI(MAIN_TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));

                // Establecer DNS principal usando esp_netif_set_dns_info
                if (esp_netif_get_dns_info(wifiSTA, ESP_NETIF_DNS_MAIN, &dns) == ESP_OK) {
                    esp_netif_dns_info_t dns_info;
                    dns_info.ip.u_addr.ip4 = dns.ip.u_addr.ip4;
                    dns_info.ip.type = IPADDR_TYPE_V4;
                    ESP_ERROR_CHECK(esp_netif_set_dns_info(wifiSTA, ESP_NETIF_DNS_MAIN, &dns_info));
                    ESP_LOGI(MAIN_TAG, "DNS configurado a:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
                }

                s_retry_num = 0;
                if (detectedILWIFI) {
                    sprintf(url, "%s", "mqtt://setup.digitaliot.es:2883");
                } else {
                    sprintf(url, "%s", MQTT_URL);
                }
                mqtt_start(url, group, topics_handler, mqtt_status_handler);
            } break;
        }
    }
}
void init_wifi() {
        static ip_addr_t dnsserver;
.
.
.
.
.
        static wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
        uint32_t IL_AP_IP = ipaddr_addr("10.8.0.1");
        ESP_ERROR_CHECK(esp_netif_init());
        ESP_ERROR_CHECK(esp_event_loop_create_default());
        wifiSTA = esp_netif_create_default_wifi_sta();
        wifiAP =  esp_netif_create_default_wifi_ap();
        ESP_ERROR_CHECK(esp_wifi_init(&config));
        /* INICIAO ESCANEO DE REDES WIFI */
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
        ESP_ERROR_CHECK(esp_wifi_start());
        #define DEFAULT_SCAN_LIST_SIZE 16
        uint16_t number = DEFAULT_SCAN_LIST_SIZE;
        wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
        uint16_t ap_count = 0;
        memset(ap_info, 0, sizeof(ap_info));
        esp_wifi_disconnect();
        esp_wifi_scan_start(NULL, true);
        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
        ESP_LOGI("SCAN******", "\n\n\n");
        ESP_LOGI("SCAN******", "Total APs scanned = %u", ap_count);
        for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
            ESP_LOGI("SCAN", "SSID \t\t%s", ap_info[i].ssid);
            ESP_LOGI("SCAN", "RSSI \t\t%d", ap_info[i].rssi);
            ESP_LOGI("SCAN", "Channel \t\t%d\n", ap_info[i].primary);
            if (strcmp((const char*)ap_info[i].ssid,IL_AP_SSID)==0) detectedILAP = 1;
            if (strcmp((const char*)ap_info[i].ssid,IL_WIFI_SSID)==0) detectedILWIFI = 1;
        }
        ESP_ERROR_CHECK(esp_wifi_stop());
        /* FIN ESCANEO WIFI */

        /* CONFIGURACION FINAL DE LA WIFI. */
        ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,IP_EVENT_STA_GOT_IP,&event_handler,NULL,NULL));
        ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,&event_handler,NULL,NULL));
        ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,&esp_bss_rssi_low_handler, NULL));
        ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));//WIFI_STORAGE_FLASH;
        ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MIN_MODEM));
        if (enableAP) ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA) );
        else ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );

        /* Configuration de la STA. */
        bzero(&wifi_config, sizeof(wifi_config_t));
        wifi_config.sta.pmf_cfg.capable = true;
        wifi_config.sta.pmf_cfg.required = false;
        wifi_config.sta.threshold.rssi = -127;
        wifi_config.sta.bssid_set = false;
        sprintf(IPV4_PARENT_ADDRESS_STR,"0.0.0.0");
        sprintf(IPV4_ADDRESS_STR,"0.0.0.0");

        if (detectedILWIFI) {
            memcpy(wifi_config.sta.ssid, IL_WIFI_SSID, strlen(IL_WIFI_SSID));
            wifi_config.sta.ssid[strlen(IL_WIFI_SSID)]=0;
            memcpy(wifi_config.sta.password, IL_WIFI_PASSWD, strlen(IL_WIFI_PASSWD));
            wifi_config.sta.password[strlen(IL_WIFI_PASSWD)]=0;
            wifi_config.sta.threshold.authmode = (wifi_auth_mode_t)WIFI_AUTH_WPA2_PSK;
            ESP_LOGI("SCAN", "ENTRAMOS EN MODO CONFIGURACION +++++++++++\n");
        } else {
            memcpy(wifi_config.sta.ssid, ROUTER_SSID, strlen(ROUTER_SSID));
            wifi_config.sta.ssid[strlen(ROUTER_SSID)]=0;
            if (ROUTER_AUTH_MODE==WIFI_AUTH_WPA2_ENTERPRISE) {
                wifi_config.sta.password[0]=0;
            } else {
                memcpy(wifi_config.sta.password, ROUTER_PASSWD, strlen(ROUTER_PASSWD));
                wifi_config.sta.password[strlen(ROUTER_PASSWD)]=0;
            }
            wifi_config.sta.threshold.authmode = (wifi_auth_mode_t)ROUTER_AUTH_MODE;
            if (ROUTER_AUTH_MODE==WIFI_AUTH_WPA2_ENTERPRISE) {
                ESP_ERROR_CHECK(esp_eap_client_set_identity((uint8_t*)ROUTER_WPA2_ENT_USER,strlen(ROUTER_WPA2_ENT_USER)));
                ESP_ERROR_CHECK(esp_eap_client_set_username((uint8_t*)ROUTER_WPA2_ENT_USER,strlen(ROUTER_WPA2_ENT_USER)));
                ESP_ERROR_CHECK(esp_eap_client_set_password((uint8_t*)ROUTER_PASSWD,strlen(ROUTER_PASSWD)));
                ESP_ERROR_CHECK(esp_wifi_sta_enterprise_enable());
            }
            ESP_LOGI("SCAN", "ENTRAMOS EN MODO NORMAL +++++++++++\n");
        }

        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));
        
        if (enableAP) {
            /* Configuracion del AP */
            init_clients();
            esp_netif_ip_info_t ipInfo_ap;
            ipInfo_ap.ip.addr = IL_AP_IP;
            ipInfo_ap.gw.addr = IL_AP_IP;
            IP4_ADDR(&ipInfo_ap.netmask, 255,255,255,0);
            esp_netif_dhcps_stop(wifiAP); // stop before setting ip WifiAP
            esp_netif_set_ip_info(wifiAP, &ipInfo_ap);
            esp_netif_dhcps_start(wifiAP);
            wifi_config_t ap_config = {
                .ap = {
                    .channel = 0,
                    .authmode = WIFI_AUTH_WPA2_PSK,
                    .ssid_hidden = 0,
                    .max_connection = MAX_CLIENTS,
                    .beacon_interval = 100,
                }
            };
            memcpy((char*)ap_config.sta.ssid, IL_AP_SSID, strlen(IL_AP_SSID));
            ap_config.sta.ssid[strlen(IL_AP_SSID)]=0;
            memcpy((char*)ap_config.sta.password, IL_AP_PASSWD, strlen(IL_AP_PASSWD));
            ap_config.sta.password[strlen(IL_AP_PASSWD)]=0;
            ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
            
            /* Configuracion del servidor DNS para el AP */
            uint32_t dns_addr = ipaddr_addr("8.8.8.8"); // Dirección del servidor DNS
            ESP_ERROR_CHECK(set_dhcps_dns(wifiAP, dns_addr));
        }
        ESP_ERROR_CHECK(esp_wifi_start());
        esp_efuse_mac_get_default(_mac);
        if (enableAP) ip_napt_enable(IL_AP_IP, 1);//Habilita el routeo de paquetes.
}

From my side, the reconnection logic is very simple: I only call esp_wifi_connect() on:

  • WIFI_EVENT_STA_START, and
  • WIFI_EVENT_STA_DISCONNECTED.

There is no timer in my application that forces a reconnection every 30 seconds.

Questions

  1. Could you please confirm if this is the correct / recommended way to set up WPA2-Enterprise on ESP devices with:

    • esp_eap_client_set_identity()
    • esp_eap_client_set_username()
    • esp_eap_client_set_password()
    • esp_wifi_sta_enterprise_enable()
      and the STA configuration above?
  2. Based on the controller logs and my code, do you see anything that could explain a client-side re-association / re-authentication loop every ~30 seconds?

I look forward to your guidance on whether our implementation is correct and how we can further debug this behavior. Thank you very much.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions