NAME

Net::IPAM::Tree - A CIDR/Block tree library for fast IP lookup with longest-prefix-match.

DESCRIPTION

A module for fast IP-routing-table lookups and IP-ACLs (Access Control Lists).

It is NOT a standard patricia-trie implementation. This isn't possible for general blocks not represented by bitmasks, every tree item is a Net::IPAM::Block.

The complexity for tree operations is in worst case O(h * log n) with h <= 128 (IPv6) or h <=32 (IPv4).

SYNOPSIS

use Net::IPAM::Tree;

my $t = Net::IPAM::Tree->new();

$t->insert(@blocks) || die("duplicate block...");
$t->remove($block)  || warn("block not in tree...");

my $block = $t->lookup($ip_or_block)
  && printf( "longest-prefix-match in tree for %s is %s\n", $ip_or_block, $block );

$t->contains($ip_or_block)
  && printf( "ip or block %s is contained in tree\n", $ip_or_block );

say $t->to_string;

▼
├─ ::/8
├─ 100::/8
├─ 2000::/3
│  ├─ 2000::/4
│  └─ 3000::/4
├─ 4000::/3
...

METHODS

new

Create Net::IPAM::Tree object.

my $t = Net::IPAM::Tree->new;

insert

Insert block(s) into the tree. Inserting a bulk of blocks is much faster than inserting unsorted single blocks in a loop.

Returns the tree object on success for method chaining.

my $t = Net::IPAM::Tree->new->insert(@blocks) // die("one or more blocks are duplicate");;

Returns undef on duplicate blocks in the tree and generate warnings (warnings can be disabled).

{
  no warnings 'Net::IPAM::Tree';
  $t->insert($dup)
}

contains($thing)

Returns true if the given $thing (Net::IPAM::IP or Net::IPAM::Block) is contained in any block of the tree. Just returns true or false and not the matching prefix, this is much faster than a full lookup for the longest-prefix-match.

This can be used for fast ACL lookups.

# make blocks
my @deny = map { Net::IPAM::Block->new($_) } qw(2001:db8::-2001:db8::1234:ffea fe80::/10);

# make tree
my $deny = Net::IPAM::Tree->new->insert(@deny) or die;

my $ip = Net::IPAM::IP->new( get_ip_from($some_request) );
say "request forbidden for $ip" if $deny->contains($ip);

lookup($thing)

Returns Net::IPAM::Block with longest prefix match for $thing (Net::IPAM::IP or Net::IPAM::Block) in the tree, undef if not found.

This can be used for fast routing table lookups.

# make blocks
my @priv = map { Net::IPAM::Block->new($_) } qw(10.0.0.0/8 172.16.0.0/12 192.168.0.0 fc00::/7);

# make tree
my $priv = Net::IPAM::Tree->new->insert(@priv) or die;

my $b = Net::IPAM::Block->new('fdcd:aa59:8bce::/48') or die;

my $lpm = $priv->lookup($b)
  && say "longest-prefix-match for $b is $lpm";

remove

Remove one block from tree, relink parent/child relation at the gap.

$t->remove($block) // warn("block not found");

Returns undef if $block is not found.

remove_branch

Remove $block and the branch below from tree.

$t->remove_branch($block) // warn("block not found");

Returns undef if $block is not found.

to_string

Returns the tree as ordered graph or undef on empty trees.

$t->to_string($callback);

The optional callback is called on every block. Returns the decorated string for block.

$t->to_string( sub { my $block = shift; return decorate($block) } );

example (without callback):

▼
├─ ::/8
├─ 100::/8
├─ 2000::/3
│  ├─ 2000::/4
│  └─ 3000::/4
├─ 6000::/3

possible example (with callback):

▼
├─ ::/8.................   "Reserved by IETF     [RFC3513][RFC4291]"
├─ 100::/8..............   "Reserved by IETF     [RFC3513][RFC4291]"
├─ 2000::/3.............   "Global Unicast       [RFC3513][RFC4291]"
│  ├─ 2000::/4.............  "Test"
│  └─ 3000::/4.............  "FREE"
├─ 6000::/3.............   "Reserved by IETF     [RFC3513][RFC4291]"

walk

Walks the tree starting at root node in depth first order.

my $err_string = $t->walk($callback);

For every node Net::IPAM::Tree::Node the callback funtion is called with the node and the current depth (counting from 0) as arguments.

my $err_string = $callback->($node, $depth);

The callback must return undef if there is no error! On error, the walk is stopped and the error is returned to the caller.

Example, get some tree statistics:

my ( $n, $max_d, $max_c ) = ( 0, 0, 0 );

my $cb = sub {
  my ( $node, $depth ) = @_;

  $n++;
  $max_c = $node->childs if $max_c < $node->childs;
  $max_d = $depth + 1    if $max_d < $depth + 1;

  return;    # explicit return (undef) if there is no error!
};

my $err = $t->walk($cb);
say "tree has $n nodes and is $max_d levels deep, the number of max childs/node is $max_c" unless $err;

len

Returns the number of blocks in the tree.

AUTHOR

Karl Gaissmaier, <karl.gaissmaier(at)uni-ulm.de>

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Net::IPAM::Tree

You can also look for information at:

  • on github

    TODO

SEE ALSO

Net::IPAM::Tree::Node Net::IPAM::IP Net::IPAM::Block

LICENSE AND COPYRIGHT

This software is copyright (c) 2020 by Karl Gaissmaier.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.