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.
87 lines
2.3 KiB
Nix
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" ];
|
|
};
|
|
};
|
|
};
|
|
}
|