File Coverage

blib/lib/Network/IPv4Addr.pm
Criterion Covered Total %
statement 69 74 93.2
branch 34 46 73.9
condition 9 15 60.0
subroutine 11 11 100.0
pod 6 8 75.0
total 129 154 83.7


line stmt bran cond sub pod time code
1             # IPv4Addr.pm - Perl module to manipulate IPv4 addresses.
2             #
3             # Author: Francis J. Lacoste
4             #
5             # Copyright (C) 1999 Francis J. Lacoste, iNsu Innovations Inc.
6             #
7             # This program is free software; you can redistribute it and/or modify
8             # it under the terms as perl itself.
9             #
10              
11             package Network::IPv4Addr;
12              
13 1     1   8862 use strict;
  1         2  
  1         44  
14 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         176  
15              
16             require Exporter;
17             require AutoLoader;
18              
19             @ISA = qw(Exporter AutoLoader);
20              
21             # Items to export into callers namespace by default. Note: do not export
22             # names by default without a very good reason. Use EXPORT_OK instead.
23             # Do not simply export all your public functions/methods/constants.
24             @EXPORT = qw();
25              
26             %EXPORT_TAGS = (
27             all => [qw{ ipv4_parse ipv4_checkip
28             ipv4_network ipv4_broadcast
29             ipv4_cidr2msk ipv4_msk2cidr
30             ipv4_in_network ipv4_dflt_netmask
31             } ],
32             );
33              
34             @EXPORT_OK = qw();
35              
36             Exporter::export_ok_tags('all');
37              
38             $VERSION = '0.05';
39              
40             # Preloaded methods go here.
41 1     1   6 use Carp;
  1         11  
  1         1791  
