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.0002';
7 8     8   1486245 use 5.006_001;
  8         59  
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   72 use vars qw($error $debug %quadmask2bits %quadhostmask2bits);
  8         10  
  8         672  
34             $debug = 1;
35              
36 8     8   51 use strict;
  8         15  
  8         177  
37 8     8   33 use warnings;
  8         24  
  8         217  
38 8     8   36 use Carp;
  8         11  
  8         455  
39 8     8   8215 use Math::BigInt;
  8         206447  
  8         39  
40 8     8   155406 use POSIX qw(floor);
  8         51  
  8         73  
41             use overload
42 8         64 '""' => \&desc,
43             '<=>' => \&cmp_net_netmask_block,
44             'cmp' => \&cmp_net_netmask_block,
45 8     8   11699 'fallback' => 1;
  8         16  
46              
47             sub new {
48 1900     1900 0 47135 my ( $package, $net, @params) = @_;
49              
50 1900         2303 my $mask = '';
51 1900 100       4093 if (@params % 2) {
52 81         115 $mask = shift(@params);
53 81 100       164 $mask = '' if !defined($mask);
54             }
55 1900         2464 my (%options) = @params;
56 1900   100     5659 my $shortnet = ( ( exists($options{shortnet}) && $options{shortnet} ) || $SHORTNET_DEFAULT );
57              
58 1900         3238 my $base;
59             my $bits;
60 1900         0 my $ibase;
61 1900         2063 my $proto = 'IPv4';
62 1900         2050 undef $error;
63              
64 1900 100 100     22512 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         928 ( $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         55 $base = $1;
68 6         14 my $quadmask = $2;
69 6 100       17 if ( exists $quadmask2bits{$quadmask} ) {
70 4         8 $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         10 $base = $1;
76 4         7 my $hostmask = $2;
77 4 100       11 if ( exists $quadhostmask2bits{$hostmask} ) {
78 2         4 $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         14 $base = $net;
86 7 100       13 if ( exists $quadmask2bits{$mask} ) {
87 5         13 $bits = $quadmask2bits{$mask};
88             } else {
89 2         13 $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         12 $base = $net;
95 6         38 my $imask = hex($mask);
96 6 100       19 if ( exists $imask2bits{$imask} ) {
97 4         8 $bits = $imask2bits{$imask};
98             } else {
99 2         6 $error = "illegal netmask: $mask ($imask)";
100             }
101             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask ) {
102 770         1671 ( $base, $bits ) = ( $net, 32 );
103             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask && $shortnet ) {
104 2         6 ( $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         6 ( $base, $bits ) = ( "$net.0.0.0", 8 );
109             } elsif ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+)/([0-9]+)$, && $shortnet ) {
110 2         7 ( $base, $bits ) = ( "$1.0", $2 );
111             } elsif ( $net =~ m,^([0-9]+\.[0-9]+)/([0-9]+)$, && $shortnet ) {
112 2         9 ( $base, $bits ) = ( "$1.0.0", $2 );
113             } elsif ( $net =~ m,^([0-9]+)/([0-9]+)$, && $shortnet ) {
114 2         9 ( $base, $bits ) = ( "$1.0.0.0", $2 );
115             } elsif ( $net eq 'default' || $net eq 'any' ) {
116 5         12 ( $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         28 $ibase = quad2int($1);
121 12         41 my $end = quad2int($2);
122 12 100 100     42 $error = "illegal dotted quad: $net"
123             unless defined($ibase) && defined($end);
124 12   100     38 my $diff = ( $end || 0 ) - ( $ibase || 0 ) + 1;
      100        
125 12         26 $bits = $size2bits{$diff};
126 12 100 100     68 $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       667 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
133 326         999 ( $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       581 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
137 272         779 ( $base, $bits, $proto ) = ( $1, 128, 'IPv6' );
138             } elsif ( $net eq 'default6' || $net eq 'any6' ) {
139 4 50       11 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
140 4         10 ( $base, $bits, $proto ) = ( "::", 0, 'IPv6' );
141             } else {
142 59         138 $error = "could not parse $net";
143 59 100       114 $error .= " $mask" if $mask;
144             }
145              
146 1900 50 66     2904 carp $error if $error && $debug;
147              
148 1900 100       2988 $bits = 0 unless $bits;
149 1900 100 100     6625 if ( ( $proto eq 'IPv4' ) && ( $bits > 32 ) ) {
    100 100        
150 2 50       6 $error = "illegal number of bits: $bits"
151             unless $error;
152 2         5 $bits = 32;
153             } elsif ( ( $proto eq 'IPv6' ) && ( $bits > 128 ) ) {
154 2 50       6 $error = "illegal number of bits: $bits"
155             unless $error;
156 2         3 $bits = 128;
157             }
158              
159 1900 100 50     5950 $ibase = ascii2int( ( $base || '::' ), $proto ) unless (defined $ibase or $error);
      100        
160 1900 100 100     294955 unless ( defined($ibase) || defined($error) ) {
161 24         47 $error = "could not parse $net";
162 24 100       40 $error .= " $mask" if $mask;
163             }
164              
165 1900 100       2742 if ($error) {
166 103         117 $ibase = 0;
167 103         106 $bits = 0;
168             }
169              
170 1900         2877 $ibase = i_getnet_addr( $ibase, $bits, $proto );
171              
172 1900 100       435730 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 26013 my ( $ibase, $bits, $proto ) = @_;
182              
183 17743 50       26773 if ( !defined($ibase) ) { return; }
  0         0  
184              
185 17743 100       27824 if ( $proto eq 'IPv4' ) {
186 3784         5164 return $ibase & $imask[$bits];
187             } else {
188 13959         30405 return $ibase & $i6mask[$bits];
189             }
190             }
191              
192             sub new2 {
193 50     50 0 3887 goto &safe_new;
194             }
195              
196             sub safe_new {
197 105     105 0 16414 local ($debug) = 0;
198 105         218 my $net = new(@_);
199 105 100       314 return if $error;
200 53         119 return $net;
201             }
202              
203 52     52 0 15085 sub errstr { return $error; }
204 2 50   2 0 164 sub debug { my $this = shift; return ( @_ ? $debug = shift : $debug ) }
  2         17  
205              
206 1021     1021 1 2412 sub base { my ($this) = @_; return int2ascii( $this->{IBASE}, $this->{PROTOCOL} ); }
  1021         1333  
207 25     25 1 43 sub bits { my ($this) = @_; return $this->{'BITS'}; }
  25         99  
208 21     21 1 36 sub protocol { my ($this) = @_; return $this->{'PROTOCOL'}; }
  21         75  
209              
210             sub size {
211 700     700 1 981 my ($this) = @_;
212              
213 700 100       1227 if ( $this->{PROTOCOL} eq 'IPv4' ) {
214 392         751 return 2**( 32 - $this->{'BITS'} );
215             } else {
216 308         664 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       9 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 14 my ($this) = @_;
232              
233 2         8 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 12923 return int2ascii( $_[0]->{IBASE}, $_[0]->{PROTOCOL} ) . '/' . $_[0]->{BITS};
241             }
242              
243             sub imask {
244 264     264 0 413 return ( 2**32 - ( 2**( 32 - $_[0] ) ) );
245             }
246              
247             sub i6mask {
248 1032     1032 0 1294 my $bits = shift;
249 1032         2031 return Math::BigInt->new(2)->bpow(128) - Math::BigInt->new(2)->bpow( 128 - $bits );
250             }
251              
252             sub mask {
253 21     21 1 37 my ($this) = @_;
254              
255 21 100       51 if ( $this->{PROTOCOL} eq 'IPv4' ) {
256 15         35 return int2quad( $imask[ $this->{'BITS'} ] );
257             } else {
258 6         17 return int2ascii( $i6mask[ $this->{'BITS'} ], $this->{PROTOCOL} );
259             }
260             }
261              
262             sub hostmask {
263 4     4 1 2626 my ($this) = @_;
264              
265 4 100       15 if ( $this->{PROTOCOL} eq 'IPv4' ) {
266 1         5 return int2quad( ~$imask[ $this->{BITS} ] );
267             } else {
268 3         12 return int2ascii( $i6mask[ $this->{BITS} ] ^ $i6mask[128], $this->{PROTOCOL} );
269             }
270             }
271              
272             sub nth {
273 528     528 1 96496 my ( $this, $index, $bitstep ) = @_;
274              
275 528 100       1061 my $maxbits = $this->{PROTOCOL} eq 'IPv4' ? 32 : 128;
276              
277 528         831 my $size = $this->size();
278 528         90128 my $ibase = $this->{'IBASE'};
279 528 100       912 $bitstep = $maxbits unless $bitstep;
280 528         693 my $increment = 2**( $maxbits - $bitstep );
281 528         911 $index *= $increment;
282 528 100       39765 $index += $size if $index < 0;
283 528 100       39710 return if $index < 0;
284 526 100       37903 return if $index >= $size;
285              
286 524         6742 my $i = $ibase + $index;
287 524         21752 return int2ascii( $i, $this->{PROTOCOL} );
288             }
289              
290             sub enumerate {
291 5     5 1 886 my ( $this, $bitstep ) = @_;
292 5         10 my $proto = $this->{PROTOCOL};
293              
294             # Set default step size by protocol
295 5 100       18 $bitstep = ( $proto eq 'IPv4' ? 32 : 128 ) unless $bitstep;
    100          
296              
297 5         17 my $size = $this->size();
298              
299 5         1173 my @ary;
300             ### We should be able to consolidate this
301 5 100       25 if ( $proto eq 'IPv4' ) {
302 3         5 my $increment = 2**( 32 - $bitstep );
303 3         4 my $ibase = $this->{'IBASE'};
304 3         7 for ( my $i = 0; $i < $size; $i += $increment ) {
305 8240         11236 push( @ary, int2quad( $ibase + $i ) );
306             }
307             } else {
308 2         10 my $increment = Math::BigInt->new(2)->bpow( 128 - $bitstep );
309              
310 2 100       753 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         531 confess("More than 1,000,000,000 results would be returned, dieing");
317             }
318              
319 1         436 for ( my $i = Math::BigInt->new(0); $i < $size; $i += $increment ) {
320 256         25690 push( @ary, $this->_ipv6next($i) );
321             }
322             }
323 4         1875 return @ary;
324             }
325              
326             sub _ipv6next {
327 258     258   1302 my ( $this, $bitstep ) = @_;
328              
329 258         339 my $istart = $this->{IBASE};
330 258         434 my $val = $istart + $bitstep;
331              
332 258         21310 return ipv6Cannonical( int2ascii( $val, $this->{PROTOCOL} ) );
333             }
334              
335             sub inaddr {
336 8     8 1 845 my ($this) = @_;
337              
338 8 100       18 if ( $this->{PROTOCOL} eq 'IPv4' ) {
339 4         9 return $this->inaddr4();
340             } else {
341 4         11 return $this->inaddr6();
342             }
343             }
344              
345             sub inaddr4 {
346 4     4 0 6 my ($this) = @_;
347 4         6 my $ibase = $this->{'IBASE'};
348 4         8 my $blocks = floor( $this->size() / 256 );
349             return (
350 4 100       24 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         133 push( @ary,
357             join( '.', unpack( 'xC3', pack( 'V', $ibase + $i * 256 ) ) ) . ".in-addr.arpa",
358             0, 255 );
359             }
360 1         11 return @ary;
361             }
362              
363             sub inaddr6 {
364 4     4 0 7 my ($this) = @_;
365              
366 4         11 my (@digits) = split //, $this->{IBASE}->to_hex;
367              
368 4         966 my $static = floor( $this->{BITS} / 4 );
369 4         11 my $len = floor( ( $static + 3 ) / 4 );
370 4         8 my $remainder = $this->{BITS} % 4;
371 4 100       12 my $blocks = $remainder ? ( 2**( 4 - $remainder ) ) : 1;
372              
373 4         4 my @tail;
374 4 100       11 if ( !$len ) {
375             # Specal case: 0 len
376 1         7 return ('ip6.arpa');
377             }
378 3         22 push @tail, reverse( @digits[ 0 .. ( $static - 1 ) ] ), 'ip6.arpa';
379              
380 3 100       8 if ( !$remainder ) {
381             # Special case - at nibble boundary already
382 2         16 return ( join '.', @tail );
383             }
384              
385 1         3 my $last = hex $digits[$static];
386 1         2 my @ary;
387 1         4 for ( my $i = 0; $i < $blocks; $i++ ) {
388 8         21 push @ary, join( '.', sprintf( "%x", $last ), @tail );
389 8         14 $last++;
390             }
391              
392 1         6 return @ary;
393             }
394              
395             sub tag {
396 11     11 1 203 my $this = shift;
397 11         12 my $tag = shift;
398 11         19 my $val = $this->{ 'T' . $tag };
399 11 100       27 $this->{ 'T' . $tag } = $_[0] if @_;
400 11         22 return $val;
401             }
402              
403             sub quad2int {
404 1336     1336 0 2954 my @bytes = split( /\./, $_[0] );
405              
406 1336 100       2338 return unless @bytes == 4;
407 1335 100 100     1889 return unless !grep { !( /^(([0-9])|([1-9][0-9]*))$/ && $_ < 256 ) } @bytes;
  5340         19025  
408              
409 1307         3948 return unpack( "N", pack( "C4", @bytes ) );
410             }
411              
412             sub int2quad {
413 8785     8785 0 26530 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 25478 if ( $_[1] eq 'IPv4' ) {
    50          
421 1611         7120 return join( '.', unpack( 'C4', pack( "N", $_[0] ) ) );
422             } elsif ( $_[1] eq 'IPv6' ) {
423 701 100       2018 my $addr = ( ref $_[0] ) ne '' ? $_[0]->to_hex : Math::BigInt->new( $_[0] )->to_hex;
424 701         201030 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 6286 if ( $_[1] eq 'IPv4' ) {
    50          
435 1312         1875 return quad2int( $_[0] );
436             } elsif ( $_[1] eq 'IPv6' ) {
437 634         1223 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 959 my $addr = shift;
446              
447 634         997 $addr = ipv6NonCompacted($addr);
448 634         1613 $addr = join '', map { sprintf( "%04x", hex($_) ) } split( /:/, $addr );
  5072         9915  
449              
450 634         2190 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 2049 my $addr = shift;
457              
458 1610 100       3856 if ( $addr !~ /:/ ) {
459 701 100       1157 if ( length($addr) < 32 ) {
460 37         103 $addr = ( "0" x ( 32 - length($addr) ) ) . $addr;
461             }
462 701         7184 $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       3158 if ( $addr =~ m/^[0-9a-f:]+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/i ) {
468 2         10 my ( $l, $r1, $r2, $r3, $r4 ) =
469             $addr =~ m/^([0-9a-f:]+)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/i;
470 2         17 $addr = sprintf( "%s%02x%02x:%02x%02x", $l, $r1, $r2, $r3, $r4 );
471             }
472              
473 1610         4053 my ( $left, $right ) = split /::/, $addr;
474 1610 100       2953 if ( !defined($right) ) { $right = '' }
  724         911  
475 1610         3371 my (@lparts) = split /:/, $left;
476 1610         2231 my (@rparts) = split /:/, $right;
477              
478             # Strip leading 0's & lowercase
479 1610         2492 @lparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @lparts;
  8156         14608  
  8156         14610  
480 1610         2208 @rparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @rparts;
  562         730  
  562         1057  
481              
482             # Expand ::
483 1610         2229 my $missing = 8 - ( @lparts + @rparts );
484 1610 100       2463 if ($missing) {
485 887         2581 $addr = join ':', @lparts, ( 0, 0, 0, 0, 0, 0, 0, 0 )[ 0 .. $missing - 1 ], @rparts;
486             } else {
487 723         1516 $addr = join ':', @lparts, @rparts;
488             }
489              
490 1610         3598 return $addr;
491             }
492              
493             # Compacts an IPv6 address (reduces successive :0: runs)
494             sub ipv6AsciiCompact {
495 976     976 0 1188 my $addr = shift;
496              
497             # Compress, per RFC5952
498 976 100       5667 if ( $addr =~ s/^0:0:0:0:0:0:0:0$/::/ ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
499 18         44 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         27 return $addr;
504             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0(:?:|$)/::/ ) {
505 285         635 return $addr;
506             } elsif ( $addr =~ s/(:?^|:)0:0:0:0(:?:|$)/::/ ) {
507 567         1025 return $addr;
508             } elsif ( $addr =~ s/(:?^|:)0:0:0(:?:|$)/::/ ) {
509 39         81 return $addr;
510             } elsif ( $addr =~ s/(:?^|:)0:0(:?:|$)/::/ ) {
511 29         60 return $addr;
512             } elsif ( $addr =~ s/(:?^|:)0(:?:|$)/::/ ) {
513 16         32 return $addr;
514             }
515 9         15 return $addr;
516             }
517             # Cannonicalize IPv6 addresses in ascii format
518             sub ipv6Cannonical {
519 976     976 0 11670 my $addr = shift;
520              
521 976         1413 $addr = ipv6NonCompacted($addr);
522 976         1460 $addr = ipv6AsciiCompact($addr);
523              
524 976         4561 return $addr;
525             }
526              
527             # IPv6 addresses are stored with a leading zero.
528             sub storeNetblock {
529 46     46 1 839 my ( $this, $t ) = @_;
530 46 100       80 $t = $remembered unless $t;
531              
532 46         67 my $base = $this->{'IBASE'};
533 46 100       88 if ( $this->{PROTOCOL} eq 'IPv6' ) {
534 14         36 $base = "0$base";
535             }
536              
537 46 100       532 $t->{$base} = [] unless exists $t->{$base};
538              
539 46         78 my $mb = maxblock($this);
540 46         62 my $bits = $this->{'BITS'};
541 46         61 my $i = $bits - $mb;
542              
543 46         106 return ( $t->{$base}[$i] = $this );
544             }
545              
546             sub deleteNetblock {
547 13     13 1 26 my ( $this, $t ) = @_;
548 13 50       29 $t = $remembered unless $t;
549              
550 13         25 my $base = $this->{'IBASE'};
551 13 100       38 if ( $this->{PROTOCOL} eq 'IPv6' ) {
552 1         5 $base = "0$base";
553             }
554              
555 13         73 my $mb = maxblock($this);
556 13         19 my $bits = $this->{'BITS'};
557 13         20 my $i = $bits - $mb;
558              
559 13 50       30 return unless defined $t->{$base};
560              
561 13         24 undef $t->{$base}->[$i];
562              
563 13         16 for my $x ( @{ $t->{$base} } ) {
  13         31  
564 46 100       90 return if $x;
565             }
566 10         25 return delete $t->{$base};
567             }
568              
569             sub findNetblock {
570 56     56 1 4965 my ( $ascii, $t ) = @_;
571 56 100       121 $t = $remembered unless $t;
572              
573 56 100       164 my $proto = ( $ascii =~ m/:/ ) ? 'IPv6' : 'IPv4';
574              
575 56         111 my $ip = ascii2int( $ascii, $proto );
576 56 100       3932 return unless defined $ip;
577 55         69 my %done;
578              
579 55 100       105 my $maxbits = $proto eq 'IPv6' ? 128 : 32;
580 55         117 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
581 1251         1827 my $nb = i_getnet_addr( $ip, $bits, $proto );
582 1251 100       399218 if ( $proto eq 'IPv6' ) {
583 568         1209 $nb = "0$nb";
584             }
585 1251 100       17926 next unless exists $t->{$nb};
586 44         85 my $mb = imaxblock( $nb, $maxbits, $proto );
587 44 100       144 next if $done{$mb}++;
588 41         80 my $i = $bits - $mb;
589 41         76 while ( $i >= 0 ) {
590             return $t->{$nb}->[$i]
591 308 100       541 if defined $t->{$nb}->[$i];
592 268         335 $i--;
593             }
594             }
595 15         49 return;
596             }
597              
598             sub findOuterNetblock {
599 56     56 1 1849 my ( $ipstr, $t ) = @_;
600 56 50       100 $t = $remembered unless $t;
601              
602 56         130 my $proto;
603             my $maxbits;
604              
605 56         0 my $ip;
606 56         0 my $len;
607 56 100       90 if ( ref($ipstr) ) {
608 28         50 $proto = $ipstr->{PROTOCOL};
609 28 100       52 $maxbits = $proto eq 'IPv4' ? 32 : 128;
610 28         66 $ip = $ipstr->{IBASE};
611 28         35 $len = $ipstr->{BITS};
612             } else {
613 28 100       77 $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
614 28 100       53 $maxbits = $proto eq 'IPv4' ? 32 : 128;
615 28         50 $ip = ascii2int( $ipstr, $proto );
616 28         2569 $len = $maxbits;
617             }
618              
619 56         118 for ( my $bits = 0; $bits <= $len; $bits++ ) {
620 2028 100       3299 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
621 2028 100       453562 if ( $proto eq 'IPv6' ) {
622 646         1272 $nb = "0$nb";
623             }
624 2028 100       21307 next unless exists $t->{$nb};
625 74         104 my $mb = imaxblock( $nb, $len, $proto );
626 74         88 my $i = $bits - $mb;
627 74 50       110 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
628 74 50       113 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
629 74         94 while ( $i >= 0 ) {
630             return $t->{$nb}->[$i]
631 173 100       278 if defined $t->{$nb}->[$i];
632 151         219 $i--;
633             }
634             }
635 34         70 return;
636             }
637              
638             sub findAllNetblock {
639 2     2 1 10 my ( $ipstr, $t ) = @_;
640 2 50       6 $t = $remembered unless $t;
641              
642 2 100       9 my $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
643 2 100       5 my $maxbits = $proto eq 'IPv4' ? 32 : 128;
644              
645 2         5 my $ip = ascii2int( $ipstr, $proto );
646              
647 2         476 my %done;
648             my @ary;
649 2         7 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
650 162 100       686 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
651 162 100       92944 if ( $proto eq 'IPv6' ) {
652 129         500 $nb = "0$nb";
653             }
654 162 100       5855 next unless exists $t->{$nb};
655 109         362 my $mb = imaxblock( $nb, $maxbits, $proto );
656 109 100       822 next if $done{$mb}++;
657 3         10 my $i = $bits - $mb;
658 3 50       16 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
659 3 50       11 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
660 3         13 while ( $i >= 0 ) {
661             push( @ary, $t->{$nb}->[$i] )
662 109 100       154 if defined $t->{$nb}->[$i];
663 109         144 $i--;
664             }
665             }
666 2         18 return @ary;
667             }
668              
669             sub dumpNetworkTable {
670 1     1 1 4 my ($t) = @_;
671 1 50       4 $t = $remembered unless $t;
672              
673 1         1 my @ary;
674 1         6 foreach my $base ( keys %$t ) {
675 6         7 push @ary, grep { defined($_) } @{ $t->{base} };
  0         0  
  6         10  
676 6         7 for my $x ( @{ $t->{$base} } ) {
  6         10  
677 19 100       39 push( @ary, $x )
678             if defined $x;
679             }
680             }
681              
682 1         6 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 2249 my ( $this, $ip ) = @_;
700 24         36 my $proto = $this->{PROTOCOL};
701              
702             # Two different protocols: return undef
703 24 100       69 if ( $ip =~ /:/ ) {
704 10 50       24 if ( $proto ne 'IPv6' ) { return }
  0         0  
705             } else {
706 14 50       33 if ( $proto ne 'IPv4' ) { return }
  0         0  
707             }
708              
709 24         52 my $i = ascii2int( $ip, $this->{PROTOCOL} );
710 24         4985 my $ia = i_getnet_addr( $i, $this->{BITS}, $proto );
711              
712 24 100       6913 if ( $proto eq 'IPv4' ) {
713 14 100       23 if ( $ia == $this->{IBASE} ) {
714 8   100     48 return ( ( $i & ~( $this->{IBASE} ) ) || "0 " );
715             } else {
716 6         22 return 0;
717             }
718             } else {
719 10 100       25 if ( $ia == $this->{IBASE} ) {
720 6   100     249 return ( ( $i - $this->{IBASE} ) || "0 " );
721             } else {
722 4         157 return 0;
723             }
724             }
725             }
726              
727             sub maxblock {
728 80     80 1 117 my ($this) = @_;
729             return ( !defined $this->{ERROR} )
730             ? imaxblock( $this->{IBASE}, $this->{BITS}, $this->{PROTOCOL} )
731 80 50       229 : undef;
732             }
733              
734             sub nextblock {
735 8     8 1 2713 my ( $this, $index ) = @_;
736 8 100       21 $index = 1 unless defined $index;
737 8         13 my $ibase = $this->{IBASE};
738 8 100       18 if ( $this->{PROTOCOL} eq 'IPv4' ) {
739 4         11 $ibase += $index * 2**( 32 - $this->{BITS} );
740             } else {
741 4         16 $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         2916 };
749              
750 8 100       17 if ( $this->{PROTOCOL} eq 'IPv4' ) {
751 4 50       12 return if $newblock->{IBASE} >= 2**32;
752             } else {
753 4 50       10 return if $newblock->{IBASE} >= Math::BigInt->new(2)->bpow(128);
754             }
755              
756 8 50       2202 return if $newblock->{IBASE} < 0;
757 8         749 return $newblock;
758             }
759              
760             sub imaxblock {
761 427     427 0 844 my ( $ibase, $tbit, $proto ) = @_;
762 427 50       929 confess unless defined $ibase;
763              
764 427 50       880 if ( !defined($proto) ) { $proto = 'IPv4'; }
  0         0  
765              
766 427         687 while ( $tbit > 0 ) {
767 14568         29862 my $ia = i_getnet_addr( $ibase, $tbit - 1, $proto );
768 14568 100       10407132 last if ( $ia != $ibase );
769 14153         2143516 $tbit--;
770             }
771 427         27824 return $tbit;
772             }
773              
774             sub range2cidrlist {
775 5     5 1 2627 my ( $startip, $endip ) = @_;
776              
777 5         40 my $proto;
778 5 100       18 if ( $startip =~ m/:/ ) {
779 2 50       9 if ( $endip =~ m/:/ ) { $proto = 'IPv6'; }
  2         4  
780             } else {
781 3 50       8 if ( $endip !~ m/:/ ) { $proto = 'IPv4'; }
  3         5  
782             }
783 5 50       13 if ( !defined($proto) ) { confess("Cannot mix IPv4 and IPv6 in range2cidrlist()"); }
  0         0  
784              
785 5         14 my $start = ascii2int( $startip, $proto );
786 5         1599 my $end = ascii2int( $endip, $proto );
787              
788 5 100       1244 ( $start, $end ) = ( $end, $start )
789             if $start > $end;
790 5         107 return irange2cidrlist( $start, $end, $proto );
791             }
792              
793             sub irange2cidrlist {
794 39     39 0 289 my ( $start, $end, $proto ) = @_;
795 39 50       80 if ( !defined($proto) ) { $proto = 'IPv4' }
  0         0  
796              
797 39 100       67 my $bits = $proto eq 'IPv4' ? 32 : 128;
798              
799 39         57 my @result;
800 39         69 while ( $end >= $start ) {
801 120         37422 my $maxsize = imaxblock( $start, $bits, $proto );
802 120         156 my $maxdiff;
803 120 100       274 if ( $proto eq 'IPv4' ) {
804 40         76 $maxdiff = $bits - _log2( $end - $start + 1 );
805             } else {
806 80         213 $maxdiff = $bits - ( $end - $start + 1 )->blog(2);
807             }
808 120 100       81738 $maxsize = $maxdiff if $maxsize < $maxdiff;
809 120         6531 push(
810             @result,
811             bless {
812             'IBASE' => $start,
813             'BITS' => $maxsize,
814             'PROTOCOL' => $proto,
815             }
816             );
817 120 100       247 if ( $proto eq 'IPv4' ) {
818 40         92 $start += 2**( 32 - $maxsize );
819             } else {
820 80         210 $start += Math::BigInt->new(2)->bpow( $bits - $maxsize );
821             }
822             }
823 39         5162 return @result;
824             }
825              
826             sub cidrs2contiglists {
827 1     1 1 331 my (@cidrs) = sort_network_blocks(@_);
828 1         3 my @result;
829 1         3 while (@cidrs) {
830 2         5 my (@r) = shift(@cidrs);
831 2         5 my $max = $r[0]->{IBASE} + $r[0]->size;
832 2   100     6 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
833 1         3 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         5 push( @result, [@r] );
838             }
839 1         4 return @result;
840             }
841              
842             sub cidrs2cidrs {
843 15     15 1 46 my (@cidrs) = sort_network_blocks(@_);
844 15         38 my @result;
845              
846             my $proto;
847 15 50       34 if ( scalar(@cidrs) ) {
848 15         27 $proto = $cidrs[0]->{PROTOCOL};
849 15 50       22 if ( grep { $proto ne $_->{PROTOCOL} } @cidrs ) {
  49         93  
850 0         0 confess("Cannot call cidrs2cidrs with mixed protocol arguments");
851             }
852             }
853              
854 15         36 while (@cidrs) {
855 24         37 my (@r) = shift(@cidrs);
856              
857 24         70 my $max = $r[0]->{IBASE} + $r[0]->size;
858 24   100     2503 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
859 25         232 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
860 25 100       1724 $max = $nm if $nm > $max;
861 25         227 push( @r, shift(@cidrs) );
862             }
863 24         120 my $start = $r[0]->{IBASE};
864 24         38 my $end = $max - 1;
865 24         1110 push( @result, irange2cidrlist( $start, $end, $proto ) );
866             }
867 15         190 return @result;
868             }
869              
870             sub cidrs2inverse {
871 10     10 1 28 my $outer = shift;
872 10 100 33     30 $outer = __PACKAGE__->new2($outer) || croak($error) unless ref($outer);
873              
874             # cidrs2cidrs validates that everything is in the same address
875             # family
876 10         21 my (@cidrs) = cidrs2cidrs(@_);
877 10         14 my $proto;
878 10 50       23 if ( scalar(@cidrs) ) {
879 10         21 $proto = $cidrs[0]->{PROTOCOL};
880             }
881              
882 10         19 my $first = $outer->{IBASE};
883 10         27 my $last = $first + $outer->size() - 1;
884 10   66     652 shift(@cidrs) while $cidrs[0] && $cidrs[0]->{IBASE} + $cidrs[0]->size < $first;
885 10         533 my @r;
886 10   66     39 while ( @cidrs && $first <= $last ) {
887              
888 16 100       116 if ( $first < $cidrs[0]->{IBASE} ) {
889 6 100       51 if ( $last <= $cidrs[0]->{IBASE} - 1 ) {
890 2         6 return ( @r, irange2cidrlist( $first, $last, $proto ) );
891             }
892 4         242 push( @r, irange2cidrlist( $first, $cidrs[0]->{IBASE} - 1, $proto ) );
893             }
894 14 50       80 last if $cidrs[0]->{IBASE} > $last;
895 14         113 $first = $cidrs[0]->{IBASE} + $cidrs[0]->size;
896 14         931 shift(@cidrs);
897             }
898 8 100       17 if ( $first <= $last ) {
899 4         57 push( @r, irange2cidrlist( $first, $last, $proto ) );
900             }
901 8         40 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 60 return !cmpblocks(@_);
911             }
912              
913             sub cmpblocks {
914 45     45 1 66 my $this = shift;
915 45         64 my $class = ref $this;
916 45 100       126 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
917 45         87 return cmp_net_netmask_block( $this, $other );
918             }
919              
920             sub contains {
921 66     66 1 358 my $this = shift;
922 66         96 my $class = ref $this;
923 66 100       135 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
924 66 100       214 return 0 if $this->{IBASE} > $other->{IBASE};
925 51 100       852 return 0 if $this->{BITS} > $other->{BITS};
926 46 100       100 return 0 if $other->{IBASE} > $this->{IBASE} + $this->size - 1;
927 40         11083 return 1;
928             }
929              
930             sub cmp_net_netmask_block {
931 7855 100 100 7855 0 17030 if ( ( $_[0]->{PROTOCOL} eq 'IPv4' ) && ( $_[1]->{PROTOCOL} eq 'IPv4' ) ) {
    100 100        
932             # IPv4
933 7813   100     13808 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
934             } elsif ( ( $_[0]->{PROTOCOL} eq 'IPv6' ) && ( $_[1]->{PROTOCOL} eq 'IPv6' ) ) {
935             # IPv6
936 36   100     97 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
937             } else {
938             # IPv4 to IPv6, order by protocol
939 6         12 return ( $_[0]->{PROTOCOL} cmp $_[1]->{PROTOCOL} );
940             }
941             }
942              
943             sub sort_network_blocks {
944 52         181 return map { $_->[0] }
945 64 50 66     403 sort { $a->[3] cmp $b->[3] || $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
946 16     16 1 26 map { [ $_, $_->{IBASE}, $_->{BITS}, $_->{PROTOCOL} ] } @_;
  52         163  
947             }
948              
949             sub sort_by_ip_address {
950 500         548 return map { $_->[0] }
951 3881         3879 sort { $a->[1] cmp $b->[1] }
952 1     1 1 4 map { [ $_, pack( "C4", split( /\./, $_ ) ) ] } @_;
  500         1336  
953              
954             }
955              
956             sub split ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
957             {
958 14     14 1 15807 my ( $self, $parts ) = @_;
959              
960 14         44 my $num_ips = $self->size;
961              
962 14 100 100     4489 confess "Parts must be defined and greater than 0."
963             unless defined($parts) && $parts > 0;
964              
965 8 100       104 confess "Netmask only contains $num_ips IPs. Cannot split into $parts."
966             unless $num_ips >= $parts;
967              
968 6         423 my $log2 = _log2($parts);
969              
970 6 100       362 confess "Parts count must be a number of base 2. Got: $parts"
971             unless ( 2**$log2 ) == $parts;
972              
973 4         12 my $new_mask = $self->bits + $log2;
974              
975 516         1368 return map { Net::Netmask->new( $_ . "/" . $new_mask ) }
976 4         31 map { $self->nth( ( $num_ips / $parts ) * $_ ) } ( 0 .. ( $parts - 1 ) );
  516         1182  
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   58 my $n = shift;
984              
985 46         51 my $ret = 0;
986 46         173 $ret++ while ( $n >>= 1 );
987              
988 46         57 return $ret;
989             }
990              
991             BEGIN {
992 8     8   53789 for ( my $i = 0; $i <= 32; $i++ ) {
993 264         370 $imask[$i] = imask($i);
994 264         569 $imask2bits{ $imask[$i] } = $i;
995 264         360 $quadmask2bits{ int2quad( $imask[$i] ) } = $i;
996 264         426 $quadhostmask2bits{ int2quad( ~$imask[$i] ) } = $i;
997 264         1091 $size2bits{ 2**( 32 - $i ) } = $i;
998             }
999              
1000 8         49 for ( my $i = 0; $i <= 128; $i++ ) {
1001 1032         1054956 $i6mask[$i] = i6mask($i);
1002             }
1003             }
1004             1;