Compare commits
1 commit
main
...
lix-compat
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4c3af09fb7 |
5 changed files with 194 additions and 325 deletions
20
flake.lock
generated
20
flake.lock
generated
|
@ -75,26 +75,13 @@
|
|||
"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": {
|
||||
"locked": {
|
||||
"lastModified": 1737885589,
|
||||
"narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=",
|
||||
"lastModified": 1739214665,
|
||||
"narHash": "sha256-26L8VAu3/1YRxS8MHgBOyOM8xALdo6N0I04PgorE7UM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8",
|
||||
"rev": "64e75cd44acf21c7933d61d7721e812eac1b5a0a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -142,7 +129,6 @@
|
|||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-parts": "flake-parts",
|
||||
"lib-net": "lib-net",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
|
|
|
@ -9,11 +9,6 @@
|
|||
|
||||
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";
|
||||
|
||||
pre-commit-hooks = {
|
||||
|
|
59
lib/misc.nix
59
lib/misc.nix
|
@ -1,43 +1,37 @@
|
|||
_inputs: _final: prev: let
|
||||
inherit
|
||||
(prev.lib)
|
||||
_inputs: _final: prev:
|
||||
let
|
||||
inherit (prev.lib)
|
||||
filter
|
||||
foldl'
|
||||
genAttrs
|
||||
genList
|
||||
mergeAttrs
|
||||
mkMerge
|
||||
stringToCharacters
|
||||
substring
|
||||
unique
|
||||
stringToCharacters
|
||||
;
|
||||
|
||||
# Counts how often each element occurrs in xs.
|
||||
# Elements must be strings.
|
||||
countOccurrences =
|
||||
foldl'
|
||||
(acc: x: acc // {${x} = (acc.${x} or 0) + 1;})
|
||||
{};
|
||||
countOccurrences = foldl' (acc: x: acc // { ${x} = (acc.${x} or 0) + 1; }) { };
|
||||
|
||||
# Returns all elements in xs that occur at least twice
|
||||
duplicates = xs: let
|
||||
occurrences = countOccurrences xs;
|
||||
in
|
||||
duplicates =
|
||||
xs:
|
||||
let
|
||||
occurrences = countOccurrences xs;
|
||||
in
|
||||
unique (filter (x: occurrences.${x} > 1) xs);
|
||||
|
||||
# Concatenates all given attrsets as if calling a // b in order.
|
||||
concatAttrs = foldl' mergeAttrs {};
|
||||
concatAttrs = foldl' mergeAttrs { };
|
||||
|
||||
# True if the path or string starts with /
|
||||
isAbsolutePath = x: substring 0 1 x == "/";
|
||||
|
||||
# Merges all given attributes from the given attrsets using mkMerge.
|
||||
# Useful to merge several top-level configs in a module.
|
||||
mergeToplevelConfigs = keys: 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);
|
||||
mergeToplevelConfigs = keys: attrs: genAttrs keys (attr: mkMerge (map (x: x.${attr} or { }) attrs));
|
||||
|
||||
hexLiteralValues = {
|
||||
"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),
|
||||
# after that the sign bit will overflow.
|
||||
hexToDec = v: foldl' (acc: x: acc * 16 + hexLiteralValues.${x}) 0 (stringToCharacters v);
|
||||
in {
|
||||
lib =
|
||||
prev.lib
|
||||
// {
|
||||
inherit
|
||||
concatAttrs
|
||||
countOccurrences
|
||||
duplicates
|
||||
hexToDec
|
||||
isAbsolutePath
|
||||
mergeToplevelConfigs
|
||||
pow
|
||||
;
|
||||
};
|
||||
hexToDec =
|
||||
v: foldl' (acc: x: (builtins.bitShiftLeft acc 4) + hexLiteralValues.${x}) 0 (stringToCharacters v);
|
||||
in
|
||||
{
|
||||
lib = prev.lib // {
|
||||
inherit
|
||||
hexToDec
|
||||
concatAttrs
|
||||
countOccurrences
|
||||
duplicates
|
||||
isAbsolutePath
|
||||
mergeToplevelConfigs
|
||||
;
|
||||
};
|
||||
}
|
||||
|
|
367
lib/net.nix
367
lib/net.nix
|
@ -1,6 +1,6 @@
|
|||
inputs: _final: prev: let
|
||||
inherit
|
||||
(prev.lib)
|
||||
inputs: _final: prev:
|
||||
let
|
||||
inherit (prev.lib)
|
||||
all
|
||||
any
|
||||
assertMsg
|
||||
|
@ -19,27 +19,27 @@ inputs: _final: prev: let
|
|||
substring
|
||||
unique
|
||||
warnIf
|
||||
;
|
||||
|
||||
# From misc.nix
|
||||
inherit
|
||||
(prev.lib)
|
||||
hexToDec
|
||||
pow
|
||||
;
|
||||
|
||||
# IP address math library
|
||||
# https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba
|
||||
# Plus some extensions by us
|
||||
libNet =
|
||||
(import "${inputs.lib-net}/net.nix" {
|
||||
(import ./netu.nix {
|
||||
inherit (inputs.nixpkgs) lib;
|
||||
})
|
||||
.lib
|
||||
.net;
|
||||
in {
|
||||
}).lib;
|
||||
in
|
||||
{
|
||||
lib = recursiveUpdate prev.lib {
|
||||
net = recursiveUpdate (removeAttrs libNet ["types"]) {
|
||||
inherit (libNet)
|
||||
arithmetic
|
||||
typechecks
|
||||
bit
|
||||
implementations
|
||||
parsers
|
||||
;
|
||||
net = recursiveUpdate (removeAttrs libNet.net [ "types" ]) {
|
||||
cidr = rec {
|
||||
# host :: (ip | mac | integer) -> cidr -> ip
|
||||
#
|
||||
|
@ -58,11 +58,13 @@ in {
|
|||
# "192.168.1.0"
|
||||
# > net.cidr.host (-257) "192.168.1.0/24"
|
||||
# <fails with an error message>
|
||||
host = i: n: let
|
||||
cap = libNet.cidr.capacity n;
|
||||
in
|
||||
host =
|
||||
i: n:
|
||||
let
|
||||
cap = libNet.net.cidr.capacity n;
|
||||
in
|
||||
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
|
||||
#
|
||||
# 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"
|
||||
# "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
|
||||
#
|
||||
# Returns just the ip part of the cidr.
|
||||
|
@ -93,7 +95,7 @@ in {
|
|||
#
|
||||
# > net.cidr.canonicalize "192.168.1.100/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)
|
||||
#
|
||||
# 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"]
|
||||
# "192.168.0.0/21"
|
||||
mergev4 = addrs_: let
|
||||
# Append /32 if necessary
|
||||
addrs = map (x:
|
||||
if hasInfix "/" x
|
||||
then x
|
||||
else "${x}/32")
|
||||
addrs_;
|
||||
# The smallest occurring length is the first we need to start checking, since
|
||||
# any greater cidr length represents a smaller address range which
|
||||
# wouldn't contain all of the original addresses.
|
||||
startLength = foldl' min 32 (map libNet.cidr.length addrs);
|
||||
possibleLengths = reverseList (range 0 startLength);
|
||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||
# used addresses.
|
||||
firstIp = ip (head addrs);
|
||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||
# in the list that covers all used addresses
|
||||
bestLength = head (filter
|
||||
# All given addresses must be contained by the generated address.
|
||||
(len:
|
||||
all (x:
|
||||
libNet.cidr.contains
|
||||
(ip x)
|
||||
(libNet.cidr.make len firstIp))
|
||||
addrs)
|
||||
possibleLengths);
|
||||
in
|
||||
mergev4 =
|
||||
addrs_:
|
||||
let
|
||||
# Append /32 if necessary
|
||||
addrs = map (x: if hasInfix "/" x then x else "${x}/32") addrs_;
|
||||
# The smallest occurring length is the first we need to start checking, since
|
||||
# any greater cidr length represents a smaller address range which
|
||||
# wouldn't contain all of the original addresses.
|
||||
startLength = foldl' min 32 (map libNet.net.cidr.length addrs);
|
||||
possibleLengths = reverseList (range 0 startLength);
|
||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||
# used addresses.
|
||||
firstIp = ip (head addrs);
|
||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||
# in the list that covers all used addresses
|
||||
bestLength = head (
|
||||
filter
|
||||
# All given addresses must be contained by the generated address.
|
||||
(len: all (x: libNet.net.cidr.contains (ip x) (libNet.net.cidr.make len firstIp)) addrs)
|
||||
possibleLengths
|
||||
);
|
||||
in
|
||||
assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
|
||||
if addrs == []
|
||||
then null
|
||||
else libNet.cidr.make bestLength firstIp;
|
||||
if addrs == [ ] then null else libNet.net.cidr.make bestLength firstIp;
|
||||
# mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null)
|
||||
#
|
||||
# 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"]
|
||||
# "fd00:c000::/18"
|
||||
mergev6 = addrs_: let
|
||||
# Append /128 if necessary
|
||||
addrs = map (x:
|
||||
if hasInfix "/" x
|
||||
then x
|
||||
else "${x}/128")
|
||||
addrs_;
|
||||
# The smallest occurring length is the first we need to start checking, since
|
||||
# any greater cidr length represents a smaller address range which
|
||||
# wouldn't contain all of the original addresses.
|
||||
startLength = foldl' min 128 (map libNet.cidr.length addrs);
|
||||
possibleLengths = reverseList (range 0 startLength);
|
||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||
# used addresses.
|
||||
firstIp = ip (head addrs);
|
||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||
# in the list that covers all used addresses
|
||||
bestLength = head (filter
|
||||
# All given addresses must be contained by the generated address.
|
||||
(len:
|
||||
all (x:
|
||||
libNet.cidr.contains
|
||||
(ip x)
|
||||
(libNet.cidr.make len firstIp))
|
||||
addrs)
|
||||
possibleLengths);
|
||||
in
|
||||
mergev6 =
|
||||
addrs_:
|
||||
let
|
||||
# Append /128 if necessary
|
||||
addrs = map (x: if hasInfix "/" x then x else "${x}/128") addrs_;
|
||||
# The smallest occurring length is the first we need to start checking, since
|
||||
# any greater cidr length represents a smaller address range which
|
||||
# wouldn't contain all of the original addresses.
|
||||
startLength = foldl' min 128 (map libNet.net.cidr.length addrs);
|
||||
possibleLengths = reverseList (range 0 startLength);
|
||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||
# used addresses.
|
||||
firstIp = ip (head addrs);
|
||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||
# in the list that covers all used addresses
|
||||
bestLength = head (
|
||||
filter
|
||||
# All given addresses must be contained by the generated address.
|
||||
(len: all (x: libNet.net.cidr.contains (ip x) (libNet.net.cidr.make len firstIp)) addrs)
|
||||
possibleLengths
|
||||
);
|
||||
in
|
||||
assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
|
||||
if addrs == []
|
||||
then null
|
||||
else libNet.cidr.make bestLength firstIp;
|
||||
if addrs == [ ] then null else libNet.net.cidr.make bestLength firstIp;
|
||||
# merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); }
|
||||
#
|
||||
# Returns the smallest cidr network that includes all given networks,
|
||||
# but yields two separate result for all given ipv4 and ipv6 addresses.
|
||||
# Equivalent to calling mergev4 and mergev6 on a partition individually.
|
||||
merge = addrs: let
|
||||
v4_and_v6 = partition (hasInfix ":") addrs;
|
||||
in {
|
||||
cidrv4 = mergev4 v4_and_v6.wrong;
|
||||
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;
|
||||
merge =
|
||||
addrs:
|
||||
let
|
||||
v4_and_v6 = partition (hasInfix ":") addrs;
|
||||
in
|
||||
{
|
||||
cidrv4 = mergev4 v4_and_v6.wrong;
|
||||
cidrv6 = mergev6 v4_and_v6.right;
|
||||
};
|
||||
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 {
|
||||
# Checks whether the given address (with or without cidr notation) is an ipv4 address.
|
||||
|
@ -275,11 +186,14 @@ in {
|
|||
mac = {
|
||||
# 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.
|
||||
addPrivate = base: offset: let
|
||||
added = libNet.mac.add base offset;
|
||||
pre = substring 0 1 added;
|
||||
suf = substring 2 (-1) added;
|
||||
in "${pre}2${suf}";
|
||||
addPrivate =
|
||||
base: offset:
|
||||
let
|
||||
added = libNet.net.mac.add base offset;
|
||||
pre = substring 0 1 added;
|
||||
suf = substring 2 (-1) added;
|
||||
in
|
||||
"${pre}2${suf}";
|
||||
# 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.
|
||||
|
@ -297,65 +211,66 @@ in {
|
|||
#
|
||||
# > 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"; }
|
||||
assignMacs = base: size: reserved: hosts: let
|
||||
capacity = pow 2 size;
|
||||
baseAsInt = libNet.mac.diff base "00:00:00:00:00:00";
|
||||
init = unique (
|
||||
flip map reserved (x:
|
||||
if builtins.typeOf x == "int"
|
||||
then x
|
||||
else libNet.mac.diff x base)
|
||||
);
|
||||
nHosts = builtins.length hosts;
|
||||
nInit = builtins.length init;
|
||||
# Pre-sort all hosts, to ensure ordering invariance
|
||||
sortedHosts =
|
||||
warnIf
|
||||
((nInit + nHosts) > 0.3 * capacity)
|
||||
"assignMacs: 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 (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} = libNet.mac.add value base;
|
||||
assignMacs =
|
||||
base: size: reserved: hosts:
|
||||
let
|
||||
capacity = builtins.bitShiftLeft 1 size;
|
||||
baseAsInt = libNet.net.mac.diff base "00:00:00:00:00:00";
|
||||
init = unique (
|
||||
flip map reserved (x: if builtins.typeOf x == "int" then x else libNet.net.mac.diff x base)
|
||||
);
|
||||
nHosts = builtins.length hosts;
|
||||
nInit = builtins.length init;
|
||||
# Pre-sort all hosts, to ensure ordering invariance
|
||||
sortedHosts =
|
||||
warnIf ((nInit + nHosts) > 0.3 * capacity)
|
||||
"assignMacs: 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 (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} = libNet.net.mac.add value base;
|
||||
};
|
||||
used = [ value ] ++ used;
|
||||
};
|
||||
used = [value] ++ used;
|
||||
};
|
||||
in
|
||||
assert assertMsg (size >= 2 && size <= 62)
|
||||
"assignMacs: size=${toString size} is not in [2, 62].";
|
||||
assert assertMsg (builtins.bitAnd (capacity - 1) baseAsInt == 0)
|
||||
"assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
|
||||
in
|
||||
assert assertMsg (size >= 2 && size <= 62) "assignMacs: size=${toString size} is not in [2, 62].";
|
||||
assert assertMsg (
|
||||
builtins.bitAnd (capacity - 1) baseAsInt == 0
|
||||
) "assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
|
||||
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
|
||||
(foldl' assignOne {
|
||||
assigned = {};
|
||||
used = init;
|
||||
}
|
||||
sortedHosts)
|
||||
.assigned;
|
||||
(foldl' assignOne {
|
||||
assigned = { };
|
||||
used = init;
|
||||
} sortedHosts).assigned;
|
||||
};
|
||||
};
|
||||
types.net = libNet.types;
|
||||
types.net = libNet.net.types;
|
||||
};
|
||||
}
|
||||
|
|
68
lib/netu.nix
68
lib/netu.nix
|
@ -2,13 +2,10 @@
|
|||
lib ? null,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
net =
|
||||
{
|
||||
ip = {
|
||||
|
||||
# add :: (ip | mac | integer) -> ip -> ip
|
||||
#
|
||||
# Examples:
|
||||
|
@ -96,7 +93,6 @@ let
|
|||
};
|
||||
|
||||
mac = {
|
||||
|
||||
# add :: (ip | mac | integer) -> mac -> mac
|
||||
#
|
||||
# Examples:
|
||||
|
@ -334,7 +330,6 @@ let
|
|||
cidr' = typechecks.cidr function "cidr" cidr;
|
||||
in
|
||||
builders.cidr (implementations.cidr.subnet length' netnum' cidr');
|
||||
|
||||
};
|
||||
}
|
||||
// (
|
||||
|
@ -344,7 +339,6 @@ let
|
|||
{
|
||||
types =
|
||||
let
|
||||
|
||||
mkParsedOptionType =
|
||||
{
|
||||
name,
|
||||
|
@ -385,10 +379,8 @@ let
|
|||
// {
|
||||
description = type.description + " in ${builtins.concatStringsSep " or " cidrs}";
|
||||
};
|
||||
|
||||
in
|
||||
rec {
|
||||
|
||||
ip = mkParsedOptionType {
|
||||
name = "ip";
|
||||
description = "IPv4 or IPv6 address";
|
||||
|
@ -449,7 +441,6 @@ let
|
|||
parser = parsers.mac;
|
||||
builder = builders.mac;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -460,20 +451,23 @@ let
|
|||
|
||||
bit =
|
||||
let
|
||||
shift =
|
||||
n: x:
|
||||
if n < 0 then
|
||||
x * math.pow 2 (-n)
|
||||
left =
|
||||
a: b:
|
||||
if a >= 64 then
|
||||
0
|
||||
else if a < 0 then
|
||||
right (-a) b
|
||||
else
|
||||
let
|
||||
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;
|
||||
builtins.bitShiftLeft b a;
|
||||
|
||||
left = n: shift (-n);
|
||||
|
||||
right = shift;
|
||||
right =
|
||||
a: b:
|
||||
if a >= 64 then
|
||||
0
|
||||
else if a < 0 then
|
||||
left (-a) b
|
||||
else
|
||||
builtins.bitShiftRight b a;
|
||||
|
||||
and = builtins.bitAnd;
|
||||
|
||||
|
@ -505,20 +499,10 @@ let
|
|||
clamp =
|
||||
a: 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 =
|
||||
let
|
||||
|
||||
# fmap :: (a -> b) -> parser a -> parser b
|
||||
fmap = f: ma: bind ma (a: pure (f a));
|
||||
|
||||
|
@ -766,7 +750,6 @@ let
|
|||
afterDoubleColon = alt ipv4' (
|
||||
liftA2 list.cons hextet (alt (then_ colon afterDoubleColon) (pure [ ]))
|
||||
);
|
||||
|
||||
in
|
||||
bind (alt (then_ (string "::") (doubleColon 0)) (part 0)) fromHextets;
|
||||
|
||||
|
@ -791,7 +774,6 @@ let
|
|||
};
|
||||
in
|
||||
liftA6 fromOctets octet octet' octet' octet' octet' octet';
|
||||
|
||||
in
|
||||
{
|
||||
ipv4 = run ipv4;
|
||||
|
@ -806,7 +788,6 @@ let
|
|||
|
||||
builders =
|
||||
let
|
||||
|
||||
ipv4 =
|
||||
address:
|
||||
let
|
||||
|
@ -832,7 +813,6 @@ let
|
|||
ipv6 =
|
||||
address:
|
||||
let
|
||||
|
||||
digits = "0123456789abcdef";
|
||||
|
||||
toHexString =
|
||||
|
@ -843,7 +823,6 @@ let
|
|||
prefix = if rest == 0 then "" else toHexString rest;
|
||||
in
|
||||
"${prefix}${builtins.substring current 1 digits}";
|
||||
|
||||
in
|
||||
if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) then
|
||||
"::${ipv4 { ipv4 = address.ipv6.d; }}"
|
||||
|
@ -851,7 +830,6 @@ let
|
|||
"::ffff:${ipv4 { ipv4 = address.ipv6.d; }}"
|
||||
else
|
||||
let
|
||||
|
||||
a = bit.right 16 address.ipv6.a;
|
||||
b = bit.mask 16 address.ipv6.a;
|
||||
c = bit.right 16 address.ipv6.b;
|
||||
|
@ -961,7 +939,6 @@ let
|
|||
f = bit.mask 8 (bit.right 0 address.mac);
|
||||
in
|
||||
"${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}";
|
||||
|
||||
in
|
||||
{
|
||||
inherit
|
||||
|
@ -1096,7 +1073,7 @@ let
|
|||
diff =
|
||||
a: b:
|
||||
let
|
||||
toIPv6 = coerce ({ ipv6.a = 0; });
|
||||
toIPv6 = coerce { ipv6.a = 0; };
|
||||
result = (subtract b (toIPv6 a)).ipv6;
|
||||
max32 = bit.left 32 1 - 1;
|
||||
in
|
||||
|
@ -1352,7 +1329,6 @@ let
|
|||
|
||||
typechecks =
|
||||
let
|
||||
|
||||
fail =
|
||||
description: function: argument:
|
||||
builtins.throw "${function}: ${argument} parameter must be ${description}";
|
||||
|
@ -1369,7 +1345,6 @@ let
|
|||
result = parser input;
|
||||
in
|
||||
if builtins.isNull result then error else result;
|
||||
|
||||
in
|
||||
{
|
||||
int =
|
||||
|
@ -1385,11 +1360,16 @@ let
|
|||
else
|
||||
meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
lib = {
|
||||
inherit net;
|
||||
inherit
|
||||
arithmetic
|
||||
net
|
||||
typechecks
|
||||
parsers
|
||||
implementations
|
||||
bit
|
||||
;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue