Add a static ULA per LAN, announced via SLAAC and RDNSS

lans.<n>.ipv6.address (a ULA /64) is assigned to the LAN, announced
with an explicit prefix (networkd only announces the delegated prefix
when prefix delegation is on) and advertised as RDNSS; Unbound listens
and answers on it. Checked at build time: valid IPv6 CIDR, /64, fc00::/7.

Covered by the onestick VM test (client SLAAC address, ping and DNS over
the ULA, RA captured on the wire), assertion cases and a unit test for
the /64 prefix helper.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 15:48:47 +01:00
co-authored by Claude Sonnet 5.5
parent 70dddad206
commit c395a43a78
9 changed files with 150 additions and 7 deletions
+21
View File
@@ -43,6 +43,27 @@ let
message = "router: ${where}: device name `${lan.device}` is longer than 15 characters.";
}
]
++ lib.optionals (lan.ipv6.address != null) (
let
m = builtins.match "([0-9a-fA-F:]+)/([0-9]{1,3})" lan.ipv6.address;
in
[
{
assertion = isV6 lan.ipv6.address && v6Cidr lan.ipv6.address && m != null;
message = "router: ${where}.ipv6.address `${lan.ipv6.address}` is not an IPv6 address in CIDR notation.";
}
]
++ lib.optionals (m != null) [
{
assertion = builtins.elemAt m 1 == "64";
message = "router: ${where}.ipv6.address must be a /64 (SLAAC needs one).";
}
{
assertion = builtins.match "[fF][cCdD][0-9a-fA-F]{2}:.*" (builtins.elemAt m 0) != null;
message = "router: ${where}.ipv6.address must be a unique-local address (fc00::/7).";
}
]
)
++ lib.optionals lan.dhcp.enable (
[
{