Pfx.partition_range
partition_range
, go back to Pfx module for more information.
Specs
Returns a list of prefixes that cover the given range of address space.
The (inclusive) range can be specified either by:
start
,stop
prefixes, orstart
,nhosts
When using the start
,stop
-prefixes as a range, both prefixes need to be
of the same type, otherwise an argument error is raised. The second form of
start
, nhosts
requires that nhosts
does not exceed the addressable capacity
of given start
prefix's type.
If start
lies to the right of stop
, they are not reversed and the build
up of the list of prefixes will wrap around the available address space.
If nhosts
is negative, start
is effectively the last address.
Note: any mask information for start
or stop
prefixes are ignored, if
possible.
Examples
iex> partition_range("10.10.10.0", "10.10.10.130")
["10.10.10.0/25", "10.10.10.128/31", "10.10.10.130"]
iex> partition_range("10.10.10.0", 131)
["10.10.10.0/25", "10.10.10.128/31", "10.10.10.130"]
iex> partition_range("10.10.10.0", 131)
...> |> Enum.map(&size/1) |> Enum.sum()
131
iex(481)> partition_range("acdc::1976", "acdc::2021")
["acdc::1976/127", "acdc::1978/125",
"acdc::1980/121", "acdc::1a00/119",
"acdc::1c00/118", "acdc::2000/123",
"acdc::2020/127"]
When working with address tuples, the result will be in address,length-tuples otherwise prefix length information would be lost.
# 128 hosts, starting with "10.10.10.128"
iex> partition_range({10, 10, 10, 128}, 128)
[{{10, 10, 10, 128}, 25}]
# 1 host, starting with "10.10.10.10"
iex> partition_range({10, 10, 10, 10}, 1)
[{{10, 10, 10, 10}, 32}]
# 0 hosts always yield an empty list
iex> partition_range({10, 10, 10, 10}, 0)
[]
iex> partition_range("10.10.10.10", 0)
[]
# binary format may not show the /len, if it is a full prefix
iex> partition_range("10.10.10.10", 1)
["10.10.10.10"]
The range is inclusive, so both start
and stop
are always included. In fact,
start
is the first address of the first prefix in the list and stop
the last
address in the last prefix.
iex> partition_range("10.10.10.10", "10.10.10.10")
["10.10.10.10"]
iex> partition_range("10.10.10.0", 512)
["10.10.10.0/23"]
iex> partition_range("10.10.10.0", 131)
["10.10.10.0/25", "10.10.10.128/31", "10.10.10.130"]
# negative number means start is actually the last address in the range
iex> partition_range("10.10.10.130", -131)
["10.10.10.0/25", "10.10.10.128/31", "10.10.10.130"]
Any mask information, if present, is ignored for both start
and stop
. Note that
when using Pfx.t/0
structs, the mask will already have been applied in which case
the address will be the first address in the prefix.
iex> partition_range("10.10.10.8/24", 10)
["10.10.10.8/29", "10.10.10.16/31"]
iex> partition_range("10.10.10.10/24", "10.10.10.17/24")
["10.10.10.10/31", "10.10.10.12/30", "10.10.10.16/31"]
# new() has already applied the mask
iex> start = new("10.10.10.10/24")
iex> stop = new("10.10.10.17/24")
iex> partition_range(start, stop)
[%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32}]
# so the above is basically the same as:
iex> partition_range("10.10.10.0", "10.10.10.0")
["10.10.10.0"]
The list of prefixes is built up starting with start
and prefixes are added
until it represents the entire range, wrapping the available address space if
needed. So if start
actually lies to the right of stop
, or if nhosts
is large enough, wrapping may occur. To avoid wrapping use Pfx.compare/2
to check whether or not to swap start
and stop
.
# happily wraps around address space boundary (so beware)
iex> partition_range("0.0.0.0", -257)
["255.255.255.0/24", "0.0.0.0"]
iex> partition_range("255.255.255.0", 257)
["255.255.255.0/24", "0.0.0.0"]
iex> partition_range("255.255.255.255", "0.0.0.255")
["255.255.255.255", "0.0.0.0/24"]
iex> start = "10.10.255.255"
iex> stop = "10.10.0.0"
iex> case compare(start, stop) do
...> :gt -> partition_range(stop, start)
...> _ -> partition_range(start, stop)
...> end
["10.10.0.0/16"]
Finally, the list of prefixes will have varying prefix lengths that ramp up and down. So, when building some sort of access control list, sorting on prefix lengths (ascending, so less specifics come first) may be preferable.
iex> partition_range("10.10.10.10", "10.10.10.31")
["10.10.10.10/31", "10.10.10.12/30", "10.10.10.16/28"]
iex> partition_range("10.10.10.10", "10.10.10.31")
...> |> Enum.sort(&(pfxlen(&1) <= pfxlen(&2)))
["10.10.10.16/28", "10.10.10.12/30", "10.10.10.10/31"]
# the above actually converts between string and `t:Pfx.t/0` twice,
# to avoid that do something like this:
iex> partition_range(new("10.10.10.10"), new("10.10.10.31"))
...> |> Enum.sort(&(pfxlen(&1) <= pfxlen(&2)))
...> |> Enum.map(&format/1)
["10.10.10.16/28", "10.10.10.12/30", "10.10.10.10/31"]