If you have configured multiple IPv6 addresses within a CentOS machine, it shows the strange behavior that it sets the last IPv6 address in IPV6ADDR_SECONDARIES as its default source address for outgoing connections, which can end up in quite annoying problems. For example, firewalls may expects the main IPv6 address and not a secondary, which changes if you add another one and don’t think about it to not add it at the end. Nothing you would like to have on a production system.
So i started to find a (more or less) clean and reliable solution to set the correct source IPv6 address on the few machines with multiple IPv6 addresses (Thanks god for SNI support…). As it turned out, that’s not as easy as i thought first.
My very first idea was to use use the source address selection table to do it, because it sounded very promising first. Unfortunately i haven’t found a way to configure it to do what i want.
You can find more informations about it here: Controlling IPv6 source address selection
My second (and finally working) idea was to set an appropriate route, or replace the default root. Replacing the default route may isn’t such a good idea because, if my rule fails to become active for whatever reason, there is not default route for IPv6 anymore at all. So i decided to use 2000::/3 in my routing rule because it’s the only global unicast address space in use at the moment, and should last for some time (Source: IANA – Internet Protocol Version 6 Address Space).
So, the route i need is:
2000::/3 via <gateway> dev <interface> src <source address> metric 1 f.ex. 2000::/3 via 2001:123:456:789::1 dev ens192 src 2001:123:456:789::100 metric 1
To get a persistent rule, i first had a look at the good old “/etc/sysconfig/static-routes-ipv6”. But it does not support to use the “src”-argument we need to set the source address.
Next, “/etc/sysconfig/network-scripts/route6-<interface>”, which is the newer and recommended way to do additional routes these days. And it supports all options an “ip -6 route add…” command would provide, because it simply adds each line within the file to this command. That’s exactly what i need.
I created the file (/etc/sysconfig/network-scripts/route6-ens192 in my case) and added the route:
2000::/3 via 2001:123:456:789::1 dev ens192 src 2001:123:456:789::100 metric 1
Perfect. Restarted the network service, and… Nothing… If i remove the “src”-argument it works, but of course it’s useless without it. It logs an “RTNETLINK answers: Invalid argument” error in /var/log/messages. This happens f.ex. when the source address does not exists on the system. But it should, because this is executed after the IPs are set. But it looks like it isn’t fully active yet.
At the very end of the “ifup-post” script (within /etc/sysconfig/network-scripts/) there’s an additional script which is executed if it’s present:
... if [ -x /sbin/ifup-local ]; then /sbin/ifup-local ${DEVICE} fi
This script does not exist on CentOS machines, so i created it and added the rule with a slight delay before it’s executed:
#!/bin/sh if [[ "$1" == "ens192" ]]; then sleep 1 ip -6 route add 2000::/3 via 2001:123:456:789::1 dev ens192 src 2001:123:456:789::100 metric 1 fi
Don’t forget to make the file executable (as usual with “chmod 750 /sbin/ifup-local”) and you’re ready to go.
That’s enough time to bring up the interface before the route is set. You may have to increase it a bit further depending on the system you use.