| Category: | Utilities |
| Author/Contact Info | Garret D. Kelly, garret dot kelly at gmail dot com |
| Description: | The aim of this program is to aid admins in watching for machines which are ARP storming. This program, when invoked as root, will open a pipe from tcpdump and wait for ARP packets. It maintains a list of IPs who are sending ARP packets, and the number of ARP packets they have sent in the past monitoring period; if the number of packets a given IP has broadcast differs from the average number of broadcast packets by more than the standard deviance, that IP is displayed in a list of ARP offenders. |
#!/usr/bin/perl -w
+
+
# Garret D. Kelly, Oct 13th, 2004
# garret.kelly@gmail.com
# Do what you will.
+
+
use strict;
use IPC::Shareable;
use POSIX qw(ceil);
use Term::ANSIColor qw(:constants);
+
+
my %options = (destroy => 'yes', create => 'yes');
+
+
my $purgedelay = 3600;
my $dispdelay = 5;
+
+
# The shareable segment remains dirty because this program never
# exits clean. Teehee. We _must_ clean the shm here or we'll get
# last run's counts.
IPC::Shareable->clean_up;
+
+
# Process command-line arguments here.
+
+
sub watch_traffic {
my %arpcounts;
tie %arpcounts, 'IPC::Shareable', 'narp', {%options};
%arpcounts = ();
# For some reason this doesn't perform like you think
# it would. I think it might be something to do with
# the way tcpdump pukes its output to us.
while(1) {
open TD, "-|", "tcpdump -ni eth0";
while(<TD>) {
if(/arp/) {
chomp;
my @dumpparts = split / /, $_;
my $addr = $dumpparts[$#dumpparts];
if(!$arpcounts{$addr}) {
$arpcounts{$addr} = 1;
} else {
$arpcounts{$addr} = $arpcounts
+{$addr} + 1;
}
}
}
close TD;
print BOLD, RED, "Warning: ", RESET, "Pipe to tcpdump
+broken\n";
}
}
+
+
+
+
$|++;
+
+
# Fork off a thread for watching for traffic.
if(fork() == 0) {
watch_traffic();
}
+
+
# We'll clear the arp counts every hour to make sure we
# don't get a huge backlog or anything like that.
if(fork() == 0) {
my %ac;
tie %ac, 'IPC::Shareable', 'narp', {%options};
while(1) {
sleep $purgedelay;
%ac = ();
}
}
# Compute stats and display abusers every so often;
while(1) {
system('clear');
my %arpcounts;
tie %arpcounts, 'IPC::Shareable', 'narp';
print BOLD, "arpsnitch", RESET, " v0.1a -- Garret D. Kelly --
+", BOLD, "Current Average: ", RESET;
my ($avg, $avgdeviance, %deviants);
for(keys %arpcounts) {
$avg += $arpcounts{$_};
}
if((scalar keys %arpcounts) == 0) {
print "N/A\n\nNo statistics have been accumulated so f
+ar.";
sleep $dispdelay;
next;
}
$avg /= scalar keys %arpcounts;
print ceil($avg) . "\n";
print BOLD, BLACK, "IP\t\t\tCOUNT\t\tDEVIANCE\n", RESET;
for(keys %arpcounts) {
$avgdeviance += $arpcounts{$_} - $avg;
}
$avgdeviance /= scalar keys %arpcounts;
for(keys %arpcounts) {
if($arpcounts{$_} - $avg > $avgdeviance) {
print BOLD, RED, "$_\t\t", RESET, "$arpcounts{
+$_}\t\t" . ceil($arpcounts{$_} - $avg) . "\n";
}
}
sleep $dispdelay;
}
|
|
|
|---|