File Coverage

blib/lib/IPv6/Address.pm
Criterion Covered Total %
statement 216 233 92.7
branch 71 102 69.6
condition 4 6 66.6
subroutine 62 65 95.3
pod 29 31 93.5
total 382 437 87.4


line stmt bran cond sub pod time code
1 1     1   13502 use strict;
  1         4  
  1         36  
2 1     1   6 use warnings;
  1         1  
  1         55  
3              
4             package IPv6::Address;
5             $IPv6::Address::VERSION = '0.206';
6              
7             =head1 NAME
8              
9             IPv6::Address - IPv6 Address Manipulation Library
10              
11             =head1 VERSION
12              
13             version 0.206
14              
15             =for html
16            
17             Coverage Status
18              
19              
20             =head1 SYNOPSIS
21              
22             use IPv6::Address;
23              
24             my $ipv6 = IPv6::Address->new('2001:648:2000::/48');
25              
26             $ipv6->contains('2001:648:2000::/64'); #true
27              
28             say $ipv6->to_string;
29             say $ipv6->string; # Same as previous
30             say $ipv6; # Same as previous
31              
32             say $ipv6->string(nocompress=>1); # do not compress using the :: notation
33             say $ipv6->string(ipv4=>1); #print the last 32 bits as an IPv4 address
34            
35             $ipv6->addr_string; # Returns '2001:648:2000::'
36            
37             $ipv6->split(4); # Split the prefix into 2^4 smaller prefixes. Returns a list.
38              
39             $ipv6->apply_mask; # Apply the mask to the address. All bits beyond the mask length become 0.
40              
41             $ipv6->first_address;
42              
43             $ipv6->last_address;
44              
45             $a->enumerate_with_offset( 5 , 64 ); #returns 2001:648:2000:4::/64
46              
47             =head1 DESCRIPTION
48              
49             A pure Perl IPv6 address manipulation library. Emphasis on manipulation of
50             prefixes and addresses. Very easy to understand and modify. The internal
51             representation of an IPv6::Address is a blessed hash with two keys, a prefix
52             length (0-128 obviously) and a 128-bit string. A multitude of methods to do
53             various tasks is provided.
54              
55              
56             =head2 Methods
57              
58             =over 12
59              
60             =cut
61              
62 1     1   3 use strict;
  1         1  
  1         12  
63 1     1   3 use warnings;
  1         1  
  1         21  
64 1     1   3 use Carp;
  1         1  
  1         62  
65 1     1   519 use Data::Dumper;
  1         7137  
  1         118  
66 1     1   533 use Sub::Install;
  1         1659  
  1         5  
67              
68             use overload
69 1         9 '""' => \&to_string,
70             '<=>' => \&n_cmp,
71 1     1   58 fallback => 1;
  1         2  