42              
43             # Functions to manipulate IPV4 address
44             my $ip_rgx = "\\d+\\.\\d+\\.\\d+\\.\\d+";
45              
46             # Given an IPv4 address in host, ip/netmask or cidr format
47             # returns a ip / cidr pair.
48             sub ipv4_parse($;$) {
49 49     49 1 64 my ($ip,$msk);
50             # Called with 2 args, assume first is IP address
51 49 100       102 if ( defined $_[1] ) {
52 12         15 $ip = $_[0];
53 12         18 $msk= $_[1];
54             } else {
55 37         169 ($ip) = $_[0] =~ /($ip_rgx)/o;
56 37         95 ($msk) = $_[0] =~ m!/(.+)!o;
57             }
58              
59             # Remove white spaces
60 49 50       107 $ip = ipv4_chkip( $ip ) or
61             croak __PACKAGE__, ": invalid IPv4 address: ", $ip, "\n";
62 49 100       116 $msk =~ s/\s//g if defined $msk;
63              
64             # Check Netmask to see if it is a CIDR or Network
65 49 100       89 if (defined $msk ) {
66 24 100       98 if ($msk =~ /^\d{1,2}$/) {
    50          
67             # Check cidr
68 20 50 33     90 croak __PACKAGE__, ": invalid cidr: ", $msk, "\n"
69             if $msk < 0 or $msk > 32;
70             } elsif ($msk =~ /^$ip_rgx$/o ) {
71 4         8 $msk = ipv4_msk2cidr($msk);
72             } else {
73 0         0 croak __PACKAGE__, ": invalid netmask specification: ", $msk, "\n";
74             }
75             } else {
76             # Host
77 25         54 return $ip;
78             }
79 24 100       93 wantarray ? ($ip,$msk) : "$ip/$msk";
80             }
81              
82             sub ipv4_dflt_netmask($) {
83 6     6 0 22 my ($ip) = ipv4_parse($_[0]);
84              
85 6         14 my ($b1) = split /\./, $ip;
86              
87 6 100       22 return "255.0.0.0" if $b1 <= 127;
88 3 100       21 return "255.255.0.0" if $b1 <= 191;
89 2         6 return "255.255.255.0";
90             }
91              
92             # Check form a valid IPv4 address.
93             sub ipv4_chkip($) {
94 62     62 0 231 my ($ip) = $_[0] =~ /($ip_rgx)/o;
95              
96 62 50       137 return undef unless $ip;
97              
98             # Check that bytes are in range
99 62         169 for (split /\./, $ip ) {
100 248 50 33     1072 return undef if $_ < 0 or $_ > 255;
101             }
102 62         198 return $ip;
103             }
104              
105             # Transform a netmask in a CIDR mask length
106             sub ipv4_msk2cidr($) {
107 13 50   13 1 27 my $msk = ipv4_chkip( $_[0] )
108             or croak __PACKAGE__, ": invalid netmask: ", $_[0], "\n";
109              
110 13         58 my @bytes = split /\./, $msk;
111              
112 13         21 my $cidr = 0;
113 13         22 for (@bytes) {
114 52         122 my $bits = unpack( "B*", pack( "C", $_ ) );
115 52         101 $cidr += $bits =~ tr /1/1/;
116             }
117 13         51 return $cidr;
118             }
119              
120             # Transform a CIDR mask length in a netmask
121             sub ipv4_cidr2msk($) {
122 6     6 1 12 my $cidr = shift;
123 6 50 33     39 croak __PACKAGE__, ": invalid cidr: ", $cidr, "\n"
124             if $cidr < 0 or $cidr > 32;
125              
126 6         19 my $bits = "1" x $cidr . "0" x (32 - $cidr);
127              
128 6         43 return join ".", (unpack 'CCCC', pack("B*", $bits ));
129             }
130              
131             # Return the network address of
132             # an IPv4 address
133             sub ipv4_network($;$) {
134 13     13 1 32 my ($ip,$cidr) = ipv4_parse( $_[0], $_[1] );
135              
136             # If only an host is given, use the default netmask
137 13 100       32 unless ($cidr) {
138 2         5 $cidr = ipv4_msk2cidr( ipv4_dflt_netmask($ip) );
139             }
140 13         55 my $u32 = unpack "N", pack "CCCC", split /\./, $ip;
141 13         41 my $bits = "1" x $cidr . "0" x (32 - $cidr );
142              
143 13         30 my $msk = unpack "N", pack "B*", $bits;
144              
145 13         65 my $net = join ".", unpack "CCCC", pack "N", $u32 & $msk;
146              
147 13 50       80 wantarray ? ( $net, $cidr) : "$net/$cidr";
148             }
149              
150             sub ipv4_broadcast($;$) {
151 3     3 1 10 my ($ip,$cidr) = ipv4_parse( $_[0], $_[1] );
152              
153             # If only an host is given, use the default netmask
154 3 100       33 unless ($cidr) {
155 1         4 $cidr = ipv4_msk2cidr( ipv4_dflt_netmask($ip) );
156             }
157              
158 3         12 my $u32 = unpack "N", pack "CCCC", split /\./, $ip;
159 3         13 my $bits = "1" x $cidr . "0" x (32 - $cidr );
160              
161 3         8 my $msk = unpack "N", pack "B*", $bits;
162              
163 3         14 my $broadcast = join ".", unpack "CCCC", pack "N", $u32 | ~$msk;
164              
165 3         14 $broadcast;
166             }
167              
168             sub ipv4_in_network($$;$$) {
169 11     11 1 18 my ($ip1,$cidr1,$ip2,$cidr2);
170 11 50       24 if ( @_ >= 3) {
171 0         0 ($ip1,$cidr1) = ipv4_parse( $_[0], $_[1] );
172 0         0 ($ip2,$cidr2) = ipv4_parse( $_[2], $_[3] );
173             } else {
174 11         25 ($ip1,$cidr1) = ipv4_parse( $_[0]);
175 11         33 ($ip2,$cidr2) = ipv4_parse( $_[1]);
176             }
177              
178             # Check for magic addresses.
179 11 100 100     59 return 1 if $ip1 eq "255.255.255.255" or $ip1 eq "0.0.0.0";
180 9 100 100     53 return 1 if $ip2 eq "255.255.255.255" or $ip2 eq "0.0.0.0";
181              
182             # Case where first argument is really an host
183 7 100       20 return $ip1 eq $ip2 unless ($cidr1);
184              
185             # Case where second argument is an host
186 5 50       11 if ( not defined $cidr2) {
    0          
187 5         12 return ipv4_network( $ip1, $cidr1) eq ipv4_network( $ip2, $cidr1 );
188             } elsif ( $cidr2 > $cidr1 ) {
189             # Netmask 2 is more specific than netmask 1
190 0           return ipv4_network( $ip1, $cidr2) eq ipv4_network( $ip2, $cidr2);
191             } else {
192             # Netmask 1 is more specific than netmask 2
193 0           return ipv4_network( $ip1, $cidr1) eq ipv4_network( $ip2, $cidr1);
194             }
195             }
196             # Autoload methods go after =cut, and are processed by the autosplit program.
197              
198             1;
199             __END__