Files
nixos-router/modules/router/lib.nix
T
nilsandClaude Sonnet 5.5 6038367252 Add IPv6 prefix delegation and PPPoE uplink
IPv6: router.wan.ipv6.enable mirrors the existing uplink config (DHCPv6 with
WithoutRA=solicit, delegated prefix). The ISP delegates a single /64, so
exactly one LAN may claim it via router.lans.<name>.ipv6.pd (asserted); other
LANs stay IPv4-only. SLAAC only, no NAT66.

PPPoE: router.wan.ipv4.type = "pppoe" runs pppd on an optional VLAN of the
uplink, with the password read from a file at service start (never the Nix
store). NAT, forwarding and MSS clamping (MTU 1492) use the derived PPP
device; native IPv6 stays on the untagged link.

Tests: basic.nix is parameterised by uplink type; the pppoe variant uses a
fake ISP with pppoe-server (VLAN 7, CHAP). Both variants cover IPv6 PD, SLAAC,
routing without NAT and WAN-deny. Assertion tests gain PD/PPPoE cases and a
full-system evaluation step.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 16:29:52 +01:00

52 lines
1.5 KiB
Nix

# Small IPv4 helpers (addresses are plain 32-bit integers inside Nix).
# Parsing returns null on malformed input so callers can turn that into an
# assertion message instead of an evaluation crash.
{ lib }:
let
pow2 = n: if n == 0 then 1 else 2 * pow2 (n - 1);
in
rec {
ipv4 = rec {
parse =
s:
let
m = builtins.match "([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})" s;
octets = map lib.toInt m;
in
if m == null || lib.any (o: o > 255) octets then
null
else
lib.foldl' (acc: o: acc * 256 + o) 0 octets;
format =
n:
lib.concatMapStringsSep "." (shift: toString (lib.mod (n / pow2 shift) 256)) [
24
16
8
0
];
netMask = prefixLength: 4294967295 - (pow2 (32 - prefixLength) - 1);
# Network address (integer) of `address` under `prefixLength`.
networkOf = address: prefixLength: builtins.bitAnd address (netMask prefixLength);
# "192.168.10.1" /24 -> "192.168.10.0/24"
cidr =
address: prefixLength:
"${format (networkOf (parse address) prefixLength)}/${toString prefixLength}";
inSubnet =
{ address, prefixLength }: ip: networkOf ip prefixLength == networkOf address prefixLength;
# Whether two subnets share any address (one contains the other's network).
overlaps =
a: b:
let
p = lib.min a.prefixLength b.prefixLength;
in
networkOf a.address p == networkOf b.address p;
};
}