init
This commit is contained in:
26
flake.lock
generated
Normal file
26
flake.lock
generated
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769909678,
|
||||||
|
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "72716169fe93074c333e8d0173151350670b824c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
53
flake.nix
Normal file
53
flake.nix
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
description = "Pure Nix library for IPv4 operations";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs-lib.url = "github:nix-community/nixpkgs.lib";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{ self, nixpkgs-lib }:
|
||||||
|
let
|
||||||
|
# Import our library with nixpkgs.lib
|
||||||
|
ipLib = import ./lib { lib = nixpkgs-lib.lib; };
|
||||||
|
|
||||||
|
# Systems for checks and packages
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
forAllSystems = f: nixpkgs-lib.lib.genAttrs systems f;
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Primary library output
|
||||||
|
lib = ipLib;
|
||||||
|
|
||||||
|
overlays = {
|
||||||
|
default = final: prev: {
|
||||||
|
lib = prev.lib.extend self.overlays.lib;
|
||||||
|
};
|
||||||
|
|
||||||
|
lib = final: prev: {
|
||||||
|
iputils = ipLib;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
checks = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
lib = nixpkgs-lib.lib;
|
||||||
|
runTests = import ./tests {
|
||||||
|
inherit lib system;
|
||||||
|
ipLib = ipLib;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
tests = runTests;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
664
lib/cidr.nix
Normal file
664
lib/cidr.nix
Normal file
@@ -0,0 +1,664 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
internal,
|
||||||
|
ip,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (builtins)
|
||||||
|
elemAt
|
||||||
|
filter
|
||||||
|
map
|
||||||
|
split
|
||||||
|
;
|
||||||
|
inherit (lib) toInt;
|
||||||
|
inherit (internal) pow2 intRange;
|
||||||
|
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
/**
|
||||||
|
Parse a CIDR string into { address; prefixLen }.
|
||||||
|
Also accepts attrsets (returns as-is).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
parse :: (String | { address :: Int; prefixLen :: Int; }) -> { address :: Int; prefixLen :: Int; }
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
parse "192.168.1.0/24"
|
||||||
|
=> { address = 3232235776; prefixLen = 24; }
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
parse =
|
||||||
|
cidr:
|
||||||
|
if builtins.isAttrs cidr then
|
||||||
|
cidr
|
||||||
|
else
|
||||||
|
let
|
||||||
|
parts = filter builtins.isString (split "/" cidr);
|
||||||
|
addr = ip.parse (elemAt parts 0);
|
||||||
|
prefix = toInt (elemAt parts 1);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
address = addr;
|
||||||
|
prefixLen = prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a CIDR from IP and prefix length.
|
||||||
|
Returns { address; prefixLen }.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
make :: (String | Int) -> Int -> { address :: Int; prefixLen :: Int; }
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
make "192.168.1.0" 24
|
||||||
|
=> { address = 3232235776; prefixLen = 24; }
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
make = addr: prefixLen: {
|
||||||
|
address = ip.parse addr;
|
||||||
|
inherit prefixLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Format a CIDR as string.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
format :: (String | { address :: Int; prefixLen :: Int; }) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
format { address = 3232235776; prefixLen = 24; }
|
||||||
|
=> "192.168.1.0/24"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
format =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
in
|
||||||
|
"${ip.format c.address}/${toString c.prefixLen}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
Calculate netmask from prefix length (as integer).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
netmask :: Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
netmask 24
|
||||||
|
=> 4294967040
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
netmask =
|
||||||
|
prefixLen:
|
||||||
|
if prefixLen == 0 then
|
||||||
|
0
|
||||||
|
else
|
||||||
|
let
|
||||||
|
# Create mask with prefixLen 1s followed by (32-prefixLen) 0s
|
||||||
|
hostBits = 32 - prefixLen;
|
||||||
|
in
|
||||||
|
4294967295 - (pow2 hostBits - 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Calculate netmask from prefix length (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
netmaskStr :: Int -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
netmaskStr 24
|
||||||
|
=> "255.255.255.0"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
netmaskStr = prefixLen: ip.format (netmask prefixLen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Calculate wildcard mask (inverse of netmask).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
wildcard :: Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
wildcard 24
|
||||||
|
=> 255
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
wildcard =
|
||||||
|
prefixLen:
|
||||||
|
let
|
||||||
|
hostBits = 32 - prefixLen;
|
||||||
|
in
|
||||||
|
pow2 hostBits - 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Calculate wildcard mask as string.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
wildcardStr :: Int -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
wildcardStr 24
|
||||||
|
=> "0.0.0.255"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
wildcardStr = prefixLen: ip.format (wildcard prefixLen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get network address of a CIDR (as integer).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
network :: (String | { address :: Int; prefixLen :: Int; }) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
network "192.168.1.50/24"
|
||||||
|
=> 3232235776
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
network =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
mask = netmask c.prefixLen;
|
||||||
|
in
|
||||||
|
builtins.bitAnd c.address mask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get network address of a CIDR (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
networkStr :: (String | { address :: Int; prefixLen :: Int; }) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
networkStr "192.168.1.50/24"
|
||||||
|
=> "192.168.1.0"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
networkStr = cidr: ip.format (network cidr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get broadcast address of a CIDR (as integer).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
broadcast :: (String | { address :: Int; prefixLen :: Int; }) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
broadcast "192.168.1.0/24"
|
||||||
|
=> 3232236031
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
broadcast =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
wild = wildcard c.prefixLen;
|
||||||
|
net = network cidr;
|
||||||
|
in
|
||||||
|
builtins.bitOr net wild;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get broadcast address of a CIDR (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
broadcastStr :: (String | { address :: Int; prefixLen :: Int; }) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
broadcastStr "192.168.1.0/24"
|
||||||
|
=> "192.168.1.255"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
broadcastStr = cidr: ip.format (broadcast cidr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get first usable host address (as integer).
|
||||||
|
For /31 and /32, returns network address.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
firstHost :: (String | { address :: Int; prefixLen :: Int; }) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
firstHost "192.168.1.0/24"
|
||||||
|
=> 3232235777
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
firstHost =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
net = network cidr;
|
||||||
|
in
|
||||||
|
if c.prefixLen >= 31 then net else net + 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get first usable host address (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
firstHostStr :: (String | { address :: Int; prefixLen :: Int; }) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
firstHostStr "192.168.1.0/24"
|
||||||
|
=> "192.168.1.1"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
firstHostStr = cidr: ip.format (firstHost cidr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get last usable host address (as integer).
|
||||||
|
For /31 and /32, returns broadcast address.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
lastHost :: (String | { address :: Int; prefixLen :: Int; }) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
lastHost "192.168.1.0/24"
|
||||||
|
=> 3232236030
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
lastHost =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
bcast = broadcast cidr;
|
||||||
|
in
|
||||||
|
if c.prefixLen >= 31 then bcast else bcast - 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get last usable host address (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
lastHostStr :: (String | { address :: Int; prefixLen :: Int; }) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
lastHostStr "192.168.1.0/24"
|
||||||
|
=> "192.168.1.254"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
lastHostStr = cidr: ip.format (lastHost cidr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get total number of addresses in CIDR.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
size :: (String | { address :: Int; prefixLen :: Int; }) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
size "192.168.1.0/24"
|
||||||
|
=> 256
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
size =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
in
|
||||||
|
pow2 (32 - c.prefixLen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get number of usable host addresses.
|
||||||
|
Excludes network and broadcast for normal subnets.
|
||||||
|
/31 returns 2, /32 returns 1 (special cases per RFC 3021).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
hostCount :: (String | { address :: Int; prefixLen :: Int; }) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
hostCount "192.168.1.0/24"
|
||||||
|
=> 254
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
hostCount =
|
||||||
|
cidr:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
total = size cidr;
|
||||||
|
in
|
||||||
|
if c.prefixLen == 32 then
|
||||||
|
1
|
||||||
|
else if c.prefixLen == 31 then
|
||||||
|
2
|
||||||
|
else if total <= 2 then
|
||||||
|
0
|
||||||
|
else
|
||||||
|
total - 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if an IP address is within a CIDR.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
contains :: (String | { address :: Int; prefixLen :: Int; }) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
contains "10.0.0.0/8" "10.1.2.3"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
contains =
|
||||||
|
cidr: addr:
|
||||||
|
let
|
||||||
|
ipInt = ip.parse addr;
|
||||||
|
net = network cidr;
|
||||||
|
bcast = broadcast cidr;
|
||||||
|
in
|
||||||
|
ipInt >= net && ipInt <= bcast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if cidr2 is a subnet of cidr1.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isSubnetOf :: (String | { address :: Int; prefixLen :: Int; }) -> (String | { address :: Int; prefixLen :: Int; }) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isSubnetOf "10.0.0.0/8" "10.1.0.0/16"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isSubnetOf =
|
||||||
|
cidr1: cidr2:
|
||||||
|
let
|
||||||
|
c1 = parse cidr1;
|
||||||
|
c2 = parse cidr2;
|
||||||
|
net1 = network cidr1;
|
||||||
|
bcast1 = broadcast cidr1;
|
||||||
|
net2 = network cidr2;
|
||||||
|
bcast2 = broadcast cidr2;
|
||||||
|
in
|
||||||
|
c2.prefixLen >= c1.prefixLen && net2 >= net1 && bcast2 <= bcast1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the Nth host in a CIDR (0-indexed from network address).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
host :: (String | { address :: Int; prefixLen :: Int; }) -> Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
host "192.168.1.0/24" 5
|
||||||
|
=> 3232235781
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
host = cidr: n: (network cidr) + n;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the Nth host in a CIDR (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
hostStr :: (String | { address :: Int; prefixLen :: Int; }) -> Int -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
hostStr "192.168.1.0/24" 5
|
||||||
|
=> "192.168.1.5"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
hostStr = cidr: n: ip.format (host cidr n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Subdivide a CIDR into smaller subnets.
|
||||||
|
Returns the Nth subnet with the new prefix length.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
subnet :: (String | { address :: Int; prefixLen :: Int; }) -> Int -> Int -> { address :: Int; prefixLen :: Int; }
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
subnet "192.168.0.0/24" 26 0
|
||||||
|
=> { address = 3232235520; prefixLen = 26; }
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
subnet =
|
||||||
|
cidr: newPrefix: n:
|
||||||
|
let
|
||||||
|
net = network cidr;
|
||||||
|
subnetSize = pow2 (32 - newPrefix);
|
||||||
|
subnetStart = net + (n * subnetSize);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
address = subnetStart;
|
||||||
|
prefixLen = newPrefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all subnets when subdividing.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
subnets :: (String | { address :: Int; prefixLen :: Int; }) -> Int -> [{ address :: Int; prefixLen :: Int; }]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
subnets "192.168.0.0/24" 26
|
||||||
|
=> [ { address = 3232235520; prefixLen = 26; } ... ] # 4 subnets
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
subnets =
|
||||||
|
cidr: newPrefix:
|
||||||
|
let
|
||||||
|
c = parse cidr;
|
||||||
|
numSubnets = pow2 (newPrefix - c.prefixLen);
|
||||||
|
in
|
||||||
|
map (n: subnet cidr newPrefix n) (intRange 0 (numSubnets - 1));
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all subnets as strings.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
subnetsStr :: (String | { address :: Int; prefixLen :: Int; }) -> Int -> [String]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
subnetsStr "192.168.0.0/24" 26
|
||||||
|
=> [ "192.168.0.0/26" "192.168.0.64/26" "192.168.0.128/26" "192.168.0.192/26" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
subnetsStr = cidr: newPrefix: map format (subnets cidr newPrefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if two CIDRs overlap.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
overlaps :: (String | { address :: Int; prefixLen :: Int; }) -> (String | { address :: Int; prefixLen :: Int; }) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
overlaps "192.168.1.0/24" "192.168.1.128/25"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
overlaps =
|
||||||
|
cidr1: cidr2:
|
||||||
|
let
|
||||||
|
net1 = network cidr1;
|
||||||
|
bcast1 = broadcast cidr1;
|
||||||
|
net2 = network cidr2;
|
||||||
|
bcast2 = broadcast cidr2;
|
||||||
|
in
|
||||||
|
!(bcast1 < net2 || bcast2 < net1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get prefix length from number of required hosts.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
prefixForHosts :: Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
prefixForHosts 100
|
||||||
|
=> 25
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
prefixForHosts =
|
||||||
|
hosts:
|
||||||
|
let
|
||||||
|
findPrefix =
|
||||||
|
prefix:
|
||||||
|
if prefix < 0 then
|
||||||
|
0
|
||||||
|
else if
|
||||||
|
hostCount {
|
||||||
|
address = 0;
|
||||||
|
prefixLen = prefix;
|
||||||
|
} >= hosts
|
||||||
|
then
|
||||||
|
prefix
|
||||||
|
else
|
||||||
|
findPrefix (prefix - 1);
|
||||||
|
in
|
||||||
|
findPrefix 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get prefix length from number of required addresses.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
prefixForSize :: Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
prefixForSize 256
|
||||||
|
=> 24
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
prefixForSize =
|
||||||
|
addresses:
|
||||||
|
let
|
||||||
|
findPrefix =
|
||||||
|
prefix:
|
||||||
|
if prefix < 0 then
|
||||||
|
0
|
||||||
|
else if
|
||||||
|
size {
|
||||||
|
address = 0;
|
||||||
|
prefixLen = prefix;
|
||||||
|
} >= addresses
|
||||||
|
then
|
||||||
|
prefix
|
||||||
|
else
|
||||||
|
findPrefix (prefix - 1);
|
||||||
|
in
|
||||||
|
findPrefix 32;
|
||||||
|
}
|
||||||
42
lib/default.nix
Normal file
42
lib/default.nix
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{ lib }:
|
||||||
|
let
|
||||||
|
# Internal helpers
|
||||||
|
internal = import ./internal.nix { inherit lib; };
|
||||||
|
|
||||||
|
# IP address operations
|
||||||
|
ip = import ./ip.nix { inherit lib internal; };
|
||||||
|
|
||||||
|
# CIDR/subnet operations
|
||||||
|
cidr = import ./cidr.nix { inherit lib internal ip; };
|
||||||
|
|
||||||
|
# Validation functions
|
||||||
|
validate = import ./validate.nix {
|
||||||
|
inherit
|
||||||
|
lib
|
||||||
|
internal
|
||||||
|
ip
|
||||||
|
cidr
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Range/iteration functions
|
||||||
|
iterate = import ./iterate.nix {
|
||||||
|
inherit
|
||||||
|
lib
|
||||||
|
internal
|
||||||
|
ip
|
||||||
|
cidr
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit
|
||||||
|
ip
|
||||||
|
cidr
|
||||||
|
validate
|
||||||
|
iterate
|
||||||
|
;
|
||||||
|
|
||||||
|
_internal = internal;
|
||||||
|
}
|
||||||
56
lib/internal.nix
Normal file
56
lib/internal.nix
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{ lib }:
|
||||||
|
let
|
||||||
|
inherit (builtins) elemAt genList;
|
||||||
|
inherit (lib) mod;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
# Power of 2 calculation
|
||||||
|
# pow2 n returns 2^n
|
||||||
|
pow2 =
|
||||||
|
n:
|
||||||
|
if n == 0 then
|
||||||
|
1
|
||||||
|
else if n == 1 then
|
||||||
|
2
|
||||||
|
else
|
||||||
|
let
|
||||||
|
half = n / 2;
|
||||||
|
halfPow = pow2 half;
|
||||||
|
in
|
||||||
|
halfPow * halfPow * (if mod n 2 == 1 then 2 else 1);
|
||||||
|
|
||||||
|
# Convert list of 4 octets to 32-bit integer
|
||||||
|
# [192 168 1 1] -> 3232235777
|
||||||
|
octetsToInt =
|
||||||
|
octets:
|
||||||
|
let
|
||||||
|
a = elemAt octets 0;
|
||||||
|
b = elemAt octets 1;
|
||||||
|
c = elemAt octets 2;
|
||||||
|
d = elemAt octets 3;
|
||||||
|
in
|
||||||
|
a * 16777216 + b * 65536 + c * 256 + d;
|
||||||
|
|
||||||
|
# Convert 32-bit integer to list of 4 octets
|
||||||
|
# 3232235777 -> [192 168 1 1]
|
||||||
|
intToOctets =
|
||||||
|
ip:
|
||||||
|
let
|
||||||
|
a = ip / 16777216;
|
||||||
|
remainder1 = mod ip 16777216;
|
||||||
|
b = remainder1 / 65536;
|
||||||
|
remainder2 = mod remainder1 65536;
|
||||||
|
c = remainder2 / 256;
|
||||||
|
d = mod remainder2 256;
|
||||||
|
in
|
||||||
|
[
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
];
|
||||||
|
|
||||||
|
# Generate a list of integers from start to end (inclusive)
|
||||||
|
# More efficient than recursive approach for large ranges
|
||||||
|
intRange = start: end: if end < start then [ ] else genList (i: start + i) (end - start + 1);
|
||||||
|
}
|
||||||
392
lib/ip.nix
Normal file
392
lib/ip.nix
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
{ lib, internal }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (builtins)
|
||||||
|
filter
|
||||||
|
map
|
||||||
|
split
|
||||||
|
;
|
||||||
|
inherit (lib) concatStringsSep toInt;
|
||||||
|
inherit (internal) octetsToInt intToOctets;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
/**
|
||||||
|
Parse an IP address string to a 32-bit integer.
|
||||||
|
Also accepts integers (returns as-is).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
parse :: (String | Int) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
parse "192.168.1.1"
|
||||||
|
=> 3232235777
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
parse =
|
||||||
|
ip:
|
||||||
|
if builtins.isInt ip then
|
||||||
|
ip
|
||||||
|
else
|
||||||
|
let
|
||||||
|
parts = filter builtins.isString (split "\\." ip);
|
||||||
|
octets = map toInt parts;
|
||||||
|
in
|
||||||
|
octetsToInt octets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Format a 32-bit integer as an IP address string.
|
||||||
|
Also accepts strings (returns as-is after validation).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
format :: (Int | String) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
format 3232235777
|
||||||
|
=> "192.168.1.1"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
format =
|
||||||
|
ip: if builtins.isString ip then ip else concatStringsSep "." (map toString (intToOctets ip));
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert IP to list of octets.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
toOctets :: (String | Int) -> [Int]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
toOctets "192.168.1.1"
|
||||||
|
=> [192 168 1 1]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
toOctets = ip: intToOctets (parse ip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert list of octets to integer.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
fromOctets :: [Int] -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
fromOctets [192 168 1 1]
|
||||||
|
=> 3232235777
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
fromOctets = octetsToInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Add offset to an IP address.
|
||||||
|
Returns integer; use format for string.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
add :: (String | Int) -> Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
add "192.168.1.1" 10
|
||||||
|
=> 3232235787
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
add = ip: offset: (parse ip) + offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Add offset to an IP address, return as string.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
addStr :: (String | Int) -> Int -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
addStr "192.168.1.1" 10
|
||||||
|
=> "192.168.1.11"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
addStr = ip: offset: format (add ip offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Subtract offset from an IP address.
|
||||||
|
Returns integer; use format for string.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
subtract :: (String | Int) -> Int -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
subtract "192.168.1.10" 5
|
||||||
|
=> 3232235781
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
subtract = ip: offset: (parse ip) - offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Subtract offset from an IP address, return as string.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
subtractStr :: (String | Int) -> Int -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
subtractStr "192.168.1.10" 5
|
||||||
|
=> "192.168.1.5"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
subtractStr = ip: offset: format (subtract ip offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Calculate difference between two IPs (ip1 - ip2).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
diff :: (String | Int) -> (String | Int) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
diff "192.168.1.10" "192.168.1.1"
|
||||||
|
=> 9
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
diff = ip1: ip2: (parse ip1) - (parse ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Compare two IP addresses.
|
||||||
|
Returns -1 if ip1 < ip2, 0 if equal, 1 if ip1 > ip2.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
compare :: (String | Int) -> (String | Int) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
compare "192.168.1.1" "192.168.1.10"
|
||||||
|
=> -1
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
compare =
|
||||||
|
ip1: ip2:
|
||||||
|
let
|
||||||
|
a = parse ip1;
|
||||||
|
b = parse ip2;
|
||||||
|
in
|
||||||
|
if a < b then
|
||||||
|
-1
|
||||||
|
else if a > b then
|
||||||
|
1
|
||||||
|
else
|
||||||
|
0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Less than comparison.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
lt :: (String | Int) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
lt "192.168.1.1" "192.168.1.10"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
lt = ip1: ip2: (parse ip1) < (parse ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Less than or equal comparison.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
lte :: (String | Int) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
lte "192.168.1.1" "192.168.1.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
lte = ip1: ip2: (parse ip1) <= (parse ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Greater than comparison.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
gt :: (String | Int) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
gt "192.168.1.10" "192.168.1.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
gt = ip1: ip2: (parse ip1) > (parse ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Greater than or equal comparison.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
gte :: (String | Int) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
gte "192.168.1.1" "192.168.1.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
gte = ip1: ip2: (parse ip1) >= (parse ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Equality comparison.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
eq :: (String | Int) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
eq "192.168.1.1" 3232235777
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
eq = ip1: ip2: (parse ip1) == (parse ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get minimum of two IPs (returns integer).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
min :: (String | Int) -> (String | Int) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
min "192.168.1.10" "192.168.1.1"
|
||||||
|
=> 3232235777
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
min =
|
||||||
|
ip1: ip2:
|
||||||
|
let
|
||||||
|
a = parse ip1;
|
||||||
|
b = parse ip2;
|
||||||
|
in
|
||||||
|
if a < b then a else b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get minimum of two IPs (returns string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
minStr :: (String | Int) -> (String | Int) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
minStr "192.168.1.10" "192.168.1.1"
|
||||||
|
=> "192.168.1.1"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
minStr = ip1: ip2: format (min ip1 ip2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get maximum of two IPs (returns integer).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
max :: (String | Int) -> (String | Int) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
max "192.168.1.1" "192.168.1.10"
|
||||||
|
=> 3232235786
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
max =
|
||||||
|
ip1: ip2:
|
||||||
|
let
|
||||||
|
a = parse ip1;
|
||||||
|
b = parse ip2;
|
||||||
|
in
|
||||||
|
if a > b then a else b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get maximum of two IPs (returns string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
maxStr :: (String | Int) -> (String | Int) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
maxStr "192.168.1.1" "192.168.1.10"
|
||||||
|
=> "192.168.1.10"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
maxStr = ip1: ip2: format (max ip1 ip2);
|
||||||
|
}
|
||||||
389
lib/iterate.nix
Normal file
389
lib/iterate.nix
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
internal,
|
||||||
|
ip,
|
||||||
|
cidr,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (builtins) genList;
|
||||||
|
inherit (lib) map;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
/**
|
||||||
|
Generate a list of IP integers from start to end (inclusive).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
range :: (String | Int) -> (String | Int) -> [Int]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
range "192.168.1.1" "192.168.1.3"
|
||||||
|
=> [ 3232235777 3232235778 3232235779 ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
range =
|
||||||
|
start: end:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
count = endInt - startInt + 1;
|
||||||
|
in
|
||||||
|
if endInt < startInt then [ ] else genList (i: startInt + i) count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generate a list of IP strings from start to end (inclusive).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
rangeStr :: (String | Int) -> (String | Int) -> [String]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
rangeStr "192.168.1.1" "192.168.1.3"
|
||||||
|
=> [ "192.168.1.1" "192.168.1.2" "192.168.1.3" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
rangeStr = start: end: map ip.format (range start end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all addresses in a CIDR (as integers).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
cidrAddresses :: (String | { address :: Int; prefixLen :: Int; }) -> [Int]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
cidrAddresses "192.168.1.0/30"
|
||||||
|
=> [ 3232235776 3232235777 3232235778 3232235779 ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
cidrAddresses =
|
||||||
|
cidrStr:
|
||||||
|
let
|
||||||
|
net = cidr.network cidrStr;
|
||||||
|
bcast = cidr.broadcast cidrStr;
|
||||||
|
in
|
||||||
|
range net bcast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all addresses in a CIDR (as strings).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
cidrAddressesStr :: (String | { address :: Int; prefixLen :: Int; }) -> [String]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
cidrAddressesStr "192.168.1.0/30"
|
||||||
|
=> [ "192.168.1.0" "192.168.1.1" "192.168.1.2" "192.168.1.3" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
cidrAddressesStr = cidrStr: map ip.format (cidrAddresses cidrStr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all usable host addresses in a CIDR (as integers).
|
||||||
|
Excludes network and broadcast for /30 and larger.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
cidrHosts :: (String | { address :: Int; prefixLen :: Int; }) -> [Int]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
cidrHosts "192.168.1.0/30"
|
||||||
|
=> [ 3232235777 3232235778 ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
cidrHosts =
|
||||||
|
cidrStr:
|
||||||
|
let
|
||||||
|
c = cidr.parse cidrStr;
|
||||||
|
first = cidr.firstHost cidrStr;
|
||||||
|
last = cidr.lastHost cidrStr;
|
||||||
|
in
|
||||||
|
if c.prefixLen >= 31 then range first last else range first last;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all usable host addresses in a CIDR (as strings).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
cidrHostsStr :: (String | { address :: Int; prefixLen :: Int; }) -> [String]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
cidrHostsStr "192.168.1.0/30"
|
||||||
|
=> [ "192.168.1.1" "192.168.1.2" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
cidrHostsStr = cidrStr: map ip.format (cidrHosts cidrStr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if an IP is within a range (inclusive).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
inRange :: (String | Int) -> (String | Int) -> (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
inRange "192.168.1.1" "192.168.1.10" "192.168.1.5"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
inRange =
|
||||||
|
start: end: addr:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
addrInt = ip.parse addr;
|
||||||
|
in
|
||||||
|
addrInt >= startInt && addrInt <= endInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Count IPs in a range (inclusive).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
countRange :: (String | Int) -> (String | Int) -> Int
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
countRange "192.168.1.1" "192.168.1.10"
|
||||||
|
=> 10
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
countRange =
|
||||||
|
start: end:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
in
|
||||||
|
if endInt < startInt then 0 else endInt - startInt + 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Map a function over an IP range.
|
||||||
|
More memory-efficient than generating the full list first.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
mapRange :: (Int -> a) -> (String | Int) -> (String | Int) -> [a]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
mapRange (i: i * 2) "0.0.0.1" "0.0.0.3"
|
||||||
|
=> [ 2 4 6 ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
mapRange =
|
||||||
|
f: start: end:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
count = endInt - startInt + 1;
|
||||||
|
in
|
||||||
|
if endInt < startInt then [ ] else genList (i: f (startInt + i)) count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Map a function over a CIDR's addresses.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
mapCidr :: (Int -> a) -> (String | { address :: Int; prefixLen :: Int; }) -> [a]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
mapCidr ip.format "192.168.1.0/30"
|
||||||
|
=> [ "192.168.1.0" "192.168.1.1" "192.168.1.2" "192.168.1.3" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
mapCidr =
|
||||||
|
f: cidrStr:
|
||||||
|
let
|
||||||
|
net = cidr.network cidrStr;
|
||||||
|
bcast = cidr.broadcast cidrStr;
|
||||||
|
in
|
||||||
|
mapRange f net bcast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Map a function over a CIDR's usable hosts.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
mapCidrHosts :: (Int -> a) -> (String | { address :: Int; prefixLen :: Int; }) -> [a]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
mapCidrHosts ip.format "192.168.1.0/30"
|
||||||
|
=> [ "192.168.1.1" "192.168.1.2" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
mapCidrHosts =
|
||||||
|
f: cidrStr:
|
||||||
|
let
|
||||||
|
first = cidr.firstHost cidrStr;
|
||||||
|
last = cidr.lastHost cidrStr;
|
||||||
|
in
|
||||||
|
mapRange f first last;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Filter IPs in a range.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
filterRange :: (Int -> Bool) -> (String | Int) -> (String | Int) -> [Int]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
filterRange (i: lib.mod i 2 == 0) "0.0.0.1" "0.0.0.5"
|
||||||
|
=> [ 2 4 ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
filterRange =
|
||||||
|
pred: start: end:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
count = endInt - startInt + 1;
|
||||||
|
allIps = if endInt < startInt then [ ] else genList (i: startInt + i) count;
|
||||||
|
in
|
||||||
|
builtins.filter pred allIps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Find first IP in range matching predicate.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
findInRange :: (Int -> Bool) -> (String | Int) -> (String | Int) -> (Int | Null)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
findInRange (i: lib.mod i 2 == 0) "0.0.0.1" "0.0.0.5"
|
||||||
|
=> 2
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
findInRange =
|
||||||
|
pred: start: end:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
find =
|
||||||
|
current:
|
||||||
|
if current > endInt then
|
||||||
|
null
|
||||||
|
else if pred current then
|
||||||
|
current
|
||||||
|
else
|
||||||
|
find (current + 1);
|
||||||
|
in
|
||||||
|
if endInt < startInt then null else find startInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Find first IP in range matching predicate (as string).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
findInRangeStr :: (Int -> Bool) -> (String | Int) -> (String | Int) -> (String | Null)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
findInRangeStr (i: lib.mod i 2 == 0) "0.0.0.1" "0.0.0.5"
|
||||||
|
=> "0.0.0.2"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
findInRangeStr =
|
||||||
|
pred: start: end:
|
||||||
|
let
|
||||||
|
result = findInRange pred start end;
|
||||||
|
in
|
||||||
|
if result == null then null else ip.format result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Take first n IPs from a range.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
takeRange :: Int -> (String | Int) -> (String | Int) -> [Int]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
takeRange 2 "192.168.1.1" "192.168.1.10"
|
||||||
|
=> [ 3232235777 3232235778 ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
takeRange =
|
||||||
|
n: start: end:
|
||||||
|
let
|
||||||
|
startInt = ip.parse start;
|
||||||
|
endInt = ip.parse end;
|
||||||
|
actualEnd = if startInt + n - 1 < endInt then startInt + n - 1 else endInt;
|
||||||
|
in
|
||||||
|
if endInt < startInt || n <= 0 then [ ] else range startInt actualEnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Take first n IPs from a range (as strings).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
takeRangeStr :: Int -> (String | Int) -> (String | Int) -> [String]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
takeRangeStr 2 "192.168.1.1" "192.168.1.10"
|
||||||
|
=> [ "192.168.1.1" "192.168.1.2" ]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
takeRangeStr =
|
||||||
|
n: start: end:
|
||||||
|
map ip.format (takeRange n start end);
|
||||||
|
}
|
||||||
371
lib/validate.nix
Normal file
371
lib/validate.nix
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
internal,
|
||||||
|
ip,
|
||||||
|
cidr,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (builtins)
|
||||||
|
elemAt
|
||||||
|
filter
|
||||||
|
length
|
||||||
|
match
|
||||||
|
split
|
||||||
|
tryEval
|
||||||
|
;
|
||||||
|
inherit (lib) all toInt;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
/**
|
||||||
|
Check if a string is a valid IPv4 address.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isValidIp :: Any -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isValidIp "192.168.1.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isValidIp =
|
||||||
|
str:
|
||||||
|
if !builtins.isString str then
|
||||||
|
false
|
||||||
|
else
|
||||||
|
let
|
||||||
|
# Match basic format: digits.digits.digits.digits
|
||||||
|
m = match "([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)" str;
|
||||||
|
in
|
||||||
|
if m == null then
|
||||||
|
false
|
||||||
|
else
|
||||||
|
let
|
||||||
|
octets = map toInt m;
|
||||||
|
validOctet = o: o >= 0 && o <= 255;
|
||||||
|
in
|
||||||
|
all validOctet octets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if a string is valid CIDR notation.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isValidCidr :: Any -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isValidCidr "192.168.1.0/24"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isValidCidr =
|
||||||
|
str:
|
||||||
|
if !builtins.isString str then
|
||||||
|
false
|
||||||
|
else
|
||||||
|
let
|
||||||
|
parts = filter builtins.isString (split "/" str);
|
||||||
|
in
|
||||||
|
if length parts != 2 then
|
||||||
|
false
|
||||||
|
else
|
||||||
|
let
|
||||||
|
ipPart = elemAt parts 0;
|
||||||
|
prefixPart = elemAt parts 1;
|
||||||
|
prefixMatch = match "[0-9]+" prefixPart;
|
||||||
|
in
|
||||||
|
if !isValidIp ipPart then
|
||||||
|
false
|
||||||
|
else if prefixMatch == null then
|
||||||
|
false
|
||||||
|
else
|
||||||
|
let
|
||||||
|
prefix = toInt prefixPart;
|
||||||
|
in
|
||||||
|
prefix >= 0 && prefix <= 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Try to parse an IP, return null on failure.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
tryParseIp :: String -> (Int | Null)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
tryParseIp "192.168.1.1"
|
||||||
|
=> 3232235777
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
tryParseIp =
|
||||||
|
str:
|
||||||
|
let
|
||||||
|
result = tryEval (if isValidIp str then ip.parse str else throw "invalid");
|
||||||
|
in
|
||||||
|
if result.success then result.value else null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Try to parse a CIDR, return null on failure.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
tryParseCidr :: String -> ({ address :: Int; prefixLen :: Int; } | Null)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
tryParseCidr "192.168.1.0/24"
|
||||||
|
=> { address = 3232235776; prefixLen = 24; }
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
tryParseCidr =
|
||||||
|
str:
|
||||||
|
let
|
||||||
|
result = tryEval (if isValidCidr str then cidr.parse str else throw "invalid");
|
||||||
|
in
|
||||||
|
if result.success then result.value else null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is in RFC 1918 private address space.
|
||||||
|
Covers 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isPrivate :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isPrivate "192.168.1.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isPrivate =
|
||||||
|
addr:
|
||||||
|
let
|
||||||
|
ipInt = ip.parse addr;
|
||||||
|
in
|
||||||
|
cidr.contains "10.0.0.0/8" ipInt
|
||||||
|
|| cidr.contains "172.16.0.0/12" ipInt
|
||||||
|
|| cidr.contains "192.168.0.0/16" ipInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is loopback (127.0.0.0/8).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isLoopback :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isLoopback "127.0.0.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isLoopback = addr: cidr.contains "127.0.0.0/8" (ip.parse addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is link-local (169.254.0.0/16).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isLinkLocal :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isLinkLocal "169.254.1.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isLinkLocal = addr: cidr.contains "169.254.0.0/16" (ip.parse addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is multicast (224.0.0.0/4).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isMulticast :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isMulticast "224.0.0.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isMulticast = addr: cidr.contains "224.0.0.0/4" (ip.parse addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is broadcast (255.255.255.255).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isBroadcast :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isBroadcast "255.255.255.255"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isBroadcast = addr: (ip.parse addr) == 4294967295;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is unspecified (0.0.0.0).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isUnspecified :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isUnspecified "0.0.0.0"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isUnspecified = addr: (ip.parse addr) == 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is documentation range.
|
||||||
|
Covers 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isDocumentation :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isDocumentation "192.0.2.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isDocumentation =
|
||||||
|
addr:
|
||||||
|
let
|
||||||
|
ipInt = ip.parse addr;
|
||||||
|
in
|
||||||
|
cidr.contains "192.0.2.0/24" ipInt
|
||||||
|
|| cidr.contains "198.51.100.0/24" ipInt
|
||||||
|
|| cidr.contains "203.0.113.0/24" ipInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is reserved for future use (240.0.0.0/4, except broadcast).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isReserved :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isReserved "240.0.0.1"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isReserved =
|
||||||
|
addr:
|
||||||
|
let
|
||||||
|
ipInt = ip.parse addr;
|
||||||
|
in
|
||||||
|
cidr.contains "240.0.0.0/4" ipInt && ipInt != 4294967295;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if IP is globally routable (not private, loopback, link-local, etc.).
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
isGlobalUnicast :: (String | Int) -> Bool
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
isGlobalUnicast "8.8.8.8"
|
||||||
|
=> true
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
isGlobalUnicast =
|
||||||
|
addr:
|
||||||
|
!isPrivate addr
|
||||||
|
&& !isLoopback addr
|
||||||
|
&& !isLinkLocal addr
|
||||||
|
&& !isMulticast addr
|
||||||
|
&& !isBroadcast addr
|
||||||
|
&& !isUnspecified addr
|
||||||
|
&& !isDocumentation addr
|
||||||
|
&& !isReserved addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Classify an IP address into its type.
|
||||||
|
Returns one of: "loopback", "private", "link-local", "multicast",
|
||||||
|
"broadcast", "unspecified", "documentation", "reserved", "global-unicast".
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
classify :: (String | Int) -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
classify "192.168.1.1"
|
||||||
|
=> "private"
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
classify =
|
||||||
|
addr:
|
||||||
|
if isLoopback addr then
|
||||||
|
"loopback"
|
||||||
|
else if isPrivate addr then
|
||||||
|
"private"
|
||||||
|
else if isLinkLocal addr then
|
||||||
|
"link-local"
|
||||||
|
else if isMulticast addr then
|
||||||
|
"multicast"
|
||||||
|
else if isBroadcast addr then
|
||||||
|
"broadcast"
|
||||||
|
else if isUnspecified addr then
|
||||||
|
"unspecified"
|
||||||
|
else if isDocumentation addr then
|
||||||
|
"documentation"
|
||||||
|
else if isReserved addr then
|
||||||
|
"reserved"
|
||||||
|
else
|
||||||
|
"global-unicast";
|
||||||
|
}
|
||||||
315
tests/cidr-tests.nix
Normal file
315
tests/cidr-tests.nix
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
cidr,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) runTests;
|
||||||
|
|
||||||
|
in
|
||||||
|
runTests {
|
||||||
|
testParseBasic = {
|
||||||
|
expr = cidr.parse "192.168.1.0/24";
|
||||||
|
expected = {
|
||||||
|
address = 3232235776;
|
||||||
|
prefixLen = 24;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testParseSlash32 = {
|
||||||
|
expr = cidr.parse "10.0.0.1/32";
|
||||||
|
expected = {
|
||||||
|
address = 167772161;
|
||||||
|
prefixLen = 32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testParseSlash0 = {
|
||||||
|
expr = cidr.parse "0.0.0.0/0";
|
||||||
|
expected = {
|
||||||
|
address = 0;
|
||||||
|
prefixLen = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testMakeBasic = {
|
||||||
|
expr = cidr.make "192.168.1.0" 24;
|
||||||
|
expected = {
|
||||||
|
address = 3232235776;
|
||||||
|
prefixLen = 24;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testFormatBasic = {
|
||||||
|
expr = cidr.format {
|
||||||
|
address = 3232235776;
|
||||||
|
prefixLen = 24;
|
||||||
|
};
|
||||||
|
expected = "192.168.1.0/24";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetmask24 = {
|
||||||
|
expr = cidr.netmaskStr 24;
|
||||||
|
expected = "255.255.255.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetmask16 = {
|
||||||
|
expr = cidr.netmaskStr 16;
|
||||||
|
expected = "255.255.0.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetmask8 = {
|
||||||
|
expr = cidr.netmaskStr 8;
|
||||||
|
expected = "255.0.0.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetmask32 = {
|
||||||
|
expr = cidr.netmaskStr 32;
|
||||||
|
expected = "255.255.255.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetmask0 = {
|
||||||
|
expr = cidr.netmaskStr 0;
|
||||||
|
expected = "0.0.0.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetmask25 = {
|
||||||
|
expr = cidr.netmaskStr 25;
|
||||||
|
expected = "255.255.255.128";
|
||||||
|
};
|
||||||
|
|
||||||
|
testWildcard24 = {
|
||||||
|
expr = cidr.wildcardStr 24;
|
||||||
|
expected = "0.0.0.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testWildcard16 = {
|
||||||
|
expr = cidr.wildcardStr 16;
|
||||||
|
expected = "0.0.255.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetworkBasic = {
|
||||||
|
expr = cidr.networkStr "192.168.1.50/24";
|
||||||
|
expected = "192.168.1.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetworkAlreadyNetwork = {
|
||||||
|
expr = cidr.networkStr "192.168.1.0/24";
|
||||||
|
expected = "192.168.1.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testNetworkSlash16 = {
|
||||||
|
expr = cidr.networkStr "172.16.45.67/16";
|
||||||
|
expected = "172.16.0.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testBroadcastBasic = {
|
||||||
|
expr = cidr.broadcastStr "192.168.1.0/24";
|
||||||
|
expected = "192.168.1.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testBroadcastSlash16 = {
|
||||||
|
expr = cidr.broadcastStr "172.16.0.0/16";
|
||||||
|
expected = "172.16.255.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testBroadcastSlash30 = {
|
||||||
|
expr = cidr.broadcastStr "192.168.1.0/30";
|
||||||
|
expected = "192.168.1.3";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFirstHostBasic = {
|
||||||
|
expr = cidr.firstHostStr "192.168.1.0/24";
|
||||||
|
expected = "192.168.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
testLastHostBasic = {
|
||||||
|
expr = cidr.lastHostStr "192.168.1.0/24";
|
||||||
|
expected = "192.168.1.254";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFirstHostSlash31 = {
|
||||||
|
expr = cidr.firstHostStr "192.168.1.0/31";
|
||||||
|
expected = "192.168.1.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testLastHostSlash31 = {
|
||||||
|
expr = cidr.lastHostStr "192.168.1.0/31";
|
||||||
|
expected = "192.168.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFirstHostSlash32 = {
|
||||||
|
expr = cidr.firstHostStr "192.168.1.1/32";
|
||||||
|
expected = "192.168.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
testSize24 = {
|
||||||
|
expr = cidr.size "192.168.1.0/24";
|
||||||
|
expected = 256;
|
||||||
|
};
|
||||||
|
|
||||||
|
testSize16 = {
|
||||||
|
expr = cidr.size "172.16.0.0/16";
|
||||||
|
expected = 65536;
|
||||||
|
};
|
||||||
|
|
||||||
|
testSize32 = {
|
||||||
|
expr = cidr.size "192.168.1.1/32";
|
||||||
|
expected = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
testSize30 = {
|
||||||
|
expr = cidr.size "192.168.1.0/30";
|
||||||
|
expected = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostCount24 = {
|
||||||
|
expr = cidr.hostCount "192.168.1.0/24";
|
||||||
|
expected = 254;
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostCount30 = {
|
||||||
|
expr = cidr.hostCount "192.168.1.0/30";
|
||||||
|
expected = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostCount31 = {
|
||||||
|
expr = cidr.hostCount "192.168.1.0/31";
|
||||||
|
expected = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostCount32 = {
|
||||||
|
expr = cidr.hostCount "192.168.1.1/32";
|
||||||
|
expected = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
testContainsTrue = {
|
||||||
|
expr = cidr.contains "10.0.0.0/8" "10.1.2.3";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testContainsFalse = {
|
||||||
|
expr = cidr.contains "10.0.0.0/8" "192.168.1.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testContainsNetwork = {
|
||||||
|
expr = cidr.contains "192.168.1.0/24" "192.168.1.0";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testContainsBroadcast = {
|
||||||
|
expr = cidr.contains "192.168.1.0/24" "192.168.1.255";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testContainsEdgeFalse = {
|
||||||
|
expr = cidr.contains "192.168.1.0/24" "192.168.2.0";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsSubnetOfTrue = {
|
||||||
|
expr = cidr.isSubnetOf "10.0.0.0/8" "10.1.0.0/16";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsSubnetOfFalse = {
|
||||||
|
expr = cidr.isSubnetOf "10.0.0.0/8" "192.168.0.0/16";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsSubnetOfSame = {
|
||||||
|
expr = cidr.isSubnetOf "10.0.0.0/8" "10.0.0.0/8";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsSubnetOfLarger = {
|
||||||
|
expr = cidr.isSubnetOf "10.1.0.0/16" "10.0.0.0/8";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostBasic = {
|
||||||
|
expr = cidr.hostStr "192.168.1.0/24" 5;
|
||||||
|
expected = "192.168.1.5";
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostZero = {
|
||||||
|
expr = cidr.hostStr "192.168.1.0/24" 0;
|
||||||
|
expected = "192.168.1.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testHostLast = {
|
||||||
|
expr = cidr.hostStr "192.168.1.0/24" 255;
|
||||||
|
expected = "192.168.1.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubnetFirst = {
|
||||||
|
expr = cidr.format (cidr.subnet "192.168.0.0/24" 26 0);
|
||||||
|
expected = "192.168.0.0/26";
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubnetSecond = {
|
||||||
|
expr = cidr.format (cidr.subnet "192.168.0.0/24" 26 1);
|
||||||
|
expected = "192.168.0.64/26";
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubnetLast = {
|
||||||
|
expr = cidr.format (cidr.subnet "192.168.0.0/24" 26 3);
|
||||||
|
expected = "192.168.0.192/26";
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubnetsCount = {
|
||||||
|
expr = builtins.length (cidr.subnets "192.168.0.0/24" 26);
|
||||||
|
expected = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubnetsStr = {
|
||||||
|
expr = cidr.subnetsStr "192.168.0.0/24" 26;
|
||||||
|
expected = [
|
||||||
|
"192.168.0.0/26"
|
||||||
|
"192.168.0.64/26"
|
||||||
|
"192.168.0.128/26"
|
||||||
|
"192.168.0.192/26"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testOverlapsTrue = {
|
||||||
|
expr = cidr.overlaps "192.168.1.0/24" "192.168.0.0/16";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testOverlapsFalse = {
|
||||||
|
expr = cidr.overlaps "192.168.1.0/24" "10.0.0.0/8";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testOverlapsAdjacent = {
|
||||||
|
expr = cidr.overlaps "192.168.1.0/24" "192.168.2.0/24";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testPrefixForHosts100 = {
|
||||||
|
expr = cidr.prefixForHosts 100;
|
||||||
|
expected = 25;
|
||||||
|
};
|
||||||
|
|
||||||
|
testPrefixForHosts254 = {
|
||||||
|
expr = cidr.prefixForHosts 254;
|
||||||
|
expected = 24;
|
||||||
|
};
|
||||||
|
|
||||||
|
testPrefixForHosts1 = {
|
||||||
|
expr = cidr.prefixForHosts 1;
|
||||||
|
expected = 32;
|
||||||
|
};
|
||||||
|
|
||||||
|
testPrefixForSize256 = {
|
||||||
|
expr = cidr.prefixForSize 256;
|
||||||
|
expected = 24;
|
||||||
|
};
|
||||||
|
|
||||||
|
testPrefixForSize1 = {
|
||||||
|
expr = cidr.prefixForSize 1;
|
||||||
|
expected = 32;
|
||||||
|
};
|
||||||
|
}
|
||||||
43
tests/default.nix
Normal file
43
tests/default.nix
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
ipLib,
|
||||||
|
system,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
ipTests = import ./ip-tests.nix {
|
||||||
|
inherit lib;
|
||||||
|
ip = ipLib.ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
cidrTests = import ./cidr-tests.nix {
|
||||||
|
inherit lib;
|
||||||
|
cidr = ipLib.cidr;
|
||||||
|
};
|
||||||
|
|
||||||
|
validateTests = import ./validate-tests.nix {
|
||||||
|
inherit lib;
|
||||||
|
validate = ipLib.validate;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterateTests = import ./iterate-tests.nix {
|
||||||
|
inherit lib;
|
||||||
|
iterate = ipLib.iterate;
|
||||||
|
ip = ipLib.ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
allTests = ipTests ++ cidrTests ++ validateTests ++ iterateTests;
|
||||||
|
|
||||||
|
in
|
||||||
|
if allTests == [ ] then
|
||||||
|
derivation {
|
||||||
|
name = "nix-ip-utils-tests";
|
||||||
|
inherit system;
|
||||||
|
builder = "/bin/sh";
|
||||||
|
args = [
|
||||||
|
"-c"
|
||||||
|
"echo 'passed!' > $out"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw "Tests failed: ${builtins.toJSON allTests}"
|
||||||
205
tests/ip-tests.nix
Normal file
205
tests/ip-tests.nix
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
{ lib, ip }:
|
||||||
|
let
|
||||||
|
inherit (lib) runTests;
|
||||||
|
in
|
||||||
|
runTests {
|
||||||
|
testParseBasic = {
|
||||||
|
expr = ip.parse "192.168.1.1";
|
||||||
|
expected = 3232235777;
|
||||||
|
};
|
||||||
|
|
||||||
|
testParseZeros = {
|
||||||
|
expr = ip.parse "0.0.0.0";
|
||||||
|
expected = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
testParseMax = {
|
||||||
|
expr = ip.parse "255.255.255.255";
|
||||||
|
expected = 4294967295;
|
||||||
|
};
|
||||||
|
|
||||||
|
testParseLoopback = {
|
||||||
|
expr = ip.parse "127.0.0.1";
|
||||||
|
expected = 2130706433;
|
||||||
|
};
|
||||||
|
|
||||||
|
testParseTen = {
|
||||||
|
expr = ip.parse "10.0.0.1";
|
||||||
|
expected = 167772161;
|
||||||
|
};
|
||||||
|
|
||||||
|
testParsePassthroughInt = {
|
||||||
|
expr = ip.parse 3232235777;
|
||||||
|
expected = 3232235777;
|
||||||
|
};
|
||||||
|
|
||||||
|
testFormatBasic = {
|
||||||
|
expr = ip.format 3232235777;
|
||||||
|
expected = "192.168.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFormatZeros = {
|
||||||
|
expr = ip.format 0;
|
||||||
|
expected = "0.0.0.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFormatMax = {
|
||||||
|
expr = ip.format 4294967295;
|
||||||
|
expected = "255.255.255.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFormatPassthroughString = {
|
||||||
|
expr = ip.format "192.168.1.1";
|
||||||
|
expected = "192.168.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
testToOctetsBasic = {
|
||||||
|
expr = ip.toOctets "192.168.1.1";
|
||||||
|
expected = [
|
||||||
|
192
|
||||||
|
168
|
||||||
|
1
|
||||||
|
1
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testToOctetsFromInt = {
|
||||||
|
expr = ip.toOctets 3232235777;
|
||||||
|
expected = [
|
||||||
|
192
|
||||||
|
168
|
||||||
|
1
|
||||||
|
1
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testToOctetsZeros = {
|
||||||
|
expr = ip.toOctets "0.0.0.0";
|
||||||
|
expected = [
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testFromOctetsBasic = {
|
||||||
|
expr = ip.fromOctets [
|
||||||
|
192
|
||||||
|
168
|
||||||
|
1
|
||||||
|
1
|
||||||
|
];
|
||||||
|
expected = 3232235777;
|
||||||
|
};
|
||||||
|
|
||||||
|
testFromOctetsZeros = {
|
||||||
|
expr = ip.fromOctets [
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
];
|
||||||
|
expected = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
testFromOctetsTen = {
|
||||||
|
expr = ip.fromOctets [
|
||||||
|
10
|
||||||
|
0
|
||||||
|
0
|
||||||
|
1
|
||||||
|
];
|
||||||
|
expected = 167772161;
|
||||||
|
};
|
||||||
|
|
||||||
|
testAddBasic = {
|
||||||
|
expr = ip.add "192.168.1.1" 10;
|
||||||
|
expected = 3232235787;
|
||||||
|
};
|
||||||
|
|
||||||
|
testAddStrBasic = {
|
||||||
|
expr = ip.addStr "192.168.1.1" 10;
|
||||||
|
expected = "192.168.1.11";
|
||||||
|
};
|
||||||
|
|
||||||
|
testAddCrossOctet = {
|
||||||
|
expr = ip.addStr "192.168.1.250" 10;
|
||||||
|
expected = "192.168.2.4";
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubtractBasic = {
|
||||||
|
expr = ip.subtract "192.168.1.10" 5;
|
||||||
|
expected = 3232235781;
|
||||||
|
};
|
||||||
|
|
||||||
|
testSubtractStrBasic = {
|
||||||
|
expr = ip.subtractStr "192.168.1.10" 5;
|
||||||
|
expected = "192.168.1.5";
|
||||||
|
};
|
||||||
|
|
||||||
|
testDiffBasic = {
|
||||||
|
expr = ip.diff "192.168.1.10" "192.168.1.1";
|
||||||
|
expected = 9;
|
||||||
|
};
|
||||||
|
|
||||||
|
testDiffNegative = {
|
||||||
|
expr = ip.diff "192.168.1.1" "192.168.1.10";
|
||||||
|
expected = -9;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCompareLess = {
|
||||||
|
expr = ip.compare "10.0.0.1" "10.0.0.2";
|
||||||
|
expected = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCompareGreater = {
|
||||||
|
expr = ip.compare "10.0.0.2" "10.0.0.1";
|
||||||
|
expected = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCompareEqual = {
|
||||||
|
expr = ip.compare "10.0.0.1" "10.0.0.1";
|
||||||
|
expected = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
testLtTrue = {
|
||||||
|
expr = ip.lt "10.0.0.1" "10.0.0.2";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testLtFalse = {
|
||||||
|
expr = ip.lt "10.0.0.2" "10.0.0.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testGtTrue = {
|
||||||
|
expr = ip.gt "10.0.0.2" "10.0.0.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testEqTrue = {
|
||||||
|
expr = ip.eq "10.0.0.1" "10.0.0.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testEqFalse = {
|
||||||
|
expr = ip.eq "10.0.0.1" "10.0.0.2";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testMinStr = {
|
||||||
|
expr = ip.minStr "10.0.0.5" "10.0.0.2";
|
||||||
|
expected = "10.0.0.2";
|
||||||
|
};
|
||||||
|
|
||||||
|
testMaxStr = {
|
||||||
|
expr = ip.maxStr "10.0.0.5" "10.0.0.2";
|
||||||
|
expected = "10.0.0.5";
|
||||||
|
};
|
||||||
|
|
||||||
|
testRoundtrip = {
|
||||||
|
expr = ip.format (ip.parse "172.16.32.64");
|
||||||
|
expected = "172.16.32.64";
|
||||||
|
};
|
||||||
|
}
|
||||||
195
tests/iterate-tests.nix
Normal file
195
tests/iterate-tests.nix
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
iterate,
|
||||||
|
ip,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) runTests;
|
||||||
|
|
||||||
|
in
|
||||||
|
runTests {
|
||||||
|
testRangeBasic = {
|
||||||
|
expr = iterate.rangeStr "10.0.0.1" "10.0.0.3";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.1"
|
||||||
|
"10.0.0.2"
|
||||||
|
"10.0.0.3"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testRangeSingle = {
|
||||||
|
expr = iterate.rangeStr "10.0.0.1" "10.0.0.1";
|
||||||
|
expected = [ "10.0.0.1" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testRangeEmpty = {
|
||||||
|
expr = iterate.rangeStr "10.0.0.5" "10.0.0.1";
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testRangeCrossOctet = {
|
||||||
|
expr = iterate.rangeStr "10.0.0.254" "10.0.1.2";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.254"
|
||||||
|
"10.0.0.255"
|
||||||
|
"10.0.1.0"
|
||||||
|
"10.0.1.1"
|
||||||
|
"10.0.1.2"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrAddresses30 = {
|
||||||
|
expr = iterate.cidrAddressesStr "10.0.0.0/30";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.0"
|
||||||
|
"10.0.0.1"
|
||||||
|
"10.0.0.2"
|
||||||
|
"10.0.0.3"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrAddresses32 = {
|
||||||
|
expr = iterate.cidrAddressesStr "10.0.0.1/32";
|
||||||
|
expected = [ "10.0.0.1" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrAddressesCount = {
|
||||||
|
expr = builtins.length (iterate.cidrAddresses "10.0.0.0/28");
|
||||||
|
expected = 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrHosts30 = {
|
||||||
|
expr = iterate.cidrHostsStr "10.0.0.0/30";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.1"
|
||||||
|
"10.0.0.2"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrHosts31 = {
|
||||||
|
expr = iterate.cidrHostsStr "10.0.0.0/31";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.0"
|
||||||
|
"10.0.0.1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrHosts32 = {
|
||||||
|
expr = iterate.cidrHostsStr "10.0.0.1/32";
|
||||||
|
expected = [ "10.0.0.1" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCidrHostsCount = {
|
||||||
|
expr = builtins.length (iterate.cidrHosts "10.0.0.0/28");
|
||||||
|
expected = 14;
|
||||||
|
};
|
||||||
|
|
||||||
|
testInRangeTrue = {
|
||||||
|
expr = iterate.inRange "10.0.0.1" "10.0.0.5" "10.0.0.3";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testInRangeStart = {
|
||||||
|
expr = iterate.inRange "10.0.0.1" "10.0.0.5" "10.0.0.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testInRangeEnd = {
|
||||||
|
expr = iterate.inRange "10.0.0.1" "10.0.0.5" "10.0.0.5";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testInRangeFalseLow = {
|
||||||
|
expr = iterate.inRange "10.0.0.2" "10.0.0.5" "10.0.0.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testInRangeFalseHigh = {
|
||||||
|
expr = iterate.inRange "10.0.0.1" "10.0.0.5" "10.0.0.6";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCountRangeBasic = {
|
||||||
|
expr = iterate.countRange "10.0.0.1" "10.0.0.10";
|
||||||
|
expected = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCountRangeSingle = {
|
||||||
|
expr = iterate.countRange "10.0.0.1" "10.0.0.1";
|
||||||
|
expected = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
testCountRangeEmpty = {
|
||||||
|
expr = iterate.countRange "10.0.0.5" "10.0.0.1";
|
||||||
|
expected = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
testMapRangeFormat = {
|
||||||
|
expr = iterate.mapRange ip.format "10.0.0.1" "10.0.0.3";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.1"
|
||||||
|
"10.0.0.2"
|
||||||
|
"10.0.0.3"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testMapRangeOctets = {
|
||||||
|
expr = iterate.mapRange ip.toOctets "10.0.0.1" "10.0.0.2";
|
||||||
|
expected = [
|
||||||
|
[
|
||||||
|
10
|
||||||
|
0
|
||||||
|
0
|
||||||
|
1
|
||||||
|
]
|
||||||
|
[
|
||||||
|
10
|
||||||
|
0
|
||||||
|
0
|
||||||
|
2
|
||||||
|
]
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testFilterRangeEven = {
|
||||||
|
expr = builtins.map ip.format (iterate.filterRange (i: lib.mod i 2 == 0) "10.0.0.1" "10.0.0.5");
|
||||||
|
expected = [
|
||||||
|
"10.0.0.2"
|
||||||
|
"10.0.0.4"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testTakeRangeBasic = {
|
||||||
|
expr = iterate.takeRangeStr 3 "10.0.0.1" "10.0.0.10";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.1"
|
||||||
|
"10.0.0.2"
|
||||||
|
"10.0.0.3"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testTakeRangeMoreThanAvailable = {
|
||||||
|
expr = iterate.takeRangeStr 10 "10.0.0.1" "10.0.0.3";
|
||||||
|
expected = [
|
||||||
|
"10.0.0.1"
|
||||||
|
"10.0.0.2"
|
||||||
|
"10.0.0.3"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testTakeRangeZero = {
|
||||||
|
expr = iterate.takeRangeStr 0 "10.0.0.1" "10.0.0.10";
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testFindInRangeBasic = {
|
||||||
|
expr = iterate.findInRangeStr (i: lib.mod i 5 == 0) "10.0.0.1" "10.0.0.10";
|
||||||
|
expected = "10.0.0.5";
|
||||||
|
};
|
||||||
|
|
||||||
|
testFindInRangeNotFound = {
|
||||||
|
expr = iterate.findInRange (i: i > 1000000000) "10.0.0.1" "10.0.0.10";
|
||||||
|
expected = null;
|
||||||
|
};
|
||||||
|
}
|
||||||
266
tests/validate-tests.nix
Normal file
266
tests/validate-tests.nix
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
{ lib, validate }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) runTests;
|
||||||
|
|
||||||
|
in runTests {
|
||||||
|
testIsValidIpBasic = {
|
||||||
|
expr = validate.isValidIp "192.168.1.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpZeros = {
|
||||||
|
expr = validate.isValidIp "0.0.0.0";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpMax = {
|
||||||
|
expr = validate.isValidIp "255.255.255.255";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpInvalidOctet = {
|
||||||
|
expr = validate.isValidIp "256.1.1.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpTooFewOctets = {
|
||||||
|
expr = validate.isValidIp "192.168.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpTooManyOctets = {
|
||||||
|
expr = validate.isValidIp "192.168.1.1.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpEmpty = {
|
||||||
|
expr = validate.isValidIp "";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpLetters = {
|
||||||
|
expr = validate.isValidIp "abc.def.ghi.jkl";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpNegative = {
|
||||||
|
expr = validate.isValidIp "-1.0.0.0";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidIpNotString = {
|
||||||
|
expr = validate.isValidIp 12345;
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrBasic = {
|
||||||
|
expr = validate.isValidCidr "192.168.1.0/24";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrSlash0 = {
|
||||||
|
expr = validate.isValidCidr "0.0.0.0/0";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrSlash32 = {
|
||||||
|
expr = validate.isValidCidr "192.168.1.1/32";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrInvalidPrefix = {
|
||||||
|
expr = validate.isValidCidr "192.168.1.0/33";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrNoPrefix = {
|
||||||
|
expr = validate.isValidCidr "192.168.1.0";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrInvalidIp = {
|
||||||
|
expr = validate.isValidCidr "256.168.1.0/24";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrEmpty = {
|
||||||
|
expr = validate.isValidCidr "";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsValidCidrNotString = {
|
||||||
|
expr = validate.isValidCidr 12345;
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testTryParseIpValid = {
|
||||||
|
expr = validate.tryParseIp "192.168.1.1";
|
||||||
|
expected = 3232235777;
|
||||||
|
};
|
||||||
|
|
||||||
|
testTryParseIpInvalid = {
|
||||||
|
expr = validate.tryParseIp "invalid";
|
||||||
|
expected = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
testTryParseCidrValid = {
|
||||||
|
expr = validate.tryParseCidr "192.168.1.0/24";
|
||||||
|
expected = { address = 3232235776; prefixLen = 24; };
|
||||||
|
};
|
||||||
|
|
||||||
|
testTryParseCidrInvalid = {
|
||||||
|
expr = validate.tryParseCidr "invalid";
|
||||||
|
expected = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsPrivate10 = {
|
||||||
|
expr = validate.isPrivate "10.1.2.3";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsPrivate172 = {
|
||||||
|
expr = validate.isPrivate "172.16.5.10";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsPrivate172Edge = {
|
||||||
|
expr = validate.isPrivate "172.31.255.255";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsPrivate172Outside = {
|
||||||
|
expr = validate.isPrivate "172.32.0.0";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsPrivate192 = {
|
||||||
|
expr = validate.isPrivate "192.168.100.50";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsPrivatePublic = {
|
||||||
|
expr = validate.isPrivate "8.8.8.8";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsLoopback127 = {
|
||||||
|
expr = validate.isLoopback "127.0.0.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsLoopback127Any = {
|
||||||
|
expr = validate.isLoopback "127.255.255.254";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsLoopbackFalse = {
|
||||||
|
expr = validate.isLoopback "128.0.0.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsLinkLocalTrue = {
|
||||||
|
expr = validate.isLinkLocal "169.254.1.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsLinkLocalFalse = {
|
||||||
|
expr = validate.isLinkLocal "169.255.1.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsMulticastTrue = {
|
||||||
|
expr = validate.isMulticast "224.0.0.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsMulticastMax = {
|
||||||
|
expr = validate.isMulticast "239.255.255.255";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsMulticastFalse = {
|
||||||
|
expr = validate.isMulticast "223.255.255.255";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsBroadcastTrue = {
|
||||||
|
expr = validate.isBroadcast "255.255.255.255";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsBroadcastFalse = {
|
||||||
|
expr = validate.isBroadcast "255.255.255.254";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsUnspecifiedTrue = {
|
||||||
|
expr = validate.isUnspecified "0.0.0.0";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsUnspecifiedFalse = {
|
||||||
|
expr = validate.isUnspecified "0.0.0.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsDocumentation192 = {
|
||||||
|
expr = validate.isDocumentation "192.0.2.1";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsDocumentation198 = {
|
||||||
|
expr = validate.isDocumentation "198.51.100.50";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsDocumentation203 = {
|
||||||
|
expr = validate.isDocumentation "203.0.113.100";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsDocumentationFalse = {
|
||||||
|
expr = validate.isDocumentation "192.0.3.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsGlobalUnicastPublic = {
|
||||||
|
expr = validate.isGlobalUnicast "8.8.8.8";
|
||||||
|
expected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsGlobalUnicastPrivate = {
|
||||||
|
expr = validate.isGlobalUnicast "192.168.1.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testIsGlobalUnicastLoopback = {
|
||||||
|
expr = validate.isGlobalUnicast "127.0.0.1";
|
||||||
|
expected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
testClassifyLoopback = {
|
||||||
|
expr = validate.classify "127.0.0.1";
|
||||||
|
expected = "loopback";
|
||||||
|
};
|
||||||
|
|
||||||
|
testClassifyPrivate = {
|
||||||
|
expr = validate.classify "192.168.1.1";
|
||||||
|
expected = "private";
|
||||||
|
};
|
||||||
|
|
||||||
|
testClassifyLinkLocal = {
|
||||||
|
expr = validate.classify "169.254.1.1";
|
||||||
|
expected = "link-local";
|
||||||
|
};
|
||||||
|
|
||||||
|
testClassifyMulticast = {
|
||||||
|
expr = validate.classify "224.0.0.1";
|
||||||
|
expected = "multicast";
|
||||||
|
};
|
||||||
|
|
||||||
|
testClassifyGlobal = {
|
||||||
|
expr = validate.classify "8.8.8.8";
|
||||||
|
expected = "global-unicast";
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user