While I think there are better methods of doing this (a horse that will be beaten long after it is turning to dust), within the criteria given the following method would work, given here as a small test script (debug code left in place). (Reading the IP information into a similar structure is left as an exercise to the reader.) The central trick is converting the IP in question, the network address, and netmask to unsigned numbers, then performing a binary AND of the IP and netmask and the network address and netmask, and see if the results match.
Actually, after testing the code at the bottom of this post, I realized there was a quicker way-build a hash of the results of AND on the network address and netmask, then loop through the IPs and netmasks and look for a match.
#!perl
use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Deepcopy = 1;
$Data::Dumper::Sortkeys = 1;
$| = 1;
srand();
my $_DEBUG = 0;
my %known = (
q{192.168.0.0} => {
network => q{192.168.0.0},
mask => q{24},
},
q{192.168.42.128} => {
network => q{192.168.42.128},
mask => q{255.255.255.128},
},
q{192.168.127.0} => {
network => q{192.168.127.0},
mask => q{255.255.255.0},
},
);
my @test = (
q{192.168.0.1}, q{192.168.42.25},
q{192.168.42.192}, q{192.168.127.0},
q{192.168.127.10}, q{192.168.127.255},
);
my %seen;
foreach my $k ( keys %known ) {
my $n = str2n( $known{$k}{network} );
my $m;
if ( $known{$k}{mask} !~ m/\./ ) {
$m = 0xFFFFFFFF << ( 32 - $known{$k}{mask} );
}
else { $m = str2n( $known{$k}{mask} ); }
$seen{$m}{ $n & $m } = $k;
}
TESTING: foreach my $ip (@test) {
my $i = str2n($ip);
foreach my $m ( sort { $b <=> $a } keys %seen ) {
my $result = $i & $m;
if ( defined( $seen{$m}{ $i & $m } ) ) {
my $k = $seen{$m}{$result};
print sprintf qq{%s in %s / %s\n}, $ip,
$known{$k}{network}, $known{$k}{mask};
next TESTING;
}
}
print sprintf qq{%s not in provided ranges\n}, $ip;
}
print Data::Dumper->Dump(
[ \@test, \%known, \%seen, ],
[qw( *test *known *seen )]
),
qq{\n}
if ($_DEBUG);
sub str2n {
print sprintf(
qq{\t\t%d %s\n\t\t%d %s\n\t\t%d %d\n},
__LINE__,
$_[0],
__LINE__,
join( q{ }, split /\D/, $_[0] ),
__LINE__,
unpack( q{N}, pack( q{C4}, split /\D/, $_[0] ), )
) if ($_DEBUG);
return unpack( q{N}, pack( q{C4}, split /\D/, $_[0] ) );
}
# Output:
#
# $ perl test-20140305-01.pl
# 192.168.0.1 in 192.168.0.0 / 24
# 192.168.42.25 not in provided ranges
# 192.168.42.192 in 192.168.42.128 / 255.255.255.128
# 192.168.127.0 in 192.168.127.0 / 255.255.255.0
# 192.168.127.10 in 192.168.127.0 / 255.255.255.0
# 192.168.127.255 in 192.168.127.0 / 255.255.255.0
#
Original code:
#!perl
use strict;
use warnings;
use Data::Dumper;
$| = 1;
srand();
my $_DEBUG = 0;
my %known = (
192.168.0.0 => {
network => q{192.168.0.0},
mask => q{24},
},
192.168.42.128 => {
network => q{192.168.42.128},
mask => q{255.255.255.128},
},
192.168.127.0 => {
network => q{192.168.127.0},
mask => q{255.255.255.0},
},
);
my @test = (
q{192.168.0.1}, q{192.168.42.25},
q{192.168.42.192}, q{192.168.127.0},
q{192.168.127.10}, q{192.168.127.255},
);
TESTING: foreach my $ip (@test) {
my $i = str2n($ip);
print
sprintf( qq{%d %s: %d (0x%8x)\n}, __LINE__, $ip, $i, $i, )
if ($_DEBUG);
foreach my $k ( keys %known ) {
print sprintf(
qq{\t%d %s / %s\n},
__LINE__, $known{$k}{network},
$known{$k}{mask},
) if ($_DEBUG);
my ( $m, $n, );
$n = str2n( $known{$k}{network} );
if ( $known{$k}{mask} !~ m/\./ ) {
$m = 0xFFFFFFFF << ( 32 - $known{$k}{mask} );
}
else { $m = str2n( $known{$k}{mask} ); }
print sprintf( qq{\t%d %d / %d\n}, __LINE__, $n, $m, )
if ($_DEBUG);
my $n_m = $n & $m;
my $i_m = $i & $m;
print sprintf( qq{\tTest range: %d / %d\n}
. qq{\tnet & mask: %d\n}
. qq{ip & mask: %d\nResults: %d\n},
$n, $m, $n_m, $i_m, $i_m == $n_m )
if ($_DEBUG);
if ( $n_m == $i_m ) {
print $ip, q{ in }, $known{$k}{network}, q{/},
$known{$k}{mask}, qq{\n};
next TESTING;
}
}
print $ip, qq{ not in provided ranges\n};
}
sub str2n {
print sprintf(
qq{\t\t%d %s\n\t\t%d %s\n\t\t%d %d\n},
__LINE__,
$_[0],
__LINE__,
join( q{ }, split /\D/, $_[0] ),
__LINE__,
unpack( q{N}, pack( q{C4}, split /\D/, $_[0] ), )
) if ($_DEBUG);
return unpack( q{N}, pack( q{C4}, split /\D/, $_[0] ) );
}
# Output:
#
# $ perl test-20140305-00.pl
# 192.168.0.1 in 192.168.0.0/24
# 192.168.42.25 not in provided ranges
# 192.168.42.192 in 192.168.42.128/255.255.255.128
# 192.168.127.0 in 192.168.127.0/255.255.255.0
# 192.168.127.10 in 192.168.127.0/255.255.255.0
# 192.168.127.255 in 192.168.127.0/255.255.255.0
#
Hope that helps.
Update: 2014-03-05
Updated code to remove $flag variable.
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.