#!/usr/bin/perl -w use strict; use vars qw/ $opt_s /; use Getopt::Std; use Net::Netmask; my $subnet; my %data = (); my $ARP = '/tmp/arp.bak'; getopt('s'); if (defined($opt_s) && $opt_s ne '') { $subnet = $opt_s; } else { help(); } my $block = new Net::Netmask($subnet); if (defined($block->{'ERROR'})) { die "Invalid subnet/mask combination."} my @range = $block->enumerate(); #We need to get our temp file system `tail -250000 /tmp/arp > $ARP`; #Open the file and read it line by line open(FH, $ARP) || die ("Couldn't open the arp file $ARP"); while () { if (/^((?:\d{1,3}\.){3}\d{1,3})/) { if ($block->match($1)) { $data{$1}++; } } } close FH; #I need to specifically remove the network, gateway and broadcast #addresses since we don't care about those. #First remove from enumeration array shift @range; #Network Address shift @range; #Gateway Address pop @range; #Broadcast Address #Now remove from the IP's found in the ARP cache delete $data{$block->base()}; delete $data{$block->nth(1)}; delete $data{$block->broadcast()}; my @matches = keys %data; #Compare the array of matched IPs to the enumerated Netblock my @intersection = my @difference = (); undef %data; foreach my $element (@matches, @range) { $data{$element}++ } foreach my $element (keys %data) { push @{ $data{$element} > 1 ? \@intersection : \@difference }, $element; } #Now I'd like to sort the IPs (a little Schwartzian Transform action here...) my @sorted = map { join '.', unpack 'N*', $_ } sort map { pack 'N*', split /\./ } @difference; print "Addresses that are candidates for reclaim in:\n"; print $block->desc(), "\n\n"; print join("\n",@sorted), "\n"; sub help { print <<'HELP'; You must supply a valid subnet. Acceptable formats are as follows: 192.168.1.0/24 <--- The preferred form. 192.168.1.0:255.255.255.0 192.168.1.0-255.255.255.0 syntax: arpscan.pl -s 192.168.1.0/24 HELP exit(1); }