IPv6: define source IP of outgoing connections

If a Linux system has more than one (public, non-link-local) ipv6 address active, one may want to restrict outgoing connections to one specific IPv6 address.

Linux chooses the outgoing address for each new connection with the following schema:

  1. Prefer same address. (i.e. destination is local machine)
  2. Prefer appropriate scope. (i.e. smallest scope shared with the destination)
  3. Avoid deprecated addresses.
  4. Prefer home addresses.
  5. Prefer outgoing interface. (i.e. prefer an address on the interface we’re sending out of)
  6. Prefer matching label.
  7. Prefer public addresses.
  8. Use longest matching prefix.

If a unique selection isn’t possible after all, Linux chooses a method which results definetely in a single choice: the most recently added/activated IPv6 address will be chosen.

This method does not always result in the desired address, especially regarding SMTP connections to external hosts:

When a server with a single main ipv6 address and some additional addresses, e.g. for webhosting with different SSL certificates, the choice should always fall on that main address.

With the setting to mark an ipv6 address as “deprecated”, this is achieved. The linux kernel will still handle incoming requests / packets to such marked IPv6 addresses, but won’t choose this address when selecting a possible outgoing address (see item nr. 3 on the previous list).

In Linux activates this setting, when the “preferred_lft” (Preferred Lifetime) is set to “0” (zero).

Currently active addresses may be changed using “ip addr change”. The first (or main) IPv6 address has to be activated as before and may not be changed, otherwise outgoing connections are not possible any more!

root@terra:~# ip -6 addr show | grep "scope global"
    inet6 2a01:138:a008::3:1/64 scope global
    inet6 2a01:138:a008::3:2/64 scope global
root@terra:~# ip addr change 2a01:138:a008::3:2 dev eth0 preferred_lft 0
root@terra:~# ip -6 addr show | grep "scope global"
    inet6 2a01:138:a008::3:1/64 scope global
    inet6 2a01:138:a008::3:2/64 scope global deprecated

Those addresses are now marked as “deprecated” and won’t be used for external outgoing connections:

root@terra:~# wget -q -O- -6 http://download.aditsystems.com/ip.php

To permanently set this parameter, it should be set in the network configuration files: all changes with “ip addr change” are only temporary and will be lost after reboot.

Debian Linux uses /etc/network/interfaces, where the main IPv6 address is defined as an interface and all additional IPv6 addresses are added upon startup of the interface.

<pre class="wp-code-highlight prettyprint linenums:1">iface eth0 inet6 static
        address         2a01:138:a008::3:1
        netmask         64
        gateway         2a01:138:a008::1
        up              /sbin/ip -6 addr add 2a01:138:a008::3:2/64 dev eth0 preferred_lft 0
        down            /sbin/ip -6 addr del 2a01:138:a008::3:2/64 dev eth0
        up              /sbin/ip -6 addr add 2a01:138:a008::3:3/64 dev eth0 preferred_lft 0
        down            /sbin/ip -6 addr del 2a01:138:a008::3:3/64 dev eth0

Debian now creates all additional IPv6 address automatically as “deprecated”, allowing the main IPv6 address as the only source IP for outgoing connections.

(Source: davidc.net)