A friend was trying to figure out what glibc is doing with DNS round robin, and information about it is somewhat scarce. He was expecting it to randomly chose an address out of the multiple at the hostname, but it was preferring only one of the addresses. This behavior doesn't seem to be well documented, so I thought I should write a blog article about it.
Starting with standards, there are two relevant ones:
RFC3484 defines the sorting algorithm for both source and destination address selection. When all else is equal, it prefers source/destination pairs with the longest matching prefix bits
RFC6724 obsoletes RFC3484, but seems to not change things significantly except that IPv4 private addresses are no longer preferred. All IPv4 (except loopback) becomes equal priority. So, with everything equal priority, it should come down to longest matching prefix. Everything at the same priority is chosen randomly.
To verify this works as expected on Linux, I tested a hostname where one of the addresses was in my LAN subnet. My client using getaddrinfo chose the IP on the LAN 100% of the time. This doesn't match the standards exactly, so let's dig into what Linux/glibc actually does.
The "Rule 9: Use longest matching prefix" section of glibc's getaddrinfo is the code actually choosing the order of addresses to return. The "prefixlen" variable is coming from the source interface on this system, and this code is looking to see if the hostname's destination addresses returned are on the same network as this system. If they are not on the same network, the addresses are considered equal.
To illustrate what this means with examples:
- hostname has two addresses: 10.0.1.2 and 10.0.0.2
- If I add an IP with:
sudo ip addr add 10.0.1.1/24 dev loI only get the 10.0.1.2 address
- If I remove that IP and add another:
sudo ip addr add 10.0.0.1/24 dev loI only get the 10.0.0.2 address
- If I add a different subnet that doesn't overlap:
sudo ip addr add 10.0.2.1/24 dev loI get both IPs
- If I add a subnet that doesn't overlap, but shares everything but the last two bits:
sudo ip addr add 10.0.1.1/32 dev loI get both IPs
So it prefers addresses on the same LAN for IPv4, but picks IPs at random otherwise.
See also: This relevant Debian bug report