Pfx.partition_range

You're seeing just the function partition_range, go back to Pfx module for more information.
Link to this function

partition_range(prefix, nhosts)

View Source

Specs

partition_range(prefix(), prefix() | integer()) :: [prefix()]

Returns a list of prefixes that cover the given range of address space.

The (inclusive) range can be specified either by:

  • start, stop prefixes, or
  • start, 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:0:0:0:0:0:0:1976/127", "acdc:0:0:0:0:0:0:1978/125",
 "acdc:0:0:0:0:0:0:1980/121", "acdc:0:0:0:0:0:0:1a00/119",
 "acdc:0:0:0:0:0:0:1c00/118", "acdc:0:0:0:0:0:0:2000/123",
 "acdc:0:0:0:0:0:0:2020/127"]

When working with address tuples, the result will be in addres,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"]