DHCPv6 prefix delegation server for linux

dhcpipv6

What linux DHCPv6 client can I set up so it requests a prefix on one interface and then on all the other interfaces it will give out addresses (or if requested, a smaller prefix delegation)? It would also need to set up the routes and router advertisements.

If there's not a way to do this, what is the IPv6 version of programming an IPv4 router to do NAT, wherever it is put? Basically, I'm trying to understand how a manufacturer would set up their IPv6 capable routers so a customer can plug them in and they would work. I'm not talking about actual NAT in IPv6, but a way for a router to have a DHCPv6 prefix delegation client which would take the prefix it received and break it into smaller prefixes and addresses and run a DHCPv6 server for each separate downstream link.

I added a diagram:

Diagram with 9 computers. #2, #4, #6, #7, and #9 are DHCPv6 clients. #1 is assigned 2001:db8:1200::/40 and giving out /48 prefix delegations. #3, #5, and #8 are DHCPv6 servers and clients at the same time as they are requesting prefixes.

Is there (a) piece(s) of software I can run on Linux that could be configured once and then put in place number 3, 5, or 8 and it would automatically set itself up depending on the prefix it was delegated?

Best Answer

This isn't a complete answer, since I'm still working through some of the problems, but here's what I've got so far, trying to deploy a near-identical setup (though for somewhat different reasons).

The good news is: it can be done, within the specs. In fact, the specs were explicitly designed to encourage this sort of subdelegation of prefixes, largely to avoid the need for painful multiple NAT layers while still allowing flexibility in how networks are structured.

The bad news is: there are no "out-of-the-box" single pieces of software that will request a prefix from an upstream, configure addresses and routes locally, and hand out sub-prefixes to downstream networks. Hell, it doesn't even appear possible to wire up pre-existing pieces of software in Linux (without patching) to do what needs to be done. Worst of all, it doesn't look like anyone really cares about this.

What I've got setup and working so far looks like this:

  • I'm using WIDE DHCPv6 client (dhcp6c) to request a prefix from the "upstream" network; I chose this client over ISC's DHCP client because, at the time I set it up, it was the only DHCPv6 client that would automatically assign addresses out of the received prefix to other interfaces. I would like to think that other DHCP clients have improved since then, but I couldn't be bothered to check at this point.

    Unfortunately, it doesn't expose the delegated prefix details to its external hook script, meaning that you can't (easily) rewrite your "downstream" dhcpd config.

  • ISC DHCP server (in v6 mode) to assign prefixes to the "downstream" network. I chose this because WIDE's DHCP server, as far as I can tell, can only delegate prefixes statically, not dynamically. It's relatively easy to configure ISC DHCP server to do dynamic prefix delegation, surprisingly; something as simple as this will do the trick:

    subnet6 2001:db8:1234:ffff::/64 {
      prefix6 2001:db8:1234:a000:: 2001:db8:c0f:aff0:: /60;
    }
    

    This will listen on whatever interface has an address in 2001:db8:1234:ffff::/64 and will hand out /60s from the range specified. As long as you can coerce dhcp6c to update the above config (next!), you can automatically update the dhcpd config when your delegated super-prefix changes.

  • dhcp6c will, as previously mentioned, take a script parameter in its config file, to run when a DHCPv6 response is received. Unfortunately, it only exposes parameters like the SIP server, and DNS resolvers, not useful info like the prefix that's been delegated. So, I've got the following scriptlet to do that for me:

    #!/bin/sh
    
    (sleep 3;
    
    base_prefix="$(ip -6 ad sh eth0 | grep 'scope global' | cut -d ' ' -f 6 | cut -d : -f 1-4 | sed 's/00$//')"
    
    if [ "$base_prefix" = "" ]; then
      exit 0
    fi
    
    cat <<-EOF >/etc/dhcp/dhcpd6.conf
    default-lease-time 1800;
    max-lease-time 7200;
    
    subnet6 ${base_prefix}00::/64 {
      prefix6 ${base_prefix}a0:: ${base_prefix}f0:: /60;
    }
    EOF
    
    svc -t /etc/service/dhcp6d) &
    
    exit 0
    

    Doing all the work in a sub-shell means that I can wait a little while (the sleep 3) for the DHCP client to actually configure the interface (it appears to run the script before configuring the interface). It works reliably enough. Note that my ISP delegates me a /56, hence why I'm only stripping the lasy two zeroes off the received prefix. If you're lucky enough to get a whole /48, the pipeline to assign base_prefix would be a lot simpler.

What doesn't already exist, and which I'm pretty sure requires patching source code, is setting up routes when a prefix gets delegated. So far as I can see, no DHCP server has the built-in ability to add the route automatically (I've carefully examined WIDE dhcp6s, it certainly can't do it, and I can't find anything on the 'tubes to suggest that ISC DHCP does it). There isn't even the ability to run an external command that takes the prefix being delegated and the client's link-local address (ISC DHCP has on commit { execute(...) }, but no way to get the client's link-local address to pass out).

The problem is that without this crucial bit of functionality, the router that's making the delegation can't know where to route the traffic for the prefix to afterwards. That's a fairly fundamental limitation, and I'm rather stunned that nobody appears to have dealt with this problem before -- or if they have, they're keeping really quiet about it.

I've just developed a patch to ISC DHCP to provide an extra "data expression" to the eval functionality; this allows me to shell out to an external program which can then add/remove routes on prefix allocation and release/expiry; the patch is available at https://github.com/mpalmer/isc-dhcp/commit/4c8ae763bcf83c9068d57a5d9f570690a581b6d6 (against ISC DHCP 4.3.1); I don't have a script to add the route (yet), but I'll probably add it under contrib in that branch once I get it written.

ADDENDUM: It turns out that further modification was needed to allow routes to be removed again; that has now been added to the client-address-data-expression branch, along with a small Ruby script that shows how it can all be combined together.

Related Question