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.