{ config, options, lib, ... }: with lib; let dhcpOpts = { start = mkOption { description = "First IP address in pool"; type = types.str; }; end = mkOption { description = "Last IP address in pool"; type = types.str; }; time = mkOption { description = "Renew time in seconds"; type = types.int; }; max-time = mkOption { description = "Max renew time in seconds"; type = types.int; }; server = mkOption { description = "Container that runs the DHCP server"; type = types.str; }; router = mkOption { description = "Gateway"; type = types.str; }; fixed-hosts = mkOption { type = with types; attrsOf str; default = {}; }; }; netOpts = { name, ... }: { options = { vlan = mkOption { description = "VLAN tag number"; type = types.int; }; subnet4 = mkOption { description = "v.w.x.y/z"; type = with types; nullOr str; default = null; }; subnet4Net = mkOption { type = with types; nullOr types.str; default = let inherit (config.site.net.${name}) subnet4; s = lib.splitString "/" subnet4; in if subnet4 != null && builtins.length s == 2 then builtins.head s else null; }; subnet4Len = mkOption { type = with types; nullOr types.int; default = let inherit (config.site.net.${name}) subnet4; s = lib.splitString "/" subnet4; in if subnet4 != null && builtins.length s == 2 then lib.toInt (elemAt s 1) else null; }; subnets6 = mkOption { description = "IPv6 subnets w/o prefixlen (always 64)"; type = with types; attrsOf str; default = {}; }; hosts4 = mkOption { description = "Attribute set of hostnames to IPv4 addresses"; type = with types; attrsOf str; default = {}; }; hosts6 = mkOption { description = "Attribute set of contexts to attribute sets of hostnames to IPv4 addresses"; type = with types; attrsOf (attrsOf str); default = {}; }; ospf = { secret = mkOption { type = with types; nullOr str; default = null; }; }; dhcp = mkOption { type = with types; nullOr (submodule { options = dhcpOpts; }); default = null; }; domainName = mkOption { description = "Domain name option"; type = types.str; default = "${name}.zentralwerk.org"; }; }; }; upstreamOpts = { upBandwidth = mkOption { type = with types; nullOr int; }; }; interfaceOpts = { name, ... }: { options = { hwaddr = mkOption { type = with types; nullOr str; default = null; description = "Static MAC address"; }; type = mkOption { type = types.enum [ "phys" "veth" ]; description = '' veth: Virtual ethernet to be attached to a bridge. phys: (Physical) interface from a server moved into the container. Do not use with VLAN interfaces because they won't be moved back after lxc-stop. ''; }; gw4 = mkOption { type = with types; nullOr str; default = null; description = "IPv4 gateway"; }; gw6 = mkOption { type = with types; nullOr str; default = null; description = "IPv6 gateway"; }; upstream = mkOption { type = with types; nullOr (submodule { options = upstreamOpts; }); default = null; description = "Upstream interface configuration"; }; }; }; hostOpts = { name, ... }: { options = { prebuilt = mkOption { type = types.bool; default = false; description = '' Include the container system in the server's `build-container` script. ''; }; role = mkOption { type = types.enum [ "ap" "switch" "server" "container" "client" ]; default = "client"; }; model = mkOption { type = types.str; default = { ap = "unknown"; switch = "unknown"; server = "pc"; container = "lxc"; client = "any"; }."${config.site.hosts.${name}.role}"; }; password = mkOption { type = with types; nullOr str; default = null; }; location = mkOption { type = with types; nullOr str; default = null; }; interfaces = mkOption { default = {}; type = with types; attrsOf (submodule interfaceOpts); description = "Network interfaces"; }; isRouter = mkOption { type = types.bool; # isRouter = Part of the core network? default = config.site.hosts.${name}.interfaces ? core && config.site.net.core.hosts4 ? ${name}; description = "Should this host route?"; }; firewall.enable = mkOption { type = types.bool; default = false; description = "Enable firewall to disallow incoming connections from core"; }; forwardPorts = mkOption { type = with types; listOf (submodule { options = { proto = mkOption { type = types.enum [ "tcp" "udp" ]; }; sourcePort = mkOption { type = types.int; }; destination = mkOption { type = types.str; }; }; }); default = []; }; ospf.stubNets4 = mkOption { type = with types; listOf str; default = []; description = "Additional IPv4 networks to announce"; }; ospf.stubNets6 = mkOption { type = with types; listOf str; default = []; description = "Additional IPv6 networks to announce"; }; ospf.allowedUpstreams = mkOption { type = with types; listOf str; default = []; description = "Accept default routes from these OSPF routers, in order of preference"; }; wireguard = mkOption { default = {}; type = with types; attrsOf (submodule ( { name, ... }: { options = { endpoint = mkOption { type = str; }; publicKey = mkOption { type = str; }; privateKey = mkOption { type = str; }; addresses = mkOption { type = listOf str; }; upBandwidth = mkOption { type = with types; nullOr int; }; }; } )); }; bgp = mkOption { default = null; type = with types; nullOr (submodule { options = bgpOpts; }); }; services.dnscache.enable = mkOption { type = types.bool; default = false; }; }; }; bgpOpts = { asn = mkOption { type = types.int; }; peers = mkOption { type = with types; attrsOf (submodule ({ name, ... }: { options = { asn = mkOption { type = types.int; }; }; })); default = {}; }; }; in { options.site = { net = mkOption { default = {}; type = with types; attrsOf (submodule netOpts); }; hosts = mkOption { default = {}; type = with types; attrsOf (submodule hostOpts); }; }; config.warnings = let findCollisions = getter: xs: (builtins.foldl' ({ known, dup }: k: let ks = builtins.toString k; in if known ? ${ks} then { inherit known; dup = dup ++ [ks]; } else { known = known // { ${ks} = true; }; inherit dup; } ) { known = {}; dup = []; } ( concatMap getter (builtins.attrValues xs) )).dup; reportCollisions = name: getter: xs: map (k: "Duplicate ${name}: ${k}") (findCollisions getter xs); ospfUpstreamXorGw = builtins.concatMap (hostName: let hostConf = config.site.hosts.${hostName}; gwNets = builtins.filter (netName: hostConf.interfaces.${netName}.gw4 != null ) (builtins.attrNames hostConf.interfaces); in if gwNets != [] && hostConf.ospf.allowedUpstreams != [] then [ '' Host ${hostName} has gateway on ${builtins.head gwNets} but accepts default routes from OSPF '' ] else [] ) (builtins.attrNames config.site.hosts); in (reportCollisions "VLAN tag" (x: [x.vlan]) config.site.net) ++ (reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++ (reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net) ++ ospfUpstreamXorGw; }