Files
nixfiles/nixos/boxes/home/routing-common/mstpd.nix
T
jackos1998 a04d11423a nixos/home: Separate router storage and uplinks
Give `river` a dedicated RDMA VF and two RSTP trunk attachments.
Keep client services on the router VIPs and carry Digiweb VLANs
over both switch paths.

Set VF trust, multicast reception, bridge MAC and ARP behavior for
local switching. Wait for Kea interface readiness and apply RSTP
costs through `mstpd` despite port-registration and rename races.

Load `palace`'s RAID module in the initrd. Record RouterOS and
RouterBOOT upgrades and validate port, carrier and switch outages.

Document the deployed architecture, interface handoffs and recovery
procedures. Add physical socket maps, NIC capacities and cable
connections alongside the logical topology and failover checks.
2026-11-15 16:23:06 +00:00

87 lines
2.3 KiB
Nix

{ lib, pkgs, ... }:
let
# TODO: Move into nixpkgs
mstpd = pkgs.mstpd.overrideAttrs {
patches = [ ./mstpd.patch ];
# Delete postInstall since it nukes the bridge-stp script we need
postInstall = "";
};
configureRSTP = pkgs.writeShellScript "configure-router-rstp" ''
set -eu
[ "$1" = lan ] || exit 0
event_port="''${2:-}"
${mstpd}/bin/mstpctl addbridge lan
${mstpd}/bin/mstpctl setforcevers lan rstp
for port in lan-dave lan-jim; do
if [ "$event_port" = "$port" ] || [ -e "/sys/class/net/lan/brif/$port" ]; then
case "$port" in
lan-dave) cost=10 ;;
lan-jim) cost=100 ;;
esac
# networkd can report configuration before mstpd has registered the port.
configured=false
for attempt in {1..50}; do
if ${mstpd}/bin/mstpctl setportpathcost lan "$port" "$cost"; then
configured=true
break
fi
${pkgs.coreutils}/bin/sleep 0.2
done
if [ "$configured" = false ]; then
echo "Failed to configure RSTP cost for $port" >&2
exit 1
fi
fi
done
'';
in
{
environment = {
systemPackages = [
# For kernel to call bridge-stp (see ./pkgs/os-specific/linux/kernel/bridge-stp-helper.patch)
mstpd
];
etc = {
"bridge-stp.conf".text = ''
MANAGE_MSTPD=n
MSTP_BRIDGES=lan
config_cmd=${configureRSTP}
'';
};
};
services = {
networkd-dispatcher.rules = {
configure-mstpd = {
onState = [ "configured" ];
script = ''
#!${pkgs.runtimeShell}
# A queued event can retain the interface's name from before a rename.
if [ -d /sys/class/net/lan/bridge ]; then
${configureRSTP} lan "$IFACE"
fi
'';
};
};
};
systemd = {
services = {
mstpd = {
description = "MSTP daemon";
before = [ "network-pre.target" ];
serviceConfig = {
Type = "forking";
ExecStart = "${mstpd}/bin/bridge-stp restart";
ExecReload = "${mstpd}/bin/bridge-stp restart_config";
PIDFile = "/run/mstpd.pid";
Restart = "always";
PrivateTmp = true;
ProtectHome = true;
};
wantedBy = [ "multi-user.target" ];
};
};
};
}