# /usr/bin/perl -Tw #----------------------------------------------------------------------- # Name: ip-cidrsift.pl # Created: 2015-08-24 # Author: Royce Williams # Purpose: Show IPs not falling into a list of CIDRs. # $Id$ #----------------------------------------------------------------------- # FIXME - too slow. Use trie? #----------------------------------------------------------------------- use strict; use warnings; use Getopt::Std; use NetAddr::IP; use File::Slurp; use Data::Dumper; $|++; #----------------------------------------------------------------------- my $cidr_list_file; my @cidr_list; my $ip_list_file; my @ip_list; my %ip_cidr_map; our $opt_c ; our $opt_i; #----------------------------------------------------------------------- # Process cmd line. getopt('c:i:'); if ($opt_c and $opt_i) { $cidr_list_file = $opt_c; $ip_list_file = $opt_i; print STDERR "- Reading list of CIDRs from " . $cidr_list_file . "\n"; print STDERR "- Reading list of IPs from " . $ip_list_file . "\n"; } else { print STDERR "Usage: ip-cidrsift.pl -c [file of CIDRs] -i [file of IPs]\n"; exit; } @ip_list = read_file($ip_list_file) or die ("Could not read $ip_list_file: $! \n"); @cidr_list = read_file($cidr_list_file) or die ("Could not read $cidr_list_file: $! \n"); #----------------------------------------------------------------------- sub sort_by_ip(@) { # Not yet used. # Kudos: brian d foy, http://stackoverflow.com/a/6919914/263879 my @sorted =@_; # Sort by IP, ignoring invalid IPs. my $array = shift; my @ip = @$array; map { $_->[0] } sort { $a->[1] <=> $b->[1] } # Will generate errors if compared to undef. map { [ $_, eval { Net::IP->new( $_ )->intip } ] } @ip; return @sorted; } #----------------------------------------------------------------------- sub get_16($) { my $ip = shift; my @new = split(/\./, $ip); pop @new; pop @new; my $string = join('.', @new); return $string; } #----------------------------------------------------------------------- # Main. # Rough filter for non-IP records. @cidr_list = grep /^[0-9]/, @cidr_list; print STDERR "- Converting to NetAddr::IP ...\n"; #@ip_list = sort_by_ip(\@ip_list); foreach my $ip (@ip_list) { print STDERR 'i'; chomp($ip); my $ip_object = NetAddr::IP->new($ip); foreach my $current_network (@cidr_list) { chomp($current_network); # Only check if in the same /16. my $ip_16 = get_16($ip); my $network_16 = get_16($current_network); next unless ($ip_16 eq $network_16); my $current_network_object = NetAddr::IP->new($current_network); if ($current_network_object->contains($ip_object)) { # If current route is more-specific than stored route, replace it. if (exists($ip_cidr_map{$ip})) { my $previous_network = $ip_cidr_map{$ip}{'network'}; my $previous_network_object = NetAddr::IP->new($previous_network); if ($previous_network_object->contains($current_network_object)) { $ip_cidr_map{$ip}{'network'} = $current_network; $ip_cidr_map{$ip}{'network_object'} = $current_network_object; print STDERR 'r'; } else { print STDERR 's'; } } else { $ip_cidr_map{$ip}{'network'} = $current_network; $ip_cidr_map{$ip}{'network_object'} = $current_network_object; print STDERR 'n'; } } else { #if (exists($ip_cidr_map{$ip})) { # $ip_cidr_map{$ip} = $current_network; #} else { #} } } } foreach my $ip (@ip_list) { my $route = $ip_cidr_map{$ip}{'network'} ? $ip_cidr_map{$ip}{'network'} : ''; print "$ip" . '|' . $route . "\n" #foreach (sort keys %ip_cidr_map) { # print "$_" . '|' . $ip_cidr_map{$_}{'network'} . "\n" } #print Dumper %ip_cidr_map; #print Dumper @ip_list; #-----------------------------------------------------------------------