File Coverage

blib/lib/Net/IPv4Addr.pm
Criterion Covered Total %
statement 79 82 96.3
branch 37 46 80.4
condition 14 21 66.6
subroutine 12 12 100.0
pod 6 8 75.0
total 148 169 87.5


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