init
This commit is contained in:
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;
|
||||
}
|
||||
Reference in New Issue
Block a user