random things and thoughts … and bad ideas

dynamic prefix delegation can be easy

A few months ago I coincidently noticed my router having an IPv6 address. I knew my ISP was in the process of rolling out IPv6 but I never got or found any announcement that it was available for me. So I tried out setting up a prefix delegation for all my local networks. Sadly I noticed two things, which in hindsight might have been incorrect even back at that time. Firstly I was only able to receive a 64-bit prefix and secondly which made things appear even worse the prefix did not seem to be static. That meant I needed automatic delegation of the possibly changing prefix but could not use SLAAC and I couldn’t get any DHCPv6 client I tried to actually do a prefix delegation with prefixes longer than 64-bit. Probably because most people follow the suggestion to never use prefixes beyond 64 so I couldn’t find any usable reference how to get it to work. To my surprise some people even seem to think it is impossible to use prefixes longer than 64-bit at all. I just went on hacking together some ugly dhcpcd hook to manually apply the prefix from the DHCPv6 reply to the interfaces and change the dnsmasq configuration. My hack even worked to some extent. Some problems with randomly vanishing host addresses remained but I could not pin point the cause. Yesterday I tried reworking my setup to find the cause and again by total coincidence I came across a note that my ISP does in fact delegate 56-bit prefixes. Obviously with that info there was no more need for dirty hacks and I could try doing my delegation properly™.

Setting up a common scenario like delegating a 56-bit IPv6 prefix to just a few local networks is actually a rather trivial task. My tools of choice for automatic network configuration on my router are dhcpcd for configuring the interface on the WAN side and dnsmasq for the local networks. Both tools are pretty lightweight and the integration of both DNS and DHCP into dnsmasq makes a centralized configuration of all devices in the home network simple and straightforward.


For the prefix delegation with dhcpcd I basically just had to follow the example from the man-page and adjust it to my specific network layout. I got three local networks a LAN, a WLAN and an OpenVPN on a tap interface. After globally disabling the Router Advertisements solicitation I configured the four interfaces. The eth0-interface is on the WAN side so the prefix delegation has to be set up there. In my opinion the man-page for dhcpcd.conf is a little ambiguous about the ia_pd option. Even after reading through the RFC describing prefix delegation I needed some trial and error to get it working.

The first number after the ia_pd option is the iaid which identifies the delegation on this router. This is necessary if more than a single prefix is requested for delegation for example if there are several interfaces on the router each needing its own delegated prefix. If omitted dhcpcd uses the last four octets of the MAC address but the iaid can actually be any 32-bit hex-number as long as it never changes. The iaid has two options prefix and prefix_len directly following it. These are sent with the delegation request in the IA_PD-options field to specify some preferences for the requested prefix. This is sometimes called prefix hint. Because I don’t have any preferences except for the length I entered the zero-address “::” for prefix and 56 for prefix_len. After iaid and its options the interfaces that are supposed to receive prefixes from the delegation can be listed. Each interface again has two options sla_id and prefix_len. The interface’s prefix_len defines how long the prefix assigned to the respective interface will be which is created from combining the delegated prefix and each interface’s sla_id. That means sla_id can only be “interface prefix length” minus “delegated prefix length” bit long. To create a new 64-bit prefix for each interface I gave each interface its own sla_id from 1 to 3 and omitted the prefix_len field to use the default which is 64. Because dhcpcd has to run on each interface it is supposed to delegate a prefix to without touching the IPv4 configuration, dnsmasq is taking care of that, I added the noipv4 option to the local interface sections leading to this snippet I added to the default configuration file.


interface eth0
ia_pd 1/::/56 eth1/1 wlan0/2 tap0/3

interface eth1

interface wlan0

interface tap0

I don’t know if it’s because dhcpcd always assumes 64 for an omitted prefix_len option or because my ISP still delegates 64-bit prefixes by default but it was necessary to add the prefix_len of 56 to iaid to get the 56-bit prefix. Without it I could only get the regular 64-bit prefix. Another thing to consider are the DUID and DHCPv6 lease. It is possible that just changing the configuration is not enough to change the delegated prefix because the ISP’s server might just send the prefix still associated to the DUID. So I deleted the dhcpcd.duid and lease6 file to get rid of all traces of my previous lease.


