Bug 190495 - getaddrinfo forces preference of IPv4 over IPv6 addresses
Summary: getaddrinfo forces preference of IPv4 over IPv6 addresses
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Fedora
Classification: Fedora
Component: glibc
Version: rawhide
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Brian Brock
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2006-05-02 21:35 UTC by Valdis Kletnieks
Modified: 2007-11-30 22:11 UTC (History)
5 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2006-05-03 15:26:48 UTC
Type: ---
Embargoed:


Attachments (Terms of Use)
Patch to add default labels for fec0::/10 and fc00::/7, fix precedence of ::ffff:0:0 to 10 (3.12 KB, patch)
2006-05-17 13:24 UTC, David Woodhouse
no flags Details | Diff

Description Valdis Kletnieks 2006-05-02 21:35:31 UTC
Description of problem:
getaddrinfo forces precedence of IPv4 over IPv6 addresses

Version-Release number of selected component (if applicable):
glibc-2.4.90-3

How reproducible:


Steps to Reproduce:
1.
2.
3.
  
Actual results:


Expected results:


Additional info:
2006-04-15  Ulrich Drepper  <drepper>

        * sysdeps/posix/getaddrinfo.c: Fix precedence for IP V4-to-V6
        mapped addresses.

This change causes an IPv4-in-v6 mapped address to be selected with higher
precedence than a native IPv6 address (to be specific, after this change, the
default_precedence[] array matches RFC3494, section 10.3 (forcing a preference
for IPv4 addresses), rather than the table given in section 2.1

This has all sorts of side effects - in particular, getaddrinfo() now returns
all the possible IPv4 addresses before IPv6, so things like sendmail, ssh, and
other IPv6-capable programs will get an IPv4 address to try first.

The "simple" fix is to change the '96,100' entry in default_precedence[] to the
RFC-suggested '96,10'.

Comment 1 Ulrich Drepper 2006-05-03 15:26:48 UTC
Using the value we had before causes real problems with networks where there are
site-local IPv6 networks.  Real problems as in connections hang for minutes
before they are failing.  This is worse then giving precedence to IPv4.

This cannot be resolved until there is a mechanism to configure getaddrifo from
the outside.  This is hinted at in rfc 3484 but no details are given.  If you
have proposals what needs to be configured and how I'd be happy to hear about
this.  But this is a new bug and this one is none.  It's a decision which had to
be made until a more complete solution is available.

Comment 2 David Woodhouse 2006-05-03 15:51:32 UTC
Do we need configuration? The default selection should depend on whether we have
a Global IPv6 address on the system -- and I think it used to.

For example, when connecting to www.infradead.org, it should use
2002:d592:9a28::1 first if the local system has a Global IPv6 address, and
213.146.154.40 if not.

I believe the recent change was committed because glibc was choosing the IPv6
address first even when the local system has only a Site-local IPv6 address. The
solution to that problem wasn't to favour IPv4 _unconditionally_, surely?



Comment 3 Valdis Kletnieks 2006-05-03 15:58:43 UTC
Checking for a global address would probably be the *optimal* solution.

However, I'd settle for leaving the precedence value at 100, and adding a new
magic value for the RES_OPTIONS 'prefer_v6' that would stuff a 10 in there
instead.  Or some similar method for configuring it.

Are there any real-life use cases that need precedence tables other than the
rfc3484 tables in section 2.1 (the "default") and 10.3 ("prefer ipv4")?  If not,
we can hang this off  almost anything that can flag a boolean into the innards
of glibc....

Comment 4 Ulrich Drepper 2006-05-03 16:11:31 UTC
There are apparently other configuration possibilities people want.  After rfc
4193 people need to specify information for various local IPv6 addresses.

I'm not interested in another RES_* option.  It must be something more complete.
 And AI_ADDRCONFIG is the only automatic recognition of the addresses and it
only controls whether IPv4 or IPv6 addresses are looked for and returned at all.

Comment 5 David Woodhouse 2006-05-03 16:33:45 UTC
I agree with the decision not to implement more configuration, at least for now
until/unless something more complete is planned.

But we've regressed in the _basic_ case, where we're trying to connect to a
remote system which has both IPv6 Global address and IPv4 addresses. 

That's the most common case where everyone cares about RFC3484 -- we should pick
the remote system's IPv6 address if we have a Global IPv6 address of our own,
and we should pick its IPv4 address if we don't. That doesn't require any
special configuration from the user -- it's a basic implementation of the
default RFC3484 behaviour.


Comment 6 Ulrich Drepper 2006-05-03 16:41:51 UTC
We did not "regressed".  There is no regression if the code still works.  And
this is the safer of the two options for the moment so I won't change it again.
 Once there is the configuration capability you can change it to your in
whatever way you want.  For now I want a configuration which has the least
probability to cause _problems_.

Comment 7 David Woodhouse 2006-05-03 16:54:14 UTC
In Fedora Core 4, we used to get IPv6 connectivity to IPv6 hosts. Now we don't
-- and that's a regression in RFC3484 compliance.

Even when we have the configuration capability I'd only want the _basic_ RFC3484
compliance -- connect with IPv6 if that makes sense, else use IPv4. And I'd
argue that that should be the _default_.

Comment 8 Valdis Kletnieks 2006-05-03 16:57:06 UTC
Maybe we need to handle this similar to the way glibc handles timezones?  Maybe
a default in-library table that "mostly works", and then read in a precompiled
table from a specified file.  Of course, then we'll also need a program to take
an ASCII (or GUI) representation and write out the precompiled table...

Comment 9 Ulrich Drepper 2006-05-03 17:16:45 UTC
David, stop that nonsense.  It's no regressions.  It's a change in behavior.  If
the program still works nobody can or should care.

And yes, outside config files of some sort are needed.  I'm not sure we need a
precompiled data file because the data is likely not that plentiful.  That's
something which can be decided later.

For the beginning, it might be sufficient to provide the possibility to
overwrite the label and precedence tables.  We'll see whether label handling
needs to be overwritten, too.   Such an implementation need not be complicated.
 We can just read in the table (from text or binary form) and then use it
instead of the default_{label,precedence} tables.  Something like this might be
sufficient:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BEGIN LABEL
::1/128        0
:/0            1
2002::/16      2
::/96          3
::ffff:0:0/96  4
END LABEL

BEGIN PRECEDENCE
::1/128        50
::/0           40
2002::/16      30
::/96          20
::ffff:0:0/96  10
END PRECEDENCE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The only tricky bit is to sort the entries so that more generic entries do not
what before the more specific ones.


Comment 10 David Woodhouse 2006-05-03 17:36:10 UTC
I think the precedence tables are a red herring. Preferring IPv6 should be fine,
as long as we implement Rule 2 ("Prefer matching scope").

To go back to my example... if we have a Global IPv6 address which matches the
scope of the AAAA record of 'www.infradead.org', that would be preferred.
Otherwise, the IPv4 address would be preferred. 

You could even argue that it's Rule 1 ("Avoid unusable destinations"), since
it's a fairly safe bet that if you don't have a Global IPv6 address, you can't
communicate with another host on its Global IPv6 address. 

Whatever we _used_ to do would be fine.

Comment 11 Valdis Kletnieks 2006-05-03 18:14:45 UTC
"It's no regressions.  It's a change in behavior.  If the program still works
nobody can or should care."

Users probably don't care.  Network managers and designers certainly *do* care.
Just like users don't care if a DNS round-robins the A records, but *somebody*
certainly can and does care.

Comment 12 David Woodhouse 2006-05-03 18:26:22 UTC
It's a change in behaviour which seems to make us less compliant with RFC3484
than we used to be -- and is very suboptimal at least for me, because I want to
_use_ IPv6 connectivity so that it gets tested.

Comment 13 David Woodhouse 2006-05-09 08:49:50 UTC
(The change in behaviour has been identified as a kernel change. See bug #188364
for details)

Comment 14 Pekka Savola 2006-05-09 12:00:53 UTC
Hmm..  I don't see why site-locals should be a problem if "prefer matching
scope" is implemented correctly.  Unique Local addresses could be a problem, but
that's a topic for a different day.

Btw, you want a local optimization, you should configure IPv6 border routers of
the site-local domain to return ICMPv6 unreachables for global address space,
instead of discarding the packets.   That way linux would fall back to IPv4
immediately.

Comment 15 David Woodhouse 2006-05-09 12:32:27 UTC
(In reply to comment #14)
> Hmm..  I don't see why site-locals should be a problem if "prefer matching
> scope" is implemented correctly.  Unique Local addresses could be a problem, 
> but that's a topic for a different day.

I think the problem might arise because the IPv4 scope doesn't match _either_.
The local address is RFC1918 IPv4, which doesn't match the global IPv4 scope in
the A record. So they're _both_ mismatching scope.

I suspect that (our implementation of) Rule 2 should be amended to recognise
that some scopes are more unequal than others: Mismatching IPv6 scope is be
_worse_ than mismatching IPv4 scope, because IPv4 is almost always NATted, while
IPv6 never is. So IPv6 with mismatching scope should be lower down the list than
IPv4 with mismatching scope.

> Btw, you want a local optimization, you should configure IPv6 border routers 
> of the site-local domain to return ICMPv6 unreachables for global address 
> space, instead of discarding the packets.   That way linux would fall back to 
> IPv4 immediately.

Yes, that's a separate issue -- the border routers shouldn't have a default
route, and should be returning unreachables already. But 'immediate' fallback is
unfortunately not good enough anyway, while we still have broken software which
uses only the first result from getaddrinfo() anyway. It needs to 'just work'
like it did before, or I'm not going to get away with running radvd on the network.

Comment 16 David Woodhouse 2006-05-09 14:42:58 UTC
Perhaps a suitable solution to this would be to add this line to the default
gai.conf:

label fec0::/16 5

AIUI this should mean that Rule 5 of the destination address selection would
kick in and do what we want.

Comment 17 David Woodhouse 2006-05-09 15:25:15 UTC
I've updated a FC5 machine to glibc-2.4-7 from updates-testing. I've installed
/etc/gai.conf as follows:

label  ::1/128       0
label  ::/0          1
label  2002::/16     2
label ::/96          3
label ::ffff:0:0/96  4
label fec0::/16      5
#
precedence  ::1/128       50
precedence  ::/0          40
precedence  2002::/16     30
precedence ::/96          20
precedence ::ffff:0:0/96  100


However, it's still giving me the IPv6 address first. In fact, it was doing that
even before I installed my own gai.conf. Shouldn't it have been giving me the
IPv4 address first? 

open("/etc/gai.conf", O_RDONLY)         = 3
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6,
"2001:8b0:10b:1::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(3, {sa_family=AF_INET6, sin6_port=htons(32772), inet_pton(AF_INET6,
"::172.16.18.67", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(80),
sin_addr=inet_addr("81.187.2.161")}, 16) = 0
getsockname(3, {sa_family=AF_INET, sin_port=htons(32772),
sin_addr=inet_addr("172.16.18.67")}, [16]) = 0
Trying 2001:8b0:10b:1::1...


