What's this? | Future ideas | Notes | p0f.udpsend | p0f.udplog | p0f.graph | p0fIP2OS.pm | p0f.php | New versions
I've stopped development of the stuff below, replacing it with similar code using a SQL database.
This stuff is just a few simple perl scripts that I made for showing
fingerprint statistics from p0f for a couple of routers.
And once I had this stuff working, of course I also wanted to include OS info in my mail statistics,
wich was an easy thing to do with p0fIP2OS.pm.
<senders IP><a hexadecimal number>.p0flog10.0.10.4.46526AE6D87.p0flogRegards
/Jonas Eckerman
#!/usr/bin/perl
use strict;
use IO::Socket;
use Proc::Daemon;
my $daemonize = 1;
my $port = 4711;
my $host = '10.0.7.10';
my $filter = 'not ether src 00:05:5d:71:93:30';
my $interface = 'ste0';
Proc::Daemon::Init if ($daemonize);
my $cli = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => 'udp');
my $sl = '';
print "p0f: /usr/local/bin/p0f -tSFi $interface '$filter'|\n" if (!$daemonize);
open(P0F,"/usr/local/bin/p0f -tSFi $interface '$filter'|") or die("Can't start.");
while (my $l = <P0F>) {
$l =~ s/[\r\n]//g;
$sl .= "$l\n" if ($sl eq '' || $l =~ /^\s+/);
if ($l =~ /^\s+->/) {
print $sl if (!$daemonize);
$cli->send($sl);
$sl = '';
}
}
close(P0F);
#!/usr/bin/perl
use strict;
use IO::Socket;
use Proc::Daemon;
use Sys::Syslog;
use Fcntl;
my $daemonize = 1;
my $port = 516;
my $path = '/var/p0f/';
my $cycle = 5*60;
my $user ;#= 'stats';
my $group ;#= 'stats';
my $facility = 'user';
my $pidfile = '/var/run/p0fudplog.pid';
openlog('p0fudplog','pid,nowait',$facility) or die("I'm alone!'");
loginf("Started");
sub loginf($) {
my ($m) = @_;
syslog('notice',$m);
print "$m\n" if (!$daemonize);
}
sub logerr($) {
my ($m) = @_;
syslog('err',$m);
print STDERR "$m\n" if (!$daemonize);
}
sub logdie($) {
my ($m) = @_;
logerr($m);
die;
}
if ($daemonize) {
Proc::Daemon::Init;
if ($pidfile && open(PF,">$pidfile")) {
print PF "$$\n";
close(PF);
}
my $u;
my $g;
if (defined($user)) {
if ($user=~/^\d+$/) { $u=$user; } else {
$u = getpwnam($user);
}
}
if (defined($group)) {
if ($group=~/^\d+$/) { $g=$group; } else {
$g = getgrnam($group);
}
}
if (defined($g)) {
$) = $g;
$( = $g;
}
if (defined($u)) {
$< = $u;
$> = $u;
}
loginf("Daemonized");
}
sysopen(LF,"$path/p0fudplog.lock",O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK,0600) or logdie("I'm too late!");
my $srv = IO::Socket::INET->new(LocalPort=>$port,Proto=>'udp') or logdie("I'm deaf!");
$srv->sockopt(SO_RCVTIMEO,5);
my $datagram;
my $flags = 0;
my %files = ();
my $cnt = 0;
my $cntt = 0;
my $last = time();
my $stop = 0;
my $now = 0;
sub killsig {
loginf("Received kill signal");
$stop = 1;
}
sub hupsig {
loginf("Received cycle signal");
$now = 1;
}
sub cyclefiles {
loginf("Renaming files with $cnt packets ($cntt total)");
my $ts = sprintf('%08X%08X%08X',time(),$cntt,$cnt);
foreach my $adr (keys %files) {
my $lf = $files{$adr}{'fh'};
close($lf);
#loginf("$adr: $files{$adr}{'pc'}");
}
foreach my $adr (keys %files) {
#loginf("Renaming $path"."tmp.$adr to $path$ts.$adr.log");
rename("$path$adr.p0ftmp","$path$adr.$ts.p0flog");
}
%files = ();
}
$SIG{STOP} = \&killsig;
$SIG{TSTP} = \&killsig;
$SIG{QUIT} = \&killsig;
$SIG{TERM} = \&killsig;
$SIG{INT} = \&killsig;
$SIG{HUP} = \&hupsig;
foreach my $aaa (keys %files) {
loginf("??? $aaa: $files{$aaa}{'pc'}");
}
my $lcnt = 0;
while (!$stop) {
my $rcv = 0;
eval {
local $SIG{ALRM} = sub { die; };
alarm 5;
$rcv = $srv->recv($datagram,1024,$flags);
alarm 0;
1;
};
if ($rcv) {
my $adr = $srv->peerhost;
if (!$files{$adr}{'pc'}) {
local *NF;
open (NF,">$path$adr.p0ftmp") or logdie("I'm blind!");
$files{$adr}{'fh'} = *NF;
$files{$adr}{'pc'} = 0;
}
my $lf = $files{$adr}{'fh'};
print $lf $datagram;
$files{$adr}{'pc'}++;
$cnt++;
$cntt++;
}
if (($now || time()-$last>$cycle) && !$stop) {
cyclefiles() if ($cnt);
$last=time();
$now = 0;
$cnt = 0;
}
$lcnt++;
}
cyclefiles() if ($cnt);
loginf("Finished with $cntt packets received.");
closelog();
unlink($pidfile) if ($daemonize && $pidfile);
close(LF);
#!/usr/local/bin/perl
# 2004-09-21 17:39
use strict;
use IPC::Signal qw(sig_num);
use Date::Parse;
use Date::Format;
#use Storable;
#use YAML qw(DumpFile LoadFile);
use Fcntl;
use DB_File;
use DWH_File qw(DB_File);
use File::Copy;
use Getopt::Mixed "nextOption";
#use GD::Graph::linespoints;
#use GD::Graph::bars;
use GD::Graph::hbars;
#use GD::Graph::area;
#use GD::Graph::pie;
my $varpath = '/var/p0f';
my $del = 1;
my $udplogpid = '/var/run/p0fudplog.pid';
my $useDWHFile = 1;
my $useworkdb = 1;
my %hosts = (
'*' => 'total',
'10.0.7.254' => 'Monty',
'10.0.37.254' => 'Satchel',
'10.0.42.254' => 'fw'
);
my $logpath = "$varpath";
my $graphpath = "$varpath/graph";
my $dbname = "$varpath/p0fstats.cnt.db";
my $iddbname = "$varpath/p0fstats.ids.db";
my $ip2osname = "$varpath/p0f.ip2os";
sub YesNo($) {
my ($b) = @_;
return 'Yes' if ($b);
return 'No';
}
my $quiet = 0;
my $nologs = 0;
my $nographs = 0;
my $donothing = 0;
my %onlyhosts = ();
my $verbose = 0;
sub ShowSettings {
print "Log path: $logpath\n".
"Graph path: $graphpath\n".
"Database: $dbname\n".
"IP->OS Database: $ip2osname\n".
"Delete logs and graphs: ".YesNo($del)."\n".
"p0f.udplog PID file: $udplogpid\n".
"Use DWH_File: ".YesNo($useDWHFile)."\n".
"Use temp DWH db: ".YesNo($useworkdb)."\n".
"Don't read logs: ".YesNo($nologs)."\n".
"Don't create graphs: ".YesNo($nographs)."\n".
"Don't do anything: ".YesNo($donothing)."\n";
my $hc = 0;
foreach my $hi (sort keys %hosts) {
if ($hc) {
print " ";
} else {
print "Host names: ";
$hc = 1;
}
print "$hi => $hosts{$hi}\n";
}
$hc = 0;
foreach my $hi (sort keys %onlyhosts) {
if ($hc) {
print " ";
} else {
print "Only read logs for: ";
$hc = 1;
}
print "$hi\n";
}
}
Getopt::Mixed::init('q v h=s no-logs no-graphs do-nothing show-settings help quiet>q host>h verbose>v');
while (my ($optn, $optv, $optp) = nextOption()) {
if ($optn eq 'h' || $optn eq 'host') {
$onlyhosts{$optv}=1;
} elsif ($optn eq 'q' || $optn eq 'quiet') {
$quiet = 1;
$verbose = 0;
} elsif ($optn eq 'no-logs') {
$nologs = 1;
} elsif ($optn eq 'no-graphs') {
$nographs = 1;
} elsif ($optn eq 'do-nothing') {
$donothing = 1;
} elsif ($optn eq 'v' || $optn eq 'verbose') {
$verbose = 1;
$quiet = 0;
} elsif ($optn eq 'show-settings') {
ShowSettings();
} elsif ($optn eq 'help') {
print "-h<ip> --host=<ip> : Only read logs from <ip>\n".
"-q --quiet : Don't show what's happening".
"--no-logs : Don't read logs\n".
"--no-graphs : Don't create graphs\n".
"--do-nothing : Don't do anything\n".
"--show-settings : Show current settings and options\n".
"--help : Show this text\n";
}
}
Getopt::Mixed::cleanup();
ShowSettings if ($verbose);
sub show($) {
my ($m) = @_;
print "$m\n" if (!$quiet);
}
sub vshow($) {
my ($m) = @_;
print "$m\n" if ($verbose);
}
if ($donothing) {
show("Not doing anything.");
exit 0;
}
my %ip2os = ();
my %iddb = ();
my %statshash = ();
my $stats = \%statshash;
my $databaseopen = 0;
sub database_name() {
return "$dbname.work" if ($useworkdb);
return $dbname;
}
sub open_database() {
if (!$databaseopen) {
copy($dbname,database_name()) if ($useworkdb);
tie(%statshash,'DWH_File',database_name(),O_RDWR|O_EXLOCK|O_CREAT,0644) or die "Could not open the database!";
$stats = \%statshash;
$databaseopen = 1;
}
return $databaseopen;
}
sub close_database() {
if ($databaseopen) {
untie %statshash;
move(database_name(),$dbname) if ($useworkdb);
$databaseopen = 0;
$stats = \%statshash;
return 1;
}
return 0;
}
sub save_database() {
if ($databaseopen) {
untie %statshash;
tie(%statshash,'DWH_File',database_name(),O_RDWR|O_CREAT,0644) or die "Could not open the database!";
$stats = \%statshash;
return 1;
}
return 0;
}
END {
if ($databaseopen) {
untie %statshash;
$databaseopen = 0;
}
}
if ($udplogpid && sig_num('HUP') && open(PF,"<$udplogpid")) {
my $pid = <PF>;
close(PF);
$pid =~ s/[\r\n]//g;
if ($pid =~ /^\d+$/ && $pid > 0) {
show("Cycling p0f.udplog...");
kill(sig_num('HUP'),$pid);
sleep(5);
}
}
tie(%iddb,'DB_File',$iddbname,O_RDWR|O_CREAT|O_EXLOCK,0644) or die ("id database error");
if ($useDWHFile) {
open_database();
} elsif ($dbname && -f $dbname) {
%statshash = LoadFile($dbname);
$stats = \%statshash;
#$stats = retrieve($dbname) or die("data in error");
}
$stats->{'@'}{'Created'} = time() if (!defined($stats->{'@'}{'Created'}));
my @files = ();
if (opendir(PD,$logpath)) {
while (my $f=readdir(PD)) {
push @files, $f if ($f =~ /^(\d+\.\d+\.\d+\.\d+)\.[A-Z0-9]+\.p0flog$/ &&
($onlyhosts{$1} || !%onlyhosts));
}
closedir(PD);
}
my $host = '-';
my $leID = '';
my $leT = 0;
my $leOS = '';
my $leOSVer = '';
my $leDist = 0;
my $leLink = '';
my $leSign = '';
sub incem($$$$) {
my ($ip,$year,$month,$cnt) = @_;
$stats->{$ip}{$cnt} = 0 if (!defined($stats->{$ip}{$cnt}));
$stats->{$ip}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{$cnt}));
$stats->{$ip}{'Year'}{$year}{$cnt} ++;
$stats->{$ip}{'Month'}{$month}{$cnt} = 0 if (!defined($stats->{$ip}{'Month'}{$month}{$cnt}));
$stats->{$ip}{'Month'}{$month}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Month'}{$month}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{'OS'}{$leOS}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'OS'}{$leOS}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'OS'}{$leOS}{$cnt} ++;
$stats->{$ip}{'Month'}{$month}{'OS'}{$leOS}{$cnt} = 0 if (!defined($stats->{$ip}{'Month'}{$month}{'OS'}{$leOS}{$cnt}));
$stats->{$ip}{'Month'}{$month}{'OS'}{$leOS}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'OS'}{$leOS}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'OS'}{$leOS}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'OS'}{$leOS}{$cnt} ++;
$stats->{$ip}{'OS'}{$leOS}{$cnt} = 0 if (!defined($stats->{$ip}{'OS'}{$leOS}{$cnt}));
$stats->{$ip}{'OS'}{$leOS}{$cnt} ++;
if ($leOSVer) {
$stats->{$ip}{'Year'}{$year}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} ++;
$stats->{$ip}{'Month'}{$month}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} = 0 if (!defined($stats->{$ip}{'Month'}{$month}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt}));
$stats->{$ip}{'Month'}{$month}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} ++;
$stats->{$ip}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} = 0 if (!defined($stats->{$ip}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt}));
$stats->{$ip}{'OS'}{$leOS}{'Ver'}{$leOSVer}{$cnt} ++;
}
if ($leDist) {
$stats->{$ip}{'Year'}{$year}{'Dist'}{$leDist}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Dist'}{$leDist}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Dist'}{$leDist}{$cnt} ++;
$stats->{$ip}{'Month'}{$month}{'Dist'}{$leDist}{$cnt} = 0 if (!defined($stats->{$ip}{'Month'}{$month}{'Dist'}{$leDist}{$cnt}));
$stats->{$ip}{'Month'}{$month}{'Dist'}{$leDist}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'Dist'}{$leDist}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'Dist'}{$leDist}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'Dist'}{$leDist}{$cnt} ++;
$stats->{$ip}{'Dist'}{$leDist}{$cnt} = 0 if (!defined($stats->{$ip}{'Dist'}{$leDist}{$cnt}));
$stats->{$ip}{'Dist'}{$leDist}{$cnt} ++;
}
if ($leLink) {
$stats->{$ip}{'Year'}{$year}{'Link'}{$leLink}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Link'}{$leLink}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Link'}{$leLink}{$cnt} ++;
$stats->{$ip}{'Month'}{$month}{'Link'}{$leLink}{$cnt} = 0 if (!defined($stats->{$ip}{'Month'}{$month}{'Link'}{$leLink}{$cnt}));
$stats->{$ip}{'Month'}{$month}{'Link'}{$leLink}{$cnt} ++;
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'Link'}{$leLink}{$cnt} = 0 if (!defined($stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'Link'}{$leLink}{$cnt}));
$stats->{$ip}{'Year'}{$year}{'Month'}{$month}{'Link'}{$leLink}{$cnt} ++;
$stats->{$ip}{'Link'}{$leLink}{$cnt} = 0 if (!defined($stats->{$ip}{'Link'}{$leLink}{$cnt}));
$stats->{$ip}{'Link'}{$leLink}{$cnt} ++;
}
}
my $ltcnt = 0;
if (@files && !$nologs) {
show("Reading logs...");
my $nrt = 0;
my $frt = 0;
foreach my $cf (sort @files) {
my $lccnt = 0;
open (LF,"$logpath/$cf") or die('Ischias!');
$host = $cf;
$host =~ s/^(\d+\.\d+\.\d+\.\d+).*$/$1/;
$hosts{$host} = $host if (!defined($hosts{$host}));
while (my $l=<LF>) {
$l =~ s/[\r\n]//g;
if ($l =~ /^\s+->.*\((.+)\)$/) {
my @ia = split(/,/,$1);
foreach my $i (@ia) {
$i =~ s/^\s+//;
$i =~ s/\s+$//;
if ($i =~ /^distance\s+(\d+)$/) {
$leDist = $1;
} elsif ($i =~ /^link:\s+(\S+)$/) {
$leLink = $1;
$leLink = 'unknown' if ($leLink =~ /^unknown/i);
}
}
} elsif ($l =~ /^\s+Signature\:\s+\[(.*)\].*$/) {
$leSign = $1;
} elsif ($l =~ /^<([^>]+)>\s+(\d+\.\d+\.\d+\.\d+)\:\d+\s+-\s+(\S+)\s*([^\(\[]*)(.*)/) {
if ($leOS) {
my $cnrt = str2time($leT);
$nrt = $cnrt if ($cnrt && $cnrt > $nrt);
$frt = $cnrt if ($cnrt && ($frt == 0 || $cnrt < $frt));
my ($ss,$mm,$hh,$dd,$month,$year,$zone) = strptime($leT);
$year += 1900;
incem($host,$year,$month,'t');
incem('*',$year,$month,'t');
$leID .= "|$leDist|$leLink|$leSign";
if (!defined($iddb{"$host|$leID"})) {
incem($host,$year,$month,'c');
$iddb{"$host|$leID"} = time();
}
if (!defined($iddb{"*|$leID"})) {
incem('*',$year,$month,'c');
$iddb{"*|$leID"} = time();
}
$ltcnt++;
$lccnt++;
$leID = '';
$leT = '';
$leOS = '';
$leOSVer = '';
$leDist = 0;
$leLink = '';
$leSign = '';
}
my $leIP = $2;
$leID = "$2|$5";
$leT = $1;
$leOS = $3;
$leOSVer = $4;
$leOS = lc($leOS) if ($leOS =~ /^UNKNOWN$/i);
$leOSVer = '' if ($leOS eq 'unknown');
$leID =~ s/\(up:[^\)]*\)//;
$leID =~ s/\s+$//;
$leID =~ s/\s+/ /g;
$leOSVer =~ s/^\s+//;
$leOSVer =~ s/\s+$//;
$leID .= "|$leOS|$leOSVer";
$leOSVer = '?' if ($leOS ne 'unknown' && $leOSVer eq '');
$ip2os{$leIP} = $leOS if ($leIP && $leOS);
}
}
close(LF);
show("$cf: $lccnt");
unlink("$logpath/$cf") if ($del);
}
$stats->{'@'}{'Newest'} = $nrt if ($nrt && (!defined($stats->{'@'}{'Newest'}) || $nrt > $stats->{'@'}{'Newest'}));
$stats->{'@'}{'Oldest'} = $frt if ($frt && (!defined($stats->{'@'}{'Oldest'}) || $frt < $stats->{'@'}{'Oldest'}));
show("$ltcnt entries imported") if ($ltcnt);
}
untie(%iddb);
if ($useDWHFile) {
save_database();
} elsif ($dbname && $ltcnt) {
DumpFile($dbname,%statshash);
#store($stats,$dbname) or die("data out error");
}
sub yticks($) {
my ($m) = @_;
$m = int($m+1) if ($m != int($m));
my $t = int(sprintf('%0.1g',$m / 10));
if ($t < 10 && $t > 5) {
$t = 10;
} elsif ($t < 5 && $t >2) {
$t = 5;
} elsif ($t == 0) {
$t = 1;
}
while ($m % $t > 0) {
$m++;
}
$t = $m/$t;
my $s = 1;
$s = 2 if ($t > 20);
return ($m,$t,$s);
}
sub graph_hbars {
my $ip = shift;
my $title = shift;
my $label = shift;
my $file = shift;
my $hash = shift;
my @data = ();
my $cnt = 0;
my $max = 0;
foreach my $key (@_) {
push @{$data[0]}, $key;
push @{$data[1]}, $hash->{$key}{'c'};
vshow("$key: ".$hash->{$key}{'c'});
$max = $hash->{$key}{'c'} if ($hash->{$key}{'c'} > $max);
$cnt ++;
}
vshow("cnt:$cnt, max:$max");
if ($cnt>0 && $max>0) {
my $graph = new GD::Graph::hbars(700,49+(16*($cnt+1)));
my ($ym,$yt,$ys) = yticks($max);
$graph->set(
title => "[$hosts{$ip}] $title",
y_label => 'Count',
x_label => $label,
y_max_value => $ym,
y_tick_number => $yt,
y_label_skip => $ys,
'3d' => 0,
bgclr => 'white',
fgclr => 'gray',
boxclr => 'lgray',
transparent => 0,
cycle_clrs => 1,
long_ticks => 1,
marker_size => 0,
line_width => 0,
correct_width => 0,
cumulate => 0,
overwrite => 0,
dclrs => [qw(lgreen lred lblue lyellow lpurple cyan lorange dred dgreen dblue dyellow dpurple)],
);
$file =~ s{([^-_A-Za-z0-9])}{'='.sprintf('%02X',ord($1))}eg;
$graph->plot(\@data);
if (open(IMAGE,">$graphpath/$hosts{$ip}_-_$file.png")) {
binmode(IMAGE);
print IMAGE $graph->gd->png;
close(IMAGE);
}
}
}
sub save_dates {
if (open(DF,">$graphpath/.data_interval")) {
print DF time2str('%Y-%m-%d %H:%M %z',$stats->{'@'}{'Oldest'})."\n";
print DF time2str('%Y-%m-%d %H:%M %z',$stats->{'@'}{'Newest'})."\n";
close(DF);
}
}
if (!$nographs) {
show("Creating graphs...");
if ($del && opendir(PD,$graphpath)) {
while (my $f=readdir(PD)) {
unlink("$graphpath/$f") if ($f =~ /^.+_-_.+\.png$/);
}
closedir(PD);
}
save_dates();
foreach my $ip (sort keys %{$stats}) {
next if ($ip eq '@');
graph_hbars($ip,"Operating System (Vendor) ($stats->{$ip}{'c'}/$stats->{$ip}{'t'})",
'Operating System (Vendor)','os',$stats->{$ip}{'OS'},
sort { $stats->{$ip}{'OS'}{$a}{'c'} <=> $stats->{$ip}{'OS'}{$b}{'c'} }
keys %{$stats->{$ip}{'OS'}});
graph_hbars($ip,"Distance ($stats->{$ip}{'c'}/$stats->{$ip}{'t'})",
'Distance','dist',$stats->{$ip}{'Dist'},
sort { $b <=> $a } keys %{$stats->{$ip}{'Dist'}});
graph_hbars($ip,"Link ($stats->{$ip}{'c'}/$stats->{$ip}{'t'})",
'Link','link',$stats->{$ip}{'Link'},
sort { $stats->{$ip}{'Link'}{$a}{'c'} <=> $stats->{$ip}{'Link'}{$b}{'c'} }
keys %{$stats->{$ip}{'Link'}});
foreach my $os (sort { $stats->{$ip}{'OS'}{$a}{'c'} <=> $stats->{$ip}{'OS'}{$b}{'c'} }
keys %{$stats->{$ip}{'OS'}}) {
graph_hbars($ip,"$os System/Version ($stats->{$ip}{'OS'}{$os}{'c'}/$stats->{$ip}{'OS'}{$os}{'t'})",
'System/Version',"osver_-_".sprintf('%08X',$stats->{$ip}{'OS'}{$os}{'c'})."_-_$os",
$stats->{$ip}{'OS'}{$os}{'Ver'},
sort { $stats->{$ip}{'OS'}{$os}{'Ver'}{$a}{'c'} <=> $stats->{$ip}{'OS'}{$os}{'Ver'}{$b}{'c'} }
keys %{$stats->{$ip}{'OS'}{$os}{'Ver'}});
}
}
}
close_database() if ($useDWHFile);
if ($ip2osname && $ltcnt) {
show("Updating ip->os database...");
my %ip2osdb = ();
tie(%ip2osdb,'DB_File',$ip2osname,O_RDWR|O_CREAT|O_EXLOCK,0644) or die ("ip2osname error");
foreach my $ip (keys %ip2os) {
$ip2osdb{$ip} = $ip2os{$ip}.'|'.time();
}
untie(%ip2osdb);
}
show("Finished");
package p0fIP2OS;
use strict;
use Fcntl;
use DB_File;
sub BEGIN {
use Exporter ();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
@EXPORT = qw(&ip2os);
@EXPORT_OK = qw();
%EXPORT_TAGS = ();
}
use vars @EXPORT, @EXPORT_OK;
use vars qw($varpath $ip2osname);
$varpath = '/var/p0f';
$ip2osname = "$varpath/p0f.ip2os";
sub ip2os($) {
my ($ip) = @_;
my $os = 'unknown';
my %ip2osdb = ();
if (tie(%ip2osdb,'DB_File',$ip2osname,O_RDONLY|O_SHLOCK)) {
$os = $ip2osdb{$ip} if (defined($ip2osdb{$ip}));
untie(%ip2osdb);
$os =~ s/\|.*$//;
}
return $os;
}
sub END {}
<?
$rdir = '/data/whatever/www/p0f';
$udir = 'p0f/';
$router = $_GET['router'];
if (preg_match('/^satchel$/i',$router)) {
$routern = 'Satchel';
} elseif (preg_match('/^monty$/i',$router)) {
$routern = 'Monty';
} elseif (preg_match('/^fw$/i',$router)) {
$routern = '?¿';
} else {
$router = 'total';
$routern = 'Monty, Satchel & ?¿';
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?
print "<title>p0f Fingerprint Stats: $routern</title>\n";
?>
</head>
<body>
<center>
<?
$tsf=fopen("$rdir/.data_interval",r);
if ($tsf) {
$dot = preg_replace('/ \+\d+$/','',fgets($tsf));
$dnt = preg_replace('/ \+\d+$/','',fgets($tsf));
fclose($tsf);
}
print "<h1>$routern p0f Fingerprint Stats</h1>\n";
print "<p>Last run at ";
include("$rdir/stamp");
if ($dot && $dnt) {
print "<br>Calculated on data from $dot to $dnt";
}
print "</p>\n";
?>
<?
$dh=opendir("$rdir");
while ($fn = readdir($dh)) {
if (preg_match("/^$router".'_-_.*\.png$/i',$fn)) {
if (preg_match('/_-_osver_-_[0-9A-Z]+_-_/',$fn)) {
$vfiles[] = $fn;
} else {
$files[] = $fn;
}
}
}
closedir($dh);
sort($files,SORT_STRING);
rsort($vfiles,SORT_STRING);
function hexchr($hex) {
$c = '';
$i = 0;
foreach($hex as $p) {
if ($i>0) {
$c.=chr(hexdec($p));
}
$i++;
}
return "$c";
}
print "<p>\n";
foreach($files as $fn) {
$fd = preg_replace_callback('/=([0-9A-Z][0-9A-Z])/',hexchr,$fn);
$fd = preg_replace('/\.png$/',"",$fd,1);
$fd = preg_replace('/^[a-zA-Z0-9]+_-_/','',$fd,1);
$fd = preg_replace('/^dist$/','Distance',$fd,1);
$fd = preg_replace('/^link$/','Link Type',$fd,1);
$fd = preg_replace('/^os$/','System/Vendor',$fd,1);
print "[<a href=\"#$fn\">$fd</a>]\n";
}
print "[<a href=\"#osver\">Systems/Versions</a>]\n";
print "<br>\n";
foreach($vfiles as $fn) {
$fd = preg_replace_callback('/=([0-9A-Z][0-9A-Z])/',hexchr,$fn);
$fd = preg_replace('/\.png$/',"",$fd,1);
$fd = preg_replace('/^[a-zA-Z0-9]+_-_/','',$fd,1);
$fd = preg_replace('/^osver_-_[0-9A-Z]+_-_/','',$fd,1);
print "[<a href=\"#$fn\">$fd</a>]\n";
}
print "</p>\n";
print "<hr>\n";
print "<p>\n";
$of=0;
foreach($files as $fn) {
if ($of) {
print "<hr width=750>\n";
}
print "<a name=\"$fn\"></a><img src=\"$udir$fn\">\n";
$of=1;
}
print "<hr>\n";
print "<a name=\"osver\"></a>\n";
print "<h2>Systems/Versions</h2>\n";
foreach($vfiles as $fn) {
print "<hr width=750>\n";
print "<a name=\"$fn\"></a><img src=\"$udir$fn\">\n";
$of=1;
}
print "</p>";
?>
<hr>
<p>This stuff was created with
<a href="p0f-stats.shtml">a couple of perl scripts</a> and
<a href="http://lcamtuf.coredump.cx/p0f.shtml">p0f 2</a>.
</center>
</body>
</html>
(2006-06-01)