File Coverage

blib/lib/Net/IP/Util.pm
Criterion Covered Total %
statement 12 152 7.8
branch 0 98 0.0
condition 0 52 0.0
subroutine 4 23 17.3
pod 15 16 93.7
total 31 341 9.0


line stmt bran cond sub pod time code
1             package Net::IP::Util;
2              
3 1     1   27427 use strict;
  1         2  
  1         47  
4 1     1   5 use warnings;
  1         3  
  1         37  
5 1     1   6 use Carp qw/croak/;
  1         6  
  1         586  
6              
7             require Exporter;
8             our @ISA = qw/Exporter/;
9             our $VERSION = '1.03';
10              
11             our @EXPORT = qw/isClassAddrA
12             isClassAddrB
13             isClassAddrC
14             isClassAddrD
15             isClassAddrE
16             dec2binIpAddr
17             bin2decIpAddr/;
18              
19             our @EXPORT_OK = qw/getAddrMaskDefault
20             getAddrClass
21             isValidMask
22             extendMaskByBits
23             calcSubnet
24             calcSubnetCIDR
25             calcSubnetExt
26             getNetworkAddr/;
27              
28             our %EXPORT_TAGS = ('class' => [qw/isClassAddrA isClassAddrB isClassAddrC
29             isClassAddrD isClassAddrE getAddrClass/],
30             'convert' => [qw/dec2binIpAddr bin2decIpAddr/]);
31              
32 1         3355 use constant { 'A' => qr'^0',
33             'B' => qr'^10',
34             'C' => qr'^110',
35             'D' => qr'^1110',
36             'E' => qr'^11110',
37             'MASKA' => '255.0.0.0',
38             'MASKB' => '255.255.0.0',
39             'MASKC' => '255.255.255.0',
40             'IPREGEXP' => qr'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$',
41 1     1   8 };
  1         2  