Comment 18 David Woodhouse 2006-05-09 16:06:46 UTC
After a reboot, the system is working properly. This default gai.conf ought to
DTRT for all systems (add label for site-local, back to IPv6 preference):

label  ::1/128       0
label  ::/0          1
label  2002::/16     2
label ::/96          3
label ::ffff:0:0/96  4
label fec0::/16      5
#
precedence  ::1/128       50
precedence  ::/0          40
precedence  2002::/16     30
precedence ::/96          20
precedence ::ffff:0:0/96  10

The default gai.conf shipped in the documentation needs
s/precendence/precedence/ throughout, btw.

Comment 19 Valdis Kletnieks 2006-05-09 16:22:40 UTC
I picked up glibc 2.4.90-6, and it didn't even require a reboot.  It *did*
require fixing the typo that David spotted though.  I'd have been sitting here
for a long afternoon staring at that otherwise.. ;)

Unless somebody has a issue that isn't solved with the proper gai.conf, I think
this one can be moved to CLOSED/RAWHIDE...

Comment 20 David Woodhouse 2006-05-09 16:38:08 UTC
(In reply to comment #19)
> Unless somebody has a issue that isn't solved with the proper gai.conf, I think
> this one can be moved to CLOSED/RAWHIDE...

By 'proper gai.conf' I assume you're referring to the one I suggest in comment
#18? I wouldn't refer to that as 'proper' -- it's different from the default
which glibc says we SHOULD be using.

However, we're _already_ diverging from RFC3484's suggested default, and
'SHOULD' only means 'unless you have a damn good reason' -- which we do.

Don't think it can quite be moved to CLOSED/RAWHIDE until Uli and Jakub have
actually changed the default though, right?

Comment 21 Ulrich Drepper 2006-05-09 16:53:43 UTC
There are a couple of things.

First, should this be added to the default file in the sources.  By default, we
don't install a gai.conf file because the parsing etc slows down the first call.
 Not much but still unnecessary.  A counter argument is that special casing the
obsolete (whether you like it or not) site-local addresses isn't the most
politically correct thing to do.

Second, the RFC 4193 addresses likely should be handled the same way.

Third, this all assumes that site-local IPv4 addresses are NATed.  This by
itself need not be the case.  This is unlikely right now and at the very least
has to be documented.

Comment 22 Valdis Kletnieks 2006-05-09 17:20:23 UTC
As of this release, for *most* sites, preferring a V4 address is the best
choice, so coding that and leaving gai.conf optional is probably the best
choice.  We'll probably want to revisit that around FC 7 or so.

If a site is using "site-local" IPv4 addresses that aren't in the 3 RFC1918
address spaces, they deserve what they get, and can hand-configure it for
themselves. :)