With that configuration the actual process of prefix delegation is done and each interface should get the interface identifier ::1 with its newly created 64-bit prefix. Still that is not enough to get hosts in the subnets on each interface receive an IPv6 address. The presence of the router and the prefix have to be announced to the subnets. Additionally to DHCP and DNS dnsmasq is also capable of doing these Router Advertisements so no standalone RA daemon like radvd is necessary. Enabling that feature is easily done with the enable-ra option inside the /etc/dnsmasq.conf file. Because I don’t want to access hosts in the network by their address but a name I chose to not just use SLAAC for assigning addresses to hosts but also stateful DHCPv6. To assign addresses with DHCPv6 a range of addresses has to be specified for each subnet in the dnsmasq configuration. Considering that it’s not sure that the prefix never changes the prefix of these ranges can not be entered statically but has to be taken from the interfaces. This can be done with the constructor option when specifying the dhcp-range in the dnsmasq configuration. Although apparently it is necessary to start the range of interface identifiers with ::1 to make constructor work. I entered my ranges from ::1 to ::ffff, set the lease time to 3 days and added the slaac mode so each host is told to both create a SLAAC address and receive an address from the range of DHCPv6 addresses. Together with all the DNS and IPv4 related options I got this configuration.






What is left at this point is getting the hosts to listen for the IPv6 RA and request an address from the DHCPv6 server which to my knowledge all recent operating systems or DHCP clients do by default. Also if the DHCP client was configured to send its hostname with the DHCP request, which should be the default too, dnsmasq automatically creates A and AAAA entries for the name lookup. That is pretty much all there is to setting up prefix delegation for a home network with just a few subnets.


As expected setting up a standard use case using standard tools is actually pretty easy. If everything went like this right from the beginning I probably wouldn’t have written about it here. But because I went through the work to split a single 64-bit prefix among my three subnets and was just about to write a lengthy post about my dirty hack I decided to at least write a post about how it is supposed to work if everything goes right. In case anyone is really facing the problem of only having a single 64-bit prefix for more than one subnet and needs some ideas how it could but probably shouldn’t be done here’s my dhcpcd-hook-hack.

# delegate nonstatic 64bit prefix to multiple interfaces

# delete lease file on startup so hook runs even with unchanged prefix
if [ "$reason" = "PREINIT" ]; then
        rm /var/lib/dhcpcd/dhcpcd-*.lease6

case "$reason" in

        # only run if there is a delegated prefix and it has been changed       
        if [ -n "$new_dhcp6_prefix" ] && [ "$new_dhcp6_prefix" != "$old_dhcp6_prefix" ]; then
                delegated_prefix=$( echo "$new_dhcp6_prefix" | cut -d/ -f1 )
                old_prefix=$( echo "$old_dhcp6_prefix" | cut -d/ -f1 )

                # add adresses with new prefix to interfaces and delete old ones
                ifconfig eth1 del "${old_prefix}1:ffff/112"
                ifconfig eth1 add "${delegated_prefix}1:ffff/112"

                ifconfig wlan0 del "${old_prefix}2:ffff/112"
                ifconfig wlan0 add "${delegated_prefix}2:ffff/112"

                ifconfig tap0 del "${old_prefix}3:ffff/112"
                ifconfig tap0 add "${delegated_prefix}3:ffff/112"

                ifconfig eth0 del "${old_prefix}d00f:affe:ffff/112"
                ifconfig eth0 add "${delegated_prefix}d00f:affe:ffff/112"

                # create dnsmasq config with new prefix and restart it to load new config
                sed "s/_PLACEHOLDER_/${delegated_prefix}/g" /etc/dnsmasq.tmpl > /etc/dnsmasq.conf
                sed "s/_PLACEHOLDER_/${delegated_prefix}/g" /etc/hosts.tmpl > /etc/hosts
                rc-config restart dnsmasq --nodeps


Additionally to the hook I created a configuration template for dnsmasq with a placeholder for the prefix in dhcp-range that was used to create a new /etc/dnsmasq.conf whenever the prefix changed. The dhcp-ranges looked like this.


Again if you feel the need to add anything to this post or just yell at me for doing this feel free to use the comments.


  • set up prefix delegation to local networks with dhcpcd
    • add prefered prefix length to the prefix hint
    • add interfaces of the networks to prefix delegation including proper sla_id
    • delete dhcpcd’s lease file and duid file so any old /64 prefix will not get renewed
  • configure dnsmasq to advertise prefixes and/or IP address ranges on each local network
    • enable router advertisements
    • use constructor to define address ranges for dhcp with dynamic prefix
  • configure SLAAC and/or DHCPv6 on the hosts in the local networks


Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s