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