File Coverage

lib/Net/Netmask.pm
Criterion Covered Total %
statement 517 538 96.1
branch 299 338 88.4
condition 107 118 90.6
subroutine 64 66 96.9
pod 33 55 60.0
total 1020 1115 91.4


line stmt bran cond sub pod time code
1             # Copyright (C) 1998-2006 David Muir Sharnoff
2             # Copyright (C) 2011-2013 Google, Inc.
3             # Copyright (C) 2018-2021 Joelle Maslak
4              
5             package Net::Netmask;
6             $Net::Netmask::VERSION = '2.0003';
7 8     8   1639755 use 5.006_001;
  8         33  
8              
9             # ABSTRACT: Understand and manipulate IP netmasks
10              
11             # Disable one-arg bless to preserve the existing interface.
12             ## no critic (ClassHierarchies::ProhibitOneArgBless)
13              
14             require Exporter;
15             @ISA = qw(Exporter);
16             @EXPORT = qw(findNetblock findOuterNetblock findAllNetblock
17             cidrs2contiglists range2cidrlist sort_by_ip_address
18             dumpNetworkTable sort_network_blocks cidrs2cidrs
19             cidrs2inverse);
20             @EXPORT_OK = (
21             @EXPORT, qw(ascii2int int2quad quad2int %quadmask2bits
22             %quadhostmask2bits imask i6mask int2ascii sameblock cmpblocks contains)
23             );
24              
25             my $remembered = {};
26             my %imask2bits;
27             my %size2bits;
28             my @imask;
29             my @i6mask;
30              
31             our $SHORTNET_DEFAULT = undef;
32              
33 8     8   56 use vars qw($error $debug %quadmask2bits %quadhostmask2bits);
  8         16  
  8         662  
34             $debug = 1;
35              
36 8     8   45 use strict;
  8         21  
  8         245  
37 8     8   37 use warnings;
  8         16  
  8         360  
38 8     8   36 use Carp;
  8         19  
  8         545  
39 8     8   10635 use Math::BigInt;
  8         313885  
  8         35  
40 8     8   220856 use POSIX qw(floor);
  8         58822  
  8         67  
41             use overload
42 8         95 '""' => \&desc,
43             '<=>' => \&cmp_net_netmask_block,
44             'cmp' => \&cmp_net_netmask_block,
45 8     8   13099 'fallback' => 1;
  8         21  
46              
47             sub new {
48 1900     1900 0 806859 my ( $package, $net, @params) = @_;
49              
50 1900         2948 my $mask = '';
51 1900 100       4670 if (@params % 2) {
52 81         155 $mask = shift(@params);
53 81 100       212 $mask = '' if !defined($mask);
54             }
55 1900         2917 my (%options) = @params;
56 1900   100     7288 my $shortnet = ( ( exists($options{shortnet}) && $options{shortnet} ) || $SHORTNET_DEFAULT );
57              
58 1900         3898 my $base;
59             my $bits;
60 1900         0 my $ibase;
61 1900         2528 my $proto = 'IPv4';
62 1900         2650 undef $error;
63              
64 1900 100 100     30123 if ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/([0-9]+)$, ) {
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 66        
    100          
    100          
    100          
65 417         1169 ( $base, $bits ) = ( $1, $2 );
66             } elsif ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)[:/]([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$, ) {
67 6         21 $base = $1;
68 6         16 my $quadmask = $2;
69 6 100       23 if ( exists $quadmask2bits{$quadmask} ) {
70 4         11 $bits = $quadmask2bits{$quadmask};
71             } else {
72 2         7 $error = "illegal netmask: $quadmask";
73             }
74             } elsif ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)[#]([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$, ) {
75 4         15 $base = $1;
76 4         11 my $hostmask = $2;
77 4 100       13 if ( exists $quadhostmask2bits{$hostmask} ) {
78 2         5 $bits = $quadhostmask2bits{$hostmask};
79             } else {
80 2         6 $error = "illegal hostmask: $hostmask";
81             }
82             } elsif ( ( $net =~ m,^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$, )
83             && ( $mask =~ m,[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$, ) )
84             {
85 7         14 $base = $net;
86 7 100       18 if ( exists $quadmask2bits{$mask} ) {
87 5         11 $bits = $quadmask2bits{$mask};
88             } else {
89 2         6 $error = "illegal netmask: $mask";
90             }
91             } elsif ( ( $net =~ m,^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$, )
92             && ( $mask =~ m,0x[a-f0-9]+,i ) )
93             {
94 6         34 $base = $net;
95 6         17 my $imask = hex($mask);
96 6 100       21 if ( exists $imask2bits{$imask} ) {
97 4         10 $bits = $imask2bits{$imask};
98             } else {
99 2         7 $error = "illegal netmask: $mask ($imask)";
100             }
101             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask ) {
102 770         1334 ( $base, $bits ) = ( $net, 32 );
103             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask && $shortnet ) {
104 2         8 ( $base, $bits ) = ( "$net.0", 24 );
105             } elsif ( $net =~ /^[0-9]+\.[0-9]+$/ && !$mask && $shortnet ) {
106 2         7 ( $base, $bits ) = ( "$net.0.0", 16 );
107             } elsif ( $net =~ /^[0-9]+$/ && !$mask && $shortnet ) {
108 2         7 ( $base, $bits ) = ( "$net.0.0.0", 8 );
109             } elsif ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+)/([0-9]+)$, && $shortnet ) {
110 2         11 ( $base, $bits ) = ( "$1.0", $2 );
111             } elsif ( $net =~ m,^([0-9]+\.[0-9]+)/([0-9]+)$, && $shortnet ) {
112 2         13 ( $base, $bits ) = ( "$1.0.0", $2 );
113             } elsif ( $net =~ m,^([0-9]+)/([0-9]+)$, && $shortnet ) {
114 2         13 ( $base, $bits ) = ( "$1.0.0.0", $2 );
115             } elsif ( $net eq 'default' || $net eq 'any' ) {
116 5         18 ( $base, $bits ) = ( "0.0.0.0", 0 );
117             } elsif ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s*-\s*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$, )
118             {
119             # whois format
120 12         40 $ibase = quad2int($1);
121 12         48 my $end = quad2int($2);
122 12 100 100     74 $error = "illegal dotted quad: $net"
123             unless defined($ibase) && defined($end);
124 12   100     134 my $diff = ( $end || 0 ) - ( $ibase || 0 ) + 1;
      100        
125 12         37 $bits = $size2bits{$diff};
126 12 100 100     66 $error = "could not find exact fit for $net"
      100        
127             if !defined $error
128             && ( !defined $bits
129             || ( $ibase & ~$imask[$bits] ) );
130             } elsif ( $net =~ m,^([0-9a-f]*:[0-9a-f]*:[0-9a-f:]*)/([0-9]+)$, ) {
131             # IPv6 with netmask - ex: 2001:db8::/32
132 326 50       869 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
133 326         1375 ( $base, $bits, $proto ) = ( $1, $2, 'IPv6' );
134             } elsif ( $net =~ m,^([0-9a-f]*:[0-9a-f]*:[0-9a-f:]*)$, ) {
135             # IPv6 without netmask - ex: 2001:db8::1234
136 272 50       931 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
137 272         1203 ( $base, $bits, $proto ) = ( $1, 128, 'IPv6' );
138             } elsif ( $net eq 'default6' || $net eq 'any6' ) {
139 4 50       17 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
140 4         13 ( $base, $bits, $proto ) = ( "::", 0, 'IPv6' );
141             } else {
142 59         146 $error = "could not parse $net";
143 59 100       135 $error .= " $mask" if $mask;
144             }
145              
146 1900 50 66     3906 carp $error if $error && $debug;
147              
148 1900 100       3750 $bits = 0 unless $bits;
149 1900 100 100     8079 if ( ( $proto eq 'IPv4' ) && ( $bits > 32 ) ) {
    100 100        
150 2 50       6 $error = "illegal number of bits: $bits"
151             unless $error;
152 2         3 $bits = 32;
153             } elsif ( ( $proto eq 'IPv6' ) && ( $bits > 128 ) ) {
154 2 50       10 $error = "illegal number of bits: $bits"
155             unless $error;
156 2         4 $bits = 128;
157             }
158              
159 1900 100 50     7718 $ibase = ascii2int( ( $base || '::' ), $proto ) unless (defined $ibase or $error);
      100        
