Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Re: finding if an ip is in a subnet

by atcroft (Monsignor)
on Mar 05, 2014 at 19:09 UTC ( #1077139=note: print w/ replies, xml ) Need Help??


in reply to finding if an ip is in a subnet

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.


Comment on Re: finding if an ip is in a subnet
Select or Download Code

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1077139]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (13)
As of 2014-08-22 16:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (159 votes), past polls