feat(lwip): Added wrapper for getaddrinfo to handle AF_UNSPEC

This commit is contained in:
Abhik Roy
2024-10-03 23:46:49 +10:00
parent 36d5d8c31c
commit d846929dc3
6 changed files with 113 additions and 1 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <netdb.h>
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;
}

View File

@@ -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__

View File

@@ -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 <stddef.h>
#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
}

View File

@@ -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