caf
caf

Reputation: 239341

Determining when to try an IPv6 connection and when to use IPv4

I'm working on a network client program that connects to public servers, specified by the user. If the user gives me a hostname to connect to that has both IPv4 and IPv6 addresses (commonly, a DNS name with both A and AAAA records), I'm not sure how I should decide which address I should connect to.

The problem is that it's quite common for machines to support both IPv4 and IPv6, but only to have global connectivity over IPv4. The most common case of this is when only IPv6 link-local addresses are configured. At the moment the best alternatives I can come up with are:

  1. Try the IPv6 address(es) first - if the connection fails, try the IPv4 address(es); or
  2. Just let the user specify it as a config setting ("prefer_ipv6" versus "prefer_ipv4").

The problem I can see with option 1 is that the connection might not fail straight away - it might take quite a while to time out.

Upvotes: 10

Views: 5881

Answers (5)

Steve-o
Steve-o

Reputation: 12866

Subsequent to the question being asked the IETF has proposed an answer to this question with RFC6555, a.k.a. Happy Eyeballs.

The pertinent point being the client and server may both have IPv4 and IPv6 but a hop in between may not so it is impossible to reliably predict which path will work.

Upvotes: 6

bames53
bames53

Reputation: 88225

This talk has the solution. To summarize;

  • Sometimes there are problems with either DNS lookups or the subsequent connection to the resolved address
  • You don't want to wait for connecting to an IPv6 address to timeout before connecting to the IPv4 address, or vice versa
  • You don't want to wait for a lookup for an AAAA record to timeout before looking for an A record or vice versa
  • You don't want to stall while waiting for both AAAA and A records before attempting to connect with whichever record you get back first.

The solution is to lookup AAAA and A records simultaneously and independently, and to connect independently to the resolved addresses. Use whatever connection succeeds first.


The easiest way to do this is to allow the networking API do it for you using connect-by-name networking APIs. For example, in Java:

InetSocketAddress socketAddress = new InetSocketAddress("www.example.com", 80);
SocketChannel channel = SocketChannel.open(socketAddress);
channel.write(buffer);

The slide notes say at this point:

Here we make an opaque object called an InetSocketAddress from a host and port, and then when we open that SocketChannel, that can complete under the covers, doing whatever is necessary, without the application ever seeing an IP address.

Windows also has connect-by-name APIs. I don’t have code fragments for those here.

Now, I’m not saying that all implementations of these APIs necessarily do the right thing today, but if applications are using these APIs, then the implementations can be improved over time.

The di!erence with getaddrinfo() and similar APIs is that they fundamentally can’t be improved over time. The API definition is that they return you a full list of addresses, so they have to wait until they have that full list to give you. There’s no way getaddrinfo can return you a partial list and then later give you some more.

Upvotes: 1

MarcH
MarcH

Reputation: 19836

You should let the system-wide configuration decide thanks to getaddrinfo(). Just like Java does. Asking every single application to try to cater for every single possible IPv6 (mis)configuration is really not scalable! In case of a misconfiguration it is much more intuitive to the user if all or none applications break.

On the other hand you want to try to log annoying delays and time-outs profusely, so users can quickly identify what to blame. Just like every other delays ideally, including (very common) DNS time-outs.

Upvotes: 2

Martin v. Löwis
Martin v. Löwis

Reputation: 127587

Please do try IPv6. In the significant majority of installations, trying to create an IPv6 connection will fail right away if it can't succeed for some reason:

  • if the system doesn't support IPv6 sockets, creating the socket will fail
  • if the system does support IPv6, and has link-local addresses configured, there won't be any routing table entry for the global IPv6 addresses. Again, the local kernel will report failure without sending any packets.
  • if the system does have a global IP address, but some link necessary for routing is missing, the source should be getting an ICMPv6 error message, indicating that the destination cannot be reached; likewise if the destination has an IPv6 address, but the service isn't listening on it.

There are of course cases where things can break, e.g. if a global (or tunnel) address is configured, and something falsely filters out ICMPv6 error messages. You shouldn't worry about this case - it may be just as well that IPv4 connectivity is somehow broken.

Of course, it's debatable whether you really need to try the IPv6 addresses first - you might just as well try them second. In general, you should try addresses in the order in which they are returned from getaddrinfo. Today, systems support configuration options that let administators decide in what order addresses should be returned from getaddrinfo.

Upvotes: 10

Will Bickford
Will Bickford

Reputation: 5386

Some ideas:

  1. Allow the user to specify the preference on a per-site basis.
  2. Try IPv4 first.
  3. Attempt IPv6 in parallel upon the first connection.
  4. On subsequent connections, use IPv6 if the connection was successful previously.

I say to try IPv4 first because that is the protocol which is better established and tested.

Upvotes: -3

Related Questions