buglwIP - A Lightweight TCP/IP stack - Bugs: bug #65583, DNS lookup should take into...


You are not allowed to post comments on this tracker with your current authentication level.

bug #65583: DNS lookup should take into account available source IPv6 and IPv4 addresses

Submitter:  Sly Gryphon <sgryphon>
Submitted:  Thu 11 Apr 2024 09:46:39 PM UTC
Category:  IPv6 Severity:  3 - Normal
Item Group:  Faulty Behaviour Status:  None
Privacy:  Public Assigned to:  None
Open/Closed:  Open Planned Release:  None
lwIP version:  2.1.3

Sat 27 Apr 2024 06:52:57 AM UTC, comment #2: 

I have a branch with my code from ESP-LWIP moved across. Rather than a simple compare, I put in the structure of a full sort, and added the RFC 6724 examples as test cases.

It still takes a few short cuts and only properly handles the LWIP case of at most 2 results (one IPv6 and one IPv4), so not all the example tests are implemented.

`getaddrinfo()` now returns both results (linked; which is handled by `freeaddrinfo()`), so you may get up to two results (e.g. could be used for fallback/happy eyeballs in a dual stack environment).

I still need to run a test app to check it works; I couldn't see anything in LWIP (did find some unit test though, which I used).

I will try and getting it running with my ESP-IDF setup.

Branch is here, if anyone is interested:

Sly Gryphon <sgryphon>
Sat 27 Apr 2024 05:18:29 AM UTC, comment #1: 

This bug also breaks DNS64/NAT64 when a client with both IPv6 and IPv4 is deployed to an IPv6-only network.

Because the client supports both IPv6 and IPv4, it can use a single DNS query with AF_UNSPEC. This will work correctly if coming from a dual-stack network to any destination, e.g. an IPv6 only destination will not have an IPv4, so fall back to IPv6.

However when on an IPv6-only network, calls to a dual-stack destination will incorrectly get back the IPv4 address.

This includes DNS64/NAT64 configurations for IPv4-only hosts. DNS64 will generate a synthetic IPv6 address, however because of the hard coded "prefer IPv4" it is never used -- the (unusable) IPv4 address continues to be used.

i.e. An IPv6-only network is only support with IPv6-only destinations.

Sly Gryphon <sgryphon>
Thu 11 Apr 2024 09:46:39 PM UTC, original submission:  

# What is wrong

When both IPv4 and IPv6 are enabled, when connected to an IPv6-only network and looking up a dual-stack host, an unusable IPv4 address is returned.

For this particular bug:

  • IPv6 is enabled
  • The network provides a public IPv6 address
  • The destination has an IPv6 address, and there is a valid route

But the connection fails.

Connections fail on an IPv6-only network when connecting to a dual-stack destination, because the preference order is statically configured to IPv4 first. Even though a local IPv4 address is not currently available, for a dual-stack destination the IPv4 address is returned by getaddrinfo() (instead of the IPv6), so the connection fails.

Note that if you disable IPv4, then the connection works; if you enable IPv4 then then connection fails. Enabling IPv4 should not make IPv6 fail (and vice-versa).

## Example code

Example code, for the ESP-IDF implementation: https://github.com/espressif/esp-idf/pull/13250

To trigger the error you need to have both IPv6 and IPv4 enabled, but connect to an IPv6-only network, and then connect to a dual-stack destination, such as https://v4v6.ipv6-test.com/api/myip.php

Although the example uses LWIP 2.1.3, the same code (and same bug) is in current LWIP.

## Solution code

A pull request with a fix by implemented RFC 6724 destination address selection has been raised in a downstream project: https://github.com/espressif/esp-lwip/pull/66

This fix, or similar, should be applied to LWIP.

Note that LWIP already has an implementation of RFC 6724 source address selection. This uses the same RFC to determination the destination address selected to return for a DNS lookup.

## What should happen

With both IPv4 and IPv6 fully enabled, DNS lookup should work correctly in all network environments -- IPv4-only, IPv6-only, and dual-stack, for all reachable destinations, by taking into account available addresses.

For example when moving a device to an IPv6-only network only an IPv6 address is available (even though IPv4 is enabled), and so connections to a dual-stack host (that has both) should use the IPv6 address to make the connection.

## Issue

The DNS lookup function getaddrinfo() does not take into account available source address, and so does not work propertly across all network types.

In particular it preferences IPv4 over IPv6, and completely fails in an IPv6-only network for a dual-stack destination, as the IPv4 address is unreachable.

You can work around this in some cases, by checking available addresses yourself and then calling getaddrinfo() multiple times -- this approach has been used in the updated http_request example.

However HTTP is not secure and the same approach can't be used with HTTPS, as the host name is needed to TLS and resolved internally in the TLS code.

## Technical details

For the https_request example the TLS code esp_tls.c eventually calls getaddrinfo() passing in AF_UNSPEC to get any address.

However the code in netdb.c then converts this into a fixed preference order (when both IPv4 and IPv6 are enabled) of NETCONN_DNS_IPV4_IPV6.

A client with both IPv4 and IPv6 enabled should work in any network IPv4-only, IPv6-only, or dual-stack, and to any reachable destination.

  • The code works in an IPv4-only network.
  • In a dual-stack it kind of works because it returns the IPv4 address by preference (even if dual stack), and only returns IPv6 if that fails (i.e. the destination is IPv6 only).
  • However it fails in an IPv6-only network where the destination is dual-stack because it returns the unreachable IPv4 address.

Changing to use a static order of NETCONN_DNS_IPV6_IPV4 wouldn't fully work either.

This other order allows IPv6-only to work, and means that dual-stack preferences IPv6 and still falls back for IPv4-only destinations.

But it has the reverse problem that in an IPv4-only network a dual stack destination will fail, as it returns the unreachable IPv6 address.

## Proposed solution

To be able to work across all networks, the address selection needs to be dynamic based on what is actually available. (Not static based on what is enabled in configuration)

For a dual-stack destination, if a global (including ULA) IPv6 address is available, then use IPv6, but if it a gobal IPv6 address is not available (even though IPv6 is enabled it may not be provided, e.g. if currently on an IPv4-only network) then the IPv4 address needs to be used.

A full implementation of this approach is detailed in RFC 6724, taking into account not only what addresses are available, but their scopes and with special allowances for deprecated address ranges.

Available addresses should be sorted according to RFC 6724, with the application using the first address returned.

The standard linux function getaddrinfo() takes this approach "The sorting function used within getaddrinfo() is defined in RFC 3484" (RFC 3484 was replaced by RFC 6724). See https://man7.org/linux/man-pages/man3/getaddrinfo.3.html

This new DNS resolution dynamically based on available addresses could be configuration flagged to allow the old behaviour (fixed preference of IPv4) to continue to be an option.

Sly Gryphon <sgryphon>


(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

No files currently attached


Depends on the following items: None found

Items that depend on this one: None found


Carbon-Copy List
  • -email is unavailable- added by sgryphon (Submitted the item)

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.


    No changes have been made to this item

    Back to the top

    Powered by Savane 3.13-f762.
    Corresponding source code