{ config, lib, ... }: with lib; { options.srv = { static = mkOption { type = types.nullOr (types.submodule { options = { ip = mkOption { type = types.str; example = "10.0.0.0"; }; prefixLength = mkOption { type = types.ints.between 0 32; default = 24; example = 32; }; interface = mkOption { type = types.str; example = "ens3"; }; }; }); default = null; }; tld = mkOption { type = types.nullOr types.str; default = null; example = "local"; }; slds = mkOption { type = types.listOf types.str; default = []; }; sldsImport = mkOption { type = types.listOf types.path; default = []; }; }; config = let netinterfaces = config: { ${config.srv.static.interface}.ipv4.addresses = [{ address = config.srv.static.ip; prefixLength = config.srv.static.prefixLength; }]; }; in { networking = mkIf (config.srv.static != null) { interfaces = netinterfaces config; # Calculate the gateway using the given static ip and prefix # We're assuming the gateway is the first (min) host on the network defaultGateway.address = let # 192.168.0.100 -> [ "192" "168" "0" "100" ] ipBytes = builtins.map (x: lib.toInt x) (lib.splitString "." config.srv.static.ip); # 2 ^^ x pow2 = x: if x == 0 then 1 else 2 * (pow2 (x - 1)); # 24 -> 0b11111111_11111111_11111111_00000000 mask = 4294967295 - ((pow2 (32 - config.srv.static.prefixLength)) - 1); # 0b11111111_11111111_11111111_00000000 -> [ 255 255 255 0 ] maskBytes = [ (builtins.bitAnd (mask / 16777216) 255) (builtins.bitAnd (mask / 65536) 255) (builtins.bitAnd (mask / 256) 255) (builtins.bitAnd mask 255) ]; # ipBytes & maskBytes networkIP = lib.zipListsWith lib.bitAnd ipBytes maskBytes; # min possible host gatewayIP = (lib.take 3 networkIP) ++ [ ((lib.last networkIP) + 1) ]; in lib.mkDefault (lib.concatMapStringsSep "." builtins.toString gatewayIP); }; virtualisation.vmVariant.networking.interfaces = mkIf (config.srv.static != null) (lib.mkVMOverride (netinterfaces (lib.recursiveUpdate config { srv.static.interface = "eth0"; }))); services.dnsmasq.settings.address = mkMerge [ (mkIf (config.srv.static != null && config.srv.tld != null) (builtins.map (sld: "/${sld}.${config.srv.tld}/${config.srv.static.ip}") config.srv.slds)) (mkIf (config.srv.sldsImport != []) (let importModule = mpath: let module = import mpath; in if builtins.isFunction module then module { config = {}; lib = {}; pkgs = {}; } else module; importSLDs = mpath: let module = importModule mpath; in (module.srv.slds or []) ++ (builtins.foldl' (acc: x: acc ++ importSLDs x) [] (module.imports or [])); sldConfigs = builtins.map (mpath: let # TODO: ugly and hard-coded, must be fixed some day m = importModule mpath; inherit (m.srv) tld; inherit (m.srv.static) ip; in builtins.map (sld: "/${sld}.${tld}/${ip}") (importSLDs mpath)) config.srv.sldsImport; in lib.flatten sldConfigs)) ]; }; }