Comment 23 David Woodhouse 2006-05-09 17:25:35 UTC
First, I think it _should_ be the default in the sources. You overruled the
counter argument yourself -- fec0::/16 isn't going to be re-used any time soon.
And to hear you talking about political correctness instead of technical
usefulness is, erm, strange. :)

Secondly, er, maybe. I don't care much about that but you're probably right.

Third, no. We're only really assuming that site-local IPv4 are more _likely_ to
be NATted than site-local IPv6. That is universally true, since IPv6 is _never_
NATted. Given the choice of RFC1918->Globalv4 or Site-local->Globalv6, I cannot
comprehend a situation in which it's going to be correct to choose the IPv6
version over the IPv4.

The suggested default is only reverting to the long-standing behaviour we used
to have by coincidence.

Comment 24 Pekka Savola 2006-05-09 17:47:56 UTC
Re: #22, while preferring v4 could possibly have been a good transition
practice, if everyone had been chosen that approach like 5 years ago, I think
it's NOT a good idea to change that preference right now -- at least with an
update such as this.

Changing it would break a lot of apps (e.g., server-side v6 apps that don't use
IPV6_V6ONLY sockopt as they can no longer create a v6 socket) and more
important, almost every IPv6 deployer's expectations on how getaddrinfo behaves
by default.


