Use Perl to Match IP to Subnets
In the case that you have many IP addresses in your logs and don’t know what networks they belong to, you can easily map them using Perl.
- You must have a database of networks to match against
- You must have a list of IP to search in the database of networks
- use NetAddr::IP module to help you do the heavy lifting
- Term::ANSIcolor and Win32::Console::ANSI colorize stuff
- Create a subroutine to convert between dotted dash format and decimal (for sorting)
- Then loop over all of your ips and see where they fit!
Custom Perl Programming can solve tedious work like this in seconds without the risk of errors. This particular snippet was written to parse millions of IP addresses against a few thousand known networks. What will you use our Perl script that matches IP to subnets script for? Please comment if it’s helped you.
Here is the script that uses Perl to match IP addresses to subnets:
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 |
#!/usr/bin/perl use warnings; use strict; use NetAddr::IP; # Term::ANSIColor doesn't work on windows without Win32 Console if( $^O eq 'MSWin32' ){{ eval { require Win32::Console::ANSI; } or last; }} use Term::ANSIColor; # I like colors use Benchmark; # in case we need to optimize # start timer my $t0 = new Benchmark; #-------------------------------- # the database of networks we know of my %nets = ( '10.1.0.0/16' => 'CORP WAN', '172.28.117.0/24' => 'VLAN 388 - DMZ Servers', '192.168.1.0/24' => 'VLAN 400 - Guest Wireless', ); my %data; # will contain deduped ips my %unknowns; # if we want to see the unknown IP, deduped my $unknowns = 0; # contains unknown Ips my $lines = 0; # just a counter # this sub converts a decimal IP to a dotted IP sub dec2ip ($) { join '.', unpack 'C4', pack 'N', shift; } # this sub converts a dotted IP to a decimal IP sub ip2dec ($) { unpack N => pack CCCC => split /\./ => shift; } # build unique list from __DATA__ while (<DATA>) { # tested: finds multiple (all on each line) if (m/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}[\/-][0-9]{1,2}|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/g) { my $ip = $1; chomp $ip; $ip = ip2dec($ip); $data{$ip}++; $lines++; } } print color ('yellow'), " ------------------ IP to Net Mapper ------------------ Mapping $lines IP to Known Networks... GIVEN IP\tFITS IN THIS NETWORK\n", color("reset"); # build network only map from input side. foreach my $ips (sort keys %data){ my $found = 0; # build ip object my $ip = NetAddr::IP->new($ips); $ips = dec2ip($ips); # loop over data hash and check every key to see where it fits # FIXME: what happens if it fits in an overlap? foreach my $cidr (keys %nets) { # define net object my $network = NetAddr::IP->new($cidr); if ($ip->within($network)) { $found++; print color ('green'),"$ips\t$cidr ($nets{$cidr})\n", color('reset'); } } # these ips were not found, so... handle unless ($found) { #print "$ips\tUNKNOWN NETWORK\n"; $unknowns++; $unknowns{$ips}++; } } # we want a list to munge and parse later, give it to us my $unique_ip = keys %data; if ($unknowns) { foreach my $unknown (sort keys %unknowns) { print color('red'),"$unknown\t0.0.0.0 (UNKNOWN)\n", color('reset'); } } # print stats: print color ('yellow')," ------------------ DONE! ------------------ INPUT: $lines UNIQUE IP: $unique_ip UNKNOWN IP: $unknowns "; my $t1 = new Benchmark; my $td = timediff($t1,$t0); print "\ntook '",timestr($td), "' seconds\n", color("reset"); # sample data __DATA__ 10.1.11.181 10.1.12.210 172.28.117.216 172.28.117.58 192.168.1.90 192.168.1.91 192.168.1.91 192.168.1.91 192.168.1.91 22.22.22.22 |
I appreciate that you shared your code.
Thanks
Thank you Johnathan.
Thanks for the code. One note, though, you are reusing the $found variable for two things, and not actually reporting the number of found IPs in the results. Still, it was an easy fix and got me moving on finding subnets for 6000 IPs in a list of results, which would take me days to match!
Thank you for the comment and pointing that out. I’ll fix and update (and add some color to it).