Files
nixfiles/nixos/boxes/home/routing-common/kea.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

225 lines
6.4 KiB
Nix

index: { lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) mkForce concatStringsSep;
inherit (lib.my) net netbootKeaClientClasses;
inherit (lib.my.c.home) domain searchDomains prefixes vips hiMTU;
dns-servers = [
{
ip-address = net.cidr.host 1 prefixes.core.v4;
port = 5353;
}
{
ip-address = net.cidr.host 2 prefixes.core.v4;
port = 5353;
}
];
in
{
users = with lib.my.c.ids; {
users.kea= {
isSystemUser = true;
uid = uids.kea;
group = "kea";
};
groups.kea.gid = gids.kea;
};
systemd.services = {
kea-dhcp4-server = {
unitConfig.StartLimitIntervalSec = 0;
preStart = ''
${config.systemd.package}/lib/systemd/systemd-networkd-wait-online \
--interface=lan-hi:routable --interface=lan-lo:routable \
--interface=lan-untrusted:routable --ipv4 --timeout=60
'';
serviceConfig = {
Restart = "on-failure";
RestartSec = 5;
DynamicUser = mkForce false;
};
};
kea-dhcp-ddns-server.serviceConfig.DynamicUser = mkForce false;
};
services = {
kea = {
dhcp4 = {
enable = true;
settings = {
interfaces-config = {
interfaces = [
"lan-hi/${assignments.hi.ipv4.address}"
"lan-lo/${assignments.lo.ipv4.address}"
"lan-untrusted/${assignments.untrusted.ipv4.address}"
];
};
lease-database = {
type = "memfile";
persist = true;
name = "/var/lib/kea/dhcp.leases";
};
option-data = [
{
name = "domain-name";
data = domain;
}
{
name = "domain-search";
data = concatStringsSep ", " searchDomains;
always-send = true;
}
];
client-classes = netbootKeaClientClasses {
tftpIP = allAssignments.river.lo.ipv4.address;
hostname = "boot.${domain}";
systems = {
sfh = "52:54:00:a5:7e:93";
castle = "c8:7f:54:6e:17:0f";
};
};
subnet4 = [
{
id = 1;
subnet = prefixes.hi.v4;
interface = "lan-hi";
option-data = [
{
name = "routers";
data = vips.hi.v4;
}
{
name = "domain-name-servers";
# VRRP VIP so DNS follows the master and clients never hit a dead router
data = vips.hi.v4;
}
{
name = "interface-mtu";
data = toString hiMTU;
}
];
pools = [
{
pool = if index == 0
then "192.168.68.120 - 192.168.69.255"
else "192.168.70.0 - 192.168.71.240";
}
];
reservations = [
{
# castle
hw-address = "24:8a:07:a8:fe:3a";
ip-address = net.cidr.host 40 prefixes.hi.v4;
}
];
}
{
id = 2;
subnet = prefixes.lo.v4;
interface = "lan-lo";
option-data = [
{
name = "routers";
data = vips.lo.v4;
}
{
name = "domain-name-servers";
# VRRP VIP so DNS follows the master and clients never hit a dead router
data = vips.lo.v4;
}
];
pools = [
{
pool = if index == 0
then "192.168.72.120 - 192.168.75.255"
else "192.168.76.0 - 192.168.79.240";
}
];
reservations = [
{
# castle
hw-address = "24:8a:07:a8:fe:3a";
ip-address = net.cidr.host 40 prefixes.lo.v4;
}
{
# avr
hw-address = "8c:a9:6f:30:03:6b";
ip-address = net.cidr.host 41 prefixes.lo.v4;
}
{
# tv
hw-address = "00:a1:59:b8:4d:86";
ip-address = net.cidr.host 42 prefixes.lo.v4;
}
{
# android tv
hw-address = "b8:7b:d4:95:c6:74";
ip-address = net.cidr.host 43 prefixes.lo.v4;
}
{
# hass-panel
hw-address = "80:30:49:cd:d7:51";
ip-address = net.cidr.host 44 prefixes.lo.v4;
}
{
# reolink-living-room
hw-address = "ec:71:db:30:69:a4";
ip-address = net.cidr.host 45 prefixes.lo.v4;
}
{
# nixlight
hw-address = "00:4b:12:3b:d3:14";
ip-address = net.cidr.host 46 prefixes.lo.v4;
}
];
}
{
id = 3;
subnet = prefixes.untrusted.v4;
interface = "lan-untrusted";
option-data = [
{
name = "routers";
data = vips.untrusted.v4;
}
{
name = "domain-name-servers";
data = "1.1.1.1, 1.0.0.1";
}
];
pools = [
{
pool = if index == 0
then "192.168.80.10 - 192.168.80.127"
else "192.168.80.128 - 192.168.80.250";
}
];
}
];
ddns-send-updates = true;
ddns-replace-client-name = "when-not-present";
ddns-qualifying-suffix = "dyn.${domain}";
ddns-generated-prefix = "ip";
ddns-update-on-renew = true;
dhcp-ddns.enable-updates = true;
};
};
dhcp-ddns = {
enable = true;
settings = {
forward-ddns.ddns-domains = [
{
name = "dyn.${domain}.";
inherit dns-servers;
}
];
};
};
};
};
}