Comment 25 David Woodhouse 2006-05-10 16:08:35 UTC
Pekka, please could you comment on my suggested plan of just adding an entry to
the default label/precedence table for site-local scope with a distinct label?
That should return us to the behaviour we used to have and everyone should be
happy, right?


Comment 26 Pekka Savola 2006-05-11 05:18:00 UTC
Re: #25, I see no harm in adding a site-local label [*].  (Btw, the prefix is
fec0::/10.)  To restore old functionality, it would seem to be a good approach
to do so.  I'd further encourage to consider removing the label in (say) FC7
timeframe so that folks don't end up using site-locals forever.

I'm not certain it fixes all the problems under every circumstance though, as
the source address selection rule 5 prefers outgoing interface over the label.

[*] site-local address leaks (e.g., in global DNS) cause minor issues though,
but as those are due to misconfiguration, that should be OK.

If you are not concerned about ULA leaks either, you might also consider adding
yet another label for FC00::/7, but that's a separate issue and I'm not certain
what's the best approach for this in longer term, so it doesn't need to be done now.



Comment 27 David Woodhouse 2006-05-17 13:24:50 UTC
Created attachment 129314 [details]
Patch to add default labels for fec0::/10 and fc00::/7, fix precedence of ::ffff:0:0 to 10

Comment 28 David Woodhouse 2006-05-17 15:29:16 UTC
glibc packages with this patch applied are available from
ftp://ftp.infradead.org/pub/dwmw2-fc5/

Comment 29 Peter Bieringer 2006-07-23 12:02:25 UTC
Following bugs are partially related to this one:
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=181061
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=199680