160 1900 100 100     410716 unless ( defined($ibase) || defined($error) ) {
161 24         73 $error = "could not parse $net";
162 24 100       63 $error .= " $mask" if $mask;
163             }
164              
165 1900 100       3558 if ($error) {
166 103         142 $ibase = 0;
167 103         169 $bits = 0;
168             }
169              
170 1900         3653 $ibase = i_getnet_addr( $ibase, $bits, $proto );
171              
172 1900 100       638954 return bless {
173             'IBASE' => $ibase,
174             'BITS' => $bits,
175             'PROTOCOL' => $proto,
176             ( $error ? ( 'ERROR' => $error ) : () ),
177             };
178             }
179              
180             sub i_getnet_addr {
181 17743     17743 0 46135 my ( $ibase, $bits, $proto ) = @_;
182              
183 17743 50       38913 if ( !defined($ibase) ) { return; }
  0         0  
184              
185 17743 100       38750 if ( $proto eq 'IPv4' ) {
186 3784         6381 return $ibase & $imask[$bits];
187             } else {
188 13959         44391 return $ibase & $i6mask[$bits];
189             }
190             }
191              
192             sub new2 {
193 50     50 0 2777 goto &safe_new;
194             }
195              
196             sub safe_new {
197 105     105 0 28913 local ($debug) = 0;
198 105         302 my $net = new(@_);
199 105 100       422 return if $error;
200 53         176 return $net;
201             }
202              
203 52     52 0 22358 sub errstr { return $error; }
204 2 50   2 0 451890 sub debug { my $this = shift; return ( @_ ? $debug = shift : $debug ) }
  2         25  
205              
206 1021     1021 1 2136 sub base { my ($this) = @_; return int2ascii( $this->{IBASE}, $this->{PROTOCOL} ); }
  1021         1229  
207 25     25 1 56 sub bits { my ($this) = @_; return $this->{'BITS'}; }
  25         121  
208 21     21 1 53 sub protocol { my ($this) = @_; return $this->{'PROTOCOL'}; }
  21         100  
