diff options
Diffstat (limited to 'sys/net/options.nix')
| -rw-r--r-- | sys/net/options.nix | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/sys/net/options.nix b/sys/net/options.nix new file mode 100644 index 0000000..0608fb9 --- /dev/null +++ b/sys/net/options.nix @@ -0,0 +1,278 @@ +{ + config, + lib, + ... +}: +with lib; let + v4PtrHierarchy = address: bits: reverseList (sublist 0 (bits / 8) (splitString "." address)); + + v6PtrHierarchy = address: bits: let + separator = lists.findFirstIndex (hextet: hextet == "") null colonSplit; + colonSplit = splitString ":" address; + + zeroFill = replicate (8 - length colonSplit + 1) "0000"; + leftSplit = sublist 0 separator colonSplit; + rightSplit = sublist (separator + 1) (length colonSplit - separator - 1) colonSplit; + + fullSplit = + if separator != null + then leftSplit ++ zeroFill ++ rightSplit + else colonSplit; + + padded = map (hextet: strings.replicate (4 - stringLength hextet) "0" + hextet) fullSplit; + in + reverseList (sublist 0 (bits / 4) (flatten (map stringToCharacters padded))); + + matchPtrRecordName = { + splitter, + netAddress, + netBits, + targetAddress, + targetBits, + }: let + netSplit = splitter netAddress netBits; + targetSplit = splitter targetAddress targetBits; + + netLength = length netSplit; + lengthDelta = length targetSplit - netLength; + + withinNet = lengthDelta >= 0 && sublist lengthDelta netLength targetSplit == netSplit; + throwMessage = "${targetAddress}/${toString targetBits} is not a subset of ${netAddress}/${toString netBits}"; + + recordHierarchy = sublist 0 lengthDelta targetSplit; + + recordName = + if recordHierarchy != [] + then concatStringsSep "." recordHierarchy + else "@"; + in + throwIfNot withinNet throwMessage recordName; +in { + options.local.nets = with lib.types; + mkOption { + readOnly = true; + + type = attrsOf (submodule ({config, ...}: { + options = let + v4config = config.v4; + v6config = config.v6; + in { + hosts = mkOption { + default = {}; + + type = attrsOf (submodule { + options = { + v4 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + suffix = mkOption { + type = str; + }; + + address = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + single = mkOption { + type = str; + readOnly = true; + }; + }; + + config = { + address = + if v4config.bits == 0 + then config.suffix + else if v4config.bits == 32 + then v4config.subnet + else "${v4config.prefix}.${config.suffix}"; + + cidr = "${config.address}/${toString v4config.bits}"; + single = "${config.address}/32"; + }; + })); + }; + + v6 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + suffix = mkOption { + type = str; + }; + + address = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + single = mkOption { + type = str; + readOnly = true; + }; + }; + + config = { + address = let + hextets = fragment: length (splitString ":" fragment); + separator = + if doubleColon + then "::" + else ":"; + doubleColon = hextets v6config.prefix + hextets config.suffix < 8; + + joined = + if v6config.bits == 128 + then v6config.prefix + else if v6config.bits == 0 + then config.suffix + else "${v6config.prefix}${separator}${config.suffix}"; + in + joined; + + cidr = "${config.address}/${toString v6config.bits}"; + single = "${config.address}/128"; + }; + })); + }; + }; + }); + }; + + v4 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + bits = mkOption { + type = enum [0 8 16 24 32]; + }; + + prefix = mkOption { + type = str; + }; + + subnet = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + ptrDomain = mkOption { + type = str; + readOnly = true; + }; + + ptrRecordName = mkOption { + type = functionTo (functionTo str); + readOnly = true; + }; + }; + + config = { + cidr = "${config.subnet}/${toString config.bits}"; + + subnet = + if config.bits != 0 + then config.prefix + strings.replicate (4 - config.bits / 8) ".0" + else "0.0.0.0"; + + ptrDomain = concatStrings (map (x: x + ".") (v4PtrHierarchy config.subnet config.bits)) + "in-addr.arpa"; + + ptrRecordName = address: bits: + matchPtrRecordName { + splitter = v4PtrHierarchy; + + netBits = config.bits; + netAddress = config.subnet; + + targetBits = bits; + targetAddress = address; + }; + }; + })); + }; + + v6 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + bits = mkOption { + type = + addCheck (ints.between 0 128) (b: mod b 4 == 0) + // { + description = "IPv6 subnet bits at nibble boundary"; + }; + }; + + prefix = mkOption { + type = str; + }; + + subnet = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + ptrDomain = mkOption { + type = str; + readOnly = true; + }; + + ptrRecordName = mkOption { + type = functionTo (functionTo str); + readOnly = true; + }; + }; + + config = { + cidr = "${config.subnet}/${toString config.bits}"; + + subnet = + if config.bits == 128 || length (splitString "::" config.prefix) > 1 + then config.prefix + else "${config.prefix}::"; + + ptrDomain = concatStrings (map (x: x + ".") (v6PtrHierarchy config.subnet config.bits)) + "ip6.arpa"; + + ptrRecordName = address: bits: + matchPtrRecordName { + splitter = v6PtrHierarchy; + + netBits = config.bits; + netAddress = config.subnet; + + targetBits = bits; + targetAddress = address; + }; + }; + })); + }; + }; + })); + }; +} |
