File Coverage

lib/Net/Netmask.pm
Criterion Covered Total %
statement 512 534 95.8
branch 296 336 88.1
condition 104 115 90.4
subroutine 63 65 96.9
pod 33 54 61.1
total 1008 1104 91.3


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.0000';
7 8     8   1613633 use 5.006_001;
  8         64  
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         15  
  8         687  
34             $debug = 1;
35              
36 8     8   47 use strict;
  8         14  
  8         207  
37 8     8   40 use warnings;
  8         22  
  8         221  
38 8     8   38 use Carp;
  8         15  
  8         513  
39 8     8   7640 use Math::BigInt;
  8         174501  
  8         42  
40 8     8   168628 use POSIX qw(floor);
  8         18  
  8         77  
41             use overload
42 8         83 '""' => \&desc,
43             '<=>' => \&cmp_net_netmask_block,
44             'cmp' => \&cmp_net_netmask_block,
45 8     8   12695 'fallback' => 1;
  8         16  
46              
47             sub new {
48 1846     1846 0 50890 my ( $package, $net, @params) = @_;
49              
50 1846         2555 my $mask = '';
51 1846 100       4030 if (@params % 2) {
52 62         120 $mask = shift(@params);
53 62 100       156 $mask = '' if !defined($mask);
54             }
55 1846         2803 my (%options) = @params;
56 1846   100     6309 my $shortnet = ( ( exists($options{shortnet}) && $options{shortnet} ) || $SHORTNET_DEFAULT );
57              
58 1846         3436 my $base;
59             my $bits;
60 1846         0 my $ibase;
61 1846         2307 my $proto = 'IPv4';
62 1846         2327 undef $error;
63              
64 1846 100 100     22352 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 410         1048 ( $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 4         14 $base = $1;
68 4         10 my $quadmask = $2;
69 4 100       13 if ( exists $quadmask2bits{$quadmask} ) {
70 3         6 $bits = $quadmask2bits{$quadmask};
71             } else {
72 1         5 $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 3         9 $base = $1;
76 3         8 my $hostmask = $2;
77 3 100       11 if ( exists $quadhostmask2bits{$hostmask} ) {
78 2         4 $bits = $quadhostmask2bits{$hostmask};
79             } else {
80 1         4 $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 5         11 $base = $net;
86 5 100       15 if ( exists $quadmask2bits{$mask} ) {
87 4         9 $bits = $quadmask2bits{$mask};
88             } else {
89 1         5 $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 4         7 $base = $net;
95 4         12 my $imask = hex($mask);
96 4 100       14 if ( exists $imask2bits{$imask} ) {
97 3         7 $bits = $imask2bits{$imask};
98             } else {
99 1         36 $error = "illegal netmask: $mask ($imask)";
100             }
101             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask ) {
102 766         1316 ( $base, $bits ) = ( $net, 32 );
103             } elsif ( $net =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ && !$mask && $shortnet ) {
104 2         7 ( $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         8 ( $base, $bits ) = ( "$net.0.0.0", 8 );
109             } elsif ( $net =~ m,^([0-9]+\.[0-9]+\.[0-9]+)/([0-9]+)$, && $shortnet ) {
110 2         9 ( $base, $bits ) = ( "$1.0", $2 );
111             } elsif ( $net =~ m,^([0-9]+\.[0-9]+)/([0-9]+)$, && $shortnet ) {
112 2         10 ( $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         14 ( $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 8         25 $ibase = quad2int($1);
121 8         19 my $end = quad2int($2);
122 8 100 100     59 $error = "illegal dotted quad: $net"
123             unless defined($ibase) && defined($end);
124 8   100     34 my $diff = ( $end || 0 ) - ( $ibase || 0 ) + 1;
      100        
125 8         21 $bits = $size2bits{$diff};
126 8 100 100     47 $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 323 50       741 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
133 323         1070 ( $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       650 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
137 272         814 ( $base, $bits, $proto ) = ( $1, 128, 'IPv6' );
138             } elsif ( $net eq 'default6' || $net eq 'any6' ) {
139 4 50       14 if ( $mask ne '' ) { $error = "mask ignored for IPv6 address" }
  0         0  
140 4         12 ( $base, $bits, $proto ) = ( "::", 0, 'IPv6' );
141             } else {
142 30         84 $error = "could not parse $net";
143 30 100       87 $error .= " $mask" if $mask;
144             }
145              
146 1846 50 66     3214 carp $error if $error && $debug;
147              
148 1846 100       3175 $bits = 0 unless $bits;
149 1846 100 100     7562 if ( ( $proto eq 'IPv4' ) && ( $bits > 32 ) ) {
    50 66        
150 1 50       6 $error = "illegal number of bits: $bits"
151             unless $error;
152 1         3 $bits = 32;
153             } elsif ( ( $proto eq 'IPv6' ) && ( $bits > 128 ) ) {
154 0 0       0 $error = "illegal number of bits: $bits"
155             unless $error;
156 0         0 $bits = 128;
157             }
158              
159 1846 100 100     5012 $ibase = ascii2int( ( $base || '::' ), $proto ) unless defined $ibase;
160 1846 100 100     218679 unless ( defined($ibase) || defined($error) ) {
161 12         40 $error = "could not parse $net";
162 12 100       30 $error .= " $mask" if $mask;
163             }
164              
165 1846         3234 $ibase = i_getnet_addr( $ibase, $bits, $proto );
166              
167 1846 100       509674 return bless {
168             'IBASE' => $ibase,
169             'BITS' => $bits,
170             'PROTOCOL' => $proto,
171             ( $error ? ( 'ERROR' => $error ) : () ),
172             };
173             }
174              
175             sub i_getnet_addr {
176 17524     17524 0 27106 my ( $ibase, $bits, $proto ) = @_;
177              
178 17524 100       26381 if ( !defined($ibase) ) { return; }
  43         77  
179              
180 17481 100       26811 if ( $proto eq 'IPv4' ) {
181 3655         5225 return $ibase & $imask[$bits];
182             } else {
183 13826         30854 return $ibase & $i6mask[$bits];
184             }
185             }
186              
187             sub new2 {
188 102     102 0 24008 local ($debug) = 0;
189 102         261 my $net = new(@_);
190 102 100       334 return if $error;
191 51         134 return $net;
192             }
193              
194 51     51 0 18510 sub errstr { return $error; }
195 2 50   2 0 175 sub debug { my $this = shift; return ( @_ ? $debug = shift : $debug ) }
  2         22  
196              
197 1021     1021 1 2442 sub base { my ($this) = @_; return int2ascii( $this->{IBASE}, $this->{PROTOCOL} ); }
  1021         1300  
198 25     25 1 71 sub bits { my ($this) = @_; return $this->{'BITS'}; }
  25         109  
199 21     21 1 44 sub protocol { my ($this) = @_; return $this->{'PROTOCOL'}; }
  21         92  
200              
201             sub size {
202 700     700 1 1144 my ($this) = @_;
203              
204 700 100       1439 if ( $this->{PROTOCOL} eq 'IPv4' ) {
205 392         883 return 2**( 32 - $this->{'BITS'} );
206             } else {
207 308         772 return Math::BigInt->new(2)->bpow( 128 - $this->{'BITS'} );
208             }
209             }
210              
211             sub next { ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
212 3     3 1 15 my ($this) = @_;
213             # TODO: CONSOLIDATE
214 3 100       10 if ( $this->{PROTOCOL} eq 'IPv4' ) {
215 1         5 return int2quad( $this->{'IBASE'} + $this->size() );
216             } else {
217 2         9 return $this->_ipv6next( $this->size );
218             }
219             }
220              
221             sub broadcast {
222 2     2 1 17 my ($this) = @_;
223              
224 2         10 return int2ascii( $this->{'IBASE'} + $this->size() - 1, $this->{PROTOCOL} );
225             }
226              
227             *first = \&base;
228             *last = \&broadcast;
229              
230             sub desc {
231 429     429 1 14394 return int2ascii( $_[0]->{IBASE}, $_[0]->{PROTOCOL} ) . '/' . $_[0]->{BITS};
232             }
233              
234             sub imask {
235 264     264 0 439 return ( 2**32 - ( 2**( 32 - $_[0] ) ) );
236             }
237              
238             sub i6mask {
239 1032     1032 0 1347 my $bits = shift;
240 1032         2171 return Math::BigInt->new(2)->bpow(128) - Math::BigInt->new(2)->bpow( 128 - $bits );
241             }
242              
243             sub mask {
244 21     21 1 49 my ($this) = @_;
245              
246 21 100       59 if ( $this->{PROTOCOL} eq 'IPv4' ) {
247 15         57 return int2quad( $imask[ $this->{'BITS'} ] );
248             } else {
249 6         22 return int2ascii( $i6mask[ $this->{'BITS'} ], $this->{PROTOCOL} );
250             }
251             }
252              
253             sub hostmask {
254 4     4 1 3172 my ($this) = @_;
255              
256 4 100       15 if ( $this->{PROTOCOL} eq 'IPv4' ) {
257 1         5 return int2quad( ~$imask[ $this->{BITS} ] );
258             } else {
259 3         17 return int2ascii( $i6mask[ $this->{BITS} ] ^ $i6mask[128], $this->{PROTOCOL} );
260             }
261             }
262              
263             sub nth {
264 528     528 1 102386 my ( $this, $index, $bitstep ) = @_;
265              
266 528 100       1118 my $maxbits = $this->{PROTOCOL} eq 'IPv4' ? 32 : 128;
267              
268 528         976 my $size = $this->size();
269 528         83117 my $ibase = $this->{'IBASE'};
270 528 100       1066 $bitstep = $maxbits unless $bitstep;
271 528         836 my $increment = 2**( $maxbits - $bitstep );
272 528         996 $index *= $increment;
273 528 100       41657 $index += $size if $index < 0;
274 528 100       41872 return if $index < 0;
275 526 100       40048 return if $index >= $size;
276              
277 524         8108 my $i = $ibase + $index;
278 524         22134 return int2ascii( $i, $this->{PROTOCOL} );
279             }
280              
281             sub enumerate {
282 5     5 1 961 my ( $this, $bitstep ) = @_;
283 5         9 my $proto = $this->{PROTOCOL};
284              
285             # Set default step size by protocol
286 5 100       18 $bitstep = ( $proto eq 'IPv4' ? 32 : 128 ) unless $bitstep;
    100          
287              
288 5         14 my $size = $this->size();
289              
290 5         1033 my @ary;
291             ### We should be able to consolidate this
292 5 100       15 if ( $proto eq 'IPv4' ) {
293 3         6 my $increment = 2**( 32 - $bitstep );
294 3         4 my $ibase = $this->{'IBASE'};
295 3         10 for ( my $i = 0; $i < $size; $i += $increment ) {
296 8240         12246 push( @ary, int2quad( $ibase + $i ) );
297             }
298             } else {
299 2         10 my $increment = Math::BigInt->new(2)->bpow( 128 - $bitstep );
300              
301 2 100       636 if ( ( $size / $increment ) > 1_000_000_000 ) {
302             # Let's help the user out and catch really obvious issues.
303             # Asking for a billion IP addresses is probably one of them.
304             #
305             # That said, please contact the author if this number causes
306             # you issues!
307 1         588 confess("More than 1,000,000,000 results would be returned, dieing");
308             }
309              
310 1         448 for ( my $i = Math::BigInt->new(0); $i < $size; $i += $increment ) {
311 256         22984 push( @ary, $this->_ipv6next($i) );
312             }
313             }
314 4         2009 return @ary;
315             }
316              
317             sub _ipv6next {
318 258     258   1294 my ( $this, $bitstep ) = @_;
319              
320 258         397 my $istart = $this->{IBASE};
321 258         438 my $val = $istart + $bitstep;
322              
323 258         17547 return ipv6Cannonical( int2ascii( $val, $this->{PROTOCOL} ) );
324             }
325              
326             sub inaddr {
327 8     8 1 1080 my ($this) = @_;
328              
329 8 100       24 if ( $this->{PROTOCOL} eq 'IPv4' ) {
330 4         11 return $this->inaddr4();
331             } else {
332 4         16 return $this->inaddr6();
333             }
334             }
335              
336             sub inaddr4 {
337 4     4 0 6 my ($this) = @_;
338 4         8 my $ibase = $this->{'IBASE'};
339 4         6 my $blocks = floor( $this->size() / 256 );
340             return (
341 4 100       26 join( '.', unpack( 'xC3', pack( 'V', $ibase ) ) ) . ".in-addr.arpa",
342             $ibase % 256,
343             $ibase % 256 + $this->size() - 1
344             ) if $blocks == 0;
345 1         3 my @ary;
346 1         6 for ( my $i = 0; $i < $blocks; $i++ ) {
347 32         143 push( @ary,
348             join( '.', unpack( 'xC3', pack( 'V', $ibase + $i * 256 ) ) ) . ".in-addr.arpa",
349             0, 255 );
350             }
351 1         11 return @ary;
352             }
353              
354             sub inaddr6 {
355 4     4 0 9 my ($this) = @_;
356              
357 4         15 my (@digits) = split //, $this->{IBASE}->to_hex;
358              
359 4         998 my $static = floor( $this->{BITS} / 4 );
360 4         16 my $len = floor( ( $static + 3 ) / 4 );
361 4         12 my $remainder = $this->{BITS} % 4;
362 4 100       13 my $blocks = $remainder ? ( 2**( 4 - $remainder ) ) : 1;
363              
364 4         7 my @tail;
365 4 100       16 if ( !$len ) {
366             # Specal case: 0 len
367 1         13 return ('ip6.arpa');
368             }
369 3         30 push @tail, reverse( @digits[ 0 .. ( $static - 1 ) ] ), 'ip6.arpa';
370              
371 3 100       9 if ( !$remainder ) {
372             # Special case - at nibble boundary already
373 2         16 return ( join '.', @tail );
374             }
375              
376 1         4 my $last = hex $digits[$static];
377 1         3 my @ary;
378 1         5 for ( my $i = 0; $i < $blocks; $i++ ) {
379 8         20 push @ary, join( '.', sprintf( "%x", $last ), @tail );
380 8         15 $last++;
381             }
382              
383 1         7 return @ary;
384             }
385              
386             sub tag {
387 11     11 1 201 my $this = shift;
388 11         15 my $tag = shift;
389 11         16 my $val = $this->{ 'T' . $tag };
390 11 100       22 $this->{ 'T' . $tag } = $_[0] if @_;
391 11         25 return $val;
392             }
393              
394             sub quad2int {
395 1350     1350 0 3311 my @bytes = split( /\./, $_[0] );
396              
397 1350 100       2470 return unless @bytes == 4;
398 1318 100 100     1986 return unless !grep { !( /^(([0-9])|([1-9][0-9]*))$/ && $_ < 256 ) } @bytes;
  5272         20899  
399              
400 1304         4347 return unpack( "N", pack( "C4", @bytes ) );
401             }
402              
403             sub int2quad {
404 8785     8785 0 28052 return join( '.', unpack( 'C4', pack( "N", $_[0] ) ) );
405             }
406              
407             # Uses the internal "raw" representation (such as IBASE).
408             # For IPv4, this is an integer
409             # For IPv6, this is a raw bit string.
410             sub int2ascii {
411 2261 100   2261 0 21964 if ( $_[1] eq 'IPv4' ) {
    50          
412 1561         6906 return join( '.', unpack( 'C4', pack( "N", $_[0] ) ) );
413             } elsif ( $_[1] eq 'IPv6' ) {
414 700 100       2136 my $addr = ( ref $_[0] ) ne '' ? $_[0]->to_hex : Math::BigInt->new( $_[0] )->to_hex;
415 700         221044 return ipv6Cannonical($addr);
416             } else {
417 0         0 confess("Incorrect call");
418             }
419             }
420              
421             # Produces the internal "raw" representation (such as IBASE).
422             # For IPv4, this is an integer
423             # For IPv6, this is a raw bit string.
424             sub ascii2int {
425 1966 100   1966 0 6384 if ( $_[1] eq 'IPv4' ) {
    50          
426 1334         2103 return quad2int( $_[0] );
427             } elsif ( $_[1] eq 'IPv6' ) {
428 632         1300 return ipv6ascii2int( $_[0] );
429             } else {
430 0         0 confess("Incorrect call");
431             }
432             }
433              
434             # Take an IPv6 ASCII address and produce a raw value
435             sub ipv6ascii2int {
436 632     632 0 1174 my $addr = shift;
437              
438 632         1135 $addr = ipv6NonCompacted($addr);
439 632         1857 $addr = join '', map { sprintf( "%04x", hex($_) ) } split( /:/, $addr );
  5056         11274  
440              
441 632         2805 return Math::BigInt->from_hex($addr);
442             }
443              
444             # Takes an IPv6 address and produces a standard version seperated by
445             # colons (without compacting)
446             sub ipv6NonCompacted {
447 1607     1607 0 2304 my $addr = shift;
448              
449 1607 100       4140 if ( $addr !~ /:/ ) {
450 700 100       1254 if ( length($addr) < 32 ) {
451 36         134 $addr = ( "0" x ( 32 - length($addr) ) ) . $addr;
452             }
453 700         7540 $addr =~ s/(....)(?=....)/$1:/gsx;
454             }
455              
456             # Handle address format with trailing IPv6
457             # Ex: 0:0:0:0:1.2.3.4
458 1607 100       3449 if ( $addr =~ m/^[0-9a-f:]+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/i ) {
459 2         11 my ( $l, $r1, $r2, $r3, $r4 ) =
460             $addr =~ m/^([0-9a-f:]+)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/i;
461 2         30 $addr = sprintf( "%s%02x%02x:%02x%02x", $l, $r1, $r2, $r3, $r4 );
462             }
463              
464 1607         4607 my ( $left, $right ) = split /::/, $addr;
465 1607 100       3120 if ( !defined($right) ) { $right = '' }
  723         988  
466 1607         3792 my (@lparts) = split /:/, $left;
467 1607         2634 my (@rparts) = split /:/, $right;
468              
469             # Strip leading 0's & lowercase
470 1607         2718 @lparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @lparts;
  8140         16988  
  8140         15757  
471 1607         2561 @rparts = map { $_ =~ s/^0+([0-9a-f]+)/$1/; lc($_) } @rparts;
  562         805  
  562         1135  
472              
473             # Expand ::
474 1607         2582 my $missing = 8 - ( @lparts + @rparts );
475 1607 100       2738 if ($missing) {
476 885         2793 $addr = join ':', @lparts, ( 0, 0, 0, 0, 0, 0, 0, 0 )[ 0 .. $missing - 1 ], @rparts;
477             } else {
478 722         1713 $addr = join ':', @lparts, @rparts;
479             }
480              
481 1607         4395 return $addr;
482             }
483              
484             # Compacts an IPv6 address (reduces successive :0: runs)
485             sub ipv6AsciiCompact {
486 975     975 0 1296 my $addr = shift;
487              
488             # Compress, per RFC5952
489 975 100       6064 if ( $addr =~ s/^0:0:0:0:0:0:0:0$/::/ ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
490 17         36 return $addr;
491             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0:0:0(:?:|$)/::/ ) {
492 2         8 return $addr;
493             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0:0(:?:|$)/::/ ) {
494 11         31 return $addr;
495             } elsif ( $addr =~ s/(:?^|:)0:0:0:0:0(:?:|$)/::/ ) {
496 285         777 return $addr;
497             } elsif ( $addr =~ s/(:?^|:)0:0:0:0(:?:|$)/::/ ) {
498 567         1063 return $addr;
499             } elsif ( $addr =~ s/(:?^|:)0:0:0(:?:|$)/::/ ) {
500 39         87 return $addr;
501             } elsif ( $addr =~ s/(:?^|:)0:0(:?:|$)/::/ ) {
502 29         64 return $addr;
503             } elsif ( $addr =~ s/(:?^|:)0(:?:|$)/::/ ) {
504 16         34 return $addr;
505             }
506 9         22 return $addr;
507             }
508             # Cannonicalize IPv6 addresses in ascii format
509             sub ipv6Cannonical {
510 975     975 0 12111 my $addr = shift;
511              
512 975         1456 $addr = ipv6NonCompacted($addr);
513 975         1660 $addr = ipv6AsciiCompact($addr);
514              
515 975         4754 return $addr;
516             }
517              
518             # IPv6 addresses are stored with a leading zero.
519             sub storeNetblock {
520 44     44 1 526 my ( $this, $t ) = @_;
521 44 100       74 $t = $remembered unless $t;
522              
523 44         60 my $base = $this->{'IBASE'};
524 44 100       81 if ( $this->{PROTOCOL} eq 'IPv6' ) {
525 13         36 $base = "0$base";
526             }
527              
528 44 100       471 $t->{$base} = [] unless exists $t->{$base};
529              
530 44         74 my $mb = maxblock($this);
531 44         78 my $bits = $this->{'BITS'};
532 44         54 my $i = $bits - $mb;
533              
534 44         97 return ( $t->{$base}[$i] = $this );
535             }
536              
537             sub deleteNetblock {
538 13     13 1 28 my ( $this, $t ) = @_;
539 13 50       32 $t = $remembered unless $t;
540              
541 13         25 my $base = $this->{'IBASE'};
542 13 100       38 if ( $this->{PROTOCOL} eq 'IPv6' ) {
543 1         4 $base = "0$base";
544             }
545              
546 13         61 my $mb = maxblock($this);
547 13         22 my $bits = $this->{'BITS'};
548 13         18 my $i = $bits - $mb;
549              
550 13 50       31 return unless defined $t->{$base};
551              
552 13         24 undef $t->{$base}->[$i];
553              
554 13         19 for my $x ( @{ $t->{$base} } ) {
  13         37  
555 46 100       96 return if $x;
556             }
557 10         24 return delete $t->{$base};
558             }
559              
560             sub findNetblock {
561 54     54 1 5023 my ( $ascii, $t ) = @_;
562 54 100       129 $t = $remembered unless $t;
563              
564 54 100       170 my $proto = ( $ascii =~ m/:/ ) ? 'IPv6' : 'IPv4';
565              
566 54         126 my $ip = ascii2int( $ascii, $proto );
567 54 100       2507 return unless defined $ip;
568 53         75 my %done;
569              
570 53 100       97 my $maxbits = $proto eq 'IPv6' ? 128 : 32;
571 53         110 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
572 1089         1556 my $nb = i_getnet_addr( $ip, $bits, $proto );
573 1089 100       334226 if ( $proto eq 'IPv6' ) {
574 439         971 $nb = "0$nb";
575             }
576 1089 100       14107 next unless exists $t->{$nb};
577 44         87 my $mb = imaxblock( $nb, $maxbits, $proto );
578 44 100       152 next if $done{$mb}++;
579 41         61 my $i = $bits - $mb;
580 41         78 while ( $i >= 0 ) {
581             return $t->{$nb}->[$i]
582 308 100       538 if defined $t->{$nb}->[$i];
583 268         328 $i--;
584             }
585             }
586 13         47 return;
587             }
588              
589             sub findOuterNetblock {
590 56     56 1 1971 my ( $ipstr, $t ) = @_;
591 56 50       103 $t = $remembered unless $t;
592              
593 56         133 my $proto;
594             my $maxbits;
595              
596 56         0 my $ip;
597 56         0 my $len;
598 56 100       85 if ( ref($ipstr) ) {
599 28         42 $proto = $ipstr->{PROTOCOL};
600 28 100       49 $maxbits = $proto eq 'IPv4' ? 32 : 128;
601 28         30 $ip = $ipstr->{IBASE};
602 28         36 $len = $ipstr->{BITS};
603             } else {
604 28 100       90 $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
605 28 100       55 $maxbits = $proto eq 'IPv4' ? 32 : 128;
606 28         50 $ip = ascii2int( $ipstr, $proto );
607 28         1206 $len = $maxbits;
608             }
609              
610 56         113 for ( my $bits = 0; $bits <= $len; $bits++ ) {
611 2028 100       3228 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
612 2028 100       451464 if ( $proto eq 'IPv6' ) {
613 646         1260 $nb = "0$nb";
614             }
615 2028 100       19407 next unless exists $t->{$nb};
616 74         112 my $mb = imaxblock( $nb, $len, $proto );
617 74         90 my $i = $bits - $mb;
618 74 50       108 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
619 74 50       98 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
620 74         96 while ( $i >= 0 ) {
621             return $t->{$nb}->[$i]
622 173 100       280 if defined $t->{$nb}->[$i];
623 151         220 $i--;
624             }
625             }
626 34         66 return;
627             }
628              
629             sub findAllNetblock {
630 2     2 1 10 my ( $ipstr, $t ) = @_;
631 2 50       6 $t = $remembered unless $t;
632              
633 2 100       7 my $proto = ( $ipstr =~ m/:/ ) ? 'IPv6' : 'IPv4';
634 2 100       5 my $maxbits = $proto eq 'IPv4' ? 32 : 128;
635              
636 2         45 my $ip = ascii2int( $ipstr, $proto );
637              
638 2         336 my %done;
639             my @ary;
640 2         6 for ( my $bits = $maxbits; $bits >= 0; $bits-- ) {
641 162 100       791 my $nb = $ip & ( $proto eq 'IPv4' ? $imask[$bits] : $i6mask[$bits] );
642 162 100       97387 if ( $proto eq 'IPv6' ) {
643 129         582 $nb = "0$nb";
644             }
645 162 100       5396 next unless exists $t->{$nb};
646 109         409 my $mb = imaxblock( $nb, $maxbits, $proto );
647 109 100       838 next if $done{$mb}++;
648 3         6 my $i = $bits - $mb;
649 3 50       7 confess "$mb, $bits, $ipstr, $nb" if $i < 0;
650 3 50       11 confess "$mb, $bits, $ipstr, $nb" if $i > $maxbits;
651 3         12 while ( $i >= 0 ) {
652             push( @ary, $t->{$nb}->[$i] )
653 109 100       148 if defined $t->{$nb}->[$i];
654 109         133 $i--;
655             }
656             }
657 2         20 return @ary;
658             }
659              
660             sub dumpNetworkTable {
661 1     1 1 2 my ($t) = @_;
662 1 50       5 $t = $remembered unless $t;
663              
664 1         2 my @ary;
665 1         7 foreach my $base ( keys %$t ) {
666 6         8 push @ary, grep { defined($_) } @{ $t->{base} };
  0         0  
  6         11  
667 6         8 for my $x ( @{ $t->{$base} } ) {
  6         10  
668 19 100       39 push( @ary, $x )
669             if defined $x;
670             }
671             }
672              
673 1         6 return ( sort @ary );
674             }
675              
676             sub checkNetblock {
677 0     0 1 0 my ( $this, $t ) = @_;
678 0 0       0 $t = $remembered unless $t;
679              
680 0         0 my $base = $this->{'IBASE'};
681              
682 0         0 my $mb = maxblock($this);
683 0         0 my $bits = $this->{'BITS'};
684 0         0 my $i = $bits - $mb;
685              
686 0         0 return defined $t->{$base}->[$i];
687             }
688              
689             sub match {
690 24     24 1 2645 my ( $this, $ip ) = @_;
691 24         45 my $proto = $this->{PROTOCOL};
692              
693             # Two different protocols: return undef
694 24 100       73 if ( $ip =~ /:/ ) {
695 10 50       28 if ( $proto ne 'IPv6' ) { return }
  0         0  
696             } else {
697 14 50       32 if ( $proto ne 'IPv4' ) { return }
  0         0  
698             }
699              
700 24         54 my $i = ascii2int( $ip, $this->{PROTOCOL} );
701 24         3442 my $ia = i_getnet_addr( $i, $this->{BITS}, $proto );
702              
703 24 100       7068 if ( $proto eq 'IPv4' ) {
704 14 100       27 if ( $ia == $this->{IBASE} ) {
705 8   100     48 return ( ( $i & ~( $this->{IBASE} ) ) || "0 " );
706             } else {
707 6         26 return 0;
708             }
709             } else {
710 10 100       26 if ( $ia == $this->{IBASE} ) {
711 6   100     317 return ( ( $i - $this->{IBASE} ) || "0 " );
712             } else {
713 4         164 return 0;
714             }
715             }
716             }
717              
718             sub maxblock {
719 78     78 1 137 my ($this) = @_;
720             return ( !defined $this->{ERROR} )
721             ? imaxblock( $this->{IBASE}, $this->{BITS}, $this->{PROTOCOL} )
722 78 50       285 : undef;
723             }
724              
725             sub nextblock {
726 8     8 1 2617 my ( $this, $index ) = @_;
727 8 100       22 $index = 1 unless defined $index;
728 8         16 my $ibase = $this->{IBASE};
729 8 100       19 if ( $this->{PROTOCOL} eq 'IPv4' ) {
730 4         13 $ibase += $index * 2**( 32 - $this->{BITS} );
731             } else {
732 4         16 $ibase += $index * Math::BigInt->new(2)->bpow( 128 - $this->{BITS} );
733             }
734              
735             my $newblock = bless {
736             IBASE => $ibase,
737             BITS => $this->{BITS},
738             PROTOCOL => $this->{PROTOCOL},
739 8         2936 };
740              
741 8 100       24 if ( $this->{PROTOCOL} eq 'IPv4' ) {
742 4 50       13 return if $newblock->{IBASE} >= 2**32;
743             } else {
744 4 50       13 return if $newblock->{IBASE} >= Math::BigInt->new(2)->bpow(128);
745             }
746              
747 8 50       2278 return if $newblock->{IBASE} < 0;
748 8         707 return $newblock;
749             }
750              
751             sub imaxblock {
752 425     425 0 1004 my ( $ibase, $tbit, $proto ) = @_;
753 425 50       933 confess unless defined $ibase;
754              
755 425 50       818 if ( !defined($proto) ) { $proto = 'IPv4'; }
  0         0  
756              
757 425         876 while ( $tbit > 0 ) {
758 14565         27747 my $ia = i_getnet_addr( $ibase, $tbit - 1, $proto );
759 14565 100       9927063 last if ( $ia != $ibase );
760 14152         1606739 $tbit--;
761             }
762 425         21446 return $tbit;
763             }
764              
765             sub range2cidrlist {
766 5     5 1 2439 my ( $startip, $endip ) = @_;
767              
768 5         7 my $proto;
769 5 100       16 if ( $startip =~ m/:/ ) {
770 2 50       8 if ( $endip =~ m/:/ ) { $proto = 'IPv6'; }
  2         3  
771             } else {
772 3 50       8 if ( $endip !~ m/:/ ) { $proto = 'IPv4'; }
  3         5  
773             }
774 5 50       13 if ( !defined($proto) ) { confess("Cannot mix IPv4 and IPv6 in range2cidrlist()"); }
  0         0  
775              
776 5         10 my $start = ascii2int( $startip, $proto );
777 5         766 my $end = ascii2int( $endip, $proto );
778              
779 5 100       695 ( $start, $end ) = ( $end, $start )
780             if $start > $end;
781 5         108 return irange2cidrlist( $start, $end, $proto );
782             }
783              
784             sub irange2cidrlist {
785 39     39 0 281 my ( $start, $end, $proto ) = @_;
786 39 50       112 if ( !defined($proto) ) { $proto = 'IPv4' }
  0         0  
787              
788 39 100       84 my $bits = $proto eq 'IPv4' ? 32 : 128;
789              
790 39         49 my @result;
791 39         91 while ( $end >= $start ) {
792 120         32601 my $maxsize = imaxblock( $start, $bits, $proto );
793 120         173 my $maxdiff;
794 120 100       263 if ( $proto eq 'IPv4' ) {
795 40         90 $maxdiff = $bits - _log2( $end - $start + 1 );
796             } else {
797 80         290 $maxdiff = $bits - ( $end - $start + 1 )->blog(2);
798             }
799 120 100       77899 $maxsize = $maxdiff if $maxsize < $maxdiff;
800 120         5421 push(
801             @result,
802             bless {
803             'IBASE' => $start,
804             'BITS' => $maxsize,
805             'PROTOCOL' => $proto,
806             }
807             );
808 120 100       282 if ( $proto eq 'IPv4' ) {
809 40         98 $start += 2**( 32 - $maxsize );
810             } else {
811 80         219 $start += Math::BigInt->new(2)->bpow( $bits - $maxsize );
812             }
813             }
814 39         4489 return @result;
815             }
816              
817             sub cidrs2contiglists {
818 1     1 1 344 my (@cidrs) = sort_network_blocks(@_);
819 1         3 my @result;
820 1         4 while (@cidrs) {
821 2         4 my (@r) = shift(@cidrs);
822 2         7 my $max = $r[0]->{IBASE} + $r[0]->size;
823 2   100     5 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
824 1         5 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
825 1 50       4 $max = $nm if $nm > $max;
826 1         3 push( @r, shift(@cidrs) );
827             }
828 2         6 push( @result, [@r] );
829             }
830 1         3 return @result;
831             }
832              
833             sub cidrs2cidrs {
834 15     15 1 42 my (@cidrs) = sort_network_blocks(@_);
835 15         43 my @result;
836              
837             my $proto;
838 15 50       50 if ( scalar(@cidrs) ) {
839 15         42 $proto = $cidrs[0]->{PROTOCOL};
840 15 50       25 if ( grep { $proto ne $_->{PROTOCOL} } @cidrs ) {
  49         97  
841 0         0 confess("Cannot call cidrs2cidrs with mixed protocol arguments");
842             }
843             }
844              
845 15         36 while (@cidrs) {
846 24         44 my (@r) = shift(@cidrs);
847              
848 24         84 my $max = $r[0]->{IBASE} + $r[0]->size;
849 24   100     2093 while ( $cidrs[0] && $cidrs[0]->{IBASE} <= $max ) {
850 25         225 my $nm = $cidrs[0]->{IBASE} + $cidrs[0]->size;
851 25 100       1421 $max = $nm if $nm > $max;
852 25         227 push( @r, shift(@cidrs) );
853             }
854 24         135 my $start = $r[0]->{IBASE};
855 24         40 my $end = $max - 1;
856 24         1032 push( @result, irange2cidrlist( $start, $end, $proto ) );
857             }
858 15         157 return @result;
859             }
860              
861             sub cidrs2inverse {
862 10     10 1 43 my $outer = shift;
863 10 100 33     33 $outer = __PACKAGE__->new2($outer) || croak($error) unless ref($outer);
864              
865             # cidrs2cidrs validates that everything is in the same address
866             # family
867 10         32 my (@cidrs) = cidrs2cidrs(@_);
868 10         16 my $proto;
869 10 50       24 if ( scalar(@cidrs) ) {
870 10         26 $proto = $cidrs[0]->{PROTOCOL};
871             }
872              
873 10         20 my $first = $outer->{IBASE};
874 10         25 my $last = $first + $outer->size() - 1;
875 10   66     640 shift(@cidrs) while $cidrs[0] && $cidrs[0]->{IBASE} + $cidrs[0]->size < $first;
876 10         517 my @r;
877 10   66     52 while ( @cidrs && $first <= $last ) {
878              
879 16 100       143 if ( $first < $cidrs[0]->{IBASE} ) {
880 6 100       57 if ( $last <= $cidrs[0]->{IBASE} - 1 ) {
881 2         7 return ( @r, irange2cidrlist( $first, $last, $proto ) );
882             }
883 4         263 push( @r, irange2cidrlist( $first, $cidrs[0]->{IBASE} - 1, $proto ) );
884             }
885 14 50       76 last if $cidrs[0]->{IBASE} > $last;
886 14         140 $first = $cidrs[0]->{IBASE} + $cidrs[0]->size;
887 14         869 shift(@cidrs);
888             }
889 8 100       20 if ( $first <= $last ) {
890 4         57 push( @r, irange2cidrlist( $first, $last, $proto ) );
891             }
892 8         48 return @r;
893             }
894              
895             sub by_net_netmask_block {
896             return $a->{'IBASE'} <=> $b->{'IBASE'}
897 0   0 0 0 0 || $a->{'BITS'} <=> $b->{'BITS'};
898             }
899              
900             sub sameblock {
901 29     29 1 62 return !cmpblocks(@_);
902             }
903              
904             sub cmpblocks {
905 45     45 1 73 my $this = shift;
906 45         70 my $class = ref $this;
907 45 100       171 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
908 45         100 return cmp_net_netmask_block( $this, $other );
909             }
910              
911             sub contains {
912 66     66 1 353 my $this = shift;
913 66         99 my $class = ref $this;
914 66 100       127 my $other = ( ref $_[0] ) ? shift : $class->new(@_);
915 66 100       173 return 0 if $this->{IBASE} > $other->{IBASE};
916 51 100       830 return 0 if $this->{BITS} > $other->{BITS};
917 46 100       89 return 0 if $other->{IBASE} > $this->{IBASE} + $this->size - 1;
918 40         9344 return 1;
919             }
920              
921             sub cmp_net_netmask_block {
922 7833 100 100 7833 0 16984 if ( ( $_[0]->{PROTOCOL} eq 'IPv4' ) && ( $_[1]->{PROTOCOL} eq 'IPv4' ) ) {
    100 100        
923             # IPv4
924 7793   100     13324 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
925             } elsif ( ( $_[0]->{PROTOCOL} eq 'IPv6' ) && ( $_[1]->{PROTOCOL} eq 'IPv6' ) ) {
926             # IPv6
927 36   100     118 return ( $_[0]->{IBASE} <=> $_[1]->{IBASE} || $_[0]->{BITS} <=> $_[1]->{BITS} );
928             } else {
929             # IPv4 to IPv6, order by protocol
930 4         10 return ( $_[0]->{PROTOCOL} cmp $_[1]->{PROTOCOL} );
931             }
932             }
933              
934             sub sort_network_blocks {
935 52         181 return map { $_->[0] }
936 64 50 66     405 sort { $a->[3] cmp $b->[3] || $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
937 16     16 1 29 map { [ $_, $_->{IBASE}, $_->{BITS}, $_->{PROTOCOL} ] } @_;
  52         165  
938             }
939              
940             sub sort_by_ip_address {
941 500         527 return map { $_->[0] }
942 3870         3925 sort { $a->[1] cmp $b->[1] }
943 1     1 1 5 map { [ $_, pack( "C4", split( /\./, $_ ) ) ] } @_;
  500         1763  
944              
945             }
946              
947             sub split ## no critic: (Subroutines::ProhibitBuiltinHomonyms)
948             {
949 14     14 1 18210 my ( $self, $parts ) = @_;
950              
951 14         39 my $num_ips = $self->size;
952              
953 14 100 100     3727 confess "Parts must be defined and greater than 0."
954             unless defined($parts) && $parts > 0;
955              
956 8 100       130 confess "Netmask only contains $num_ips IPs. Cannot split into $parts."
957             unless $num_ips >= $parts;
958              
959 6         376 my $log2 = _log2($parts);
960              
961 6 100       452 confess "Parts count must be a number of base 2. Got: $parts"
962             unless ( 2**$log2 ) == $parts;
963              
964 4         14 my $new_mask = $self->bits + $log2;
965              
966 516         1647 return map { Net::Netmask->new( $_ . "/" . $new_mask ) }
967 4         50 map { $self->nth( ( $num_ips / $parts ) * $_ ) } ( 0 .. ( $parts - 1 ) );
  516         1393  
968             }
969              
970             # Implement log2 sub routine directly, to avoid precision problems with floor()
971             # problems with perls built with uselongdouble defined.
972             # Credit: xenu, on IRC
973             sub _log2 {
974 46     46   68 my $n = shift;
975              
976 46         50 my $ret = 0;
977 46         184 $ret++ while ( $n >>= 1 );
978              
979 46         73 return $ret;
980             }
981              
982             BEGIN {
983 8     8   55804 for ( my $i = 0; $i <= 32; $i++ ) {
984 264         402 $imask[$i] = imask($i);
985 264         622 $imask2bits{ $imask[$i] } = $i;
986 264         721 $quadmask2bits{ int2quad( $imask[$i] ) } = $i;
987 264         477 $quadhostmask2bits{ int2quad( ~$imask[$i] ) } = $i;
988 264         1206 $size2bits{ 2**( 32 - $i ) } = $i;
989             }
990              
991 8         42 for ( my $i = 0; $i <= 128; $i++ ) {
992 1032         960171 $i6mask[$i] = i6mask($i);
993             }
994             }
995             1;