1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
package Net::Subnets;
use strict;
use vars qw($VERSION);
$VERSION = '0.21';
sub new {
my $self = shift;
return bless( {}, ( ref($self) || $self ) );
}
sub subnets {
my ( $self, $subnets ) = @_;
my %masks;
foreach (@$subnets) {
/^(.+?)\/(.+)$/o;
my $revmask = 32 - ( $2 || 32 );
$self->{subnets}{$revmask}
{ unpack( "N", pack( "C4", split( /\./, $1 ) ) ) >> $revmask } = $_;
$masks{$revmask}++;
}
@{ $self->{masks} } =
sort( { $masks{$a} <=> $masks{$b} } keys(%masks) );
}
sub check {
my ( $self, $address ) = @_;
foreach ( @{ $self->{masks} } ) {
my $option =
unpack( "N", pack( "C4", split( /\./, $$address ) ) ) >> $_;
if ( $self->{subnets}{$_}{$option} ) {
return \( $self->{subnets}{$_}{$option} );
}
}
return 0;
}
sub range {
my ( $self, $subnet ) = @_;
$$subnet =~ /^(.+?)\/(.+)$/o;
my $net =
pack( 'C4', split( /\./, $1 ) ) &
pack( 'B*', ( 1 x $2 ) . ( 0 x ( 32 - ( $2 || 32 ) ) ) );
my $lowip =
join( '.', unpack( 'C4', pack( 'B*', ( 0 x 31 ) . 1 ) | $net ) );
my $highip = join(
'.',
unpack(
'C4', pack( 'B*', ( 0 x $2 ) . ( 1 x ( 31 - $2 ) ) . 0 ) | $net
)
);
if ( $2 == 32 ) {
return ( \$highip, \$highip );
}
return ( \$lowip, \$highip );
}
sub list {
my ( $self, $lowip, $highip ) = @_;
my $lowint = unpack( "N", pack( "C4", split( /\./, $$lowip ) ) );
my $highint = unpack( "N", pack( "C4", split( /\./, $$highip ) ) );
my @list = ( join( '.', unpack( 'C4', pack( 'N', $lowint ) ) ) );
while ( $lowint lt $highint ) {
push( @list, join( '.', unpack( 'C4', pack( 'N', ++$lowint ) ) ) );
}
return \@list;
}
1;
__END__
=head1 NAME
Net::Subnets - Computing subnets in large scale networks
=head1 SYNOPSIS
use Net::Subnets;
my $sn = Net::Subnets->new;
$sn->subnets( \@subnets );
if ( my $subnetref = $sn->check( \$address ) ) {
...
}
my ( $lowipref, highipref ) = $sn->range( \$subnet );
my $listref = $sn->list( \( $lowipref, $highipref ) );
=head1 DESCRIPTION
Very fast matches large lists of IP addresses against many CIDR subnets and
calculates IP address ranges.
The following functions are provided by this module:
new()
Creates an "Net::Subnets" object.
It takes no arguments.
subnets( \@subnets )
The subnets() function lets you prepare a list of CIDR subnets.
It takes an array reference.
check( \$address )
The check() function lets you check an IP address against the
previously prepared subnets.
It takes a scalar reference and returns a scalar reference to
the first matching CIDR subnet.
range( \$subnet )
The range() function lets you calculate the IP address range
of a subnet.
It takes a scalar reference and returns two scalar references to
the lowest and highest IP address.
list( \$lowip, \$highip )
The list() function lets you calculate a list containing all IP
addresses in a given range.
It takes two scalar references and returns a reference to a list
containing the IP addresses.
This is a simple and efficient example for subnet matching:
use Net::Subnets;
my @subnets = qw(10.0.0.0/24 10.0.1.0/24);
my @addresses = qw(10.0.0.1 10.0.1.2 10.0.3.1);
my $sn = Net::Subnets->new;
$sn->subnets( \@subnets );
my $results;
foreach my $address ( @addresses ) {
if ( my $subnetref = $sn->check( \$address ) ) {
$results .= "$address: $$subnetref\n";
}
else {
$results .= "$address: not found\n";
}
}
print( $results );
This is a simple example for range calculation:
use Net::Subnets;
my @subnets = qw(10.0.0.0/24 10.0.1.0/24);
my $sn = Net::Subnets->new;
my $results;
foreach my $subnet ( @subnets ) {
my ( $lowipref, $highipref ) = $sn->range( \$subnet );
$results .= "$subnet: $$lowipref - $$highipref\n";
}
print( $results );
This is a simple example for list generation:
use Net::Subnets;
my $lowip = '192.168.0.1';
my $highip = '192.168.0.100';
my $sn = Net::Subnets->new;
my $listref = $sn->list( \( $lowip, $highip ) );
foreach my $address ( @{ $listref } ) {
# do something cool
}
=head1 AUTHOR
Sebastian Riedel (sri@cpan.org),
Juergen Peters (juergen.peters@taulmarill.de)
=head1 COPYRIGHT
Copyright 2003 Sebastian Riedel & Juergen Peters. All rights reserved.
This library is free software. You can redistribute it and/or
modify it under the same terms
as perl itself.
=cut
|