{ lib, ... }: let ethLink = (name: (mac: { matchConfig = { Type = "ether"; MACAddress = mac; }; linkConfig.Name = name; })); vlanNetdev = (name: (id: { netdevConfig = { Name = name; Kind = "vlan"; }; vlanConfig.Id = id; })); vlanNetwork = (name: (id: { matchConfig.Name = name; # Embed ID directly in IPv4 address for clarity. address = [ "192.168.${toString id}.1/24" ]; })); in { systemd.network = { enable = true; links."10-wan0" = ethLink "wan0" "a8:a1:59:43:95:36"; networks."10-wan0" = { matchConfig.Name = "wan0"; networkConfig.DHCP = "ipv4"; dhcpV4Config = { UseDNS = true; UseDomains = true; }; }; links."15-mgmt0" = ethLink "mgmt0" "a0:36:9f:fa:5d:6c"; networks."15-mgmt0" = { matchConfig.Name = "mgmt0"; address = [ "192.168.0.1/24" ]; vlan = [ "iot" "guest" ]; networkConfig = { DHCP = "no"; Domains = "home"; }; }; # unused interface links."16-mgmt1" = ethLink "mgmt1" "a0:36:9f:fa:5d:6d"; # IoT VLAN. netdevs."25-iot" = vlanNetdev "iot" 10; networks."25-iot" = vlanNetwork "iot" 10; # Guest VLAN. netdevs."30-guest" = vlanNetdev "guest" 20; networks."30-guest" = vlanNetwork "guest" 20; # ignore these interfaces, as they are not used wait-online.ignoredInterfaces = [ "mgmt1" "wlp8s0" ]; }; # don't use systemd-resolved on the router services.resolved.enable = false; networking.hostName = "carmel"; networking.useDHCP = false; networking.firewall = { enable = true; allowPing = true; # If rejectPackets = true, refused packets are rejected rather than dropped (ignored). This # means that an ICMP "port unreachable" error message is sent back to the client (or a TCP RST # packet in case of an existing connection). Rejecting packets makes port scanning somewhat # easier. rejectPackets = false; trustedInterfaces = [ "mgmt0" "iot" "guest" "wg0" ]; logRefusedConnections = true; logRefusedPackets = false; logReversePathDrops = true; interfaces = { "wan0" = { allowedTCPPorts = [ 22 # ssh 51413 # transmission ]; allowedUDPPorts = [ 35947 # wireguard 51413 # transmission ]; }; }; }; networking.nat = { enable = true; externalInterface = "wan0"; internalInterfaces = [ "mgmt0" "guest" "iot" ]; }; networking.private-wireguard.enable = true; my.services.tailscale.enable = true; }