diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index 5108e5cca3..6fb648f5d6 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -141,6 +141,11 @@ if(CONFIG_LWIP_ENABLE) list(APPEND srcs "lwip/src/core/ipv4/acd.c") endif() + if(CONFIG_LWIP_USE_ESP_GETADDRINFO) + list(APPEND srcs "apps/netdb/esp_netdb.c") + endif() + + if(NOT ${target} STREQUAL "linux") # Support for vfs and linker fragments only for target builds set(linker_fragments linker.lf) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 33f87955b2..bbaa605f93 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -1215,6 +1215,16 @@ menu "LWIP" It's typically used with CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF which configures a callback to collect the DNS info on esp_netif layer. + config LWIP_USE_ESP_GETADDRINFO + bool "Enable esp_getaddrinfo() instead of lwip_getaddrinfo()" + depends on LWIP_IPV4 && LWIP_IPV6 + default n # Should be "true" by default, causes DNS issues in OpenThread when enabled (IDF-11771) + help + Use esp_getaddrinfo() for DNS lookups instead of lwip_getaddrinfo(). + This function correctly handles the AF_UNSPEC flag for resolving + both IPv4 and IPv6 addresses. Available only when both IPv4 and IPv6 + are enabled. + endmenu # DNS config LWIP_BRIDGEIF_MAX_PORTS diff --git a/components/lwip/apps/netdb/esp_netdb.c b/components/lwip/apps/netdb/esp_netdb.c new file mode 100644 index 0000000000..4eb39df5a9 --- /dev/null +++ b/components/lwip/apps/netdb/esp_netdb.c @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + + +int esp_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct addrinfo hints_copy = *hints; + struct addrinfo *res4 = NULL, *res6 = NULL; + int ret4, ret6; + + if (hints->ai_family == AF_UNSPEC) { + // Attempt to get IPv4 addresses + hints_copy.ai_family = AF_INET; + ret4 = lwip_getaddrinfo(nodename, servname, &hints_copy, &res4); + + // Attempt to get IPv6 addresses + hints_copy.ai_family = AF_INET6; + ret6 = lwip_getaddrinfo(nodename, servname, &hints_copy, &res6); + + // Handle results + if (ret4 != 0 && ret6 != 0) { + return ret4; // Both calls failed, return the first error code + } + + *res = res4 ? res4 : res6; // Start with the non-NULL result + + if (res4 && res6) { + // Append IPv6 list to the end of IPv4 list + struct addrinfo *last = res4; + while (last->ai_next) { + last = last->ai_next; + } + last->ai_next = res6; + } + + return ERR_OK; // Success + } else if ((hints->ai_family == AF_INET) || (hints->ai_family == AF_INET6)) { + return lwip_getaddrinfo(nodename, servname, hints, res); + } + + // Handle unsupported cases + return EAI_FAMILY; +} diff --git a/components/lwip/include/apps/esp_netdb.h b/components/lwip/include/apps/esp_netdb.h new file mode 100644 index 0000000000..ac4c71cd93 --- /dev/null +++ b/components/lwip/include/apps/esp_netdb.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_NETDB_H__ +#define __ESP_NETDB_H__ + +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Custom getaddrinfo() wrapper for lwIP that handles AF_UNSPEC correctly. + * + * Resolves both IPv4 and IPv6 addresses when AF_UNSPEC is specified. Works + * even if only one protocol (IPv4 or IPv6) is enabled in lwIP. Merges results + * if both protocols are available. + * + * @return 0 on success, or an error code on failure. + * - `EAI_FAMILY`: Address family not supported. + * + * @note Caller must free the result list with freeaddrinfo(). + * + * @see getaddrinfo(), freeaddrinfo() + */ +#if CONFIG_LWIP_USE_ESP_GETADDRINFO +int esp_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __ESP_NETDB_H__ diff --git a/components/lwip/include/lwip/netdb.h b/components/lwip/include/lwip/netdb.h index 326fea1ff5..8a9ec4d316 100644 --- a/components/lwip/include/lwip/netdb.h +++ b/components/lwip/include/lwip/netdb.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,9 @@ #include #include_next "lwip/netdb.h" #include "sdkconfig.h" +#if CONFIG_LWIP_USE_ESP_GETADDRINFO +#include_next "esp_netdb.h" +#endif #ifdef __cplusplus extern "C" { @@ -20,7 +23,11 @@ static inline struct hostent *gethostbyname(const char *name) static inline void freeaddrinfo(struct addrinfo *ai) { lwip_freeaddrinfo(ai); } static inline int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) +#ifdef CONFIG_LWIP_USE_ESP_GETADDRINFO +{ return esp_getaddrinfo(nodename, servname, hints, res); } +#else { return lwip_getaddrinfo(nodename, servname, hints, res); } +#endif #ifdef __cplusplus } diff --git a/docs/en/api-guides/lwip.rst b/docs/en/api-guides/lwip.rst index 77a6c281c1..ec3433b518 100644 --- a/docs/en/api-guides/lwip.rst +++ b/docs/en/api-guides/lwip.rst @@ -474,6 +474,8 @@ The number of IP addresses returned by network database APIs such as ``getaddrin In the implementation of ``getaddrinfo()``, the canonical name is not available. Therefore, the ``ai_canonname`` field of the first returned ``addrinfo`` structure will always refer to the ``nodename`` argument or a string with the same contents. +The ``getaddrinfo()`` system call in lwIP within ESP-IDF has a limitation when using ``AF_UNSPEC``, as it defaults to returning only an IPv4 address in dual stack mode. This can cause issues in IPv6-only networks. To handle this, a workaround involves making two sequential calls to ``getaddrinfo()``: the first with AF_INET to query for IPv4 addresses, and the second with AF_INET6 to retrieve IPv6 addresses. To address this, the custom ``esp_getaddrinfo()`` function has been added to the lwIP port layer to handle both IPv4 and IPv6 addresses when AF_UNSPEC is used. The :ref:`CONFIG_LWIP_USE_ESP_GETADDRINFO` option, available when both IPv4 and IPv6 are enabled, controls whether ``esp_getaddrinfo()`` or the default lwIP implementation is used. It is disabled by default. + Calling ``send()`` or ``sendto()`` repeatedly on a UDP socket may eventually fail with ``errno`` equal to ``ENOMEM``. This failure occurs due to the limitations of buffer sizes in the lower-layer network interface drivers. If all driver transmit buffers are full, the UDP transmission will fail. For applications that transmit a high volume of UDP datagrams and aim to avoid any dropped datagrams by the sender, it is advisable to implement error code checking and employ a retransmission mechanism with a short delay. .. only:: esp32