File Coverage

blib/lib/Net/CIDR.pm
Criterion Covered Total %
statement 248 457 54.2
branch 90 214 42.0
condition 31 138 22.4
subroutine 15 22 68.1
pod 8 8 100.0
total 392 839 46.7


line stmt bran cond sub pod time code
1             # Net::CIDR
2             #
3             # Copyright 2001-2019 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   588 use Carp;
  1         2  
  1         5635  
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.20";
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 12     12 1 72 my @cidr=@_;
266              
267 12         23 my @r;
268              
269 12         28 while ($#cidr >= 0)
270             {
271 32         43 my $cidr=shift @cidr;
272              
273 32         63 $cidr =~ s/\s//g;
274              
275 32 100       100 unless ($cidr =~ /(.*)\/(.*)/)
276             {
277 10         19 push @r, $cidr;
278 10         23 next;
279             }
280              
281 22         64 my ($ip, $pfix)=($1, $2);
282              
283 22         33 my $isipv6;
284              
285 22         33 my @ips=_iptoipa($ip);
286              
287 22         34 $isipv6=shift @ips;
288              
289 22 50 33     140 croak "$pfix, as in '$cidr', does not make sense"
      33        
290             unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/;
291              
292 22         51 my @rr=_cidr2iprange($pfix, @ips);
293              
294 22         44 while ($#rr >= 0)
295             {
296 22         31 my $a=shift @rr;
297 22         33 my $b=shift @rr;
298              
299 22         37 $a =~ s/\.$//;
300 22         29 $b =~ s/\.$//;
301              
302 22 100       42 if ($isipv6)
303             {
304 11         19 $a=_ipv4to6($a);
305 11         22 $b=_ipv4to6($b);
306             }
307              
308 22         104 push @r, "$a-$b";
309             }
310             }
311              
312 12         36 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 87     87   112 my $ipv6=shift;
323              
324 87 100       228 return (undef, $ipv6) unless $ipv6 =~ /:/;
325              
326 42 50       129 croak "Syntax error: $ipv6"
327             unless $ipv6 =~ /^[a-fA-F0-9:\.]+$/;
328              
329 42         67 my $ip4_suffix="";
330              
331 42 50       132 ($ipv6, $ip4_suffix)=($1, $2)
332             if $ipv6 =~ /^(.*:)([0-9]+\.[0-9\.]+)$/;
333              
334 42         131 $ipv6 =~ s/([a-fA-F0-9]+)/_h62d($1)/ge;
  158         258  
335              
336 42         83 my $ipv6_suffix="";
337              
338 42 100       140 if ($ipv6 =~ /(.*)::(.*)/)
339             {
340 41         108 ($ipv6, $ipv6_suffix)=($1, $2);
341 41         60 $ipv6_suffix .= ".$ip4_suffix";
342             }
343             else
344             {
345 1         4 $ipv6 .= ".$ip4_suffix";
346             }
347              
348 42         282 my @p=grep (/./, split (/[^0-9]+/, $ipv6));
349              
350 42         182 my @s=grep (/./, split (/[^0-9]+/, $ipv6_suffix));
351              
352 42         344 push @p, 0 while $#p + $#s < 14;
353              
354 42         158 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 42         154 return (1, $n);
360             }
361              
362             # Let's go the other way around
363              
364             sub _ipv4to6 {
365 796     796   5470 my @octets=split(/[^0-9]+/, shift);
366              
367 796 50       1517 croak "Internal error in _ipv4to6"
368             unless $#octets == 15;
369              
370 796         2782 my @dummy=@octets;
371              
372 796 50       2146 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 796         1575 my @words;
376              
377             my $i;
378              
379 796         1532 for ($i=0; $i < 8; $i++)
380             {
381 6368         18036 $words[$i]=sprintf("%x", $octets[$i*2] * 256 + $octets[$i*2+1]);
382             }
383              
384 796         998 my $ind= -1;
385 796         910 my $indlen= -1;
386              
387 796         1250 for ($i=0; $i < 8; $i++)
388             {
389 2644 100       4846 next unless $words[$i] eq "0";
390              
391 868         984 my $j;
392              
393 868         1398 for ($j=$i; $j < 8; $j++)
394             {
395 4691 100       8696 last if $words[$j] ne "0";
396             }
397              
398 868 50       1413 if ($j - $i > $indlen)
399             {
400 868         1023 $indlen= $j-$i;
401 868         1308 $ind=$i;
402 868         1501 $i=$j-1;
403             }
404             }
405              
406 796 100       1223 return "::" if $indlen == 8;
407              
408 775 100       1334 return join(":", @words) if $ind < 0;
409              
410 758         1101 my @s=splice (@words, $ind+$indlen);
411              
412 758         4325 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 39     39   57 my $iparg=shift;
423              
424 39         56 my $isipv6;
425             my $ip;
426              
427 39         65 ($isipv6, $ip)=_ipv6to4($iparg);
428              
429 39         254 my @ips= split (/\.+/, $ip);
430              
431             grep {
432 39 50 33     70 croak "$_, in $iparg, is not a byte" unless $_ >= 0 && $_ <= 255 && $_ =~ /^[0-9]+$/;
  372   33     1848  
433             } @ips;
434              
435 39         138 return ($isipv6, @ips);
436             }
437              
438             sub _h62d {
439 158     158   244 my $h=shift;
440              
441 158         257 $h=hex("0x$h");
442              
443 158         560 return ( int($h / 256) . "." . ($h % 256));
444             }
445              
446             sub _cidr2iprange {
447 139     139   334 my @ips=@_;
448 139         174 my $pfix=shift @ips;
449              
450 139 100       217 if ($pfix == 0)
451             {
452 21         33 grep { $_=0 } @ips;
  92         130  
453              
454 21         39 my @ips2=@ips;
455              
456 21         23 grep { $_=255 } @ips2;
  92         118  
457              
458 21         104 return ( join(".", @ips), join(".", @ips2));
459             }
460              
461 118 100       226 if ($pfix >= 8)
462             {
463 117         138 my $octet=shift @ips;
464              
465 117         240 @ips=_cidr2iprange($pfix - 8, @ips);
466              
467 117         190 grep { $_="$octet.$_"; } @ips;
  234         420  
468 117         248 return @ips;
469             }
470              
471 1         4 my $octet=shift @ips;
472              
473 1         2 grep { $_=0 } @ips;
  10         14  
474              
475 1         3 my @ips2=@ips;
476              
477 1         2 grep { $_=255 } @ips2;
  10         14  
478              
479 1         5 my @r= _cidr2range8(($octet, $pfix));
480              
481 1         16 $r[0] = join (".", ($r[0], @ips));
482 1         6 $r[1] = join (".", ($r[1], @ips2));
483              
484 1         4 return @r;
485             }
486              
487             #
488             # ADDRESS to list of CIDR netblocks
489             #
490              
491             sub addr2cidr {
492 14     14 1 158 my @ips=_iptoipa(shift);
493              
494 14         22 my $isipv6=shift @ips;
495              
496 14         20 my $nbits;
497              
498 14 100       25 if ($isipv6)
499             {
500 6 50       12 croak "An IPv6 address is 16 bytes long" unless $#ips == 15;
501 6         10 $nbits=128;
502             }
503             else
504             {
505 8 50       26 croak "An IPv4 address is 4 bytes long" unless $#ips == 3;
506 8         9 $nbits=32;
507             }
508              
509 14         18 my @blocks;
510              
511 14         49 foreach my $bits (reverse 0..$nbits)
512             {
513 1038         2497 my @ipcpy=@ips;
514              
515 1038         1233 my $n=$bits;
516              
517 1038         1704 while ($n < $nbits)
518             {
519 7168         10573 @ipcpy[$n / 8] &= (0xFF00 >> ($n % 8));
520              
521 7168         8095 $n += 8;
522              
523 7168         10565 $n &= 0xF8;
524             }
525              
526 1038         2715 my $s=join(".", @ipcpy);
527              
528 1038 100       2183 push @blocks, ($isipv6 ? _ipv4to6($s):$s) . "/$bits";
529             }
530 14         203 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   7 my @c=@_;
770              
771 3         4 my @r;
772              
773 3         7 while ($#c >= 0)
774             {
775 3         4 my $a=shift @c;
776 3         5 my $b=shift @c;
777              
778 3 50 33     20 croak "Bad starting address" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      33        
779 3 50 33     31 croak "Bad ending address" unless $b >= 0 && $b <= 8 && $b =~ /^[0-9]+$/;
      33        
780              
781 3         7 my $n= 1 << (8-$b);
782              
783 3         24 $a &= ($n-1) ^ 255;
784              
785 3         8 push @r, $a;
786 3         8 push @r, $a + ($n-1);
787             }
788 3         9 return @r;
789             }
790              
791             sub _ipcmp {
792 24     24   32 my $aa=shift;
793 24         31 my $bb=shift;
794              
795 24         31 my $isipv6_1;
796             my $isipv6_2;
797              
798 24         40 ($isipv6_1, $aa)=_ipv6to4($aa);
799 24         42 ($isipv6_2, $bb)=_ipv6to4($bb);
800              
801 24         70 my @a=split (/\./, $aa);
802 24         62 my @b=split (/\./, $bb);
803              
804 24 100       58 unshift @a, (0,0,0,0,0,0,0,0,0,0,255,255)
805             unless $isipv6_1;
806              
807 24 100       44 unshift @b, (0,0,0,0,0,0,0,0,0,0,255,255)
808             unless $isipv6_2;
809              
810 24 50       46 croak "Different number of octets in IP addresses" unless $#a == $#b;
811              
812 24   66     95 while ($#a >= 0 && $a[0] == $b[0])
813             {
814 163         183 shift @a;
815 163         413 shift @b;
816             }
817              
818 24 50       45 return 0 if $#a < 0;
819              
820 24         100 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 61 my @cidr=@_;
853              
854 2         4 my @r;
855              
856 2         7 while ($#cidr >= 0)
857             {
858 3         6 my $cidr=shift @cidr;
859              
860 3         9 $cidr =~ s/\s//g;
861              
862 3 50       14 croak "CIDR doesn't look like a CIDR\n" unless ($cidr =~ /(.*)\/(.*)/);
863              
864 3         12 my ($ip, $pfix)=($1, $2);
865              
866 3         5 my $isipv6;
867              
868 3         6 my @ips=_iptoipa($ip);
869              
870 3         6 $isipv6=shift @ips;
871              
872 3 50 33     38 croak "$pfix, as in '$cidr', does not make sense"
      33        
873             unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/;
874              
875 3         6 my $i;
876              
877 3         7 for ($i=0; $i <= $#ips; $i++)
878             {
879 20 100       44 last if $pfix - $i * 8 < 8;
880             }
881              
882 3         8 my @msb=splice @ips, 0, $i;
883              
884 3         5 my $bitsleft= $pfix - $i * 8;
885              
886 3 100 66     12 if ($#ips < 0 || $bitsleft == 0)
887             {
888 1 50 33     5 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         4 push @r, join(".", @msb);
911             }
912 1         3 next;
913             }
914              
915 2         6 my @rr=_cidr2range8(($ips[0], $bitsleft));
916              
917 2         5 while ($#rr >= 0)
918             {
919 2         4 my $a=shift @rr;
920 2         2 my $b=shift @rr;
921              
922             grep {
923 2 100       5 if ($isipv6)
  8         13  
924             {
925 4         6 push @msb, $_;
926 4         10 _push_ipv6_octets(\@r, \@msb);
927 4         10 pop @msb;
928             }
929             else
930             {
931 4         14 push @r, join(".", (@msb, $_));
932             }
933             } ($a .. $b);
934             }
935             }
936              
937 2         10 return @r;
938             }
939              
940             sub _push_ipv6_octets {
941 4     4   5 my $ary_ref=shift;
942 4         5 my $octets=shift;
943              
944 4 50       5 if ( ($#{$octets} % 2) == 0) # Odd number of octets
  4         10  
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         6 my $s="";
957              
958 4         8 for ($i=0; $i <= $#{$octets}; $i += 2)
  32         56  
959             {
960 28 100       43 $s .= ":" if $s ne "";
961 28         62 $s .= sprintf("%02x%02x", $$octets[$i], $$octets[$i+1]);
962             }
963 4         8 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 10     10 1 204 my @cidr=@_;
1155              
1156 10         22 my $ip=shift @cidr;
1157              
1158 10 50       38 $ip="$ip-$ip" unless $ip =~ /[-\/]/;
1159              
1160 10         20 unshift @cidr, $ip;
1161              
1162 10         21 @cidr=cidr2range(@cidr);
1163              
1164 10         13 my @a;
1165             my @b;
1166              
1167             grep {
1168 10 50       19 croak "This doesn't look like start-end\n" unless /(.*)-(.*)/;
  30         94  
1169 30         60 push @a, $1;
1170 30         56 push @b, $2;
1171             } @cidr;
1172              
1173 10         20 my $lo=shift @a;
1174 10         16 my $hi=shift @b;
1175              
1176 10         18 my $i;
1177              
1178 10         23 for ($i=0; $i <= $#a; $i++)
1179             {
1180 17 100       44 next if _ipcmp($b[$i], $lo) < 0;
1181 7 100       19 next if _ipcmp($hi, $a[$i]) < 0;
1182 4         23 return 1;
1183             }
1184              
1185 6         29 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 315 my $v=shift;
1210              
1211 12         30 $v =~ s/\s//g;
1212              
1213 12         31 $v=lc($v);
1214              
1215 12         15 my $suffix;
1216              
1217 12 100       72 ($v, $suffix)=($1, $2) if $v =~ m@(.*)/(.*)@;
1218              
1219 12 100       24 if (defined $suffix)
1220             {
1221 8 50 33     61 return undef unless $suffix =~ /^\d+$/ &&
      33        
1222             ($suffix eq "0" || $suffix =~ /^[123456789]/);
1223             }
1224              
1225 12 100 100     72 if ($v =~ /^([0-9\.]+)$/ || $v =~ /^::ffff:([0-9\.]+)$/ ||
      66        
1226             $v =~ /^:([0-9\.]+)$/)
1227             {
1228 6         13 my $n=$1;
1229              
1230 6 50 33     30 return undef if $n =~ /^\./ || $n =~ /\.$/ || $n =~ /\.\./;
      33        
1231              
1232 6         17 my @o= split(/\./, $n);
1233              
1234 6         18 while ($#o < 3)
1235             {
1236 0         0 push @o, "0";
1237             }
1238              
1239 6         15 $n=join(".", @o);
1240              
1241 6 50       13 return undef if $#o != 3;
1242              
1243 6         9 foreach (@o)
1244             {
1245 24 50       41 return undef if /^0./;
1246 24 50 33     82 return undef if $_ < 0 || $_ > 255;
1247             }
1248              
1249 6 100       15 if ($v =~ /^::ffff/)
1250             {
1251 3 100       6 $suffix=128 unless defined $suffix;
1252              
1253 3 50       10 return undef if $suffix < 128-32;
1254              
1255 3         11 $suffix -= 128-32;
1256             }
1257             else
1258             {
1259 3 100       6 $suffix=32 unless defined $suffix;
1260             }
1261              
1262 6         15 foreach (addr2cidr($n))
1263             {
1264 86 100       159 return $_ if $_ eq "$n/$suffix";
1265             }
1266 2         9 return undef;
1267             }
1268              
1269 6 50       20 return undef unless $v =~ /^[0-9a-f:]+$/;
1270              
1271 6 50 33     46 return undef if $v =~ /:::/ || $v =~ /^:[^:]/ || $v =~ /[^:]:$/
      33        
      33        
1272             || $v =~ /::.*::/;
1273              
1274 6         42 my @o=grep (/./, split(/:/, $v));
1275              
1276 6 50 66     35 return undef if ($#o >= 8 || ($#o<7 && $v !~ /::/));
      33        
1277              
1278 6         18 foreach (@o)
1279             {
1280 19 50       50 return undef if length ($_) > 4;
1281             }
1282              
1283 6 100       14 $suffix=128 unless defined $suffix;
1284              
1285 6         26 $v =~ s/([0-9A-Fa-f]+)/_triml0($1)/ge;
  19         34  
1286              
1287 6         16 foreach (addr2cidr($v))
1288             {
1289 322 100       565 return $_ if $_ eq "$v/$suffix";
1290             }
1291 1         16 return undef;
1292             }
1293              
1294             sub _triml0 {
1295 19     19   35 my ($a) = @_;
1296              
1297 19         35 $a =~ s/^0+//g;
1298 19 100       35 $a = "0" if $a eq '';
1299 19         57 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__