#!/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(
) {
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 far.";
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;
}
|