1
1
Fork 1
mirror of https://github.com/oddlama/nixos-extra-modules.git synced 2025-10-10 22:00:39 +02:00

feat: lix-compat

This commit is contained in:
Patrick 2025-02-04 22:50:54 +01:00
parent 8daa7e8318
commit 4c3af09fb7
No known key found for this signature in database
GPG key ID: 451F95EFB8BECD0F
5 changed files with 194 additions and 325 deletions

20
flake.lock generated
View file

@ -75,26 +75,13 @@
"type": "github" "type": "github"
} }
}, },
"lib-net": {
"flake": false,
"locked": {
"lastModified": 1596309860,
"narHash": "sha256-izAzepR/6cDvnRfaa2ceSolMLMwqzQB5x9q62aR5J2g=",
"type": "tarball",
"url": "https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba/archive/3885f7cd9ed0a746a9d675da6f265d41e9fd6704.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba/archive/3885f7cd9ed0a746a9d675da6f265d41e9fd6704.tar.gz"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1737885589, "lastModified": 1739214665,
"narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=", "narHash": "sha256-26L8VAu3/1YRxS8MHgBOyOM8xALdo6N0I04PgorE7UM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8", "rev": "64e75cd44acf21c7933d61d7721e812eac1b5a0a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -142,7 +129,6 @@
"inputs": { "inputs": {
"devshell": "devshell", "devshell": "devshell",
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"lib-net": "lib-net",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks" "pre-commit-hooks": "pre-commit-hooks"
} }

View file

@ -9,11 +9,6 @@
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.url = "github:hercules-ci/flake-parts";
lib-net = {
url = "https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba/archive/3885f7cd9ed0a746a9d675da6f265d41e9fd6704.tar.gz";
flake = false;
};
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
pre-commit-hooks = { pre-commit-hooks = {

View file

@ -1,26 +1,24 @@
_inputs: _final: prev: let _inputs: _final: prev:
inherit let
(prev.lib) inherit (prev.lib)
filter filter
foldl' foldl'
genAttrs genAttrs
genList
mergeAttrs mergeAttrs
mkMerge mkMerge
stringToCharacters
substring substring
unique unique
stringToCharacters
; ;
# Counts how often each element occurrs in xs. # Counts how often each element occurrs in xs.
# Elements must be strings. # Elements must be strings.
countOccurrences = countOccurrences = foldl' (acc: x: acc // { ${x} = (acc.${x} or 0) + 1; }) { };
foldl'
(acc: x: acc // {${x} = (acc.${x} or 0) + 1;})
{};
# Returns all elements in xs that occur at least twice # Returns all elements in xs that occur at least twice
duplicates = xs: let duplicates =
xs:
let
occurrences = countOccurrences xs; occurrences = countOccurrences xs;
in in
unique (filter (x: occurrences.${x} > 1) xs); unique (filter (x: occurrences.${x} > 1) xs);
@ -33,11 +31,7 @@ _inputs: _final: prev: let
# Merges all given attributes from the given attrsets using mkMerge. # Merges all given attributes from the given attrsets using mkMerge.
# Useful to merge several top-level configs in a module. # Useful to merge several top-level configs in a module.
mergeToplevelConfigs = keys: attrs: mergeToplevelConfigs = keys: attrs: genAttrs keys (attr: mkMerge (map (x: x.${attr} or { }) attrs));
genAttrs keys (attr: mkMerge (map (x: x.${attr} or {}) attrs));
# Calculates base^exp, but careful, this overflows for results > 2^62
pow = base: exp: foldl' (a: x: x * a) 1 (genList (_: base) exp);
hexLiteralValues = { hexLiteralValues = {
"0" = 0; "0" = 0;
@ -66,19 +60,18 @@ _inputs: _final: prev: let
# Converts the given hex string to an integer. Only reliable for inputs in [0, 2^63), # Converts the given hex string to an integer. Only reliable for inputs in [0, 2^63),
# after that the sign bit will overflow. # after that the sign bit will overflow.
hexToDec = v: foldl' (acc: x: acc * 16 + hexLiteralValues.${x}) 0 (stringToCharacters v); hexToDec =
in { v: foldl' (acc: x: (builtins.bitShiftLeft acc 4) + hexLiteralValues.${x}) 0 (stringToCharacters v);
lib = in
prev.lib {
// { lib = prev.lib // {
inherit inherit
hexToDec
concatAttrs concatAttrs
countOccurrences countOccurrences
duplicates duplicates
hexToDec
isAbsolutePath isAbsolutePath
mergeToplevelConfigs mergeToplevelConfigs
pow
; ;
}; };
} }

View file

@ -1,6 +1,6 @@
inputs: _final: prev: let inputs: _final: prev:
inherit let
(prev.lib) inherit (prev.lib)
all all
any any
assertMsg assertMsg
@ -19,27 +19,27 @@ inputs: _final: prev: let
substring substring
unique unique
warnIf warnIf
;
# From misc.nix
inherit
(prev.lib)
hexToDec hexToDec
pow
; ;
# IP address math library # IP address math library
# https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba # https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba
# Plus some extensions by us # Plus some extensions by us
libNet = libNet =
(import "${inputs.lib-net}/net.nix" { (import ./netu.nix {
inherit (inputs.nixpkgs) lib; inherit (inputs.nixpkgs) lib;
}) }).lib;
.lib in
.net; {
in {
lib = recursiveUpdate prev.lib { lib = recursiveUpdate prev.lib {
net = recursiveUpdate (removeAttrs libNet ["types"]) { inherit (libNet)
arithmetic
typechecks
bit
implementations
parsers
;
net = recursiveUpdate (removeAttrs libNet.net [ "types" ]) {
cidr = rec { cidr = rec {
# host :: (ip | mac | integer) -> cidr -> ip # host :: (ip | mac | integer) -> cidr -> ip
# #
@ -58,11 +58,13 @@ in {
# "192.168.1.0" # "192.168.1.0"
# > net.cidr.host (-257) "192.168.1.0/24" # > net.cidr.host (-257) "192.168.1.0/24"
# <fails with an error message> # <fails with an error message>
host = i: n: let host =
cap = libNet.cidr.capacity n; i: n:
let
cap = libNet.net.cidr.capacity n;
in in
assert assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}"; assert assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}";
libNet.cidr.host i n; libNet.net.cidr.host i n;
# hostCidr :: (ip | mac | integer) -> cidr -> cidr # hostCidr :: (ip | mac | integer) -> cidr -> cidr
# #
# Returns the nth host in the given cidr range (like cidr.host) # Returns the nth host in the given cidr range (like cidr.host)
@ -72,7 +74,7 @@ in {
# #
# > net.cidr.hostCidr 2 "192.168.1.0/24" # > net.cidr.hostCidr 2 "192.168.1.0/24"
# "192.168.1.2/24" # "192.168.1.2/24"
hostCidr = n: x: "${libNet.cidr.host n x}/${toString (libNet.cidr.length x)}"; hostCidr = n: x: "${libNet.net.cidr.host n x}/${toString (libNet.net.cidr.length x)}";
# ip :: (cidr | ip) -> ip # ip :: (cidr | ip) -> ip
# #
# Returns just the ip part of the cidr. # Returns just the ip part of the cidr.
@ -93,7 +95,7 @@ in {
# #
# > net.cidr.canonicalize "192.168.1.100/24" # > net.cidr.canonicalize "192.168.1.100/24"
# "192.168.1.0/24" # "192.168.1.0/24"
canonicalize = x: libNet.cidr.make (libNet.cidr.length x) (ip x); canonicalize = x: libNet.net.cidr.make (libNet.net.cidr.length x) (ip x);
# mergev4 :: [cidrv4 | ipv4] -> (cidrv4 | null) # mergev4 :: [cidrv4 | ipv4] -> (cidrv4 | null)
# #
# Returns the smallest cidr network that includes all given networks. # Returns the smallest cidr network that includes all given networks.
@ -103,37 +105,30 @@ in {
# #
# > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"] # > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"]
# "192.168.0.0/21" # "192.168.0.0/21"
mergev4 = addrs_: let mergev4 =
addrs_:
let
# Append /32 if necessary # Append /32 if necessary
addrs = map (x: addrs = map (x: if hasInfix "/" x then x else "${x}/32") addrs_;
if hasInfix "/" x
then x
else "${x}/32")
addrs_;
# The smallest occurring length is the first we need to start checking, since # The smallest occurring length is the first we need to start checking, since
# any greater cidr length represents a smaller address range which # any greater cidr length represents a smaller address range which
# wouldn't contain all of the original addresses. # wouldn't contain all of the original addresses.
startLength = foldl' min 32 (map libNet.cidr.length addrs); startLength = foldl' min 32 (map libNet.net.cidr.length addrs);
possibleLengths = reverseList (range 0 startLength); possibleLengths = reverseList (range 0 startLength);
# The first ip address will be "expanded" in cidr length until it covers all other # The first ip address will be "expanded" in cidr length until it covers all other
# used addresses. # used addresses.
firstIp = ip (head addrs); firstIp = ip (head addrs);
# Return the first (i.e. greatest length -> smallest prefix) cidr length # Return the first (i.e. greatest length -> smallest prefix) cidr length
# in the list that covers all used addresses # in the list that covers all used addresses
bestLength = head (filter bestLength = head (
filter
# All given addresses must be contained by the generated address. # All given addresses must be contained by the generated address.
(len: (len: all (x: libNet.net.cidr.contains (ip x) (libNet.net.cidr.make len firstIp)) addrs)
all (x: possibleLengths
libNet.cidr.contains );
(ip x)
(libNet.cidr.make len firstIp))
addrs)
possibleLengths);
in in
assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses"; assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
if addrs == [] if addrs == [ ] then null else libNet.net.cidr.make bestLength firstIp;
then null
else libNet.cidr.make bestLength firstIp;
# mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null) # mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null)
# #
# Returns the smallest cidr network that includes all given networks. # Returns the smallest cidr network that includes all given networks.
@ -143,128 +138,44 @@ in {
# #
# > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"] # > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
# "fd00:c000::/18" # "fd00:c000::/18"
mergev6 = addrs_: let mergev6 =
addrs_:
let
# Append /128 if necessary # Append /128 if necessary
addrs = map (x: addrs = map (x: if hasInfix "/" x then x else "${x}/128") addrs_;
if hasInfix "/" x
then x
else "${x}/128")
addrs_;
# The smallest occurring length is the first we need to start checking, since # The smallest occurring length is the first we need to start checking, since
# any greater cidr length represents a smaller address range which # any greater cidr length represents a smaller address range which
# wouldn't contain all of the original addresses. # wouldn't contain all of the original addresses.
startLength = foldl' min 128 (map libNet.cidr.length addrs); startLength = foldl' min 128 (map libNet.net.cidr.length addrs);
possibleLengths = reverseList (range 0 startLength); possibleLengths = reverseList (range 0 startLength);
# The first ip address will be "expanded" in cidr length until it covers all other # The first ip address will be "expanded" in cidr length until it covers all other
# used addresses. # used addresses.
firstIp = ip (head addrs); firstIp = ip (head addrs);
# Return the first (i.e. greatest length -> smallest prefix) cidr length # Return the first (i.e. greatest length -> smallest prefix) cidr length
# in the list that covers all used addresses # in the list that covers all used addresses
bestLength = head (filter bestLength = head (
filter
# All given addresses must be contained by the generated address. # All given addresses must be contained by the generated address.
(len: (len: all (x: libNet.net.cidr.contains (ip x) (libNet.net.cidr.make len firstIp)) addrs)
all (x: possibleLengths
libNet.cidr.contains );
(ip x)
(libNet.cidr.make len firstIp))
addrs)
possibleLengths);
in in
assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses"; assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
if addrs == [] if addrs == [ ] then null else libNet.net.cidr.make bestLength firstIp;
then null
else libNet.cidr.make bestLength firstIp;
# merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); } # merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); }
# #
# Returns the smallest cidr network that includes all given networks, # Returns the smallest cidr network that includes all given networks,
# but yields two separate result for all given ipv4 and ipv6 addresses. # but yields two separate result for all given ipv4 and ipv6 addresses.
# Equivalent to calling mergev4 and mergev6 on a partition individually. # Equivalent to calling mergev4 and mergev6 on a partition individually.
merge = addrs: let merge =
addrs:
let
v4_and_v6 = partition (hasInfix ":") addrs; v4_and_v6 = partition (hasInfix ":") addrs;
in { in
{
cidrv4 = mergev4 v4_and_v6.wrong; cidrv4 = mergev4 v4_and_v6.wrong;
cidrv6 = mergev6 v4_and_v6.right; cidrv6 = mergev6 v4_and_v6.right;
}; };
# assignIps :: cidr -> [int | ip] -> [string] -> [ip]
#
# Assigns a semi-stable ip address from the given cidr network to each hostname.
# The algorithm is based on hashing (abusing sha256) with linear probing.
# The order of hosts doesn't matter. No ip (or offset) from the reserved list
# will be assigned. The network address and broadcast address will always be reserved
# automatically.
#
# Examples:
#
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c"]
# { a = "192.168.100.202"; b = "192.168.100.74"; c = "192.168.100.226"; }
#
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c" "a-new-elem"]
# { a = "192.168.100.202"; a-new-elem = "192.168.100.88"; b = "192.168.100.74"; c = "192.168.100.226"; }
#
# > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"]
# { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; }
assignIps = net: reserved: hosts: let
cidrSize = libNet.cidr.size net;
capacity = libNet.cidr.capacity net;
# The base address of the network. Used to convert ip-based reservations to offsets
baseAddr = host 0 net;
# Reserve some values for the network, host and broadcast address.
# The network and broadcast address should never be used, and we
# want to reserve the host address for the host. We also convert
# any ips to offsets here.
init = unique (
[0 (capacity - 1)]
++ flip map reserved (x:
if builtins.typeOf x == "int"
then x
else -(libNet.ip.diff baseAddr x))
);
nHosts = builtins.length hosts;
nInit = builtins.length init;
# Pre-sort all hosts, to ensure ordering invariance
sortedHosts =
warnIf
((nInit + nHosts) > 0.3 * capacity)
"assignIps: hash stability may be degraded since utilization is >30%"
(builtins.sort builtins.lessThan hosts);
# Generates a hash (i.e. offset value) for a given hostname
hashElem = x:
builtins.bitAnd (capacity - 1)
(hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
# Do linear probing. Returns the first unused value at or after the given value.
probe = avoid: value:
if elem value avoid
# TODO lib.mod
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
# class of x modulo 2^n, so we can use bitAnd instead.
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
else value;
# Hash a new element and avoid assigning any existing values.
assignOne = {
assigned,
used,
}: x: let
value = probe used (hashElem x);
in {
assigned =
assigned
// {
${x} = host value net;
};
used = [value] ++ used;
};
in
assert assertMsg (cidrSize >= 2 && cidrSize <= 62)
"assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
assert assertMsg (nHosts <= capacity - nInit)
"assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
# Assign an ip in the subnet to each element, in order
(foldl' assignOne {
assigned = {};
used = init;
}
sortedHosts)
.assigned;
}; };
ip = rec { ip = rec {
# Checks whether the given address (with or without cidr notation) is an ipv4 address. # Checks whether the given address (with or without cidr notation) is an ipv4 address.
@ -275,11 +186,14 @@ in {
mac = { mac = {
# Adds offset to the given base address and ensures the result is in # Adds offset to the given base address and ensures the result is in
# a locally administered range by replacing the second nibble with a 2. # a locally administered range by replacing the second nibble with a 2.
addPrivate = base: offset: let addPrivate =
added = libNet.mac.add base offset; base: offset:
let
added = libNet.net.mac.add base offset;
pre = substring 0 1 added; pre = substring 0 1 added;
suf = substring 2 (-1) added; suf = substring 2 (-1) added;
in "${pre}2${suf}"; in
"${pre}2${suf}";
# assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac] # assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac]
# #
# Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname. # Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname.
@ -297,65 +211,66 @@ in {
# #
# > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"] # > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"]
# { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; } # { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
assignMacs = base: size: reserved: hosts: let assignMacs =
capacity = pow 2 size; base: size: reserved: hosts:
baseAsInt = libNet.mac.diff base "00:00:00:00:00:00"; let
capacity = builtins.bitShiftLeft 1 size;
baseAsInt = libNet.net.mac.diff base "00:00:00:00:00:00";
init = unique ( init = unique (
flip map reserved (x: flip map reserved (x: if builtins.typeOf x == "int" then x else libNet.net.mac.diff x base)
if builtins.typeOf x == "int"
then x
else libNet.mac.diff x base)
); );
nHosts = builtins.length hosts; nHosts = builtins.length hosts;
nInit = builtins.length init; nInit = builtins.length init;
# Pre-sort all hosts, to ensure ordering invariance # Pre-sort all hosts, to ensure ordering invariance
sortedHosts = sortedHosts =
warnIf warnIf ((nInit + nHosts) > 0.3 * capacity)
((nInit + nHosts) > 0.3 * capacity)
"assignMacs: hash stability may be degraded since utilization is >30%" "assignMacs: hash stability may be degraded since utilization is >30%"
(builtins.sort builtins.lessThan hosts); (builtins.sort builtins.lessThan hosts);
# Generates a hash (i.e. offset value) for a given hostname # Generates a hash (i.e. offset value) for a given hostname
hashElem = x: hashElem =
builtins.bitAnd (capacity - 1) x: builtins.bitAnd (capacity - 1) (hexToDec (substring 0 16 (builtins.hashString "sha256" x)));
(hexToDec (substring 0 16 (builtins.hashString "sha256" x)));
# Do linear probing. Returns the first unused value at or after the given value. # Do linear probing. Returns the first unused value at or after the given value.
probe = avoid: value: probe =
if elem value avoid avoid: value:
if
elem value avoid
# TODO lib.mod # TODO lib.mod
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue # Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
# class of x modulo 2^n, so we can use bitAnd instead. # class of x modulo 2^n, so we can use bitAnd instead.
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1)) then
else value; probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
else
value;
# Hash a new element and avoid assigning any existing values. # Hash a new element and avoid assigning any existing values.
assignOne = { assignOne =
{
assigned, assigned,
used, used,
}: x: let }:
x:
let
value = probe used (hashElem x); value = probe used (hashElem x);
in { in
assigned = {
assigned assigned = assigned // {
// { ${x} = libNet.net.mac.add value base;
${x} = libNet.mac.add value base;
}; };
used = [ value ] ++ used; used = [ value ] ++ used;
}; };
in in
assert assertMsg (size >= 2 && size <= 62) assert assertMsg (size >= 2 && size <= 62) "assignMacs: size=${toString size} is not in [2, 62].";
"assignMacs: size=${toString size} is not in [2, 62]."; assert assertMsg (
assert assertMsg (builtins.bitAnd (capacity - 1) baseAsInt == 0) builtins.bitAnd (capacity - 1) baseAsInt == 0
"assignMacs: the size=${toString size} least significant bits of the base mac address must be 0."; ) "assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
assert assertMsg (nHosts <= capacity - nInit) assert assertMsg (nHosts <= capacity - nInit)
"assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})"; "assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
# Assign an ip in the subnet to each element, in order # Assign an ip in the subnet to each element, in order
(foldl' assignOne { (foldl' assignOne {
assigned = { }; assigned = { };
used = init; used = init;
} } sortedHosts).assigned;
sortedHosts)
.assigned;
}; };
}; };
types.net = libNet.types; types.net = libNet.net.types;
}; };
} }

View file

@ -2,13 +2,10 @@
lib ? null, lib ? null,
... ...
}: }:
let let
net = net =
{ {
ip = { ip = {
# add :: (ip | mac | integer) -> ip -> ip # add :: (ip | mac | integer) -> ip -> ip
# #
# Examples: # Examples:
@ -96,7 +93,6 @@ let
}; };
mac = { mac = {
# add :: (ip | mac | integer) -> mac -> mac # add :: (ip | mac | integer) -> mac -> mac
# #
# Examples: # Examples:
@ -334,7 +330,6 @@ let
cidr' = typechecks.cidr function "cidr" cidr; cidr' = typechecks.cidr function "cidr" cidr;
in in
builders.cidr (implementations.cidr.subnet length' netnum' cidr'); builders.cidr (implementations.cidr.subnet length' netnum' cidr');
}; };
} }
// ( // (
@ -344,7 +339,6 @@ let
{ {
types = types =
let let
mkParsedOptionType = mkParsedOptionType =
{ {
name, name,
@ -385,10 +379,8 @@ let
// { // {
description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; description = type.description + " in ${builtins.concatStringsSep " or " cidrs}";
}; };
in in
rec { rec {
ip = mkParsedOptionType { ip = mkParsedOptionType {
name = "ip"; name = "ip";
description = "IPv4 or IPv6 address"; description = "IPv4 or IPv6 address";
@ -449,7 +441,6 @@ let
parser = parsers.mac; parser = parsers.mac;
builder = builders.mac; builder = builders.mac;
}; };
}; };
} }
); );
@ -460,20 +451,23 @@ let
bit = bit =
let let
shift = left =
n: x: a: b:
if n < 0 then if a >= 64 then
x * math.pow 2 (-n) 0
else if a < 0 then
right (-a) b
else else
let builtins.bitShiftLeft b a;
safeDiv = n: d: if d == 0 then 0 else n / d;
d = math.pow 2 n;
in
if x < 0 then not (safeDiv (not x) d) else safeDiv x d;
left = n: shift (-n); right =
a: b:
right = shift; if a >= 64 then
0
else if a < 0 then
left (-a) b
else
builtins.bitShiftRight b a;
and = builtins.bitAnd; and = builtins.bitAnd;
@ -505,20 +499,10 @@ let
clamp = clamp =
a: b: c: a: b: c:
max a (min b c); max a (min b c);
pow =
x: n:
if n == 0 then
1
else if bit.and n 1 != 0 then
x * pow (x * x) ((n - 1) / 2)
else
pow (x * x) (n / 2);
}; };
parsers = parsers =
let let
# fmap :: (a -> b) -> parser a -> parser b # fmap :: (a -> b) -> parser a -> parser b
fmap = f: ma: bind ma (a: pure (f a)); fmap = f: ma: bind ma (a: pure (f a));
@ -766,7 +750,6 @@ let
afterDoubleColon = alt ipv4' ( afterDoubleColon = alt ipv4' (
liftA2 list.cons hextet (alt (then_ colon afterDoubleColon) (pure [ ])) liftA2 list.cons hextet (alt (then_ colon afterDoubleColon) (pure [ ]))
); );
in in
bind (alt (then_ (string "::") (doubleColon 0)) (part 0)) fromHextets; bind (alt (then_ (string "::") (doubleColon 0)) (part 0)) fromHextets;
@ -791,7 +774,6 @@ let
}; };
in in
liftA6 fromOctets octet octet' octet' octet' octet' octet'; liftA6 fromOctets octet octet' octet' octet' octet' octet';
in in
{ {
ipv4 = run ipv4; ipv4 = run ipv4;
@ -806,7 +788,6 @@ let
builders = builders =
let let
ipv4 = ipv4 =
address: address:
let let
@ -832,7 +813,6 @@ let
ipv6 = ipv6 =
address: address:
let let
digits = "0123456789abcdef"; digits = "0123456789abcdef";
toHexString = toHexString =
@ -843,7 +823,6 @@ let
prefix = if rest == 0 then "" else toHexString rest; prefix = if rest == 0 then "" else toHexString rest;
in in
"${prefix}${builtins.substring current 1 digits}"; "${prefix}${builtins.substring current 1 digits}";
in in
if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) then if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) then
"::${ipv4 { ipv4 = address.ipv6.d; }}" "::${ipv4 { ipv4 = address.ipv6.d; }}"
@ -851,7 +830,6 @@ let
"::ffff:${ipv4 { ipv4 = address.ipv6.d; }}" "::ffff:${ipv4 { ipv4 = address.ipv6.d; }}"
else else
let let
a = bit.right 16 address.ipv6.a; a = bit.right 16 address.ipv6.a;
b = bit.mask 16 address.ipv6.a; b = bit.mask 16 address.ipv6.a;
c = bit.right 16 address.ipv6.b; c = bit.right 16 address.ipv6.b;
@ -961,7 +939,6 @@ let
f = bit.mask 8 (bit.right 0 address.mac); f = bit.mask 8 (bit.right 0 address.mac);
in in
"${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}"; "${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}";
in in
{ {
inherit inherit
@ -1096,7 +1073,7 @@ let
diff = diff =
a: b: a: b:
let let
toIPv6 = coerce ({ ipv6.a = 0; }); toIPv6 = coerce { ipv6.a = 0; };
result = (subtract b (toIPv6 a)).ipv6; result = (subtract b (toIPv6 a)).ipv6;
max32 = bit.left 32 1 - 1; max32 = bit.left 32 1 - 1;
in in
@ -1352,7 +1329,6 @@ let
typechecks = typechecks =
let let
fail = fail =
description: function: argument: description: function: argument:
builtins.throw "${function}: ${argument} parameter must be ${description}"; builtins.throw "${function}: ${argument} parameter must be ${description}";
@ -1369,7 +1345,6 @@ let
result = parser input; result = parser input;
in in
if builtins.isNull result then error else result; if builtins.isNull result then error else result;
in in
{ {
int = int =
@ -1385,11 +1360,16 @@ let
else else
meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input; meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input;
}; };
in in
{ {
lib = { lib = {
inherit net; inherit
arithmetic
net
typechecks
parsers
implementations
bit
;
}; };
} }