You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
6.8 KiB
Nix
239 lines
6.8 KiB
Nix
{ lib, self, ... }@flakeArg:
|
|
let
|
|
inherit (builtins)
|
|
concatMap
|
|
concatStringsSep
|
|
mapAttrs
|
|
elemAt
|
|
fromTOML
|
|
genList
|
|
isAttrs
|
|
length
|
|
match
|
|
replaceStrings
|
|
;
|
|
inherit (lib.asserts) assertMsg;
|
|
inherit (lib.lists)
|
|
count
|
|
imap1
|
|
last
|
|
singleton
|
|
sublist
|
|
;
|
|
inherit (lib.math) binToInt intToBin;
|
|
inherit (lib.strings)
|
|
commonPrefixLength
|
|
hasInfix
|
|
splitString
|
|
substring
|
|
toIntBase10
|
|
toLower
|
|
;
|
|
inherit (lib.trivial) flip pipe toHexString;
|
|
fixedWidthStrSuffix =
|
|
width: filler: str:
|
|
let
|
|
strw = lib.stringLength str;
|
|
reqWidth = width - (lib.stringLength filler);
|
|
in
|
|
assert lib.assertMsg (strw <= width)
|
|
"fixedWidthString: requested string length (${toString width}) must not be shorter than actual length (${toString strw})";
|
|
if strw == width then str else fixedWidthStrSuffix reqWidth filler str + filler;
|
|
fromHexString = str: (fromTOML "v=0x${str}").v; # TODO not (yet) available in nixpkgs.lib
|
|
toHex = str: toLower (toHexString str);
|
|
toIpClass =
|
|
ipArg:
|
|
let
|
|
statics = {
|
|
ipv4 = {
|
|
_group_bits = 8;
|
|
_group_count = 4;
|
|
_group_sep = ".";
|
|
_group_toInt = toIntBase10;
|
|
compressed = ip.decCompressed;
|
|
shorted = ip.decCompressed;
|
|
};
|
|
ipv6 = {
|
|
_group_bits = 16;
|
|
_group_count = 8;
|
|
_group_sep = ":";
|
|
_group_toInt = fromHexString;
|
|
compressed = ip.hexShorted; # TODO temporary
|
|
shorted = ip.hexShorted;
|
|
};
|
|
};
|
|
ip =
|
|
statics.${ipArg.version} # avoid recursion error
|
|
// {
|
|
type = "ipAddress";
|
|
# internal operators
|
|
__toString = s: s.cidrCompressed;
|
|
# decimal
|
|
decGroups = map ip._group_toInt ip._groups;
|
|
decCompressed = concatStringsSep ip._group_sep (map toString ip.decGroups);
|
|
# binary
|
|
binGroups = map (v: intToBin ip._group_bits) ip.decGroups; # shortcut compared to hexGroups
|
|
binRaw = concatStringsSep "" ip.binGroups;
|
|
# hex
|
|
hexGroups = map toHex ip.decGroups;
|
|
hexShorted = concatStringsSep ":" ip.hexGroups;
|
|
# TODO hexCompressed
|
|
# TODO hexExploded
|
|
# network
|
|
binRawNet = substring 0 ip.cidrInt ip.binRaw;
|
|
_cidr_max = ip._group_count * ip._group_bits;
|
|
cidrInt = if ip._cidrGroup == null then ip._cidr_max else toIntBase10 ip._cidrGroup;
|
|
cidrCompressed = "${ip.compressed}/${ip.cidrStr}";
|
|
cidrShorted = "${ip.shorted}/${ip.cidrStr}";
|
|
cidrStr = "${toString ip.cidrInt}";
|
|
# helpers
|
|
isCompatible =
|
|
o:
|
|
assert self.isParsedIP o;
|
|
ip.type == o.type;
|
|
__verifyCompat = fun: o: if ip.isCompatible o then fun o else false;
|
|
split = map (self.parseBinNet ip.version) [
|
|
"${ip.binRawNet}0"
|
|
"${ip.binRawNet}1"
|
|
];
|
|
}
|
|
// mapAttrs (_: ip.__verifyCompat) {
|
|
contains = o: ip.cidrInt <= commonPrefixLength ip.binRawNet (o.binRawNet);
|
|
equals = o: ip.decGroups == o.decGroups && ip.cidrInt == o.cidrInt;
|
|
sameNetwork = o: ip.binRawNet == o.binRawNet;
|
|
}
|
|
// ipArg;
|
|
in
|
|
assert assertMsg (length ip._groups == ip._group_count)
|
|
"invalid IP group count, expected ${toString ip._group_count}, got: ${toString (length ip._groups)}, input: ${ipArg._input} (bug, please report)";
|
|
assert ip.cidrInt <= ip._cidr_max;
|
|
ip;
|
|
in
|
|
rec {
|
|
|
|
formatMAC =
|
|
let
|
|
badChars = [
|
|
"."
|
|
":"
|
|
"_"
|
|
"-"
|
|
];
|
|
goodChars = map (x: "") badChars;
|
|
in
|
|
mac:
|
|
pipe mac [
|
|
(replaceStrings badChars goodChars)
|
|
toLower
|
|
];
|
|
|
|
isParsedIP = x: isAttrs x && x.type or null == "ipAddress";
|
|
isParsedIPv4 = x: isParsedIP x && x.version == "ipv4";
|
|
isParsedIPv6 = x: isParsedIP x && x.version == "ipv6";
|
|
|
|
parseIP = ip: if hasInfix ":" ip then parseIPv6 ip else parseIPv4 ip;
|
|
|
|
parseIPv4 =
|
|
ipStr:
|
|
let
|
|
parsed = match ''^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)(/([0-9]+))?$'' ipStr;
|
|
ip = toIpClass {
|
|
version = "ipv4";
|
|
_input = ipStr;
|
|
_cidrGroup = last parsed;
|
|
_groups = substring 0 4 parsed;
|
|
};
|
|
in
|
|
assert parsed != null; # TODO improve
|
|
ip;
|
|
|
|
parseIPv6 =
|
|
# TODO add support for IPv4 mapped addresses
|
|
ipStr:
|
|
let
|
|
parsed = match ''^(([0-9a-f]{0,4}:){1,7}[0-9a-f]{0,4})(/([0-9]+))?$'' (toLower ipStr);
|
|
rawGroups = pipe parsed [
|
|
(flip elemAt 0)
|
|
(splitString ":")
|
|
# first & last zeros might be omitted as well, but are not a compression artifact
|
|
(imap1 (i: x: if (i == 1 || i == length rawGroups) && x == "" then "0" else x))
|
|
];
|
|
groups = flip concatMap rawGroups (
|
|
x: if x == "" then genList (_: "0") (9 - length rawGroups) else singleton x
|
|
);
|
|
ip = toIpClass {
|
|
version = "ipv6";
|
|
_input = ipStr;
|
|
_cidrGroup = last parsed;
|
|
_groups = groups;
|
|
};
|
|
in
|
|
assert parsed != null;
|
|
assert count (g: g == "") rawGroups <= 1;
|
|
ip;
|
|
|
|
parseIPv6IfId =
|
|
ipStr:
|
|
let
|
|
parsed = match ''^(([0-9a-f]{0,4}:){1,3}[0-9a-f]{0,4})$'' (toLower ipStr);
|
|
rawGroups = pipe parsed [
|
|
(flip elemAt 0)
|
|
(splitString ":")
|
|
# first & last zeros might be omitted as well, but are not a compression artifact
|
|
(imap1 (i: x: if (i == 1 || i == length rawGroups) && x == "" then "0" else x))
|
|
];
|
|
groups = flip concatMap rawGroups (
|
|
x: if x == "" then genList (_: "0") (5 - length rawGroups) else singleton x
|
|
);
|
|
ip = toIpClass {
|
|
type = "ipInterfaceIdentifier";
|
|
version = "ipv6";
|
|
_group_count = 4;
|
|
_input = ipStr;
|
|
_cidrGroup = null;
|
|
_groups = groups;
|
|
};
|
|
in
|
|
assert parsed != null;
|
|
assert count (g: g == "") rawGroups <= 1;
|
|
ip;
|
|
|
|
parseBinNet =
|
|
ipV: binStr:
|
|
let
|
|
ip = toIpClass {
|
|
version = ipV;
|
|
_input = binStr;
|
|
# special overwrites - TODO integrate into toIpClass
|
|
cidrInt = length binStr;
|
|
binRaw = fixedWidthStrSuffix ip._cidr_max "0" binStr;
|
|
binGroups = genList (i: substring (ip._group_bits * i) ip.binRaw) ip._group_count;
|
|
decGroups = map binToInt ip.binGroups;
|
|
# shortcuts
|
|
binRawNet = binStr;
|
|
};
|
|
in
|
|
ip;
|
|
|
|
mergeIPv6IfId =
|
|
prefix: suffix:
|
|
let
|
|
pref = parseIPv6 prefix;
|
|
suff = parseIPv6IfId suffix;
|
|
in
|
|
assert pref.cidrInt <= 64;
|
|
"${concatStringsSep ":" (sublist 0 4 pref.hexGroups ++ suff.hexGroups)}/64";
|
|
|
|
netMinus =
|
|
excl: net:
|
|
if net.sameNetwork excl then
|
|
[ ]
|
|
else if !net.contains excl then
|
|
singleton net
|
|
else
|
|
net.split;
|
|
|
|
netListMinus = excl: concatMap (netMinus excl);
|
|
|
|
}
|