72            
73             my $DEBUG = 0;
74              
75             sub debug {
76 107 50   107 0 169 $DEBUG&&print STDERR $_[0];
77 107 50       159 $DEBUG&&print STDERR "\n";
78            
79             }
80              
81             =item C
82              
83             Takes a string representation of an IPv6 address and creates a corresponding
84             IPv6::Address object.
85              
86             =cut
87              
88             #takes a normal address as argument. Example 2001:648:2000::/48
89             sub new {
90 41 50   41 1 3047 my $class = shift(@_) or croak "incorrect call to new";
91 41 50       68 my $ipv6_string = shift(@_) or croak "Cannot use an empty string as argument";
92 41         161 my ($ipv6,$prefixlen) = ( $ipv6_string =~ /([0-9A-Fa-f:]+)\/(\d+)/ );
93 41 50       68 croak "IPv6 address part not parsable" if (!defined($ipv6));
94 41 50       60 croak "IPv6 prefix length part not parsable" if (!defined($prefixlen));
95 41         88 debug("ipv6 is $ipv6, length is $prefixlen");
96 41         52 my @arr;
97 41         135 my @_parts = ( $ipv6 =~ /([0-9A-Fa-f]+)/g );
98 41         43 my $nparts = scalar @_parts;
99 41 100       95 if ($nparts != 8) {
100 33         62 for(my $i=1;$i<=(8-$nparts);$i++) { push @arr,hex "0000" };
  194         250  
101             }
102              
103 41 100       167 my @parts = map { ($_ eq '::')? @arr : hex $_ } ( $ipv6 =~ /((?:[0-9A-Fa-f]+)|(?:::))/g );
  167         266  
104            
105 41         64 debug(join(":",map { sprintf "%04x",$_ } @parts));
  328         443  
106              
107 41         104 my $bitstr = pack 'n8',@parts;
108            
109 41         174 return bless {
110             bitstr => $bitstr,
111             prefixlen => $prefixlen,
112             },$class;
113             }
114              
115             =item C
116              
117             Creates a new IPv6::Address out of a bitstring and a prefix length. The
118             bitstring must be binary, please do not use a '0' or '1' character string.
119              
120             =cut
121              
122             #takes a bitstr (0101010101111010010....) and a prefix length as arguments
123             sub raw_new {
124 34     34 1 39 my $class = $_[0];
125 34         165 return bless {
126             bitstr => $_[1],
127             prefixlen => $_[2],
128             },$class;
129             }
130              
131             =item C
132              
133             Returns the bitstr of the object.
134              
135             =cut
136              
137             #returns the bitstr (11010111011001....)
138             sub get_bitstr {
139 215     215 1 536 return $_[0]->{bitstr};
140             }
141              
142              
143             =item C
144              
145             Returns the prefix length of the address.
146              
147             =cut
148              
149             #returns the length of the IPv6 address prefix
150             sub get_prefixlen {
151 215     215 1 886 return $_[0]->{prefixlen};
152             }
153              
154             =item C
155              
156             Returns a 128-bit string with the first prefix-length bits equal
157             to 1, rest equal to 0. Essentially takes the prefix length of the object and
158             returns a corresponding bit mask.
159              
160             =cut
161              
162             #returns a 1111100000 corresponding to the prefix length
163             sub get_mask_bitstr {
164 3     3 1 8 generate_bitstr( $_[0]->get_prefixlen )
165             }
166              
167             =item C
168              
169             Returns the bitstring, after zeroing out all the bits after the prefix length.
170             Essentially applies the prefix mask to the address.
171              
172             =cut
173             sub get_masked_address_bitstr {
174 8     8 1 13 generate_bitstr( $_[0]->get_prefixlen ) & $_[0]->get_bitstr;
175             }
176              
177             =item C
178              
179             Not a method, returns 128-bit string, first n-items are 1, rest is 0.
180              
181             =cut
182              
183             sub generate_bitstr {
184             #TODO trick bellow is stupid ... fix
185 19     19 1 62 pack 'B128',join('',( ( map { '1' } ( 1 .. $_[0] ) ) , ( map { '0' } ( 1 .. 128-$_[0] ) ) ));
  768         536  
  1664         1248  
186             }
187              
188             =item C
189              
190             Not a method, AND's two bitstrings, returns result.
191              
192             =cut
193             #takes two bitstrs as arguments and returns their logical or as bitstr
194             sub bitstr_and {
195 1     1 1 5 return $_[0] & $_[1]
196             }
197              
198             =item C
199              
200             Not a method, OR's two bitstrings, returns result.
201              
202             =cut
203             #takes two bitstrs as arguments and returns their logical or as bitstr
204             sub bitstr_or {
205 1     1 1 4 return $_[0] | $_[1]
206             }
207              
208             =item C
209              
210             Not a method, inverts a bitstring.
211              
212             =cut
213             #takes a bitstr and inverts it
214             sub bitstr_not {
215 1     1 1 6 return ~ $_[0]
216             }
217              
218             =item C
219              
220             Not a method, takes a string of characters 0 or 1, returns corresponding binary
221             bitstring. Please do not use more than 128 characters, rest will be ignored.
222              
223             =cut
224              
225             #converts a bitstr (111010010010....) to a binary string
226             sub from_str {
227 32     32 1 32 my $str = shift(@_);
228 32         110 return pack("B128",$str);
229             }
230              
231             =item C
232              
233             Not a method, takes a binary bitstring, returns a string composed of 0's and
234             1's. Please supply bitstrings of max. 128 bits, rest of the bits will be
235             ignored.
236              
237             =cut
238              
239             #converts from binary to literal bitstr
240             sub to_str {
241 41     41 1 52 my $bitstr = shift(@_);
242 41         167 return join('',unpack("B128",$bitstr));
243             }
244              
245             =item C
246              
247             This method takes an argument which is either an IPv6::Address or a plain string
248             that can be promoted to a valid IPv6::Address, and tests whether the object
249             contains it. Obviously returns true or false.
250              
251             =cut
252              
253             sub contains {
254 9 50   9 1 19 defined( my $self = shift(@_) ) or die 'incorrect call';
255 9 50       15 defined( my $other = shift(@_) ) or die 'incorrect call';
256 9 50       17 if (ref($other) eq '') {
257 9         11 $other = __PACKAGE__->new($other);
258             }
259 9 100       13 return if ($self->get_prefixlen > $other->get_prefixlen);
260 8 100       17 return 1 if $self->get_masked_address_bitstr eq ( generate_bitstr( $self->get_prefixlen ) & $other->get_bitstr );
261             #return 1 if (substr($self->get_bitstr,0,$self->get_prefixlen) eq substr($other->get_bitstr,0,$self->get_prefixlen));
262 1         21 return;
263             }
264              
265             =item C
266              
267             Returns the address part of the IPv6::Address. Using the option ipv4=>1 like
268              
269             $a->addr_string(ipv4=>1)
270              
271             will make the last 32-bits appear as an IPv4 address. Also, using nocompress=>1
272             like
273              
274             $a->addr_string( nocompress => 1 )
275              
276             will prevent the string from containing a '::' part. So it will be 8 parts
277             separated by ':' colons.
278              
279             =cut
280              
281             #returns the address part (2001:648:2000:0000:0000....)
282             sub addr_string {
283 54     54 1 45 my $self = shift(@_);
284 54         81 my $str = join(':',map { sprintf("%x",$_) } (unpack("nnnnnnnn",$self->get_bitstr)) );
  432         583  
285 54         106 my $str2 = join(':',map { sprintf("%04x",$_) } (unpack("nnnnnnnn",$self->get_bitstr)) );
  432         484  
286             #print Dumper(@_);
287 54         114 my %option = (@_) ;
288             #print Dumper(\%option);
289 54 50 66     116 if (defined($option{ipv4}) && $option{ipv4}) {
290             ###print "string:",$str,"\n";
291 3         6 $str = join(':',map { sprintf("%x",$_) } (unpack("nnnnnn",$self->get_bitstr)) ).':'.join('.', map {sprintf("%d",hex $_)} ($str2 =~ /([0-9A-Fa-f]{2})([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/));
  18         39  
  12         20  
292             #print STDERR $ipv4,"\n";
293            
294             }
295             #print 'DEBUG:' . $str,"\n";
296 54 100       84 return $str2 if $option{full};
297 53 100       79 return $str if $option{nocompress};
298 51 100       108 return '::' if($str eq '0:0:0:0:0:0:0:0');
299 48         102 for(my $i=7;$i>1;$i--) {
300 181         408 my $zerostr = join(':',split('','0'x$i));
301             ###print "DEBUG: $str $zerostr \n";
302 181 100       2816 if($str =~ /:$zerostr$/) {
    100          
    100          
303 34         146 $str =~ s/:$zerostr$/::/;
304 34         122 return $str;
305             }
306             elsif ($str =~ /:$zerostr:/) {
307 9         40 $str =~ s/:$zerostr:/::/;
308 9         52 return $str;
309             }
310             elsif ($str =~ /^$zerostr:/) {
311 4         25 $str =~ s/^$zerostr:/::/;
312 4         33 return $str;
313             }
314             }
315 1         2 return $str;
316             }
317              
318             =item C
319              
320             Returns the full IPv6 address, with the prefix in its end.
321              
322             =cut
323              
324             #returns the full IPv6 address
325             sub string {
326 48     48 1 67 my $self = shift(@_);
327 48         88 return $self->addr_string(@_).'/'.$self->get_prefixlen;
328             }
329              
330             =item C
331              
332             Used internally by the overload module.
333              
334             =cut
335             #to be used by the overload module
336             sub to_string {
337 41     41 1 5256 return $_[0]->string();
338             }
339              
340             =item C
341              
342             Splits the address to the order of two of the number given as first argument.
343             Example: if argument is 3, 2^3=8, address is split into 8 parts. The final parts
344             have prefix length equal to the target_length specified in the second argument.
345              
346             =cut
347             sub split {
348 0     0 1 0 my $self = shift(@_);
349 0         0 my $split_length = shift(@_);#example: 3
350 0         0 my $networks = 2**$split_length;#2**3 equals 8 prefixes
351 0         0 my @bag = ();
352 0         0 for(my $i=0;$i<$networks;$i++) { #from 0 to 7
353 0         0 my $b_str = sprintf("%0${split_length}b",$i); # 001,010,011 and so on util 111 (7)
354 0         0 my $addr_str = $self->get_bitstr; #get the original bitstring of the address
355 0         0 substr($addr_str,$self->get_prefixlen,$split_length) = $b_str; #replace the correct 3 bits with $b_str
356 0         0 debug $addr_str,"\n";
357 0         0 push @bag,(__PACKAGE__->raw_new($addr_str,$self->get_prefixlen + $split_length)); #create and store the new addr
358             }
359 0         0 return @bag;
360             }
361              
362            
363             =item C
364              
365             Applies the prefix length mask to the address. Does not return anything. Works on $self.
366             BThis will alter the object.
367              
368             =cut
369             sub apply_mask {
370 0     0 1 0 my $self = shift(@_);
371 0         0 $self->{bitstr} = bitstr_and($self->get_bitstr,$self->get_mask_bitstr);
372             }
373              
374             =item C
375              
376             Returns the first address of the prefix that is represented by the object. E.g.
377             consider 2001:648:2000::1234/64. First address will be 2001:648:2000::/64.
378              
379             =cut
380              
381             sub first_address {
382 1     1 1 4 my $bitstr = bitstr_and( $_[0]->get_bitstr , $_[0]->get_mask_bitstr );
383 1         10 IPv6::Address->raw_new( $bitstr, $_[0]->get_prefixlen);
384             }
385              
386             =item C
387              
388             Returns the last address of the prefix that is represented by the object. E.g.
389             consider 2001:648:2000::1234/64. Last address will be
390             2001:648:2000::ffff:ffff:ffff:ffff/64.
391              
392             =cut
393             sub last_address {
394 1     1 1 3 my $bitstr = bitstr_or( $_[0]->get_bitstr , bitstr_not( $_[0]->get_mask_bitstr ) );
395 1         12 IPv6::Address->raw_new( $bitstr, $_[0]->get_prefixlen);
396             }
397            
398              
399             =item C , C , C
400              
401             Returns true or false depending on whether the address falls into the
402             corresponding category stated by the method name. E.g.
403              
404             IPv6::Address->new('::1')->is_loopback # returns true
405              
406             =cut
407              
408             my %patterns = (
409             unspecified => "^::\$",
410             loopback => "^::1\$",
411             multicast => "^ff",
412             );
413             #@TODO: implement this
414             my %binary_patterns = (
415             "link-local unicast" => "^",
416             );
417              
418              
419             for my $item (keys %patterns) {
420             Sub::Install::install_sub({
421             code => sub {
422 6 100   6   327 return ( shift(@_)->addr_string =~ /$patterns{$item}/i )? 1 : 0;
423             },
424             into => __PACKAGE__,
425             as => 'is_'.$item,
426             });
427             }
428              
429 1     1   1665 use strict;
  1         4  
  1         858  
430              
431             =item C
432              
433             Not a method, takes an IPv4 address, returns a character string consisting of 32
434             characters that are 0 or 1. Used internally, not too useful for the end user.
435              
436             =cut
437             sub ipv4_to_binarray {
438 1 50   1 1 3 defined( my $ipv4 = shift ) or die 'Missing IPv4 address argument';
439 1         4 my @parts = ( split('\.',$ipv4) );
440 1         2 my @binarray = split('',join('',map { sprintf "%08b",$_ } @parts));
  4         13  
441             #debug(Dumper(\@binarray));
442 1         14 return @binarray;
443             }
444              
445              
446              
447             =item C
448              
449             Takes an IPv4 address and uses a part of it to enumerate inside the Ipv6 prefix
450             of the object. E.g.
451              
452             IPv6::Address->new('2001:648:2001::/48')->enumerate_with_IPv4('0.0.0.1',0x0000ffff) #will yield 2001:648::2001:0001::/64
453              
454             The return value will be a new IPv6::Address object, so the original object
455             remains intact. The part that will be used as an offset is extracted from the
456             ipv4 by using the mask.
457              
458             =cut
459              
460             sub enumerate_with_IPv4 {
461 1 50   1 1 297 my ($self,$IPv4,$mask) = (@_) or die 'Incorrect call';
462 1         5 my $binmask = sprintf "%032b",$mask;
463            
464 1         3 my @IPv4 = ipv4_to_binarray($IPv4);
465 1         2 my $binary = '';
466 1         6 for(my $i=0;$i<32;$i++) {
467             #debug("$i ".substr($binmask,$i,1));
468 32 100       87 $binary = $binary.$IPv4[$i] if substr($binmask,$i,1) == 1;
469             }
470 1         4 debug($binary);
471 1         4 my $new_prefixlen = $self->get_prefixlen + length($binary);
472 1         4 my $new_bitstr = to_str( $self->get_bitstr );
473 1         4 debug($new_bitstr);
474 1         3 substr($new_bitstr, ($self->get_prefixlen), length($binary)) = $binary;
475 1         4 debug("old bitstring is ".$self->get_bitstr);
476 1         4 debug("new bitstring is $new_bitstr");
477 1         3 debug($new_prefixlen);
478            
479 1         3 return __PACKAGE__->raw_new(from_str($new_bitstr),$new_prefixlen);
480             }
481              
482             =item C
483              
484             Takes a non-negative integer offset and returns a prefix whose relative position
485             inside the object is defined by the offset. The prefix length of the result is
486             defined by the second argument. E.g.
487              
488             IPv6::Address->new('2001:648:2000::/48')->enumerate_with_offset( 5 , 64 ) #2001:648:2000:4::/64
489              
490             =cut
491              
492             sub enumerate_with_offset {
493 20 50   20 1 432 my ($self,$offset,$desired_length) = (@_) or die 'Incorrect call';
494 20         33 my $to_replace_len = $desired_length - $self->get_prefixlen;
495 20         34 my $new_bitstr = to_str( $self->get_bitstr );
496 20         62 my $offset_bitstr = sprintf("%0*b",$to_replace_len,$offset);
497 20         50 debug("offset number is $offset (or: $offset_bitstr)");
498             #consistency check
499 20 100       67 die "Tried to replace $to_replace_len bits, but for $offset, ".length($offset_bitstr)." bits are required"
500             if(length($offset_bitstr) > $to_replace_len);
501 18         24 substr($new_bitstr, ($self->get_prefixlen), length($offset_bitstr) ) = $offset_bitstr;
502 18         28 return __PACKAGE__->raw_new(from_str($new_bitstr),$desired_length);
503             }
504              
505             =item C
506              
507             Increments the IPv6::Address object by offset. Offsets larger than 2^32-1 are
508             not acceptable. This method is probably not too useful, but is provided for
509             completeness.
510              
511             =cut
512              
513             sub increment {
514 15 50   15 1 437 my ( $self , $offset ) = (@_) or die 'Incorrect call';
515              
516 15         11 my $max_int = 2**32-1;
517 15 50       29 die 'Sorry, offsets beyond 2^32-1 are not acceptable' if( $offset > $max_int );
518 15 100       19 die 'Sorry, cannot offset a /0 prefix. ' if ( $self->get_prefixlen == 0 );
519              
520 14         25 my $new_bitstr = to_str( $self->get_bitstr ); #will use it to store the new bitstr
521              
522 14 50       28 $DEBUG && print STDERR "Original bitstring is $new_bitstr\n";
523              
524             # 0..127
525 14 50       17 my $start = ($self->get_prefixlen>=32)? $self->get_prefixlen - 32 : 0 ;
526 14         19 my $len = $self->get_prefixlen - $start;
527              
528 14 50       24 $DEBUG && print STDERR "will replace from pos $start (from 0) and for $len len\n";
529              
530             # extract start..start+len part, 0-pad to 32 bits, pack into a network byte order $n
531 14         79 my $n = unpack('N',pack('B32',sprintf("%0*s",32,substr($new_bitstr, $start , $len ))));
532              
533 14 50       31 $DEBUG && print STDERR "Original n=".$n."\n";
534 14         11 $n += $offset;
535 14 50       18 $DEBUG && print STDERR "Result n=".$n."\n";
536              
537 14 100       28 die "Sorry, address part exceeded $max_int" if( $n > $max_int ); #just a precaution
538              
539             # repack the $n into a 32bit network ordered integer, convert into "1000101010101..." string
540 13         53 my $bstr = unpack( "B32", pack( 'N' , $n ) );
541              
542 13 50       18 $DEBUG && print STDERR "Replacement bitstr is $bstr\n";
543 13 50       22 die 'internal error. Address should be 32-bits long' unless (length($bstr) == 32); #another precaution
544            
545             #replace into new_bitstr from start and for len with bstr up for len bytes counting from the *end*
546 13         20 substr( $new_bitstr , $start , $len ) = substr( $bstr, - $len);
547              
548             # result is ready, return it
549 13         20 return __PACKAGE__->raw_new(from_str($new_bitstr),$self->get_prefixlen);
550             }
551              
552             =item C
553              
554             Takes the bitstring of the address and unpacks it using the first argument.
555             Internal use mostly.
556              
557             =cut
558              
559             sub nxx_parts {
560 44     44 1 47 unpack($_[1],$_[0]->get_bitstr)
561             }
562              
563             =item C
564              
565             Splits the address into an 8-item array of unsigned short integers. Network byte
566             order is implied, a short integer is 16-bits long.
567              
568             =cut
569              
570             #@TODO add tests for this method
571             sub n16_parts {
572 0     0 1 0 ( $_[0]->nxx_parts('nnnnnnnn') )
573             }
574              
575             =item C
576              
577             Splits the address into an 4-item array of unsigned long integers. Network byte
578             order is implied, a long integer is 32-bits long.
579              
580             =cut
581             #@TODO add tests for this method
582             sub n32_parts {
583 44     44 0 61 ( $_[0]->nxx_parts('NNNN') )
584             }
585              
586             =item C
587              
588             Takes two 128-bit bitstr arguments, compares them and returns the result as -1,
589             0 or 1. The semantics are the same as that of the spaceship operator <=>.
590              
591             This method will overload the <=> operator for IPv6::Address objects, so
592             comparing IPv6::Address objects like they were integers produces the correct
593             results.
594              
595             =cut
596              
597             #@TODO add tests for this method
598             sub n_cmp {
599 22     22 1 38 my @a = $_[0]->n32_parts;
600 22         31 my @b = $_[1]->n32_parts;
601 22         34 for ( 0 .. 3 ) {
602 64         51 my $cmp = ( $a[$_] <=> $b[$_] );
603 64 100       112 return $cmp if ( $cmp != 0 );
604             }
605 10         27 return 0;
606             }
607              
608             =item C
609              
610             Sorts an array of bitstrs using the n_cmp function.
611              
612             =cut
613              
614             sub n_sort {
615 1     1 1 7 sort { $a <=> $b } @_;
  8         12  
616             }
617              
618             =item C
619              
620             Returns a string suitable to be returned as an IPv6 Radius AV-pair. See RFC 3162
621             for an explanation of the format.
622              
623             =back
624             =cut
625              
626             sub radius_string {
627 6 50   6 1 617 defined(my $self = shift) or die 'Missing argument';
628             #Framed-IPv6-Prefix := 0x0040200106482001beef
629 6         14 my $partial_bitstr = substr(to_str( $self->get_bitstr ),0,$self->get_prefixlen);
630 6         14 my $remain = $self->get_prefixlen % 8;
631 6 100       17 if($remain > 0) {
632 2         9 $partial_bitstr = $partial_bitstr . '0'x(8 - $remain);
633             }
634 6         8 return '0x00'.sprintf("%02x",$self->get_prefixlen).join('',map {unpack("H",pack("B4",$_))} ($partial_bitstr =~ /([01]{4})/g) );
  84         140  
635             }
636              
637             package IPv4Subnet;
638             $IPv4Subnet::VERSION = '0.206';
639 1     1   510 use Socket;
  1         3077  
  1         322  
640 1     1   5 use strict;
  1         0  
  1         15  
641 1     1   2 use Carp;
  1         1  
  1         33  
642 1     1   3 use warnings;
  1         0  
  1         24  
643 1     1   2 use Data::Dumper;
  1         1  
  1         583  
644              
645              
646             sub new {
647 35 50   35   376 defined ( my $class = shift ) or die "missing class";
648 35 50       49 defined ( my $str = shift ) or die "missing string";
649 35 50       171 my ( $ip , $length_n ) = ( $str =~ /^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/ ) or croak "Cannot parse $str";
650 35         51 bless { ip_n => my_aton($ip) , length_n => $length_n } , $class ;
651             }
652              
653             sub new_from_start_stop {
654 1     1   7 $_[0]->new( $_[1].'/'.(32 - log( ( my_aton($_[1]) ^ my_aton($_[2]) ) + 1)/log(2)))
655             }
656              
657             sub to_string {
658 1     1   7 $_[0]->get_start_ip . '/' . $_[0]->get_length_n
659             }
660              
661             sub get_ip_n {
662 58     58   80 return $_[0]->{ip_n} ;
663             }
664              
665             sub get_start {
666 58     58   64 return $_[0]->get_ip_n & $_[0]->get_mask_n;
667             }
668              
669             sub get_stop {
670 6     6   14 return $_[0]->get_start + $_[0]->get_length - 1;
671             }
672              
673             sub get_start_ip {
674 4     4   13 return my_ntoa($_[0]->get_start);
675             }
676              
677             sub get_stop_ip {
678 3     3   7 return my_ntoa($_[0]->get_stop);
679             }
680              
681             sub get_length {
682 29     29   626 return 2**(32-$_[0]->get_length_n);
683             }
684              
685             sub enumerate {
686 1     1   4 map { my_ntoa( $_ ) } ($_[0]->get_start .. $_[0]->get_stop)
  256         210  
687             }
688              
689             sub get_length_n {
690 160     160   495 return $_[0]->{length_n};
691             }
692              
693             sub get_mask_n {
694 70 100   70   70 ($_[0]->get_length_n == 0 )?
695             0 : hex('0xffffffff') << ( 32 - $_[0]->get_length_n ) ;
696             }
697              
698             sub get_mask {
699 6     6   8 my_ntoa( $_[0]->get_mask_n );
700             }
701              
702             sub get_wildcard {
703 6     6   12 my_ntoa( ~ $_[0]->get_mask_n );
704             }
705              
706             sub my_aton {
707 82 50   82   238 defined ( my $aton_str = inet_aton( $_[0] ) ) or croak '$_[0] cannot be fed to inet_aton';
708 82         233 return unpack('N',$aton_str);
709             }
710              
711             sub my_ntoa {
712 275     275   699 return inet_ntoa(pack('N',$_[0]));
713             }
714              
715             sub position {
716 45     45   38 my $self = shift;
717 45 50       58 defined ( my $arg = shift ) or die "Incorrect call";
718 45         40 my $number = my_aton($arg);
719 45 50       57 $DEBUG && print STDERR "number is ",my_ntoa($number)," and start is ",my_ntoa($self->get_start)," and stop is ",my_ntoa($self->get_stop),"\n";
720 45         47 return $number - $self->get_start;
721             }
722              
723             sub contains {
724 19 100 66 19   322 return ( ($_[0]->position($_[1]) < $_[0]->get_length) && ( $_[0]->position($_[1]) >= 0 ) )? 1 : 0;
725             }
726              
727             sub calculate_compound_offset {
728 10 50   10   27 defined( my $address = shift ) or die 'missing address';
729 10 50       45 defined( my $blocks = shift ) or die 'missing block reference';
730            
731 10         10 my $offset = 0;
732 10         5 for my $block (@{$blocks}) {
  10         18  
733 12         22 my $subnet = IPv4Subnet->new($block);
734 12 100       18 if ($subnet->contains($address)) {
735 10         11 return ( $subnet->position($address) + $offset );
736             }
737             else {
738 2         4 $offset = $offset + $subnet->get_length;
739             }
740             }
741 0           die "Address $address does not belong to range:",join(',',@{$blocks});
  0            
742 0           return;
743             }
744              
745             =head1 AUTHOR
746              
747             Athanasios Douitsis C<< >>
748              
749             =head1 SUPPORT
750              
751             Please open a ticket at L.
752              
753             =head1 COPYRIGHT & LICENSE
754            
755             Copyright 2008-2015 Athanasios Douitsis, all rights reserved.
756            
757             This program is free software; you can use it
758             under the terms of Artistic License 2.0 which can be found at
759             http://www.perlfoundation.org/artistic_license_2_0
760            
761             =cut
762              
763             1;
764            
765