How To Get That Dynamic IPv6 Address To The DNS Server

In a previous post on my first “IPv6 call” to a service at home I reported about the pains of finding a Dynamic DNS provider that supports IPv6 and supports it with a Time To Live (TTL) value of 60 seconds. The later part is important as this is the time the domain is not reachable in the worst case when the IPv6 address changes. For the moment my solution is to use a CNAME entry in my domain name I host at to another domain name at For details see the previous post. The next challenge now was to find out how to automatically update the IPv6 address at the DNS server once it changes as this does not work the same way as for IPv4 addresses.

Updating the dynamic IPv4 address at a DNS server is straight forward. Either the Network Address Translation (NAT) router already has the functionality to send an update message to the dynamic DNS server or a simple http(s) request from any machine behind the NAT with some authentication information in the URL does the trick. No IP address is required in the request as the DNS server gets the request from the backhaul facing public IP address and just takes the value from layer 3. With IPv6 this doesn’t work for two reasons:

There is no NAT anymore so each server behind the router has its own public IPv6 address and thus has to send the request by itself. O.k., so far, no big deal. The next problem is that the server can have more than one public IPv6 address. While the public prefix for these IPv6 addresses are the same, there can be different interface IDs in case IPv6 privacy extensions are enabled. On a server, privacy extensions are not necessary but Ubuntu server seems to have it switched on by default. Also, for a little while there can be deprecated IPv6 addresses in the address list with a prefix that is no longer valid. To get the IPv6 address across to the dynamic DNS server one could do as before and just send an http(s) request with a token if the URL is only reachable via IPv6. The problem with this approach is that the update domains for the service I tried to not support that, they are both IPv4 and IPv6 reachable.

Another problem occurs in case privacy extensions are used on the server as described before. In this case the interface ID will be randomly assigned for one of the IPv6 addresses bound to the interface. While the dynamic DNS server won’t mind, the router at home will because, at least my model blocks incoming IPv6 requests to all interface IDs by default and exceptions have to be statically configured. That’s a good thing but this requires that the interface ID of the server remains static. One of the two IPv6 addresses on my server fulfills this requirement as the interface ID is based on the Ethernet adapter’s MAC address so I can use this interface ID to configure the IPv6 firewall in the router. However, the request to the dynamic DNS server to update the IPv6 address does not originate from this IPv6 address but from the one with the randomly assigned interface id. Twice out of luck.

The solution to the problem is to find out which global dynamic IPv6 addresses are currently auto-configured on the server and to select the IPv6 address form the list that uses the MAC address as interface ID in the IPv6 address. The easiest way to do this I have found so far is a great shell script offers for the purpose that can be found here. For my purposes I’ve made two changes:

First, I adapted the command that finds the current IPv6 address. The original script does not take IPv6 privacy extensions into account which can be fixed by modifying the line as follows:

address=$(ip -6 addr list scope global $device | grep -v ” fd” | grep “global dynamic” | sed -n ‘s/.*inet6 ([0-9a-f:]+).*/1/p’ | head -n 1)

And the second change I’ve done in the script is to use https instead of http to actually run the queries as dynv6 supports https for the updates as well. No need to spread the word about my id token in plain http requests. The beauty of the script is that it checks if the IPv6 address has changed and only sends an update if it has. This way I can call the script with a cron job once a minute without sending and update to every time.

There we go, this problem is now solved as well!