diff --git a/components/esp_netif/include/esp_netif_defaults.h b/components/esp_netif/include/esp_netif_defaults.h index b215aca1dc..dee5cae807 100644 --- a/components/esp_netif/include/esp_netif_defaults.h +++ b/components/esp_netif/include/esp_netif_defaults.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -52,7 +52,8 @@ extern "C" { .if_key = "WIFI_STA_DEF", \ .if_desc = "sta", \ .route_prio = 100, \ - .bridge_info = NULL \ + .bridge_info = NULL, \ + .mtu = 0 \ } \ #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT @@ -66,7 +67,8 @@ extern "C" { .if_key = "WIFI_AP_DEF", \ .if_desc = "ap", \ .route_prio = 10, \ - .bridge_info = NULL \ + .bridge_info = NULL, \ + .mtu = 0 \ } #endif @@ -79,7 +81,8 @@ extern "C" { .lost_ip_event = 0, \ .if_key = "WIFI_NAN_DEF", \ .if_desc = "nan", \ - .route_prio = 10 \ + .route_prio = 10, \ + .mtu = 0 \ }; #define ESP_NETIF_INHERENT_DEFAULT_ETH() \ @@ -92,7 +95,8 @@ extern "C" { .if_key = "ETH_DEF", \ .if_desc = "eth", \ .route_prio = 50, \ - .bridge_info = NULL \ + .bridge_info = NULL, \ + .mtu = 0 \ } #ifdef CONFIG_PPP_SUPPORT @@ -106,7 +110,8 @@ extern "C" { .if_key = "PPP_DEF", \ .if_desc = "ppp", \ .route_prio = 20, \ - .bridge_info = NULL \ + .bridge_info = NULL, \ + .mtu = 0 \ } #endif /* CONFIG_PPP_SUPPORT */ @@ -120,7 +125,8 @@ extern "C" { .if_key = "BR0", \ .if_desc = "br0", \ .route_prio = 70, \ - .bridge_info = NULL \ + .bridge_info = NULL, \ + .mtu = 0 \ } #define ESP_NETIF_INHERENT_DEFAULT_BR_DHCPS() \ @@ -133,7 +139,8 @@ extern "C" { .if_key = "BR1", \ .if_desc = "br1", \ .route_prio = 70, \ - .bridge_info = NULL \ + .bridge_info = NULL, \ + .mtu = 0 \ } /** diff --git a/components/esp_netif/include/esp_netif_types.h b/components/esp_netif/include/esp_netif_types.h index 7205eedcc3..850ed4e5b0 100644 --- a/components/esp_netif/include/esp_netif_types.h +++ b/components/esp_netif/include/esp_netif_types.h @@ -255,6 +255,7 @@ typedef struct esp_netif_inherent_config { A higher value of route_prio indicates a higher priority */ bridgeif_config_t *bridge_info; /*!< LwIP bridge configuration */ + uint16_t mtu; /*!< Optional initial MTU (bytes). 0 = use stack default */ } esp_netif_inherent_config_t; typedef struct esp_netif_config esp_netif_config_t; diff --git a/components/esp_netif/loopback/esp_netif_loopback.c b/components/esp_netif/loopback/esp_netif_loopback.c index 2e7393e925..af858b764c 100644 --- a/components/esp_netif/loopback/esp_netif_loopback.c +++ b/components/esp_netif/loopback/esp_netif_loopback.c @@ -488,13 +488,11 @@ esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key) // MTU control is not supported in loopback build. esp_err_t esp_netif_set_mtu(esp_netif_t *esp_netif, uint16_t mtu) { - ESP_LOGD(TAG, "%s esp_netif:%p mtu:%u", __func__, esp_netif, (unsigned)mtu); return ESP_ERR_NOT_SUPPORTED; } esp_err_t esp_netif_get_mtu(esp_netif_t *esp_netif, uint16_t *mtu) { - ESP_LOGD(TAG, "%s esp_netif:%p", __func__, esp_netif); return ESP_ERR_NOT_SUPPORTED; } #endif /* CONFIG_ESP_NETIF_LOOPBACK */ diff --git a/components/esp_netif/lwip/esp_netif_lwip.c b/components/esp_netif/lwip/esp_netif_lwip.c index 1713846e9d..120dd3ab78 100644 --- a/components/esp_netif/lwip/esp_netif_lwip.c +++ b/components/esp_netif/lwip/esp_netif_lwip.c @@ -31,6 +31,7 @@ #include "lwip/priv/tcpip_priv.h" #include "lwip/netif.h" #include "lwip/etharp.h" +#include "lwip/prot/ip4.h" #if CONFIG_ESP_NETIF_BRIDGE_EN #include "netif/bridgeif.h" #endif // CONFIG_ESP_NETIF_BRIDGE_EN @@ -672,6 +673,9 @@ static err_t netif_mld_mac_filter_cb(struct netif *netif, const ip6_addr_t *grou static esp_err_t esp_netif_init_configuration(esp_netif_t *esp_netif, const esp_netif_config_t *cfg) { +#define MAX_MTU_SIZE 9000 /* lwip doesn't have a global maximum, 9000 is selected as a reasonable max MTU for most cases + it is possible to override this upper bound by runtime configuration esp_netif_set_mtu() */ + // Basic esp_netif and lwip is a mandatory configuration and cannot be updated after esp_netif_new() if (cfg == NULL || cfg->base == NULL || cfg->stack == NULL) { return ESP_ERR_ESP_NETIF_INVALID_PARAMS; @@ -703,6 +707,12 @@ static esp_err_t esp_netif_init_configuration(esp_netif_t *esp_netif, const esp_ if (cfg->base->route_prio) { esp_netif->route_prio = cfg->base->route_prio; } + // Store initial MTU preference (applied after netif_add()). 0 keeps stack default. + if (cfg->base->mtu >= IP_HLEN && cfg->base->mtu <= MAX_MTU_SIZE) { + esp_netif->configured_mtu = cfg->base->mtu; + } else { + esp_netif->configured_mtu = 0; + } #if CONFIG_ESP_NETIF_BRIDGE_EN // Setup bridge configuration if the interface is to be bridge @@ -1001,6 +1011,10 @@ static esp_err_t esp_netif_lwip_add(esp_netif_t *esp_netif) #if CONFIG_ESP_NETIF_BRIDGE_EN } #endif // CONFIG_ESP_NETIF_BRIDGE_EN + // Apply configured MTU (if provided) after netif has been added and initialized + if (esp_netif->configured_mtu) { + esp_netif->lwip_netif->mtu = esp_netif->configured_mtu; + } if (esp_netif->driver_set_mac_filter) { #if LWIP_IPV4 && LWIP_IGMP netif_set_igmp_mac_filter(esp_netif->lwip_netif, netif_igmp_mac_filter_cb); @@ -2701,9 +2715,6 @@ esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name) static esp_err_t esp_netif_set_mtu_api(esp_netif_api_msg_t *msg) { esp_netif_t *esp_netif = msg->esp_netif; - if (esp_netif == NULL || esp_netif->lwip_netif == NULL || msg->data == NULL) { - return ESP_ERR_ESP_NETIF_INVALID_PARAMS; - } uint16_t mtu = *(uint16_t *)msg->data; esp_netif->lwip_netif->mtu = mtu; return ESP_OK; @@ -2715,6 +2726,11 @@ esp_err_t esp_netif_set_mtu(esp_netif_t *esp_netif, uint16_t mtu) if (esp_netif == NULL || esp_netif->lwip_netif == NULL) { return ESP_ERR_ESP_NETIF_INVALID_PARAMS; } + /* Validate MTU is at least large enough for IP header */ + if (mtu < IP_HLEN) { + ESP_LOGE(TAG, "MTU is too small, must be at least %d", IP_HLEN); + return ESP_ERR_ESP_NETIF_INVALID_PARAMS; + } return esp_netif_lwip_ipc_call(esp_netif_set_mtu_api, esp_netif, &mtu); } diff --git a/components/esp_netif/lwip/esp_netif_lwip_internal.h b/components/esp_netif/lwip/esp_netif_lwip_internal.h index 332934d3b0..878c2b61bb 100644 --- a/components/esp_netif/lwip/esp_netif_lwip_internal.h +++ b/components/esp_netif/lwip/esp_netif_lwip_internal.h @@ -123,6 +123,8 @@ struct esp_netif_obj { #ifdef CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF ip_addr_t dns[DNS_MAX_SERVERS]; #endif + // initial MTU preference to apply after netif_add(); 0 means use stack default + uint16_t configured_mtu; }; typedef enum esp_netif_set_default_state { diff --git a/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c b/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c index 9dcd8c6623..b6e007154f 100644 --- a/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c +++ b/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c @@ -658,6 +658,48 @@ TEST(esp_netif, set_get_dnsserver) } } +TEST(esp_netif, initial_mtu_config_applied) +{ + // Ensure TCP/IP stack is initialized + test_case_uses_tcpip(); + + // Minimal driver config to satisfy start-time sanity checks + esp_netif_driver_ifconfig_t driver_config = { .handle = (void*)1, .transmit = dummy_transmit }; + + // Case 1: explicit MTU configured + uint16_t mtu_out = 0; + esp_netif_inherent_config_t base1 = { .if_key = "mtu_if0" }; + base1.mtu = 1400; + esp_netif_config_t cfg1 = { + .base = &base1, + .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA, + .driver = &driver_config, + }; + esp_netif_t *n1 = esp_netif_new(&cfg1); + TEST_ASSERT_NOT_NULL(n1); + esp_netif_action_start(n1, NULL, 0, NULL); + TEST_ASSERT_EQUAL(ESP_OK, esp_netif_get_mtu(n1, &mtu_out)); + TEST_ASSERT_EQUAL_UINT16(1400, mtu_out); + esp_netif_destroy(n1); + + // Case 2: default MTU (0 means use stack default, e.g., 1500) + esp_netif_inherent_config_t base2 = { .if_key = "mtu_if1" }; + // base2.mtu intentionally left 0 + esp_netif_config_t cfg2 = { + .base = &base2, + .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA, + .driver = &driver_config, + }; + esp_netif_t *n2 = esp_netif_new(&cfg2); + TEST_ASSERT_NOT_NULL(n2); + esp_netif_action_start(n2, NULL, 0, NULL); + TEST_ASSERT_EQUAL(ESP_OK, esp_netif_get_mtu(n2, &mtu_out)); + TEST_ASSERT_EQUAL_UINT16(1500, mtu_out); + struct netif *netif = esp_netif_get_netif_impl(n2); + TEST_ASSERT_EQUAL_UINT16(1500, netif->mtu); + esp_netif_destroy(n2); +} + TEST_GROUP_RUNNER(esp_netif) { /** @@ -686,6 +728,7 @@ TEST_GROUP_RUNNER(esp_netif) RUN_TEST_CASE(esp_netif, dhcp_server_state_transitions_wifi_ap) RUN_TEST_CASE(esp_netif, dhcp_server_state_transitions_mesh) #endif + RUN_TEST_CASE(esp_netif, initial_mtu_config_applied) RUN_TEST_CASE(esp_netif, route_priority) RUN_TEST_CASE(esp_netif, set_get_dnsserver) RUN_TEST_CASE(esp_netif, unified_netif_status_event) diff --git a/examples/protocols/icmp/pmtu_probe/README.md b/examples/protocols/icmp/pmtu_probe/README.md index 763af3e9ef..34219d8143 100644 --- a/examples/protocols/icmp/pmtu_probe/README.md +++ b/examples/protocols/icmp/pmtu_probe/README.md @@ -23,18 +23,30 @@ Notes: 3) Example output: ``` -I (xxx) pmtu_probe: Connecting network... -I (xxx) pmtu_probe: Probing PMTU to host www.espressif.com (IPv4) -I (xxx) pmtu_probe: Search range payload=[0, 1472] -I (xxx) pmtu_probe: Best payload=1392 -> MTU=1420 -I (xxx) pmtu_probe: Applying MTU 1420 to default netif +I (8236) example_common: Connected to example_netif_sta +I (8246) example_common: - IPv4 address: 192.168.0.35, +I (8276) pmtu_probe: Probing PMTU to host www.espressif.com (IPv4) +I (8276) pmtu_probe: Search range payload=[0, 1472] +I (8286) pmtu_probe: Trying payload once with size 736 +I (8436) pmtu_probe: Trying payload once with size 1104 +I (8586) pmtu_probe: Trying payload once with size 1288 +I (8736) pmtu_probe: Trying payload once with size 1380 +I (8886) pmtu_probe: Trying payload once with size 1426 +I (9036) pmtu_probe: Trying payload once with size 1449 +I (9186) pmtu_probe: Trying payload once with size 1461 +I (9336) pmtu_probe: Trying payload once with size 1467 +I (9486) pmtu_probe: Trying payload once with size 1470 +I (9636) pmtu_probe: Trying payload once with size 1471 +I (9786) pmtu_probe: Trying payload once with size 1472 +I (9936) pmtu_probe: Best payload=1472 -> MTU=1500 +I (9936) pmtu_probe: Applying MTU 1500 to default netif +I (9936) main_task: Returned from app_main() ``` ## Implementation details - Uses `esp_ping` (ping_sock) to attempt 1 echo per payload size and waits synchronously for success/timeout. - Bounds the search using the current interface MTU when available, otherwise falls back to `1472`. -- Applies MTU via new API: +- Applies MTU via these API: - `esp_netif_set_mtu(esp_netif_t *netif, uint16_t mtu)` - `esp_netif_get_mtu(esp_netif_t *netif, uint16_t *mtu)` - diff --git a/examples/protocols/icmp/pmtu_probe/main/pmtu_probe_main.c b/examples/protocols/icmp/pmtu_probe/main/pmtu_probe_main.c index 271a4a9c9b..920e43e063 100644 --- a/examples/protocols/icmp/pmtu_probe/main/pmtu_probe_main.c +++ b/examples/protocols/icmp/pmtu_probe/main/pmtu_probe_main.c @@ -47,8 +47,9 @@ static void on_ping_end(esp_ping_handle_t hdl, void *args) } } -static bool try_payload_once(const ip_addr_t *target, uint32_t payload) +static bool try_payload_once(const ip_addr_t *target, uint32_t payload_size) { + ESP_LOGI(TAG, "Trying payload once with size %u", payload_size); probe_ctx_t ctx = { .done = xSemaphoreCreateBinary(), .got_reply = false }; if (!ctx.done) { return false; @@ -58,7 +59,7 @@ static bool try_payload_once(const ip_addr_t *target, uint32_t payload) cfg.count = 1; cfg.timeout_ms = 1000; // 1s timeout cfg.interval_ms = 100; // not used for count=1 - cfg.data_size = payload; + cfg.data_size = payload_size; esp_ping_callbacks_t cbs = { .cb_args = &ctx, .on_ping_success = on_ping_success, @@ -106,7 +107,9 @@ static bool resolve_host(const char *host, ip_addr_t *out) void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_LOGI(TAG, "Connecting network..."); ESP_ERROR_CHECK(example_connect()); @@ -123,7 +126,7 @@ void app_main(void) return; } - ESP_LOGI(TAG, "Probing PMTU to host %s (IPv4)"); + ESP_LOGI(TAG, "Probing PMTU to host %s (IPv4)", host); uint16_t if_mtu = 0; uint32_t lo = 0;