Pfx (Pfx v0.5.0) View Source
Functions to make working with prefixes easier, especially IP prefixes (IPv4 and IPv6).
Pfx
defines a prefix as a struct with a number of bits
and a maximum
maxlen
length. Hence a Pfx
struct represents some domain-specific value,
like an IPv4/6 address or network, a MAC address, a MAC OUI range or something
completely different.
A Pfx
struct can be created with Pfx.new/2
using:
- a
bitstring/0
and anon_neg_integer/0
for the maximum length, - a
Pfx.t/0
and anon_neg_integer/0
for a (new) maximum length.
This allows for the creation of any sort of prefix.
Use Pfx.new/1
to create a prefix struct from:
- a
Pfx.ip_address/0
, - a
Pfx.ip_prefix/0
, or - a
binary/0
, a string in IPv4 CIDR, IPv6, EUI-48 or EUI-64 format
This creates a IPv4, IPv6, EUI-48 or an EUI-64 prefix. Other means to create
prefixes include Pfx.from_mac/1
and Pfx.from_hex/1
.
Several functions, like Pfx.unique_local?/1
are more IP oriented, and are
included along with the more generic Pfx
functions (like Pfx.cut/3
) in
order to have one module to rule them all.
Functions generally accept either a Pfx.t/0
, a Pfx.ip_address/0
, a
Pfx.ip_prefix/0
or a binary from option 4 above and yield their result in
the same fashion:
iex> hosts("10.10.10.0/30")
["10.10.10.0", "10.10.10.1", "10.10.10.2", "10.10.10.3"]
iex> hosts({{10, 10, 10, 0}, 30})
[
{{10, 10, 10, 0}, 32},
{{10, 10, 10, 1}, 32},
{{10, 10, 10, 2}, 32},
{{10, 10, 10, 3}, 32}
]
iex> hosts(%Pfx{bits: <<10, 10, 10, 0::6>>, maxlen: 32})
[
%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32},
%Pfx{bits: <<10, 10, 10, 1>>, maxlen: 32},
%Pfx{bits: <<10, 10, 10, 2>>, maxlen: 32},
%Pfx{bits: <<10, 10, 10, 3>>, maxlen: 32},
]
# adopt representation of first argument
iex> band({10, 10, 10, 1}, "255.255.255.0")
{10, 10, 10, 0}
iex> multicast?("ff00::1")
true
# MAC OUI prefix
iex> keep("aa:bb:cc:dd:ee:ff", 24)
"AA-BB-CC-00-00-00/24"
# get IPv4 from a IPv4 compatible IPv6 address
iex> cut("::1.2.3.4", -1, -32)
"1.2.3.4"
Validity
The Pfx.new/2
function will silently clip the provided bits
-string to
maxlen
-bits when needed, since a Pfx
struct named pfx
is valid, iff:
bit_size(pfx.bits)
<=pfx.maxlen
, and wherepfx.maxlen
is anon_neg_integer/0
Keep that in mind when instantiating directly or updating a Pfx
, otherwise
functions will choke on it.
Same goes for Pfx.ip_address/0
representations, which must be a valid
:inet.ip_address()
, representing either an IPv4 or IPv6 address through a
tuple of four 8
-bit wide numbers or eight 16
-bit wide numbers.
If used as the first element in a Pfx.ip_prefix/0
tuple, the second element
is interpreted as the mask, used to clip the bitstring when creating the Pfx
struct. IPv4 masks must be in range 0..32
and IPv6 masks in range 0..128
.
The resulting Pfx
will have its maxlen
set to 32
for IPv4 tuples and
128
for IPv6 tuples.
Last but not least, binaries are interpreted as either an IPv4 in CIDR-notation, an IPv6 address/prefix, an EUI-48 or EUI-64 formatted string.
# IPv4
iex> new("1.2.3.4")
%Pfx{bits: <<1, 2, 3, 4>>, maxlen: 32}
iex> new({1, 2, 3, 4})
%Pfx{bits: <<1, 2, 3, 4>>, maxlen: 32}
iex> new("1.2.3.0/24")
%Pfx{bits: <<1, 2, 3>>, maxlen: 32}
iex> new({{1, 2, 3, 0}, 24})
%Pfx{bits: <<1, 2, 3>>, maxlen: 32}
# IPv6
iex> new("acdc:1975::")
%Pfx{bits: <<0xACDC::16, 0x1975::16, 0::96>>, maxlen: 128}
iex> new({44252, 6517, 0, 0, 0, 0, 0, 0})
%Pfx{bits: <<0xACDC::16, 0x1975::16, 0::96>>, maxlen: 128}
# EUI-48
iex> new("00-88-88-88-88-88")
%Pfx{bits: <<0, 0x88, 0x88, 0x88, 0x88, 0x88>>, maxlen: 48}
iex> new("0088.8888.8888")
%Pfx{bits: <<0, 0x88, 0x88, 0x88, 0x88, 0x88>>, maxlen: 48}
# EUI-64
iex> new("02-88-88-FF-FE-88-88-88")
%Pfx{bits: <<0x02, 0x88, 0x88, 0xFF, 0xFE, 0x88, 0x88, 0x88>>, maxlen: 64}
iex> new("0288.88FF.FE88.8888")
%Pfx{bits: <<0x02, 0x88, 0x88, 0xFF, 0xFE, 0x88, 0x88, 0x88>>, maxlen: 64}
Ancient tradition
Pfx.new/1
accepts CIDR-strings which are ultimately processed using erlang's
:inet.parse_address
which, at the time of writing, still honors the ancient
linux tradition of injecting zero's (rather than appending them) when presented
with less than four IPv4 digits in a CIDR string.
# "d" -> "0.0.0.d"
iex> new("10") |> format()
"0.0.0.10"
iex> new("10/8") |> format()
"0.0.0.0/8"
# "d1.d2" -> "d1.0.0.d2"
iex> new("10.10") |> format()
"10.0.0.10"
iex> new("10.10/16") |> format()
"10.0.0.0/16"
# "d1.d2.d3" -> "d1.d2.0.d3"
iex> new("10.10.10") |> format()
"10.10.0.10"
iex> new("10.10.10/24") |> format()
"10.10.0.0/24"
Bottom line: never go short, you may be unpleasantly surprised.
EUI-64's
Since a string is first parsed as an IP prefix, EUI-64's like
"11:22:33:44:55:66:77:88" will come out as an IPv6 prefix with their maxlen
property set to 128
. So, when parsing EUI's that might use ':'-s as
punctuation, use Pfx.from_mac/1
, which also supports the tuple formats. Like
Pfx.new/1
, this function always returns a Pfx.t/0
-struct.
# new/1 parses EUI-64's like these correctly:
iex> new("1122.3344.5566.7788")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
iex> new("11-22-33-44-55-66-77-88")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
# but new/1 turns this valid EUI-64 into IPv6 due to ':'-punctuation used:
iex> new("01:02:03:04:05:06:07:08")
%Pfx{bits: <<0x1::16, 0x2::16, 0x3::16, 0x4::16, 0x5::16, 0x6::16, 0x7::16, 0x8::16>>, maxlen: 128}
# in this case, use from_mac/1
iex> from_mac("01:02:03:04:05:06:07:08")
%Pfx{bits: <<0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8>>, maxlen: 64}
# and supports digit-styled EUI's
iex> from_mac({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8})
%Pfx{bits: <<0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8>>, maxlen: 64}
# from {{digits}, len}, keeping first 3 bytes
iex> from_mac({{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 24})
%Pfx{bits: <<0x1, 0x2, 0x3>>, maxlen: 64}
Enumeration
A Pfx.t/0
implements the Enumerable
protocol:
iex> for ip <- %Pfx{bits: <<1, 2, 3, 0::6>>, maxlen: 32}, do: ip
[
%Pfx{bits: <<1, 2, 3, 0>>, maxlen: 32},
%Pfx{bits: <<1, 2, 3, 1>>, maxlen: 32},
%Pfx{bits: <<1, 2, 3, 2>>, maxlen: 32},
%Pfx{bits: <<1, 2, 3, 3>>, maxlen: 32},
]
String.Chars
Pfx.t/0
implements the String.Chars
protocol with some defaults for
prefixes that formats prefixes with:
maxlen: 32
as an IPv4 CIDR string,maxlen: 48
as a EUI-48 address string andmaxlen: 64
as a EUI-64 address stringmaxlen: 128
as an IPv6 string
Other maxlen
's will simply come out as a series of 8-bit numbers joined by "."
followed by /num_of_bits
. The latter is omitted if equal to pfx.bits
length.
If other formatting is required, use the Pfx.format/2
function, which takes
some options that help shape the string representation for a Pfx
struct.
iex> "#{%Pfx{bits: <<10, 11, 12>>, maxlen: 32}}"
"10.11.12.0/24"
iex> "#{new(<<44252::16, 6518::16>>, 128)}"
"acdc:1976:0:0:0:0:0:0/32"
iex> "#{%Pfx{bits: <<0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6>>, maxlen: 48}}"
"A1-B2-C3-D4-E5-F6"
iex> "#{new(<<1, 2, 3, 4, 5>>, 64)}"
"01-02-03-04-05-00-00-00/40"
# the enumeration example earlier, could also read:
iex> for ip <- new("1.2.3.0/30"), do: "#{ip}"
[
"1.2.3.0",
"1.2.3.1",
"1.2.3.2",
"1.2.3.3"
]
# or
iex> for ip <- new("1.2.3.0/30"), do: digits(ip, 8) |> elem(0)
[
{1, 2, 3, 0},
{1, 2, 3, 1},
{1, 2, 3, 2},
{1, 2, 3, 3}
]
Limitations
A lot of Pfx
-functions convert the Pfx.bits
bitstring to an integer using
Pfx.cast/1
, before performing some, often Bitwise
-related, calculation on
them. Luckily Elixir can handle pretty
large numbers which seem mostly limited by the available system memory.
Other functions, like Pfx.digits/2
return a tuple with numbers and are so
limited by the maximum number of elements in a tuple (~16M+).
So if you're taking this somewhere far, far away, heed these limitations before take off.
Also, everything is done in Elixir with no extra, external dependencies. Usually fast enough, but if you really feel the need for speed, you might want to look elsewhere.
Anyway, enough downplay, here are some more examples.
Examples
# IANA's OUI range 00-00-5e-xx-xx-xx
iex> new("00-00-5e-00-00-00/24")
%Pfx{bits: <<0, 0, 94>>, maxlen: 48}
# IANA's VRRP MAC address range 00-00-5e-00-01-{VRID}
iex> vrrp_mac_range = new("00-00-5e-00-01-00/40")
%Pfx{bits: <<0, 0, 94, 0, 1>>, maxlen: 48}
iex>
iex> vrrp_mac = new("00-00-5e-00-01-0f")
%Pfx{bits: <<0, 0, 94, 0, 1, 15>>, maxlen: 48}
iex>
iex> member?(vrrp_mac, vrrp_mac_range)
true
iex> cut(vrrp_mac, -1, -8) |> cast()
15
iex> new("10.10.10.0/24")
%Pfx{bits: <<10, 10, 10>>, maxlen: 32}
iex> mask("10.10.10.0/25")
"255.255.255.128"
iex> inv_mask("10.10.10.0/25")
"0.0.0.127"
iex> dns_ptr("acdc:1975::b1ba:2021")
"1.2.0.2.a.b.1.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.5.7.9.1.c.d.c.a.ip6.arpa"
iex> teredo_decode("2001:0000:4136:e378:8000:63bf:3fff:fdd2")
%{
server: "65.54.227.120",
client: "192.0.2.45",
port: 40000,
flags: {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
prefix: "2001:0000:4136:e378:8000:63bf:3fff:fdd2"
}
iex> eui64_encode("0288.8888.8888")
"00-88-88-FF-FE-88-88-88"
Some functions have IP related names (like Pfx.teredo_decode/1
, but most of
the times functions have generic names, since they apply to all sorts of
prefixes, e.g.
iex> partition("10.10.10.0/24", 26)
[ "10.10.10.0/26",
"10.10.10.64/26",
"10.10.10.128/26",
"10.10.10.192/26"
]
iex> new(<<1, 2, 3, 4>>, 32)
...> |> format(width: 1, unit: 8)
"00000001.00000010.00000011.00000100"
iex> from_hex("123456789abcdef")
...> |> keep(20)
%Pfx{bits: <<0x12, 0x34, 0x5::4>>, maxlen: 60}
iex> brot("1.2.3.4", 8)
"4.1.2.3"
Link to this section Summary
Types
An :inet IPv4 or IPv6 address (tuple)
An IPv4 prefix ({t:inet.ip4_address/0
, 0..32}) or an IPv6 prefix ({t:inet.ip6_address/0
, 0..128}).
A prefix expressed as either a Pfx.t/0
struct, an IP address-tuple, an
address,length-tuple or a CIDR string.
A prefix struct with fields: bits
and maxlen
.
IP Functions
Returns the broadcast prefix (full address) for given pfx
.
Return a reverse DNS name (pointer) for given pfx
.
Decode a modified EUI-64 back into the original EUI-48 address.
Create a modified EUI-64 out of eui
(an EUI-48 address or an EUI-64).
Return a map with link-local address components for given pfx
.
Returns true if pfx
is a link-local prefix, false otherwise
Returns true is pfx
is a multicast prefix, false otherwise
Returns a map with multicast address components for given pfx
.
Returns the embedded IPv4 address of a nat64 pfx
Return an IPv4 embedded IPv6 address for given pfx6
and pfx4
.
Returns the this-network prefix (full address) for given pfx
.
Returns true if prefix
is a teredo address, false otherwise
Returns a map with the teredo address components of pfx
or nil.
Encode given server
, client
, port
and flags
as an IPv6 teredo address.
Returns true if pfx
is designated as "private-use".
Guards
Guard that ensures both prefixes are valid and comparable (same maxlen).
Guard that ensures a given pfx
is actually valid.
Functions
A bitwise AND of two prefix/0
's.
Return pfx
prefix's bit-value at given position
.
Return the concatenation of 1 or more series of bits of the given pfx
.
Return a series of bits for given pfx
, for starting position
& length
.
A bitwise NOT of the pfx.bits
.
A bitwise OR of two prefixes.
Rotate the pfx.bits
by n
positions.
Set all pfx.bits
to either 0
or 1
.
Arithmetic shift left the pfx.bits
by n
positions.
Arithmetic shift right the pfx.bits
by n
positions.
A bitwise XOR of two prefix/0
's.
Compare function for sorting.
Contrast two Pfx
prefixes
Cut out a series of bits and turn it into its own Pfx
.
Transform a Pfx
prefix into {{digit, ..}, length}
format.
Drop count
lsb bits from given pfx
.
Turn a prefix
into a list of {number, width}
-fields.
Returns the first full length prefix from the set represented by pfx
.
Flip a single bit at position
in given pfx.bits
Generic formatter to turn a Pfx
into a string, using several options
Create a Pfx.t/0
out of a hexadecimal string.
Create a Pfx
struct from a EUI48/64 strings or tuples.
Return the nth
host in given pfx
.
Returns a list of address prefixes for given pfx
.
Insert some bits
into pfx
-s bitstring.
Returns the inverted mask for given pfx
.
Keep count
msb bits of given pfx
.
Returns the last full length prefix from the set represented by pfx
.
Given a t.Pfx.t/0
prefix, try to represent it in its original form.
Return the mask for given pfx
.
Return the nth
-member of a given pfx
.
Return the nth
subprefix for a given pfx
, using width
bits.
Returns true is prefix pfx1
is a member of prefix pfx2
Returns the neighboring prefix such that both can be combined in a supernet.
Creates a new prefix from address tuples or binaries.
Creates a new Pfx.t/0
-prefix.
Left pad the pfx.bits
to its full length using 0
-bits.
Left pad the pfx.bits
to its full length using either 0
or 1
-bits.
Left pad the pfx.bits
with n
bits of either 0
or 1
's.
Right pad the pfx.bits
to its full length using 0
-bits.
Right pad the pfx.bits
to its full length using either 0
or 1
-bits.
Right pad the pfx.bits
with n
bits of either 0
or 1
's.
Partition a Pfx
prefix into a list of new prefixes, each bitlen
long.
Remove length
bits from given pfx
-s bitstring, starting at position
.
Returns another Pfx
at distance offset
.
Returns the number of full addresses represented by given pfx
.
Return the Pfx
prefix represented by the digits
, actual length
and a given
field width
.
Returns boolean indicating whether pfx
is a valid prefix/0
or not.
Link to this section Types
Specs
ip_address() :: :inet.ip4_address() | :inet.ip6_address()
An :inet IPv4 or IPv6 address (tuple)
Specs
ip_prefix() :: {:inet.ip4_address(), 0..32} | {:inet.ip6_address(), 0..128}
An IPv4 prefix ({t:inet.ip4_address/0
, 0..32}) or an IPv6 prefix ({t:inet.ip6_address/0
, 0..128}).
Specs
prefix() :: t() | ip_address() | ip_prefix() | String.t()
A prefix expressed as either a Pfx.t/0
struct, an IP address-tuple, an
address,length-tuple or a CIDR string.
Specs
t() :: %Pfx{bits: bitstring(), maxlen: non_neg_integer()}
A prefix struct with fields: bits
and maxlen
.
Link to this section IP Functions
Specs
Returns the broadcast prefix (full address) for given pfx
.
Yields the same result as Pfx.last/1
, included for nostalgia.
Examples
iex> broadcast("10.10.0.0/16")
"10.10.255.255"
# a full address is its own broadcast address
iex> broadcast({10, 10, 10, 1})
{10, 10, 10, 1}
iex> broadcast({{10, 10, 10, 1}, 30})
{{10, 10, 10, 3}, 32}
iex> broadcast(%Pfx{bits: <<10, 10, 10>>, maxlen: 32})
%Pfx{bits: <<10, 10, 10, 255>>, maxlen: 32}
iex> broadcast(%Pfx{bits: <<0xacdc::16, 0x1976::16>>, maxlen: 128})
%Pfx{bits: <<0xACDC::16, 0x1976::16, -1::96>>, maxlen: 128}
iex> broadcast("acdc:1976::/112")
"acdc:1976:0:0:0:0:0:ffff"
Specs
Return a reverse DNS name (pointer) for given pfx
.
The prefix will be padded right with 0
-bits to a multiple of 8 for IPv4 prefixes and
to a multiple of 4 for IPv6 prefixes. Note that this might give unexpected results.
So dns_ptr/1
works best if the prefix given is actually a multiple of 4 or 8.
Examples
iex> dns_ptr("10.10.0.0/16")
"10.10.in-addr.arpa"
# "1.2.3.0/23" actually encodes as %Pfx{bits: <<1, 2, 1::size(7)>>, maxlen: 32}
# and padding right with 0-bits to a /24 yields the 1.2.2.0/24 ...
iex> dns_ptr("1.2.3.0/23")
"2.2.1.in-addr.arpa"
iex> dns_ptr("acdc:1976::/32")
"6.7.9.1.c.d.c.a.ip6.arpa"
iex> dns_ptr("acdc:1975::b1ba:2021")
"1.2.0.2.a.b.1.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.5.7.9.1.c.d.c.a.ip6.arpa"
Specs
Decode a modified EUI-64 back into the original EUI-48 address.
This function flips the 7-th bit and removes 16-bits from the middle.
Those 16-bits should be 0xFFFE
, but this is not checked or enforced.
Examples
iex> eui64_decode("0088.88FE.FF88.8888")
"02-88-88-88-88-88"
iex> eui64_decode("0288.88FF.FE88.8888")
"00-88-88-88-88-88"
iex> eui64_decode("02-88-88-FF-FE-88-88-88")
"00-88-88-88-88-88"
iex> eui64_decode({0x02, 0x88, 0x88, 0xFF, 0xFE, 0x88, 0x88, 0x88})
{0x00, 0x88, 0x88, 0x88, 0x88, 0x88}
iex> new("2001:db8:1:2:020c:29ff:fe0c:47d5")
...> |> cut(-1, -64)
...> |> eui64_decode()
...> |> format()
"00-0C-29-0C-47-D5"
Specs
Create a modified EUI-64 out of eui
(an EUI-48 address or an EUI-64).
This flips the 7-th bit (U/L - universal/local) and inserts 0xFFFE
in the
middle.
The function assumes either an EUI-48 or EUI-64 address. In the latter case, it'll only flip the 7-th bit.
Examples
iex> eui64_encode("0088.8888.8888")
"02-88-88-FF-FE-88-88-88"
iex> eui64_encode("0288.8888.8888")
"00-88-88-FF-FE-88-88-88"
iex> eui64_encode({0x00, 0x88, 0x88, 0x88, 0x88, 0x88})
{0x02, 0x88, 0x88, 0xFF, 0xFE, 0x88, 0x88, 0x88}
# modified EUI-64 from an existing EUI-64, simply flip the 7th bit
iex> eui64_encode("01:23:45:67:89:AB:CD:EF")
"03-23-45-67-89-AB-CD-EF"
Specs
Return a map with link-local address components for given pfx
.
Returns nil if pfx
is not link-local as per
rfc3927 or
rfc4291
Examples
iex> x = link_local("169.254.128.233")
iex> x
%{ digits: {169, 254, 128, 233},
prefix: "169.254.0.0/16",
ifaceID: 33001,
address: "169.254.128.233"
}
#
iex> host(x.prefix, x.ifaceID)
"169.254.128.233"
iex> y = link_local("fe80::acdc:1976")
iex> y
%{ preamble: 1018,
prefix: "fe80:0:0:0:0:0:0:0/64",
ifaceID: 2900105590,
address: "fe80::acdc:1976"
}
#
iex> host(y.prefix, y.ifaceID)
"fe80:0:0:0:0:0:acdc:1976"
Specs
Returns true if pfx
is a link-local prefix, false otherwise
Link local prefixes include:
0.0.0.0/8
, rfc1122, 'this-network'255.255.255.255/32
, rfc1f22, limited broadcast169.254.0.0/16
, rfc3927, link-local (see examples)fe80::/64
, rfc4291, link-local
Examples
# first 256 addresses are reserved
iex> link_local?("169.254.0.0")
false
# last 256 addresses are reserved
iex> link_local?("169.254.255.0")
false
# rest is considered link local
iex> link_local?("169.254.1.0")
true
iex> link_local?("169.254.254.255")
true
iex> link_local?("0.0.0.0")
true
iex> link_local?("0.255.255.255")
true
iex> link_local?({0, 255, 255, 255})
true
iex> link_local?("fe80::acdc:1975")
true
iex> link_local?("1.1.1.1")
false
# bad prefix
iex> link_local?("10.10.10.256")
false
Specs
Returns true is pfx
is a multicast prefix, false otherwise
Checks if pfx
given is a member of:
Examples
iex> multicast?("224.0.0.1")
true
iex> multicast?("ff02::1")
true
iex> multicast?({{224, 0, 0, 1}, 32})
true
iex> multicast?({224, 0, 0, 1})
true
iex> multicast?(%Pfx{bits: <<224, 0, 0, 1>>, maxlen: 32})
true
iex> multicast?("1.1.1.1")
false
# bad prefix
iex> multicast?("224.0.0.256")
false
Specs
Returns a map with multicast address components for given pfx
.
Address components are parsed according to:
Rfc specific fields are put in their own map under the rfc
-key.
- rfc6034 - Unicast-Prefix-Based IPv4 Multicast Addresses
- rfc3180 - GLOP Addressing in 233/8
- rfc3956 - Embedding the Rendezvous Point (RP) Address in an IPv6 Multicast Address
- rfc3306 - Unicast-Prefix-based IPv6 Multicast Addresses
Note that for unicast-prefix-based IPv4 multicast addresses, the unicast prefix is always taken to be 24 bits long. That should still allow for identification of the origin by looking up the assigned unicast address space that includes the /24.
Examples
iex> multicast_decode("234.192.0.2")
%{
multicast_address: "234.192.0.2",
protocol: :ipv4,
rfc: %{
group_id: 2,
multicast_prefix: "234.0.0.0/8",
rfc: 6034,
unicast_prefix: "192.0.2.0/24"
}
}
iex> Pfx.multicast_decode("FF32:0030:3FFE:FFFF:0001::/96")
%{
flags: {0, 0, 1, 1},
multicast_address: "ff32:30:3ffe:ffff:1:0:0:0/96",
multicast_prefix: "ff30:0:0:0:0:0:0:0/12",
protocol: :ipv6,
rfc: %{
group_id: 0,
unicast_prefix: "3ffe:ffff:1:0:0:0:0:0/48",
plen: 48,
reserved: 0,
rfc: 3306
},
scope: 2
}
Specs
Returns true if pfx
is matched by the Well-Known Prefixes defined in
rfc6053 and
rfc8215, false otherwise.
Note that organisation specific prefixes might still be used for nat64.
Examples
iex> nat64?("64:ff9b::10.10.10.10")
true
iex> nat64?("64:ff9b:1::10.10.10.10")
true
iex> nat64?({{0x64, 0xff9b, 0, 0, 0, 0, 0x1010, 0x1010}, 128})
true
iex> nat64?({0x64, 0xff9b, 0, 0, 0, 0, 0x1010, 0x1010})
true
iex> nat64?(%Pfx{bits: <<0x64::16, 0xff9b::16, 0::64, 0x1010::16, 0x1010::16>>, maxlen: 128})
true
# illegal/bad prefix
iex> nat64?("64:ff9b:1::10.10.10.256")
false
Specs
Returns the embedded IPv4 address of a nat64 pfx
The pfx
prefix should be a full IPv6 address. The len
defaults to 96
,
but if specified it should be one of [96, 64, 56, 48, 40, 32].
Examples
iex> nat64_decode("64:ff9b::10.10.10.10")
"10.10.10.10"
iex> nat64_decode("64:ff9b:1:0a0a:000a:0a00::", 48)
"10.10.10.10"
# from rfc6052, section 2.4
iex> nat64_decode("2001:db8:c000:221::", 32)
"192.0.2.33"
iex> nat64_decode("2001:db8:1c0:2:21::", 40)
"192.0.2.33"
iex> nat64_decode("2001:db8:122:c000:2:2100::", 48)
"192.0.2.33"
iex> nat64_decode("2001:db8:122:3c0:0:221::", 56)
"192.0.2.33"
iex> nat64_decode("2001:db8:122:344:c0:2:2100::", 64)
"192.0.2.33"
iex> nat64_decode({0x2001, 0xdb8, 0x122, 0x344, 0xC0, 0x2, 0x2100, 0x0}, 64)
{192, 0, 2, 33}
iex> nat64_decode({{0x2001, 0xdb8, 0x122, 0x344, 0xC0, 0x2, 0x2100, 0x0}, 128}, 64)
{{192, 0, 2, 33}, 32}
iex> nat64_decode("2001:db8:122:344::192.0.2.33", 96)
"192.0.2.33"
iex> nat64_decode("2001:db8:122:344::192.0.2.33", 90)
** (ArgumentError) nat64 prefix length not in [96, 64, 56, 48, 40, 32], got 90
Specs
Return an IPv4 embedded IPv6 address for given pfx6
and pfx4
.
The length of the pfx6.bits
should be one of [96, 64, 56, 48, 40, 32] as defined
in rfc6052. The pfx4
prefix should be a full address.
Examples
iex> nat64_encode("2001:db8:100::/40", "192.0.2.33")
"2001:db8:1c0:2:21:0:0:0"
iex> nat64_encode("2001:db8:122::/48", "192.0.2.33")
"2001:db8:122:c000:2:2100:0:0"
iex> nat64_encode("2001:db8:122:300::/56", "192.0.2.33")
"2001:db8:122:3c0:0:221:0:0"
iex> nat64_encode("2001:db8:122:344::/64", "192.0.2.33")
"2001:db8:122:344:c0:2:2100:0"
iex> nat64_encode("2001:db8:122:344::/96", "192.0.2.33")
"2001:db8:122:344:0:0:c000:221"
iex> nat64_encode({{0x2001, 0xdb8, 0, 0, 0, 0, 0, 0}, 32}, "192.0.2.33")
{{0x2001, 0xdb8, 0xc000, 0x221, 0, 0, 0, 0}, 128}
iex> nat64_encode(%Pfx{bits: <<0x2001::16, 0xdb8::16>>, maxlen: 128}, "192.0.2.33")
%Pfx{bits: <<0x2001::16, 0xdb8::16, 0xc000::16, 0x221::16, 0::64>>, maxlen: 128}
iex> nat64_encode("2001:db8::/32", "192.0.2.33")
"2001:db8:c000:221:0:0:0:0"
Specs
Returns the this-network prefix (full address) for given pfx
.
Yields the same result as Pfx.first/1
, included for nostalgia.
Examples
iex> network("10.10.10.1/24")
"10.10.10.0"
iex> network("acdc:1976::/32")
"acdc:1976:0:0:0:0:0:0"
# a full address is its own this-network
iex> network({10, 10, 10, 1})
{10, 10, 10, 1}
iex> network({{10, 10, 10, 1}, 24})
{{10, 10, 10, 0}, 32}
iex> network(%Pfx{bits: <<10, 10, 10>>, maxlen: 32})
%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32}
iex> network(%Pfx{bits: <<0xacdc::16, 0x1976::16>>, maxlen: 128})
%Pfx{bits: <<0xACDC::16, 0x1976::16, 0::96>>, maxlen: 128}
Specs
Returns true if prefix
is a teredo address, false otherwise
IPv6 address within the teredo service prefix of 2000:0::/32
More details in rfc4380.
Examples
iex> teredo?("2001:0000:4136:e378:8000:63bf:3fff:fdd2")
true
iex> teredo?("1.1.1.1")
false
iex> teredo?(42)
false
Specs
Returns a map with the teredo address components of pfx
or nil.
Returns nil if pfx
is not a
teredo address.
Teredo address consist of:
- the teredo service prefix of
2000:0::/32
- IPv4 address of the teredo server
- flags (16 bits) that document type of address and NAT
- Port (16 bits), the obfuscated "mapped UDP port" at the client
- IPv4 address (obfucated) of the teredo client.
More details in rfc4380.
Examples
# example from https://en.wikipedia.org/wiki/Teredo_tunneling#IPv6_addressing
iex> teredo_decode("2001:0000:4136:e378:8000:63bf:3fff:fdd2")
%{
server: "65.54.227.120",
client: "192.0.2.45",
port: 40000,
flags: {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
prefix: "2001:0000:4136:e378:8000:63bf:3fff:fdd2"
}
iex> teredo_decode({0x2001, 0, 0x4136, 0xe378, 0x8000, 0x63bf, 0x3fff, 0xfdd2})
%{
server: {65, 54, 227, 120},
client: {192, 0, 2, 45},
port: 40000,
flags: {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
prefix: {0x2001, 0x0, 0x4136, 0xe378, 0x8000, 0x63bf, 0x3fff, 0xfdd2}
}
iex> teredo_decode("1.1.1.1")
nil
Specs
Encode given server
, client
, port
and flags
as an IPv6 teredo address.
The client
and server
must be full IPv4 adresses, while both port
and flags
are interpreted as 16-bit unsigned integers.
The result mirrors the representation format of the client
argument.
Examples
iex> flags = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
iex> teredo_encode("192.0.2.45", "65.54.227.120", 40000, flags)
"2001:0:4136:e378:8000:63bf:3fff:fdd2"
iex> flags = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
iex> teredo_encode({192, 0, 2, 45}, "65.54.227.120", 40000, flags)
{0x2001, 0, 0x4136, 0xe378, 0x8000, 0x63bf, 0x3fff, 0xfdd2}
iex> flags = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
iex> teredo_encode({{192, 0, 2, 45}, 32}, "65.54.227.120", 40000, flags)
{{0x2001, 0, 0x4136, 0xe378, 0x8000, 0x63bf, 0x3fff, 0xfdd2}, 128}
iex> flags = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
iex> teredo_encode(%Pfx{bits: <<192, 0, 2, 45>>, maxlen: 32}, "65.54.227.120", 40000, flags)
%Pfx{bits: <<0x2001::16, 0::16, 0x4136::16, 0xe378::16, 0x8000::16, 0x63bf::16, 0x3fff::16, 0xfdd2::16>>, maxlen: 128}
Specs
Returns true if pfx
is designated as "private-use".
This includes the rfc1918 prefixes:
10.0.0.0/8
,172.16.0.0/12
, and192.168.0.0/16
.
And the rfc4193 prefix
fc00::/7
.
Examples
iex> unique_local?("172.31.255.255")
true
iex> unique_local?("10.10.10.10")
true
iex> unique_local?("fc00:acdc::")
true
iex> unique_local?("172.32.0.0")
false
iex> unique_local?("10.255.255.255")
true
iex> unique_local?({{172, 31, 255, 255}, 32})
true
iex> unique_local?({172, 31, 255, 255})
true
iex> unique_local?(%Pfx{bits: <<172, 31, 255, 255>>, maxlen: 32})
true
# bad prefix
iex> unique_local?("10.255.255.256")
false
Link to this section Guards
Guard that ensures both prefixes are valid and comparable (same maxlen).
Guard that ensures a given pfx
is actually valid.
- it is a
Pfx.t/0
struct, pfx.maxlen
is at:non-neg-integer/0
,pfx.maxlen
is >= 0, andbit_size(pfx.bits) <= pfx.maxlen
Link to this section Functions
Specs
A bitwise AND of two prefix/0
's.
Both prefixes must have the same maxlen
. The resulting prefix
will have the same number of bits as the first argument.
Examples
iex> band("10.10.10.10", "255.255.0.0")
"10.10.0.0"
iex> band("10.10.10.0/24", "255.255.0.0")
"10.10.0.0/24"
iex> x = new(<<128, 129, 130, 131>>, 32)
iex> y = new(<<255, 255>>, 32)
iex>
iex> band(x, y)
%Pfx{bits: <<128, 129, 0, 0>>, maxlen: 32}
iex>
iex> band(y,x)
%Pfx{bits: <<128, 129>>, maxlen: 32}
# results adopt the format of the first argument
iex> band("1.2.3.4", {255, 255, 0, 0})
"1.2.0.0"
iex> band({1, 2, 3, 4}, "255.255.0.0")
{1, 2, 0, 0}
iex> band({{1, 2, 3, 4}, 24}, {255, 255, 0, 0})
{{1, 2, 0, 0}, 24}
# honoring the ancient tradition
iex> band("1.2.3.4", "255.255")
"1.0.0.4"
Specs
Return pfx
prefix's bit-value at given position
.
A bit position is a 0
-based index from the left with range 0..maxlen-1
.
A negative bit position is taken relative to Pfx.maxlen
.
A bit position in the range of bit_size(pfx.bits) .. pfx.maxlen - 1
always
yields 0
.
Examples
iex> bit("1.2.0.0", 14)
1
# same bit
iex> bit("1.2.0.0", -18)
1
iex> bit("1.2.0.0/16", 14)
1
iex> bit({1, 2, 0, 0}, 14)
1
iex> bit({{1, 2, 0, 0}, 16}, 14)
1
iex> bit(%Pfx{bits: <<1, 2>>, maxlen: 32}, 14)
1
# 'masked' bits are deemed to be `0`
iex> bit("1.2.0.0/16", 24)
0
# errors out on invalid positions
iex> bit("255.255.255.255", 33)
** (ArgumentError) invalid bit position: 33
iex> bit("10.10.0.0/16", -33)
** (ArgumentError) invalid bit position: -33
Specs
Return the concatenation of 1 or more series of bits of the given pfx
.
Examples
iex> bits("1.2.3.4", [{0, 8}, {-1, -8}])
<<1, 4>>
iex> bits("1.2.3.0/24", [{0, 8}, {-1, -8}])
<<1, 0>>
iex> bits({1, 2, 3, 4}, [{0, 8}, {-1, -8}])
<<1, 4>>
iex> bits({{1, 2, 3, 0}, 24}, [{0,8}, {-1, -8}])
<<1, 0>>
iex> bits(%Pfx{bits: <<1, 2, 3, 4>>, maxlen: 32}, [{0,8}, {-1, -8}])
<<1, 4>>
Specs
Return a series of bits for given pfx
, for starting position
& length
.
Negative position
's are relative to the end of the pfx.bits
bitstring,
while negative length
will collect bits going left instead of to the
right. Note that the bit at given position
is always included in the
result regardless of direction. Finally, a length
of 0
results in
an empty bitstring.
Examples
# last two bytes
iex> bits("128.0.128.1", 16, 16)
<<128, 1>>
iex> bits({128, 0, 128, 1}, 16, 16) # same
<<128, 1>>
iex> bits({128, 0, 128, 1}, 31, -16) # same
<<128, 1>>
iex> bits({{128, 0, 128, 1}, 32}, 31, -16) # same
<<128, 1>>
# first byte
iex> bits(%Pfx{bits: <<128, 0, 0, 1>>, maxlen: 32}, 0, 8)
<<128>>
# same as
iex> bits(%Pfx{bits: <<128, 0, 0, 1>>, maxlen: 32}, 7, -8)
<<128>>
# missing bits are filled in as `0`
iex> x = new(<<128>>, 32)
iex> bits(x, 0, 32)
<<128, 0, 0, 0>>
iex> x = new(<<128>>, 32)
iex> bits(x, 0, 16)
<<128, 0>>
iex> x = new(<<128>>, 32)
iex> bits(x, 15, -16)
<<128, 0>>
# the last 5 bits
iex> x = new(<<255>>, 32)
iex> bits(x, 7, -5)
<<0b11111::size(5)>>
Specs
A bitwise NOT of the pfx.bits
.
Results are returned in the same representation as given pfx
.
Examples
iex> bnot("255.255.0.0")
"0.0.255.255"
iex> bnot({255, 255, 0, 0})
{0, 0, 255, 255}
iex> bnot({{255, 255, 0, 0}, 32})
{{0, 0, 255, 255}, 32}
iex> new(<<255, 255, 0, 0>>, 32) |> bnot()
%Pfx{bits: <<0, 0, 255, 255>>, maxlen: 32}
iex> bnot("5323:e689::/32")
"acdc:1976:0:0:0:0:0:0/32"
Specs
A bitwise OR of two prefixes.
Both prefixes must have the same maxlen
.
Examples
iex> bor("1.2.3.4", "0.0.255.0")
"1.2.255.4"
iex> bor({1, 2, 3, 4}, "0.0.255.0")
{1, 2, 255, 4}
iex> bor({{1, 2, 3, 4}, 16}, {0, 255, 255, 0})
{{1, 255, 0, 0}, 16}
# same sized `bits`
iex> x = new(<<10, 11, 12, 13>>, 32)
iex> y = new(<<0, 0, 255, 255>>, 32)
iex> bor(x, y)
%Pfx{bits: <<10, 11, 255, 255>>, maxlen: 32}
# same `maxlen` but differently sized `bits`: missing bits are considered to be `0`
iex> bor("10.11.12.13", new(<<255, 255>>, 32)) # "255.255.0.0/16"
"255.255.12.13"
Specs
Rotate the pfx.bits
by n
positions.
Positive n
rotates right, negative rotates left.
Note that the length of the resulting pfx.bits
stays the same.
Examples
iex> brot("1.2.3.4", 8)
"4.1.2.3"
iex> brot("1.2.3.4", -8)
"2.3.4.1"
iex> brot({1, 2, 3, 4}, 8)
{4, 1, 2, 3}
iex> brot({{1, 2, 3, 4}, 32}, -8)
{{2, 3, 4, 1}, 32}
# note: the `bits` <<1, 2>> get rotated (!)
iex> brot("1.2.0.0/16", 8)
"2.1.0.0/16"
iex> brot(%Pfx{bits: <<1, 2, 3, 4>>, maxlen: 32}, 8)
%Pfx{bits: <<4, 1, 2, 3>>, maxlen: 32}
Specs
Set all pfx.bits
to either 0
or 1
.
Examples
# defaults to `0`-bit
iex> bset("1.1.1.0/24")
"0.0.0.0/24"
iex> bset("1.1.1.0/24", 1)
"255.255.255.0/24"
iex> bset({{1, 1, 1, 0}, 24}, 1)
{{255, 255, 255, 0}, 24}
iex> bset(%Pfx{bits: <<1, 1, 1>>, maxlen: 32})
%Pfx{bits: <<0, 0, 0>>, maxlen: 32}
iex> bset(%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, 1)
%Pfx{bits: <<255, 255, 255>>, maxlen: 32}
Specs
Arithmetic shift left the pfx.bits
by n
positions.
A positive n
shifts to the left, negative n
shifts to the right.
Note that the length of pfx.bits
stays the same.
Examples
iex> bsl("1.2.3.4", 1)
"2.4.6.8"
iex> bsl("1.2.0.0/16", 2)
"4.8.0.0/16"
iex> bsl({1, 2, 3, 4}, 2)
{4, 8, 12, 16}
# note: the `bits` <<1, 2>> get shifted left 2 bits
iex> bsl({{1, 2, 0, 0}, 16}, 2)
{{4, 8, 0, 0}, 16}
iex> bsl(%Pfx{bits: <<1, 2>>, maxlen: 32}, 2)
%Pfx{bits: <<4, 8>>, maxlen: 32}
iex> bsl(%Pfx{bits: <<1, 2>>, maxlen: 32}, -2)
%Pfx{bits: <<0, 64>>, maxlen: 32}
Specs
Arithmetic shift right the pfx.bits
by n
positions.
A negative n
actually shifts to the left.
Note that the pfx.bits
stays stays the same.
Examples
iex> bsr("1.2.0.0/16", 2)
"0.64.0.0/16"
# no mask, so all 32 bits get shifted
iex> bsr({1, 2, 0, 0}, 2)
{0, 64, 128, 0}
iex> bsr({{1, 2, 0, 0}, 16}, 2)
{{0, 64, 0, 0}, 16}
iex> bsr(%Pfx{bits: <<1, 2>>, maxlen: 32}, 2)
%Pfx{bits: <<0, 64>>, maxlen: 32}
# now shift to the left
iex> bsr(%Pfx{bits: <<1, 2>>, maxlen: 32}, -2)
%Pfx{bits: <<4, 8>>, maxlen: 32}
Specs
A bitwise XOR of two prefix/0
's.
Both prefixes must have the same maxlen
.
Examples
iex> bxor("10.11.12.13", "255.255.0.0")
"245.244.12.13"
iex> bxor({10, 11, 12, 13}, {255, 255, 0, 0})
{245, 244, 12, 13}
# mix 'n match
iex> bxor({{10, 11, 12, 13}, 32}, "255.255.0.0")
{{245, 244, 12, 13}, 32}
iex> x = new(<<10, 11, 12, 13>>, 32)
iex> y = new(<<255, 255>>, 32)
iex> bxor(x, y)
%Pfx{bits: <<245, 244, 12, 13>>, maxlen: 32}
Specs
cast(prefix()) :: non_neg_integer()
Cast a prefix/0
to an integer.
After right padding the given pfx
, the pfx.bits
are interpreted as a number
of maxlen
bits wide. Empty prefixes evaluate to 0
, since all 'missing'
bits are taken to be zero (even if maxlen
is 0
).
See cut/3
for how this capability might be useful.
Examples
iex> cast("255.255.0.0")
4294901760
iex> cast("255.255.0.0/16")
4294901760
iex> cast({255, 255, 0, 0})
4294901760
iex> cast({{255, 255, 0, 0}, 32})
4294901760
iex> cast(%Pfx{bits: <<255, 255>>, maxlen: 32})
4294901760
iex> %Pfx{bits: <<4294901760::32>>, maxlen: 32}
%Pfx{bits: <<255, 255, 0, 0>>, maxlen: 32}
# missing bits filled in as `0`s
iex> cast(%Pfx{bits: <<255>>, maxlen: 16})
65280
iex> cast(%Pfx{bits: <<-1::128>>, maxlen: 128})
340282366920938463463374607431768211455
iex> cast(%Pfx{bits: <<>>, maxlen: 8})
0
# a bit weird, but:
iex> cast(%Pfx{bits: <<>>, maxlen: 0})
0
Specs
Compare function for sorting.
:eq
prefix1 is equal to prefix2:lt
prefix1 has more bits or lies to the left of prefix2:gt
prefix1 has less bits or lies to the right of prefix2
The prefixes must have the same maxlen and are first compared by size (i.e. a shorter prefix is considered larger), and second on their bitstring value.
Examples
iex> compare("10.0.0.0/8", "11.0.0.0/8")
:lt
iex> compare("10.0.0.0/8", {{11, 0, 0, 0}, 8})
:lt
iex> compare({10, 0, 0, 0}, {{11, 0, 0, 0}, 16})
:lt
iex> compare(new(<<10>>, 32), new(<<11>>, 32))
:lt
# sort on prefixes, first on bit_size than bits-values
iex> list = ["10.11.0.0/16", "10.10.10.0/24", "10.10.0.0/16"]
iex> Enum.sort(list, Pfx)
[
"10.10.10.0/24",
"10.10.0.0/16",
"10.11.0.0/16"
]
#
# whereas regular sort does:
#
iex> Enum.sort(list)
[
"10.10.0.0/16",
"10.10.10.0/24",
"10.11.0.0/16"
]
iex> list = [new(<<10, 11>>, 32), new(<<10,10,10>>, 32), new(<<10,10>>, 32)]
iex> Enum.sort(list, Pfx)
[
%Pfx{bits: <<10, 10, 10>>, maxlen: 32},
%Pfx{bits: <<10, 10>>, maxlen: 32},
%Pfx{bits: <<10, 11>>, maxlen: 32}
]
# not advisable, but mixed representations are possible as well
iex> l = ["10.11.0.0/16", {{10, 10, 10, 0}, 24}, %Pfx{bits: <<10, 10>>, maxlen: 32}]
iex> Enum.sort(l, Pfx)
[
{{10, 10, 10, 0}, 24},
%Pfx{bits: <<10, 10>>, maxlen: 32},
"10.11.0.0/16",
]
# note: all prefixes must have the same `maxlen`
iex> compare(new(<<10>>, 32), new(<<10>>, 128))
** (ArgumentError) prefixes have different maxlen's: {%Pfx{bits: "\n", maxlen: 32}, %Pfx{bits: "\n", maxlen: 128}}
Specs
Contrast two Pfx
prefixes
Contrasting two prefixes will yield one of:
:equal
pfx1 is equal to pfx2:more
pfx1 is a more specific version of pfx2:less
pfx1 is a less specific version of pfx2:left
pfx1 is left-adjacent to pfx2:right
pfx1 is right-adjacent to pfx2:disjoint
pfx1 has no match with pfx2 whatsoever.
Examples
iex> contrast("10.10.0.0/16", "10.10.0.0/16")
:equal
iex> contrast("10.10.10.0/24", "10.10.0.0/16")
:more
iex> contrast("10.0.0.0/8", "10.255.255.0/24")
:less
iex> contrast("1.2.3.0/24", "1.2.4.0/24")
:left
iex> contrast("1.2.3.4/30", "1.2.3.0/30")
:right
iex> contrast("10.10.0.0/16", "9.0.0.0/8")
:disjoint
iex> contrast("10.10.0.0/16", %Pfx{bits: <<10,12>>, maxlen: 32})
:disjoint
Specs
Cut out a series of bits and turn it into its own Pfx
.
Extracts the bits and returns a new Pfx.t/0
with bits
set to the
bits extracted and maxlen
set to the length of the bits
-string.
Examples
iex> cut("::ffff:192.0.2.128", -1, -32)
"192.0.2.128"
iex> teredo = new("2001:0:4136:e378:8000:63bf:3fff:fdd2")
iex>
iex> # client
iex> cut(teredo, 96, 32) |> bnot() |> format()
"192.0.2.45"
iex>
iex>
iex> # udp port
iex> cut(teredo, 80, 16) |> bnot() |> cast()
40000
iex>
iex> # teredo server
iex> cut(teredo, 32, 32) |> format()
"65.54.227.120"
iex>
iex> # flags
iex> cut(teredo, 64, 16) |> digits(1) |> elem(0)
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
'Masked' bits are considered to be zero.
# extract 2nd and 3rd byte:
iex> %Pfx{bits: <<255, 255>>, maxlen: 32} |> cut(8, 16)
%Pfx{bits: <<255, 0>>, maxlen: 16}
Less useful, but cut will mirror the representation given:
iex> cut("10.11.12.13", 8, 16)
"11.12"
iex> cut({1, 2, 3, 4}, 16, 16)
{3, 4}
iex> cut({{1, 2, 0, 0}, 16}, 8, 16)
{{2, 0}, 16}
Extraction must stay within maxlen
of given pfx
.
# cannot exceed boundaries though:
iex> %Pfx{bits: <<255, 255>>, maxlen: 32} |> cut(8, 32)
** (ArgumentError) invalid index range: {8, 32}
Specs
digits(prefix(), pos_integer()) :: {tuple(), pos_integer()}
Transform a Pfx
prefix into {{digit, ..}, length}
format.
The pfx
is padded to its maximum length using 0
's and the resulting
bits are grouped into digits, each width
-bits wide. The resulting length
denotes the prefix' original bit_size.
Note: works best if the prefix' maxlen
is a multiple of the width
used,
otherwise maxlen
cannot be inferred from this format by tuple_size(digits) * width
(e.g. by Pfx.undigits
)
Examples
iex> digits("10.11.12.0/24", 8)
{{10, 11, 12, 0}, 24}
# mask is applied first
iex> digits("10.11.12.13/24", 8)
{{10, 11, 12, 0}, 24}
iex> digits("acdc:1976::/32", 16)
{{44252, 6518, 0, 0, 0, 0, 0, 0}, 32}
iex> digits({{0xacdc, 0x1976, 0, 0, 0, 0, 0, 0}, 32}, 16)
{{44252, 6518, 0, 0, 0, 0, 0, 0}, 32}
iex> digits(%Pfx{bits: <<10, 11, 12>>, maxlen: 32}, 8)
{{10, 11, 12, 0}, 24}
iex> digits(%Pfx{bits: <<10, 11, 12, 1::1>>, maxlen: 32}, 8)
{{10, 11, 12, 128}, 25}
iex> digits(%Pfx{bits: <<0x12, 0x34, 0x56, 0x78>>, maxlen: 128}, 4)
{{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 32}
Specs
drop(prefix(), non_neg_integer()) :: prefix()
Drop count
lsb bits from given pfx
.
If count
exceeds the actual number of bits in pfx.bits
, simply drops all
bits.
Examples
iex> drop("1.2.3.0/31", 1)
"1.2.3.0/30"
iex> drop("1.2.3.2/31", 1)
"1.2.3.0/30"
iex> drop("1.2.3.128/25", 1)
"1.2.3.0/24"
iex> drop("1.2.3.0/24", 512)
"0.0.0.0/0"
iex> drop("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 64)
"2001:db8:85a3:0:0:0:0:0/64"
iex> drop({1, 2, 3, 4}, 8)
{1, 2, 3, 0}
iex> drop({{1, 2, 3, 4}, 32}, 16)
{{1, 2, 0, 0}, 16}
iex> drop(%Pfx{bits: <<1, 2, 3, 4>>, maxlen: 32}, 16)
%Pfx{bits: <<1, 2>>, maxlen: 32}
Specs
fields(prefix(), non_neg_integer()) :: [{non_neg_integer(), non_neg_integer()}]
Turn a prefix
into a list of {number, width}
-fields.
If bit_size(pfx.bits)
is not a multiple of width
, the last
{number, width}
-tuple, will have a smaller width.
Examples
iex> fields("10.11.12.13", 8)
[{10, 8}, {11, 8}, {12, 8}, {13, 8}]
iex> fields({10, 11, 12, 13}, 8)
[{10, 8}, {11, 8}, {12, 8}, {13, 8}]
iex> fields({{10, 11, 12, 0}, 24}, 8)
[{10, 8}, {11, 8}, {12, 8}]
iex> fields(%Pfx{bits: <<10, 11, 12, 13>>, maxlen: 32}, 8)
[{10, 8}, {11, 8}, {12, 8}, {13, 8}]
# pfx.bits is not a multiple of 8, hence the {0, 1} at the end
iex> fields("10.11.12.0/25", 8)
[{10, 8}, {11, 8}, {12, 8}, {0, 1}]
iex> new(<<0xacdc::16>>, 128) |> fields(4)
[{10, 4}, {12, 4}, {13, 4}, {12, 4}]
# only 1 field with less bits than given width of 64
iex> new(<<255, 255>>, 32) |> fields(64)
[{65535, 16}]
Specs
Returns the first full length prefix from the set represented by pfx
.
Examples
iex> first("10.10.10.1/24")
"10.10.10.0"
iex> first("acdc:1976::/32")
"acdc:1976:0:0:0:0:0:0"
# a full address is its own this-network
iex> first({10, 10, 10, 1})
{10, 10, 10, 1}
iex> first({{10, 10, 10, 1}, 24})
{{10, 10, 10, 0}, 32}
iex> first(%Pfx{bits: <<10, 10, 10>>, maxlen: 32})
%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32}
iex> first(%Pfx{bits: <<0xacdc::16, 0x1976::16>>, maxlen: 128})
%Pfx{bits: <<0xACDC::16, 0x1976::16, 0::96>>, maxlen: 128}
Specs
flip(t(), non_neg_integer()) :: t()
Flip a single bit at position
in given pfx.bits
A negative position
is relative to the end of the pfx.bits
bitstring.
It is an error to point to a bit outside the range of available bits.
Examples
# flip some bit
iex> flip("255.255.254.0", 23)
"255.255.255.0"
iex> flip("255.255.255.0", -9)
"255.255.254.0"
# flip the 7th bit
iex> flip("0088.8888.8888", 6)
"02-88-88-88-88-88"
iex> flip({1, 2, 3, 0}, 24)
{1, 2, 3, 128}
# flip last bit
iex> flip({{1, 2, 3, 128}, 25}, 24)
{{1, 2, 3, 0}, 25}
iex> flip({{1, 2, 3, 128}, 25}, -1)
{{1, 2, 3, 0}, 25}
iex> flip(%Pfx{bits: <<1, 2, 3, 1::1>>, maxlen: 32}, -1)
%Pfx{bits: <<1, 2, 3, 0::1>>, maxlen: 32}
Specs
Generic formatter to turn a Pfx
into a string, using several options:
:width
, field width (default 8):base
, howto turn a field into a string (default 10, use 16 for hex numbers):unit
, how many fields go into 1 section (default 1):ssep
, howto join the sections together (default "."):lsep
, howto join a mask if required (default "/"):mask
, whether to add a mask (default false):reverse
, whether to reverse fields before grouping/joining (default false):padding
, whether to pad out thepfx.bits
(default true)
The defaults are geared towards IPv4 prefixes, but the options should be able to accomodate other domains as well.
Notes:
- the prefix.bits-length is omitted if equal to the prefix.bits-size
- domain specific submodules probably implement their own formatter.
Examples
iex> format(%Pfx{bits: <<10, 11, 12>>, maxlen: 32})
"10.11.12.0/24"
iex> format({{10, 11, 12, 0}, 24})
"10.11.12.0/24"
iex> format({10, 11, 12, 0})
"10.11.12.0"
# non-sensical, but there you go
iex> format("10.11.12.0/24")
"10.11.12.0/24"
# bitstring, note that mask is applied when new creates the `pfx`
iex> format("1.2.3.4/24", width: 1, base: 2, unit: 8, mask: false)
"00000001.00000010.00000011.00000000"
# mask not appended as its redundant for a full-sized prefix
iex> format(%Pfx{bits: <<10, 11, 12, 13>>, maxlen: 32})
"10.11.12.13"
iex> pfx = new(<<0xacdc::16, 0x1976::16>>, 128)
iex> format(pfx, width: 16, base: 16, ssep: ":")
"acdc:1976:0:0:0:0:0:0/32"
#
# similar, but grouping 4 fields, each 4 bits wide, into a single section
#
iex> format(pfx, width: 4, base: 16, unit: 4, ssep: ":")
"acdc:1976:0000:0000:0000:0000:0000:0000/32"
#
# this time, omit the acutal pfx length
#
iex> format(pfx, width: 16, base: 16, ssep: ":", mask: false)
"acdc:1976:0:0:0:0:0:0"
#
# ptr for IPv6 using the nibble format:
# - dot-separated reversal of all hex digits in the expanded address
#
iex> pfx
...> |> format(width: 4, base: 16, mask: false, reverse: true)
...> |> String.downcase()
...> |> (fn x -> "#{x}.ip6.arpa." end).()
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.7.9.1.c.d.c.a.ip6.arpa."
# turn off padding to get reverse zone dns ptr record
iex> new(<<10, 11, 12>>, 32)
...> |> format(padding: false, reverse: true, mask: false)
...> |> (&"#{&1}.in-addr.arpa.").()
"12.11.10.in-addr.arpa."
Create a Pfx.t/0
out of a hexadecimal string.
This always returns a Pfx.t
struct. A list of punctuation characters can
be supplied as a second argument and defaults to [?:, ?-, ?.]
. Note that
'/' should not be used as that separates the mask from the rest of the
binary. Punctuation characeters are simply ignored and there are no
positional checks performed.
Contrary to Pfx.from_mac/1
, this function turns a random hexadecimal into a
prefix. Do not use this to create a prefix out of an IPv6 address.
Examples
iex> from_hex("1-2:3.4:a-bcdef")
%Pfx{bits: <<0x12, 0x34, 0xAB, 0xCD, 0xEF>>, maxlen: 40}
iex> from_hex("1-2:3.4:a-bcdef/16")
%Pfx{bits: <<0x12, 0x34>>, maxlen: 40}
iex> from_hex("1|2|3|A|B|C|DEF", [?|])
%Pfx{bits: <<0x12, 0x3A, 0xBC, 0xDE, 0xF::4>>, maxlen: 36}
iex> from_hex("ABC")
%Pfx{bits: <<0xAB, 0xC::4>>, maxlen: 12}
# not for IPv6 addresses ..
iex> from_hex("2001::1")
%Pfx{bits: <<0x20, 0x01, 0x1::4>>, maxlen: 20}
Specs
Create a Pfx
struct from a EUI48/64 strings or tuples.
Parsing strings is somewhat relaxed since punctuation characters are interchangeable as long as their positions are correct.
Note that new/1
tries to parse binaries as IP prefixes first and would turn
an EUI-64 using ":" for punctuation into an IPv6 address. Similarly, a
8-element tuple is seen as IPv6 address. Hence, if you really need to parse
EUI-64 binaries with ":", or have EUI-48/64 tuples, use this function.
from_mac/1
also accepts a Pfx
struct, but only if its maxlen is either 48
or 64
. If not, an ArgumentError
is raised.
Examples
iex> from_mac("11:22:33:44:55:66")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66>>, maxlen: 48}
iex> from_mac("11-22-33-44-55-66")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66>>, maxlen: 48}
iex> from_mac("1122.3344.5566")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66>>, maxlen: 48}
iex> from_mac({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff})
%Pfx{bits: <<0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff>>, maxlen: 48}
# keep the OUI
iex> from_mac({{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, 24})
%Pfx{bits: <<0xaa, 0xbb, 0xcc>>, maxlen: 48}
iex> from_mac("11:22:33:44:55:66/24")
%Pfx{bits: <<0x11, 0x22, 0x33>>, maxlen: 48}
# a EUI-64
iex> from_mac("11-22-33-44-55-66-77-88")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
iex> from_mac("11:22:33:44:55:66:77:88")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
iex> from_mac("11:22:33:44:55:66:77:88/24")
%Pfx{bits: <<0x11, 0x22, 0x33>>, maxlen: 64}
iex> from_mac({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
iex> from_mac({{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, 24})
%Pfx{bits: <<0x11, 0x22, 0x33>>, maxlen: 64}
# Note: from_mac reads nibbles so each address element must be 2 nibbles (!)
iex> from_mac("01:02:03:04:05:06:07:08")
%Pfx{bits: <<0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8>>, maxlen: 64}
# mix and match
# ":" and "-" are interchangeable
iex> from_mac("11:22-33:44-55:66")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66>>, maxlen: 48}
Specs
Return the nth
host in given pfx
.
Note that offset nth
wraps around. See Pfx.member/2
.
Examples
iex> host("10.10.10.0/24", 128)
"10.10.10.128"
iex> host({{10, 10, 10, 0}, 24}, 128)
{{10, 10, 10, 128}, 32}
iex> host(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, 128)
%Pfx{bits: <<10, 10, 10, 128>>, maxlen: 32}
# wraps around
iex> host("10.10.10.0/24", 256)
"10.10.10.0"
Specs
Returns a list of address prefixes for given pfx
.
Examples
iex> hosts("10.10.10.0/30")
[
"10.10.10.0",
"10.10.10.1",
"10.10.10.2",
"10.10.10.3"
]
iex> hosts({{10, 10, 10, 0}, 30})
[
{{10, 10, 10, 0}, 32},
{{10, 10, 10, 1}, 32},
{{10, 10, 10, 2}, 32},
{{10, 10, 10, 3}, 32}
]
iex> hosts(%Pfx{bits: <<10, 10, 10, 0::6>>, maxlen: 32})
[
%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32},
%Pfx{bits: <<10, 10, 10, 1>>, maxlen: 32},
%Pfx{bits: <<10, 10, 10, 2>>, maxlen: 32},
%Pfx{bits: <<10, 10, 10, 3>>, maxlen: 32}
]
Specs
Insert some bits
into pfx
-s bitstring.
The resulting bitstring is silently clipped to the pfx.maxlen
.
Valid bit positions are bits_size(pfx.bits) .. min(pfx.maxlen-1, bit_size(pfx.bits))
. A position of 0
will prepend the bits
, while a
position of bit_size(pfx.bits)
will append the bits
as long as the prefix
is not a full length prefix already.
A negative position is taken relative to the end. Note that this cannot
be used for appending bits, since -1
refers to the last actual bit and
there is no such thing as -0
..
Examples
# prepend bits
iex> insert("0.0.0.0/0", <<255>>, 0)
"255.0.0.0/8"
# append bits
iex> insert("255.255.0.0/16", <<255>>, 16)
"255.255.255.0/24"
# cannot append to a full prefix, positions go from 0..31
iex> insert("1.2.3.4", <<255>>, 32)
** (ArgumentError) invalid bit position: 32
# but inserting inside the bitstring, is ok
iex> insert("1.2.3.4", <<255>>, 16)
"1.2.255.3"
# turn EUI48 into a modified EUI64
iex> new("0088.8888.8888")
...> |> new(64)
...> |> flip(6)
...> |> insert(<<0xFF, 0xFE>>, 24)
%Pfx{bits: <<0x02, 0x88, 0x88, 0xFF, 0xFE, 0x88, 0x88, 0x88>>, maxlen: 64}
# sliently clips to pfx's maxlen
iex> insert("1.2.3.0/24", <<255, 255, 255, 255>>, 0)
"255.255.255.255"
iex> insert("1.2.3.0/24", <<255, 255, 255, 255>>, 24)
"1.2.3.255"
Specs
Returns the inverted mask for given pfx
.
The result is always a full length prefix.
Examples
iex> inv_mask("10.10.10.0/25")
"0.0.0.127"
iex> inv_mask({10, 10, 10, 0})
{0, 0, 0, 0}
iex> inv_mask({{10, 10, 10, 0}, 25})
{{0, 0, 0, 127}, 32}
iex> inv_mask(%Pfx{bits: <<10, 10, 10, 0::1>>, maxlen: 32})
%Pfx{bits: <<0, 0, 0, 127>>, maxlen: 32}
Specs
keep(prefix(), non_neg_integer()) :: prefix()
Keep count
msb bits of given pfx
.
If count
exceeds the actual number of bits in pfx.bits
, simply keeps all
bits.
Examples
iex> keep("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 64)
"2001:db8:85a3:0:0:0:0:0/64"
iex> keep("1.2.3.0/31", 30)
"1.2.3.0/30"
iex> keep("1.2.3.2/31", 30)
"1.2.3.0/30"
iex> keep("1.2.3.128/25", 24)
"1.2.3.0/24"
iex> keep("1.2.3.0/24", 512)
"1.2.3.0/24"
iex> keep({1, 2, 3, 4}, 24)
{1, 2, 3, 0}
iex> keep({{1, 2, 3, 4}, 32}, 16)
{{1, 2, 0, 0}, 16}
iex> keep(%Pfx{bits: <<1, 2, 3, 4>>, maxlen: 32}, 16)
%Pfx{bits: <<1, 2>>, maxlen: 32}
Specs
Returns the last full length prefix from the set represented by pfx
.
Examples
iex> last("10.10.0.0/16")
"10.10.255.255"
# a full address is its own last address
iex> last({10, 10, 10, 1})
{10, 10, 10, 1}
iex> last({{10, 10, 10, 1}, 30})
{{10, 10, 10, 3}, 32}
iex> last(%Pfx{bits: <<10, 10, 10>>, maxlen: 32})
%Pfx{bits: <<10, 10, 10, 255>>, maxlen: 32}
iex> last(%Pfx{bits: <<0xacdc::16, 0x1976::16>>, maxlen: 128})
%Pfx{bits: <<0xACDC::16, 0x1976::16, -1::96>>, maxlen: 128}
iex> last("acdc:1976::/112")
"acdc:1976:0:0:0:0:0:ffff"
Specs
Given a t.Pfx.t/0
prefix, try to represent it in its original form.
The exact original is not required, the pfx
is transformed by the shape of
the original
argument: string vs two-element tuple vs tuple. If none of
the three shapes match, the pfx
is returned unchanged.
This is used to allow results to be the same shape as their (first) argument
that needed to turn into a Pfx.t/0
for some calculation.
Note that when turning a prefix into a address-tuple, the first full length member comes out as an address.
Examples
# original is a string
iex> marshall(%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, "any string really")
"1.1.1.0/24"
# original is any two-element tuple
iex> marshall(%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, {0,0})
{{1, 1, 1, 0}, 24}
# original is any other tuple, actually turns prefix into this-network address
iex> marshall(%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, {})
{1, 1, 1, 0}
# original is a Pfx struct
iex> marshall(%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, %Pfx{bits: <<>>, maxlen: 0})
%Pfx{bits: <<1, 1, 1>>, maxlen: 32}
Specs
Return the mask for given pfx
.
The result is always a full length prefix.
Examples
iex> mask("10.10.10.0/25")
"255.255.255.128"
iex> mask({10, 10, 10, 0})
{255, 255, 255, 255}
iex> mask({{10, 10, 10, 0}, 25})
{{255, 255, 255, 128}, 32}
iex> mask("acdc:1976::/32")
"ffff:ffff:0:0:0:0:0:0"
# and now for something completely different:
iex> mask(%Pfx{bits: <<10, 10, 0::1>>, maxlen: 20})
%Pfx{bits: <<255, 255, 8::4>>, maxlen: 20}
Specs
Return the nth
-member of a given pfx
.
A prefix represents a range of (possibly longer) prefixes which can be
seen as members of the prefix. So a prefix of n
-bits long represents:
- 1 prefix of
n
-bits long (i.e. itself), - 2 prefixes of
n+1
-bits long, - 4 prefixes of
n+2
-bits long - ..
- 2^w prefixes of
n+w
-bits long
where n+w
<= pfx.maxlen
.
Not specifying a width
assumes the maximum width available. If a width
is specified, the nth
-offset is added to the prefix as a number
width
-bits wide. This wraps around the available address space.
Examples
iex> member("10.10.10.0/24", 255)
"10.10.10.255"
# wraps around
iex> member("10.10.10.0/24", 256)
"10.10.10.0"
iex> member({{10, 10, 10, 0}, 24}, 255)
{{10, 10, 10, 255}, 32}
iex> member(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, 0)
%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32}
iex> member(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, 255)
%Pfx{bits: <<10, 10, 10, 255>>, maxlen: 32}
# wraps around
iex> member(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, 256)
%Pfx{bits: <<10, 10, 10, 0>>, maxlen: 32}
iex> member(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, -1)
%Pfx{bits: <<10, 10, 10, 255>>, maxlen: 32}
# a full prefix always returns itself
iex> member(%Pfx{bits: <<10, 10, 10, 10>>, maxlen: 32}, 0)
%Pfx{bits: <<10, 10, 10, 10>>, maxlen: 32}
iex> member(%Pfx{bits: <<10, 10, 10, 10>>, maxlen: 32}, 3)
%Pfx{bits: <<10, 10, 10, 10>>, maxlen: 32}
Specs
member(prefix(), integer(), pos_integer()) :: t()
Return the nth
subprefix for a given pfx
, using width
bits.
Examples
iex> member("10.10.10.0/24", 1, 2)
"10.10.10.64/26"
iex> member("10.10.10.0/24", 2, 2)
"10.10.10.128/26"
iex> member({{10, 10, 10, 0}, 24}, 2, 2)
{{10, 10, 10, 128}, 26}
# the first sub-prefix that is 2 bits longer
iex> member(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, 0, 2)
%Pfx{bits: <<10, 10, 10, 0::2>>, maxlen: 32}
# the second sub-prefix that is 2 bits longer
iex> member(%Pfx{bits: <<10, 10, 10>>, maxlen: 32}, 1, 2)
%Pfx{bits: <<10, 10, 10, 1::2>>, maxlen: 32}
Specs
Returns true is prefix pfx1
is a member of prefix pfx2
If either prfx1
or pfx2
is invalid, member? simply returns false
Examples
iex> member?("10.10.10.10", "10.0.0.0/8")
true
iex> member?({10, 10, 10, 10}, "10.0.0.0/8")
true
iex> member?({{10, 10, 10, 10}, 24}, "10.0.0.0/8")
true
iex> member?({{11, 0, 0, 0}, 8}, {{10, 0, 0, 0}, 8})
false
iex> member?(%Pfx{bits: <<10, 10, 10, 10>>, maxlen: 32}, %Pfx{bits: <<10>>, maxlen: 32})
true
# bad prefix
iex> member?("10.10.10.10", "10.10.10.256/24")
false
Specs
Returns the neighboring prefix such that both can be combined in a supernet.
Examples
iex> neighbor("1.1.1.128/25")
"1.1.1.0/25"
iex> neighbor("1.1.1.0/25")
"1.1.1.128/25"
iex> neighbor({1, 1, 1, 1})
{1, 1, 1, 0}
iex> neighbor({{1, 1, 1, 128}, 25})
{{1, 1, 1, 0}, 25}
iex> neighbor(%Pfx{bits: <<1, 1, 1, 1::1>>, maxlen: 32})
%Pfx{bits: <<1, 1, 1, 0::1>>, maxlen: 32}
Specs
new(ip_address() | ip_prefix() | String.t()) :: t()
Creates a new prefix from address tuples or binaries.
Use:
- a binary in CIDR-notation,
- a binary in EUI-48 or EUI-64 format (EUI-64 must be using hyphens !)
- an {
ip_address/0
,length
}-tuple to truncate the bits tolength
. - an ipv4 or ipv6
ip_address/0
tuple directly for a full address, or - a
Pfx.t/0
struct
Binaries are processed by :inet.parse_address/1
, so be aware of IPv4 shorthand
notations that may yield surprising results, since digits are taken to be:
d1.d2.d3.d4
->d1.d2.d3.d4
(full address)d1.d2.d3
->d1.d2.0.d3
d1.d2
->d1.0.0.d2
d1
->0.0.0.d1
If :inet.parse_address/1
fails to create an IPv4 or IPv6 address, an
attempt is made to parse the binary as an EUI-48 or EUI-64 MAC address.
Parsing EUI's is somewhat relaxed, punctuation chars "-", ":", "." are
interchangeable, but their positions should be correct.
Note that EUI-64's that use ":"-punctuation are indistinguishable from IPv6,
e.g. "11:22:33:44:55:66:77:88". Use from_mac/1
when in doubt about
punctuations used while parsing MAC addresses.
Examples
# from CIDR strings
iex> new("10.10.0.0")
%Pfx{bits: <<10, 10, 0, 0>>, maxlen: 32}
iex> new("10.10.10.10/16")
%Pfx{bits: <<10, 10>>, maxlen: 32}
iex> new("acdc:1976::/32")
%Pfx{bits: <<0xacdc::16, 0x1976::16>>, maxlen: 128}
# from an {address-tuple, length}
iex> new({{0xacdc, 0x1976, 0, 0, 0, 0, 0, 0}, 32})
%Pfx{bits: <<0xacdc::16, 0x1976::16>>, maxlen: 128}
iex> new({{10, 10, 0, 0}, 16})
%Pfx{bits: <<10, 10>>, maxlen: 32}
# from an address-tuple
iex> new({10, 10, 0, 0})
%Pfx{bits: <<10, 10, 0, 0>>, maxlen: 32}
# from a struct
iex> new(%Pfx{bits: <<10, 10>>, maxlen: 32})
%Pfx{bits: <<10, 10>>, maxlen: 32}
# 10.10/16 is interpreted as 10.0.0.10/16 (!)
iex> new("10.10/16")
%Pfx{bits: <<10, 0>>, maxlen: 32}
# some EUI-48's
iex> new("aa:bb:cc:dd:ee:ff")
%Pfx{bits: <<0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff>>, maxlen: 48}
iex> new("aa-bb-cc-dd-ee-ff")
%Pfx{bits: <<0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff>>, maxlen: 48}
iex> new("aabb.ccdd.eeff")
%Pfx{bits: <<0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff>>, maxlen: 48}
# keep only OUI
iex> new("aa-bb-cc-dd-ee-ff/24")
%Pfx{bits: <<0xaa, 0xbb, 0xcc>>, maxlen: 48}
# some EUI-64's
iex> new("11-22-33-44-55-66-77-88")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
iex> new("1122.3344.5566.7788")
%Pfx{bits: <<0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88>>, maxlen: 64}
# but note the maxlen here ...
iex> new("11:22:33:44:55:66:77:88")
%Pfx{bits: <<0x11::16, 0x22::16, 0x33::16, 0x44::16, 0x55::16, 0x66::16, 0x77::16, 0x88::16>>, maxlen: 128}
Specs
new(t() | bitstring(), non_neg_integer()) :: t()
Creates a new Pfx.t/0
-prefix.
Create a new prefix from:
- from a bitstring and a maximum length, truncating the bits as needed,
- from a
Pfx.t/0
prefix and a new maxlen, again truncating as needed,
Examples
iex> new(<<10, 10>>, 32)
%Pfx{bits: <<10, 10>>, maxlen: 32}
iex> new(<<10, 10>>, 8)
%Pfx{bits: <<10>>, maxlen: 8}
# changing 'maxlen' usually changes the prefix' meaning
iex> new(%Pfx{bits: <<10, 10>>, maxlen: 32}, 128)
%Pfx{bits: <<10, 10>>, maxlen: 128}
Specs
Left pad the pfx.bits
to its full length using 0
-bits.
Examples
iex> padl("1.2.0.0/16")
"0.0.1.2"
iex> padl({{1, 2, 0, 0}, 16})
{{0, 0, 1, 2}, 32}
iex> padl(%Pfx{bits: <<1, 2>>, maxlen: 32})
%Pfx{bits: <<0, 0, 1, 2>>, maxlen: 32}
Specs
Left pad the pfx.bits
to its full length using either 0
or 1
-bits.
Examples
iex> padl("1.2.0.0/16", 1)
"255.255.1.2"
iex> padl({{1, 2, 0, 0}, 16}, 1)
{{255, 255, 1, 2}, 32}
iex> padl(%Pfx{bits: <<1, 2>>, maxlen: 32}, 1)
%Pfx{bits: <<255, 255, 1, 2>>, maxlen: 32}
Specs
padl(prefix(), 0 | 1, non_neg_integer()) :: prefix()
Left pad the pfx.bits
with n
bits of either 0
or 1
's.
Examples
iex> padl("255.255.0.0/16", 0, 16)
"0.0.255.255"
iex> padl("255.255.0.0/16", 1, 16)
"255.255.255.255"
iex> padl({{255, 255, 0, 0}, 16}, 0, 16)
{{0, 0, 255, 255}, 32}
iex> padl(%Pfx{bits: <<255, 255>>, maxlen: 32}, 0, 16)
%Pfx{bits: <<0, 0, 255, 255>>, maxlen: 32}
Specs
Right pad the pfx.bits
to its full length using 0
-bits.
The result is always a full prefix with maxlen
bits.
Examples
# already a full address
iex> padr("1.2.3.4")
"1.2.3.4"
# mask applied first, then padded with zero's
iex> padr("1.2.3.4/16")
"1.2.0.0"
# mask applied first, than padded with zero's
iex> padr({{1, 2, 0, 0}, 16})
{{1, 2, 0, 0}, 32}
iex> padr(%Pfx{bits: <<1, 2>>, maxlen: 32})
%Pfx{bits: <<1, 2, 0, 0>>, maxlen: 32}
Specs
Right pad the pfx.bits
to its full length using either 0
or 1
-bits.
Examples
iex> padr("1.2.0.0/16", 1)
"1.2.255.255"
iex> padr({{1, 2, 0, 0}, 16}, 1)
{{1, 2, 255, 255}, 32}
# nothing to padr, already a full prefix
iex> padr("1.2.0.0", 1)
"1.2.0.0"
iex> padr(%Pfx{bits: <<1, 2>>, maxlen: 32}, 1)
%Pfx{bits: <<1, 2, 255, 255>>, maxlen: 32}
Specs
padr(prefix(), 0 | 1, non_neg_integer()) :: prefix()
Right pad the pfx.bits
with n
bits of either 0
or 1
's.
The result is clipped at maxlen
bits without warning.
Examples
# expand a /16 to a /24
iex> padr("255.255.0.0/16", 0, 8)
"255.255.0.0/24"
iex> padr("255.255.0.0/16", 1, 8)
"255.255.255.0/24"
iex> padr({{255, 255, 0, 0}, 16}, 1, 8)
{{255, 255, 255, 0}, 24}
# results are clipped to maxlen
iex> padr("1.2.0.0/16", 1, 512)
"1.2.255.255"
iex> padr(%Pfx{bits: <<255, 255>>, maxlen: 32}, 0, 8)
%Pfx{bits: <<255, 255, 0>>, maxlen: 32}
iex> padr(%Pfx{bits: <<255, 255>>, maxlen: 32}, 1, 8)
%Pfx{bits: <<255, 255, 255>>, maxlen: 32}
Specs
partition(prefix(), non_neg_integer()) :: [prefix()]
Partition a Pfx
prefix into a list of new prefixes, each bitlen
long.
Note that bitlen
must be in the range of bit_size(pfx.bits)..pfx.maxlen-1
.
Examples
# break out the /26's in a /24
iex> partition("10.11.12.0/24", 26)
[
"10.11.12.0/26",
"10.11.12.64/26",
"10.11.12.128/26",
"10.11.12.192/26"
]
iex> partition({{10, 11, 12, 0}, 24}, 26)
[
{{10, 11, 12, 0}, 26},
{{10, 11, 12, 64}, 26},
{{10, 11, 12, 128}, 26},
{{10, 11, 12, 192}, 26},
]
iex> partition(%Pfx{bits: <<10, 11, 12>>, maxlen: 32}, 26)
[
%Pfx{bits: <<10, 11, 12, 0::size(2)>>, maxlen: 32},
%Pfx{bits: <<10, 11, 12, 1::size(2)>>, maxlen: 32},
%Pfx{bits: <<10, 11, 12, 2::size(2)>>, maxlen: 32},
%Pfx{bits: <<10, 11, 12, 3::size(2)>>, maxlen: 32}
]
Specs
remove(t(), non_neg_integer(), non_neg_integer()) :: t()
Remove length
bits from given pfx
-s bitstring, starting at position
.
A negative position
is relative to the end of the pfx.bits
-string.
Valid range for position
is -bit_size(pfx.bits) .. bit_size(pfx.bits)-1
.
If length
is positive, bits are removed to the right. If it is negative
bits are removed going to the left.
Note:
length
is silently clipped to the maximum number of bits available to remove- removing bits from
pfx.bits
does not change itspfx.maxlen
Examples
iex> remove("1.2.3.4", 8, 8)
"1.3.4.0/24"
iex> remove("1.2.3.128/25", -1, 1)
"1.2.3.0/24"
iex> remove("0288.88FF.FE88.8888", 24, 16)
"02-88-88-88-88-88-00-00/48"
Specs
Returns another Pfx
at distance offset
.
This basically increases or decreases the number represented by the pfx.bits
while keeping pfx.maxlen
the same.
Note that the length of pfx.bits
will not change and cycling through
all siblings will eventually wrap around.
Examples
iex> sibling("1.2.3.0/24", -1)
"1.2.2.0/24"
iex> sibling("0.0.0.0", -1)
"255.255.255.255"
iex> sibling({{1, 2, 3, 0}, 24}, 256)
{{1, 3, 3, 0}, 24}
iex> sibling(%Pfx{bits: <<10, 11>>, maxlen: 32}, 1)
%Pfx{bits: <<10, 12>>, maxlen: 32}
iex> sibling(%Pfx{bits: <<10, 11, 0>>, maxlen: 32}, 255)
%Pfx{bits: <<10, 11, 255>>, maxlen: 32}
# wraps around
iex> sibling(%Pfx{bits: <<10, 11, 0>>, maxlen: 32}, 256)
%Pfx{bits: <<10, 12, 0>>, maxlen: 32}
iex> new(<<0, 0, 0, 0>>, 32) |> sibling(-1)
%Pfx{bits: <<255, 255, 255, 255>>, maxlen: 32}
# zero bit-length stays zero bit-length
iex> sibling(%Pfx{bits: <<>>, maxlen: 0}, 1)
%Pfx{bits: <<>>, maxlen: 0}
Specs
size(prefix()) :: pos_integer()
Returns the number of full addresses represented by given pfx
.
size(pfx) == 2^(pfx.maxlen - bit_size(pfx.bits))
Examples
iex> size("1.1.1.0/23")
512
iex> size({1,1,1,1})
1
iex> size({{1, 1, 1, 0}, 16})
65536
iex> size(%Pfx{bits: <<1, 1, 1>>, maxlen: 32})
256
Specs
undigits({tuple(), pos_integer()}, pos_integer()) :: t()
Return the Pfx
prefix represented by the digits
, actual length
and a given
field width
.
The pfx.bits
are formed by first concatenating the digits
expressed as
bitstrings of width
-bits wide and then truncating to the length
-msb bits.
The pfx.maxlen
is inferred as tuple_size(digits) * width
.
Note: if a digit does not fit in width
-bits, only the width
-least
significant bits are preserved, which may yield surprising results.
Examples
# truncated to the first 24 bits and maxlen is 32 (4*8)
iex> undigits({{10, 11, 12, 0}, 24}, 8)
%Pfx{bits: <<10, 11, 12>>, maxlen: 32}
iex> undigits({{-1, -1, 0, 0}, 32}, 8) |> format()
"255.255.0.0"
# bits are truncated to empty bitstring (`length` is 0)
iex> undigits({{1,2,3,4}, 0}, 8)
%Pfx{bits: <<>>, maxlen: 32}
# 32 4-bit wide numbers turn into an IPv6 prefix, truncated to 32 bits
# and maxlen is set to 32 * 4 = 128
iex> undigits({{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 32},4)
%Pfx{bits: <<0x12, 0x34, 0x56, 0x78>>, maxlen: 128}
Specs
Returns boolean indicating whether pfx
is a valid prefix/0
or not.
Examples
iex> valid?("1.2.3.4")
true
iex> valid?("1.2.3.4/8")
true
iex> valid?({1, 2, 3, 4})
true
iex> valid?({{1, 2, 3, 4}, 24})
true
iex> valid?(%Pfx{bits: <<1,2,3,4>>, maxlen: 32})
true
# bits exceed maxlen
iex> valid?(%Pfx{bits: <<1,2,3,4>>, maxlen: 16})
false