Controlling IPv6 source address selection

In a previous article, I discussed how IPv6 source address selection worked. Normally it all Just Works, but there are several situations where you may want or need to control the address selection process. In this article, we’ll look at why you might want to control source address selection, and how you can do it.

Why control source address selection?

There are some situations where you might want to alter the default source address selection behaviour on a host. Here are three common ones:

  • A host with a tunnel to the IPv6 Internet will use the address of the tunnel interface as the source address when communicating with the outside world. You may wish the outside world to see a more stable address, if one is available.
  • If you use ULA (Unique Local Addressing) you will typically want addresses from your ULA prefix to be used for internal communications, but globally routable addresses to be used for communications with the outside world.
  • If you need communications with a particular remote host to be from a particular source address (for IP based licensing, for example), you may need to select the source address just for that one remote host.

How to control source address selection?

To recap, source address selection is done by the operating system working through several rules. The rules winnow a list of candidate addresses down to one – or none if there is no suitable address that can be used.

The rules are fixed, but there are still ways you can exercise some control over the selection. Which technique you use will depend on what you want to achieve.

Technique One: Deprecate the addresses you don’t want selected

Rule 3, you may recall, says “avoid deprecated addresses”. By manually deprecating one or more addresses, you can cause other addresses to be selected. This is a fairly blunt tool, but it does work.

One disadvantage is that it is all-or-nothing; the address(es) you have deprecated will never be used for anything as long as there are suitable non-deprecated addresses available.

I use this technique to avoid having a tunnel interface address used as the source address for a server I run. The server is running a tunnel client, which produces a virtual interface with a tunnel endpoint address on it. Here is the situation (much compressed):

kauer@summer:~$ ifconfig
eth1      Link encap:Ethernet  HWaddr 00:09:6B:30:3E:92
          inet addr:192.168.1.35  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: 2001:db8:0:100::1/64 Scope:Global
          inet6 addr: fe80::209:6bff:fe30:3e92/64 Scope:Link

tun       Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet6 addr: 2001:db8::1:1/128 Scope:Global

The server is also advertising a prefix, 2001:db8:0:100::/64,that is used inside my network, and the server itself has an address in that prefix. The path from the server to the Internet is over the tunnel. This means that packets going out to the Internet from the server itself (not routed via the server, but actually sourced by the server) would normally use the tunnel endpoint address as their source address.

I would rather that connections were always made from the server using the address in the prefix. The prefix addresses are the “official” addresses of that network. The prefix is unlikely to change, but the tunnel endpoint addresses probably will. I’d also rather that the outside world saw a name like “myserver.mydomain.com.au” rather than “customer27.v6tunnels.provider.com.au” when they look up the address.

By deprecating the address on the “tun” interface, the address on the ethernet interface is used for outgoing packets instead. In Linux, this is accomplished like this:

ip addr change 2001:db8::1:1/128 dev tun preferred_lft 0

To make this change permanent, it needs to be run whenever the “tun” interface comes up, so I have incorporated it into my tunnel script. The appropriate techniques for making such a change, and for making it permanent if desired, will differ depending on how you set up your tunnel.

Technique Two: Modify the label table

Rule 6 in source address selection (“prefer matching label”) does a lookup in a table of prefixes to find a label for the destination address. It then does the same lookup for each of the candidate source addresses, and prefers those that have the same label as the destination address. By modifying the table, we can change which addresses are selected. That is of course provided that Rule 6 actually applies! In the tunnel example above, Rule 5 (“prefer outgoing interface”) will cause the tunnel address to be selected regardless of the label table.

To see the source address selection table you are actually using, do this in Linux:

ip addrlabel list

To add a line to the table, do this:

ip addrlabel add prefix $PREFIX label $LABEL

… substituting your own values for $PREFIX and $LABEL of course. You can also delete and replace lines – for more information, read the man page on the ip program.

In theory, modifying the file /etc/gai.conf should work as well. All the doco says that the contents of /etc/gai.conf will be used to control source and destination address selection. In reality, /etc/gai.conf is used only to control destination address selection. Using the ip program will definitely work, but it does mean that to make changes permanent, you will have to script them and make sure the script runs at boot time. For some more information – including a script that will do most of the heavy lifting for you – see this article.

