Design Notes
This document keeps early design notes that are not yet part of the stable resource model.
DS-Lite Without Prefix Delegation
Reference:
During PR-400NE / NTT Flets testing, some hosts can receive DHCPv6 Prefix Delegation immediately after the home gateway restarts, while another host may continue sending DHCPv6 Solicit for IA_PD without receiving Advertise or Reply. When PD is unavailable, a possible fallback is:
- keep the WAN-side IPv6 address obtained by RA/SLAAC or DHCPv6 IA_NA;
- establish DS-Lite using that upstream IPv6 reachability;
- provide IPv4 service to the LAN through DHCPv4 and DS-Lite;
- bridge or otherwise pass through IPv6 to the LAN instead of routing a delegated prefix.
This is only a design note. It needs separate validation before it becomes a resource model because IPv6 bridging changes the ownership boundary:
- routerd would no longer own a routed LAN IPv6 prefix;
- RA, DHCPv6, firewall, and neighbor-discovery behavior may be controlled by the upstream home gateway;
- firewall policy must avoid accidentally exposing LAN hosts when IPv6 is bridged;
- DS-Lite tunnel source selection must support an address that is not derived from a delegated LAN prefix.
Potential future resource shape:
- a WAN state label for "PD unavailable but upstream IPv6 usable";
- a DS-Lite local-address source that can use WAN SLAAC / IA_NA addresses;
- an IPv6 bridge/pass-through resource with explicit firewall defaults;
- a retry policy that records previously delegated prefixes, DUID, IAID, and lease metadata to prefer renewal-like behavior when a home gateway is sensitive to fresh DHCPv6-PD requests.
Current groundwork:
IPv6PrefixDelegationrecords observed prefix state underipv6PrefixDelegation.<name>.*in the routerd state store.- The last known prefix is retained when no current downstream prefix is visible.
IPv6PrefixDelegation.spec.convergenceTimeoutkeeps a recently observed current prefix alive for a short grace period while the OS DHCPv6 client is reacquiring PD. This timeout is separate from systemd-networkd or KAMEdhcp6cretransmission timers; routerd does not currently tune those client-specific packet timers.- For systemd-networkd, routerd records IAID/DUID material when it can be observed from networkd runtime files. For NTT profiles it also records the expected link-layer DUID derived from the uplink MAC address.
- For FreeBSD
dhcp6c, routerd records IAID from configuration and DUID from/var/db/dhcp6c_duid. This matters because a home gateway may treat a remembered DUID/IAID pair as an existing lease that should be renewed rather than a new client that should receive a fresh lease. - For FreeBSD, routerd observes the delegated prefix from addresses that KAME
dhcp6chas already placed on the downstream interface, then adds the configured stable suffix address as a secondary address. - FreeBSD
dhcp6cis rendered with-n, and routerd uses SIGUSR1 for required restarts, because normal stops send DHCPv6 Release and can make a home gateway keep a stale lease while the client falls back to fresh Solicit. IPv6PrefixDelegation.spec.hintFromStatedefaults totrue. When routerd has a last observed prefix whose valid lifetime has not elapsed, it feeds that prefix back to systemd-networkd or KAMEdhcp6cas a prefix hint. If the lease memory is missing or too old, routerd falls back to a prefix-length hint. This keeps the request harmless when the upstream forgot the old lease.- PR-400NE testing showed DHCPv6 Advertise/Reply packets with UDP destination port 546 and a non-547 source port. Firewall policy must match the client destination port and must not require source port 547.
- PR-400NE testing also showed that a home gateway restart can make multiple
/60PD leases appear at once, while fresh Solicit attempts before restart may look unanswered. The working model is to keep DUID/IAID and last-prefix memory, avoid unnecessary Release traffic, and leave renewal-like retry as a separate future step.
This still does not synthesize DHCPv6 Renew/Rebind packets. That should remain a separate implementation step because it must preserve management connectivity and must not fight the OS DHCPv6 client.
DHCPv6 Prefix Delegation Behavior in Other Routers
This note compares open-source and commercial DHCPv6 Prefix Delegation implementations to guide routerd's next PD design steps.
References:
- RFC 9915: Dynamic Host Configuration Protocol for IPv6
- OpenWrt odhcp6c README
- OpenWrt odhcp6c source cross-reference
- OpenWrt odhcpd README
- OpenWrt odhcpd source cross-reference
- systemd.network manual
- FreeBSD dhcp6c(8)
- FreeBSD dhcp6c.conf(5)
- pfSense advanced networking documentation
- OPNsense DHCP documentation
- MikroTik RouterOS DHCP documentation
- MikroTik RouterOS IP pools documentation
- Cisco IOS XE DHCPv6 Prefix Delegation
- Cisco DHCPv6 PD configuration example
- Juniper Junos IA_NA and Prefix Delegation
- Juniper Junos subscriber LAN prefix delegation
- Juniper Junos DHCPv6 client reference
Observed Patterns
| Implementation | Lifetime and lease state | Restart behavior | Identity handling | Renew / Rebind / Solicit / Release policy | Loss handling |
|---|---|---|---|---|---|
OpenWrt odhcp6c | Exposes prefixes with preferred and valid lifetimes to scripts and ubus. Packet counters include Solicit, Request, Renew, Rebind, Reply, and Release. | Event-driven state scripts update the rest of the system when the DHCPv6 state changes. | Client ID is exposed as DHCPv6 option 1; OpenWrt keeps the client daemon as the owner of DHCPv6 packet state. | Implements the normal DHCPv6 state machine and reports bound, updated, rebound, unbound, and stopped. | unbound explicitly means all DHCPv6 servers were lost and the client will restart. |
OpenWrt odhcpd | Server-side leases are persisted in a lease file; PD leases include interface, DUID, IAID, expiration, assignment, length, and active prefixes. | Dynamic reconfiguration happens when prefixes change. | Server bindings are keyed by DUID and IAID. | Server and relay behavior is separated from the client. | Can relay RA, DHCPv6, and NDP when no delegated prefix is available. |
| systemd-networkd | Owns the DHCPv6 client and downstream prefix assignment. The manual documents upstream and downstream PD sections, prefix hints, subnet IDs, and RA announcement. | Recreates runtime state from network files and DHCPv6 exchange; it does not expose a stable lease database suitable as routerd's source of truth. | DUIDType, DUIDRawData, and IAID can be configured. | SendRelease exists; systemd changed release behavior over time, so routerd should render it explicitly when relying on no-release behavior. | PD changes are handled inside networkd; external orchestration needs to observe resulting addresses/routes and logs. |
FreeBSD / KAME dhcp6c | Stores client DUID in /var/db/dhcp6c_duid; configured IA_PD and prefix-interface drive downstream assignment. | SIGHUP reinitializes and SIGTERM stops; both normally send Release. SIGUSR1 stops without Release. | IAID is configured in dhcp6c.conf; DUID is in the DUID file. | -n prevents Release on exit. This is important for home gateways that remember leases but do not answer fresh Solicit reliably. | Resource removal is local; another process must observe whether downstream prefixes remain present. |
| pfSense / OPNsense | Both expose DHCPv6-PD through a firewall UI. pfSense documents DUID editing and a no-release option. OPNsense documents active prefix leases and DUID matching for static mappings. | Configuration is rendered into underlying DHCP components; operators can preserve a DUID across reinstall. | pfSense accepts raw DUID, DUID-LLT, DUID-EN, DUID-LL, and DUID-UUID. OPNsense server-side static mapping uses DUID, with documentation noting active leases. | pfSense explicitly documents that dhcp6c sends Release by default and offers an option to prevent it. | The user-facing model surfaces DHCPv6 status and allows debugging logs. |
| MikroTik RouterOS | Client shows prefix, expires-after, and status. Server-side bindings contain DUID, IAID, lifetime, expiration, and last-seen. Dynamic pools receive an expiration time. | Received prefixes populate dynamic pools; addresses can be built from pools and old prefixes are advertised with zero lifetime when removed. | Client supports custom DUID, interface-based DUID, custom IA_PD ID, and custom IA_NA ID. Server bindings use DUID plus IAID. | Client states include searching, requesting, bound, renewing, rebinding, stopping. renew attempts renewal and then reinitializes if renewal fails; release explicitly releases and restarts. | Status and scripts expose whether PD is valid and the current prefix value. |
| Cisco IOS XE | DUID is stable and formed from the lowest-numbered interface MAC address. Different IAIDs with the same DUID are treated as different clients. | General prefixes can be learned by a DHCPv6 client and then referenced by interfaces. | DUID stability and IAID separation are documented. | Four-message exchange is default; rapid commit can be enabled. Examples expose IA_PD T1/T2 in show output. | Operational commands show client state, delegated prefix, DNS/domain options, and general prefix state. |
| Juniper Junos | Lease time is used to set address/prefix lifetimes and Renew/Rebind timers. Multiple IA_NA/IA_PD requests can have independent lease timers. | Subscriber delegated prefix preservation can preserve a prefix after logout and allocate the same delegated prefix after login. | DHCPv6 client identifier DUID type is configurable; subscriber systems track bindings and leases. | Junos documentation prefers a single Solicit containing both IA_NA and IA_PD in some subscriber cases. | Operational commands expose DUIDs, lease timers, and binding state. |
| VyOS | Current VyOS uses generated configuration around DHCPv6 client behavior; historical discussions and bugs show WIDE dhcp6c-style PD, sla-id, and downstream interface rendering. | PD depends on interface readiness; PPPoE and downstream interface timing have been recurring design concerns. | WIDE-style DUID files and IA_PD configuration appear in user-visible logs and generated behavior. | The practical lesson is to avoid blind daemon restarts around interface bring-up and to keep identity stable. | PD rendering bugs tend to surface as wrong downstream prefix length or missing downstream address. |
| dnsmasq / WIDE DHCPv6 | dnsmasq is useful for DHCPv6 server/RA and DUID control, but it is not the right source of truth for WAN PD client state in routerd. WIDE/KAME dhcp6c is relevant for client PD. | dnsmasq lease data is server-side; WIDE/KAME client state depends on DUID file, config, and live daemon state. | dnsmasq can configure server DUID; WIDE/KAME has a client DUID file. | Keep dnsmasq for LAN services and do not make it responsible for WAN PD acquisition. | LAN DHCP/RA should react to routerd-observed PD state. |
Design Conclusions for routerd
Adopt:
- Store a structured PD lease record in routerd state: resource name, interface, client implementation, DUID, IAID, server DUID when observable, current prefix, previous prefix, preferred lifetime, valid lifetime, T1, T2, last observed time, last missing time, and acquisition state.
- Feed a valid previous prefix to the OS DHCPv6 client as a prefix hint. This is useful for home gateways that remember a DUID/IAID/prefix tuple, and it degrades to an ordinary Solicit when the upstream ignores the hint.
- Treat DUID and IAID as first-class desired state. Render them explicitly for
systemd-networkd and KAME
dhcp6cwhere possible, and warn when observed identity differs from the profile expectation. - Keep no-release behavior explicit. For FreeBSD/KAME, continue using
-nand SIGUSR1 for required restarts. For systemd-networkd, renderSendRelease=noif the target systemd version supports it and document fallback behavior. - Add a
renewoperation abstraction that asks the owning OS client to renew when the OS has a safe command for it. It should not synthesize DHCPv6 packets in the first implementation. - Expose PD lifecycle through the control API: current state, timers, last packet-level transition if known, and warnings. This mirrors RouterOS, Cisco, Junos, and OpenWrt operational visibility.
- Keep the existing convergence timeout, but make it secondary to real preferred/valid lifetimes once those are observable.
- When downstream prefixes disappear, deprecate or withdraw LAN-side RA/DHCPv6 data intentionally rather than leaving stale service state. MikroTik's "advertise old prefix with zero lifetime" behavior is a good model for the future RA renderer.
- Keep firewall rules broad enough for valid DHCPv6 replies: allow UDP destination port 546 for the client and do not require source port 547.
Defer:
- Do not generate Renew/Rebind packets inside routerd yet. That requires a full DHCPv6 client state machine, server-id handling, retransmission timers, authentication/reconfigure behavior, and careful interaction with OS clients.
- Do not make dnsmasq responsible for WAN PD state. It remains a LAN DHCP/RA/DNS component.
- Do not assume every home gateway honors prefix hints or remembers prefixes in a standards-friendly way. Profiles should express quirks, but the default model must still follow DHCPv6 lifetimes and the OS client state machine.
- Do not implement commercial-router-style subscriber accounting until routerd has a stable lease record and control API for one router's own PD lifecycle.
Backlog Items
- Add
routerctl show pdwith DUID, IAID, prefix, lifetimes, T1/T2, last observed time, last missing time, client status, and warnings. - Extend the internal
PDLeasestate model with server DUID, preferred lifetime, valid lifetime, T1, T2, and acquisition state when each OS client exposes them. - Add OS-specific renew hooks:
- systemd-networkd: investigate whether DBus,
networkctl, or service reload can request renewal without releasing state. - FreeBSD/KAME
dhcp6c: investigate control socket support and safe signal behavior beyond SIGUSR1/no-release restart.
- systemd-networkd: investigate whether DBus,
- Add explicit
releasePolicyorsendReleaseconfiguration with conservative defaults for NTT/home-gateway profiles. - Add profile fields for prefix hint, IA_NA/IA_PD combined request preference, DUID type, IAID, no-release behavior, convergence timeout, and firewall reply matching.
- Add downstream deprecation behavior for removed PD prefixes before removing addresses and RA/DHCPv6 service data.