I have installed now glibc from
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=190495#c28
which solves the sort order issue for mentioned bugs.

It does not solve the connect to the wrong host if IPv6 connect results in
connection refused.

try following:

# Behavior IPv4-only:

1) extend /etc/resolv.conf:
search 3.getaddrinfo.bieringer.de 5.getaddrinfo.bieringer.de

2) telnet to a unresolvable host
$ telnet test.unknown
Trying 127.0.0.3...
telnet: connect to address 127.0.0.3: Connection refused

-> end of program

$ host -t any test.unknown.3.getaddrinfo.bieringer.de
test.unknown.3.getaddrinfo.bieringer.de has address 127.0.0.3
$ host -t any test.unknown.5.getaddrinfo.bieringer.de
test.unknown.5.getaddrinfo.bieringer.de has address 127.0.0.5

Correct behavior, result from search suffix 5.getaddrinfo.bieringer.de is not used.


# Behavior IPv4/IPv6 mixed environment:
2) extend /etc/resolv.conf:
search 2.getaddrinfo.bieringer.de 3.getaddrinfo.bieringer.de

$ telnet test.unknown
Trying fec0::2...
telnet: connect to address fec0::2: Connection refused
Trying 127.0.0.3...
telnet: connect to address 127.0.0.3: Connection refused

-> end of program

$ host -t any test.unknown.2.getaddrinfo.bieringer.de
test.unknown.2.getaddrinfo.bieringer.de has IPv6 address fec0::2
$ host -t any test.unknown.3.getaddrinfo.bieringer.de
test.unknown.3.getaddrinfo.bieringer.de has address 127.0.0.3

Faulty behavior.

After IPv6 connect to test.unknown.2.getaddrinfo.bieringer.de fails it falls
back to IPv4 connect to test.unknown.3.getaddrinfo.bieringer.de, which is a
different host!

This should not happen imho, glibc has to prevent (or filter results) from other
search suffices than the one with leads to a the successful AAAA lookup.


Comment 30 Peter Bieringer 2006-07-23 13:21:50 UTC
The glibc installed from
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=190495#c28 has still a
strange impact which can be greatly demonstrated using e.g. firefox.

1) setup a local http server

2) configure site local addresses to your loopback interface by e.g.
# for i in 1 2 3 4 5; do ip addr add  fec0::$i dev lo; done

3) add at least 2.getaddrinfo.bieringer.de to your search suffices

4) start firefox and go to URL www.redhat.com

Result: default page of local http server is displayed.

Reason (check using tcpdump), AAAA queries are made through all search suffices,
afterwards A queries are done. Because of the sorting, now the AAAA result wins,
but for host "www.redhat.com.2.getaddrinfo.bieringer.de", which points to fec0::2.

 AAAA? www.redhat.com. (32)
 AAAA? www.redhat.com. (43)
 AAAA? www.redhat.com.2.getaddrinfo.bieringer.de. (59)
 A? www.redhat.com. (32)

This is imho a faulty and bad behavior.
Lookups have to be done in a different way like:

for $suffix in ("", searchsuffices(/etc/resolv.conf)) {
 $result_aaaa = lookup(AAAA,$host.$suffix)
 $result_a = lookup(A, $host.$suffix)
 if (defined $result_aaaa || $defined result_a) {
   break;
 };
}
sortresults($result_a, $result_aaaa)


Current implementation looks like working in following manner:

for $suffix in ("", searchsuffices(/etc/resolv.conf)) {
 $result_aaaa = lookup(AAAA,$host.$suffix)
 if (defined $result_aaaa) {
   break;
 };
};
for $suffix in ("", searchsuffices(/etc/resolv.conf)) {
 $result_a = lookup(A, $host.$suffix)
 if ($defined result_a) {
   break;
 };
};
sortresults($result_a, $result_aaaa)

And this leads to strange behavior and has a security impact because unexpected
addresses can be delivered to a client.

BTW: if you add only 1.getaddrinfo.bieringer.de, nothing will happen at
all...except that I can track all your request (via named query log) to hosts
which still doesn't provide an AAAA entry. This would be a privacy issue.

I recommend to fix all currently maintained glibc version to prevent such issue.


Note You need to log in before you can comment on or make changes to this bug.