Bug 12377 - getaddrinfo() with AI_ADDRCONFIG won't suppress AAAA DNS queries when only IPv6 loopback and link-local addresses are present
Summary: getaddrinfo() with AI_ADDRCONFIG won't suppress AAAA DNS queries when only IP...
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: network (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-01-08 20:57 UTC by Tore Anderson
Modified: 2023-09-20 15:12 UTC (History)
8 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
Patch to make getaddrinfo() ignore link-local addresses for AI_ADDRCONFIG (250 bytes, patch)
2011-02-11 15:53 UTC, Tore Anderson
Details | Diff
a temporary fix to ignore the whole AI_ADDRCONFIG thing (1010 bytes, patch)
2012-09-22 15:43 UTC, Pavel Šimerda
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tore Anderson 2011-01-08 20:57:34 UTC
If the system is configured with link-local (and loopback) IPv6 addresses only, getaddrinfo() will look up AAAA records in DNS and return any results to the caller, even if the caller is specififying the AI_ADDRCONFIG hint.

This defeats the purpose of AI_ADDRCONFIG, as an IPv6-capable operating system (which includes all modern GNU/Linux distributions) will automatically configure link-local IPv6 addresses on every interface.

RFC 2553 says the following about AI_ADDRCONFIG:

      - The AI_ADDRCONFIG flag specifies that a query for AAAA records
        should occur only if the node has at least one IPv6 source
        address configured and a query for A records should occur only
        if the node has at least one IPv4 source address configured.

While it does not mention link-local (or loopback) addresses specifically, it is obvious that its intended use is to allow an application to avoid getting addresses returned that cannot be used for communication.  In that regard link-local addresses are very similar to loopback addresses, in the sense that they're automatically configured on all hosts regardless of any external connectivity, and can not be used when communicating with the outside world.  And, as it happens, loopback addresses are already disregarded by getaddrinfo() for AI_ADDRCONFIG (even for IPv4 loopback addresses).  This is done in in sysdeps/unix/sysv/linux/check_pf.cl line 179, in make_request().

For what it's worth, both Microsoft Windows and Mac OS X appear to disregard link-local IPv6 addresses for AI_ADDRCONFIG purposes, with no apparent ill effects.
Comment 1 Tore Anderson 2011-02-11 15:53:59 UTC
Created attachment 5236 [details]
Patch to make getaddrinfo() ignore link-local addresses for AI_ADDRCONFIG

Here's the patch - it should be really obvious. I've tested that it works fine.

Tore
Comment 2 rick jones 2011-07-06 20:29:54 UTC
(In reply to comment #0)
> While it does not mention link-local (or loopback) addresses specifically, it
> is obvious that its intended use is to allow an application to avoid getting
> addresses returned that cannot be used for communication.  In that regard
> link-local addresses are very similar to loopback addresses, in the sense that
> they're automatically configured on all hosts regardless of any external
> connectivity, and can not be used when communicating with the outside world. 

How does getaddrinfo() know that the name to be looked-up isn't supposed to map to a link-local IPv6 address that will be reachable?

Interestingly enough, I seem to have the opposite problem - it appears that for 2.12, as shipped with Maverick anyway, that the presence of a link-local IPv6 address is not sufficient to allow "::1" (or other IPv6 addresses, for example " fe80::21c:c4ff:fe47:d3f9" which happens to be the link-local IPv6 address on my system's eth0 interface) to be resolved when AI_ADDRCONFIG is set - which I consider a bug :)

> For what it's worth, both Microsoft Windows and Mac OS X appear to disregard
> link-local IPv6 addresses for AI_ADDRCONFIG purposes, with no apparent ill
> effects.

Perhaps no-one is attempting to actually do link-local IPv6 communication yet.
Comment 3 Tore Anderson 2011-07-07 09:19:32 UTC
(In reply to comment #2)

> How does getaddrinfo() know that the name to be looked-up isn't supposed to map
> to a link-local IPv6 address that will be reachable?

A link-local address on its own is not very useful. You'll need an interface ID
to make it unambigous, and as far as I know, DNS/getaddrinfo() cannot supply this
information. Try for example adding "mycomputer IN AAAA fe80::21c:c4ff:fe47:d3f9"
in DNS, and then do "ping6 mycomputer" - it'll fail with «Invalid argument».

> Interestingly enough, I seem to have the opposite problem - it appears that for
> 2.12, as shipped with Maverick anyway, that the presence of a link-local IPv6
> address is not sufficient to allow "::1" (or other IPv6 addresses, for example
> " fe80::21c:c4ff:fe47:d3f9" which happens to be the link-local IPv6 address on
> my system's eth0 interface) to be resolved when AI_ADDRCONFIG is set - which I
> consider a bug :)

You could make the exact same argument about loopback addresses as well:

«How does getaddrinfo() know that the name to be looked-up isn't supposed to
map to a loopback IPv6 address that will be reachable?»

However, the spec for AI_ADDRCONFIG is very clear that loopback addresses should
be ignored. So, for example, if you're looking up "localhost" on a machine that
has no IPv4 network connectivity using AI_ADDRCONFIG, it might make sense to get
back 127.0.0.1, but the spec is very clear: you should not.

From this it is easy to infer the true purpose of AI_ADDRCONFIG - avoid looking
up addresses that it is highly unlikely that you can make use of. Loopbacks and
link-locals are corner cases since they're always present regardless of external
connectivity, and link-locals is even more of a corner-case than loopbacks as
a name cannot resolve to a usable link-local address (with an interface id).
Therefore link-locals should be ignored when determining external connectivity
by AI_ADDRCONFIG, just like loopbacks are.

That said, I cannot reproduce the behaviour you're seeing on my Fedora 15
workstation:

$ ip -6 address list ; ./gai-test -ac localhost
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
    inet6 fe80::21d:60ff:fe48:f59e/64 scope link 
       valid_lft forever preferred_lft forever
[         0us] -ac seen, using AI_ADDRCONFIG from now on

[         0us] begin gai_and_connect(localhost)
[+      560us] getaddinfo(localhost) done
[+       15us] dest = ::1 (AF_INET6)
[+        8us] about to connect()
[+       61us] connect() suceeds
[+       39us] dest = 127.0.0.1 (AF_INET)
[+        7us] about to connect()
[+       49us] connect() suceeds
$ 

If I remove the link-local address, however, the ::1 isn't returned, as
expected:

$ sudo ip -6 address flush dev eth0; ip -6 address list ; ./gai-test -ac localhost
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
[         0us] -ac seen, using AI_ADDRCONFIG from now on

[         0us] begin gai_and_connect(localhost)
[+      509us] getaddinfo(localhost) done
[+       13us] dest = 127.0.0.1 (AF_INET)
[+        8us] about to connect()
[+       57us] connect() suceeds
[+       40us] dest = 127.0.0.1 (AF_INET)
[+        6us] about to connect()
[+       39us] connect() suceeds
$ 

> > For what it's worth, both Microsoft Windows and Mac OS X appear to disregard
> > link-local IPv6 addresses for AI_ADDRCONFIG purposes, with no apparent ill
> > effects.
> 
> Perhaps no-one is attempting to actually do link-local IPv6 communication yet.

Link-local communication is absolutely essential to the IPv6 protocol, without
it Neighbour Discovery, Router Advertisements/Solicitations, and Duplicate
Address Detection cannot work. So pretty much everyone is doing link-local
communication, but it's used on a lower level than getaddrinfo().

Apple is also using link-local addressing for other stuff (i.e. management of
their Airport/TimeCapsule wireless routers), as far as I know, but getaddrinfo()
isn't involved there either.

Tore
Comment 4 rick jones 2011-07-07 17:24:23 UTC
> From this it is easy to infer the true purpose of AI_ADDRCONFIG - avoid looking
> up addresses that it is highly unlikely that you can make use of. Loopbacks and
> link-locals are corner cases since they're always present regardless of
> external
> connectivity, and link-locals is even more of a corner-case than loopbacks as
> a name cannot resolve to a usable link-local address (with an interface id).

Isn't that why there is the "%ifname" convention for the "name" one is passing to getaddrinfo()?  So it can fill-in the desired/required scope?  I have distinct recollections of using that frequently with netperf when running IPv6 tests using link-local addressing (though with IPv6 addrs as strings passing to getaddrinfo()).


> That said, I cannot reproduce the behaviour you're seeing on my Fedora 15
> workstation:
> 
> $ ip -6 address list ; ./gai-test -ac localhost
> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 
>     inet6 ::1/128 scope host 
>        valid_lft forever preferred_lft forever
> 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
>     inet6 fe80::21d:60ff:fe48:f59e/64 scope link 
>        valid_lft forever preferred_lft forever
> [         0us] -ac seen, using AI_ADDRCONFIG from now on
> 
> [         0us] begin gai_and_connect(localhost)
> [+      560us] getaddinfo(localhost) done
> [+       15us] dest = ::1 (AF_INET6)
> [+        8us] about to connect()
> [+       61us] connect() suceeds
> [+       39us] dest = 127.0.0.1 (AF_INET)
> [+        7us] about to connect()
> [+       49us] connect() suceeds
> $ 
> 
> If I remove the link-local address, however, the ::1 isn't returned, as
> expected:
> 
> $ sudo ip -6 address flush dev eth0; ip -6 address list ; ./gai-test -ac
> localhost
> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 
>     inet6 ::1/128 scope host 
>        valid_lft forever preferred_lft forever
> [         0us] -ac seen, using AI_ADDRCONFIG from now on
> 
> [         0us] begin gai_and_connect(localhost)
> [+      509us] getaddinfo(localhost) done
> [+       13us] dest = 127.0.0.1 (AF_INET)
> [+        8us] about to connect()
> [+       57us] connect() suceeds
> [+       40us] dest = 127.0.0.1 (AF_INET)
> [+        6us] about to connect()
> [+       39us] connect() suceeds
> $ 

My situation under Maverick does seem odd.  Running my test program under gdb I single-stepped through make_request() and while the "ifam" showed a family of 10 (PF_INET6) on some of the passes through the loop looking at the netlink returns, it never hit the path that set seen_ipv6 to true.  I pulled the eglibc sources and lifted __check_pf() and make_request from them, and after a modicum of massaging to get them to compile on their own, a test program calling those *did* set seen_ipv6.
Comment 5 rick jones 2011-07-07 19:37:40 UTC
(In reply to comment #0)

> For what it's worth, both Microsoft Windows and Mac OS X appear to disregard
> link-local IPv6 addresses for AI_ADDRCONFIG purposes, with no apparent ill
> effects.

Perhaps there is different behaviour based on OS X version?

I put my test program into the hands of some OS X users, and on their systems where there were only IPv4 and link-scope IPv6 addresses, the setting of AI_ADDRCONFIG did not preclude resolving the likes of "::1" or a link-scope IP address (so presumably "::1" was not special cased).  One of them is known to be running 10.6.8, the other I don't have their revision information.

I can provide both the test program and output of it and ifconfig and such if desired, but won't clutter the bugzilla with it unless asked.
Comment 6 Tore Anderson 2011-07-08 15:16:58 UTC
(In reply to comment #4)
> > From this it is easy to infer the true purpose of AI_ADDRCONFIG - avoid looking
> > up addresses that it is highly unlikely that you can make use of. Loopbacks and
> > link-locals are corner cases since they're always present regardless of
> > external
> > connectivity, and link-locals is even more of a corner-case than loopbacks as
> > a name cannot resolve to a usable link-local address (with an interface id).
> 
> Isn't that why there is the "%ifname" convention for the "name" one is passing
> to getaddrinfo()?  So it can fill-in the desired/required scope?  I have
> distinct recollections of using that frequently with netperf when running IPv6
> tests using link-local addressing (though with IPv6 addrs as strings passing to
> getaddrinfo()).

The %ifname convention has nothing to do with getaddrinfo(), it's interpreted by the application. When passing it to getaddrinfo() you'll get this result:

tore@wrath:~$ ./gai-test "sourceware.org%eth0"
[         0us] begin gai_and_connect(sourceware.org%eth0)
[+    99007us] getaddrinfo(sourceware.org%eth0) failed: Name or service not known
[+       18us] socket() failed

Which, on the wire, shows up as:

17:09:57.165199 IP 192.168.1.42.42753 > 195.159.0.100.domain: 26635+ A? sourceware.org%eth0. (37)
17:09:57.165215 IP 192.168.1.42.42753 > 195.159.0.100.domain: 30258+ AAAA? sourceware.org%eth0. (37)
17:09:57.185490 IP 195.159.0.100.domain > 192.168.1.42.42753: 26635 NXDomain 0/1/0 (112)
17:09:57.185929 IP 195.159.0.100.domain > 192.168.1.42.42753: 30258 NXDomain 0/1/0 (112)                                                                           

> My situation under Maverick does seem odd.  Running my test program under gdb I
> single-stepped through make_request() and while the "ifam" showed a family of
> 10 (PF_INET6) on some of the passes through the loop looking at the netlink
> returns, it never hit the path that set seen_ipv6 to true.  I pulled the eglibc
> sources and lifted __check_pf() and make_request from them, and after a modicum
> of massaging to get them to compile on their own, a test program calling those
> *did* set seen_ipv6.

Probably the Ubuntu guys have done some patching on their own to get around the problem with unwanted AAAA lookups hitting broken DNS server.

(In reply to comment #5)
> Perhaps there is different behaviour based on OS X version?
> 
> I put my test program into the hands of some OS X users, and on their systems
> where there were only IPv4 and link-scope IPv6 addresses, the setting of
> AI_ADDRCONFIG did not preclude resolving the likes of "::1" or a link-scope IP
> address (so presumably "::1" was not special cased).  One of them is known to
> be running 10.6.8, the other I don't have their revision information.

I don't have a Mac OS X machine handy right now, but it could be that they only apply AI_ADDRCONFIG logic when looking up names in DNS, and that any address found in another name service (such as /etc/hosts) will be returned by getaddrinfo() regardless of AI_ADDRCONFIG. I know for certain that AI_ADDRCONFIG will suppress AAAA DNS lookups on Mac OS X if there's only loopback and link-locals present, though.

Tore
Comment 7 rick jones 2011-07-08 16:47:50 UTC
(In reply to comment #6)

> The %ifname convention has nothing to do with getaddrinfo(), it's interpreted
> by the application. When passing it to getaddrinfo() you'll get this result:

When did that change, or is that only for IP addresses passed-in to the call?  I was running netperf tests over IPv6 passing-in the likes of "fe80::207:43ff:fe05:5904%eth2" and I know that all netperf was doing was passing that to getaddrinfo() and getting scope that way (rhel5.mumble, sles10, sles11 sp1, lenny, and solaris 10)
Comment 8 Tore Anderson 2011-07-09 11:28:55 UTC
(In reply to comment #7)
> (In reply to comment #6)
> 
> > The %ifname convention has nothing to do with getaddrinfo(), it's interpreted
> > by the application. When passing it to getaddrinfo() you'll get this result:
> 
> When did that change, or is that only for IP addresses passed-in to the call? 
> I was running netperf tests over IPv6 passing-in the likes of
> "fe80::207:43ff:fe05:5904%eth2" and I know that all netperf was doing was
> passing that to getaddrinfo() and getting scope that way (rhel5.mumble, sles10,
> sles11 sp1, lenny, and solaris 10)

I don't know. It's probably only if you're passing it IP literals - which doesn't appear to generate any (reverse) DNS lookups in any case.

If I'm not mistaken this is not really relevant to the problem that I want to see solved, i.e., making AI_ADDRCONFIG suppress unnecessary AAAA DNS lookups from hosts that have no IPv6 connectivity. There are no shortage of DNS servers out there that do not understand AAAA lookups, which in turn causes getaddrinfo() to stall until a timeout has been reached, leading to a abysmal end-user experience compared to the other major operating systems. The number of comments and duplicates for bug reports such as https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/417757 clearly shows that this is a major problem.

Tore
Comment 9 Pavel Šimerda 2012-07-27 13:53:27 UTC
> The %ifname convention has nothing to do with getaddrinfo(), it's interpreted
> by the application.

This is incorrect. I'm using %-addresses daily with OpenSSH and it passes the whole address to getaddrinfo()

That's why there is scope_id in struct sockaddr_in6.

  struct sockaddr_in6 {
    sa_family_t     sin6_family;   /* AF_INET6 */
    in_port_t       sin6_port;     /* port number */
    uint32_t        sin6_flowinfo; /* IPv6 flow information */
    struct in6_addr sin6_addr;     /* IPv6 address */
    uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
  };

Interface name behind % must be translated to the scope_id.

> When passing it to getaddrinfo() you'll get this result:
> 
> tore@wrath:~$ ./gai-test "sourceware.org%eth0"
> [         0us] begin gai_and_connect(sourceware.org%eth0)
> [+    99007us] getaddrinfo(sourceware.org%eth0) failed: Name or service not
> known

This is a bug and it's caused exactly by the patch from this bugreport and by making AI_ADDRCONFIG default in getaddrinfo.

It breaks IPv6 node-local and link-local networking:

https://bugzilla.redhat.com/show_bug.cgi?id=808147

It breaks IPv4 node-local networking too (and probably also link-local if anybody uses that):

https://bugzilla.redhat.com/show_bug.cgi?id=721350

You can't just kill localhost and link-local networking as if nothing happens.
Comment 10 Pavel Šimerda 2012-07-27 14:01:21 UTC
Testing environment:

glibc-2.15-51.fc17.x86_64

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:8e:93:da brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:eb:e9:fb brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:feeb:e9fb/64 scope link 
       valid_lft forever preferred_lft forever

IPv4 literal localhost test:

# ./gai 127.0.0.1
getaddrinfo 1( 127.0.0.1, NULL, ... )
     ai_family=2, ai_socktype=1, ai_flags=32
getaddrinfo 1 => EAI_NONAME(-2)
error

IPv6 literal localhost test:

# ./gai ::1
getaddrinfo 1( 127.0.0.1, NULL, ... )
     ai_family=2, ai_socktype=1, ai_flags=32
getaddrinfo 1 => EAI_NONAME(-2)
error

IPv4 DNS localhost test:

# ./gai localhost4
getaddrinfo 1( 127.0.0.1, NULL, ... )
     ai_family=2, ai_socktype=1, ai_flags=32
getaddrinfo 1 => EAI_NONAME(-2)
error

IPv6 DNS localhost test:

# ./gai localhost6
getaddrinfo 1( 127.0.0.1, NULL, ... )
     ai_family=2, ai_socktype=1, ai_flags=32
getaddrinfo 1 => EAI_NONAME(-2)
error

Neutral DNS test:

# ./gai localhost
getaddrinfo 1( 127.0.0.1, NULL, ... )
     ai_family=2, ai_socktype=1, ai_flags=32
getaddrinfo 1 => EAI_NONAME(-2)
error

A quick test to see if it works normally *with* a global IPv4 address:

# ./gai localhost
getaddrinfo 1( 127.0.0.1, NULL, ... )
     ai_family=2, ai_socktype=1, ai_flags=32
getaddrinfo 1 => Success(0)
family: AF_INET
sin_addr 0x0100007f sin_port 0x0000

Note: This only happens with this version if my gai.c uses AI_ADDRCONFIG.
Comment 11 Pavel Šimerda 2012-07-27 14:07:28 UTC
> This is a bug and it's caused exactly by the patch from this bugreport and by
> making AI_ADDRCONFIG default in getaddrinfo.

Forget this please and read it as:

This problem is caused by using AI_ADDRCONFIG by applications and by GLIBC's bug that prevents even IP literals from working correctly. Please default to AI_ADDRCONFIG. Please re-think the whole AI_ADDRCONFIG idea as it seems to be an ugly and fragile hack.
Comment 12 Pavel Šimerda 2012-07-27 14:10:53 UTC
See also bug 12398.
Comment 13 Pavel Šimerda 2012-07-29 08:30:36 UTC
I have done some new tests (still with glibc-2.15-51.fc17.x86_64):

AF_UNSPEC + AI_ADDRCONFIG: In this case AI_ADDRCONFIG doesn't seem to work at all
(read doesn't remove *any* addresses). It is useless. It's against the manpage
and RFCThis is the default (plus
the also useless AI_V4MAPPED as in bug 14415).

AF_INET + AI_ADDRCONFIG: In this case AI_ADDRCONFIG does exactly what's written down in the RFC 3493 (and almost what is in the manpage). That means it breaks
'localhost', 'localhost4' and '127.0.0.1' and it would break any other address assigned to the loopback interface. I haven't tested IPv6 link-local addresses.

AF_INET6 + AI_ADDRCONFIG: Same as above. Breaks 'localhost', 'localhost6' and '::1'. With some new addition, it also breaks link-local addresses
(e.g. fe80::21f:3cff:fe1b:9e5e%wlan0) and would also break names pointing to
them if getaddrinfo was implemented in nsswitch (bug 14413).

AF_INET6 + AI_ADDRCONFIG + AI_V4MAPPED (+ AI_ALL): Same behavior as above,
broken localhost for both IPv6 and mapped IPv4.

From http://tools.ietf.org/html/rfc3493#section-6.1

  If the AI_ADDRCONFIG flag is specified, IPv4 addresses shall be returned only
  if an IPv4 address is configured on the local system, and IPv6 addresses shall
  be returned only if an IPv6 address is configured on the local system.

This is a stupid way and cannot work. Even the authors new it, that's why there's something added at the and of the paragraph.

  The loopback address is not considered for this case as valid as a configured
  address.

But this assumes you never need to use the usual addresses on the loopback (which is wrong) and that you never add more addresses to the loopback e.g.
for testing (which is also wrong). And it also assumes you never have
non-global addresses on other interfaces (which is also wrong, you
have IPv6 link-local and you could have IPv4 link-local).

This option is probably meant to guess whether we can sendto() data to this address (for connect() it is not necessary as connect() immediately returns
failure for nonroutable addresses). But I can't think of *any* reason
to use this hack when it's so easy to ask the system if the address is actually
routable.

$ ip route get 2001:db8:1:1::1
$

You see, this address is *not* routable. But even if you try to fool it:

# ip route add default via fe80::23:45ff:fe67:8901 dev eth0

$ ip route get 2001:db8:1:1::1
2001:db8:1:1::1 from :: via fe80::23:45ff:fe67:8901 dev eth0  src fe80::12:34ff:fe56:7890  metric 0
    cache

You can still easily detect the problem as the dst address is global and src
address is link-local. So there's actually no guessing needed when the information is right at your hand.
Comment 14 Pavel Šimerda 2012-07-29 10:41:38 UTC
Just a correction of AF_UNSPEC, another test says AF_UNSPEC with AI_ADDRCONFIG works (or rather, fails) in the same way as specific families. This behavior probably depends on some other circumstances.

The test where AF_UNSPEC does something and breaks IPv6 localhost is made with IPv4 connectivity and with IPv6 link-local addresses (connected to an IPv4-only network).

The test where AF_UNSPEC does nothing is made without connectivity and without
link. In the current kernel (I think it used to be different), disconnected
interfaces that are UP don't get IPv6 link-local addresses. So this test was
also without IPv6 link-local.

These behavioral changes are weird and should be perfectly documented in the manpage if they are to be present at all.
Comment 15 Tore Anderson 2012-07-31 12:19:37 UTC
(In reply to comment #13)

> This option is probably meant to guess whether we can sendto() data to this
> address (for connect() it is not necessary as connect() immediately returns
> failure for nonroutable addresses). But I can't think of *any* reason
> to use this hack when it's so easy to ask the system if the address is
> actually routable.

I am assuming that by «this option» you mean AI_ADDRCONFIG?

If so, I disagree. The way I see it, the primary reason for using AI_ADDRCONFIG is to avoid making lookups from which any results cannot be used. In other words, if there is no IPv4 connectivity, avoid making "IN A" lookups. If there is no IPv6 connectivity, avoid making "IN AAAA" lookups. Making useless lookups consume bandwidth/battery, and in the case of broken DNS server implementations that choke on the useless lookup type (e.g. IPv4-only home gateways that don't respond to IN AAAA lookups), they may even cause getaddrinfo() stall for a long time, waiting for the IN AAAA lookup to time out, before returning to the caller.

> $ ip route get 2001:db8:1:1::1
> $
> 
> You see, this address is *not* routable. But even if you try to fool it:
> 
> # ip route add default via fe80::23:45ff:fe67:8901 dev eth0
> 
> $ ip route get 2001:db8:1:1::1
> 2001:db8:1:1::1 from :: via fe80::23:45ff:fe67:8901 dev eth0  src
> fe80::12:34ff:fe56:7890  metric 0
>     cache
> 
> You can still easily detect the problem as the dst address is global and src
> address is link-local. So there's actually no guessing needed when the
> information is right at your hand.

I don't think AI_ADDRCONFIG makes much sense when passing literal IP addresses to getaddrinfo(). It could probably be ignored in that case, I think. However, when passing hostnames to getaddrinfo() you can't do a routing test before doing the lookup(s), because the IP addresses aren't known at that point.

That said, it is possible to connect to a global address from a link-local source address in one corner case. For example, say that your default router in the example above (fe80::23:45ff:fe67:8901) has 2001:db8:1:1::1 configured on its loopback interface. In that case, you could communicate with 2001:db8:1:1::1 from fe80::12:34ff:fe56:7890 without any problems.

Tore
Comment 16 Pavel Šimerda 2012-07-31 13:16:43 UTC
> I am assuming that by «this option» you mean AI_ADDRCONFIG?

Sure.

> If so, I disagree. 

Disagree with what exactly?

The way I see it, the primary reason for using AI_ADDRCONFIG
is to avoid making lookups from which any results cannot be used.

And it fails terribly, giving both false positives and false negatives
and even breaking literal IP addresses.

> words, if there is no IPv4 connectivity, avoid making "IN A" lookups.

This is exactly what AI_ADDRCONFIG doesn't do. It breaks various non-DNS
cases.

> If there is no IPv6 connectivity, avoid making "IN AAAA" lookups.

Same as above.

> Making useless lookups consume bandwidth/battery, and in the case of broken
> DNS server implementations

*None* of my objections was DNS-related. All were about either literal addresses, /etc/hosts names or possible link-local NSS plugins.

That said, if bug 14413 was resolved, you could do this sort of black magic
entirely in nss-dns.so. Without it, it's really hard to implement a
working implementation of AI_ADDRCONFIG.

Maybe you could just specialcase DNS, it's a hack but certainly not worse
than what we're doing now.

> I don't think AI_ADDRCONFIG makes much sense when passing literal IP addresses
> to getaddrinfo(). It could probably be ignored in that case, I think. However,
> when passing hostnames to getaddrinfo() you can't do a routing test before
> doing the lookup(s), because the IP addresses aren't known at that point.

You unfortunately can't. But I couldn't know what is AI_ADDRCONFIG for if it's
not documented in the manual page. There's not a single word about DNS queries.

> That said, it is possible to connect to a global address from a link-local
> source address in one corner case. For example, say that your default router in
> the example above (fe80::23:45ff:fe67:8901) has 2001:db8:1:1::1 configured on
> its loopback interface. In that case, you could communicate with
> 2001:db8:1:1::1 from fe80::12:34ff:fe56:7890 without any problems.

Correct. You'd have to be a little bit more careful in assumptions even than
I am. But this also makes a cornercase for the current behavior. But even then I have already spent like three days just testing this.
Comment 17 Pavel Šimerda 2012-07-31 13:33:57 UTC
From downstream bug report:

https://bugzilla.redhat.com/show_bug.cgi?id=808147

Pavel Simerda 2012-07-31 09:27:53 EDT

> Tore Anderson 2012-07-31 09:02:12 EDT 
> I have thought a bit on how to handle the problem in a better way, one that
> will not cancel out the primary function of AI_ADDRCONFIG (namely to
> suppress pointless and potentially harmful DNS lookups). My suggestion is to
> make getaddrinfo() ignore AI_ADDRCONFIG in the following circumstances:
> 
> 1) when looking up literal IP addresses, and
> 2) when returning answers from /etc/hosts

3) when returning answers from any future nss plugin that may return link-local
addresses (and node-local, if it's that case).

So this is an unlimited number of special cases. Did you try to think it the other way round? Specialcasing just DNS for the beginning? As far as I
understand, this is meant as an 'ugly hack' to work around particular DNS
misconfigurations and maybe an optimization technique for DNS.

This would work as a quick fix. The proper way would be to fix:

http://sourceware.org/bugzilla/show_bug.cgi?id=14413

Then every plugin would be able to provide full getaddrinfo() semantics including
AI_ADDRCONFIG and this could be done by nss-dns.
Comment 18 Tore Anderson 2012-07-31 13:59:09 UTC
(In reply to comment #16)

> > If so, I disagree. 
> 
> Disagree with what exactly?

With the entire statement. To break it down a bit:

> This option is probably meant to guess whether we can sendto() data to this
> address

I disagree, I believe the purpose of AI_ADDRCONFIG was primarily to suppress IN A and IN AAAA DNS lookups in the case when they are likely to be not useful. Quoting from RFC 2553:

      - The AI_ADDRCONFIG flag specifies that a query for AAAA records
        should occur only if the node has at least one IPv6 source
        address configured and a query for A records should occur only
        if the node has at least one IPv4 source address configured.

> (for connect() it is not necessary as connect() immediately returns
> failure for nonroutable addresses)

I disagree again. One of the applications that will benefit the most from AI_ADDRCONFIG is a web browser, which can use it to avoid generating IN AAAA DNS queries when looking up hostnames, when running on an IPv4-only machine. (Or avoid IN A lookups when running on an IPv6-only machine.) As you probably are well aware, a web browser will be a heavy user of connect(), while rarely if ever use sendto().

> But I can't think of *any* reason to use this hack

Disagree, as above, a web browser has a very good reason to use AI_ADDRCONFIG.

> when it's so easy to ask the system if the address is actually routable.

Again, disagreed. AI_ADDRCONFIG is useful when looking up names to IP addresses. using DNS. Before knowing the IP address, it is impossible to ask the system whether or not it is usable.

> The way I see it, the primary reason for using AI_ADDRCONFIG
> is to avoid making lookups from which any results cannot be used.
> 
> And it fails terribly, giving both false positives and false negatives
> and even breaking literal IP addresses.

As far as I can tell, AI_ADDRCONFIG works perfectly for (forward) DNS lookups, which I belive is what it's primarily meant to do. If I remove all IPv6 addresses except ::1 and look up a name, no IN AAAA queries are sent. Or if I remove all IPv4 addresses except 127.0.0.1, no IN A queries are sent.

RFC 3493 changed the definition of getaddrinfo() from the original DNS-only definition in RFC 2553 to a more general one that resembles the one that is included in glibc. However, the example described still revolves around forward DNS IN A/AAAA lookups so I'm not quite sure if the change was a sensible one. There was certainly not much operational experience with it at that point in time.

> > words, if there is no IPv4 connectivity, avoid making "IN A" lookups.
> 
> This is exactly what AI_ADDRCONFIG doesn't do.

On my system, it does exactly this on my system. I don't see any IN A queries sent to the DNS server when I only have 127.0.0.1 configured on my system, only IN AAAA.

> It breaks various non-DNS cases.

With that I agree completely. I don't see any good reason to apply AI_ADDRCONFIG logic in other cases than when using DNS. For what it's worth, I believe that Mac OS X still implements getaddrinfo() only when doing forward DNS IN A/AAAA lookups (in other words the original RFC 2553 specification). I can try to get that confirmed if you're interested.

> *None* of my objections was DNS-related. All were about either literal
> addresses, /etc/hosts names or possible link-local NSS plugins.

Ok - I think we're kind of in agreement, then - perhaps talking about two different use cases, really. I see AI_ADDRCONFIG as only useful for DNS ("forward" DNS, i.e. IN A/IN AAAA). However, in that case, it is also *very* useful. However, for it to be useful in the IPv4-only host case (which is presently the most important one, given the amount of defective resolvers embedded in IPv4-only home gateways that choke on IN AAAA queries), you'll also need to disregard the automatically configured link-local IPv6 addresses on the host when deciding whether or not to suppress the IN AAAA query or not, otherwise you'll pretty much never do it.

> That said, if bug 14413 was resolved, you could do this sort of black magic
> entirely in nss-dns.so. Without it, it's really hard to implement a
> working implementation of AI_ADDRCONFIG.
> 
> Maybe you could just specialcase DNS, it's a hack but certainly not worse
> than what we're doing now.

A hack, perhaps...or simply a return to the good old original RFC 2553 specification.  ;-)

Tore
Comment 19 Pavel Šimerda 2012-07-31 15:01:01 UTC
> > > words, if there is no IPv4 connectivity, avoid making "IN A" lookups.
> > 
> > This is exactly what AI_ADDRCONFIG doesn't do.

On my system it edits the list of addresses *after* doing any sort of parsing
and optional lookup. And AF_UNSPEC with AI_ADDRCONFIG without IPv4 and IPv6
connectivity does not do the same thing to IPv6 addresses as with IPv6
connectivity.

I can test this for you when it's fixed. But also *please* document the exact expected behavior and its purpose in the manpage.

> On my system, it does exactly this on my system. I don't see any IN A queries
> sent to the DNS server when I only have 127.0.0.1 configured on my system, only
> IN AAAA.

If it was only doing this, I would have *no* objections.

> > It breaks various non-DNS cases.
> 
> With that I agree completely. I don't see any good reason to apply
> AI_ADDRCONFIG logic in other cases than when using DNS. For what it's worth, I
> believe that Mac OS X still implements getaddrinfo() only when doing forward
> DNS IN A/AAAA lookups (in other words the original RFC 2553 specification). I
> can try to get that confirmed if you're interested.

Then we have a way out. It should be done only for DNS (or for a list of
global-address-only methods that would only include DNS for the beginning).

> > *None* of my objections was DNS-related. All were about either literal
> > addresses, /etc/hosts names or possible link-local NSS plugins.
> 
> Ok - I think we're kind of in agreement, then - perhaps talking about two
> different use cases, really. I see AI_ADDRCONFIG as only useful for DNS
> ("forward" DNS, i.e. IN A/IN AAAA). However, in that case, it is also *very*
> useful. However, for it to be useful in the IPv4-only host case (which is
> presently the most important one, given the amount of defective resolvers
> embedded in IPv4-only home gateways that choke on IN AAAA queries), you'll also
> need to disregard the automatically configured link-local IPv6 addresses on the
> host when deciding whether or not to suppress the IN AAAA query or not,
> otherwise you'll pretty much never do it.

Then of course yes. You can guess that contacting DNS-resulted IPv6 addresses
without a global/site IPv6 address is not necessary.

> > That said, if bug 14413 was resolved, you could do this sort of black magic
> > entirely in nss-dns.so. Without it, it's really hard to implement a
> > working implementation of AI_ADDRCONFIG.
> > 
> > Maybe you could just specialcase DNS, it's a hack but certainly not worse
> > than what we're doing now.
> 
> A hack, perhaps...or simply a return to the good old original RFC 2553
> specification.  ;-)

It specifies the getaddrinfo(), that's the bug 14413 I mentioned above. If you strictly follow the spec, you're afaik not ignoring link-local. In this case,
the specifications deserves a fix too.

I'm already trying to do this with RDNSS:

See http://tools.ietf.org/html/draft-gont-6man-slaac-dns-config-issues-00

But, this is orthogonal to the solution of the current problem.
Comment 20 Pavel Šimerda 2012-07-31 15:51:21 UTC
Ah, now I see what you mean by good old RFC 2553...

You're cheating, you cited from an obsolete RFC:

  The AI_ADDRCONFIG flag specifies that a query for AAAA records
  should occur only if the node has at least one IPv6 source
  address configured and a query for A records should occur only
  if the node has at least one IPv4 source address configured.

The current RFC reads:

  If the AI_ADDRCONFIG flag is specified, IPv4 addresses shall be
  returned only if an IPv4 address is configured on the local system,
  and IPv6 addresses shall be returned only if an IPv6 address is
  configured on the local system.  The loopback address is not
  considered for this case as valid as a configured address.

    For example, when using the DNS, a query for AAAA records should
    occur only if the node has at least one IPv6 address configured
    (other than IPv6 loopback) and a query for A records should occur
    only if the node has at least one IPv4 address configured (other
    than the IPv4 loopback).

DNS is now only as an example. This part of the new RFC is *wrong* and we should
not implement it. The specification should be fixed then.
Comment 21 Tore Anderson 2012-07-31 18:34:09 UTC
(In reply to comment #19)

> On my system it edits the list of addresses *after* doing any sort of parsing
> and optional lookup. And AF_UNSPEC with AI_ADDRCONFIG without IPv4 and IPv6
> connectivity does not do the same thing to IPv6 addresses as with IPv6
> connectivity.

Very strange! If I purge my system of IPv6 addresses (ignoring the ::1 loopback address and link-locals), resolving addresses using my getaddrinfo() test program available at http://fud.no/gai.c (use the "-ac" parameter to make it use AI_ADDRCONFIG), causes IN AAAA lookups to be suppressed. Looking at the DNS traffic on the wire, only IN A lookups are seen. The same thing happens when using Mozilla Firefox, which is also using AI_ADDRCONFIG for all other names than "localhost" (plus some variants).

I'm running Fedora 17 with a completely stock glibc-2.15-51.fc17.x86_64.rpm.

However, during testing now I did notice that when I purge my system of IPv4 addresses, leaving only the 127.0.0.1 loopback one, IN A lookups are *not* suppressed when AI_ADDRCONFIG is in use. That's certainly a (different) bug.

(In reply to comment #20)
> Ah, now I see what you mean by good old RFC 2553...
> 
> You're cheating, you cited from an obsolete RFC:

Excuse me?

You were saying that AI_ADDRCONFIG was a «hack» that had no useful purpose, at least that is how I understood you. By showing you to the original definition of it, I wanted to show you that it was a very good reason why it was invented in the first place, that there actually was (and still is) a problem that it attempted to solve.

Furthermore, I never hid the fact that RFC 2553 isn't the current one. I pointed that out explicitly, in fact, by saying «RFC 3493 changed the definition of getaddrinfo() from the original DNS-only definition in RFC 2553 [...]».

So I strenuously object to your accusation of me «cheating» here.

> DNS is now only as an example. This part of the new RFC is *wrong* and we
> should not implement it. The specification should be fixed then.

Yes, I agree.

The language in RFC 3493 is more general, I'm guessing the reason for changing it in that way was so that it would stand up to changes in DNS itself (or even the possible replacement of DNS with something else entirely). For example, RFC 2553 refers specifically to AAAA records, but after its publication, the (now abandoned) A6 resource record had been defined in RFC 2874. A strict interpretation of RFC 2553 would not cause A6 queries to be suppressed in the same way as AAAA records, even though it is obivous that would have been the *intention* of RFC 2553. The RFC 3493 language, on the other hand, would apply equally to both AAAA and A6 records.

A strict interpretation of the new language in RFC 3493 does indeed mean that AI_ADDRCONFIG lookups for IP address literals should fail if there are no global address of the same address family configured on the node, even for link locals and loopback addresses. Also that the "localhost" host name only resolve to the address family from which there are global addresses, in the case of a single-stack host (with a dual-stacked loopback interface).

When considering the original specification of AI_ADDRCONFIG and trying to understand the problem it tried to solve, and what went with DNS between its publication and that of RFC 3493, I am of the opinion that the problematic side effects caused by the new language in RFC 3493 were entirely unintentional.

Tore
Comment 22 Tore Anderson 2012-07-31 19:00:30 UTC
(In reply to comment #21)

> However, during testing now I did notice that when I purge my system of IPv4
> addresses, leaving only the 127.0.0.1 loopback one, IN A lookups are *not*
> suppressed when AI_ADDRCONFIG is in use. That's certainly a (different) bug.

This needs more investigation. On my IPv6-only Ubuntu Precise machine, this works as expected, IN A lookups are suppressed if (and only if) AI_ADDRCONFIG is specified. On F17, it did. Will try more and report back.

Tore
Comment 23 Pavel Šimerda 2012-07-31 22:22:25 UTC
> However, during testing now I did notice that when I purge my system of IPv4
> addresses, leaving only the 127.0.0.1 loopback one, IN A lookups are *not*
> suppressed when AI_ADDRCONFIG is in use. That's certainly a (different) bug.

This is what I observed.

> Furthermore, I never hid the fact that RFC 2553 isn't the current one. I
> pointed that out explicitly, in fact, by saying «RFC 3493 changed the
> definition of getaddrinfo() from the original DNS-only definition in RFC 2553
> [...]».
> 
> So I strenuously object to your accusation of me «cheating» here.

Don't take things too serious.

> > DNS is now only as an example. This part of the new RFC is *wrong* and we
> > should not implement it. The specification should be fixed then.
> 
> Yes, I agree.
> 
> The language in RFC 3493 is more general, I'm guessing the reason for changing
> it in that way was so that it would stand up to changes in DNS itself (or even
> the possible replacement of DNS with something else entirely). For example, RFC
> 2553 refers specifically to AAAA records, but after its publication, the (now
> abandoned) A6 resource record had been defined in RFC 2874.

Yes, the new text misses a lot of important things.

> A strict interpretation of the new language in RFC 3493 does indeed mean that
> AI_ADDRCONFIG lookups for IP address literals should fail if there are no
> global address of the same address family configured on the node, even for link
> locals and loopback addresses. Also that the "localhost" host name only resolve
> to the address family from which there are global addresses,

This is sort of narrow minded. Probably the editors didn't have enough time to
think about it in real world.

> in the case of a single-stack host (with a dual-stacked loopback interface).

Maybe this is the problem. People are thinking about single stack and dual stack
hosts. But we actually don't have many single stack hosts. But we do have single
stack (or single version) networks.

> When considering the original specification of AI_ADDRCONFIG and trying to
> understand the problem it tried to solve, and what went with DNS between its
> publication and that of RFC 3493, I am of the opinion that the problematic side
> effects caused by the new language in RFC 3493 were entirely unintentional.

I'm sure about it. „Always assume good will“
Comment 24 Tore Anderson 2012-08-01 09:22:07 UTC
(In reply to comment #22)
> This needs more investigation. On my IPv6-only Ubuntu Precise machine, this
> works as expected, IN A lookups are suppressed if (and only if) AI_ADDRCONFIG
> is specified. On F17, it did. Will try more and report back.

Okay, this definitively works correctly in Ubuntu, and but not in Fedora. IN AAAA lookups are suppressed correctly from IPv4-only hosts in Fedora, though.

I submitted a new bug in the Fedora bug tracker about this:

https://bugzilla.redhat.com/show_bug.cgi?id=844921

Tore
Comment 25 Pavel Šimerda 2012-09-22 15:43:59 UTC
Created attachment 6648 [details]
a temporary fix to ignore the whole AI_ADDRCONFIG thing

Thanks.

Until this issue is resolved, I'm building GLIBC with this patch to avoid
problems with node-local and link-local networking.
Comment 26 Jackie Rosen 2014-02-16 18:23:35 UTC Comment hidden (spam)
Comment 27 Mathieu Parent 2016-03-17 13:59:53 UTC
Any news on this?