diff --git a/modules/clan/gw-router/default.nix b/modules/clan/gw-router/default.nix index 8dea8d3..0a5beea 100644 --- a/modules/clan/gw-router/default.nix +++ b/modules/clan/gw-router/default.nix @@ -136,10 +136,17 @@ }; accessPorts = lib.mkOption { - type = lib.types.listOf lib.types.str; + type = lib.types.attrsOf lib.types.submodule { + options = { + vlanId = lib.mkOption { + type = lib.types.int; + description = "Untagged traffic in from the device gets tagged VLANs inside the bridge, and VLANs traffic going back out to the device gets untagged, so the device itself never has to know VLANs exist."; + }; + }; + }; description = '' Ports acting as untagged access ports on a single VLAN (port name -> - VLAN name). Frames are untagged on the wire; the bridge tags them with + VLAN id). Frames are untagged on the wire; the bridge tags them with the VLAN's PVID. Use for an always-available on-site mgmt port. ''; }; @@ -177,7 +184,7 @@ }; } // lib.optionalAttrs (settings.wan.vlanId != null) { - "15-wan-vlan" = { + "15-wan-lan" = { netdevConfig = { Name = "wan-vlan"; Kind = "vlan"; @@ -196,12 +203,65 @@ } ) settings.vlans; - systemd.network.networks = { - "10-wan" = { - matchConfig.Name = settings.wan.interface; - networkConfig.LinkLocalAddressing = "no"; - }; - }; + systemd.network.networks = + let + allVLANs = lib.mapAttrsToList (_: vlan: { VLAN = vlan.id; }) settings.vlans; + in + { + "10-wan" = { + matchConfig.Name = settings.wan.interface; + networkConfig.LinkLocalAddressing = "no"; + vlan = lib.optional (settings.wan.vlanId != null) "wan-wlan"; + linkConfig = { + RequiredForOnline = "carrier"; + } + // lib.optionalAttrs (settings.wan.macAddress != null) { + MACAddress = settings.wan.macAddress; + }; + }; + } + // lib.optionalAttrs (settings.wan.vlanId != null) { + "15-wan-lan" = { + matchConfig.Name = "wan-vlan"; + networkConfig.LinkLocalAddressing = "no"; + linkConfig.RequiredForOnline = "no"; + }; + } + // { + "20-br0" = { + matchConfig.Name = "br0"; + networkConfig.LinkLocalAddressing = "no"; + vlan = lib.mapAttrsToList (name: _: vlanIf name) settings.vlans; + bridgeVLANs = allVLANs; + linkConfig.RequiredForOnline = "no"; + }; + } + // lib.listToAttrs ( + map (port: { + name = "25-trunk-${port}"; + value = { + matchConfig.Name = port; + networkConfig.Bridge = "br0"; + bridgeVLANs = allVLANs; + linkConfig.RequiredForOnline = "no"; + }; + }) settings.trunkPorts + ) + // lib.mapAttrs' ( + iface: port: + lib.nameValuePair "20-access-${iface}" { + matchConfig.Name = port; + networkConfig.Bridge = "br0"; + bridgeVLANs = [ + { + VLAN = port.vlanId; + PVID = port.vlanId; + EgressUntagged = port.vlanId; + } + ]; + linkConfig.RequiredForOnline = "no"; + } + ) settings.accessPorts; environment.systemPackages = with pkgs; [ tcpdump