Iptrie (Iptrie v0.2.0) View Source
IP lookup, with longest prefix match, for IPv4, IPv6 prefixes (and others).
Iptrie manages multiple Radix
trees, one for each type of
Pfx.t/0
prefix used as determined by their maxlen
property. That way,
IPv4 prefixes (maxlen: 32
) use a different radix tree as opposed to e.g. IPv6
(maxlen: 128
).
Iptrie has a bias towards IPv4 and IPv6 since it uses Pfx
to convert
arguments to a Pfx.t/0
struct. So, doing other types of prefixes will
require the actual Pfx.t/0
structs as arguments for the various Iptrie
functions.
Like Pfx
, Iptrie tries to mirror the representation of results to the
argument(s) given, if possible.
Example
iex> ipt = new()
...> |> put("1.2.3.0/24", "v4")
...> |> put("acdc:1975::/32", "T.N.T")
...> |> put("0.0.0.0/0", "v4 default")
...> |> put("::/0", "no dynamite")
...> |> put(%Pfx{bits: <<0xaa, 0xbb, 0xcc, 0xdd>>, maxlen: 48}, "some OUI")
iex>
iex> lookup(ipt, "1.2.3.128")
{"1.2.3.0/24", "v4"}
iex>
iex> # mirror digits representation
iex>
iex> lookup(ipt, {1, 2, 3, 128})
iex> {{{1, 2, 3, 0}, 24}, "v4"}
iex>
iex> lookup(ipt, "10.11.12.13")
{"0.0.0.0/0", "v4 default"}
iex>
iex> lookup(ipt, "acdc:1975::")
{"acdc:1975:0:0:0:0:0:0/32", "T.N.T"}
iex>
iex> lookup(ipt, "abba::")
{"0:0:0:0:0:0:0:0/0", "no dynamite"}
iex>
iex> # use %Pfx{}, since Iptrie only converts IPv4 / IPv6 representations
iex>
iex> lookup(ipt, %Pfx{bits: <<0xaa, 0xbb, 0xcc, 0xdd, 0xee>>, maxlen: 48})
{%Pfx{bits: <<0xAA, 0xBB, 0xCC, 0xDD>>, maxlen: 48}, "some OUI"}
iex>
iex> # uses three separate radix trees:
iex>
iex> Map.get(ipt, 32)
{0, {7, [{"", "v4 default"}], [{<<1, 2, 3>>, "v4"}]}, nil}
iex>
iex> Map.get(ipt, 128)
{0, [{"", "no dynamite"}], [{<<172, 220, 25, 117>>, "T.N.T"}]}
iex>
iex> Map.get(ipt, 48)
{0, nil, [{<<170, 187, 204, 221>>, "some OUI"}]}
iex>
Link to this section Summary
Types
A prefix represented as an opague Pfx.t/0
struct, an
Pfx.ip_address/0
, Pfx.ip_prefix/0
or CIDR string.
Functions
Delete one or more prefix, value-pairs from the trie
using an exact match.
Return one or more prefix,value-pair(s) using an exact match for given prefix(es)
.
Return all prefixes stored in all available radix trees in trie
.
Return the prefixes stored in the radix tree(s) in trie
for given type
.
Return all the prefix,value-pairs whose prefix is a prefix for the given
search prefix
.
Return the prefix,value-pair, whose prefix is the longest match for given search prefix
.
Return all the prefix,value-pairs where the search prefix
is a prefix for
the stored prefix.
Create an new, empty Iptrie.
Populate the trie
with a list of {prefix,value}-pairs.
Puts value
under prefix
in the trie
.
Return the radix tree for given type
.
Invoke fun
on all prefix,value-pairs in all radix trees in the trie
.
Invoke fun
on each prefix,value-pair in the radix tree for given type
Return all prefix,value-pairs from all available radix trees in trie
.
Returns the prefix,value-pairs from the radix trees in trie
for given
type
(s).
Lookup prefix
and update the matched entry, only if found.
Lookup prefix
and, if found, update its value or insert the default.
Return all the values stored in all radix trees in trie
.
Return the values stored in the radix trees in trie
for given type
.
Link to this section Types
Specs
prefix() :: Pfx.prefix()
A prefix represented as an opague Pfx.t/0
struct, an
Pfx.ip_address/0
, Pfx.ip_prefix/0
or CIDR string.
See: Pfx
.
Specs
t() :: %Iptrie{}
An Iptrie struct that contains a Radix
tree per type of Pfx.t/0
used.
A prefix' type is determined by its maxlen
property: IPv4 has maxlen: 32
, IPv6 has maxlen: 128
, MAC addresses have maxlen: 48
and so on.
Although Iptrie facilitates lpm lookups of any type of prefix, it has a bias towards IP prefixes. So, any binaries (strings) are interpreted as CIDR-strings and tuples of address digits and/or {address-digits, length) are interpreted as IPv4 or IPv6 representations.
Link to this section Functions
Specs
Delete one or more prefix, value-pairs from the trie
using an exact match.
The list of prefixes to delete may contain all types, so all sorts of prefixes can be deleted from multiple radix trees in one go.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", "one")
...> |> put("2.2.2.0/24", "two")
iex>
iex> lookup(ipt, "1.1.1.1")
{"1.1.1.0/24", "one"}
iex>
iex> Map.get(ipt, 32) |> Radix.keys()
[<<1, 1, 1>>, <<2, 2, 2>>]
iex>
iex> ipt = delete(ipt, "1.1.1.0/24")
iex>
iex> lookup(ipt, "1.1.1.1")
nil
iex>
iex> Map.get(ipt, 32) |> Radix.keys()
[<<2, 2, 2>>]
iex> ipt = new()
...> |> put("1.1.1.0/24", "one")
...> |> put("2.2.2.0/24", "two")
...> |> put("abba:1973::/32", "Ring Ring")
...> |> put("acdc:1975::/32", "T.N.T")
iex>
iex> ipt = delete(ipt, ["1.1.1.0/24", "abba:1973::/32"])
iex>
iex> Map.get(ipt, 32) |> Radix.keys()
[<<2, 2, 2>>]
iex>
iex> Map.get(ipt, 128) |> Radix.keys()
[<<0xacdc::16, 0x1975::16>>]
Specs
Return one or more prefix,value-pair(s) using an exact match for given prefix(es)
.
Examples
iex> ipt = new([{"1.1.1.0/30", "A"}, {"1.1.1.0/31", "B"}, {"1.1.1.0", "C"}])
iex>
iex> get(ipt, "1.1.1.0/31")
{"1.1.1.0/31", "B"}
iex>
iex> # or get a list of entries
iex>
iex> get(ipt, ["1.1.1.0/30", "1.1.1.0"])
[{"1.1.1.0/30", "A"}, {"1.1.1.0", "C"}]
Specs
Return all prefixes stored in all available radix trees in trie
.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> keys(ipt)
...> |> Enum.map(fn x -> "#{x}" end)
[
"1.1.1.0/24",
"2.2.2.0/24",
"acdc:1975:0:0:0:0:0:0/32",
"acdc:2021:0:0:0:0:0:0/32"
]
Specs
Return the prefixes stored in the radix tree(s) in trie
for given type
.
Where type
is a single maxlen or a list thereof.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> keys(ipt, 32)
[
%Pfx{bits: <<1, 1, 1>>, maxlen: 32},
%Pfx{bits: <<2, 2, 2>>, maxlen: 32}
]
iex>
iex> keys(ipt, 128)
[
%Pfx{bits: <<0xacdc::16, 0x1975::16>>, maxlen: 128},
%Pfx{bits: <<0xacdc::16, 0x2021::16>>, maxlen: 128}
]
iex>
iex> keys(ipt, 48)
[]
iex>
iex> keys(ipt, [32, 48, 128])
[
%Pfx{bits: <<1, 1, 1>>, maxlen: 32},
%Pfx{bits: <<2, 2, 2>>, maxlen: 32},
%Pfx{bits: <<0xacdc::16, 0x1975::16>>, maxlen: 128},
%Pfx{bits: <<0xacdc::16, 0x2021::16>>, maxlen: 128}
]
Specs
Return all the prefix,value-pairs whose prefix is a prefix for the given
search prefix
.
This returns the less specific entries that enclose the given search
prefix
. Note that any bitstring is always a prefix of itself. So, if
present, the search key will be included in the result.
If prefix
is not present or not valid, or cannot be encoded as an Ipv4 op
IPv6 Pfx.t/0
, an empty list is returned.
Example
iex> ipt = new()
...> |> put("1.1.1.0/25", "A25-lower")
...> |> put("1.1.1.128/25", "A25-upper")
...> |> put("1.1.1.0/30", "A30")
...> |> put("1.1.2.0/24", "B24")
iex>
iex> less(ipt, "1.1.1.0/30")
[
{"1.1.1.0/30", "A30"},
{"1.1.1.0/25", "A25-lower"},
]
iex> less(ipt, "2.2.2.2")
[]
Specs
Return the prefix,value-pair, whose prefix is the longest match for given search prefix
.
Returns nil if there is no match for search prefix
.
Silently ignores any errors when encoding the given search prefix
by returning nil.
Example
Specs
Return all the prefix,value-pairs where the search prefix
is a prefix for
the stored prefix.
This returns the more specific entries that are enclosed by given search
prefix
. Note that any bitstring is always a prefix of itself. So, if
present, the search prefix
will be included in the result.
If prefix
is not valid, or cannot be encoded as an Ipv4 op IPv6 t:Pfx.t
, nil
is returned.
Example
iex> ipt = new()
...> |> put("1.1.1.0/25", "A25-lower")
...> |> put("1.1.1.128/25", "A25-upper")
...> |> put("1.1.1.0/30", "A30")
...> |> put("1.1.2.0/24", "B24")
iex>
iex> more(ipt, "1.1.1.0/24")
[
{"1.1.1.0/30", "A30"},
{"1.1.1.0/25", "A25-lower"},
{"1.1.1.128/25", "A25-upper"}
]
Specs
new() :: t()
Create an new, empty Iptrie.
Example
iex> Iptrie.new()
%Iptrie{}
Specs
Create a new Iptrie, populated via a list of {prefix/0
, any/0
}-pairs.
Example
iex> ipt = Iptrie.new([{"1.1.1.0/24", "net1"}, {"acdc:1975::/32", "TNT"}])
iex> Map.get(ipt, 32)
{0, [{<<1, 1, 1>>, "net1"}], nil}
iex> Map.get(ipt, 128)
{0, nil, [{<<172, 220, 25, 117>>, "TNT"}]}
Specs
Populate the trie
with a list of {prefix,value}-pairs.
This always uses an exact match for prefix, updating its value if it exists. Any errors are silently ignored as the trie is always returned.
Example
iex> ipt = new([{"1.1.1.0/24", 0}, {"1.1.1.1", 0}, {"1.1.1.1", "x"}])
iex>
iex> get(ipt, "1.1.1.1")
{"1.1.1.1", "x"}
Specs
Puts value
under prefix
in the trie
.
This always uses an exact match for prefix, replacing its value if it exists. Any errors are silently ignored as the tree is always returned.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 0)
...> |> put("1.1.1.1", 1)
...> |> put("1.1.1.1", "x")
iex>
iex> get(ipt, "1.1.1.1")
{"1.1.1.1", "x"}
Specs
radix(t(), integer()) :: Radix.tree()
Return the radix tree for given type
.
If trie
has no Radix
tree for given type
, an empty radix will be returned.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> radix(ipt, 32)
{0, {6, [{<<1, 1, 1>>, 1}], [{<<2, 2, 2>>, 2}]}, nil}
iex>
iex> radix(ipt, 128)
{0, nil, {18, [{<<172, 220, 25, 117>>, 3}], [{<<172, 220, 32, 33>>, 4}]}}
iex> radix(ipt, 48)
{0, nil, nil}
Specs
Invoke fun
on all prefix,value-pairs in all radix trees in the trie
.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> reduce(ipt, 0, fn _key, value, acc -> acc + value end)
10
iex>
iex> reduce(ipt, %{}, fn key, value, acc -> Map.put(acc, key, value) end)
%{<<1, 1, 1>> => 1, <<2, 2, 2>> => 2, <<172, 220, 25, 117>> => 3, <<172, 220, 32, 33>> => 4}
Specs
reduce( t(), non_neg_integer() | [non_neg_integer()], any(), (bitstring(), any(), any() -> any()) ) :: any()
Invoke fun
on each prefix,value-pair in the radix tree for given type
This simply wraps Radix.reduce/3
for the radix tree in trie
at given
type
. The function fun
is called with the radix key, value and acc
accumulator and should return an updated accumulator. The result is the last
acc
accumulator returned.
If type
is a list of type
's, the acc
accumulator is updated across all
radix trees of type
's. Probably not entirely usefull, but there you go.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> reduce(ipt, 32, 0, fn _key, value, acc -> acc + value end)
3
iex> reduce(ipt, 48, 0, fn _key, value, acc -> acc + value end)
0
iex> reduce(ipt, 128, 0, fn _key, value, acc -> acc + value end)
7
iex>
iex> reduce(ipt, [32, 48, 128], 0, fn _key, value, acc -> acc + value end)
10
Specs
Return all prefix,value-pairs from all available radix trees in trie
.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> to_list(ipt)
[
{%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, 1},
{%Pfx{bits: <<2, 2, 2>>, maxlen: 32}, 2},
{%Pfx{bits: <<0xacdc::16, 0x1975::16>>, maxlen: 128}, 3},
{%Pfx{bits: <<0xacdc::16, 0x2021::16>>, maxlen: 128}, 4}
]
Specs
to_list(t(), non_neg_integer() | [non_neg_integer()]) :: [{prefix(), any()}]
Returns the prefix,value-pairs from the radix trees in trie
for given
type
(s).
If the radix tree for type
does not exist, an empty list is returned.
If type
is a list of types, a flat list of all prefix,value-pairs of all
radix trees of given types is returned.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> to_list(ipt, 32)
[
{%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, 1},
{%Pfx{bits: <<2, 2, 2>>, maxlen: 32}, 2}
]
iex> to_list(ipt, [32, 48, 128])
[
{%Pfx{bits: <<1, 1, 1>>, maxlen: 32}, 1},
{%Pfx{bits: <<2, 2, 2>>, maxlen: 32}, 2},
{%Pfx{bits: <<0xacdc::16, 0x1975::16>>, maxlen: 128}, 3},
{%Pfx{bits: <<0xacdc::16, 0x2021::16>>, maxlen: 128}, 4}
]
Specs
Lookup prefix
and update the matched entry, only if found.
Uses longest prefix match, so search prefix
is usually matched by some less
specific prefix. If matched, fun
is called on its value. If
prefix
had no longest prefix match, the trie
is returned unchanged.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 0)
...> |> update("1.1.1.0", fn x -> x + 1 end)
...> |> update("1.1.1.1", fn x -> x + 1 end)
...> |> update("2.2.2.2", fn x -> x + 1 end)
iex> get(ipt, "1.1.1.0/24")
{"1.1.1.0/24", 2}
iex> lookup(ipt, "2.2.2.2")
nil
Specs
Lookup prefix
and, if found, update its value or insert the default.
Uses longest prefix match, so search prefix
is usually matched by some less
specific prefix. If matched, fun
is called on the entry's value. If
prefix
had no longest prefix match, the default is inserted and fun
is
not called.
Example
iex> ipt = new()
...> |> update("1.1.1.0/24", 0, fn x -> x + 1 end)
...> |> update("1.1.1.0", 0, fn x -> x + 1 end)
...> |> update("1.1.1.1", 0, fn x -> x + 1 end)
...> |> update("2.2.2.2", 0, fn x -> x + 1 end)
iex> lookup(ipt, "1.1.1.2")
{"1.1.1.0/24", 2}
iex>
iex> # probably not what you wanted:
iex>
iex> lookup(ipt, "2.2.2.2")
{"2.2.2.2", 0}
Specs
Return all the values stored in all radix trees in trie
.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> values(ipt)
[1, 2, 3, 4]
Specs
Return the values stored in the radix trees in trie
for given type
.
Where type
is a either single maxlen or a list thereof.
Example
iex> ipt = new()
...> |> put("1.1.1.0/24", 1)
...> |> put("2.2.2.0/24", 2)
...> |> put("acdc:1975::/32", 3)
...> |> put("acdc:2021::/32", 4)
iex>
iex> values(ipt, 32)
[1, 2]
iex>
iex> values(ipt, 128)
[3, 4]
iex>
iex> values(ipt, 48)
[]
iex>
iex> values(ipt, [32, 48, 128])
[1, 2, 3, 4]