{ config, lib, pkgs, ... }: with lib; let cfg = config.nix-nos.network; ifaceCfg = config.nix-nos.interfaces; interfaceType = types.submodule { options = { name = mkOption { type = types.str; description = "Interface name (e.g., eth0, ens3)"; }; addresses = mkOption { type = types.listOf types.str; default = []; description = "List of IP addresses in CIDR notation"; example = [ "192.168.1.10/24" "2001:db8::1/64" ]; }; mtu = mkOption { type = types.nullOr types.int; default = null; description = "Maximum Transmission Unit (MTU) size"; example = 9000; }; vlan = mkOption { type = types.nullOr types.int; default = null; description = "VLAN ID if this is a VLAN interface"; }; }; }; # New systemd-networkd based interface type systemdInterfaceType = types.submodule { options = { addresses = mkOption { type = types.listOf types.str; default = []; description = "IP addresses with CIDR (e.g., 192.168.1.10/24)"; example = [ "192.168.1.10/24" "2001:db8::1/64" ]; }; gateway = mkOption { type = types.nullOr types.str; default = null; description = "Default gateway for this interface"; example = "192.168.1.1"; }; dns = mkOption { type = types.listOf types.str; default = []; description = "DNS servers"; example = [ "8.8.8.8" "8.8.4.4" ]; }; dhcp = mkOption { type = types.bool; default = false; description = "Enable DHCP on this interface"; }; mtu = mkOption { type = types.nullOr types.int; default = null; description = "MTU size"; example = 1500; }; }; }; in { options.nix-nos.interfaces = mkOption { type = types.attrsOf systemdInterfaceType; default = {}; description = "Network interface configurations using systemd-networkd"; example = literalExpression '' { eth0 = { addresses = [ "192.168.1.10/24" ]; gateway = "192.168.1.1"; dns = [ "8.8.8.8" "8.8.4.4" ]; }; eth1 = { dhcp = true; }; } ''; }; options.nix-nos.network = { interfaces = mkOption { type = types.listOf interfaceType; default = []; description = "Network interface configurations"; example = literalExpression '' [ { name = "eth0"; addresses = [ "10.0.0.10/24" ]; mtu = 1500; } { name = "eth0.100"; addresses = [ "192.168.100.1/24" ]; vlan = 100; } ] ''; }; enableIpForwarding = mkOption { type = types.bool; default = false; description = "Enable IP forwarding (routing)"; }; }; config = mkMerge [ # Map nix-nos.interfaces to systemd-networkd (mkIf (ifaceCfg != {}) { assertions = [ { assertion = all (name: iface: (iface.dhcp || (length iface.addresses) > 0) ) (mapAttrsToList nameValuePair ifaceCfg); message = "nix-nos.interfaces: Each interface must have either dhcp=true or at least one address"; } ]; systemd.network.enable = true; systemd.network.networks = mapAttrs' (name: iface: nameValuePair "10-${name}" { matchConfig.Name = name; address = iface.addresses; gateway = optional (iface.gateway != null) iface.gateway; dns = iface.dns; DHCP = if iface.dhcp then "yes" else "no"; linkConfig = optionalAttrs (iface.mtu != null) { MTUBytes = toString iface.mtu; }; }) ifaceCfg; }) # Legacy: Map nix-nos.network.interfaces to NixOS networking.interfaces (mkIf config.nix-nos.enable { networking.interfaces = mkMerge ( map (iface: { ${iface.name} = { ipv4.addresses = map (addr: let parts = splitString "/" addr; ip = elemAt parts 0; prefixLength = toInt (elemAt parts 1); in { address = ip; inherit prefixLength; } ) (filter (addr: ! (hasInfix ":" addr)) iface.addresses); ipv6.addresses = map (addr: let parts = splitString "/" addr; ip = elemAt parts 0; prefixLength = toInt (elemAt parts 1); in { address = ip; inherit prefixLength; } ) (filter (addr: hasInfix ":" addr) iface.addresses); mtu = mkIf (iface.mtu != null) iface.mtu; }; }) cfg.interfaces ); # Enable IP forwarding if requested boot.kernel.sysctl = mkIf cfg.enableIpForwarding { "net.ipv4.ip_forward" = 1; "net.ipv6.conf.all.forwarding" = 1; }; }) ]; }