209              
210             sub size {
211 700     700 1 1253 my ($this) = @_;
212              
213 700 100       1510 if ( $this->{PROTOCOL} eq 'IPv4' ) {
214 392         929 return 2**( 32 - $this->{'BITS'} );
215             } else {
216 308         858 return Math::BigInt->new(2)->bpow( 128 - $this->{'BITS'} );
217             }
218             }
219              
220             sub next { ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
221 3     3 1 10 my ($this) = @_;
222             # TODO: CONSOLIDATE
223 3 100       32 if ( $this->{PROTOCOL} eq 'IPv4' ) {
224 1         4 return int2quad( $this->{'IBASE'} + $this->size() );
225             } else {
226 2         5 return $this->_ipv6next( $this->size );
227             }
228             }
229              
230             sub broadcast {
231 2     2 1 12 my ($this) = @_;
232              
233 2         6 return int2ascii( $this->{'IBASE'} + $this->size() - 1, $this->{PROTOCOL} );
234             }
235              
236             *first = \&base;
237             *last = \&broadcast;
238              
239             sub desc {
240 480     480 1 16450 return int2ascii( $_[0]->{IBASE}, $_[0]->{PROTOCOL} ) . '/' . $_[0]->{BITS};
241             }
242              
243             sub imask {
244 264     264 0 524 return ( 2**32 - ( 2**( 32 - $_[0] ) ) );
245             }
246              
247             sub i6mask {
248 1032     1032 0 1656 my $bits = shift;
249 1032         2678 return Math::BigInt->new(2)->bpow(128) - Math::BigInt->new(2)->bpow( 128 - $bits );
250             }
251              
252             sub mask {
253 21     21 1 48 my ($this) = @_;
254              
255 21 100       72 if ( $this->{PROTOCOL} eq 'IPv4' ) {
256 15         56 return int2quad( $imask[ $this->{'BITS'} ] );
257             } else {
258 6         30 return int2ascii( $i6mask[ $this->{'BITS'} ], $this->{PROTOCOL} );
259             }
260             }
261              
262             sub hostmask {
263 4     4 1 7534 my ($this) = @_;
264              
265 4 100       19 if ( $this->{PROTOCOL} eq 'IPv4' ) {
266 1         7 return int2quad( ~$imask[ $this->{BITS} ] );
267             } else {
268 3         18 return int2ascii( $i6mask[ $this->{BITS} ] ^ $i6mask[128], $this->{PROTOCOL} );
269             }
270             }
271              
272             sub nth {
273 528     528 1 160555 my ( $this, $index, $bitstep ) = @_;
274              
275 528 100       1287 my $maxbits = $this->{PROTOCOL} eq 'IPv4' ? 32 : 128;
276              
277 528         998 my $size = $this->size();
278 528         140231 my $ibase = $this->{'IBASE'};
279 528 100       1175 $bitstep = $maxbits unless $bitstep;
280 528         886 my $increment = 2**( $maxbits - $bitstep );
281 528         1090 $index *= $increment;
282 528 100       68324 $index += $size if $index < 0;
283 528 100       47342 return if $index < 0;
284 526 100       46340 return if $index >= $size;
285              
286 524         9868 my $i = $ibase + $index;
287 524         33959 return int2ascii( $i, $this->{PROTOCOL} );
288             }
289              
290             sub enumerate {
291 5     5 1 2286 my ( $this, $bitstep ) = @_;
292 5         13 my $proto = $this->{PROTOCOL};
293              
294             # Set default step size by protocol
295 5 100       22 $bitstep = ( $proto eq 'IPv4' ? 32 : 128 ) unless $bitstep;
    100          
296              
297 5         18 my $size = $this->size();
298              
299 5         1971 my @ary;
300             ### We should be able to consolidate this
301 5 100       16 if ( $proto eq 'IPv4' ) {
302 3         7 my $increment = 2**( 32 - $bitstep );
303 3         8 my $ibase = $this->{'IBASE'};
304 3         31 for ( my $i = 0; $i < $size; $i += $increment ) {
305 8240         20066 push( @ary, int2quad( $ibase + $i ) );
306             }
307             } else {
308 2         37 my $increment = Math::BigInt->new(2)->bpow( 128 - $bitstep );
309              
310 2 100       1365 if ( ( $size / $increment ) > 1_000_000_000 ) {
311             # Let's help the user out and catch really obvious issues.
312             # Asking for a billion IP addresses is probably one of them.
313             #
314             # That said, please contact the author if this number causes
315             # you issues!
316 1         1028 confess("More than 1,000,000,000 results would be returned, dieing");
317             }
318              
319 1         729 for ( my $i = Math::BigInt->new(0); $i < $size; $i += $increment ) {
320 256         53765 push( @ary, $this->_ipv6next($i) );
321             }
322             }
323 4         3227 return @ary;
324             }
325              
326             sub _ipv6next {
327 258     258   1736 my ( $this, $bitstep ) = @_;
328              
329 258         544 my $istart = $this->{IBASE};
330 258         742 my $val = $istart + $bitstep;
331              
332 258         42000 return ipv6Cannonical( int2ascii( $val, $this->{PROTOCOL} ) );
333             }
334              
335             sub inaddr {
336 8     8 1 1660 my ($this) = @_;
337              
338 8 100       29 if ( $this->{PROTOCOL} eq 'IPv4' ) {
339 4         13 return $this->inaddr4();
340             } else {
341 4         19 return $this->inaddr6();
342             }
343             }
344              
345             sub inaddr4 {
346 4     4 0 8 my ($this) = @_;
347 4         8 my $ibase = $this->{'IBASE'};
348 4         11 my $blocks = floor( $this->size() / 256 );
349             return (
350 4 100       36 join( '.', unpack( 'xC3', pack( 'V', $ibase ) ) ) . ".in-addr.arpa",
351             $ibase % 256,
352             $ibase % 256 + $this->size() - 1
353             ) if $blocks == 0;
354 1         3 my @ary;
355 1         5 for ( my $i = 0; $i < $blocks; $i++ ) {
356 32         183 push( @ary,
357             join( '.', unpack( 'xC3', pack( 'V', $ibase + $i * 256 ) ) ) . ".in-addr.arpa",
358             0, 255 );
359             }
360 1         24 return @ary;
361             }
362              
363             sub inaddr6 {
364 4     4 0 11 my ($this) = @_;
365              
366 4         19 my (@digits) = split //, $this->{IBASE}->to_hex;
367              
368 4         1565 my $static = floor( $this->{BITS} / 4 );
369 4         15 my $len = floor( ( $static + 3 ) / 4 );
370 4         12 my $remainder = $this->{BITS} % 4;
371 4 100       19 my $blocks = $remainder ? ( 2**( 4 - $remainder ) ) : 1;
372              
373 4         7 my @tail;
374 4 100       14 if ( !$len ) {
375             # Specal case: 0 len
376 1         18 return ('ip6.arpa');
377             }
378 3         36 push @tail, reverse( @digits[ 0 .. ( $static - 1 ) ] ), 'ip6.arpa';
379              
380 3 100       14 if ( !$remainder ) {
381             # Special case - at nibble boundary already
382 2         50 return ( join '.', @tail );
383             }
384              
385 1         3 my $last = hex $digits[$static];
386 1         3 my @ary;
387 1         7 for ( my $i = 0; $i < $blocks; $i++ ) {
388 8         32 push @ary, join( '.', sprintf( "%x", $last ), @tail );
389 8         19 $last++;
390             }
391              
392 1         12 return @ary;
393             }
394              
395             sub tag {
396 11     11 1 234 my $this = shift;
397 11         20 my $tag = shift;
398 11         24 my $val = $this->{ 'T' . $tag };
399 11 100       39 $this->{ 'T' . $tag } = $_[0] if @_;
400 11         37 return $val;
401             }
402              
403             sub quad2int {
404 1336     1336 0 3301 my @bytes = split( /\./, $_[0] );
405              
406 1336 100       2541 return unless @bytes == 4;
407 1335 100 100     3180 return unless !grep { !( /^(([0-9])|([1-9][0-9]*))$/ && $_ < 256 ) } @bytes;
  5340         18639  
408              
409 1307         4158 return unpack( "N", pack( "C4", @bytes ) );
410             }
411              
412             sub int2quad {
413 8785     8785 0 56211 return join( '.', unpack( 'C4', pack( "N", $_[0] ) ) );
414             }
415              
416             # Uses the internal "raw" representation (such as IBASE).
417             # For IPv4, this is an integer
418             # For IPv6, this is a raw bit string.
419             sub int2ascii {
420 2312 100   2312 0 241994 if ( $_[1] eq 'IPv4' ) {
    50          
421 1611         7668 return join( '.', unpack( 'C4', pack( "N", $_[0] ) ) );
422             } elsif ( $_[1] eq 'IPv6' ) {
423 701 100       2823 my $addr = ( ref $_[0] ) ne '' ? $_[0]->to_hex : Math::BigInt->new( $_[0] )->to_hex;
424 701         283016 return ipv6Cannonical($addr);
425             } else {
426 0         0 confess("Incorrect call");
427             }
428             }
429              
430             # Produces the internal "raw" representation (such as IBASE).
431             # For IPv4, this is an integer
432             # For IPv6, this is a raw bit string.
433             sub ascii2int {
434 1946 100   1946 0 7615 if ( $_[1] eq 'IPv4' ) {
    50          
435 1312         2173 return quad2int( $_[0] );
436             } elsif ( $_[1] eq 'IPv6' ) {
437 634         1588 return ipv6ascii2int( $_[0] );
438             } else {
439 0         0 confess("Incorrect call");
440             }
441             }
442              
443             # Take an IPv6 ASCII address and produce a raw value
444             sub ipv6ascii2int {
445 634     634 0 1434 my $addr = shift;
446              
447 634         1618 $addr = ipv6NonCompacted($addr);
448 634         2541 $addr = join '', map { sprintf( "%04x", hex($_) ) } split( /:/, $addr );
  5072         13237  
449              
450 634         3813 return Math::BigInt->from_hex($addr);
451             }
452              
453             # Takes an IPv6 address and produces a standard version seperated by
454             # colons (without compacting)
455             sub ipv6NonCompacted {
456 1610     1610 0 2597 my $addr = shift;
457              
458 1610 100       5522 if ( $addr !~ /:/ ) {
459 701 100       1589 if ( length($addr) < 32 ) {
460 37         117 $addr = ( "0" x ( 32 - length($addr) ) ) . $addr;
461             }
462 701         11573 $addr =~ s/(....)(?=....)/$1:/gsx;
463             }
464              
465             # Handle address format with trailing IPv6
466             # Ex: 0:0:0:0:1.2.3.4
467 1610 100       4386 if ( $addr =~ m/^[0-9a-f:]+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/i ) {
468 2         16 my ( $l, $r1, $r2, $r3, $r4 ) =
469             $addr =~ m/^([0-9a-f:]+)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/i;
470 2         18 $addr = sprintf( "%s%02x%02x:%02x%02x", $l, $r1, $r2, $r3, $r4 );
471             }
472              
473 1610         5908 my ( $left, $right ) = split /::/, $addr;
474 1610 100       3726 if ( !defined($right) ) { $right = '' }
  724         1221  
475 1610         4675 my (@lparts) = split /:/, $left;
476 1610         2916 my (@rparts) = split /:/, $right;
477              
478             # Strip leading 0's & lowercase
479 1610         3559 @lparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @lparts;
  8156         20948  
  8156         19811  
480 1610         3077 @rparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @rparts;
  562         944  
  562         1380  
481              
482             # Expand ::
483 1610         3122 my $missing = 8 - ( @lparts + @rparts );
484 1610 100       3621 if ($missing) {
485 887         4725 $addr = join ':', @lparts, ( 0, 0, 0, 0, 0, 0, 0, 0 )[ 0 .. $missing - 1 ], @rparts;
486             } else {
487 723         2206 $addr = join ':', @lparts, @rparts;
488             }
489              
490 1610         5649 return $addr;
491             }
492              
493             # Compacts an IPv6 address (reduces successive :0: runs)
494             sub ipv6AsciiCompact {
495 976     976 0 1558 my $addr = shift;
496              
497             # Compress, per RFC5952
498 976 100       9238 if ( $addr =~ s/^0:0:0:0:0:0:0:0$/::/ ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
499 18         46 return $addr;
500             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0:0:0(:?:|$)/::/ ) {
501 2         9 return $addr;
502             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0:0(:?:|$)/::/ ) {
503 11         33 return $addr;
504             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0(:?:|$)/::/ ) {
505 285         778 return $addr;
506             } elsif ( $addr =~ s/(:?^|:)0:0:0:0(:?:|$)/::/ ) {
507 567         1645 return $addr;
508             } elsif ( $addr =~ s/(:?^|:)0:0:0(:?:|$)/::/ ) {
509 39         96 return $addr;
510             } elsif ( $addr =~ s/(:?^|:)0:0(:?:|$)/::/ ) {
511 29         69 return $addr;
512             } elsif ( $addr =~ s/(:?^|:)0(:?:|$)/::/ ) {
513 16         43 return $addr;
514             }
515 9         27 return $addr;
516             }
517             # Cannonicalize IPv6 addresses in ascii format
518             sub ipv6Cannonical {
519 976     976 0 220131 my $addr = shift;
520              
521 976         4724 $addr = ipv6NonCompacted($addr);
522 976         2199 $addr = ipv6AsciiCompact($addr);
523              
524 976         6797 return $addr;
525             }
526              
527             # IPv6 addresses are stored with a leading zero.
528             sub storeNetblock {
529 46     46 1 1107 my ( $this, $t ) = @_;
530 46 100       106 $t = $remembered unless $t;
531              
532 46         82 my $base = $this->{'IBASE'};
533 46 100       157 if ( $this->{PROTOCOL} eq 'IPv6' ) {
534 14         65 $base = "0$base";
535             }
536              
537 46 100       843 $t->{$base} = [] unless exists $t->{$base};
538              
539 46         118 my $mb = maxblock($this);
540 46         89 my $bits = $this->{'BITS'};
541 46         64 my $i = $bits - $mb;
542              
543 46         150 return ( $t->{$base}[$i] = $this );
544             }
545              
546             sub deleteNetblock {
547 13     13 1 36 my ( $this, $t ) = @_;
548 13 50       42 $t = $remembered unless $t;
549              
550 13         29 my $base = $this->{'IBASE'};
551 13 100       40 if ( $this->{PROTOCOL} eq 'IPv6' ) {
552 1         7 $base = "0$base";
553             }
554              
555 13         88 my $mb = maxblock($this);
556 13         27 my $bits = $this->{'BITS'};
557 13         25 my $i = $bits - $mb;
558              
559 13 50       45 return unless defined $t->{$base};
560              
561 13         35 undef $t->{$base}->[$i];
562              
563 13         23 for my $x ( @{ $t->{$base} } ) {
  13         45  
564 46 100       130 return if $x;
565             }
566 10         38 return delete $t->{$base};
567             }
568              
569             sub findNetblock {
570 56     56 1 7499 my ( $ascii, $t ) = @_;
571 56 100       156 $t = $remembered unless $t;
572              
573 56 100       194 my $proto = ( $ascii =~ m/:/ ) ? 'IPv6' : 'IPv4';
574              
575 56         158 my $ip = ascii2int( $ascii, $proto );
576 56 100       4553 return unless defined $ip;
577 55         86 my %done;
578              
579 55 100       129 my $maxbits = $proto eq 'IPv6' ? 128 : 32;
580 55         157 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
581 1251         2291 my $nb = i_getnet_addr( $ip, $bits, $proto );
582 1251 100       464729 if ( $proto eq 'IPv6' ) {
583 568         1880 $nb = "0$nb";
584             }
585 1251 100       27540 next unless exists $t->{$nb};
586 44         98 my $mb = imaxblock( $nb, $maxbits, $proto );
587 44 100       199 next if $done{$mb}++;
588 41         64 my $i = $bits - $mb;
589 41         95 while ( $i >= 0 ) {
590             return $t->{$nb}->[$i]
591 308 100       664 if defined $t->{$nb}->[$i];
592 268         410 $i--;
593             }
594             }
595 15         112 return;
596             }
597              
598             sub findOuterNetblock {
599 56     56 1 1924 my ( $ipstr, $t ) = @_;
600 56 50       123 $t = $remembered unless $t;
601              
602 56         157 my $proto;
603             my $maxbits;
604              
605 56         0 my $ip;
606 56         0 my $len;
607 56 100       109 if ( ref($ipstr) ) {
608 28         66 $proto = $ipstr->{PROTOCOL};
609 28 100       69 $maxbits = $proto eq 'IPv4' ? 32 : 128;
610 28         55 $ip = $ipstr->{IBASE};
611 28         49 $len = $ipstr->{BITS};
612             } else {
613 28 100       71 $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
614 28 100       45 $maxbits = $proto eq 'IPv4' ? 32 : 128;
615 28         56 $ip = ascii2int( $ipstr, $proto );
616 28         2616 $len = $maxbits;
617             }
618              
619 56         155 for ( my $bits = 0; $bits <= $len; $bits++ ) {
620 2028 100       4229 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
621 2028 100       483975 if ( $proto eq 'IPv6' ) {
622 646         1789 $nb = "0$nb";
623             }
624 2028 100       30313 next unless exists $t->{$nb};
625 74         140 my $mb = imaxblock( $nb, $len, $proto );
626 74         108 my $i = $bits - $mb;
627 74 50       132 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
628 74 50       173 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
629 74         144 while ( $i >= 0 ) {
630             return $t->{$nb}->[$i]
631 173 100       399 if defined $t->{$nb}->[$i];
632 151         275 $i--;
633             }
634             }
635 34         118 return;
636             }
637              
638             sub findAllNetblock {
639 2     2 1 13 my ( $ipstr, $t ) = @_;
640 2 50       7 $t = $remembered unless $t;
641              
642 2 100       11 my $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
643 2 100       6 my $maxbits = $proto eq 'IPv4' ? 32 : 128;
644              
645 2         7 my $ip = ascii2int( $ipstr, $proto );
646              
647 2         393 my %done;
648             my @ary;
649 2         7 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
650 162 100       899 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
651 162 100       138189 if ( $proto eq 'IPv6' ) {
652 129         742 $nb = "0$nb";
653             }
654 162 100       9283 next unless exists $t->{$nb};
655 109         415 my $mb = imaxblock( $nb, $maxbits, $proto );
656 109 100       1072 next if $done{$mb}++;
657 3         8 my $i = $bits - $mb;
658 3 50       11 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
659 3 50       10 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
660 3         11 while ( $i >= 0 ) {
661             push( @ary, $t->{$nb}->[$i] )
662 109 100       149 if defined $t->{$nb}->[$i];
663 109         166 $i--;
664             }
665             }
666 2         145 return @ary;
667             }
668              
669             sub dumpNetworkTable {
670 1     1 1 2 my ($t) = @_;
671 1 50       3 $t = $remembered unless $t;
672              
673 1         1 my @ary;
674 1         5 foreach my $base ( keys %$t ) {
675 6         6 push @ary, grep { defined($_) } @{ $t->{base} };
  0         0  
  6         24  
676 6         7 for my $x ( @{ $t->{$base} } ) {
  6         9  
677 19 100       31 push( @ary, $x )
678             if defined $x;
679             }
680             }
681              
682 1         5 return ( sort @ary );
683             }
684              
685             sub checkNetblock {
686 0     0 1 0 my ( $this, $t ) = @_;
687 0 0       0 $t = $remembered unless $t;
688              
689 0         0 my $base = $this->{'IBASE'};
690              
691 0         0 my $mb = maxblock($this);
692 0         0 my $bits = $this->{'BITS'};
693 0         0 my $i = $bits - $mb;
694              
695 0         0 return defined $t->{$base}->[$i];
696             }
697              
698             sub match {
699 24     24 1 2599 my ( $this, $ip ) = @_;
700 24         34 my $proto = $this->{PROTOCOL};
701              
702             # Two different protocols: return undef
703 24 100       71 if ( $ip =~ /:/ ) {
704 10 50       35 if ( $proto ne 'IPv6' ) { return }
  0         0  
705             } else {
706 14 50       28 if ( $proto ne 'IPv4' ) { return }
  0         0  
707             }
708              
709 24         55 my $i = ascii2int( $ip, $this->{PROTOCOL} );
710 24         4982 my $ia = i_getnet_addr( $i, $this->{BITS}, $proto );
711              
712 24 100       6450 if ( $proto eq 'IPv4' ) {
713 14 100       26 if ( $ia == $this->{IBASE} ) {
714 8   100     43 return ( ( $i & ~( $this->{IBASE} ) ) || "0 " );
715             } else {
716 6         22 return 0;
717             }
718             } else {
719 10 100       31 if ( $ia == $this->{IBASE} ) {
720 6   100     349 return ( ( $i - $this->{IBASE} ) || "0 " );
721             } else {
722 4         183 return 0;
723             }
724             }
725             }
726              
727             sub maxblock {
728 80     80 1 158 my ($this) = @_;
729             return ( !defined $this->{ERROR} )
730             ? imaxblock( $this->{IBASE}, $this->{BITS}, $this->{PROTOCOL} )
731 80 50       366 : undef;
732             }
733              
734             sub nextblock {
735 8     8 1 2072 my ( $this, $index ) = @_;
736 8 100       18 $index = 1 unless defined $index;
737 8         14 my $ibase = $this->{IBASE};
738 8 100       14 if ( $this->{PROTOCOL} eq 'IPv4' ) {
739 4         14 $ibase += $index * 2**( 32 - $this->{BITS} );
740             } else {
741 4         12 $ibase += $index * Math::BigInt->new(2)->bpow( 128 - $this->{BITS} );
742             }
743              
744             my $newblock = bless {
745             IBASE => $ibase,
746             BITS => $this->{BITS},
747             PROTOCOL => $this->{PROTOCOL},
748 8         3063 };
749              
750 8 100       15 if ( $this->{PROTOCOL} eq 'IPv4' ) {
751 4 50       11 return if $newblock->{IBASE} >= 2**32;
752             } else {
753 4 50       11 return if $newblock->{IBASE} >= Math::BigInt->new(2)->bpow(128);
754             }
755              
756 8 50       2006 return if $newblock->{IBASE} < 0;
757 8         527 return $newblock;
758             }
759              
760             sub imaxblock {
761 427     427 0 1109 my ( $ibase, $tbit, $proto ) = @_;
762 427 50       1250 confess unless defined $ibase;
763              
764 427 50       1000 if ( !defined($proto) ) { $proto = 'IPv4'; }
  0         0  
765              
766 427         980 while ( $tbit > 0 ) {
767 14568         44291 my $ia = i_getnet_addr( $ibase, $tbit - 1, $proto );
768 14568 100       13498046 last if ( $ia != $ibase );
769 14153         2415959 $tbit--;
770             }
771 427         31881 return $tbit;
772             }
773              
774             sub range2cidrlist {
775 5     5 1 2728 my ( $startip, $endip ) = @_;
776              
777 5         10 my $proto;
778 5 100       24 if ( $startip =~ m/:/ ) {
779 2 50       9 if ( $endip =~ m/:/ ) { $proto = 'IPv6'; }
  2         4  
780             } else {
781 3 50       10 if ( $endip !~ m/:/ ) { $proto = 'IPv4'; }
  3         8  
782             }
783 5 50       32 if ( !defined($proto) ) { confess("Cannot mix IPv4 and IPv6 in range2cidrlist()"); }
  0         0  
784              
785 5         17 my $start = ascii2int( $startip, $proto );
786 5         1573 my $end = ascii2int( $endip, $proto );
787              
788 5 100       1137 ( $start, $end ) = ( $end, $start )
789             if $start > $end;
790 5         110 return irange2cidrlist( $start, $end, $proto );
791             }
792              
793             sub irange2cidrlist {
794 39     39 0 446 my ( $start, $end, $proto ) = @_;
795 39 50       87 if ( !defined($proto) ) { $proto = 'IPv4' }
  0         0  
796              
797 39 100       94 my $bits = $proto eq 'IPv4' ? 32 : 128;
798              
799 39         57 my @result;
800 39         88 while ( $end >= $start ) {
801 120         62370 my $maxsize = imaxblock( $start, $bits, $proto );
802 120         249 my $maxdiff;
803 120 100       339 if ( $proto eq 'IPv4' ) {
804 40         110 $maxdiff = $bits - _log2( $end - $start + 1 );
805             } else {
806 80         375 $maxdiff = $bits - ( $end - $start + 1 )->blog(2);
807             }
808 120 100       140404 $maxsize = $maxdiff if $maxsize < $maxdiff;
809 120         11663 push(
810             @result,
811             bless {
812             'IBASE' => $start,
813             'BITS' => $maxsize,
814             'PROTOCOL' => $proto,
815             }
816             );
817 120 100       366 if ( $proto eq 'IPv4' ) {
818 40         109 $start += 2**( 32 - $maxsize );
819             } else {
820 80         328 $start += Math::BigInt->new(2)->bpow( $bits - $maxsize );
821             }
822             }
823 39         7276 return @result;
824             }
825              
826             sub cidrs2contiglists {
827 1     1 1 318 my (@cidrs) = sort_network_blocks(@_);
828 1         4 my @result;
829 1         5 while (@cidrs) {
830 2         4 my (@r) = shift(@cidrs);
831 2         9 my $max = $r[0]->{IBASE} + $r[0]->size;
832 2   100     7 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
833 1         6 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
834 1 50       4 $max = $nm if $nm > $max;
835 1         3 push( @r, shift(@cidrs) );
836             }
837 2         6 push( @result, [@r] );
838             }
839 1         3 return @result;
840             }
841              
842             sub cidrs2cidrs {
843 15     15 1 51 my (@cidrs) = sort_network_blocks(@_);
844 15         56 my @result;
845              
846             my $proto;
847 15 50       39 if ( scalar(@cidrs) ) {
848 15         30 $proto = $cidrs[0]->{PROTOCOL};
849 15 50       28 if ( grep { $proto ne $_->{PROTOCOL} } @cidrs ) {
  49         130  
850 0         0 confess("Cannot call cidrs2cidrs with mixed protocol arguments");
851             }
852             }
853              
854 15         33 while (@cidrs) {
855 24         48 my (@r) = shift(@cidrs);
856              
857 24         111 my $max = $r[0]->{IBASE} + $r[0]->size;
858 24   100     3765 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
859 25         327 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
860 25 100       2071 $max = $nm if $nm > $max;
861 25         261 push( @r, shift(@cidrs) );
862             }
863 24         153 my $start = $r[0]->{IBASE};
864 24         76 my $end = $max - 1;
865 24         1535 push( @result, irange2cidrlist( $start, $end, $proto ) );
866             }
867 15         224 return @result;
868             }
869              
870             sub cidrs2inverse {
871 10     10 1 31 my $outer = shift;
872 10 100 33     34 $outer = __PACKAGE__->new2($outer) || croak($error) unless ref($outer);
873              
874             # cidrs2cidrs validates that everything is in the same address
875             # family
876 10         27 my (@cidrs) = cidrs2cidrs(@_);
877 10         15 my $proto;
878 10 50       22 if ( scalar(@cidrs) ) {
879 10         21 $proto = $cidrs[0]->{PROTOCOL};
880             }
881              
882 10         16 my $first = $outer->{IBASE};
883 10         26 my $last = $first + $outer->size() - 1;
884 10   66     1226 shift(@cidrs) while $cidrs[0] && $cidrs[0]->{IBASE} + $cidrs[0]->size < $first;
885 10         1111 my @r;
886 10   66     55 while ( @cidrs && $first <= $last ) {
887              
888 16 100       176 if ( $first < $cidrs[0]->{IBASE} ) {
889 6 100       80 if ( $last <= $cidrs[0]->{IBASE} - 1 ) {
890 2         8 return ( @r, irange2cidrlist( $first, $last, $proto ) );
891             }
892 4         532 push( @r, irange2cidrlist( $first, $cidrs[0]->{IBASE} - 1, $proto ) );
893             }
894 14 50       100 last if $cidrs[0]->{IBASE} > $last;
895 14         167 $first = $cidrs[0]->{IBASE} + $cidrs[0]->size;
896 14         1800 shift(@cidrs);
897             }
898 8 100       19 if ( $first <= $last ) {
899 4         83 push( @r, irange2cidrlist( $first, $last, $proto ) );
900             }
901 8         53 return @r;
902             }
903              
904             sub by_net_netmask_block {
905             return $a->{'IBASE'} <=> $b->{'IBASE'}
906 0   0 0 0 0 || $a->{'BITS'} <=> $b->{'BITS'};
907             }
908              
909             sub sameblock {
910 29     29 1 64 return !cmpblocks(@_);
911             }
912              
913             sub cmpblocks {
914 45     45 1 86 my $this = shift;
915 45         78 my $class = ref $this;
916 45 100       138 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
917 45         100 return cmp_net_netmask_block( $this, $other );
918             }
919              
920             sub contains {
921 66     66 1 436 my $this = shift;
922 66         137 my $class = ref $this;
923 66 100       137 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
924 66 100       222 return 0 if $this->{IBASE} > $other->{IBASE};
925 51 100       1266 return 0 if $this->{BITS} > $other->{BITS};
926 46 100       134 return 0 if $other->{IBASE} > $this->{IBASE} + $this->size - 1;
927 40         17898 return 1;
928             }
929              
930             sub cmp_net_netmask_block {
931 7838 100 100 7838 0 15686 if ( ( $_[0]->{PROTOCOL} eq 'IPv4' ) && ( $_[1]->{PROTOCOL} eq 'IPv4' ) ) {
    100 100        
932             # IPv4
933 7797   100     13055 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
934             } elsif ( ( $_[0]->{PROTOCOL} eq 'IPv6' ) && ( $_[1]->{PROTOCOL} eq 'IPv6' ) ) {
935             # IPv6
936 36   100     115 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
937             } else {
938             # IPv4 to IPv6, order by protocol
939 5         11 return ( $_[0]->{PROTOCOL} cmp $_[1]->{PROTOCOL} );
940             }
941             }
942              
943             sub sort_network_blocks {
944 52         231 return map { $_->[0] }
945 64 50 66     612 sort { $a->[3] cmp $b->[3] || $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
946 16     16 1 32 map { [ $_, $_->{IBASE}, $_->{BITS}, $_->{PROTOCOL} ] } @_;
  52         209  
947             }
948              
949             sub sort_by_ip_address {
950 500         565 return map { $_->[0] }
951 3873         3599 sort { $a->[1] cmp $b->[1] }
952 1     1 1 4 map { [ $_, pack( "C4", split( /\./, $_ ) ) ] } @_;
  500         1437  
953              
954             }
955              
956             sub split ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
957             {
958 14     14 1 19558 my ( $self, $parts ) = @_;
959              
960 14         50 my $num_ips = $self->size;
961              
962 14 100 100     7354 confess "Parts must be defined and greater than 0."
963             unless defined($parts) && $parts > 0;
964              
965 8 100       374 confess "Netmask only contains $num_ips IPs. Cannot split into $parts."
966             unless $num_ips >= $parts;
967              
968 6         754 my $log2 = _log2($parts);
969              
970 6 100       722 confess "Parts count must be a number of base 2. Got: $parts"
971             unless ( 2**$log2 ) == $parts;
972              
973 4         17 my $new_mask = $self->bits + $log2;
974              
975 516         1793 return map { Net::Netmask->new( $_ . "/" . $new_mask ) }
976 4         68 map { $self->nth( ( $num_ips / $parts ) * $_ ) } ( 0 .. ( $parts - 1 ) );
  516         1461  
977             }
978              
979             # Implement log2 sub routine directly, to avoid precision problems with floor()
980             # problems with perls built with uselongdouble defined.
981             # Credit: xenu, on IRC
982             sub _log2 {
983 46     46   123 my $n = shift;
984              
985 46         61 my $ret = 0;
986 46         248 $ret++ while ( $n >>= 1 );
987              
988 46         76 return $ret;
989             }
990              
991             BEGIN {
992 8     8   65781 for ( my $i = 0; $i <= 32; $i++ ) {
993 264         417 $imask[$i] = imask($i);
994 264         669 $imask2bits{ $imask[$i] } = $i;
995 264         408 $quadmask2bits{ int2quad( $imask[$i] ) } = $i;
996 264         482 $quadhostmask2bits{ int2quad( ~$imask[$i] ) } = $i;
997 264         1202 $size2bits{ 2**( 32 - $i ) } = $i;
998             }
999              
1000 8         64 for ( my $i = 0; $i <= 128; $i++ ) {
1001 1032         1505294 $i6mask[$i] = i6mask($i);
1002             }
1003             }
1004             1;