Now to an example. Let’s say you are using ULA. The default source selection table used by Rule 6 doesn’t make any distinction between ULA and non-ULA addresses. That means that if a host with a ULA address sends a packet to an external global address, the ULA address may be chosen as the source. This would be bad, as ULA addresses are for internal use only and will be dropped by your border gateway routers. It’s also very unlikely, as Rule 8 (“longest matching prefix”) will probably cause a global address rather than a ULA address to be selected as the source address.

However, the other half of this issue is that the host with a ULA address may choose a non-ULA source address when sending to an internal host. While this will work, it’s certainly not what you want.

We can fix both problems by adding a line with a new label to the source address selection table

fc00::/7 10

This line will match ULA source addresses and ULA destination addresses. The result will be that ULA addresses will be selected as the source addresses for packets being sent to other ULA addresses, and will NOT be selected for packets being sent to non-ULA addresses. The latter will continue to match the standard catch-all ::/0 prefix.

Note that the label value used here, “10”, is in no way special. It just has to be a value that is not already used in the table. If some other line uses “10” already, just choose some other value.

Now let’s say that you have a host with a globally routable address which has been registered with a licence server. Later you add ULA addressing (and make the above modification to the source selection table), and all internal communications start happening using ULA addresses. The problem is that now your host can’t get its licence, because the licence server doesn’t accept the ULA address.

What is needed is a specific “rule” just for the single destination address, that of the licence server. Assuming the licence server is on 2001:db8:0:100::27 and the global address of your host is 2001:db8:0:200::90, you could add these lines to the source address selection table:

2001:db8:0:100::27/128 99
2001:db8:0:200::90/128 99

The destination address will match the first line and the desired source address will match the second line, so packets from the host to that specific destination will use that specific source address.

As with the previous example, the actual value “99” is not special. Just make sure that there is no existing label with that value, and (in this case) that the value used is the same for both the new lines.

Technique Three: Prefer privacy addresses

This technique is for the specific case where you want to use privacy addresses as the source addresses. Recall that according to the standard, privacy addresses will NOT be selected as source addresses (Rule 7). Recall also that the standard requires that this default be able to be reversed.

Some operating systems do not follow the standard, and instead select privacy addresses “out of the box”. Others follow the standard. Consult the documentation for your operating system to find out how to configure it to select (or not select) privacy addresses over stable addresses.

In Linux, it can be accomplished using sysctl. This configures the use of privacy addresses for eth0:

sysctl -w /proc/sys/net/ipv6/conf/eth0/use_tempaddr=2

To make the change permanent, add this line to /etc/sysctl.conf:

net.ipv6.conf.eth0.use_tempaddr=2

The value “2” means “use privacy addresses, and prefer them as source addresses”. If for some reason you want to use privacy addresses but still prefer non-privacy addresses, use “1” instead. And to disable the use of privacy addresses, use “0”.

You will need to disable and re-enable IPv6 for the changes to take effect. The simplest way is to reboot the host. Alternatively, do this:

sysctl -w /proc/sys/net/ipv6/conf/eth0/disable_ipv6=1
sysctl -w /proc/sys/net/ipv6/conf/eth0/disable_ipv6=0

7 thoughts on “Controlling IPv6 source address selection

  1. Thx for this post!

    I use Technique Three for ULAs because some of my hosts are dual-boot and macOS and Linux generate different private ULAs and this prevents stable entries in my DNS configuration.

    The adequate solution is to prevent the private addresses and use simply the standard Interface ID generation from MAC addresses. Unfortunately I loose then also private Interface IDs for the global unicast addresses.
    Is there a possibility to prevent the private address generation only for ULAs but not for global unicast addresses?

    • There is a very simple way to prevent SLAAC for any prefix – configure a subnet size other than /64. This does make your subnet non-standard though, and is generally not to be recommended.

      You can also prevent SLAAC by not advertising the prefix on the link. Most routers have a setting to help you do this. If you still want hosts on that link to have addresses in that prefix, you need to configure those hosts using a different method; static configuration or DHCPv6 for example.

  2. Technique 2 doesn’t work; labels have no effect whatsoever. It’s highly likely that it’s superseded by some of the other rules in the context you mention.

Leave a Reply to Andrej Podzimek Cancel reply

Your email address will not be published. Required fields are marked *