You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32-C6 Wi-Fi中继NAPT源IP转换异常排查求助

问题描述

在ESP-IDF 5.4.0环境下为ESP32-C6配置AP/STA模式,当前状态:

  • STA成功连接上级网络,获取192.168.1.127,可被同网段设备Ping通;
  • 其他设备可连接ESP32的AP网络,获取192.168.4.X段IP,AP网段内设备可互相Ping通或建立TCP连接传输数据;

核心问题:AP侧设备无法访问互联网。执行esp_netif_napt_enable(sta_netif)返回ESP_OK,但AP侧设备Ping STA网段设备时,ICMP包源IP仍为AP网段的192.168.4.2,未被NAT转换为ESP32的STA接口IP(192.168.1.127)。

已完成的配置:

  • 菜单配置中启用WPA3;
  • 启用LWIP -> Enable NAT & NAT Port Mapping;

相关代码

File main.c

#include <stdio.h>

// tmp NVS
#include "nvs_flash.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_event.h"

#include "esp_log.h"

#include "net_tools.c"

void init_nvs()
{
    //  Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
}

int app_main(int argc, char **argv)
{
    vTaskDelay(1000);

    init_nvs();

    esp_err_t crel_err = esp_event_loop_create_default();

    if (crel_err != ESP_OK)
    {
        printf("Failed to create event loop");
    }

    printf("Start Wi-Fi :\n");

    esp_log_level_set("esp_netif_lwip", ESP_LOG_VERBOSE);
    esp_log_level_set("lwip", ESP_LOG_DEBUG);
    esp_log_level_set(0x00U, ESP_LOG_DEBUG);

    // Be a AP_STA with IP address forwarding
    init_AP_STA(); // net_tools.c

    return 0;
}

File net_tools.c

#include "esp_log.h"
#include "esp_wifi.h"
#include <string.h>

#include "keys.c" // My Wi-Fi passwords

#define DEFAULT_SCAN_LIST_SIZE 10
#define DHCPS_OFFER_DNS 0x02

////////// DEBUG TAGS ///////////
static const char *STA_WIFI = "Wi-Fi STA";
static const char *AP_WIFI = "Wi-Fi AP";
static const char *NAPT = "Wi-Fi NAPT";

esp_netif_t *ap_netif;
esp_netif_t *sta_netif;

void softap_set_dns_addr()
{
    esp_netif_dns_info_t dns;
    esp_netif_get_dns_info(sta_netif,ESP_NETIF_DNS_MAIN,&dns);
    // uint8_t dhcps_offer_option = DHCPS_OFFER_DNS;
    uint8_t dhcps_offer_option = 0;
    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(ap_netif));
    ESP_ERROR_CHECK(esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_offer_option, sizeof(dhcps_offer_option)));
    ESP_ERROR_CHECK(esp_netif_set_dns_info(ap_netif, ESP_NETIF_DNS_MAIN, &dns));
    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(ap_netif));
}

void enable_nat(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
#ifdef NAPT_DEBUG
        ESP_LOGI(NAPT, "NAPT_DEBUG DEFINED !!!");
#endif

        ESP_LOGI(NAPT, "START ENABLE NAT FOR STA");
        esp_netif_ip_info_t ip_info;

        esp_netif_get_ip_info(sta_netif, &ip_info);

        ESP_LOGI(NAPT, "STA IP address: " IPSTR, IP2STR(&ip_info.ip));

        if (ip_info.ip.addr != 0) {

            //
            esp_netif_attach(sta_netif, esp_netif_get_io_driver(sta_netif));

            // Enable NAPT on the STA interface
            esp_err_t err = esp_netif_napt_enable(sta_netif);

            if (err != ESP_OK) {
                if(err != 0x106)
                {
                    ESP_LOGE(NAPT, "esp_netif_napt_enable error -> ESP_ERR_NOT_SUPPORTED");
                    ESP_LOGW(NAPT, "you probably didn't enable napt in Menuconfig.");
                }
                ESP_LOGE(NAPT, "Failed to enable NAPT: %s", esp_err_to_name(err));
                return;
            }

            ESP_LOGI(NAPT, "Added route for AP subnet via STA interface");

        } else {
            ESP_LOGW(NAPT, "No IP address assigned to STA interface. Cannot enable NAPT.");
        }

        ESP_LOGI(NAPT, "Set up DNS configuration");

        softap_set_dns_addr();

        esp_netif_ip_info_t sta_ip_info, ap_ip_info;
        esp_netif_get_ip_info(sta_netif, &sta_ip_info);
        esp_netif_get_ip_info(ap_netif, &ap_ip_info);
        ESP_LOGI(NAPT, "Routes - STA: " IPSTR "/24 via " IPSTR, IP2STR(&sta_ip_info.ip), IP2STR(&sta_ip_info.gw));
        ESP_LOGI(NAPT, "Routes - AP: " IPSTR "/24 via " IPSTR, IP2STR(&ap_ip_info.ip), IP2STR(&ap_ip_info.gw));
    }
}

