Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please update/clarify/justify recommendations for IPv4-only vs. any-protocol #62

Open
smcv opened this issue Apr 17, 2018 · 7 comments
Open

Comments

@smcv
Copy link

smcv commented Apr 17, 2018

The Debian package for nss-mdns gets occasional requests to enable mdns{_minimal,} instead of mdns4{_minimal,} by default. It isn't clear that obeying those requests would actually be a good thing, but it would be useful for the recommendations and their reasons to be written down in the upstream documentation, where they can be updated in-place if the facts change.

I'll reply to this issue report with my understanding of the situation.

@smcv
Copy link
Author

smcv commented Apr 17, 2018

Background

There are several lookups that we might be asked to carry out:

  • Resolve a name to zero or more addresses (gethostbyname)
    • The name might be in .local., or remote
    • The name might exist, or not
    • The client might ask for it as AF_UNSPEC, AF_INET only, AF_INET6 only (unlikely), AF_INET6 falling back to AF_INET on failure, or AF_INET falling back to AF_INET6 on failure
  • Resolve an address to zero or more names (gethostbyaddr)
    • The address might be link-local, on a local LAN, or remote
    • The address might have a name, or not

Our goals and constraints are:

  • If resolution would succeed, we want to let it succeed.
  • If resolution would fail, we want it to fail promptly. nss-mdns is often installed by default or pulled in by dependencies, and users complain bitterly if it makes their software slow, so we want to avoid it making their software slow wherever possible.
  • We can't know whether resolution actually succeeds without waiting up to 5 seconds (with Avahi defaults) which is not always acceptable.
  • So, if we have reason to believe that resolution would probably fail, we don't want to try.
  • Each resolution attempt is independent, and we do not have an opportunity to carry over state from one resolution attempt to the next.

For gethostbyname, by default mdns{,4,6} apply the approximately same heuristics as mdns{,4,6}_minimal: if the name is not in .local., then they decline to resolve it, so that clients won't have to wait 5 seconds to find out that there is no such name. This avoids a user-visible performance impact on resolving nonexistent domain names, for instance if the user types nosuchname.example.com into a web browser. So we get:

  • getent hosts thisexists.local: very short delay, returns its address(es)
  • getent hosts doesnotexist.local: 5 second delay, returns no addresses
  • getent hosts example.com: no delay, mDNS not used
  • getent hosts nosuchname.example.com: no delay, mDNS not used

For gethostbyaddr, by default mdns{,4,6}_minimal will decline to resolve anything that isn't in the IPv4 link-local range. This avoids a user-visible performance impact on looking up remote IP addresses with no reverse DNS, for example in SpamAssassin rules or when running mtr. The non-minimal version attempts to resolve all IP addresses, resulting in a 5 second delay if the IP address has no reverse record (PTR) in another information source (DNS, hosts file etc.).

Distributions typically only enable mdns4{,_minimal} in resolv.conf. This means gethostbyname will only return IPv4 addresses, and gethostbyaddr will decline to resolve IPv6 addresses to their names even if we are not using the minimal module. Users sometimes ask for this default to be changed to mdns{,_minimal}.

Reasons to use mdns instead of mdns4

If we used mdns instead of mdns4, the advantages would be:

  • gethostbyname could return IPv6 addresses as well as IPv4 addresses if both exist.
    • Rebuttal: For dual-stack clients this is not a real advantage, because IPv4 works just as well on LANs as IPv6 does. My claim is that IPv6-only clients are not common and there is little reason to have them, because clients often need to be able to access IPv4 servers somehow (even if via NAT). IPv6 is desirable in general because there is a shortage of globally-unique IPv4 addresses, but mDNS is a link-local protocol that only needs locally-unique IPv4 addresses (169.254.x.x or RFC 1918), and there is no shortage of those. If an unusual client has been specially configured to be IPv6-only, then it can equally well be configured to use mdns instead of mdns4.
  • gethostbyname could return IPv6 addresses if the service has no IPv4 addresses.
    • Rebuttal: as above, this only helps if either the client or the service is IPv6-only, but there seems little point in making a client or a service be IPv6-only.

Reasons to use mdns4 instead of mdns

If we used mdns instead of mdns4, the disadvantages would be: more delays. A high-quality IPv6-native client should use AF_UNSPEC when resolving a name, so that it can get IPv4 and IPv6 addresses in any order. However, when porting old programs like telnet to support IPv6, it is or was common to make them do an IPv6 lookup, followed by an IPv4 lookup if the IPv6 lookup failed (or perhaps the other way around). Because mDNS can't return negative results without waiting a few seconds, this makes those programs slow in the presence of mDNS:

  • if the name is IPv4-only: IPv6 lookup, wait 5 seconds for failure, IPv4 lookup, success -> 5 second delay, instead of almost-immediate success for mdns4
  • if the name doesn't exist: IPv6 lookup, wait 5 seconds for failure, IPv4 lookup, wait 5 seconds for failure -> 10 second delay, instead of 5 for mdns4

This is exacerbated by older mDNS implementations like 2008-era Windows and 2008-era embedded devices, which often only advertised IPv4 addresses (or perhaps even only had IPv4 addresses), although newer implementations should hopefully all be dual-stack-capable.

@smcv
Copy link
Author

smcv commented Apr 17, 2018

A relevant Debian bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=466014

@smcv
Copy link
Author

smcv commented Apr 26, 2018

One thing that might potentially help us to get out of this hole is to observe that the case where we get extra delays is exactly the case where the application does the lookup with AF_INET6, waits for failure, and then retries with AF_INET.

If the application does this, then current glibc doesn't return the scope ID, which means IPv6 link-local addresses (the fe80:: range) don't work anyway. So perhaps logic like this would be useful as a default?

if (lookup is AF_UNSPEC)
  try to look up the address in both families
else if (lookup is AF_INET)
  try to look it up as IPv4 only)
else
  decline to resolve, to avoid delay (and to not return addresses without their scopes)

@pavlix
Copy link

pavlix commented Apr 26, 2018

Hello. I gathered lots of information on glibc resolver issues, getaddrinfo API limitations and bugs including totally wrong AI_ADDRCONFIG behavior, dual-stack application testing and DNSSEC considerations. But this is not a topic for a single ticket, so please reach out to me (e.g. on IRC Freenode) if you're interested in my feedback.

@lathiat
Copy link

lathiat commented Apr 26, 2018

Absent other logic (which may be smarter); I would suggest that in reality you could reduce the timeout to 1 second (at the very least for IPv6) without much ill effect. Which would probably significantly improve the situation.

I also wonder if we could hack in some smarts (would probably have to be on the avahi-daemon side) to timeout quickly the IPv6 for a given hostname if we already have an IPv4 host in the cache.

@agoode
Copy link
Contributor

agoode commented Sep 4, 2018

If we introduced a new config file (#65) is there anything from this issue that could go in there?

@mkj
Copy link

mkj commented Mar 18, 2022

Would returning just a first response of either v4 or v6 be an option? That seems like it would be fairly straightforward to add, sending both queries to Avahi and waiting for the first FD to have data.

It isn't quite correct behaviour for getaddrinfo(), but then neither is only returning v4 addresses when v6 ones exist.

(My use case was a little embedded board with only ipv6 link local addressing to avoid extra ipv4 config, was surprised that nss-mdns wasn't working with ipv6 out of the box)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants