From 82e69df33e379bf491bea647e217d6d56c5b8090 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Oct 2024 14:49:49 +1000 Subject: [PATCH] esp32: Apply the LWIP active TCP socket limit. This is a workaround for a bug in ESP-IDF where the configuration setting for maximum active TCP sockets (PCBs) is not applied. Fixes cases where a lot of short-lived TCP connections can cause: - Excessive memory usage (unbounded number of sockets in TIME-WAIT). - Much higher risk of stalled connections due to repeated port numbers. The maximum number of active TCP PCBs is reduced from 16 to 12 to further reduce this risk (trade-off against possibility of TIME-WAIT Assassination as described in RFC1337). This is not a watertight fix for the second point: a peer can still reuse a port number while a previous socket is in TIME-WAIT, and LWIP will reject that connection (in an RFC compliant way) causing the peer to stall. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/esp32/CMakeLists.txt | 4 ++ ports/esp32/boards/sdkconfig.base | 4 ++ ports/esp32/esp32_common.cmake | 1 + ports/esp32/lwip_patch.c | 64 +++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 ports/esp32/lwip_patch.c diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index d3927cd48d82..83fca88b6972 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -74,5 +74,9 @@ list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) # Enable the panic handler wrapper idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) +# Patch LWIP memory pool allocators (see lwip_patch.c) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) + # Define the project. project(micropython) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index c7d326c89e8a..c7179b6125a1 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -133,3 +133,7 @@ CONFIG_NEWLIB_NANO_FORMAT=y # Due to limitations in the PMP system this feature breaks native emitters # so is disabled by default. CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=n + +# Further limit total sockets in TIME-WAIT when there are many short-lived +# connections. +CONFIG_LWIP_MAX_ACTIVE_TCP=12 diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 2c81e8c2b3a5..565f6feec232 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -108,6 +108,7 @@ list(APPEND MICROPY_SOURCE_PORT network_wlan.c mpnimbleport.c modsocket.c + lwip_patch.c modesp.c esp32_nvs.c esp32_partition.c diff --git a/ports/esp32/lwip_patch.c b/ports/esp32/lwip_patch.c new file mode 100644 index 000000000000..9c5e10fcbb40 --- /dev/null +++ b/ports/esp32/lwip_patch.c @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "lwip/memp.h" + +// This is a link-time patch to enforce the limit of max active TCP PCBs. A +// workaround for upstream issue https://github.com/espressif/esp-idf/issues/9670 +// +// Without this limit the number of TCP PCBs in TIME-WAIT is unbounded, which can +// have two problems on systems with a lot of short-lived TCP connections: +// +// - Higher memory usage. +// - Increased chance of stalled TCP connections due to port reuse. + +static unsigned active_tcp_pcbs; + +void *__real_memp_malloc(memp_t type); +void __real_memp_free(memp_t type, void *mem); + +void *__wrap_memp_malloc(memp_t type) { + if (type != MEMP_TCP_PCB) { + return __real_memp_malloc(type); + } + + if (active_tcp_pcbs >= MEMP_NUM_TCP_PCB) { + return NULL; + } + + void *res = __real_memp_malloc(MEMP_TCP_PCB); + if (res != NULL) { + ++active_tcp_pcbs; + } + return res; +} + +void __wrap_memp_free(memp_t type, void *mem) { + __real_memp_free(type, mem); + if (type == MEMP_TCP_PCB && mem != NULL) { + assert(active_tcp_pcbs); + --active_tcp_pcbs; + } +}