42              
43             sub isClassAddrA {
44 0     0 1   my ($addr) = @_;
45              
46 0 0 0       (isBinIpAddr($addr)) && _validateIp(&bin2decIpAddr($addr))
47             || ($addr = &dec2binIpAddr($addr));
48 0 0         return 1 if($addr =~ A);
49             }
50              
51             sub isClassAddrB {
52 0     0 1   my ($addr) = @_;
53              
54 0 0 0       (isBinIpAddr($addr)) && _validateIp(&bin2decIpAddr($addr))
55             || ($addr = &dec2binIpAddr($addr));
56 0 0         return 1 if($addr =~ B);
57             }
58              
59             sub isClassAddrC {
60 0     0 1   my ($addr) = @_;
61              
62 0 0 0       (isBinIpAddr($addr)) && _validateIp(&bin2decIpAddr($addr))
63             || ($addr = &dec2binIpAddr($addr));
64 0 0         return 1 if($addr =~ C);
65             }
66              
67             sub isClassAddrD {
68 0     0 1   my ($addr) = @_;
69              
70 0 0 0       (isBinIpAddr($addr)) && _validateIp(&bin2decIpAddr($addr))
71             || ($addr = &dec2binIpAddr($addr));
72 0 0         return 1 if($addr =~ D);
73             }
74              
75             sub isClassAddrE {
76 0     0 1   my ($addr) = @_;
77              
78 0 0 0       (isBinIpAddr($addr)) && _validateIp(&bin2decIpAddr($addr))
79             || ($addr = &dec2binIpAddr($addr));
80 0 0         return 1 if($addr =~ E);
81             }
82              
83             sub getAddrMaskDefault {
84 0     0 1   my ($addr) = @_;
85              
86 0 0         (! isBinIpAddr($addr)) && ($addr = &dec2binIpAddr($addr));
87              
88 0           my ($class) = getAddrClass($addr);
89              
90 0           my $mask = eval 'MASK'.$class;
91              
92 0           return $mask;
93             }
94              
95             sub getAddrClass {
96 0     0 1   my ($addr) = @_;
97              
98 0 0 0       (isBinIpAddr($addr)) && _validateIp(&bin2decIpAddr($addr))
99             || ($addr = &dec2binIpAddr($addr));
100              
101 0 0         my $class = (($addr =~ A)?'A'
    0          
    0          
    0          
    0          
102             :($addr =~ B)?'B'
103             :($addr =~ C)?'C'
104             :($addr =~ D)?'D'
105             :($addr =~ E)?'E'
106             :undef);
107              
108 0           return $class;
109             }
110              
111             sub dec2binIpAddr {
112 0     0 1   my ($addr) = @_;
113              
114 0           _validateIp($addr);
115              
116 0           my @octets = split /\./,$addr;
117              
118 0           map {$_ = sprintf '%08b', $_} @octets;
  0            
119              
120 0           return join '.',@octets;
121             }
122              
123             sub bin2decIpAddr {
124 0     0 1   my ($addr) = @_;
125 0           my @octets = split /\./, $addr;
126              
127 0           map {$_ = oct "0b$_"} @octets;
  0            
128              
129 0           my $decAddr = join '.',@octets;
130              
131 0           _validateIp($decAddr);
132              
133 0           return $decAddr;
134             }
135              
136             sub isBinIpAddr {
137 0     0 0   my ($addr) = @_;
138              
139 0 0         return 1 if ($addr =~ /^(([0|1]){8}\.){3}([0|1]){8}$/);
140             }
141              
142             sub _validateIp {
143 0     0     my ($addr) = @_;
144              
145 0 0         ($addr =~ /^[01.]*$/) && ($addr = &bin2decIpAddr($addr));
146 0 0   0     my $validate = sub { map { return 0 if ( $_ > 255);}@_; return 1;};
  0            
  0            
  0            
147              
148 0 0 0       if(($addr =~ IPREGEXP) && $validate->($1,$2,$3,$4)) {
149 0           return 1;
150             }
151             else {
152 0           croak "$addr is not a valid IP address";
153             }
154             }
155              
156             sub isValidMask {
157 0     0 1   my ($addr) = @_;
158              
159 0 0         (!isBinIpAddr($addr)) && ($addr = &dec2binIpAddr($addr));
160              
161 0           my ($prefix) = ($addr =~ /(.*1)/);
162              
163 0 0         ($prefix =~ /0/)?return 0:return 1;
164             }
165              
166             sub extendMaskByBits {
167 0     0 1   my ($mask,$noBits) = @_;
168              
169 0 0         (! isBinIpAddr($mask)) && ($mask = &dec2binIpAddr($mask));
170              
171 0 0         return $mask if(!$noBits);
172              
173 0 0 0       croak "Bits $noBits is invalid!!" if ($noBits !~ /^\d+$/ || $noBits > 24);
174 0 0         croak "Mask $mask invalid!!" if (! isValidMask($mask));
175              
176 0           for (1..$noBits) {
177 0           $mask =~ s/0/1/;
178             }
179              
180 0           return $mask;
181             }
182              
183             sub calcSubnet {
184 0     0 1   my ($addr) = @_;
185              
186 0 0 0       (isBinIpAddr($addr)) && _validateIp($addr)
187             || ($addr = &dec2binIpAddr($addr));
188 0           my $mask = getAddrMaskDefault($addr);
189              
190 0 0 0       ($mask && ! isBinIpAddr($mask)) && ($mask = dec2binIpAddr($mask));
191              
192 0   0       my $numZeros = ($mask =~ s/0//g) || 0;
193 0           my $numHosts = 2 ** $numZeros - 2;
194              
195 0           return (0, $numHosts);
196             }
197              
198             sub calcSubnetCIDR {
199 0     0 1   my ($addr, $mask) = @_;
200              
201 0           my ($ip, $maskBits) = split (/\//, $addr);
202              
203 0 0 0       (isBinIpAddr($ip)) && _validateIp(&bin2decIpAddr($ip))
204             || ($ip = &dec2binIpAddr($ip));
205              
206 0 0 0       ($maskBits !~ /^\d+$/ || $maskBits < 0 || $maskBits > 32) && croak "Given mask bits $maskBits is invalid!";
      0        
207              
208 0           my $numOnes;
209 0 0         if(!$mask) {
210 0           $mask = &dec2binIpAddr(getAddrMaskDefault($ip));
211 0           my $temp = $mask;
212 0   0       $numOnes = ($temp =~ s/1//g) ||0;
213             }
214             else {
215 0 0         (! isValidMask($mask)) && croak "Given mask $mask is not valid!!";
216 0 0         (! isBinIpAddr($mask)) && ($mask = &dec2binIpAddr($mask));
217 0           my $temp = $mask;
218 0   0       $numOnes = ($temp =~ s/1//g) ||0;
219             }
220            
221 0           my $diffBits = $maskBits - $numOnes;
222              
223 0 0         ($diffBits < 0) && croak "Given masking bits $maskBits should be >= $numOnes (no. of default mask 1 bits";
224 0           my $extendedMask = extendMaskByBits($mask,$diffBits);
225              
226 0 0         $diffBits = 0 if(!defined $diffBits);
227              
228 0           my $numSubnets = 2 ** $diffBits;
229              
230 0   0       my $numZeros = ($extendedMask =~ s/0//g) || 0;
231 0           my $numHosts = 2 ** $numZeros - 2;
232              
233 0           return ($numSubnets, $numHosts);
234             }
235              
236             sub calcSubnetExt {
237 0     0 1   my ($addr,$borrow) = @_;
238              
239 0 0 0       (isBinIpAddr($addr)) && _validateIp($addr)
240             || ($addr = &dec2binIpAddr($addr));
241              
242 0           my $defaultMask = getAddrMaskDefault($addr);
243              
244 0           my $extendedMask = extendMaskByBits($defaultMask,$borrow);
245              
246 0 0         $borrow = 0 if(!defined $borrow);
247              
248 0           my $numSubnets = 2 ** $borrow;
249              
250 0   0       my $numZeros = ($extendedMask =~ s/0//g) || 0;
251 0           my $numHosts = 2 ** $numZeros - 2;
252              
253 0           return ($numSubnets, $numHosts);
254             }
255              
256             sub getNetworkAddr {
257 0     0 1   my ($addr, $defaultMask, $subnetMask, $bc) = @_;
258              
259 0 0         $defaultMask && isValidMask($defaultMask);
260 0 0         !$defaultMask && ($defaultMask = getAddrMaskDefault($addr));
261              
262 0 0         $subnetMask && isValidMask($subnetMask);
263 0 0         (! isBinIpAddr($addr)) && ($addr = &dec2binIpAddr($addr));
264 0 0         (! isBinIpAddr($defaultMask)) && ($defaultMask = &dec2binIpAddr($defaultMask));
265 0 0         (! isBinIpAddr($subnetMask)) && ($subnetMask = &dec2binIpAddr($subnetMask));
266              
267 0           my $subnetMaskOnBits = ($subnetMask =~ s/1/1/g);
268 0           my $defaultMaskOnBits = ($defaultMask =~ s/1/1/g);
269 0           my $numSubnetBits = ($subnetMaskOnBits - $defaultMaskOnBits);
270              
271 0 0         croak "Default mask : $defaultMask and/or Subnet mask : $subnetMask incorrect!!" if($numSubnetBits !~/^\d+$/);
272              
273 0           my $numHostBits = 32 - $subnetMaskOnBits;
274 0           my $numHostAddrs = 2 ** $numHostBits;
275              
276 0           my $numSubnets = 2 ** $numSubnetBits;
277              
278 0           my @nwAddrs;
279            
280 0 0         if ($bc) {
281 0           for (0..($numSubnets-1)) {
282 0           push @nwAddrs, bin2decIpAddr(_incrIpAddr($addr, $numHostAddrs-1));
283 0           $addr = _incrIpAddr($addr, $numHostAddrs);
284             }
285             }
286             else {
287 0           for (0..($numSubnets-1)) {
288 0           push @nwAddrs, bin2decIpAddr ($addr);
289 0           $addr = _incrIpAddr($addr, $numHostAddrs);
290             }
291             }
292 0           return (@nwAddrs);
293             }
294              
295             sub _incrIpAddr {
296 0     0     my ($addr, $incr) = @_;
297 0           $addr = bin2decIpAddr($addr);
298              
299 0           my ($o4,$o3,$o2,$o1) = split /\./,$addr;
300              
301 0           $o1 += $incr;
302 0 0         if($o1 <= 255) {
303 0           return dec2binIpAddr("$o4.$o3.$o2.$o1");
304             }
305             else {
306 0           $incr = $o1 - 255;
307 0           $o1 = 255;
308              
309 0           $o2 += $incr;
310 0 0         if ($o2 <= 255) {
311 0           return dec2binIpAddr("$o4.$o3.$o2.$o1");
312             }
313             else {
314 0           $incr = $o2 - 255;
315 0           $o2 = 255;
316              
317 0           $o3 += $incr;
318 0 0         if($o3 <= 255) {
319 0           return dec2binIpAddr("$o4.$o3.$o2.$o1");
320             }
321             else {
322 0           $incr = $o3 - 255;
323 0           $o3 = 255;
324              
325 0           $o4 += $incr;
326 0           return dec2binIpAddr("$o4.$o3.$o2.$o1");
327             }
328             }
329             }
330             }
331              
332             1;
333              
334             =head1 NAME
335              
336             Net::IP::Util - Common useful routines like converting decimal address to binary and vice versa, determining address class,
337             determining default mask, subnets and hosts and broadcast addresses for hosts in subnet.
338              
339             =head1 SYNOPSIS
340              
341             use Net::IP::Util; ## subroutines isClassAddrA-E, bin2decIpAddr, dec2binIpAddr
342             use Net::IP::Util qw/:class/; ## subroutines isClassAddrA-E, getAddrClass
343             use Net::IP::Util qw/:convert/; ## subroutines bin2decIpAddr, dec2binIpAddr
344             use Net::IP::Util qw/getAddrMaskDefault
345             getAddrClass
346             isValidMask
347             extendMaskByBits
348             calcSubnet
349             calcSubnetCIDR
350             calcSubnetExt
351             getNetworkAddr ## Explicit inclusions
352              
353             isClassAddrA('127.0.32.45');
354             isClassAddrA('00001111.11110010.00100100.10000001');
355              
356             dec2binIpAddr('128.0.0.56');
357             bin2decIpAddr('10001000.10100001.00010101.00000001');
358              
359             getAddrMaskDefault('124.45.0.0');
360             getAddrMaskDefault('10000000.00000001.01010101.10000001');
361              
362             getAddrClass('124.45.0.0');
363             getAddrClass('00001111.11110010.00100100.10000001');
364              
365             isValidMask('255.255.252.0');
366             isValidMask('11111111.00000000.00000000.00000000');
367              
368             extendMaskByBits('255.255.0.0',2);
369             extendMaskByBits('11111111.00000000.00000000.00000000',2);
370              
371             calcSubnet('128.8.9.0');
372             calcSubnet('10001000.10100001.00010101.00000001');
373              
374             calcSubnetCIDR('128.9.0.218/24');
375             calcSubnetCIDR('128.9.0.218/28', '255.255.255.0');
376              
377             calcSubnetExt('128.0.0.1',4);
378             calcSubnetExt('10001000.10100001.00010101.00000001',4);
379            
380             getNetworkAddr('198.23.16.0','255.255.255.240','255.255.255.252');
381             getNetworkAddr('198.23.16.0','255.255.255.240','255.255.255.252', 1);
382             getNetworkAddr('10000000.00000001.01010101.10000001',
383             '11111111.11111111.11111111.11110000',
384             '11111111.11111111.11111111.11111100',);
385             getNetworkAddr('10000000.00000001.01010101.10000001',
386             '11111111.11111111.11111111.11110000',
387             '11111111.11111111.11111111.11111100', 1);
388              
389             =head1 ABSTRACT
390              
391             This module tries provide the basic functionalities related to IPv4 addresses.
392             Address class, subnet masks, subnet addresses, broadcast addresses can be deduced
393             using the given methods. Ip addresses passed are also validated implicitly.
394              
395             Provision has been given to specify IP addresses in either dotted decimal notation
396             or dotted binary notation, methods have been provided for conversion to-from these
397             to notations which are internally used by other methods too.
398              
399             =head1 METHODS
400              
401             =head2 isClassAddrA,isClassAddrB,isClassAddrC,isClassAddrD,isClassAddrE
402              
403             isClassAddrA() : returns 1 if true
404             eg.
405             isClassAddrA('127.0.32.45');
406             isClassAddrA('00001111.11110010.00100100.10000001');
407              
408             =head2 dec2binIpAddr
409              
410             dec2binIpAddr() : returns ip in binary dotted notation
411             eg.
412             dec2binIpAddr('128.0.0.56');
413              
414             =head2 bin2decIpAddr
415              
416             bin2decIpAddr() : returns ip in decimal dotted notation
417             eg.
418             bin2decIpAddr('10001000.10100001.00010101.00000001');
419              
420             =head2 getAddrMaskDefault
421              
422             getAddrMaskDefault() : returns default subnet mask in dotted decimal notation
423             eg.
424             getAddrMaskDefault('124.45.0.0'); >> 255.0.0.0
425             getAddrMaskDefault('10000000.00000001.01010101.10000001'); >> 255.0.0.0
426              
427             =head2 getAddrClass
428              
429             getAddrClass() : returns class (A/B/C/D/E) of ip address
430             eg.
431             getAddrClass('124.45.0.0');
432             getAddrClass('00001111.11110010.00100100.10000001');
433              
434             =head2 isValidMask
435              
436             isValidMask() : returns 1 if valid mask
437             eg.
438             isValidMask('255.255.252.0');
439             isValidMask('11111111.00000000.00000000.00000000');
440              
441             =head2 extendMaskByBits
442              
443             extendMaskByBits(,)
444             : returns mask after extending/turning on given no. of bits after the already on bits of the mask
445             eg.
446             extendMaskByBits('255.255.0.0',2); >> 255.255.192.0
447             extendMaskByBits('11111111.00000000.00000000.00000000',2); >> 11111111.11000000.00000000.00000000
448              
449             =head2 calcSubnet
450              
451             calcSubnet() : returns (no. of subnets, no. of hosts)
452             calcSubnet('128.90.80.12');
453             calcSubnet('11000000.00000000.11000000.01011100');
454             - These always assumes Default Mask in calculation - hence no of subnets returned is always 0
455              
456             =head2 calcSubnetCIDR
457              
458             calcSubnetCIDR(, [])
459             : returns (no. of subnets, no. of hosts)
460             calcSubnetCIDR('128.87.56.26/28');
461             calcSubnetCIDR('128.87.56.26/28','255.255.252.0');
462              
463             =head2 calcSubnetExt
464              
465             calcSubnetExt(ip addr in decimal/binary notation>, no. of bits to extend in default mask OR no. of borrowed bits)
466             : returns (no. of subnets, no. of hosts)
467             eg.
468             calcSubnetExt('128.0.0.1',4);
469             calcSubnetExt('10001000.10100001.00010101.00000001',4);
470              
471             Expln : no. of borrowed bits is added to the default subnet mask of ip addr to subnet mask
472             and subnetting is done so :
473             ***************************************************
474             127.0.40.1 = ip addr
475             255.0.0.0 = default subnet mask
476             no. of borrowed bits = 4
477             => 255.240.0.0 = extended mask
478             ***************************************************
479              
480             =head2 getNetworkAddr
481              
482             getNetworkAddr(,
483             ,
484             ,
485            
486             ) : returns network/broadcast addresses of the subnets after subnetting as a list
487             eg.
488             getNetworkAddr('198.23.16.0','255.255.255.240','255.255.255.252'); >> ('198.23.16.0','198.23.16.4','198.23.16.8','198.23.16.12')
489             getNetworkAddr('198.23.16.0','255.255.255.240','255.255.255.252',1); >> ('198.23.16.3','198.23.16.7','198.23.16.11','198.23.16.15')
490             getNetworkAddr('10000000.00000001.01010101.10000001',
491             '11111111.11111111.11111111.11110000',
492             '11111111.11111111.11111111.11111100',); >> Always returns n/w addresses in dotted decimal irrespective of binary/decimal
493             address parameter passed
494              
495             =head1 CAVEAT
496              
497             IPv4 only
498             Validation of IP addresses are done, but because of conversions here and there it may not show the IP address properly in the error message
499             as passed earlier by the user.
500              
501             =head1 Similar Modules
502              
503             Net::IP, Net::IpAddr etc.
504              
505             =head1 SUPPORT
506              
507             debashish@cpan.org
508              
509             =head1 ACKNOWLEDGEMENTS
510              
511             =head1 COPYRIGHT & LICENSE
512              
513             Copyright 2013 Debashish Parasar, all rights reserved.
514              
515             This program is free software; you can redistribute it and/or modify it
516             under the same terms as Perl itself.
517              
518             =cut