Support for IPv6-only networks with RFC 8925 (v6-only-preferred)
Some initial questions
- Are you sure your feature is not already implemented in the latest Kea version? Yes
- Are you sure what you would like to do is not possible using some other mechanisms? Yes
- Have you discussed your idea on kea-users or kea-dev mailing lists? Yes: https://lists.isc.org/pipermail/kea-users/2023-July/004207.html
Is your feature request related to a problem? Please describe. In APRICOT 2024 next year, there will be a separate IPv6-only wifi network (in parallel with the main conference SSID). There's nothing new about that.
However, this year we want to ensure that clients will gracefully fall back to IPv6-only operation and make use of a NAT64 device to access the IPv4-only Internet via the client's embedded CLAT. MacOS, Android and iOS support this mode of operation. Full details are in the article here: https://labs.ripe.net/author/ondrej_caletka_1/deploying-ipv6-mostly-access-networks/
In summary, these clients will request option 108 in their (DHCPv4) discover request. The server should respond with yiaddr 0.0.0.0 and option 108. The client will then activate their CLAT, as long as they also get the NAT64 prefix from a separate field (PREF64) in the RA's.
Running ISC-KEA or ISC-DHCPD for the DHCPv4 service doesn't currently work well here. Well, it does work for those clients which support option 108. But for clients which don't, they will be offered an IPv4 address, and will configure their interface with it. This is supposed to be an IPv6-only network, and hence we don't want machines to pick up an IPv4 address. They will find they have failing connectivity when they try to use their IPv4 address.
Not running a DHCPv4 server at all is not an option, because the clients won't enable their CLAT unless they've successfully done the option 108 dance.
Describe the solution you'd like
I would like KEA to implement the following:
- Allow IPv4 subnets to have no pool
- Allow IPv4 subnets to have a flag for enabling v6-preferred
- In such a subnet, if a client requests option 108, then return yiaddr 0.0.0.0 with option 108
- (Optionally: if I client sents option 116, then return yiaddr 0.0.0.0 with option 116)
- Otherwise, if there's no pool, then do whatever you'd normally do when the pool is exhausted (presumably either send no response, or send a NAK)
(Option 116 is RFC 2563: it also returns yiaddr 0.0.0.0. It lets you tell a client not to configure a global v4 address, and also to tell it whether or not to configure a link-local address. In principle, it could also reduce DHCPv4 discovery chatter in networks without v4. However I mark this support "optionally" because I've not actually found a client which makes use of this option yet)
Describe alternatives you've considered There's a discussion in the list at https://lists.isc.org/pipermail/kea-users/2023-July/004207.html
The key thing I need to make sure is that no IPv4 address is returned to a client unless it requests option 108.
Note that the client doesn't actually send option 108, it puts 108 in its parameter request list, and it seems to be quite tricky to handle this in KEA. The best I could come up with was:
"client-classes": [
{
"name": "rfc8925",
// We need to test whether option 108 is in the client's parameter request list (option 55).
// That's not the same as "option[108].exists"
// https://kea.readthedocs.io/en/latest/arm/classify.html#using-expressions-in-classification
"test": "substring(option[55].hex, 0, 1) == 0x6c
or substring(option[55].hex, 1, 1) == 0x6c or substring(option[55].hex, 2, 1) == 0x6c
or substring(option[55].hex, 3, 1) == 0x6c or substring(option[55].hex, 4, 1) == 0x6c
or substring(option[55].hex, 5, 1) == 0x6c or substring(option[55].hex, 6, 1) == 0x6c
or substring(option[55].hex, 7, 1) == 0x6c or substring(option[55].hex, 8, 1) == 0x6c
or substring(option[55].hex, 9, 1) == 0x6c or substring(option[55].hex, 10, 1) == 0x6c
or substring(option[55].hex, 11, 1) == 0x6c or substring(option[55].hex, 12, 1) == 0x6c"
},
],
(it's ugly and incomplete - what if the client send more than 13 options?)
Then avoid sending any response to clients which don't provide this option:
"pools": [
{
// Only give OFFERs to devices which support RFC 8925
"pool": "10.12.65.2 - 10.12.65.254",
"client-class": "rfc8925"
}
],
"option-data": [
{
"name": "v6-only-preferred",
"data": "0"
}
],
That kind-of works. It's still not entirely RFC 8925 compliant as it should set the yiaddr to 0.0.0.0 (I couldn't see how to do that with the non-commercial plugins) and it shouldn't need to consume an address from the pool, although I can make that pool arbitrarily large.
As described before: the option of not running DHCPv4 service doesn't work, because the clients won't activate their CLAT without having received option 108.
What I have ended up doing is writing some custom DHCPv4 server modules for a standalone Go DHCP server: https://github.com/coredhcp/coredhcp/pull/170
Additional context This is a demonstration network at a conference, so it's not exactly "production" but more is a technology demonstrator of how well an IPv6-only network with NAT64 could work.
When I've tested this locally, I find that IPv4 access via the NAT64 works nicely, even when using IPv4 literals. For example, "ping 8.8.8.8" or browse to https://1.1.1.1 work fine (except from Safari). The traffic is actually IPv6 across to the NAT64. No DNS64 is required.
We'd like to demonstrate this so people can evaluate the feasibility of real deployment of single-stack IPv6 networks now or in the future.
Funding its development Only in-kind development contributions
Participating in development Yes, willing to contribute to discussions and/or testing.
Contacting you brian@nsrc.org