mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/netif_mtu_path_disc' into 'master'
[esp_netif]: Support for MTU path discovery Closes IDFGH-10355 and IDFGH-14560 See merge request espressif/esp-idf!42088
This commit is contained in:
@@ -487,9 +487,46 @@ int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif);
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS
|
||||
*/
|
||||
*/
|
||||
esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name);
|
||||
|
||||
/**
|
||||
* @brief Set interface MTU at runtime
|
||||
*
|
||||
* Updates the underlying stack's MTU for the given interface. This affects
|
||||
* TCP effective MSS calculation and IP fragmentation behavior for future
|
||||
* transmissions on this interface.
|
||||
*
|
||||
* Notes:
|
||||
* - Applies per-interface, not per-connection. Existing TCP connections may
|
||||
* adjust naturally based on effective MSS calculations during send.
|
||||
* - Some interfaces may renegotiate MTU (e.g., DHCP/PPP) and override this;
|
||||
* reapply as needed after link events.
|
||||
* - On stacks that do not support runtime MTU updates, returns ESP_ERR_NOT_SUPPORTED.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[in] mtu New MTU value to set (in bytes)
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS if parameters are invalid or interface not ready
|
||||
* - ESP_ERR_NOT_SUPPORTED if not supported by the current net stack
|
||||
*/
|
||||
esp_err_t esp_netif_set_mtu(esp_netif_t *esp_netif, uint16_t mtu);
|
||||
|
||||
/**
|
||||
* @brief Get interface MTU
|
||||
*
|
||||
* Reads the underlying stack's MTU for the given interface.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[out] mtu Pointer to store MTU (in bytes)
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS if parameters are invalid or interface not ready
|
||||
* - ESP_ERR_NOT_SUPPORTED if not supported by the current net stack
|
||||
*/
|
||||
esp_err_t esp_netif_get_mtu(esp_netif_t *esp_netif, uint16_t *mtu);
|
||||
|
||||
/**
|
||||
* @brief Enable NAPT on an interface
|
||||
*
|
||||
|
||||
@@ -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 \
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -484,4 +484,15 @@ esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key)
|
||||
{
|
||||
return esp_netif_get_handle_from_ifkey_unsafe(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)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_get_mtu(esp_netif_t *esp_netif, uint16_t *mtu)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
#endif /* CONFIG_ESP_NETIF_LOOPBACK */
|
||||
|
||||
@@ -35,6 +35,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
|
||||
@@ -680,6 +681,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;
|
||||
@@ -711,6 +715,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
|
||||
@@ -1009,6 +1019,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);
|
||||
@@ -2719,6 +2733,48 @@ esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name)
|
||||
return esp_netif_lwip_ipc_call(esp_netif_get_netif_impl_name_api, esp_netif, name);
|
||||
}
|
||||
|
||||
static esp_err_t esp_netif_set_mtu_api(esp_netif_api_msg_t *msg)
|
||||
{
|
||||
esp_netif_t *esp_netif = msg->esp_netif;
|
||||
uint16_t mtu = *(uint16_t *)msg->data;
|
||||
esp_netif->lwip_netif->mtu = mtu;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
static esp_err_t esp_netif_get_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_out = (uint16_t *)msg->data;
|
||||
*mtu_out = (uint16_t)esp_netif->lwip_netif->mtu;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
if (esp_netif == NULL || esp_netif->lwip_netif == NULL || mtu == NULL) {
|
||||
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
|
||||
}
|
||||
return esp_netif_lwip_ipc_call(esp_netif_get_mtu_api, esp_netif, mtu);
|
||||
}
|
||||
|
||||
#if IP_NAPT
|
||||
static esp_err_t esp_netif_napt_control_api(esp_netif_api_msg_t *msg)
|
||||
{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -698,6 +698,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)
|
||||
{
|
||||
/**
|
||||
@@ -727,6 +769,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)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(pmtu_probe)
|
||||
@@ -0,0 +1,52 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# Path MTU Probe (ICMP)
|
||||
|
||||
This example probes a safe Path MTU (PMTU) using ICMP echo requests and applies it to the active network interface at runtime via `esp_netif_set_mtu()`.
|
||||
|
||||
It performs a simple binary search over the ICMP payload size to find the largest size that receives a reply within the timeout, then estimates the interface MTU as `payload + 28` (20-byte IPv4 header + 8-byte ICMP header) and sets that MTU on the default interface.
|
||||
|
||||
Notes:
|
||||
- This is a pragmatic approach for constrained networks (e.g., LTE) where fragmentation or ICMP handling can be problematic. It is not a full standards PMTUD/PLPMTUD implementation.
|
||||
- For IPv6 targets, lwIP’s ND6 manages per-destination PMTU internally. The interface MTU cap still bounds TCP effective MSS.
|
||||
- DHCP or PPP may renegotiate MTU; re-run the probe or reapply the MTU after reconnect events if needed.
|
||||
|
||||
## How to use
|
||||
|
||||
1) Configure network and probe target:
|
||||
- `idf.py menuconfig` → Example Connection Configuration → select Wi-Fi or Ethernet and credentials.
|
||||
- `idf.py menuconfig` → Example Configuration → set "Probe Hostname/IP" (default: `www.espressif.com`).
|
||||
|
||||
2) Build/flash/monitor:
|
||||
- `idf.py -p PORT flash monitor`
|
||||
|
||||
3) Example output:
|
||||
```
|
||||
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 these API:
|
||||
- `esp_netif_set_mtu(esp_netif_t *netif, uint16_t mtu)`
|
||||
- `esp_netif_get_mtu(esp_netif_t *netif, uint16_t *mtu)`
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "pmtu_probe_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_netif esp_event nvs_flash lwip protocol_examples_common)
|
||||
@@ -0,0 +1,9 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_PROBE_HOST
|
||||
string "Probe Hostname/IP"
|
||||
default "www.espressif.com"
|
||||
help
|
||||
Hostname or IPv4 address to probe for MTU.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* Path MTU probe example using ICMP ping payload search
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_netif.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "ping/ping_sock.h"
|
||||
|
||||
static const char *TAG = "pmtu_probe";
|
||||
|
||||
typedef struct {
|
||||
SemaphoreHandle_t done;
|
||||
bool got_reply;
|
||||
} probe_ctx_t;
|
||||
|
||||
static void on_ping_success(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
probe_ctx_t *ctx = (probe_ctx_t *)args;
|
||||
ctx->got_reply = true;
|
||||
}
|
||||
|
||||
static void on_ping_timeout(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
(void)hdl; (void)args;
|
||||
}
|
||||
|
||||
static void on_ping_end(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
probe_ctx_t *ctx = (probe_ctx_t *)args;
|
||||
if (ctx && ctx->done) {
|
||||
xSemaphoreGive(ctx->done);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
esp_ping_config_t cfg = ESP_PING_DEFAULT_CONFIG();
|
||||
cfg.target_addr = *target;
|
||||
cfg.count = 1;
|
||||
cfg.timeout_ms = 1000; // 1s timeout
|
||||
cfg.interval_ms = 100; // not used for count=1
|
||||
cfg.data_size = payload_size;
|
||||
esp_ping_callbacks_t cbs = {
|
||||
.cb_args = &ctx,
|
||||
.on_ping_success = on_ping_success,
|
||||
.on_ping_timeout = on_ping_timeout,
|
||||
.on_ping_end = on_ping_end,
|
||||
};
|
||||
esp_ping_handle_t ping = NULL;
|
||||
if (esp_ping_new_session(&cfg, &cbs, &ping) != ESP_OK) {
|
||||
vSemaphoreDelete(ctx.done);
|
||||
return false;
|
||||
}
|
||||
esp_ping_start(ping);
|
||||
(void)xSemaphoreTake(ctx.done, pdMS_TO_TICKS(1500));
|
||||
esp_ping_delete_session(ping);
|
||||
vSemaphoreDelete(ctx.done);
|
||||
return ctx.got_reply;
|
||||
}
|
||||
|
||||
static bool resolve_host(const char *host, ip_addr_t *out)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
struct sockaddr_in6 s6;
|
||||
if (inet_pton(AF_INET6, host, &s6.sin6_addr) == 1) {
|
||||
ipaddr_aton(host, out);
|
||||
return true;
|
||||
}
|
||||
struct addrinfo hint = { 0 }, *res = NULL;
|
||||
if (getaddrinfo(host, NULL, &hint, &res) != 0 || !res) {
|
||||
return false;
|
||||
}
|
||||
#if CONFIG_LWIP_IPV4
|
||||
if (res->ai_family == AF_INET) {
|
||||
struct in_addr a4 = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
|
||||
inet_addr_to_ip4addr(ip_2_ip4(out), &a4);
|
||||
freeaddrinfo(res);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
// Prefer IPv4 for simple MTU formula; fall back to first result
|
||||
ipaddr_aton(host, out);
|
||||
freeaddrinfo(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
const char *host = CONFIG_EXAMPLE_PROBE_HOST;
|
||||
ip_addr_t target;
|
||||
if (!resolve_host(host, &target)) {
|
||||
ESP_LOGE(TAG, "Failed to resolve host: %s", host);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_netif_t *netif = get_example_netif();
|
||||
if (!netif) {
|
||||
ESP_LOGE(TAG, "No default netif available");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Probing PMTU to host %s (IPv4)", host);
|
||||
|
||||
uint16_t if_mtu = 0;
|
||||
uint32_t lo = 0;
|
||||
uint32_t hi = 1472; // fallback max payload (1500 - 28)
|
||||
if (esp_netif_get_mtu(netif, &if_mtu) == ESP_OK && if_mtu >= 28) {
|
||||
hi = (if_mtu >= 28) ? (if_mtu - 28) : 0;
|
||||
}
|
||||
ESP_LOGI(TAG, "Search range payload=[%u, %u]", (unsigned)lo, (unsigned)hi);
|
||||
|
||||
uint32_t best = 0;
|
||||
while (lo <= hi) {
|
||||
uint32_t mid = lo + ((hi - lo) / 2);
|
||||
bool ok = try_payload_once(&target, mid);
|
||||
if (ok) {
|
||||
best = mid;
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
if (mid == 0) {
|
||||
break; // can't go lower
|
||||
}
|
||||
hi = mid - 1;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
|
||||
uint32_t new_mtu = best + 28; // IPv4 header (20) + ICMP (8)
|
||||
if (new_mtu < 576) {
|
||||
// Be conservative; typical minimum IPv4 MTU recommendation
|
||||
new_mtu = 576;
|
||||
}
|
||||
ESP_LOGI(TAG, "Best payload=%u -> MTU=%u", (unsigned)best, (unsigned)new_mtu);
|
||||
ESP_LOGI(TAG, "Applying MTU %u to default netif", (unsigned)new_mtu);
|
||||
esp_err_t err = esp_netif_set_mtu(netif, (uint16_t)new_mtu);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Failed to set MTU: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user