void init_AP_STA()
{
    // Init
    esp_netif_init();

    sta_netif = esp_netif_create_default_wifi_sta();
    ap_netif = esp_netif_create_default_wifi_ap();

    // Set Wi-Fi mode
    esp_wifi_set_mode(WIFI_MODE_APSTA);

    // Set configuration
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    esp_netif_set_hostname(sta_netif, "STA_HANDLE");

    wifi_config_t wifi_config_sta = {
        .sta = {
            .ssid = AP_NAME,
            .password = PSWD,
        },
    };
    esp_wifi_set_config(WIFI_IF_STA, &wifi_config_sta);


    wifi_config_t wifi_config_ap = {
        .ap = {
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            // .authmode = WIFI_AUTH_WPA2_PSK
            .authmode = WIFI_AUTH_WPA2_WPA3_PSK
        },
    };
    esp_wifi_set_config(WIFI_IF_AP, &wifi_config_ap);

    // Start Wi-Fi interfaces
    esp_err_t ap_result = esp_wifi_start();
    if (ap_result != ESP_OK)
    {
        ESP_LOGE(AP_WIFI, "Wi-Fi AP start failed: %s", esp_err_to_name(ap_result));
    }else{
        ESP_LOGI(AP_WIFI, "Wi-Fi AP started: %s", esp_err_to_name(ap_result));
    }

    // Event handlers
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, enable_nat, NULL);

    // Connect to source esp-sta to source AP
    esp_err_t sta_connect = esp_wifi_connect();
    if(sta_connect == ESP_OK)
    {
        ESP_LOGI(STA_WIFI, "Wi-Fi STA connected successfully\n");
    }else{
        ESP_LOGI(STA_WIFI, "Wi-Fi STA failed to connect\n");
    }

}
可能的原因及解决方案

1. NAPT启用对象错误

ESP-IDF中,NAPT需要启用在AP侧网络接口,而非STA侧。NAPT的作用是将AP子网的私有地址转换为STA接口的公网(上级网络)地址,必须在入口接口(AP)上启用,指定STA为出口转发接口。

解决方法:
修改enable_nat函数中的NAPT启用代码:

// 替换原有的esp_netif_napt_enable(sta_netif);
esp_err_t err = esp_netif_napt_enable(ap_netif, 1); // 第二个参数1表示启用NAPT

2. 路由表配置缺失

即使启用NAPT,若没有正确的路由规则,AP侧流量无法被转发到STA接口。需要确保ESP32的路由表中存在AP子网到STA网关的路由。

解决方法:
在enable_nat函数的NAPT启用成功后,添加静态路由配置:

ip4_addr_t gw;
esp_netif_get_ip_info(sta_netif, &sta_ip_info);
gw.addr = sta_ip_info.gw.addr;

// 添加AP子网路由,指向STA网关
ip4_addr_t ap_subnet;
IP4_ADDR(&ap_subnet, 192, 168, 4, 0);
err = esp_netif_add_route(ap_netif, &ap_subnet, 24, &gw, NULL);
if (err != ESP_OK) {
    ESP_LOGE(NAPT, "Failed to add route: %s", esp_err_to_name(err));
}

3. DHCP DNS配置错误

当前softap_set_dns_addr函数中,dhcps_offer_option被设置为0,导致AP侧设备无法获取正确的DNS服务器地址,无法解析域名。

解决方法:
恢复dhcps_offer_option为DHCPS_OFFER_DNS,确保DHCP服务器向AP侧设备推送DNS信息:

uint8_t dhcps_offer_option = DHCPS_OFFER_DNS; // 替换原有的0
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(ap_netif));
ESP_ERROR_CHECK(esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_offer_option, sizeof(dhcps_offer_option)));
ESP_ERROR_CHECK(esp_netif_set_dns_info(ap_netif, ESP_NETIF_DNS_MAIN, &dns));
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(ap_netif));

4. 冗余的接口挂载操作

代码中esp_netif_attach(sta_netif, esp_netif_get_io_driver(sta_netif));属于冗余操作,esp_netif_create_default_wifi_sta已经完成了接口与驱动的绑定,重复挂载可能导致网络栈异常。

解决方法:删除该行代码。

5. LWIP IP转发未启用

仅启用NAPT不足以完成流量转发,还需要开启LWIP的IPv4转发功能。

解决方法:
在idf.py menuconfig中:

  • 进入Component config -> LWIP -> IPv4,勾选Enable IPv4 forwarding;
  • 确认Component config -> LWIP -> NAT下的Enable NAT和Enable NAPT均已勾选;
辅助调试所需信息
  • ESP32的路由表输出:通过esp_netif_dump_all()或LWIP的netif_list打印完整路由表;
  • AP侧设备的网络配置:包括IP、子网掩码、网关、DNS服务器地址;
  • 上级路由器的ARP表:查看是否存在ESP32 STA接口IP对应的ARP条目;
  • LWIP的NAT日志:开启Component config -> LWIP -> Debug -> Enable debug prints for NAT,查看NAT转换的详细日志;

内容的提问来源于stack exchange,提问作者v42

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 17:11:01