Skip to content

Commit

Permalink
fix(dns): Dynamic DNS sorting so AF_UNSPEC works in IPv6-only network
Browse files Browse the repository at this point in the history
This fix adds an option to dynamically set the order based on whether
an the host has a global (or ULA) IPv6 address, allowing a device
with both IPv4 and IPv6 enabled to work in any network configuration,
including IPv4-only, IPv6-only, and dual-stack.

Without the option there is a static ordering preferring IPv4, which
means on an IPv6-only network, a dual-stack destination returns the
IPv4 address, which fails. Setting the static preference to IPv6
would have the opposite problem. A dynamic sort, based on available
source addresses, is required to work on all networks.
  • Loading branch information
sgryphon committed Feb 25, 2024
1 parent f792214 commit b894c48
Showing 1 changed file with 27 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/api/netdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,34 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
}
#endif /* ESP_LWIP */
}
#if LWIP_DNS_DYNAMIC_SORT
else {
/* AF_UNSPEC prefer IPv6 if we have global IPv6 */
// This is not a full implementation of RFC 6724, but it's a simple heuristic that works for most cases.
bool has_global_scope_ipv6 = false;
struct netif *netif;
for(netif = netif_list; netif != NULL; netif = netif->next) {
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
// Both global and unique local addresses have global scope.
// ULA assumes either private DNS or NAT66 (same assumpation as IPv4 private address ranges).
if (ip6_addr_isglobal(netif_ip6_addr(netif, i)) || ip6_addr_isuniquelocal(netif_ip6_addr(netif, i))) {
has_global_scope_ipv6 = true;
break;
}
}
if (has_global_scope_ipv6) {
break;
}
}
// NOTE: This will still preference IPv4 on IPv4-only networks (i.e. with only a link-local IPv6),
// and IPv6 on IPv6-only (and dual-stack) networks, so works for all cases.
if (has_global_scope_ipv6) {
type = NETCONN_DNS_IPV6_IPV4;
}
}
#endif /* LWIP_DNS_DYNAMIC_SORT */
#endif /* LWIP_IPV4 && LWIP_IPV6 */
// NOTE: If LWIP_IPV4 && LWIP_IPV6 are not defined this is a macro that eliminates `type`, so it still compiles
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
if (err != ERR_OK) {
return EAI_FAIL;
Expand Down

0 comments on commit b894c48

Please sign in to comment.