File Coverage

blib/lib/Net/CIDR.pm
Criterion Covered Total %
statement 213 457 46.6
branch 77 214 35.9
condition 29 144 20.1
subroutine 13 22 59.0
pod 8 8 100.0
total 340 845 40.2


line stmt bran cond sub pod time code
1             # Net::CIDR
2             #
3             # Copyright 2001-2018 Sam Varshavchik.
4             #
5             # with contributions from David Cantrell.
6             #
7             # This program is free software; you can redistribute it
8             # and/or modify it under the same terms as Perl itself.
9              
10             package Net::CIDR;
11              
12             require 5.000;
13             #use strict;
14             #use warnings;
15              
16             require Exporter;
17             # use AutoLoader qw(AUTOLOAD);
18 1     1   428 use Carp;
  1         1  
  1         3954  
19              
20             @ISA = qw(Exporter);
21              
22             # Items to export into callers namespace by default. Note: do not export
23             # names by default without a very good reason. Use EXPORT_OK instead.
24             # Do not simply export all your public functions/methods/constants.
25              
26             # This allows declaration use Net::CIDR ':all';
27             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
28             # will save memory.
29             %EXPORT_TAGS = ( 'all' => [ qw( range2cidr
30             cidr2range
31             cidr2octets
32             cidradd
33             cidrlookup
34             cidrvalidate
35             addr2cidr
36             addrandmask2cidr
37             ) ] );
38              
39             @EXPORT_OK = ( qw( range2cidr
40             cidr2range
41             cidr2octets
42             cidradd
43             cidrlookup
44             cidrvalidate
45             addr2cidr
46             addrandmask2cidr
47             ));
48              
49             @EXPORT = qw(
50              
51             );
52              
53             $VERSION = "0.19";
54              
55             1;
56              
57              
58             =pod
59              
60             =head1 NAME
61              
62             Net::CIDR - Manipulate IPv4/IPv6 netblocks in CIDR notation
63              
64             =head1 SYNOPSIS
65              
66             use Net::CIDR;
67              
68             use Net::CIDR ':all';
69              
70             print join("\n",
71             Net::CIDR::range2cidr("192.168.0.0-192.168.255.255",
72             "10.0.0.0-10.3.255.255"))
73             . "\n";
74             #
75             # Output from above:
76             #
77             # 192.168.0.0/16
78             # 10.0.0.0/14
79              
80             print join("\n",
81             Net::CIDR::range2cidr(
82             "dead:beef::-dead:beef:ffff:ffff:ffff:ffff:ffff:ffff"))
83             . "\n";
84              
85             #
86             # Output from above:
87             #
88             # dead:beef::/32
89              
90             print join("\n",
91             Net::CIDR::range2cidr("192.168.1.0-192.168.2.255"))
92             . "\n";
93             #
94             # Output from above:
95             #
96             # 192.168.1.0/24
97             # 192.168.2.0/24
98              
99             print join("\n", Net::CIDR::cidr2range("192.168.0.0/16")) . "\n";
100             #
101             # Output from above:
102             #
103             # 192.168.0.0-192.168.255.255
104              
105             print join("\n", Net::CIDR::cidr2range("dead::beef::/46")) . "\n";
106             #
107             # Output from above:
108             #
109             # dead:beef::-dead:beef:3:ffff:ffff:ffff:ffff:ffff
110              
111             @list=("192.168.0.0/24");
112             @list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @list);
113              
114             print join("\n", @list) . "\n";
115             #
116             # Output from above:
117             #
118             # 192.168.0.0/23
119              
120             print join("\n", Net::CIDR::cidr2octets("192.168.0.0/22")) . "\n";
121             #
122             # Output from above:
123             #
124             # 192.168.0
125             # 192.168.1
126             # 192.168.2
127             # 192.168.3
128              
129             print join("\n", Net::CIDR::cidr2octets("dead::beef::/46")) . "\n";
130             #
131             # Output from above:
132             #
133             # dead:beef:0000
134             # dead:beef:0001
135             # dead:beef:0002
136             # dead:beef:0003
137              
138             @list=("192.168.0.0/24");
139             print Net::CIDR::cidrlookup("192.168.0.12", @list);
140             #
141             # Output from above:
142             #
143             # 1
144              
145             @list = Net::CIDR::addr2cidr("192.168.0.31");
146             print join("\n", @list);
147             #
148             # Output from above:
149             #
150             # 192.168.0.31/32
151             # 192.168.0.30/31
152             # 192.168.0.28/30
153             # 192.168.0.24/29
154             # 192.168.0.16/28
155             # 192.168.0.0/27
156             # 192.168.0.0/26
157             # 192.168.0.0/25
158             # 192.168.0.0/24
159             # 192.168.0.0/23
160             # [and so on]
161              
162             print Net::CIDR::addrandmask2cidr("195.149.50.61", "255.255.255.248")."\n";
163             #
164             # Output from above:
165             #
166             # 195.149.50.56/29
167              
168             =head1 DESCRIPTION
169              
170             The Net::CIDR package contains functions that manipulate lists of IP
171             netblocks expressed in CIDR notation.
172             The Net::CIDR functions handle both IPv4 and IPv6 addresses.
173              
174             =head2 @cidr_list=Net::CIDR::range2cidr(@range_list);
175              
176             Each element in the @range_list is a string "start-finish", where
177             "start" is the first IP address and "finish" is the last IP address.
178             range2cidr() converts each range into an equivalent CIDR netblock.
179             It returns a list of netblocks except in the case where it is given
180             only one parameter and is called in scalar context.
181              
182             For example:
183              
184             @a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255");
185              
186             The result is a one-element array, with $a[0] being "192.168.0.0/16".
187             range2cidr() processes each "start-finish" element in @range_list separately.
188             But if invoked like so:
189              
190             $a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255");
191              
192             The result is a scalar "192.168.0.0/16".
193              
194             Where each element cannot be expressed as a single CIDR netblock
195             range2cidr() will generate as many CIDR netblocks as are necessary to cover
196             the full range of IP addresses. Example:
197              
198             @a=Net::CIDR::range2cidr("192.168.1.0-192.168.2.255");
199              
200             The result is a two element array: ("192.168.1.0/24","192.168.2.0/24");
201              
202             @a=Net::CIDR::range2cidr(
203             "d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff");
204              
205             The result is an one element array: ("d08c:43::/32") that reflects this
206             IPv6 netblock in CIDR notation.
207              
208             range2cidr() does not merge adjacent or overlapping netblocks in
209             @range_list.
210              
211             =head2 @range_list=Net::CIDR::cidr2range(@cidr_list);
212              
213             The cidr2range() functions converts a netblock list in CIDR notation
214             to a list of "start-finish" IP address ranges:
215              
216             @a=Net::CIDR::cidr2range("10.0.0.0/14", "192.168.0.0/24");
217              
218             The result is a two-element array:
219             ("10.0.0.0-10.3.255.255", "192.168.0.0-192.168.0.255").
220              
221             @a=Net::CIDR::cidr2range("d08c:43::/32");
222              
223             The result is a one-element array:
224             ("d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff").
225              
226             cidr2range() does not merge adjacent or overlapping netblocks in
227             @cidr_list.
228              
229             =head2 @netblock_list = Net::CIDR::addr2cidr($address);
230              
231             The addr2cidr function takes an IP address and returns a list of all
232             the CIDR netblocks it might belong to:
233              
234             @a=Net::CIDR::addr2cidr('192.168.0.31');
235              
236             The result is a thirtythree-element array:
237             ('192.168.0.31/32', '192.168.0.30/31', '192.168.0.28/30', '192.168.0.24/29',
238             [and so on])
239             consisting of all the possible subnets containing this address from
240             0.0.0.0/0 to address/32.
241              
242             Any addresses supplied to addr2cidr after the first will be ignored.
243             It works similarly for IPv6 addresses, returning a list of one hundred
244             and twenty nine elements.
245              
246             =head2 $cidr=Net::CIDR::addrandmask2cidr($address, $netmask);
247              
248             The addrandmask2cidr function takes an IP address and a netmask, and
249             returns the CIDR range whose size fits the netmask and which contains
250             the address. It is an error to supply one parameter in IPv4-ish
251             format and the other in IPv6-ish format, and it is an error to supply
252             a netmask which does not consist solely of 1 bits followed by 0 bits.
253             For example, '255.255.248.192' is an invalid netmask, as is
254             '255.255.255.32' because both contain 0 bits in between 1 bits.
255              
256             Technically speaking both of those *are* valid netmasks, but a) you'd
257             have to be insane to use them, and b) there's no corresponding CIDR
258             range.
259              
260             =cut
261              
262             # CIDR to start-finish
263              
264             sub cidr2range {
265 2     2 1 36 my @cidr=@_;
266              
267 2         2 my @r;
268              
269 2         6 while ($#cidr >= 0)
270             {
271 2         3 my $cidr=shift @cidr;
272              
273 2         3 $cidr =~ s/\s//g;
274              
275 2 50       9 unless ($cidr =~ /(.*)\/(.*)/)
276             {
277 0         0 push @r, $cidr;
278 0         0 next;
279             }
280              
281 2         5 my ($ip, $pfix)=($1, $2);
282              
283 2         3 my $isipv6;
284              
285 2         10 my @ips=_iptoipa($ip);
286              
287 2         3 $isipv6=shift @ips;
288              
289 2 50 33     12 croak "$pfix, as in '$cidr', does not make sense"
      33        
290             unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/;
291              
292 2         5 my @rr=_cidr2iprange($pfix, @ips);
293              
294 2         4 while ($#rr >= 0)
295             {
296 2         3 my $a=shift @rr;
297 2         2 my $b=shift @rr;
298              
299 2         4 $a =~ s/\.$//;
300 2         3 $b =~ s/\.$//;
301              
302 2 100       3 if ($isipv6)
303             {
304 1         3 $a=_ipv4to6($a);
305 1         2 $b=_ipv4to6($b);
306             }
307              
308 2         9 push @r, "$a-$b";
309             }
310             }
311              
312 2         7 return @r;
313             }
314              
315             #
316             # If the input is an IPv6-formatted address, convert it to an IPv4 decimal
317             # format, since the other functions know how to deal with it. The hexadecimal
318             # IPv6 address is represented in dotted-decimal form, like IPv4.
319             #
320              
321             sub _ipv6to4 {
322 19     19   18 my $ipv6=shift;
323              
324 19 100       46 return (undef, $ipv6) unless $ipv6 =~ /:/;
325              
326 8 50       18 croak "Syntax error: $ipv6"
327             unless $ipv6 =~ /^[a-fA-F0-9:\.]+$/;
328              
329 8         11 my $ip4_suffix="";
330              
331 8 50       22 ($ipv6, $ip4_suffix)=($1, $2)
332             if $ipv6 =~ /^(.*:)([0-9]+\.[0-9\.]+)$/;
333              
334 8         34 $ipv6 =~ s/([a-fA-F0-9]+)/_h62d($1)/ge;
  25         30  
335              
336 8         16 my $ipv6_suffix="";
337              
338 8 100       24 if ($ipv6 =~ /(.*)::(.*)/)
339             {
340 7         16 ($ipv6, $ipv6_suffix)=($1, $2);
341 7         9 $ipv6_suffix .= ".$ip4_suffix";
342             }
343             else
344             {
345 1         3 $ipv6 .= ".$ip4_suffix";
346             }
347              
348 8         54 my @p=grep (/./, split (/[^0-9]+/, $ipv6));
349              
350 8         37 my @s=grep (/./, split (/[^0-9]+/, $ipv6_suffix));
351              
352 8         51 push @p, 0 while $#p + $#s < 14;
353              
354 8         36 my $n=join(".", @p, @s);
355              
356             # return (undef, $1)
357             # if $n =~ /^0\.0\.0\.0\.0\.0\.0\.0\.0\.0\.255\.255\.(.*)$/;
358              
359 8         26 return (1, $n);
360             }
361              
362             # Let's go the other way around
363              
364             sub _ipv4to6 {
365 776     776   4126 my @octets=split(/[^0-9]+/, shift);
366              
367 776 50       1117 croak "Internal error in _ipv4to6"
368             unless $#octets == 15;
369              
370 776         2214 my @dummy=@octets;
371              
372 776 50       1430 return ("::ffff:" . join(".", $octets[12], $octets[13], $octets[14], $octets[15]))
373             if join(".", splice(@dummy, 0, 12)) eq "0.0.0.0.0.0.0.0.0.0.255.255";
374              
375 776         1112 my @words;
376              
377             my $i;
378              
379 776         1015 for ($i=0; $i < 8; $i++)
380             {
381 6208         12334 $words[$i]=sprintf("%x", $octets[$i*2] * 256 + $octets[$i*2+1]);
382             }
383              
384 776         765 my $ind= -1;
385 776         636 my $indlen= -1;
386              
387 776         884 for ($i=0; $i < 8; $i++)
388             {
389 2530 100       3204 next unless $words[$i] eq "0";
390              
391 848         691 my $j;
392              
393 848         916 for ($j=$i; $j < 8; $j++)
394             {
395 4615 100       6273 last if $words[$j] ne "0";
396             }
397              
398 848 50       953 if ($j - $i > $indlen)
399             {
400 848         685 $indlen= $j-$i;
401 848         701 $ind=$i;
402 848         997 $i=$j-1;
403             }
404             }
405              
406 776 100       893 return "::" if $indlen == 8;
407              
408 755 100       848 return join(":", @words) if $ind < 0;
409              
410 738         859 my @s=splice (@words, $ind+$indlen);
411              
412 738         2944 return join(":", splice (@words, 0, $ind)) . "::"
413             . join(":", @s);
414             }
415              
416             # An IP address to an octet list.
417              
418             # Returns a list. First element, flag: true if it was an IPv6 flag. Remaining
419             # values are octets.
420              
421             sub _iptoipa {
422 19     19   23 my $iparg=shift;
423              
424 19         20 my $isipv6;
425             my $ip;
426              
427 19         24 ($isipv6, $ip)=_ipv6to4($iparg);
428              
429 19         91 my @ips= split (/\.+/, $ip);
430              
431             grep {
432 19 50 33     27 croak "$_, in $iparg, is not a byte" unless $_ >= 0 && $_ <= 255 && $_ =~ /^[0-9]+$/;
  172   33     660  
433             } @ips;
434              
435 19         57 return ($isipv6, @ips);
436             }
437              
438             sub _h62d {
439 25     25   27 my $h=shift;
440              
441 25         33 $h=hex("0x$h");
442              
443 25         73 return ( int($h / 256) . "." . ($h % 256));
444             }
445              
446             sub _cidr2iprange {
447 9     9   23 my @ips=@_;
448 9         9 my $pfix=shift @ips;
449              
450 9 100       15 if ($pfix == 0)
451             {
452 1         2 grep { $_=0 } @ips;
  2         3  
453              
454 1         1 my @ips2=@ips;
455              
456 1         2 grep { $_=255 } @ips2;
  2         2  
457              
458 1         4 return ( join(".", @ips), join(".", @ips2));
459             }
460              
461 8 100       10 if ($pfix >= 8)
462             {
463 7         7 my $octet=shift @ips;
464              
465 7         24 @ips=_cidr2iprange($pfix - 8, @ips);
466              
467 7         8 grep { $_="$octet.$_"; } @ips;
  14         23  
468 7         12 return @ips;
469             }
470              
471 1         2 my $octet=shift @ips;
472              
473 1         2 grep { $_=0 } @ips;
  10         10  
474              
475 1         1 my @ips2=@ips;
476              
477 1         3 grep { $_=255 } @ips2;
  10         14  
478              
479 1         5 my @r= _cidr2range8(($octet, $pfix));
480              
481 1         5 $r[0] = join (".", ($r[0], @ips));
482 1         10 $r[1] = join (".", ($r[1], @ips2));
483              
484 1         3 return @r;
485             }
486              
487             #
488             # ADDRESS to list of CIDR netblocks
489             #
490              
491             sub addr2cidr {
492 14     14 1 92 my @ips=_iptoipa(shift);
493              
494 14         16 my $isipv6=shift @ips;
495              
496 14         12 my $nbits;
497              
498 14 100       20 if ($isipv6)
499             {
500 6 50       10 croak "An IPv6 address is 16 bytes long" unless $#ips == 15;
501 6         6 $nbits=128;
502             }
503             else
504             {
505 8 50       10 croak "An IPv4 address is 4 bytes long" unless $#ips == 3;
506 8         9 $nbits=32;
507             }
508              
509 14         11 my @blocks;
510              
511 14         45 foreach my $bits (reverse 0..$nbits)
512             {
513 1038         1781 my @ipcpy=@ips;
514              
515 1038         888 my $n=$bits;
516              
517 1038         1137 while ($n < $nbits)
518             {
519 7168         7176 @ipcpy[$n / 8] &= (0xFF00 >> ($n % 8));
520              
521 7168         5705 $n += 8;
522              
523 7168         7511 $n &= 0xF8;
524             }
525              
526 1038         2017 my $s=join(".", @ipcpy);
527              
528 1038 100       1715 push @blocks, ($isipv6 ? _ipv4to6($s):$s) . "/$bits";
529             }
530 14         140 return @blocks;
531             }
532              
533             # Address and netmask to CIDR
534              
535             sub addrandmask2cidr {
536 0     0 1 0 my $address = shift;
537 0         0 my($a_isIPv6) = _ipv6to4($address);
538 0         0 my($n_isIPv6, $netmask) = _ipv6to4(shift);
539 0 0 0     0 die("Both address and netmask must be the same type")
      0        
540             if( defined($a_isIPv6) && defined($n_isIPv6) && $a_isIPv6 != $n_isIPv6);
541 0         0 my $bitsInNetmask = 0;
542 0         0 my $previousNMoctet = 255;
543 0         0 foreach my $octet (split/\./, $netmask) {
544 0 0 0     0 die("Invalid netmask") if($previousNMoctet != 255 && $octet != 0);
545 0         0 $previousNMoctet = $octet;
546 0 0       0 $bitsInNetmask +=
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
547             ($octet == 255) ? 8 :
548             ($octet == 254) ? 7 :
549             ($octet == 252) ? 6 :
550             ($octet == 248) ? 5 :
551             ($octet == 240) ? 4 :
552             ($octet == 224) ? 3 :
553             ($octet == 192) ? 2 :
554             ($octet == 128) ? 1 :
555             ($octet == 0) ? 0 :
556             die("Invalid netmask");
557             }
558 0         0 return (grep { /\/$bitsInNetmask$/ } addr2cidr($address))[0];
  0         0  
559             }
560              
561             #
562             # START-FINISH to CIDR list
563             #
564              
565             sub range2cidr {
566 0     0 1 0 my @r=@_;
567              
568 0         0 my $i;
569              
570             my @c;
571              
572 0         0 for ($i=0; $i <= $#r; $i++)
573             {
574 0         0 $r[$i] =~ s/\s//g;
575              
576 0 0       0 if ($r[$i] =~ /\//)
577             {
578 0         0 push @c, $r[$i];
579 0         0 next;
580             }
581              
582 0 0       0 $r[$i]="$r[$i]-$r[$i]" unless $r[$i] =~ /(.*)-(.*)/;
583              
584 0         0 $r[$i] =~ /(.*)-(.*)/;
585              
586 0         0 my ($a,$b)=($1,$2);
587              
588 0         0 my $isipv6_1;
589             my $isipv6_2;
590              
591 0         0 ($isipv6_1, $a)=_ipv6to4($a);
592 0         0 ($isipv6_2, $b)=_ipv6to4($b);
593              
594 0 0 0     0 if ($isipv6_1 || $isipv6_2)
595             {
596 0 0 0     0 croak "Invalid netblock range: $r[$i]"
597             unless $isipv6_1 && $isipv6_2;
598             }
599              
600 0         0 my @a=split(/\.+/, $a);
601 0         0 my @b=split(/\.+/, $b);
602              
603 0 0       0 croak unless $#a == $#b;
604              
605 0         0 my @cc=_range2cidr(\@a, \@b);
606              
607 0         0 while ($#cc >= 0)
608             {
609 0         0 $a=shift @cc;
610 0         0 $b=shift @cc;
611              
612 0 0       0 $a=_ipv4to6($a) if $isipv6_1;
613              
614 0         0 push @c, "$a/$b";
615             }
616             }
617 0 0 0     0 return @c unless(1==@r && 1==@c && !wantarray());
      0        
618 0         0 return $c[0];
619             }
620              
621             sub _range2cidr {
622 0     0   0 my $a=shift;
623 0         0 my $b=shift;
624              
625 0         0 my @a=@$a;
626 0         0 my @b=@$b;
627              
628 0         0 $a=shift @a;
629 0         0 $b=shift @b;
630              
631 0 0       0 return _range2cidr8($a, $b) if $#a < 0; # Least significant octet pair.
632              
633 0 0 0     0 croak "Bad starting address\n" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      0        
634 0 0 0     0 croak "Bad ending address\n" unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a;
      0        
      0        
635              
636 0         0 my @c;
637              
638 0 0       0 if ($a == $b) # Same start/end octet
639             {
640 0         0 my @cc= _range2cidr(\@a, \@b);
641              
642 0         0 while ($#cc >= 0)
643             {
644 0         0 my $c=shift @cc;
645              
646 0         0 push @c, "$a.$c";
647              
648 0         0 $c=shift @cc;
649 0         0 push @c, $c+8;
650             }
651 0         0 return @c;
652             }
653              
654 0         0 my $start0=1;
655 0         0 my $end255=1;
656              
657 0 0       0 grep { $start0=0 unless $_ == 0; } @a;
  0         0  
658 0 0       0 grep { $end255=0 unless $_ == 255; } @b;
  0         0  
659              
660 0 0       0 if ( ! $start0 )
661             {
662 0         0 my @bcopy=@b;
663              
664 0         0 grep { $_=255 } @bcopy;
  0         0  
665              
666 0         0 my @cc= _range2cidr(\@a, \@bcopy);
667              
668 0         0 while ($#cc >= 0)
669             {
670 0         0 my $c=shift @cc;
671              
672 0         0 push @c, "$a.$c";
673              
674 0         0 $c=shift @cc;
675 0         0 push @c, $c + 8;
676             }
677              
678 0         0 ++$a;
679             }
680              
681 0 0       0 if ( ! $end255 )
682             {
683 0         0 my @acopy=@a;
684              
685 0         0 grep { $_=0 } @acopy;
  0         0  
686              
687 0         0 my @cc= _range2cidr(\@acopy, \@b);
688              
689 0         0 while ($#cc >= 0)
690             {
691 0         0 my $c=shift @cc;
692              
693 0         0 push @c, "$b.$c";
694              
695 0         0 $c=shift @cc;
696 0         0 push @c, $c + 8;
697             }
698              
699 0         0 --$b;
700             }
701              
702 0 0       0 if ($a <= $b)
703             {
704 0         0 grep { $_=0 } @a;
  0         0  
705              
706 0         0 my $pfix=join(".", @a);
707              
708 0         0 my @cc= _range2cidr8($a, $b);
709              
710 0         0 while ($#cc >= 0)
711             {
712 0         0 my $c=shift @cc;
713              
714 0         0 push @c, "$c.$pfix";
715              
716 0         0 $c=shift @cc;
717 0         0 push @c, $c;
718             }
719             }
720 0         0 return @c;
721             }
722              
723             sub _range2cidr8 {
724              
725 0     0   0 my @c;
726              
727 0         0 my @r=@_;
728              
729 0         0 while ($#r >= 0)
730             {
731 0         0 my $a=shift @r;
732 0         0 my $b=shift @r;
733              
734 0 0 0     0 croak "Bad starting address\n" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      0        
735 0 0 0     0 croak "Bad ending address\n" unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a;
      0        
      0        
736              
737 0         0 ++$b;
738              
739 0         0 while ($a < $b)
740             {
741 0         0 my $i=0;
742 0         0 my $n=1;
743              
744 0         0 while ( ($n & $a) == 0)
745             {
746 0         0 ++$i;
747 0         0 $n <<= 1;
748 0 0       0 last if $i >= 8;
749             }
750              
751 0   0     0 while ($i && $n + $a > $b)
752             {
753 0         0 --$i;
754 0         0 $n >>= 1;
755             }
756              
757 0         0 push @c, $a;
758 0         0 push @c, 8-$i;
759              
760 0         0 $a += $n;
761             }
762             }
763              
764 0         0 return @c;
765             }
766              
767             sub _cidr2range8 {
768              
769 3     3   5 my @c=@_;
770              
771 3         3 my @r;
772              
773 3         7 while ($#c >= 0)
774             {
775 3         10 my $a=shift @c;
776 3         4 my $b=shift @c;
777              
778 3 50 33     17 croak "Bad starting address" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      33        
779 3 50 33     17 croak "Bad ending address" unless $b >= 0 && $b <= 8 && $b =~ /^[0-9]+$/;
      33        
780              
781 3         5 my $n= 1 << (8-$b);
782              
783 3         14 $a &= ($n-1) ^ 255;
784              
785 3         6 push @r, $a;
786 3         6 push @r, $a + ($n-1);
787             }
788 3         7 return @r;
789             }
790              
791             sub _ipcmp {
792 0     0   0 my $aa=shift;
793 0         0 my $bb=shift;
794              
795 0         0 my $isipv6_1;
796             my $isipv6_2;
797              
798 0         0 ($isipv6_1, $aa)=_ipv6to4($aa);
799 0         0 ($isipv6_2, $bb)=_ipv6to4($bb);
800              
801 0 0 0     0 if ($isipv6_1 || $isipv6_2)
802             {
803 0 0 0     0 croak "Invalid netblock: $aa-$bb"
804             unless $isipv6_1 && $isipv6_2;
805             }
806              
807 0         0 my @a=split (/\./, $aa);
808 0         0 my @b=split (/\./, $bb);
809              
810 0 0       0 croak "Different number of octets in IP addresses" unless $#a == $#b;
811              
812 0   0     0 while ($#a >= 0 && $a[0] == $b[0])
813             {
814 0         0 shift @a;
815 0         0 shift @b;
816             }
817              
818 0 0       0 return 0 if $#a < 0;
819              
820 0         0 return $a[0] <=> $b[0];
821             }
822              
823              
824             =pod
825              
826             =head2 @octet_list=Net::CIDR::cidr2octets(@cidr_list);
827              
828             cidr2octets() takes @cidr_list and returns a list of leading octets
829             representing those netblocks. Example:
830              
831             @octet_list=Net::CIDR::cidr2octets("10.0.0.0/14", "192.168.0.0/24");
832              
833             The result is the following five-element array:
834             ("10.0", "10.1", "10.2", "10.3", "192.168.0").
835              
836             For IPv6 addresses, the hexadecimal words in the resulting list are
837             zero-padded:
838              
839             @octet_list=Net::CIDR::cidr2octets("::dead:beef:0:0/110");
840              
841             The result is a four-element array:
842             ("0000:0000:0000:0000:dead:beef:0000",
843             "0000:0000:0000:0000:dead:beef:0001",
844             "0000:0000:0000:0000:dead:beef:0002",
845             "0000:0000:0000:0000:dead:beef:0003").
846             Prefixes of IPv6 CIDR blocks should be even multiples of 16 bits, otherwise
847             they can potentially expand out to a 32,768-element array, each!
848              
849             =cut
850              
851             sub cidr2octets {
852 2     2 1 47 my @cidr=@_;
853              
854 2         3 my @r;
855              
856 2         6 while ($#cidr >= 0)
857             {
858 3         4 my $cidr=shift @cidr;
859              
860 3         5 $cidr =~ s/\s//g;
861              
862 3 50       12 croak "CIDR doesn't look like a CIDR\n" unless ($cidr =~ /(.*)\/(.*)/);
863              
864 3         9 my ($ip, $pfix)=($1, $2);
865              
866 3         3 my $isipv6;
867              
868 3         7 my @ips=_iptoipa($ip);
869              
870 3         5 $isipv6=shift @ips;
871              
872 3 50 33     20 croak "$pfix, as in '$cidr', does not make sense"
      33        
873             unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/;
874              
875 3         4 my $i;
876              
877 3         7 for ($i=0; $i <= $#ips; $i++)
878             {
879 20 100       29 last if $pfix - $i * 8 < 8;
880             }
881              
882 3         6 my @msb=splice @ips, 0, $i;
883              
884 3         4 my $bitsleft= $pfix - $i * 8;
885              
886 3 100 66     10 if ($#ips < 0 || $bitsleft == 0)
887             {
888 1 50 33     4 if ($pfix == 0 && $bitsleft == 0)
    50          
889             {
890 0         0 foreach (0..255)
891             {
892 0         0 my @n=($_);
893              
894 0 0       0 if ($isipv6)
895             {
896 0         0 _push_ipv6_octets(\@r, \@n);
897             }
898             else
899             {
900 0         0 push @r, $n[0];
901             }
902             }
903             }
904             elsif ($isipv6)
905             {
906 0         0 _push_ipv6_octets(\@r, \@msb);
907             }
908             else
909             {
910 1         3 push @r, join(".", @msb);
911             }
912 1         3 next;
913             }
914              
915 2         6 my @rr=_cidr2range8(($ips[0], $bitsleft));
916              
917 2         4 while ($#rr >= 0)
918             {
919 2         3 my $a=shift @rr;
920 2         2 my $b=shift @rr;
921              
922             grep {
923 2 100       3 if ($isipv6)
  8         10  
924             {
925 4         4 push @msb, $_;
926 4         8 _push_ipv6_octets(\@r, \@msb);
927 4         8 pop @msb;
928             }
929             else
930             {
931 4         11 push @r, join(".", (@msb, $_));
932             }
933             } ($a .. $b);
934             }
935             }
936              
937 2         7 return @r;
938             }
939              
940             sub _push_ipv6_octets {
941 4     4   5 my $ary_ref=shift;
942 4         8 my $octets=shift;
943              
944 4 50       5 if ( ($#{$octets} % 2) == 0) # Odd number of octets
  4         9  
945             {
946 0         0 foreach (0 .. 255)
947             {
948 0         0 push @$octets, $_;
949 0         0 _push_ipv6_octets($ary_ref, $octets);
950 0         0 pop @$octets;
951             }
952 0         0 return;
953             }
954              
955 4         4 my $i;
956 4         4 my $s="";
957              
958 4         13 for ($i=0; $i <= $#{$octets}; $i += 2)
  32         41  
959             {
960 28 100       36 $s .= ":" if $s ne "";
961 28         40 $s .= sprintf("%02x%02x", $$octets[$i], $$octets[$i+1]);
962             }
963 4         5 push @$ary_ref, $s;
964             }
965              
966             =pod
967              
968             =head2 @cidr_list=Net::CIDR::cidradd($block, @cidr_list);
969              
970             The cidradd() functions allows a CIDR list to be built one CIDR netblock
971             at a time, merging adjacent and overlapping ranges.
972             $block is a single netblock, expressed as either "start-finish", or
973             "address/prefix".
974             Example:
975              
976             @cidr_list=Net::CIDR::range2cidr("192.168.0.0-192.168.0.255");
977             @cidr_list=Net::CIDR::cidradd("10.0.0.0/8", @cidr_list);
978             @cidr_list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @cidr_list);
979              
980             The result is a two-element array: ("10.0.0.0/8", "192.168.0.0/23").
981             IPv6 addresses are handled in an analogous fashion.
982              
983             =cut
984              
985             sub cidradd {
986 0     0 1 0 my @cidr=@_;
987              
988 0         0 my $ip=shift @cidr;
989              
990 0 0       0 $ip="$ip-$ip" unless $ip =~ /[-\/]/;
991              
992 0         0 unshift @cidr, $ip;
993              
994 0         0 @cidr=cidr2range(@cidr);
995              
996 0         0 my @a;
997             my @b;
998              
999             grep {
1000 0 0       0 croak "This doesn't look like start-end\n" unless /(.*)-(.*)/;
  0         0  
1001 0         0 push @a, $1;
1002 0         0 push @b, $2;
1003             } @cidr;
1004              
1005 0         0 my $lo=shift @a;
1006 0         0 my $hi=shift @b;
1007              
1008 0         0 my $i;
1009              
1010 0         0 for ($i=0; $i <= $#a; $i++)
1011             {
1012 0 0       0 last if _ipcmp($lo, $hi) > 0;
1013              
1014 0 0       0 next if _ipcmp($b[$i], $lo) < 0;
1015 0 0       0 next if _ipcmp($hi, $a[$i]) < 0;
1016              
1017 0 0 0     0 if (_ipcmp($a[$i],$lo) <= 0 && _ipcmp($hi, $b[$i]) <= 0)
1018             {
1019 0         0 $lo=_add1($hi);
1020 0         0 last;
1021             }
1022              
1023 0 0       0 if (_ipcmp($a[$i],$lo) <= 0)
1024             {
1025 0         0 $lo=_add1($b[$i]);
1026 0         0 next;
1027             }
1028              
1029 0 0       0 if (_ipcmp($hi, $b[$i]) <= 0)
1030             {
1031 0         0 $hi=_sub1($a[$i]);
1032 0         0 next;
1033             }
1034              
1035 0         0 $a[$i]=undef;
1036 0         0 $b[$i]=undef;
1037             }
1038              
1039 0 0 0     0 unless ((! defined $lo) || (! defined $hi) || _ipcmp($lo, $hi) > 0)
      0        
1040             {
1041 0         0 push @a, $lo;
1042 0         0 push @b, $hi;
1043             }
1044              
1045 0         0 @cidr=();
1046              
1047 0         0 @a=grep ( (defined $_), @a);
1048 0         0 @b=grep ( (defined $_), @b);
1049              
1050 0         0 for ($i=0; $i <= $#a; $i++)
1051             {
1052 0         0 push @cidr, "$a[$i]-$b[$i]";
1053             }
1054              
1055             @cidr=sort {
1056 0         0 $a =~ /(.*)-/;
  0         0  
1057              
1058 0         0 my $c=$1;
1059              
1060 0         0 $b =~ /(.*)-/;
1061              
1062 0         0 my $d=$1;
1063              
1064 0         0 my $e=_ipcmp($c, $d);
1065 0         0 return $e;
1066             } @cidr;
1067              
1068 0         0 $i=0;
1069              
1070 0         0 while ($i < $#cidr)
1071             {
1072 0         0 $cidr[$i] =~ /(.*)-(.*)/;
1073              
1074 0         0 my ($k, $l)=($1, $2);
1075              
1076 0         0 $cidr[$i+1] =~ /(.*)-(.*)/;
1077              
1078 0         0 my ($m, $n)=($1, $2);
1079              
1080 0 0       0 if (_ipcmp( _add1($l), $m) == 0)
1081             {
1082 0         0 splice @cidr, $i, 2, "$k-$n";
1083 0         0 next;
1084             }
1085 0         0 ++$i;
1086             }
1087              
1088 0         0 return range2cidr(@cidr);
1089             }
1090              
1091              
1092             sub _add1 {
1093 0     0   0 my $n=shift;
1094              
1095 0         0 my $isipv6;
1096              
1097 0         0 ($isipv6, $n)=_ipv6to4($n);
1098              
1099 0         0 my @ip=split(/\./, $n);
1100              
1101 0         0 my $i=$#ip;
1102              
1103 0         0 while ($i >= 0)
1104             {
1105 0 0       0 last if ++$ip[$i] < 256;
1106 0         0 $ip[$i]=0;
1107 0         0 --$i;
1108             }
1109              
1110 0 0       0 return undef if $i < 0;
1111              
1112 0         0 $i=join(".", @ip);
1113 0 0       0 $i=_ipv4to6($i) if $isipv6;
1114 0         0 return $i;
1115              
1116             }
1117              
1118             sub _sub1 {
1119 0     0   0 my $n=shift;
1120              
1121 0         0 my $isipv6;
1122              
1123 0         0 ($isipv6, $n)=_ipv6to4($n);
1124              
1125 0         0 my @ip=split(/\./, $n);
1126              
1127 0         0 my $i=$#ip;
1128              
1129 0         0 while ($i >= 0)
1130             {
1131 0 0       0 last if --$ip[$i] >= 0;
1132 0         0 $ip[$i]=255;
1133 0         0 --$i;
1134             }
1135              
1136 0 0       0 return undef if $i < 0;
1137              
1138 0         0 $i=join(".", @ip);
1139 0 0       0 $i=_ipv4to6($i) if $isipv6;
1140 0         0 return $i;
1141             }
1142              
1143             =pod
1144              
1145             =head2 $found=Net::CIDR::cidrlookup($ip, @cidr_list);
1146              
1147             Search for $ip in @cidr_list. $ip can be a single IP address, or a
1148             netblock in CIDR or start-finish notation.
1149             lookup() returns 1 if $ip overlaps any netblock in @cidr_list, 0 if not.
1150              
1151             =cut
1152              
1153             sub cidrlookup {
1154 0     0 1 0 my @cidr=@_;
1155              
1156 0         0 my $ip=shift @cidr;
1157              
1158 0 0       0 $ip="$ip-$ip" unless $ip =~ /[-\/]/;
1159              
1160 0         0 unshift @cidr, $ip;
1161              
1162 0         0 @cidr=cidr2range(@cidr);
1163              
1164 0         0 my @a;
1165             my @b;
1166              
1167             grep {
1168 0 0       0 croak "This doesn't look like start-end\n" unless /(.*)-(.*)/;
  0         0  
1169 0         0 push @a, $1;
1170 0         0 push @b, $2;
1171             } @cidr;
1172              
1173 0         0 my $lo=shift @a;
1174 0         0 my $hi=shift @b;
1175              
1176 0         0 my $i;
1177              
1178 0         0 for ($i=0; $i <= $#a; $i++)
1179             {
1180 0 0       0 next if _ipcmp($b[$i], $lo) < 0;
1181 0 0       0 next if _ipcmp($hi, $a[$i]) < 0;
1182 0         0 return 1;
1183             }
1184              
1185 0         0 return 0;
1186             }
1187              
1188             =pod
1189              
1190             =head2 $ip=Net::CIDR::cidrvalidate($ip);
1191              
1192             Validate whether $ip is a valid IPv4 or IPv6 address, or a CIDR.
1193             Returns its argument or undef.
1194             Spaces are removed, and IPv6 hexadecimal address are converted to lowercase.
1195              
1196             $ip with less than four octets gets filled out with additional octets, and
1197             the modified value gets returned. This turns "192.168/16" into a proper
1198             "192.168.0.0/16".
1199              
1200             If $ip contains a "/", it must be a valid CIDR, otherwise it must be a valid
1201             IPv4 or an IPv6 address.
1202              
1203             A technically invalid CIDR, such as "192.168.0.1/24" fails validation, returning
1204             undef.
1205              
1206             =cut
1207              
1208             sub cidrvalidate {
1209 12     12 1 201 my $v=shift;
1210              
1211 12         23 $v =~ s/\s//g;
1212              
1213 12         22 $v=lc($v);
1214              
1215 12         11 my $suffix;
1216              
1217 12 100       51 ($v, $suffix)=($1, $2) if $v =~ m@(.*)/(.*)@;
1218              
1219 12 100       22 if (defined $suffix)
1220             {
1221 8 50 33     43 return undef unless $suffix =~ /^\d+$/ &&
      33        
1222             ($suffix eq "0" || $suffix =~ /^[123456789]/);
1223             }
1224              
1225 12 100 100     50 if ($v =~ /^([0-9\.]+)$/ || $v =~ /^::ffff:([0-9\.]+)$/ ||
      66        
1226             $v =~ /^:([0-9\.]+)$/)
1227             {
1228 6         11 my $n=$1;
1229              
1230 6 50 33     22 return undef if $n =~ /^\./ || $n =~ /\.$/ || $n =~ /\.\./;
      33        
1231              
1232 6         12 my @o= split(/\./, $n);
1233              
1234 6         12 while ($#o < 3)
1235             {
1236 0         0 push @o, "0";
1237             }
1238              
1239 6         11 $n=join(".", @o);
1240              
1241 6 50       8 return undef if $#o != 3;
1242              
1243 6         10 foreach (@o)
1244             {
1245 24 50       30 return undef if /^0./;
1246 24 50 33     54 return undef if $_ < 0 || $_ > 255;
1247             }
1248              
1249 6 100       10 if ($v =~ /^::ffff/)
1250             {
1251 3 100       6 $suffix=128 unless defined $suffix;
1252              
1253 3 50       5 return undef if $suffix < 128-32;
1254              
1255 3         4 $suffix -= 128-32;
1256             }
1257             else
1258             {
1259 3 100       5 $suffix=32 unless defined $suffix;
1260             }
1261              
1262 6         9 foreach (addr2cidr($n))
1263             {
1264 86 100       113 return $_ if $_ eq "$n/$suffix";
1265             }
1266 2         7 return undef;
1267             }
1268              
1269 6 50       17 return undef unless $v =~ /^[0-9a-f:]+$/;
1270              
1271 6 50 33     31 return undef if $v =~ /:::/ || $v =~ /^:[^:]/ || $v =~ /[^:]:$/
      33        
      33        
1272             || $v =~ /::.*::/;
1273              
1274 6         32 my @o=grep (/./, split(/:/, $v));
1275              
1276 6 50 66     42 return undef if ($#o >= 8 || ($#o<7 && $v !~ /::/));
      33        
1277              
1278 6         10 foreach (@o)
1279             {
1280 19 50       34 return undef if length ($_) > 4;
1281             }
1282              
1283 6 100       9 $suffix=128 unless defined $suffix;
1284              
1285 6         21 $v =~ s/([0-9A-Fa-f]+)/_triml0($1)/ge;
  19         28  
1286              
1287 6         13 foreach (addr2cidr($v))
1288             {
1289 322 100       434 return $_ if $_ eq "$v/$suffix";
1290             }
1291 1         9 return undef;
1292             }
1293              
1294             sub _triml0 {
1295 19     19   24 my ($a) = @_;
1296              
1297 19         24 $a =~ s/^0+//g;
1298 19 100       25 $a = "0" if $a eq '';
1299 19         49 return $a
1300             }
1301              
1302             =pod
1303              
1304             =head1 BUGS
1305              
1306             Garbage in, garbage out.
1307             Always use cidrvalidate() before doing anything with untrusted input.
1308             Otherwise,
1309             "slightly" invalid input will work (extraneous whitespace
1310             is generally OK),
1311             but the functions will croak if you're totally off the wall.
1312              
1313             =head1 AUTHOR
1314              
1315             Sam Varshavchik
1316              
1317             With some contributions from David Cantrell
1318              
1319             =cut
1320              
1321             __END__