Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d1562d150 | |||
| 49de78d47f | |||
| 832d0b5542 |
@@ -71,8 +71,10 @@ in
|
||||
|
||||
python3Packages.python-lsp-server
|
||||
nil # nix language server
|
||||
nixd # another nix language server
|
||||
zls # zig language server
|
||||
rust-analyzer
|
||||
pyright
|
||||
|
||||
cowsay
|
||||
fortune
|
||||
|
||||
+9
-1
@@ -312,9 +312,14 @@ rec {
|
||||
lo = 110;
|
||||
untrusted = 120;
|
||||
wan = 130;
|
||||
wan-pon = 131;
|
||||
|
||||
# Digiweb delivers the ISP VLAN (pon-isp, 10) single-tagged at the ONT alongside the ONT's
|
||||
# own untagged management traffic. The switch feeding river tags the untagged ONT port as
|
||||
# wan-pon-ont (140) and swaps ingress VLAN 10 to wan-pon-isp (141), so river sees both
|
||||
# single-tagged and never has to touch VLAN 10 itself (PPPoE runs on wan-pon-isp).
|
||||
pon-isp = 10;
|
||||
wan-pon-ont = 140;
|
||||
wan-pon-isp = 141;
|
||||
};
|
||||
hiMTU = 9000;
|
||||
routers = [
|
||||
@@ -330,6 +335,9 @@ rec {
|
||||
modem = {
|
||||
v4 = "192.168.0.0/24";
|
||||
};
|
||||
ont = {
|
||||
v4 = "192.168.100.0/24";
|
||||
};
|
||||
all = {
|
||||
v4 = "192.168.64.0/18";
|
||||
v6 = "2a0e:97c0:4d0::/60";
|
||||
|
||||
@@ -9,16 +9,19 @@
|
||||
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (builtins) elemAt;
|
||||
inherit (lib) mkForce mkMerge mkIf;
|
||||
inherit (lib.my) networkdAssignment mkVLAN;
|
||||
inherit (lib) mkForce mkMerge;
|
||||
inherit (lib.my) net networkdAssignment mkVLAN;
|
||||
inherit (lib.my.c) networkd;
|
||||
inherit (lib.my.c.home) vlans domain prefixes roceBootModules routersPubV4;
|
||||
|
||||
# Digiweb currently delivers the ISP VLAN (pon-isp, 10) single-tagged, so PPPoE runs on a
|
||||
# VLAN 10 sitting directly on the physical WAN link. Flip this to true to nest it back
|
||||
# inside the wan-pon (131) transport VLAN — double-stacking also needs QinQ (tag-stacking)
|
||||
# on the switch feeding the ONT, or the BRAS never answers PADI.
|
||||
wanStacked = false;
|
||||
# river reaches the ONT over its 100G `lan` uplink to the dave switch (which downlinks to
|
||||
# jim, where the ONT's fibre lands). Digiweb delivers the ISP VLAN (pon-isp, 10) single-tagged
|
||||
# at the ONT alongside the ONT's untagged management traffic. The switch tags the ONT's
|
||||
# untagged port as wan-pon-ont (140) and swaps the ISP's VLAN 10 to wan-pon-isp (141) on
|
||||
# ingress, so both arrive at river single-tagged and PPPoE runs directly on wan-pon-isp.
|
||||
# river takes .100 in the ONT's /24 (matching stream's modem-mgmt .100) to reach its web
|
||||
# UI at 192.168.100.1.
|
||||
ontV4 = net.cidr.host 100 prefixes.ont.v4;
|
||||
|
||||
# river is routing-common index 0; the Digiweb static IP we request via IPCP
|
||||
pubV4 = elemAt routersPubV4 0;
|
||||
@@ -93,7 +96,7 @@
|
||||
enable = true;
|
||||
# Password is shared across all Digiweb customers, so no need for a secret
|
||||
config = ''
|
||||
plugin pppoe.so wan-vlan-inner
|
||||
plugin pppoe.so wan-pon-isp
|
||||
name "digiweb@nga.digiweb.ie"
|
||||
password "digiweb"
|
||||
# request our static IP as the local address in IPCP (local:remote, remote left open)
|
||||
@@ -115,9 +118,10 @@
|
||||
};
|
||||
};
|
||||
|
||||
# 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.
|
||||
# PPPoE WAN (Digiweb): pppd owns the `wan` interface on top of wan-pon-isp (the switch's
|
||||
# swap of the ISP's 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 = {
|
||||
@@ -143,8 +147,8 @@
|
||||
|
||||
systemd.network = {
|
||||
netdevs = mkMerge [
|
||||
(mkIf wanStacked (mkVLAN "wan-vlan-outer" vlans.wan-pon))
|
||||
(mkVLAN "wan-vlan-inner" vlans.pon-isp)
|
||||
(mkVLAN "wan-pon-ont" vlans.wan-pon-ont)
|
||||
(mkVLAN "wan-pon-isp" vlans.wan-pon-isp)
|
||||
];
|
||||
|
||||
links = {
|
||||
@@ -176,24 +180,26 @@
|
||||
|
||||
networks = {
|
||||
"55-lan" = {
|
||||
# outer transport VLAN when stacked, otherwise the ISP VLAN directly on lan
|
||||
vlan = [ (if wanStacked then "wan-vlan-outer" else "wan-vlan-inner") ];
|
||||
# both WAN VLANs arrive single-tagged on the 100G uplink to dave
|
||||
vlan = [ "wan-pon-ont" "wan-pon-isp" ];
|
||||
};
|
||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||
"60-lan-hi".networkConfig.KeepConfiguration = "static";
|
||||
|
||||
"70-wan-vlan-outer" = mkIf wanStacked {
|
||||
matchConfig.Name = "wan-vlan-outer";
|
||||
vlan = [ "wan-vlan-inner" ];
|
||||
networkConfig = networkd.noL3;
|
||||
# baby jumbo: carries the inner VLAN's frames, whose 4B tag counts as payload
|
||||
# at this layer, so it needs 1512 (inner's 1508B payload + the inner 802.1Q tag)
|
||||
linkConfig.MTUBytes = "1512";
|
||||
# ONT management: the switch tags the ONT's untagged port as wan-pon-ont, so give
|
||||
# ourselves an address in its /24 to reach the ONT's web UI at 192.168.100.1.
|
||||
"70-wan-pon-ont" = {
|
||||
matchConfig.Name = "wan-pon-ont";
|
||||
address = [ "${ontV4}/24" ];
|
||||
linkConfig = {
|
||||
RequiredForOnline = "no";
|
||||
MTUBytes = "1500";
|
||||
};
|
||||
# pppd attaches PPPoE to this; just needs to be up with no L3. Hangs off
|
||||
# wan-vlan-outer when stacked, otherwise directly off lan (see "55-lan").
|
||||
"71-wan-vlan-inner" = {
|
||||
matchConfig.Name = "wan-vlan-inner";
|
||||
};
|
||||
# pppd attaches PPPoE to this; just needs to be up with no L3. Carries the ISP's
|
||||
# VLAN 10, swapped to wan-pon-isp by the switch (see "55-lan").
|
||||
"71-wan-pon-isp" = {
|
||||
matchConfig.Name = "wan-pon-isp";
|
||||
linkConfig = {
|
||||
RequiredForOnline = "no";
|
||||
# baby jumbo: PPPoE's 8B overhead leaves a clean 1500 on ppp
|
||||
|
||||
@@ -11,12 +11,6 @@ in
|
||||
{
|
||||
nixos.systems."${name}" = {
|
||||
assignments = {
|
||||
modem = {
|
||||
ipv4 = {
|
||||
address = net.cidr.host (254 - index) prefixes.modem.v4;
|
||||
gateway = null;
|
||||
};
|
||||
};
|
||||
core = {
|
||||
name = "${name}-core";
|
||||
inherit domain;
|
||||
@@ -100,9 +94,11 @@ in
|
||||
|
||||
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||
let
|
||||
inherit (lib) mkIf mkMerge mkForce;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
inherit (lib) mkIf mkMerge mkForce optionalString concatStringsSep;
|
||||
inherit (lib.my) mkOpt' networkdAssignment;
|
||||
inherit (lib.my.c) networkd;
|
||||
|
||||
cfg = config.my.homeRouter;
|
||||
in
|
||||
{
|
||||
imports = map (m: import m index) [
|
||||
@@ -112,6 +108,20 @@ in
|
||||
./kea.nix
|
||||
];
|
||||
|
||||
# Per-box WAN-management specifics: the Virgin Media modem on stream lives on the `wan`
|
||||
# interface itself, whereas river's ONT sits on its own interface. Declared as options the
|
||||
# box sets so routing-common itself carries no modem/ONT knowledge.
|
||||
options.my.homeRouter = with lib.types; {
|
||||
dns.wanSkipBroadcasts = mkOpt' (listOf str) [ ] ''
|
||||
Broadcast addresses to exclude when auto-selecting the router's own `wan` A record,
|
||||
for extra static subnets that share the `wan` interface.
|
||||
'';
|
||||
firewall.untrustedRejectV4 = mkOpt' (listOf str) [ ] ''
|
||||
IPv4 prefixes untrusted clients must be explicitly rejected from reaching. Only needed
|
||||
for subnets sharing the `wan` interface, since `wan` egress is otherwise accepted.
|
||||
'';
|
||||
};
|
||||
|
||||
config = {
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
@@ -338,7 +348,7 @@ in
|
||||
return
|
||||
}
|
||||
chain filter-untrusted {
|
||||
ip daddr ${prefixes.modem.v4} reject
|
||||
${optionalString (cfg.firewall.untrustedRejectV4 != [ ]) "ip daddr { ${concatStringsSep ", " cfg.firewall.untrustedRejectV4} } reject"}
|
||||
oifname wan accept
|
||||
return
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ in
|
||||
${name} IN LUA ${lib.my.dns.ifaceA {
|
||||
inherit pkgs;
|
||||
iface = "wan";
|
||||
skipBroadcasts = [ (lib.my.netBroadcast prefixes.modem.v4) ];
|
||||
skipBroadcasts = config.my.homeRouter.dns.wanSkipBroadcasts;
|
||||
}}
|
||||
${otherName} IN LUA ${lib.my.dns.lookupIP {
|
||||
inherit pkgs;
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
nixpkgs = "mine";
|
||||
home-manager = "mine";
|
||||
|
||||
configuration = { lib, pkgs, config, assignments, ... }:
|
||||
configuration = { lib, pkgs, config, ... }:
|
||||
let
|
||||
inherit (lib) mkMerge;
|
||||
inherit (lib.my) networkdAssignment;
|
||||
inherit (lib.my) net;
|
||||
inherit (lib.my.c) networkd;
|
||||
inherit (lib.my.c.home) prefixes;
|
||||
|
||||
# Static address on the Virgin Media modem's management subnet. Kept as a plain interface
|
||||
# address (not a network assignment) since it's local to this box's WAN uplink.
|
||||
modemV4 = net.cidr.host 100 prefixes.modem.v4;
|
||||
in
|
||||
{
|
||||
imports = [ ./routing-common/mstpd.nix ];
|
||||
@@ -77,7 +82,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
# wan carries a permanent static modem-management address (assignments.modem)
|
||||
# wan carries a permanent static modem-management address (modemV4)
|
||||
# alongside the DHCP public IP, so wait-online@wan reports "online" as soon as
|
||||
# the static address is up - before the DHCP lease arrives. ipsec's left= is the
|
||||
# public IP, so gating on wait-online lets it start unoriented and never connect.
|
||||
@@ -190,13 +195,16 @@
|
||||
CompensationMode=none
|
||||
'';
|
||||
};
|
||||
"50-wan" = mkMerge [
|
||||
(networkdAssignment "wan" assignments.modem)
|
||||
{
|
||||
"50-wan" = {
|
||||
matchConfig.Name = "wan";
|
||||
# Static modem-management address alongside the DHCP public lease. It has no
|
||||
# gateway, so the wan-wait-online gate keys off the DHCP default route instead.
|
||||
address = [ "${modemV4}/24" ];
|
||||
DHCP = "ipv4";
|
||||
dns = [ "127.0.0.1" "::1" ];
|
||||
dhcpV4Config.UseDNS = false;
|
||||
# IPv4-only WAN (public IPv6 arrives over the tunnel, not this link).
|
||||
networkConfig.IPv6AcceptRA = false;
|
||||
|
||||
qdiscConfig = {
|
||||
Parent = "ingress";
|
||||
@@ -208,8 +216,7 @@
|
||||
Bandwidth=48M
|
||||
RTTSec=50ms
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -218,6 +225,12 @@
|
||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
|
||||
};
|
||||
server.enable = true;
|
||||
# The modem's management subnet shares the `wan` interface: skip its address when
|
||||
# picking our own wan A record, and reject untrusted clients from reaching it.
|
||||
homeRouter = {
|
||||
dns.wanSkipBroadcasts = [ (lib.my.netBroadcast prefixes.modem.v4) ];
|
||||
firewall.untrustedRejectV4 = [ prefixes.modem.v4 ];
|
||||
};
|
||||
# deploy.node.hostname = "192.168.68.2";
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user