From e63cee7b09e781ce8d8e1dab1b0015354655089b Mon Sep 17 00:00:00 2001 From: Jack O'Sullivan Date: Sun, 19 Jul 2026 22:37:05 +0100 Subject: [PATCH] nixos/home: Migrate river WAN to PPPoE over Digiweb river's WAN moves to a PPPoE session (Digiweb, over VLAN 10 with baby-jumbo 1508 MTU) driven by pppd, with its ip-up/ip-down hooks toggling a new wan-online.target. stream keeps its DHCP WAN, so the existing 50-wan / 50-wan-ifb networkd config moves out of routing-common into stream, and the shared consumers (ipsec, ipv6-clear-default-route) now attach to wan-online.target instead of depending on systemd-networkd-wait-online@wan directly. Co-Authored-By: Claude Opus 4.8 --- lib/constants.nix | 3 + nixos/boxes/home/palace/vms/river.nix | 93 ++++++++++++++++++++- nixos/boxes/home/routing-common/default.nix | 58 +++---------- nixos/boxes/home/stream.nix | 49 ++++++++++- 4 files changed, 152 insertions(+), 51 deletions(-) diff --git a/lib/constants.nix b/lib/constants.nix index 21e6b04..019d936 100644 --- a/lib/constants.nix +++ b/lib/constants.nix @@ -312,6 +312,9 @@ rec { lo = 110; untrusted = 120; wan = 130; + wan-pon = 131; + + pon-isp = 10; }; hiMTU = 9000; routers = [ diff --git a/nixos/boxes/home/palace/vms/river.nix b/nixos/boxes/home/palace/vms/river.nix index d3144e7..32d34d1 100644 --- a/nixos/boxes/home/palace/vms/river.nix +++ b/nixos/boxes/home/palace/vms/river.nix @@ -8,6 +8,7 @@ configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }: let + inherit (lib) mkForce mkMerge; inherit (lib.my) networkdAssignment mkVLAN; inherit (lib.my.c) networkd; inherit (lib.my.c.home) vlans domain prefixes roceBootModules; @@ -71,11 +72,72 @@ dmeventd.enable = true; }; fstrim.enable = true; + + # TODO: re-enable once scheduling is tested + networkd-dispatcher.enable = mkForce false; + + pppd = { + enable = true; + peers.digiweb = { + autostart = true; + enable = true; + # Password is shared across all Digiweb customers, so no need for a secret + config = '' + plugin pppoe.so wan-vlan-inner + name "digiweb@nga.digiweb.ie" + password "digiweb" + noipdefault + # no usepeerdns: we ignore Digiweb's resolvers and use the local recursive resolver + lcp-echo-interval 1 + lcp-echo-failure 4 + noauth + persist + maxfail 0 + holdoff 5 + mtu 1500 + mru 1500 + noaccomp + default-asyncmap + ifname wan + ''; + }; + }; + }; + + # PPPoE WAN (Digiweb): pppd owns the `wan` interface on top of VLAN 10, and its + # ip-up/ip-down hooks toggle the shared wan-online.target. Nothing else Wants the + # target, so it stays inactive until the link is actually up. + systemd.targets.wan-online.unitConfig.DefaultDependencies = false; + + environment.etc = { + ppp-up = { + target = "ppp/ip-up"; + mode = "0755"; + text = '' + #!${pkgs.runtimeShell} + ${pkgs.iproute2}/bin/ip route add default dev wan scope link metric 100 + ${config.systemd.package}/bin/systemctl --no-block start wan-online.target + ''; + }; + ppp-down = { + target = "ppp/ip-down"; + mode = "0755"; + text = '' + #!${pkgs.runtimeShell} + ${config.systemd.package}/bin/systemctl --no-block stop wan-online.target + ${pkgs.iproute2}/bin/ip route del default dev wan scope link metric 100 + ''; + }; }; systemd.network = { + netdevs = mkMerge [ + (mkVLAN "wan-vlan-outer" vlans.wan-pon) + (mkVLAN "wan-vlan-inner" vlans.pon-isp) + ]; + links = { - "10-wan" = { + "10-wan-old" = { matchConfig = { # Matching against MAC address seems to break VLAN interfaces # (since they share the same MAC address) @@ -83,7 +145,7 @@ PermanentMACAddress = "e0:d5:5e:68:0c:6e"; }; linkConfig = { - Name = "wan"; + Name = "wan-old"; RxBufferSize = 4096; TxBufferSize = 4096; }; @@ -101,8 +163,31 @@ }; }; - # So we don't drop the IP we use to connect to NVMe-oF! - networks."60-lan-hi".networkConfig.KeepConfiguration = "static"; + networks = { + "55-lan" = { + vlan = [ "wan-vlan-outer" ]; + }; + # So we don't drop the IP we use to connect to NVMe-oF! + "60-lan-hi".networkConfig.KeepConfiguration = "static"; + + "70-wan-vlan-outer" = { + matchConfig.Name = "wan-vlan-outer"; + vlan = [ "wan-vlan-inner" ]; + networkConfig = networkd.noL3; + # baby jumbo: 1508 so the inner VLAN below can also carry it + linkConfig.MTUBytes = "1508"; + }; + # pppd attaches PPPoE to this; just needs to be up with no L3 + "71-wan-vlan-inner" = { + matchConfig.Name = "wan-vlan-inner"; + linkConfig = { + RequiredForOnline = "no"; + # baby jumbo: PPPoE's 8B overhead leaves a clean 1500 on ppp + MTUBytes = "1508"; + }; + networkConfig = networkd.noL3; + }; + }; }; my = { diff --git a/nixos/boxes/home/routing-common/default.nix b/nixos/boxes/home/routing-common/default.nix index 0066a25..db228fd 100644 --- a/nixos/boxes/home/routing-common/default.nix +++ b/nixos/boxes/home/routing-common/default.nix @@ -152,26 +152,29 @@ in networking = { inherit domain; }; - systemd.services = - let - waitOnline = "systemd-networkd-wait-online@wan.service"; - in - { + # Uniform "WAN is up" gate. Consumers attach to this target (via wantedBy + + # partOf) rather than depending on it, so it is never pulled in / prematurely + # activated. Each box wires up how the target actually gets reached: stream + # gates it on networkd's wait-online, river drives it from the pppd hooks. + systemd.targets.wan-online.description = "WAN is online"; + + systemd.services = { ipsec = { - after = [ waitOnline ]; - requires = [ waitOnline ]; + after = [ "wan-online.target" ]; + wantedBy = [ "wan-online.target" ]; + partOf = [ "wan-online.target" ]; }; ipv6-clear-default-route = { description = "Clear IPv6 RA default route"; - after = [ waitOnline ]; - requires = [ waitOnline ]; + after = [ "wan-online.target" ]; + wantedBy = [ "wan-online.target" ]; + partOf = [ "wan-online.target" ]; script = '' # Seems like we can sometimes pick up a default route somehow... ${pkgs.iproute2}/bin/ip -6 route del default via fe80::1 || true ''; serviceConfig.Type = "oneshot"; - wantedBy = [ "multi-user.target" ]; }; }; @@ -220,41 +223,6 @@ in in mkMerge [ { - "50-wan-ifb" = { - matchConfig.Name = "wan-ifb"; - networkConfig = networkd.noL3; - extraConfig = '' - [CAKE] - Bandwidth=490M - RTTSec=50ms - PriorityQueueingPreset=besteffort - # DOCSIS preset - OverheadBytes=18 - MPUBytes=64 - CompensationMode=none - ''; - }; - "50-wan" = mkMerge [ - (networkdAssignment "wan" assignments.modem) - { - matchConfig.Name = "wan"; - DHCP = "ipv4"; - dns = [ "127.0.0.1" "::1" ]; - dhcpV4Config.UseDNS = false; - - qdiscConfig = { - Parent = "ingress"; - Handle = "0xffff"; - }; - extraConfig = '' - [CAKE] - Parent=root - Bandwidth=48M - RTTSec=50ms - ''; - } - ]; - "55-lan" = { matchConfig.Name = "lan"; vlan = [ "lan-hi" "lan-lo" "lan-untrusted" "wan-tunnel" ]; diff --git a/nixos/boxes/home/stream.nix b/nixos/boxes/home/stream.nix index ba88b7e..0ec849b 100644 --- a/nixos/boxes/home/stream.nix +++ b/nixos/boxes/home/stream.nix @@ -6,9 +6,11 @@ nixpkgs = "mine"; home-manager = "mine"; - configuration = { lib, pkgs, config, ... }: + configuration = { lib, pkgs, config, assignments, ... }: let - inherit (lib); + inherit (lib) mkMerge; + inherit (lib.my) networkdAssignment; + inherit (lib.my.c) networkd; in { imports = [ ./routing-common/mstpd.nix ]; @@ -75,6 +77,14 @@ }; }; + # WAN is a plain networkd-managed DHCP link here; gate the shared wan-online + # target on networkd reporting it online. + systemd.targets.wan-online = { + requires = [ "systemd-networkd-wait-online@wan.service" ]; + after = [ "systemd-networkd-wait-online@wan.service" ]; + wantedBy = [ "multi-user.target" ]; + }; + systemd.network = { netdevs = { "25-lan" = { @@ -147,6 +157,41 @@ matchConfig.Name = "lan-dave"; networkConfig.Bridge = "lan"; }; + + "50-wan-ifb" = { + matchConfig.Name = "wan-ifb"; + networkConfig = networkd.noL3; + extraConfig = '' + [CAKE] + Bandwidth=490M + RTTSec=50ms + PriorityQueueingPreset=besteffort + # DOCSIS preset + OverheadBytes=18 + MPUBytes=64 + CompensationMode=none + ''; + }; + "50-wan" = mkMerge [ + (networkdAssignment "wan" assignments.modem) + { + matchConfig.Name = "wan"; + DHCP = "ipv4"; + dns = [ "127.0.0.1" "::1" ]; + dhcpV4Config.UseDNS = false; + + qdiscConfig = { + Parent = "ingress"; + Handle = "0xffff"; + }; + extraConfig = '' + [CAKE] + Parent=root + Bandwidth=48M + RTTSec=50ms + ''; + } + ]; }; };