#!/usr/bin/perl # This is GPL'ed code, orginally written by Lordy # Web: http://www.lordy.de # eMail: nitpicker@lordy.de # Version: 0.5 # Date: 20040920 use strict; use warnings; use Getopt::Long; use Net::Netmask; use File::Binary; use File::Find; use Time::Local; use XML::Smart; my $xml = XML::Smart->new(); my $fh; my $block; my ($header,$firstflow,$lastflow,$tbytes,$tflows,$tpkts); my ($proto,$soffset,$eoffset,$sdpkts,$sdbytes,$dspkts,$dsbytes,$sip,$dip,$sport,$dport); my ($stime,$etime); my (@flowfiles,$file); my ($dummy, $x); my %input; my %output; my %inpkts; my %outpkts; my %iplist; my %protoname = ('1' => 'ICMP', '6' => 'TCP', '17' => 'UDP', '50' => 'ESP'); my $timestamp; my ($currentfile,$totalfiles); my $timer; #### # Get the command line options #### my ($start,$end,$host,$net,$flowdir,$verbose,$debug); GetOptions ('start=s' => \$start, 'end=s' => \$end, 'host=s' => \$host, 'net=s' => \$net, 'verbose' => \$verbose, 'debug' => \$debug, 'flowdir=s' => \$flowdir); #### # start + end must be defined #### if (not($end)) { $end = timelocal(localtime); } if (not($start)) { $start = $end - 86400; } #### # convert start time to epoch if necessary #### $start = &time2epoch($start); $end = &time2epoch($end); #### # either --host or --net must be defined #### if ( not ($host xor $net) ) { &usage; exit(1); } #### # check $host for sanity #### if ($host and (not($host =~ /\d+\.\d+\.\d+\.\d+/)) ) { print "$host not a valid IP !\n"; exit(1); } #### # check $net for sanity #### if ($net and (not($net =~ /\d+\.\d+\.\d+\.\d+\/\d+/)) ) { print "$net not a valid network !\n"; exit(1); } #### # check $flowdir and chdir() #### if ($flowdir) { chdir($flowdir) || die "Cannot chdir() to $flowdir !\n"; } #### # create network block object #### if ($net) { $block = Net::Netmask->new($net); } #### # 3,2,1,ACTION #### find(\&list_files, $flowdir); foreach $file (@flowfiles) { if ((-f $file) && (-r $file)) { if (&is_flowfile($file)) { &verbose("Now processing $file\n"); $fh = File::Binary->new($file); $header = $fh->get_bytes(6); $firstflow = $fh->get_ui32(); $lastflow = $fh->get_ui32(); $dummy = $fh->get_bytes(8); $tflows = $fh->get_ui32(); $tpkts = $fh->get_ui32(); $dummy = $fh->get_bytes(226); # ignore files that start after end or end before start... clear ? :-) if (($firstflow > $end) || ($lastflow < $start)) { $tflows = 0; } $x = 0; while ($x < $tflows) { $proto = ord($fh->get_bytes(1)); $soffset = $fh->get_ui16(); $eoffset = $fh->get_ui16(); $sdpkts = $fh->get_ui32(); $sdbytes = $fh->get_ui32(); $dspkts = $fh->get_ui32(); $dsbytes = $fh->get_ui32(); $sip = &ui32_ip($fh->get_ui32()); $dip = &ui32_ip($fh->get_ui32()); $sport = $fh->get_ui16(); $dport = $fh->get_ui16(); if ($proto == 1) { ($sport,$dport) = '0'; }; $stime = $firstflow + $soffset; $etime = $firstflow + $eoffset; $x++; #### # don't count internal traffic #### if ($net) { if ($block->match($sip) and $block->match($dip)) { ($sdbytes, $dsbytes, $sdpkts, $dspkts) = 0; } } if ($stime >= $start) { # <-- this is faster then doing AND (~5%) if ($etime <= $end) { &debug("Flow: $stime\:$etime $protoname{$proto} $sip\:$sport -> $dip\:$dport $sdbytes $dsbytes\n"); $output{$sip} += $sdbytes; $input{$sip} += $dsbytes; $output{$dip} += $dsbytes; $input{$dip} += $sdbytes; $outpkts{$sip} += $sdpkts; $inpkts{$sip} += $dspkts; $outpkts{$dip} += $dspkts; $inpkts{$dip} += $sdpkts; $iplist{$sip}++; $iplist{$dip}++; } } } # show progress $currentfile++; if ($verbose) { #&show_progress(); } } else { print STDERR "$file is _not_ a Nitpicker Flowfile !\n"; } $fh->close(); } } if ($host) { $xml = $xml->{report}; $start = { timestamp => $start }; push (@{$xml->{start}}, $start); $end = { timestamp => $end }; push (@{$xml->{end}}, $end); $dummy = { ipaddress => $host, input => $input{$host}, output => $output{$host}, inpkts => $inpkts{$host}, outpkts => $outpkts{$host} }; push (@{$xml->{host}}, $dummy); } print $xml->data; exit; sub usage() { print "$0 --start