Feature #2347


Add routes into the routing table for delegated IPv6 prefixes.

Added by Seth Mos almost 10 years ago. Updated over 9 years ago.

Target version:
Start date:
Due date:
% Done:


Estimated time:
Plus Target Version:
Release Notes:


Currently we support Prefix Delegation in the DCHPv6 server (ISC dhcpd 4.2.3). However, the dhcpd server does not add routes into the routing tables for the prefix delegation to work.

Ideally a route should be added and removed from the routing table whenever a Prefix is delegated or released.

There are 2 possible ways to go about this, we can do this outside dhcpd where we monitor the leases file and check for prefix delegations and process that for configuring the routes. The problem being that this will likely always trail the actual dhcp6 request from the client.

Example /var/dhcpd/var/db/dhcpd6.leases file:

ia-pd "\000\000\000\000\000\001\000\001\027\013\002\031\000\014)l&}" {
  cltt 5 2012/04/06 12:13:11;
  iaprefix 2001:470:d72c:5000::/56 {
    binding state active;
    preferred-life 4500;
    max-life 7200;
    ends 5 2012/04/06 14:13:11;

For the approach on making this event based we need hooks into the current dhcpv6 server.

There is a message "Picking pool prefix" in dhcpv6.c which is triggered whenever a client gets a IA_PD assigned. There is another message "Picking pool address" just before that holds the IA_NA address which is used for the delegation.

At line 3274 of dhcpv6.c we could probably craft a log message if a prefix is leased to a client.
In the sources of dhcp-4.2.3/server/dhcpv6.c there is the following function which already logs a release.
We could hook into here for removing a route for a delegated prefix. Also see ia_pd_nomatch_release() for orphaned prefixes.

static void
ia_pd_match_release(const struct data_string *client_id,
                    const struct data_string *iapref,
                    struct iasubopt *prefix)
        char tmp_addr[INET6_ADDRSTRLEN];

        log_info("Client %s releases prefix %s/%u",
                 print_hex_1(client_id->len, client_id->data, 60),
                 inet_ntop(AF_INET6, iapref->data + 9,
                           tmp_addr, sizeof(tmp_addr)),
                 (unsigned) getUChar(iapref->data + 8));
        if (prefix != NULL) {
                release_lease6(prefix->ipv6_pool, prefix);
                prefix->ia->cltt = cur_time;

We could also opt for a hybrid approach, fire off a external script on lease/release that parses and adjusts the routes where required.

Actions #1

Updated by Seth Mos almost 10 years ago


$leases_file = "/var/dhcpd/var/db/dhcpd6.leases";
$leasefile = file($leases_file);

foreach($leasefile as $line) {
        // echo "$line";
        if(preg_match("/^(ia-[np][ad])[ ]+\"(.*?)\"/i ", $line, $duidmatch)) {
                $type = $duidmatch[1];
                $duid = $duidmatch[2];

        /* is it active? otherwise just discard */
        if(preg_match("/binding state active/i", $line, $activematch)) {
                $active = true;

        if(preg_match("/iaaddr[ ]+([0-9a-f:]+)[ ]+/i", $line, $addressmatch)) {
                $ia_na = $addressmatch[1];

        if(preg_match("/iaprefix[ ]+([0-9a-f:\/]+)[ ]+/i", $line, $prefixmatch)) {
                $ia_pd = $prefixmatch[1];

        /* closing bracket */
        if(preg_match("/^}/i ", $line)) {
                switch($type) {
                        case "ia-na":
                                $duid_arr[$duid][$type] = $ia_na;
                        case "ia-pd":
                                $duid_arr[$duid][$type] = $ia_pd;

    [\000\000\000\000\000\001\000\001\027\013\002\031\000\014)l&}] => Array
            [ia-na] => 2001:470:d72c::1000
            [ia-pd] => 2001:470:d72c:5000::/56

    [\000\000\000\000\000\001\000\001\026\353\336\354\000\014)\020\267\227] => Array
            [ia-na] => 2001:470:d72c::a40

    [\000\000\000\000\000\001\000\001\026\353\336\364\000\014)\256\031\275] => Array
            [ia-na] => 2001:470:d72c::fa1


Actions #2

Updated by Chris Buechler almost 10 years ago

  • Target version changed from 8 to 2.1
Actions #3

Updated by Seth Mos almost 10 years ago

  • Status changed from New to Feedback
  • Code checked in, Ermal added a command option to dhcpleases to trigger the external php script that adds routes for delegated prefixes.

Needs testing.

Actions #4

Updated by Seth Mos almost 10 years ago

ok, the dhcpleases6 triggers adding of routes and that works, I can still access the ipv6 internet from behind 3 routers deep with fully automatic prefix delegation.

The automatic rule creation for inbound and outbound traffic also works, but they can be overriden in the UI by each one.

There is a problem where pfSense itself can not reach the ipv6 internet leading to issues with anything that attempts to load remote ipv6 content causing issues in dns, http etc. This could be the DHCP6 client setting the routes too strict, or the firewall rules itself on pfSense are too strict to get out. This might be specific to the dhcp6 client setup.

Actions #5

Updated by Jim Pingle over 9 years ago

  • Status changed from Feedback to Resolved

Closing this as the routing for PD nets is working great now, I plugged my ALIX in with a stock config and it pulled a subnet for LAN, and clients behind it were able to route over v6 as expected. Same with a VM in a similar situation.

Splitting the host route issue off into its own ticket (#2414)


Also available in: Atom PDF