DHCPv6 Server with dynamic Prefix Pool

Unfortunately, like with many things that involve using the dynamic IPv6 prefix given to you by the ISP, scripting will be required.

  1. But first, don't waste the /56 prefix provided by your ISP in a pool with pool-prefix-length=128. Instead, you should use pool-prefix-length=64 in the DHCP client setting.

  2. Before being able to do that, go to this site to get a random ULA prefix. Let's assume that you got the prefix fdc3:7058:2954::/48. Use it to create a pool:

    /ipv6 pool
    add name=dhcpv6-ula prefix=fdc3:7058:2954::/80 prefix-length=128
    
  3. Use that pool for the DHCPv6 server instance telekom-vlan16 instead of pppoe-telekom-dhcpv6-pool. The DHCPv6 server will gives out ULA addresses (instead of addresses with the prefix from the ISP). Also modify this DHCPv6 instance to shorten the lease time from the default 1d to 30 minutes or even shorter at 10 minutes. We don't want to wait up to one day for the clients to change their prefix after the ISP changes the prefix.

  4. Now you can modify the DHCPv6 client, so that it uses pool-prefix-length=64 instead of pool-prefix-length=128 for the pool pppoe-telekom-dhcpv6-pool.

  5. Then, use that pppoe-telekom-dhcpv6-pool pool to announce a /64 prefix from the pool on the interface vlan16-private. That way SLAAC will work on the interface and devices will get dynamic IPv6 addresses for them via SLAAC.

    /ipv6 address
    add address=::1234 comment="From Telekom" \
        from-pool=pppoe-telekom-dhcpv6-pool interface=vlan16-private
    
  6. Now add a script named update_ipv6_prefix with the following content:

    :local commenttag "dynamic prefix";
    
    :local PREFIX2IP6 do={
        :return [:toip6 [:pick $1 0 [:find $1 "/" 0]]];
    };
    
    :local EXTRACTQUOTE do={
        :local start [:find $1 "\"" 0];
        :local end [:find $1 "\"" $start];
        :if ($start>=0 && $end>$start) do={
            :return [:pick $1 ($start+1) $end];
        };
        :return "";
    };
    
    /ipv6 dhcp-server binding
    :foreach bding in=[find comment~"$commenttag"] do={
        :local addr [get number=$bding address];
        :local clientduid [get number=$bding duid];
        :local ifname [$EXTRACTQUOTE [get number=$bding comment]];
        :if (0=[:len $ifname]) do={
            :log warning "dynamic_prefix_update: A DHCPv6 static binding is marked for update but does not name an interface.";
        } else={
            :local ifaddresses [/ipv6 address find global !dynamic interface=$ifname];
            :if (1>[:len $ifaddresses]) do={
                :log error "dynamic_prefix_update: Found $[:len $ifaddresses] global IPv6 addresses for interface \"$ifname\".";
            } else={
                :if (1!=[:len $ifaddresses]) do={
                    :log warning "dynamic_prefix_update: Found $[:len $ifaddresses] global IPv6 addresses for interface \"$ifname\".";
                }
                :local addrlen [:pick $addr ([:find $addr "/" 0]+1) [:len $addr]];
                :local prefix [/ipv6 address get number=($ifaddresses->0) address];
                :local prefixlen [:pick $prefix ([:find $prefix "/" 0]+1) [:len $prefix]];
                :if ($addrlen < $prefixlen) do={
                    :set prefixlen $addrlen;
                };
                :local mask [$PREFIX2IP6 [[:parse ":return $(~::)/$prefixlen"]]];
                :local newaddr ((([$PREFIX2IP6 $addr] & ~$mask) | ([$PREFIX2IP6 $prefix] & $mask))."/".$addrlen);
                :if ($addr!=$newaddr) do={
                    :log info "dynamic_prefix_update: Updating \"$clientduid\" $addr -> $newaddr (prefix from \"$ifname\").";
                    set number=$bding address=$newaddr prefix-pool="";
                };
            };
        };
    };
    
  7. Modify the DHCPv6 client instance to call this script whenever a new prefix is obtained:

    if (1=$"pd-valid") do={
        /delay 2;
        /system script run update_ipv6_prefix;
    };
    
  8. Go to the IPv6 -> DHCP Server -> Bindings table, that is now populated with bindings with ULA addresses with the fdc3:7058:2954:: prefix. Choose the entries that you want to make static and make them static, edit the suffix part of the addresses to your liking, such as ::0815, but before saving the entries, give them each a comment that must contain the text dynamic prefix "vlan16-private" somewhere in the comment.

    Please note that vlan16-private in this case is the name of the interface that has SLAAC active, and that has a /64 prefix assigned from the ISP pool.

  9. Once you have created the static bindings (still all with the ULA prefix), execute the script update_ipv6_prefix at least once. You'll see that all the static bindings now have their prefix changed to the correct prefix from the ISP, matching the prefix assigned to the vlan16-private interface. We'll need the lease time on the client to be at least 50% elapsed before the clients reflect the changes though.

    And because the script is called everytime the DHCPv6 client obtains a new prefix, the bindings will have their prefix updated too.

That's how I am assigning GUA addresses from my ISP via DHCPv6 to my devices. The script above was adapted from an old thread on this forum, the original code updated the prefix in address list entries. However, the original author has since edited his post and removed the script content.

2 Likes