Skip to main content

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:

  • IPv6PrefixDelegation records observed prefix state under ipv6PrefixDelegation.<name>.* in the routerd state store.
  • The last known prefix is retained when no current downstream prefix is visible.
  • IPv6PrefixDelegation.spec.convergenceTimeout keeps 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 KAME dhcp6c retransmission 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 dhcp6c has already placed on the downstream interface, then adds the configured stable suffix address as a secondary address.
  • FreeBSD dhcp6c is 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.hintFromState defaults to true. When routerd has a last observed prefix whose valid lifetime has not elapsed, it feeds that prefix back to systemd-networkd or KAME dhcp6c as 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 /60 PD 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:

Observed Patterns

ImplementationLifetime and lease stateRestart behaviorIdentity handlingRenew / Rebind / Solicit / Release policyLoss handling
OpenWrt odhcp6cExposes 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 odhcpdServer-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-networkdOwns 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 dhcp6cStores 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 / OPNsenseBoth 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 RouterOSClient 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 XEDUID 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 JunosLease 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.
VyOSCurrent 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 DHCPv6dnsmasq 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 dhcp6c where possible, and warn when observed identity differs from the profile expectation.
  • Keep no-release behavior explicit. For FreeBSD/KAME, continue using -n and SIGUSR1 for required restarts. For systemd-networkd, render SendRelease=no if the target systemd version supports it and document fallback behavior.
  • Add a renew operation 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 pd with DUID, IAID, prefix, lifetimes, T1/T2, last observed time, last missing time, client status, and warnings.
  • Extend the internal PDLease state 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.
  • Add explicit releasePolicy or sendRelease configuration 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.