Linux: Prefer IPv4 over IPv6 in dual-stack environment (and prevent problems when only IPv4 exists)

Filed in Linux | Networking Leave a comment

On Linux, there is a feature of IPv6 stack on linux that allow IPv4 mapped as IPv6 address using IPv6 mapped IPv4 address (::ffff/96). This allow IPv6 capable application to use only single socket to accept or connect both IPv4 and IPv6.

By default, if you have both IPv4 and native IPv6. Linux system application (glibc) will use IPv6 by default if it exists because getaddrinfo() will return IPv6 first. This is controlled by glibc’s IPv6 address selection preference for getaddrinfo() in /etc/gai.conf. (Note: some application may have mechanism to decide which address to use other than what provide by getaddrinfo()).

/etc/gai.conf control address selection by label and preferences. This is some of it in Gentoo Linux

# Configuration for getaddrinfo(3).
# So far only configuration for the destination address sorting is needed.
# RFC 3484 governs the sorting. But the RFC also says that system
# administrators should be able to overwrite the defaults. This can be
# achieved here.
# All lines have an initial identifier specifying the option followed by
# up to two values. Information specified in this file replaces the
# default information. Complete absence of data of one kind causes the
# appropriate default information to be used. The supported commands include:
# reload
# If set to yes, each getaddrinfo(3) call will check whether this file
# changed and if necessary reload. This option should not really be
# used. There are possible runtime problems. The default is no.
# label
# Add another rule to the RFC 3484 label table. See section 2.1 in
# RFC 3484. The default is:
#label ::1/128 0
#label ::/0 1
#label 2002::/16 2
#label ::/96 3
#label ::ffff:0:0/96 4
#label fec0::/10 5
#label fc00::/7 6
#label 2001:0::/32 7
# This default differs from the tables given in RFC 3484 by handling
# (now obsolete) site-local IPv6 addresses and Unique Local Addresses.
# The reason for this difference is that these addresses are never
# NATed while IPv4 site-local addresses most probably are. Given
# the precedence of IPv6 over IPv4 (see below) on machines having only
# site-local IPv4 and IPv6 addresses a lookup for a global address would
# see the IPv6 be preferred. The result is a long delay because the
# site-local IPv6 addresses cannot be used while the IPv4 address is
# (at least for the foreseeable future) NATed. We also treat Teredo
# tunnels special.
# precedence
# Add another rule to the RFC 3484 precedence table. See section 2.1
# and 10.3 in RFC 3484. The default is:
#precedence ::1/128 50
#precedence ::/0 40
#precedence 2002::/16 30
#precedence ::/96 20
#precedence ::ffff:0:0/96 10
# For sites which prefer IPv4 connections change the last line to
#precedence ::ffff:0:0/96 100
# scopev4
# Add another rule to the RFC 3484 scope table for IPv4 addresses.
# The definitions in RFC 3484 are equivalent to:
#scopev4 ::ffff: 2
#scopev4 ::ffff: 2
#scopev4 ::ffff: 5
#scopev4 ::ffff: 5
#scopev4 ::ffff: 5
#scopev4 ::ffff: 14
# For sites which use site-local IPv4 addresses behind NAT there is
# the problem that even if IPv4 addresses are preferred they do not
# have the same scope and are therefore not sorted first. To change
# this use only these rules:
scopev4 ::ffff: 2
scopev4 ::ffff: 2
scopev4 ::ffff: 14

In some network this assumption may cause some problems because most IPv6 connection right now is not as good as IPv4 (more lattency, etc.). Sometimes even cause serious problems because IPv6 link-local (address begin with ff80::) are considered valid IPv6 address. Since current Linux distributions shipped with IPv6 enabled (ipv6 modules loaded or compiled in), some application failed to work if you do not have IPv6 connectivity or at least slow to connect because IPv6 was attempt first and it timed out.

As you see in the comment
If you want to prefer IPv4 over IPv6, just change precedence line of ::ffff:0:0/96 from 10 to 100
(If you do not have /etc/gai.conf at all, create it and add only that line, it should work).

But … Some other default configuration of glibc on some distros are still to have problems with /etc/hosts. Make sure you have this line in [b]/etc/host.conf[/b] if you want dual stack to work as it should. 

multi on

This line is require to allow glibc to return multiple addresses when using /etc/hosts, otherwise it will return only the first match. IPv4 or IPv6 only applications will have problems. Let’s say if you have ‘::1 localhost’ first and ‘ localhost’ second. ping localhost will failed if you not have ‘multi on’ in /etc/host.conf. (This will have performance problems if you have large /etc/hosts, but this is only way until glibc address hinting on /etc/hosts get implemented)

Moreover, many application are not aware that when both IPv4 and IPv6 exists, they don’t do multiple attempts (fallback) using different address family (when glibc return multiple address so). Some times they do but you need to explicitly config it to do that.

One application I found with this problem is [b]postfix[/b] it seems failed to fallback to another address family while multiple address family exists (IPv4 and IPv6). You need to add this line to postfix configuration ( or your mail may stuck in bounce queue because it cannot connect to another server via IPv6.


, , , , , ,