File Coverage

lib/Net/IPTrie.pm
Criterion Covered Total %
statement 108 112 96.4
branch 49 68 72.0
condition 19 33 57.5
subroutine 16 16 100.0
pod 5 6 83.3
total 197 235 83.8


line stmt bran cond sub pod time code
1             package Net::IPTrie;
2              
3 1     1   51320 use warnings;
  1         4  
  1         40  
4 1     1   5 use strict;
  1         2  
  1         30  
5 1     1   6 use Carp;
  1         14  
  1         131  
6 1     1   1805 use NetAddr::IP;
  1         67413  
  1         81  
7 1     1   580 use Net::IPTrie::Node;
  1         3  
  1         30  
8 1     1   15 use vars qw($VERSION);
  1         2  
  1         253  
9             $VERSION = '0.7';
10              
11             1;
12              
13             =head1 NAME
14              
15             Net::IPTrie - Perl module for building IPv4 and IPv6 address space hierarchies
16              
17             =head1 SYNOPSIS
18              
19             use Net::IPTrie;
20             my $tr = Net::IPTrie->new(version=>4); # IPv4
21             my $n = $tr->add(address=>'10.0.0.0', prefix=>8);
22             my $a = $tr->add(address=>'10.0.0.1', data=>$data) # prefix defaults to 32
23             $a->parent->address eq $n->address and print "$a is within $n";
24              
25             # Addresses can be provided in integer (decimal) format
26             # 10.0.0.7 == 167772167
27             my $b = $tr->add(iaddress=>'167772167', data=>'blah');
28             if ( my $c = $tr->find(address=>"10.0.0.7" ) {
29             print $c->data; # should print "blah"
30             }
31              
32             # If the IP does not exist:
33             my $d = $tr->find(address=>"10.0.0.8")
34             print $d->address; # should print "10.0.0.0", which is the closest parent block
35              
36             =head1 DESCRIPTION
37              
38             This module uses a radix tree (or trie) to quickly build the hierarchy of a given address space
39             (both IPv4 and IPv6). This allows the user to perform fast subnet or routing lookups.
40             It is implemented exclusively in Perl.
41              
42             =head1 CLASS METHODS
43              
44             =head2 new - Class Constructor
45              
46             Arguments:
47             Hash with the following keys:
48             version - IP version (4|6)
49             Returns:
50             New Net::IPTrie object
51             Examples:
52             my $tr = Net::IPTrie->new(version=>4);
53              
54             =cut
55              
56             sub new {
57 2     2 1 773 my ($proto, %argv) = @_;
58 2 50       10 croak "Missing required parameters: version" unless defined $argv{version};
59 2   33     13 my $class = ref($proto) || $proto;
60 2         6 my $self = {};
61 2 100       11 if ( $argv{version} == 4 ){
    50          
62 1         5 $self->{_size} = 32;
63             }elsif ( $argv{version} == 6 ){
64             # IPv6 numbers are larger than what a normal integer can hold
65 1     1   3125 use bigint;
  1         5001  
  1         7  
66 1         4 $self->{_size} = 128;
67             }else{
68 0         0 croak("Invalid IP version: $argv{version}");
69             }
70 2         7 $self->{_version} = $argv{version};
71 2         19 $self->{_trie} = Net::IPTrie::Node->new();
72 2         5 bless $self, $class;
73 2         8 return $self;
74             }
75              
76             ############################################################################
77              
78             =head1 INSTANCE METHODS
79              
80             =head2 version - Set or get IP version (4 or 6)
81              
82             Arguments:
83             IP version (4 or 6) - optional
84             Returns:
85             version (4 or 6)
86             Examples:
87             print $tr->version;
88              
89             =cut
90              
91             sub version {
92 12     12 1 24 my ($self, $v) = @_;
93 12 50       53 croak "version is an instance method" unless ref($self);
94              
95 12 50       32 $self->{_version} = $v if ( defined $v );
96 12         50 return $self->{_version};
97             }
98              
99             ############################################################################
100              
101             =head2 size - Set or get IP size (32 or 128)
102              
103             Arguments:
104             Size (32 or 128) - optional
105             Returns:
106             Address size in bits (32 or 128)
107             Examples:
108             print $tr->size;
109              
110             =cut
111              
112             sub size {
113 585     585 1 26560 my ($self, $s) = @_;
114 585 50       1253 croak "size is an instance method" unless ref($self);
115 585 50       1192 $self->{_size} = $s if ( defined $s );
116 585         2280 return $self->{_size};
117             }
118              
119             ############################################################################
120              
121             =head2 find - Find an IP object in the trie
122              
123             If the given IP does not exist, there are two options:
124             a) If the "deep" flag is off, the closest covering IP block is returned. This is
125             the default behavior.
126             b) If the "deep" flag is on, the node where the searched IP should be inserted is returned.
127             This is basically only useful for the "add" method.
128              
129             Arguments:
130             Hash with following keys:
131             address - String (i.e. "10.0.0.1") address
132             iaddress - Integer (i.e. "167772161") address, IPv4 or IPv6.
133             prefix - Prefix Length (optional - defaults to host mask)
134             deep - Flag (optional). If not found, return the node where object should be inserted.
135             Returns:
136             Net::IPTrie::Node object.
137             Examples:
138             my $n = $tr->find("10.0.0.1", 32);
139              
140             =cut
141              
142             sub find {
143 12     12 1 1467 my ($self, %argv) = @_;
144 12 50       42 croak "find is an instance method" unless ref($self);
145              
146 12         42 my ($address, $iaddress, $prefix, $deep) = @argv{'address', 'iaddress', 'prefix', 'deep'};
147 12 50 66     66 croak "Missing required arguments: address or iaddress"
148             unless (defined $address || defined $iaddress);
149            
150 12 100       43 $prefix = $self->size unless ( defined $prefix );
151 12         26 my $p = $self->{_trie}; # pointer that starts at the root
152 12         34 my $bit = $self->size; # Start at the most significant bit
153              
154             # Convert string address into integer if necessary
155 12 100 66     42 if ( defined $address && !defined $iaddress ){
156 3         12 $iaddress = $self->_ip2int($address);
157             }
158              
159 12         81 while ( $bit > $self->size - $prefix ){
160 471         54365 $bit--;
161              
162             # bit comparison.
163 471 100       13586 my $r = ($iaddress & 2**$bit) == 0 ? 'left' : 'right';
164            
165 471 100       233986 if ( !defined $p->$r ){
166 164 100       1378 if ( $deep ){
167             # Insert new node
168 163         561 $p->$r(Net::IPTrie::Node->new(up=>$p));
169             }else{
170             # Just return the closest covering IP block
171 1 50       24 if ( $p->iaddress ){
172 0         0 return $p;
173             }else{
174 1         15 return $p->parent;
175             }
176             }
177             }
178            
179             # Walk one step down the tree
180 470         13025 $p = $p->$r;
181              
182 470 100 100     12935 if ( defined $p->iaddress ){
    100          
183             # If the address matches, return node
184 17 100 100     452 if ( $p->iaddress == $iaddress && $p->prefix == $prefix ){
185 1         37 return $p;
186             }
187             }elsif ( !$deep && ($bit == $self->size - $prefix) ){
188             # This is a deleted node
189 1         7 return $p->parent;
190             }
191             }
192             # We fell off the bottom. We tell where to create a new node.
193 9         1513 return $p;
194             }
195              
196             ############################################################################
197              
198             =head2 add - Add an IP to the trie
199              
200             Arguments:
201             Hash with following keys:
202             address - String address, IPv4 or IPv6 (i.e. "10.0.0.1")
203             iaddress - Integer address, IPv4 or IPv6 (i.e. "167772161")
204             prefix - Prefix Length (optional - defaults to host mask)
205             data - Data (optional)
206             Returns:
207             New Net::IPTrie::Node object
208             Examples:
209             my $n = $tr->add(address=>"10.0.0.1", prefix=>32, data=>\$data);
210              
211             =cut
212              
213             sub add {
214 9     9 1 5040 my ($self, %argv) = @_;
215 9 50       40 croak "add is an instance method" unless ref($self);
216              
217 9         29 my ($address, $iaddress, $prefix, $data) = @argv{'address', 'iaddress', 'prefix', 'data'};
218 9 50 66     40 croak "Missing required arguments: address\n"
219             unless ( defined $address || defined $iaddress );
220              
221 9 100       32 $prefix = $self->size unless ( defined $prefix );
222              
223             # Convert string address into integer if necessary
224 9 100 66     76 if ( defined $address && !defined $iaddress ){
    50 33        
225 8         31 $iaddress = $self->_ip2int($address);
226             }elsif ( defined $iaddress && !defined $address ){
227 1         6 $address = $self->_int2ip($iaddress);
228             }
229              
230 9         303 my $n = $self->find(iaddress=>$iaddress, prefix=>$prefix, deep=>1);
231            
232 9 50 33     234 unless ( defined $n->iaddress && $n->iaddress == $iaddress ){
233 9         262 $n->iaddress($iaddress);
234 9         243 $n->address($address);
235 9         238 $n->prefix($prefix);
236 9         234 $n->data($data);
237             }
238 9         167 return $n;
239             }
240              
241              
242             ############################################################################
243             =head2 traverse - Traverse every node in the tree
244              
245             Arguments:
246             root - node object (optional - defaults to tree root)
247             code - coderef (will be passed the Net::IPTrie::Node object to act upon)
248             mode - (depth_first only, for now)
249             Returns:
250             Number of actual IP nodes visited
251             Examples:
252             # Store all IP nodes in an array, ordered.
253             my $list = ();
254             my $code = sub { push @$list, shift @_ };
255             my $count = $tr->traverse(code=>$code);
256            
257             =cut
258              
259             sub traverse {
260 1     1 0 1371 my ($self, %argv) = @_;
261 1 50       7 croak "traverse is an instance method" unless ref($self);
262 1         4 my ($root, $code, $mode) = @argv{'root', 'code', 'mode'};
263            
264 1   33     12 my $p = $root || $self->{_trie};
265 1         3 my $count = 0;
266 1         4 $mode |= 'depth_first';
267 1 50       4 if ( $mode eq 'depth_first' ){
268 1         7 $self->_depth_first(node=>$p, code=>$code, count=>\$count);
269             }else{
270 0         0 croak "Unknown climb mode: $mode";
271             }
272 1         13 return $count;
273             }
274              
275              
276             ############################################################################
277             #
278             # PRIVATE METHODS
279             #
280             ############################################################################
281              
282              
283             ############################################################################
284             # _ip2int - Convert string IP to integer
285             #
286             # Arguments:
287             # IP address in string format ('10.0.0.1')
288             # Returns:
289             # IP address in integer format
290             # Examples:
291             # my $number = $tr->ip2int('10.0.0.1');
292             #
293             sub _ip2int {
294 11     11   19 my ($self, $ip) = @_;
295 11         17 my $nip;
296 11 100       47 if ( $self->version == 4 ){
297 7         51 $nip = NetAddr::IP->new($ip);
298             }else{
299 4         37 $nip = NetAddr::IP->new6($ip);
300             }
301 11 50       2506 croak "Invalid IP: $ip" unless $nip;
302 11         2997 return $nip->numeric;
303             }
304              
305             ############################################################################
306             # _int2ip - Convert integer IP to string
307             #
308             # Arguments:
309             # IP address in integer format
310             # Returns:
311             # IP address in string format
312             # Examples:
313             # my $dottedquad = $tr->_int2ip(167772161);
314             #
315             sub _int2ip {
316 1     1   2 my ($self, $int) = @_;
317 1         3 my $nip;
318 1 50       4 if ( $self->version == 4 ){
319 1         11 $nip = NetAddr::IP->new($int);
320             }else{
321 0         0 $nip = NetAddr::IP->new6($int);
322             }
323 1 50       44 croak "Invalid IP integer: $int" unless $nip;
324 1         288 return $nip->addr;
325             }
326              
327             ############################################################################
328             # _depth_first - Recursively visit each node in depth-first mode
329             #
330             # Arguments:
331             # Hash with following key/value pairs:
332             # node - Starting node
333             # code - coderef (will be passed the Net::IPTrie::Node object to act upon)
334             # count - Scalar reference
335             # Returns:
336             # Examples:
337             #
338             #
339             sub _depth_first {
340 36     36   1225 my ($self, %argv) = @_;
341 36         67 my ($n, $code, $count) = @argv{'node', 'code', 'count'};
342            
343 36 100       744 if ( $n->address ){
344 5 50 33     60 if ( defined $code && ref($code) eq "CODE" ){
345             # execute code
346 5         13 $code->($n);
347             }
348 5         17 $$count++;
349             }
350 36 100       870 $self->_depth_first(node=>$n->left, code=>$code, count=>$count) if ( defined $n->left );
351 36 100       922 $self->_depth_first(node=>$n->right, code=>$code, count=>$count) if ( defined $n->right );
352             }
353              
354             =head1 AUTHOR
355              
356             Carlos Vicente
357              
358             =head1 SEE ALSO
359              
360             Net::IPTrie::Node
361             Net::Patricia
362              
363             =head1 LICENCE AND COPYRIGHT
364              
365             Copyright (c) 2007-2010, Carlos Vicente . All rights reserved.
366              
367             This module is free software; you can redistribute it and/or
368             modify it under the same terms as Perl itself. See L.
369              
370              
371             =head1 DISCLAIMER OF WARRANTY
372              
373             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
374             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
375             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
376             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
377             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
378             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
379             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
380             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
381             NECESSARY SERVICING, REPAIR, OR CORRECTION.
382              
383             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
384             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
385             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
386             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
387             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
388             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
389             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
390             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
391             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
392             SUCH DAMAGES.
393             =cut