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.0001';
7 8     8   1834908 use 5.006_001;
  8         69  
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   60 use vars qw($error $debug %quadmask2bits %quadhostmask2bits);
  8         17  
  8         709  
34             $debug = 1;
35              
36 8     8   53 use strict;
  8         16  
  8         242  
37 8     8   46 use warnings;
  8         19  
  8         261  
38 8     8   46 use Carp;
  8         20  
  8         555  
39 8     8   8953 use Math::BigInt;
  8         198829  
  8         43  
40 8     8   192335 use POSIX qw(floor);
  8         19  
  8         91  
41             use overload
42 8         97 '""' => \&desc,
43             '<=>' => \&cmp_net_netmask_block,
44             'cmp' => \&cmp_net_netmask_block,
45 8     8   14400 'fallback' => 1;
  8         21  
46              
47             sub new {
48 1900     1900 0 74052 my ( $package, $net, @params) = @_;
49              
50 1900         2898 my $mask = '';
51 1900 100       4588 if (@params % 2) {
52 81         148 $mask = shift(@params);
53 81 100       245 $mask = '' if !defined($mask);
54             }
55 1900         3012 my (%options) = @params;
56 1900   100     7131 my $shortnet = ( ( exists($options{shortnet}) && $options{shortnet} ) || $SHORTNET_DEFAULT );
57              
58 1900         4080 my $base;
59             my $bits;
60 1900         0 my $ibase;
61 1900         2621 my $proto = 'IPv4';
62 1900         2642 undef $error;
63              
64 1900 100 100     26774 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         1207 ( $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         18 $base = $1;
68 6         15 my $quadmask = $2;
69 6 100       24 if ( exists $quadmask2bits{$quadmask} ) {
70 4         12 $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         16 $base = $1;
76 4         11 my $hostmask = $2;
77 4 100       13 if ( exists $quadhostmask2bits{$hostmask} ) {
78 2         7 $bits = $quadhostmask2bits{$hostmask};
79             } else {
80 2         8 $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         15 $base = $net;
86 7 100       18 if ( exists $quadmask2bits{$mask} ) {
87 5         12 $bits = $quadmask2bits{$mask};
88             } else {
89 2         7 $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         13 $base = $net;
95 6         17 my $imask = hex($mask);
96 6 100       19 if ( exists $imask2bits{$imask} ) {
97 4         13 $bits = $imask2bits{$imask};
98             } else {
99 2         10 $error = "illegal netmask: $mask ($imask)";
100             }
101             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask ) {
102 770         1707 ( $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         9 ( $base, $bits ) = ( "$net.0.0", 16 );
107             } elsif ( $net =~ /^[0-9]+$/ && !$mask && $shortnet ) {
108 2         9 ( $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         11 ( $base, $bits ) = ( "$1.0.0.0", $2 );
115             } elsif ( $net eq 'default' || $net eq 'any' ) {
116 5         16 ( $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         33 $ibase = quad2int($1);
121 12         27 my $end = quad2int($2);
122 12 100 100     53 $error = "illegal dotted quad: $net"
123             unless defined($ibase) && defined($end);
124 12   100     46 my $diff = ( $end || 0 ) - ( $ibase || 0 ) + 1;
      100        
125 12         34 $bits = $size2bits{$diff};
126 12 100 100     58 $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       861 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
133 326         1302 ( $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       668 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
137 272         884 ( $base, $bits, $proto ) = ( $1, 128, 'IPv6' );
138             } elsif ( $net eq 'default6' || $net eq 'any6' ) {
139 4 50       15 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
140 4         12 ( $base, $bits, $proto ) = ( "::", 0, 'IPv6' );
141             } else {
142 59         155 $error = "could not parse $net";
143 59 100       137 $error .= " $mask" if $mask;
144             }
145              
146 1900 50 66     3786 carp $error if $error && $debug;
147              
148 1900 100       3552 $bits = 0 unless $bits;
149 1900 100 100     8417 if ( ( $proto eq 'IPv4' ) && ( $bits > 32 ) ) {
    100 100        
150 2 50       9 $error = "illegal number of bits: $bits"
151             unless $error;
152 2         5 $bits = 32;
153             } elsif ( ( $proto eq 'IPv6' ) && ( $bits > 128 ) ) {
154 2 50       8 $error = "illegal number of bits: $bits"
155             unless $error;
156 2         4 $bits = 128;
157             }
158              
159 1900 100 50     8250 $ibase = ascii2int( ( $base || '::' ), $proto ) unless (defined $ibase or $error);
      100        
160 1900 100 100     231790 unless ( defined($ibase) || defined($error) ) {
161 24         58 $error = "could not parse $net";
162 24 100       51 $error .= " $mask" if $mask;
163             }
164              
165 1900 100       3458 if ($error) {
166 103         145 $ibase = 0;
167 103         123 $bits = 0;
168             }
169              
170 1900         3642 $ibase = i_getnet_addr( $ibase, $bits, $proto );
171              
172 1900 100       537256 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 31224 my ( $ibase, $bits, $proto ) = @_;
182              
183 17743 50       33264 if ( !defined($ibase) ) { return; }
  0         0  
184              
185 17743 100       32125 if ( $proto eq 'IPv4' ) {
186 3784         6701 return $ibase & $imask[$bits];
187             } else {
188 13959         36773 return $ibase & $i6mask[$bits];
189             }
190             }
191              
192             sub new2 {
193 50     50 0 5399 goto &safe_new;
194             }
195              
196             sub safe_new {
197 105     105 0 20431 local ($debug) = 0;
198 105         254 my $net = new(@_);
199 105 100       383 return if $error;
200 53         176 return $net;
201             }
202              
203 52     52 0 18713 sub errstr { return $error; }
204 2 50   2 0 196 sub debug { my $this = shift; return ( @_ ? $debug = shift : $debug ) }
  2         19  
205              
206 1021     1021 1 3125 sub base { my ($this) = @_; return int2ascii( $this->{IBASE}, $this->{PROTOCOL} ); }
  1021         1678  
207 25     25 1 66 sub bits { my ($this) = @_; return $this->{'BITS'}; }
  25         140  
208 21     21 1 70 sub protocol { my ($this) = @_; return $this->{'PROTOCOL'}; }
  21         119  
209              
210             sub size {
211 700     700 1 1208 my ($this) = @_;
212              
213 700 100       1387 if ( $this->{PROTOCOL} eq 'IPv4' ) {
214 392         964 return 2**( 32 - $this->{'BITS'} );
215             } else {
216 308         766 return Math::BigInt->new(2)->bpow( 128 - $this->{'BITS'} );
217             }
218             }
219              
220             sub next { ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
221 3     3 1 13 my ($this) = @_;
222             # TODO: CONSOLIDATE
223 3 100       12 if ( $this->{PROTOCOL} eq 'IPv4' ) {
224 1         4 return int2quad( $this->{'IBASE'} + $this->size() );
225             } else {
226 2         6 return $this->_ipv6next( $this->size );
227             }
228             }
229              
230             sub broadcast {
231 2     2 1 17 my ($this) = @_;
232              
233 2         12 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 21146 return int2ascii( $_[0]->{IBASE}, $_[0]->{PROTOCOL} ) . '/' . $_[0]->{BITS};
241             }
242              
243             sub imask {
244 264     264 0 582 return ( 2**32 - ( 2**( 32 - $_[0] ) ) );
245             }
246              
247             sub i6mask {
248 1032     1032 0 1550 my $bits = shift;
249 1032         2431 return Math::BigInt->new(2)->bpow(128) - Math::BigInt->new(2)->bpow( 128 - $bits );
250             }
251              
252             sub mask {
253 21     21 1 64 my ($this) = @_;
254              
255 21 100       85 if ( $this->{PROTOCOL} eq 'IPv4' ) {
256 15         73 return int2quad( $imask[ $this->{'BITS'} ] );
257             } else {
258 6         29 return int2ascii( $i6mask[ $this->{'BITS'} ], $this->{PROTOCOL} );
259             }
260             }
261              
262             sub hostmask {
263 4     4 1 4396 my ($this) = @_;
264              
265 4 100       20 if ( $this->{PROTOCOL} eq 'IPv4' ) {
266 1         8 return int2quad( ~$imask[ $this->{BITS} ] );
267             } else {
268 3         21 return int2ascii( $i6mask[ $this->{BITS} ] ^ $i6mask[128], $this->{PROTOCOL} );
269             }
270             }
271              
272             sub nth {
273 528     528 1 103916 my ( $this, $index, $bitstep ) = @_;
274              
275 528 100       1163 my $maxbits = $this->{PROTOCOL} eq 'IPv4' ? 32 : 128;
276              
277 528         988 my $size = $this->size();
278 528         86527 my $ibase = $this->{'IBASE'};
279 528 100       1105 $bitstep = $maxbits unless $bitstep;
280 528         867 my $increment = 2**( $maxbits - $bitstep );
281 528         1115 $index *= $increment;
282 528 100       43112 $index += $size if $index < 0;
283 528 100       43679 return if $index < 0;
284 526 100       41161 return if $index >= $size;
285              
286 524         8264 my $i = $ibase + $index;
287 524         21963 return int2ascii( $i, $this->{PROTOCOL} );
288             }
289              
290             sub enumerate {
291 5     5 1 1526 my ( $this, $bitstep ) = @_;
292 5         16 my $proto = $this->{PROTOCOL};
293              
294             # Set default step size by protocol
295 5 100       21 $bitstep = ( $proto eq 'IPv4' ? 32 : 128 ) unless $bitstep;
    100          
296              
297 5         24 my $size = $this->size();
298              
299 5         1293 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         5 my $ibase = $this->{'IBASE'};
304 3         11 for ( my $i = 0; $i < $size; $i += $increment ) {
305 8240         14279 push( @ary, int2quad( $ibase + $i ) );
306             }
307             } else {
308 2         9 my $increment = Math::BigInt->new(2)->bpow( 128 - $bitstep );
309              
310 2 100       796 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         670 confess("More than 1,000,000,000 results would be returned, dieing");
317             }
318              
319 1         570 for ( my $i = Math::BigInt->new(0); $i < $size; $i += $increment ) {
320 256         28588 push( @ary, $this->_ipv6next($i) );
321             }
322             }
323 4         2389 return @ary;
324             }
325              
326             sub _ipv6next {
327 258     258   1461 my ( $this, $bitstep ) = @_;
328              
329 258         416 my $istart = $this->{IBASE};
330 258         536 my $val = $istart + $bitstep;
331              
332 258         22376 return ipv6Cannonical( int2ascii( $val, $this->{PROTOCOL} ) );
333             }
334              
335             sub inaddr {
336 8     8 1 1515 my ($this) = @_;
337              
338 8 100       34 if ( $this->{PROTOCOL} eq 'IPv4' ) {
339 4         14 return $this->inaddr4();
340             } else {
341 4         25 return $this->inaddr6();
342             }
343             }
344              
345             sub inaddr4 {
346 4     4 0 6 my ($this) = @_;
347 4         10 my $ibase = $this->{'IBASE'};
348 4         11 my $blocks = floor( $this->size() / 256 );
349             return (
350 4 100       34 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         134 push( @ary,
357             join( '.', unpack( 'xC3', pack( 'V', $ibase + $i * 256 ) ) ) . ".in-addr.arpa",
358             0, 255 );
359             }
360 1         25 return @ary;
361             }
362              
363             sub inaddr6 {
364 4     4 0 10 my ($this) = @_;
365              
366 4         19 my (@digits) = split //, $this->{IBASE}->to_hex;
367              
368 4         1259 my $static = floor( $this->{BITS} / 4 );
369 4         21 my $len = floor( ( $static + 3 ) / 4 );
370 4         35 my $remainder = $this->{BITS} % 4;
371 4 100       66 my $blocks = $remainder ? ( 2**( 4 - $remainder ) ) : 1;
372              
373 4         10 my @tail;
374 4 100       15 if ( !$len ) {
375             # Specal case: 0 len
376 1         18 return ('ip6.arpa');
377             }
378 3         35 push @tail, reverse( @digits[ 0 .. ( $static - 1 ) ] ), 'ip6.arpa';
379              
380 3 100       13 if ( !$remainder ) {
381             # Special case - at nibble boundary already
382 2         24 return ( join '.', @tail );
383             }
384              
385 1         5 my $last = hex $digits[$static];
386 1         3 my @ary;
387 1         7 for ( my $i = 0; $i < $blocks; $i++ ) {
388 8         25 push @ary, join( '.', sprintf( "%x", $last ), @tail );
389 8         17 $last++;
390             }
391              
392 1         8 return @ary;
393             }
394              
395             sub tag {
396 11     11 1 377 my $this = shift;
397 11         17 my $tag = shift;
398 11         24 my $val = $this->{ 'T' . $tag };
399 11 100       29 $this->{ 'T' . $tag } = $_[0] if @_;
400 11         33 return $val;
401             }
402              
403             sub quad2int {
404 1336     1336 0 3767 my @bytes = split( /\./, $_[0] );
405              
406 1336 100       2749 return unless @bytes == 4;
407 1335 100 100     2302 return unless !grep { !( /^(([0-9])|([1-9][0-9]*))$/ && $_ < 256 ) } @bytes;
  5340         24969  
408              
409 1307         4962 return unpack( "N", pack( "C4", @bytes ) );
410             }
411              
412             sub int2quad {
413 8785     8785 0 33380 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 26857 if ( $_[1] eq 'IPv4' ) {
    50          
421 1611         10655 return join( '.', unpack( 'C4', pack( "N", $_[0] ) ) );
422             } elsif ( $_[1] eq 'IPv6' ) {
423 701 100       2281 my $addr = ( ref $_[0] ) ne '' ? $_[0]->to_hex : Math::BigInt->new( $_[0] )->to_hex;
424 701         252664 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 7589 if ( $_[1] eq 'IPv4' ) {
    50          
435 1312         2477 return quad2int( $_[0] );
436             } elsif ( $_[1] eq 'IPv6' ) {
437 634         1393 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 1162 my $addr = shift;
446              
447 634         1276 $addr = ipv6NonCompacted($addr);
448 634         2051 $addr = join '', map { sprintf( "%04x", hex($_) ) } split( /:/, $addr );
  5072         12242  
449              
450 634         2916 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 2347 my $addr = shift;
457              
458 1610 100       4753 if ( $addr !~ /:/ ) {
459 701 100       1485 if ( length($addr) < 32 ) {
460 37         142 $addr = ( "0" x ( 32 - length($addr) ) ) . $addr;
461             }
462 701         8537 $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       4045 if ( $addr =~ m/^[0-9a-f:]+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/i ) {
468 2         13 my ( $l, $r1, $r2, $r3, $r4 ) =
469             $addr =~ m/^([0-9a-f:]+)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/i;
470 2         20 $addr = sprintf( "%s%02x%02x:%02x%02x", $l, $r1, $r2, $r3, $r4 );
471             }
472              
473 1610         5253 my ( $left, $right ) = split /::/, $addr;
474 1610 100       3626 if ( !defined($right) ) { $right = '' }
  724         1201  
475 1610         4353 my (@lparts) = split /:/, $left;
476 1610         2692 my (@rparts) = split /:/, $right;
477              
478             # Strip leading 0's & lowercase
479 1610         3099 @lparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @lparts;
  8156         19054  
  8156         17677  
480 1610         2936 @rparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @rparts;
  562         901  
  562         1306  
481              
482             # Expand ::
483 1610         2942 my $missing = 8 - ( @lparts + @rparts );
484 1610 100       3269 if ($missing) {
485 887         3078 $addr = join ':', @lparts, ( 0, 0, 0, 0, 0, 0, 0, 0 )[ 0 .. $missing - 1 ], @rparts;
486             } else {
487 723         2060 $addr = join ':', @lparts, @rparts;
488             }
489              
490 1610         4501 return $addr;
491             }
492              
493             # Compacts an IPv6 address (reduces successive :0: runs)
494             sub ipv6AsciiCompact {
495 976     976 0 1487 my $addr = shift;
496              
497             # Compress, per RFC5952
498 976 100       7105 if ( $addr =~ s/^0:0:0:0:0:0:0:0$/::/ ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
499 18         64 return $addr;
500             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0:0:0(:?:|$)/::/ ) {
501 2         8 return $addr;
502             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0:0(:?:|$)/::/ ) {
503 11         30 return $addr;
504             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0(:?:|$)/::/ ) {
505 285         773 return $addr;
506             } elsif ( $addr =~ s/(:?^|:)0:0:0:0(:?:|$)/::/ ) {
507 567         1250 return $addr;
508             } elsif ( $addr =~ s/(:?^|:)0:0:0(:?:|$)/::/ ) {
509 39         102 return $addr;
510             } elsif ( $addr =~ s/(:?^|:)0:0(:?:|$)/::/ ) {
511 29         71 return $addr;
512             } elsif ( $addr =~ s/(:?^|:)0(:?:|$)/::/ ) {
513 16         40 return $addr;
514             }
515 9         27 return $addr;
516             }
517             # Cannonicalize IPv6 addresses in ascii format
518             sub ipv6Cannonical {
519 976     976 0 14239 my $addr = shift;
520              
521 976         1760 $addr = ipv6NonCompacted($addr);
522 976         1885 $addr = ipv6AsciiCompact($addr);
523              
524 976         7162 return $addr;
525             }
526              
527             # IPv6 addresses are stored with a leading zero.
528             sub storeNetblock {
529 46     46 1 1449 my ( $this, $t ) = @_;
530 46 100       112 $t = $remembered unless $t;
531              
532 46         92 my $base = $this->{'IBASE'};
533 46 100       108 if ( $this->{PROTOCOL} eq 'IPv6' ) {
534 14         48 $base = "0$base";
535             }
536              
537 46 100       607 $t->{$base} = [] unless exists $t->{$base};
538              
539 46         101 my $mb = maxblock($this);
540 46         85 my $bits = $this->{'BITS'};
541 46         68 my $i = $bits - $mb;
542              
543 46         138 return ( $t->{$base}[$i] = $this );
544             }
545              
546             sub deleteNetblock {
547 13     13 1 35 my ( $this, $t ) = @_;
548 13 50       34 $t = $remembered unless $t;
549              
550 13         27 my $base = $this->{'IBASE'};
551 13 100       38 if ( $this->{PROTOCOL} eq 'IPv6' ) {
552 1         6 $base = "0$base";
553             }
554              
555 13         84 my $mb = maxblock($this);
556 13         33 my $bits = $this->{'BITS'};
557 13         19 my $i = $bits - $mb;
558              
559 13 50       37 return unless defined $t->{$base};
560              
561 13         27 undef $t->{$base}->[$i];
562              
563 13         20 for my $x ( @{ $t->{$base} } ) {
  13         38  
564 46 100       120 return if $x;
565             }
566 10         31 return delete $t->{$base};
567             }
568              
569             sub findNetblock {
570 56     56 1 7867 my ( $ascii, $t ) = @_;
571 56 100       167 $t = $remembered unless $t;
572              
573 56 100       213 my $proto = ( $ascii =~ m/:/ ) ? 'IPv6' : 'IPv4';
574              
575 56         142 my $ip = ascii2int( $ascii, $proto );
576 56 100       3285 return unless defined $ip;
577 55         91 my %done;
578              
579 55 100       143 my $maxbits = $proto eq 'IPv6' ? 128 : 32;
580 55         133 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
581 1251         2279 my $nb = i_getnet_addr( $ip, $bits, $proto );
582 1251 100       495249 if ( $proto eq 'IPv6' ) {
583 568         1419 $nb = "0$nb";
584             }
585 1251 100       20829 next unless exists $t->{$nb};
586 44         91 my $mb = imaxblock( $nb, $maxbits, $proto );
587 44 100       182 next if $done{$mb}++;
588 41         69 my $i = $bits - $mb;
589 41         91 while ( $i >= 0 ) {
590             return $t->{$nb}->[$i]
591 308 100       710 if defined $t->{$nb}->[$i];
592 268         417 $i--;
593             }
594             }
595 15         89 return;
596             }
597              
598             sub findOuterNetblock {
599 56     56 1 2905 my ( $ipstr, $t ) = @_;
600 56 50       137 $t = $remembered unless $t;
601              
602 56         162 my $proto;
603             my $maxbits;
604              
605 56         0 my $ip;
606 56         0 my $len;
607 56 100       121 if ( ref($ipstr) ) {
608 28         56 $proto = $ipstr->{PROTOCOL};
609 28 100       59 $maxbits = $proto eq 'IPv4' ? 32 : 128;
610 28         50 $ip = $ipstr->{IBASE};
611 28         44 $len = $ipstr->{BITS};
612             } else {
613 28 100       99 $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
614 28 100       63 $maxbits = $proto eq 'IPv4' ? 32 : 128;
615 28         61 $ip = ascii2int( $ipstr, $proto );
616 28         1448 $len = $maxbits;
617             }
618              
619 56         152 for ( my $bits = 0; $bits <= $len; $bits++ ) {
620 2028 100       4215 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
621 2028 100       562744 if ( $proto eq 'IPv6' ) {
622 646         1533 $nb = "0$nb";
623             }
624 2028 100       24833 next unless exists $t->{$nb};
625 74         131 my $mb = imaxblock( $nb, $len, $proto );
626 74         111 my $i = $bits - $mb;
627 74 50       135 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
628 74 50       129 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
629 74         129 while ( $i >= 0 ) {
630             return $t->{$nb}->[$i]
631 173 100       372 if defined $t->{$nb}->[$i];
632 151         283 $i--;
633             }
634             }
635 34         90 return;
636             }
637              
638             sub findAllNetblock {
639 2     2 1 13 my ( $ipstr, $t ) = @_;
640 2 50       10 $t = $remembered unless $t;
641              
642 2 100       13 my $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
643 2 100       9 my $maxbits = $proto eq 'IPv4' ? 32 : 128;
644              
645 2         7 my $ip = ascii2int( $ipstr, $proto );
646              
647 2         449 my %done;
648             my @ary;
649 2         11 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
650 162 100       786 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
651 162 100       107817 if ( $proto eq 'IPv6' ) {
652 129         506 $nb = "0$nb";
653             }
654 162 100       5946 next unless exists $t->{$nb};
655 109         383 my $mb = imaxblock( $nb, $maxbits, $proto );
656 109 100       972 next if $done{$mb}++;
657 3         8 my $i = $bits - $mb;
658 3 50       13 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
659 3 50       12 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
660 3         9 while ( $i >= 0 ) {
661             push( @ary, $t->{$nb}->[$i] )
662 109 100       190 if defined $t->{$nb}->[$i];
663 109         169 $i--;
664             }
665             }
666 2         25 return @ary;
667             }
668              
669             sub dumpNetworkTable {
670 1     1 1 3 my ($t) = @_;
671 1 50       5 $t = $remembered unless $t;
672              
673 1         2 my @ary;
674 1         7 foreach my $base ( keys %$t ) {
675 6         9 push @ary, grep { defined($_) } @{ $t->{base} };
  0         0  
  6         13  
676 6         8 for my $x ( @{ $t->{$base} } ) {
  6         16  
677 19 100       43 push( @ary, $x )
678             if defined $x;
679             }
680             }
681              
682 1         7 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 3461 my ( $this, $ip ) = @_;
700 24         47 my $proto = $this->{PROTOCOL};
701              
702             # Two different protocols: return undef
703 24 100       77 if ( $ip =~ /:/ ) {
704 10 50       29 if ( $proto ne 'IPv6' ) { return }
  0         0  
705             } else {
706 14 50       41 if ( $proto ne 'IPv4' ) { return }
  0         0  
707             }
708              
709 24         57 my $i = ascii2int( $ip, $this->{PROTOCOL} );
710 24         4177 my $ia = i_getnet_addr( $i, $this->{BITS}, $proto );
711              
712 24 100       8436 if ( $proto eq 'IPv4' ) {
713 14 100       30 if ( $ia == $this->{IBASE} ) {
714 8   100     67 return ( ( $i & ~( $this->{IBASE} ) ) || "0 " );
715             } else {
716 6         39 return 0;
717             }
718             } else {
719 10 100       31 if ( $ia == $this->{IBASE} ) {
720 6   100     307 return ( ( $i - $this->{IBASE} ) || "0 " );
721             } else {
722 4         198 return 0;
723             }
724             }
725             }
726              
727             sub maxblock {
728 80     80 1 168 my ($this) = @_;
729             return ( !defined $this->{ERROR} )
730             ? imaxblock( $this->{IBASE}, $this->{BITS}, $this->{PROTOCOL} )
731 80 50       417 : undef;
732             }
733              
734             sub nextblock {
735 8     8 1 3477 my ( $this, $index ) = @_;
736 8 100       27 $index = 1 unless defined $index;
737 8         16 my $ibase = $this->{IBASE};
738 8 100       22 if ( $this->{PROTOCOL} eq 'IPv4' ) {
739 4         15 $ibase += $index * 2**( 32 - $this->{BITS} );
740             } else {
741 4         30 $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         3145 };
749              
750 8 100       23 if ( $this->{PROTOCOL} eq 'IPv4' ) {
751 4 50       11 return if $newblock->{IBASE} >= 2**32;
752             } else {
753 4 50       12 return if $newblock->{IBASE} >= Math::BigInt->new(2)->bpow(128);
754             }
755              
756 8 50       2308 return if $newblock->{IBASE} < 0;
757 8         786 return $newblock;
758             }
759              
760             sub imaxblock {
761 427     427 0 1202 my ( $ibase, $tbit, $proto ) = @_;
762 427 50       1034 confess unless defined $ibase;
763              
764 427 50       1157 if ( !defined($proto) ) { $proto = 'IPv4'; }
  0         0  
765              
766 427         1149 while ( $tbit > 0 ) {
767 14568         35774 my $ia = i_getnet_addr( $ibase, $tbit - 1, $proto );
768 14568 100       11484134 last if ( $ia != $ibase );
769 14153         1828310 $tbit--;
770             }
771 427         24207 return $tbit;
772             }
773              
774             sub range2cidrlist {
775 5     5 1 4202 my ( $startip, $endip ) = @_;
776              
777 5         10 my $proto;
778 5 100       42 if ( $startip =~ m/:/ ) {
779 2 50       11 if ( $endip =~ m/:/ ) { $proto = 'IPv6'; }
  2         9  
780             } else {
781 3 50       11 if ( $endip !~ m/:/ ) { $proto = 'IPv4'; }
  3         7  
782             }
783 5 50       18 if ( !defined($proto) ) { confess("Cannot mix IPv4 and IPv6 in range2cidrlist()"); }
  0         0  
784              
785 5         15 my $start = ascii2int( $startip, $proto );
786 5         1111 my $end = ascii2int( $endip, $proto );
787              
788 5 100       918 ( $start, $end ) = ( $end, $start )
789             if $start > $end;
790 5         145 return irange2cidrlist( $start, $end, $proto );
791             }
792              
793             sub irange2cidrlist {
794 39     39 0 350 my ( $start, $end, $proto ) = @_;
795 39 50       103 if ( !defined($proto) ) { $proto = 'IPv4' }
  0         0  
796              
797 39 100       103 my $bits = $proto eq 'IPv4' ? 32 : 128;
798              
799 39         55 my @result;
800 39         99 while ( $end >= $start ) {
801 120         39949 my $maxsize = imaxblock( $start, $bits, $proto );
802 120         218 my $maxdiff;
803 120 100       436 if ( $proto eq 'IPv4' ) {
804 40         86 $maxdiff = $bits - _log2( $end - $start + 1 );
805             } else {
806 80         433 $maxdiff = $bits - ( $end - $start + 1 )->blog(2);
807             }
808 120 100       96881 $maxsize = $maxdiff if $maxsize < $maxdiff;
809 120         6964 push(
810             @result,
811             bless {
812             'IBASE' => $start,
813             'BITS' => $maxsize,
814             'PROTOCOL' => $proto,
815             }
816             );
817 120 100       379 if ( $proto eq 'IPv4' ) {
818 40         106 $start += 2**( 32 - $maxsize );
819             } else {
820 80         296 $start += Math::BigInt->new(2)->bpow( $bits - $maxsize );
821             }
822             }
823 39         5342 return @result;
824             }
825              
826             sub cidrs2contiglists {
827 1     1 1 532 my (@cidrs) = sort_network_blocks(@_);
828 1         5 my @result;
829 1         3 while (@cidrs) {
830 2         6 my (@r) = shift(@cidrs);
831 2         7 my $max = $r[0]->{IBASE} + $r[0]->size;
832 2   100     7 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
833 1         4 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
834 1 50       5 $max = $nm if $nm > $max;
835 1         4 push( @r, shift(@cidrs) );
836             }
837 2         8 push( @result, [@r] );
838             }
839 1         4 return @result;
840             }
841              
842             sub cidrs2cidrs {
843 15     15 1 63 my (@cidrs) = sort_network_blocks(@_);
844 15         49 my @result;
845              
846             my $proto;
847 15 50       46 if ( scalar(@cidrs) ) {
848 15         31 $proto = $cidrs[0]->{PROTOCOL};
849 15 50       31 if ( grep { $proto ne $_->{PROTOCOL} } @cidrs ) {
  49         122  
850 0         0 confess("Cannot call cidrs2cidrs with mixed protocol arguments");
851             }
852             }
853              
854 15         46 while (@cidrs) {
855 24         53 my (@r) = shift(@cidrs);
856              
857 24         81 my $max = $r[0]->{IBASE} + $r[0]->size;
858 24   100     2557 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
859 25         295 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
860 25 100       1896 $max = $nm if $nm > $max;
861 25         285 push( @r, shift(@cidrs) );
862             }
863 24         153 my $start = $r[0]->{IBASE};
864 24         49 my $end = $max - 1;
865 24         1229 push( @result, irange2cidrlist( $start, $end, $proto ) );
866             }
867 15         181 return @result;
868             }
869              
870             sub cidrs2inverse {
871 10     10 1 44 my $outer = shift;
872 10 100 33     51 $outer = __PACKAGE__->new2($outer) || croak($error) unless ref($outer);
873              
874             # cidrs2cidrs validates that everything is in the same address
875             # family
876 10         33 my (@cidrs) = cidrs2cidrs(@_);
877 10         23 my $proto;
878 10 50       32 if ( scalar(@cidrs) ) {
879 10         22 $proto = $cidrs[0]->{PROTOCOL};
880             }
881              
882 10         25 my $first = $outer->{IBASE};
883 10         32 my $last = $first + $outer->size() - 1;
884 10   66     767 shift(@cidrs) while $cidrs[0] && $cidrs[0]->{IBASE} + $cidrs[0]->size < $first;
885 10         578 my @r;
886 10   66     55 while ( @cidrs && $first <= $last ) {
887              
888 16 100       159 if ( $first < $cidrs[0]->{IBASE} ) {
889 6 100       64 if ( $last <= $cidrs[0]->{IBASE} - 1 ) {
890 2         17 return ( @r, irange2cidrlist( $first, $last, $proto ) );
891             }
892 4         282 push( @r, irange2cidrlist( $first, $cidrs[0]->{IBASE} - 1, $proto ) );
893             }
894 14 50       83 last if $cidrs[0]->{IBASE} > $last;
895 14         140 $first = $cidrs[0]->{IBASE} + $cidrs[0]->size;
896 14         930 shift(@cidrs);
897             }
898 8 100       25 if ( $first <= $last ) {
899 4         67 push( @r, irange2cidrlist( $first, $last, $proto ) );
900             }
901 8         58 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 82 return !cmpblocks(@_);
911             }
912              
913             sub cmpblocks {
914 45     45 1 74 my $this = shift;
915 45         82 my $class = ref $this;
916 45 100       158 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
917 45         113 return cmp_net_netmask_block( $this, $other );
918             }
919              
920             sub contains {
921 66     66 1 455 my $this = shift;
922 66         116 my $class = ref $this;
923 66 100       164 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
924 66 100       266 return 0 if $this->{IBASE} > $other->{IBASE};
925 51 100       1080 return 0 if $this->{BITS} > $other->{BITS};
926 46 100       126 return 0 if $other->{IBASE} > $this->{IBASE} + $this->size - 1;
927 40         11500 return 1;
928             }
929              
930             sub cmp_net_netmask_block {
931 7833 100 100 7833 0 23623 if ( ( $_[0]->{PROTOCOL} eq 'IPv4' ) && ( $_[1]->{PROTOCOL} eq 'IPv4' ) ) {
    100 100        
932             # IPv4
933 7793   100     18822 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
934             } elsif ( ( $_[0]->{PROTOCOL} eq 'IPv6' ) && ( $_[1]->{PROTOCOL} eq 'IPv6' ) ) {
935             # IPv6
936 36   100     162 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
937             } else {
938             # IPv4 to IPv6, order by protocol
939 4         30 return ( $_[0]->{PROTOCOL} cmp $_[1]->{PROTOCOL} );
940             }
941             }
942              
943             sub sort_network_blocks {
944 52         224 return map { $_->[0] }
945 64 50 66     544 sort { $a->[3] cmp $b->[3] || $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
946 16     16 1 37 map { [ $_, $_->{IBASE}, $_->{BITS}, $_->{PROTOCOL} ] } @_;
  52         220  
947             }
948              
949             sub sort_by_ip_address {
950 500         692 return map { $_->[0] }
951 3870         4762 sort { $a->[1] cmp $b->[1] }
952 1     1 1 6 map { [ $_, pack( "C4", split( /\./, $_ ) ) ] } @_;
  500         1593  
953              
954             }
955              
956             sub split ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
957             {
958 14     14 1 19765 my ( $self, $parts ) = @_;
959              
960 14         41 my $num_ips = $self->size;
961              
962 14 100 100     3803 confess "Parts must be defined and greater than 0."
963             unless defined($parts) && $parts > 0;
964              
965 8 100       125 confess "Netmask only contains $num_ips IPs. Cannot split into $parts."
966             unless $num_ips >= $parts;
967              
968 6         374 my $log2 = _log2($parts);
969              
970 6 100       470 confess "Parts count must be a number of base 2. Got: $parts"
971             unless ( 2**$log2 ) == $parts;
972              
973 4         20 my $new_mask = $self->bits + $log2;
974              
975 516         1822 return map { Net::Netmask->new( $_ . "/" . $new_mask ) }
976 4         70 map { $self->nth( ( $num_ips / $parts ) * $_ ) } ( 0 .. ( $parts - 1 ) );
  516         1391  
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   71 my $n = shift;
984              
985 46         72 my $ret = 0;
986 46         209 $ret++ while ( $n >>= 1 );
987              
988 46         88 return $ret;
989             }
990              
991             BEGIN {
992 8     8   65274 for ( my $i = 0; $i <= 32; $i++ ) {
993 264         510 $imask[$i] = imask($i);
994 264         698 $imask2bits{ $imask[$i] } = $i;
995 264         459 $quadmask2bits{ int2quad( $imask[$i] ) } = $i;
996 264         550 $quadhostmask2bits{ int2quad( ~$imask[$i] ) } = $i;
997 264         1341 $size2bits{ 2**( 32 - $i ) } = $i;
998             }
999              
1000 8         55 for ( my $i = 0; $i <= 128; $i++ ) {
1001 1032         1092978 $i6mask[$i] = i6mask($i);
1002             }
1003             }
1004             1;