{ lib, config, ... }: let types = { portList = lib.types.listOf lib.types.int; service = lib.types.submodule { options = { tcp = lib.mkOption { type = types.portList; default = [ ]; }; udp = lib.mkOption { type = types.portList; default = [ ]; }; }; }; vlan = lib.types.submodule { options = { id = lib.mkOption { type = lib.types.int; }; staticLeases = lib.mkOption { type = lib.types.attrsOf lib.types.str; default = { }; }; ipv6 = lib.mkEnableOption "ipv6"; }; }; bridge = lib.types.submodule { options = { enable = lib.mkEnableOption "Enable bridge"; pvid = lib.mkOption { type = lib.types.int; }; bindNetwork = lib.mkOption { type = lib.types.str; }; network = lib.mkOption { type = lib.types.str; }; netdev = lib.mkOption { type = lib.types.str; }; }; }; }; cfg = config.modules.vlans; vlans = lib.mapAttrsToList ( attrName: { name ? attrName, ... }@vlan: ({ inherit name; } // vlan) ) cfg.networks; vlanAttrs = lib.listToAttrs ( lib.map ( { name, ... }@value: { inherit name value; } ) vlans ); buildNetdev = name: { id, ... }: { name = "20-${name}"; value = { netdevConfig = { Name = name; Kind = "vlan"; MACAddress = "00:0d:b9:49:d2:${toString id}"; }; vlanConfig = { Id = id; }; }; }; buildStaticLease = macAddress: address: '' [DHCPServerStaticLease] MACAddress=${macAddress} Address=${address} ''; buildNetwork = name: { id, ipv6, staticLeases, ... }: { name = "30-${name}"; value = { matchConfig = { Name = name; }; networkConfig = { Address = "10.${toString id}.0.1/23"; IPMasquerade = "ipv4"; DHCPServer = true; IPv6AcceptRA = false; IPv6SendRA = ipv6; DHCPPrefixDelegation = ipv6; }; dhcpServerConfig = { PoolOffset = 255; DNS = "10.${toString id}.0.1"; }; extraConfig = lib.concatLines (lib.mapAttrsToList buildStaticLease staticLeases); }; }; vlanIds = lib.mapAttrsToList (_: { id, ... }: id) vlanAttrs; vlanRange = { min = lib.foldr lib.min 999 vlanIds; max = lib.foldr lib.max 0 vlanIds; }; bridgeVLANConfig = '' [BridgeVLAN] VLAN=${toString vlanRange.min}-${toString vlanRange.max} EgressUntagged=${toString cfg.bridge.pvid} PVID=${toString cfg.bridge.pvid} ''; bridgeNetDev = if (cfg.bridge.enable) then { "${cfg.bridge.netdev}".bridgeConfig = { VLANFiltering = true; DefaultPVID = cfg.bridge.pvid; }; } else { }; bridgeBindNetwork = if (cfg.bridge.enable) then { ${cfg.bridge.bindNetwork}.extraConfig = bridgeVLANConfig; } else { }; bridgeNetwork = if (cfg.bridge.enable) then { "${cfg.bridge.network}" = { vlan = lib.map ({ name, ... }: name) vlans; bridgeVLANs = lib.map ( { id, ... }: { VLAN = id; } ) vlans; }; } else { }; netdevs = lib.mergeAttrsList [ (lib.mapAttrs' buildNetdev vlanAttrs) bridgeNetDev ]; networks = lib.mergeAttrsList [ (lib.mapAttrs' buildNetwork vlanAttrs) bridgeBindNetwork bridgeNetwork ]; in { options.modules.vlans = { enable = lib.mkEnableOption "Enable VLANs"; networks = lib.mkOption { type = lib.types.attrsOf types.vlan; }; bridge = lib.mkOption { type = types.bridge; }; }; config = lib.mkIf cfg.enable { systemd.network = { enable = lib.mkDefault true; inherit netdevs networks; }; }; }