When creating a socket unless manually specified, the OS will automatically determine the source address to use. However the OS’s default choice may not always be desired. Source Address Selection allows for influencing the sources address chosen by the OS.
What is Source Address Selection?
When a host with multiple routable IP addresses sends a packet to another host, it needs to determine which of its local addresses to use as the source “from” address.
Hosts having multiple routable local addresses was not very common with IPv4, but it is becoming increasingly more common with IPv6. Especially if there are multiple Router-Advertised subnets and SLAAC addresses bound to the same host, either on the same interface or different interfaces.
RFC 6724 Rules
Unless manually specified, the source address for a packet is determined by the host’s OS. The method for determining a source address is outlined in RFC 6724 and should be consistent across all platforms.
Below are the rules outlined in RFC 6724. The specifics of each rule are beyond the scope of this post. See the RFC for more details. Suffice to say the defaults are good (ex: respond to traffic with the same IP it was sent to) but there are cases there you will want to override the defaults.
- Prefer same address.
- Prefer appropriate scope.
- Avoid deprecated addresses.
- Prefer home addresses.
- Prefer outgoing interface.
- Prefer matching label.
- Prefer privacy addresses.
- Use longest matching prefix.
Why Override Source Address Selection?
Why is this important if the routing table is correct and the defaults work?
Here are a few examples:
- If a host has multiple routable IP addresses, and sends traffic to anther host behind a firewall that only allows one source address through. The default source address selection should be overridden to ensure all traffic destined to the firewalled host uses the correct source address to ensure it is not blocked.
- If a host is on multiple networks, or dual-homed such as a router, with a statically routed IP or subnet, the host will ensure that any traffic destined to or from the statically routed IP/subnet uses the correct interface.
- Ex: You have a server with multiple uplinks and IP 2 addresses. Your primary dynamically routed address is
5.5.5.5
and is routable from every interface and secondary address is in the statically routed subnet3.3.3.1/24
routable from only a single uplink. If the default source address selection outlined in RFC 6724 decides to use3.3.3.1/24
as the default source address, your outgoing traffic may be sent from the statically routed source address to a peer/gateway that only accepts the dynamically routed address. This packet would likely be dropped on a well configured network that implemented BCP38 route filtering.
- Ex: You have a server with multiple uplinks and IP 2 addresses. Your primary dynamically routed address is
Overriding source address selection in Linux
Rule 6 “Prefer matching label” from the above routing source address selection rules can be influenced by custom IP address labels table. By adding to this table it is possible to influence the source address selection used.
The address label tables works as a simple lookup table. For each new outgoing connection the IP address label table is searched for the destination address to find a matching label. If there are multiple matches, the most specific match is used. Once found, the same table is then searched for all possible source addresses with a matching label. If one is found, that address is used as the source address.
The label values are numeric, but they have no ranking or propose other than being a unique value to compare to other labels for equality. A label of 27 is not any more or less significant than the label 42.
To view the current source address selection table, run: ip addrlabel list
.
Example:
$ ip addrlabel list
prefix ::1/128 label 0
prefix ::/96 label 3
prefix ::ffff:0.0.0.0/96 label 4
prefix 2001::/32 label 6
prefix 2001:10::/28 label 7
prefix 3ffe::/16 label 12
prefix 2002::/16 label 2
prefix fec0::/10 label 11
prefix fc00::/7 label 5
prefix ::/0 label 1
A new address label can be added to the table with:
ip addrlabel add prefix $PREFIX dev $IFACE label $LABEL
The dev $IFACE
portion is not required. But it is a good idea to be specific and force the label entry to a particular device.
Example
Lets say you have a host with the IPv6 addresses 2001:0DB8:AAAA::2/64
and 2001:0DB8:BBBB::2/64
. All traffic egressing from this host should use the source IP 2001:0DB8:AAAA::2
except traffic destined for anything in the subnet 2001:0DB8:FOO:BAR::/64
which should use 2001:0DB8:BBBB::2
. This can be forced by adding the following IP address labels:
# set the specific case with label 42
ip addrlabel add prefix 2001:0DB8:FOO:BAR::/64 label 42
ip addrlabel add prefix 2001:0DB8:BBBB::2 label 42
# set the default case with label 99
# The following are actually not needed but included for completeness.
# the prior rules will take the addresses with label 42 out of
# the pool of matching addresses using the default labels.
ip addrlabel add prefix ::/0 label 99
ip addrlabel add prefix 2001:0DB8:AAAA::2 label 99
In this example the labels 99
and 42
are used. But they are arbitrary and can be any number not already in use by an existing address label.
Exclude an Address from the List of Possible Defaults to Select
Sometimes a system may have multiple addresses, and you may just want to exclude an address from the list of possible defaults from the OS to choose from. For this particular case you only need to add the address to the address list table with a label that is not already in use.
Since the address will not have any other matching addresses or networks in the table with the same label, it won’t match anything, and any other address will take prescience.
The following can be used to remove the address 2001:0DB8:BBBB::2
from the host’s default address selection if we want all traffic to use another source address (assuming no other rules change this)
ip addrlabel add prefix 2001:0DB8:BBBB::2 label 32
Set Source Address Rules Address Labels at Boot in Debian
Once the address labels are added, they will take effect immediately. However they are not persistent and will be lost on reboot. On Debian based systems a post-up
hook can be used to add the rule automatically once the parent interface is brought online. Rules associated with an interface are automatically removed when that interface disappears.
In /etc/network/interfaces
or /etc/network/interfaces.d/INTERFACE_CONFIG
if you have a per-interface config, set your ip addrlabel add ...
command in the post-up
section of the stanza for the desired interface. The variable $IFACE
can be used as a placeholder for the current interface name.
Example:
iface eno1 inet6 static
address 2001:0DB8:AAAA::2/64
gateway 2001:0DB8:AAAA::1
iface eno1 inet6 static
address 2001:0DB8:BBBB::2/64
#set high label (42) to not conflict with existing label42
post-up ip addrlabel add prefix 2001:0DB8:BBBB::0/64 dev $IFACE label 42 || true
post-up ip addrlabel add prefix 2001:0DB8:FOO:BAR::/64 dev $IFACE label 42 || true
In this example no address labels are set for the desired default source address 2001:0DB8:AAAA::2
. This still works as now the non-default source address has a non-default label, causing it to not be selected for outgoing traffic by default unless another rule matches the address label.
The post-up
commands end in || true
so that they always return with a 0 exit code. If the command was to fail and return a non-zero exit code, the interface may not finish being brought up by the OS. If this is a server, it is far more important for some of the network to be brought online so that it can be accessed (and fixed) remotely in this case instead of taking itself offline. Adding an address label that already exists will cause an error exit code, but since it already exists, its safe to ignore as it already exists.