mirror of
https://github.com/oddlama/nixos-extra-modules.git
synced 2025-10-10 22:00:39 +02:00
feat: implement pure nix shift
This commit is contained in:
parent
8daa7e8318
commit
ddc2349760
3 changed files with 344 additions and 254 deletions
59
lib/misc.nix
59
lib/misc.nix
|
@ -1,43 +1,40 @@
|
||||||
_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
|
stringToCharacters
|
||||||
substring
|
substring
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
|
inherit (final.lib)
|
||||||
|
bit
|
||||||
|
;
|
||||||
|
|
||||||
# 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 =
|
||||||
occurrences = countOccurrences xs;
|
xs:
|
||||||
in
|
let
|
||||||
|
occurrences = countOccurrences xs;
|
||||||
|
in
|
||||||
unique (filter (x: occurrences.${x} > 1) xs);
|
unique (filter (x: occurrences.${x} > 1) xs);
|
||||||
|
|
||||||
# Concatenates all given attrsets as if calling a // b in order.
|
# 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 /
|
# True if the path or string starts with /
|
||||||
isAbsolutePath = x: substring 0 1 x == "/";
|
isAbsolutePath = x: substring 0 1 x == "/";
|
||||||
|
|
||||||
# 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 +63,17 @@ _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 = v: foldl' (acc: x: (bit.left acc 4) + hexLiteralValues.${x}) 0 (stringToCharacters v);
|
||||||
in {
|
in
|
||||||
lib =
|
{
|
||||||
prev.lib
|
lib = prev.lib // {
|
||||||
// {
|
inherit
|
||||||
inherit
|
hexToDec
|
||||||
concatAttrs
|
concatAttrs
|
||||||
countOccurrences
|
countOccurrences
|
||||||
duplicates
|
duplicates
|
||||||
hexToDec
|
isAbsolutePath
|
||||||
isAbsolutePath
|
mergeToplevelConfigs
|
||||||
mergeToplevelConfigs
|
;
|
||||||
pow
|
};
|
||||||
;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
408
lib/net.nix
408
lib/net.nix
|
@ -1,6 +1,6 @@
|
||||||
inputs: _final: prev: let
|
inputs: _final: prev:
|
||||||
inherit
|
let
|
||||||
(prev.lib)
|
inherit (prev.lib)
|
||||||
all
|
all
|
||||||
any
|
any
|
||||||
assertMsg
|
assertMsg
|
||||||
|
@ -22,24 +22,29 @@ inputs: _final: prev: let
|
||||||
;
|
;
|
||||||
|
|
||||||
# From misc.nix
|
# From misc.nix
|
||||||
inherit
|
inherit (prev.lib)
|
||||||
(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.net;
|
||||||
.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 [ "types" ]) {
|
||||||
cidr = rec {
|
cidr = rec {
|
||||||
# host :: (ip | mac | integer) -> cidr -> ip
|
# host :: (ip | mac | integer) -> cidr -> ip
|
||||||
#
|
#
|
||||||
|
@ -58,11 +63,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:
|
||||||
in
|
let
|
||||||
|
cap = libNet.cidr.capacity n;
|
||||||
|
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.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)
|
||||||
|
@ -103,37 +110,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 =
|
||||||
# Append /32 if necessary
|
addrs_:
|
||||||
addrs = map (x:
|
let
|
||||||
if hasInfix "/" x
|
# Append /32 if necessary
|
||||||
then x
|
addrs = map (x: if hasInfix "/" x then x else "${x}/32") addrs_;
|
||||||
else "${x}/32")
|
# The smallest occurring length is the first we need to start checking, since
|
||||||
addrs_;
|
# any greater cidr length represents a smaller address range which
|
||||||
# The smallest occurring length is the first we need to start checking, since
|
# wouldn't contain all of the original addresses.
|
||||||
# any greater cidr length represents a smaller address range which
|
startLength = foldl' min 32 (map libNet.cidr.length addrs);
|
||||||
# wouldn't contain all of the original addresses.
|
possibleLengths = reverseList (range 0 startLength);
|
||||||
startLength = foldl' min 32 (map libNet.cidr.length addrs);
|
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||||
possibleLengths = reverseList (range 0 startLength);
|
# used addresses.
|
||||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
firstIp = ip (head addrs);
|
||||||
# used addresses.
|
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||||
firstIp = ip (head addrs);
|
# in the list that covers all used addresses
|
||||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
bestLength = head (
|
||||||
# in the list that covers all used addresses
|
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: all (x: libNet.cidr.contains (ip x) (libNet.cidr.make len firstIp)) addrs)
|
||||||
(len:
|
possibleLengths
|
||||||
all (x:
|
);
|
||||||
libNet.cidr.contains
|
in
|
||||||
(ip x)
|
|
||||||
(libNet.cidr.make len firstIp))
|
|
||||||
addrs)
|
|
||||||
possibleLengths);
|
|
||||||
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.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,48 +143,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 =
|
||||||
# Append /128 if necessary
|
addrs_:
|
||||||
addrs = map (x:
|
let
|
||||||
if hasInfix "/" x
|
# Append /128 if necessary
|
||||||
then x
|
addrs = map (x: if hasInfix "/" x then x else "${x}/128") addrs_;
|
||||||
else "${x}/128")
|
# The smallest occurring length is the first we need to start checking, since
|
||||||
addrs_;
|
# any greater cidr length represents a smaller address range which
|
||||||
# The smallest occurring length is the first we need to start checking, since
|
# wouldn't contain all of the original addresses.
|
||||||
# any greater cidr length represents a smaller address range which
|
startLength = foldl' min 128 (map libNet.cidr.length addrs);
|
||||||
# wouldn't contain all of the original addresses.
|
possibleLengths = reverseList (range 0 startLength);
|
||||||
startLength = foldl' min 128 (map libNet.cidr.length addrs);
|
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||||
possibleLengths = reverseList (range 0 startLength);
|
# used addresses.
|
||||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
firstIp = ip (head addrs);
|
||||||
# used addresses.
|
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||||
firstIp = ip (head addrs);
|
# in the list that covers all used addresses
|
||||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
bestLength = head (
|
||||||
# in the list that covers all used addresses
|
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: all (x: libNet.cidr.contains (ip x) (libNet.cidr.make len firstIp)) addrs)
|
||||||
(len:
|
possibleLengths
|
||||||
all (x:
|
);
|
||||||
libNet.cidr.contains
|
in
|
||||||
(ip x)
|
|
||||||
(libNet.cidr.make len firstIp))
|
|
||||||
addrs)
|
|
||||||
possibleLengths);
|
|
||||||
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.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 =
|
||||||
v4_and_v6 = partition (hasInfix ":") addrs;
|
addrs:
|
||||||
in {
|
let
|
||||||
cidrv4 = mergev4 v4_and_v6.wrong;
|
v4_and_v6 = partition (hasInfix ":") addrs;
|
||||||
cidrv6 = mergev6 v4_and_v6.right;
|
in
|
||||||
};
|
{
|
||||||
|
cidrv4 = mergev4 v4_and_v6.wrong;
|
||||||
|
cidrv6 = mergev6 v4_and_v6.right;
|
||||||
|
};
|
||||||
# assignIps :: cidr -> [int | ip] -> [string] -> [ip]
|
# assignIps :: cidr -> [int | ip] -> [string] -> [ip]
|
||||||
#
|
#
|
||||||
# Assigns a semi-stable ip address from the given cidr network to each hostname.
|
# Assigns a semi-stable ip address from the given cidr network to each hostname.
|
||||||
|
@ -203,68 +199,76 @@ in {
|
||||||
#
|
#
|
||||||
# > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"]
|
# > 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"; }
|
# { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; }
|
||||||
assignIps = net: reserved: hosts: let
|
assignIps =
|
||||||
cidrSize = libNet.cidr.size net;
|
net: reserved: hosts:
|
||||||
capacity = libNet.cidr.capacity net;
|
let
|
||||||
# The base address of the network. Used to convert ip-based reservations to offsets
|
cidrSize = libNet.cidr.size net;
|
||||||
baseAddr = host 0 net;
|
capacity = libNet.cidr.capacity net;
|
||||||
# Reserve some values for the network, host and broadcast address.
|
# The base address of the network. Used to convert ip-based reservations to offsets
|
||||||
# The network and broadcast address should never be used, and we
|
baseAddr = host 0 net;
|
||||||
# want to reserve the host address for the host. We also convert
|
# Reserve some values for the network, host and broadcast address.
|
||||||
# any ips to offsets here.
|
# The network and broadcast address should never be used, and we
|
||||||
init = unique (
|
# want to reserve the host address for the host. We also convert
|
||||||
[0 (capacity - 1)]
|
# any ips to offsets here.
|
||||||
++ flip map reserved (x:
|
init = unique (
|
||||||
if builtins.typeOf x == "int"
|
[
|
||||||
then x
|
0
|
||||||
else -(libNet.ip.diff baseAddr x))
|
(capacity - 1)
|
||||||
);
|
]
|
||||||
nHosts = builtins.length hosts;
|
++ flip map reserved (x: if builtins.typeOf x == "int" then x else -(libNet.ip.diff baseAddr x))
|
||||||
nInit = builtins.length init;
|
);
|
||||||
# Pre-sort all hosts, to ensure ordering invariance
|
nHosts = builtins.length hosts;
|
||||||
sortedHosts =
|
nInit = builtins.length init;
|
||||||
warnIf
|
# Pre-sort all hosts, to ensure ordering invariance
|
||||||
((nInit + nHosts) > 0.3 * capacity)
|
sortedHosts =
|
||||||
"assignIps: hash stability may be degraded since utilization is >30%"
|
warnIf ((nInit + nHosts) > 0.3 * capacity)
|
||||||
(builtins.sort builtins.lessThan hosts);
|
"assignIps: hash stability may be degraded since utilization is >30%"
|
||||||
# Generates a hash (i.e. offset value) for a given hostname
|
(builtins.sort builtins.lessThan hosts);
|
||||||
hashElem = x:
|
# Generates a hash (i.e. offset value) for a given hostname
|
||||||
builtins.bitAnd (capacity - 1)
|
hashElem =
|
||||||
(hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
|
x:
|
||||||
# Do linear probing. Returns the first unused value at or after the given value.
|
builtins.bitAnd (capacity - 1) (
|
||||||
probe = avoid: value:
|
hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x))
|
||||||
if elem value avoid
|
);
|
||||||
# TODO lib.mod
|
# Do linear probing. Returns the first unused value at or after the given value.
|
||||||
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
|
probe =
|
||||||
# class of x modulo 2^n, so we can use bitAnd instead.
|
avoid: value:
|
||||||
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
if
|
||||||
else value;
|
elem value avoid
|
||||||
# Hash a new element and avoid assigning any existing values.
|
# TODO lib.mod
|
||||||
assignOne = {
|
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
|
||||||
assigned,
|
# class of x modulo 2^n, so we can use bitAnd instead.
|
||||||
used,
|
then
|
||||||
}: x: let
|
probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
||||||
value = probe used (hashElem x);
|
else
|
||||||
in {
|
value;
|
||||||
assigned =
|
# Hash a new element and avoid assigning any existing values.
|
||||||
assigned
|
assignOne =
|
||||||
// {
|
{
|
||||||
${x} = host value net;
|
assigned,
|
||||||
|
used,
|
||||||
|
}:
|
||||||
|
x:
|
||||||
|
let
|
||||||
|
value = probe used (hashElem x);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assigned = assigned // {
|
||||||
|
${x} = host value net;
|
||||||
|
};
|
||||||
|
used = [ value ] ++ used;
|
||||||
};
|
};
|
||||||
used = [value] ++ used;
|
in
|
||||||
};
|
assert assertMsg (
|
||||||
in
|
cidrSize >= 2 && cidrSize <= 62
|
||||||
assert assertMsg (cidrSize >= 2 && cidrSize <= 62)
|
) "assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
|
||||||
"assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
|
|
||||||
assert assertMsg (nHosts <= capacity - nInit)
|
assert assertMsg (nHosts <= capacity - nInit)
|
||||||
"assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString 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
|
# 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;
|
|
||||||
};
|
};
|
||||||
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 +279,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:
|
||||||
pre = substring 0 1 added;
|
let
|
||||||
suf = substring 2 (-1) added;
|
added = libNet.mac.add base offset;
|
||||||
in "${pre}2${suf}";
|
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]
|
# 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,63 +304,64 @@ 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
|
||||||
init = unique (
|
capacity = libNet.bit.left 1 size;
|
||||||
flip map reserved (x:
|
baseAsInt = libNet.net.mac.diff base "00:00:00:00:00:00";
|
||||||
if builtins.typeOf x == "int"
|
init = unique (
|
||||||
then x
|
flip map reserved (x: if builtins.typeOf x == "int" then x else libNet.net.mac.diff x base)
|
||||||
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 ((nInit + nHosts) > 0.3 * capacity)
|
||||||
warnIf
|
"assignMacs: hash stability may be degraded since utilization is >30%"
|
||||||
((nInit + nHosts) > 0.3 * capacity)
|
(builtins.sort builtins.lessThan hosts);
|
||||||
"assignMacs: hash stability may be degraded since utilization is >30%"
|
# Generates a hash (i.e. offset value) for a given hostname
|
||||||
(builtins.sort builtins.lessThan hosts);
|
hashElem =
|
||||||
# Generates a hash (i.e. offset value) for a given hostname
|
x: builtins.bitAnd (capacity - 1) (hexToDec (substring 0 16 (builtins.hashString "sha256" x)));
|
||||||
hashElem = x:
|
# Do linear probing. Returns the first unused value at or after the given value.
|
||||||
builtins.bitAnd (capacity - 1)
|
probe =
|
||||||
(hexToDec (substring 0 16 (builtins.hashString "sha256" x)));
|
avoid: value:
|
||||||
# Do linear probing. Returns the first unused value at or after the given value.
|
if
|
||||||
probe = avoid: value:
|
elem value avoid
|
||||||
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
|
||||||
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
||||||
else value;
|
else
|
||||||
# Hash a new element and avoid assigning any existing values.
|
value;
|
||||||
assignOne = {
|
# Hash a new element and avoid assigning any existing values.
|
||||||
assigned,
|
assignOne =
|
||||||
used,
|
{
|
||||||
}: x: let
|
assigned,
|
||||||
value = probe used (hashElem x);
|
used,
|
||||||
in {
|
}:
|
||||||
assigned =
|
x:
|
||||||
assigned
|
let
|
||||||
// {
|
value = probe used (hashElem x);
|
||||||
${x} = libNet.mac.add value base;
|
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].";
|
||||||
in
|
assert assertMsg (
|
||||||
assert assertMsg (size >= 2 && size <= 62)
|
builtins.bitAnd (capacity - 1) baseAsInt == 0
|
||||||
"assignMacs: size=${toString size} is not in [2, 62].";
|
) "assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
|
||||||
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)
|
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.types;
|
||||||
|
|
131
lib/netu.nix
131
lib/netu.nix
|
@ -460,20 +460,111 @@ let
|
||||||
|
|
||||||
bit =
|
bit =
|
||||||
let
|
let
|
||||||
shift =
|
lut = {
|
||||||
n: x:
|
"0" = 1;
|
||||||
if n < 0 then
|
"1" = 2;
|
||||||
x * math.pow 2 (-n)
|
"2" = 4;
|
||||||
|
"3" = 8;
|
||||||
|
"4" = 16;
|
||||||
|
"5" = 32;
|
||||||
|
"6" = 64;
|
||||||
|
"7" = 128;
|
||||||
|
"8" = 256;
|
||||||
|
"9" = 512;
|
||||||
|
"10" = 1024;
|
||||||
|
"11" = 2048;
|
||||||
|
"12" = 4096;
|
||||||
|
"13" = 8192;
|
||||||
|
"14" = 16384;
|
||||||
|
"15" = 32768;
|
||||||
|
"16" = 65536;
|
||||||
|
"17" = 131072;
|
||||||
|
"18" = 262144;
|
||||||
|
"19" = 524288;
|
||||||
|
"20" = 1048576;
|
||||||
|
"21" = 2097152;
|
||||||
|
"22" = 4194304;
|
||||||
|
"23" = 8388608;
|
||||||
|
"24" = 16777216;
|
||||||
|
"25" = 33554432;
|
||||||
|
"26" = 67108864;
|
||||||
|
"27" = 134217728;
|
||||||
|
"28" = 268435456;
|
||||||
|
"29" = 536870912;
|
||||||
|
"30" = 1073741824;
|
||||||
|
"31" = 2147483648;
|
||||||
|
"32" = 4294967296;
|
||||||
|
"33" = 8589934592;
|
||||||
|
"34" = 17179869184;
|
||||||
|
"35" = 34359738368;
|
||||||
|
"36" = 68719476736;
|
||||||
|
"37" = 137438953472;
|
||||||
|
"38" = 274877906944;
|
||||||
|
"39" = 549755813888;
|
||||||
|
"40" = 1099511627776;
|
||||||
|
"41" = 2199023255552;
|
||||||
|
"42" = 4398046511104;
|
||||||
|
"43" = 8796093022208;
|
||||||
|
"44" = 17592186044416;
|
||||||
|
"45" = 35184372088832;
|
||||||
|
"46" = 70368744177664;
|
||||||
|
"47" = 140737488355328;
|
||||||
|
"48" = 281474976710656;
|
||||||
|
"49" = 562949953421312;
|
||||||
|
"50" = 1125899906842624;
|
||||||
|
"51" = 2251799813685248;
|
||||||
|
"52" = 4503599627370496;
|
||||||
|
"53" = 9007199254740992;
|
||||||
|
"54" = 18014398509481984;
|
||||||
|
"55" = 36028797018963968;
|
||||||
|
"56" = 72057594037927936;
|
||||||
|
"57" = 144115188075855872;
|
||||||
|
"58" = 288230376151711744;
|
||||||
|
"59" = 576460752303423488;
|
||||||
|
"60" = 1152921504606846976;
|
||||||
|
"61" = 2305843009213693952;
|
||||||
|
"62" = 4611686018427387904;
|
||||||
|
};
|
||||||
|
intmin = (-9223372036854775807) - 1;
|
||||||
|
intmax = 9223372036854775807;
|
||||||
|
left =
|
||||||
|
a: b:
|
||||||
|
if a >= 64 then
|
||||||
|
# It's allowed to shift out all bits
|
||||||
|
0
|
||||||
|
else if a == 0 then
|
||||||
|
b
|
||||||
|
else if a < 0 then
|
||||||
|
right (-a) b
|
||||||
else
|
else
|
||||||
let
|
let
|
||||||
safeDiv = n: d: if d == 0 then 0 else n / d;
|
inv = 63 - a;
|
||||||
d = math.pow 2 n;
|
mask = if inv == 63 then intmax else lut.${toString inv} - 1;
|
||||||
|
masked = and b mask;
|
||||||
|
checker = if inv == 63 then intmin else lut.${toString inv};
|
||||||
|
negate = (and b checker) != 0;
|
||||||
|
mult = if a == 63 then intmin else lut.${toString a};
|
||||||
|
result = masked * mult;
|
||||||
in
|
in
|
||||||
if x < 0 then not (safeDiv (not x) d) else safeDiv x d;
|
if !negate then result else intmin + result;
|
||||||
|
|
||||||
left = n: shift (-n);
|
right =
|
||||||
|
a: b:
|
||||||
right = shift;
|
if a >= 64 then
|
||||||
|
0
|
||||||
|
else if a == 0 then
|
||||||
|
b
|
||||||
|
else if a < 0 then
|
||||||
|
left (-a) b
|
||||||
|
else
|
||||||
|
let
|
||||||
|
masked = and b intmax;
|
||||||
|
negate = b < 0;
|
||||||
|
result = masked / lut.${toString a};
|
||||||
|
inv = 63 - a;
|
||||||
|
highest_bit = lut.${toString inv};
|
||||||
|
in
|
||||||
|
if !negate then result else result + highest_bit;
|
||||||
|
|
||||||
and = builtins.bitAnd;
|
and = builtins.bitAnd;
|
||||||
|
|
||||||
|
@ -505,15 +596,6 @@ 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 =
|
||||||
|
@ -951,8 +1033,6 @@ let
|
||||||
lower = bit.mask 4 n;
|
lower = bit.mask 4 n;
|
||||||
in
|
in
|
||||||
"${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}";
|
"${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}";
|
||||||
in
|
|
||||||
let
|
|
||||||
a = bit.mask 8 (bit.right 40 address.mac);
|
a = bit.mask 8 (bit.right 40 address.mac);
|
||||||
b = bit.mask 8 (bit.right 32 address.mac);
|
b = bit.mask 8 (bit.right 32 address.mac);
|
||||||
c = bit.mask 8 (bit.right 24 address.mac);
|
c = bit.mask 8 (bit.right 24 address.mac);
|
||||||
|
@ -1390,6 +1470,13 @@ in
|
||||||
|
|
||||||
{
|
{
|
||||||
lib = {
|
lib = {
|
||||||
inherit net;
|
inherit
|
||||||
|
arithmetic
|
||||||
|
net
|
||||||
|
typechecks
|
||||||
|
parsers
|
||||||
|
implementations
|
||||||
|
bit
|
||||||
|
;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue