2013-03-20

Outstanding issue in Debian: unroutable public v6 IP

Given how Debian and Ubuntu nowadays enable IPv6 support by default, the routing table includes some IPv6 entries by standard. However, this has the unpleasant side-effect that lib C tries to connect to public IPv6 addresses, even in a case when the only IPv6 entries are for loopback and local link. Apparently, libc incorrectly assumes that the mere presence of any IPv6 means that there is a usable default IPv6 route. This results in, among other things, APT failing at fetching packages from the Debian repository if the DNS rotation returned the IPv6 address for the server first. I was thus wondering whether libc6 could be made to reject public IPv6 addresses, in cases when the host has no IPv6 gateway in its routing table? Or would there be a smarter way to prevent applications from failing at contacting IPv6 addresses whenever there is no IPv6 connection to the outside world?

21 comments:

Philipp Kern said...

If you don't have IPv6, it should instantly fail anyway. Apart from that, have a look at /etc/gai.conf. But rather invest time in getting IPv6 up and running.

Martin-Éric said...

This ISP doesn't offer IPv6.

Expecting end-users to set up their own IPV6 tunnel, just to work around the issue, amounts to avoid dealing with bad programing. The resolver should simply be smart about this: if there's no publicly routable IPv6 connection, silently discard all IPv6 addresses returned by DNS.

Philipp Kern said...

Now I need to dig up a machine without IPv6, which is surpringsly hard. There it is:

pkern@i30pc49:~$ telnet www.google.de 80
Trying 173.194.35.159...
Connected to www.google.de.
Escape character is '^]'.

Now tell me what the problem is, because:

pkern@i30pc49:~$ ip -6 route
fe80::/64 dev eth0 proto kernel metric 256

pkern@i30pc49:~$ ip -6 addr
1: lo: mtu 16436
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qlen 1000
inet6 fe80::f66d:4ff:fe71:9752/64 scope link
valid_lft forever preferred_lft forever

There's link-local addressing and routing and it doesn't try to connect to IPv6.

Martin-Éric said...

You're describing the problem opposite to what I experience.

Take the typical Debian mirror which publishes both IPv4 and IPv6 addresses and returns either one of them on a rotary basis. Whenever DNS happens to return the IPv6 address, apt-get replies with:

W: Failed to fetch http://http.debian.net/debian/dists/testing/contrib/i18n/Translation-en Cannot initiate the connection to ftp.fi.debian.org:80 (2001:708:310:54::99). - connect (101: Network is unreachable) [IP: 2001:708:310:54::99 80]

This is because my router doesn't have any routable public IPv6 address to the outside world, since this ISP doesn't offer IPv6 connectivity.

Philipp Kern said...

ip -6 addr and route please.

Georg Müller said...

If the DNS only returns IPv6, this is surely a problem. How should the system know that each DNS may return another IP.

But if it returns both v6 and v4, your system should do the right choice.

Georg Müller said...

I mean "each DNS request"

Martin-Éric said...

$ sudo route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.1.254 0.0.0.0 UG 0 0 0 wlan0
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 wlan0
172.16.1.0 0.0.0.0 255.255.255.0 U 0 0 0 wlan0


$ ip -6 addr
1: lo: mtu 16436
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: wlan0: mtu 1500 qlen 1000
inet6 fe80::213:2ff:fe0f:f065/64 scope link
valid_lft forever preferred_lft forever

Philipp Kern said...

Well, route's obviously unhelpful, I did mean ip -6 route. Apart from that uname -a.

Martin-Éric said...

$ ip -6 route
fe80::/64 dev wlan0 proto kernel metric 256

$ uname -a
Linux tupla 3.2.0-4-686-pae #1 SMP Debian 3.2.39-2 i686 GNU/Linux

Philipp Kern said...

Works for me, even if I simulate a redirect to a dual-stack host on my own server. It falls back to IPv4 without a warning (wheezy kernel and wheezy apt), even if I configure an IPv6 address that's not being routed.

Maybe we need an strace (possibly with a high -s value) to see what's happening here. ENETUNREACH should just cause a retry with the next entry out of the DNS round-robin list.

Steinar H. Gunderson said...

What you want is “Happy Eyeballs”, which tries both IPv4 and IPv6 in parallel (possibly with a small head start for IPv6, to prefer it) and just takes the one that comes back first. Unfortunately, Linux doesn't have a standard interface for this (yet?); OS X has. Both Firefox and Chrome implements this for some time now.

I have to concur this sounds like a configuration error somewhere, though; if you don't have IPv6, you should also not get a public IPv6 address. And if you only have link-local (or ULA), this should fail quite instantly (through slightly different mechanisms for the two).

/* Steinar */

Sam Morris said...

What's your output from this command?

$ getent ahosts http.debian.net
46.4.205.43 STREAM http.debian.net
46.4.205.43 DGRAM
46.4.205.43 RAW
2a01:4f8:131:152b::42 STREAM
2a01:4f8:131:152b::42 DGRAM
2a01:4f8:131:152b::42 RAW

I believe the order of the output is significant; my ISP does not provide IPv6 connectivity, so the IPv6 addresses are only tried after the IPv4 addresses fail.

On a server with IPv6 connectivity:

$ getent ahosts http.debian.net
2a01:4f8:131:152b::42 STREAM http.debian.net
2a01:4f8:131:152b::42 DGRAM
2a01:4f8:131:152b::42 RAW
46.4.205.43 STREAM
46.4.205.43 DGRAM
46.4.205.43 RAW

Martin-Éric said...

$ getent ahosts http.debian.net
46.4.205.43 STREAM http.debian.net
46.4.205.43 DGRAM
46.4.205.43 RAW
2a01:4f8:131:152b::42 STREAM
2a01:4f8:131:152b::42 DGRAM
2a01:4f8:131:152b::42 RAW

Philipp Kern said...

http.debian.net is not relevant, it's failing after the redirect.

Martin-Éric said...

In my case, the redirect most probably goes to:

$ getent ahosts ftp.fi.debian.org
130.230.54.99 STREAM ftp.fi.debian.org
130.230.54.99 DGRAM
130.230.54.99 RAW
2001:708:310:54::99 STREAM
2001:708:310:54::99 DGRAM
2001:708:310:54::99 RAW

Martin-Éric said...

My point being that, on systems without a public IPv6 connection to the outside world, the resolver should have flat out discarded 2001:708:310:54::99 from its answer. Because it did not, if the connection to 130.230.54.99 times out, it apparently tries to connect to the IPv6 address, which of course fails abruptly because there's no IPv6 route to the outside world. As to whether the best place to implement such a discard would in apt-get, in libc6 or in the kernel, I wouldn't know.

Philipp Kern said...

Aha, so you only shared half of the story. Sure, if IPv4 times out, it will try IPv6. But what's the benefit of not doing that? It should just display that both attempts failed and not just the last one which then indicates IPv6 breakage, while it was really IPv4 breakage.

Obviously most people including me despise NAT, but it is possible even with link-local addresses.

Martin-Éric said...

I can only guess that it's caused by a timeout on an HTTP connection to the IPv4 address.

How would connecting to the outside world using the IPv6 link-local work? Keeping in mind that I'm connecting via a WRT54GL running OpenWRT and set up to attempt to request IPv6 from the ISP (which currently fails, since the ISP doesn't offer IPv6). The router *is* IPv6-capable, but currently not using the capability since the ISP doesn't offer it.

Raphael Geissert said...

Pointing to the corresponding bug report would be better for context.
There shouldn't be a reason for libc to strip the v6 addresses by requiring it to check the routing table.

Martin-Éric said...

I welcome better suggestions on how libc6 should deal with this. Loudly failing the way it currently does is not acceptable.