#!/usr/local/bin/perl #********************************************************************** # # Copyright 2002-2003 by Carnegie Mellon University # # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that copyright notice and this permission notice appear in # supporting documentation, and that the name of CMU not be # used in advertising or publicity pertaining to distribution of the # software without specific, written prior permission. # # CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL # CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. # # Originally written by Chas DiFatta # Modifications by Kevin Miller, Kunal Trivedi # #********************************************************************* my ($file, $data, $ipAddr, $inPkts, $outPkts, $inBytes, $outBytes, $flows, $services); my ($srv, $srv1, $srv2, $srv3); my ($ipName, $num_files, $hosts, $dports, $tdports, $srvTotal, $service_name, $ismac); my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks); my ($inBytesTotal, $outBytesTotal, $inPktsTotal, $outPktsTotal, $flowsTotal, %ip_mac,%hn_mac); my $input_file_limit; my $stop_alarm = 0; $| = 1; # topt version number my $version = "0.61"; # location of data files my $ARCHIVEDIR = "/home/argus/archive"; # location of filters for -f option my $FILTERS = '/afs/andrew/data/db/net/argus/topt.filters'; # location of bootptab for -a and -m options my $BOOTPTAB = '/afs/andrew/data/db/net/bootp/bootptab-dynamic'; # location of ra command my $racmd = "/home/argus/bin/ra "; #location of ra's localized rarc scripts my $raargs = "-n -F /home/argus/lib/rarc.scripts "; # default number of lines to display use constant DEFAULT_LINES => 20; # the data files common denominator is 300 sec worth of data use constant TOPT_FILE_PERIOD => 300; # file prefixes for each sampling point my @FILE_PREFIXES = qw/egress.I1 argus-wireless/; # default number of Argus records to read use constant INPUT_FILE_LIMIT => 10000; use constant INBYTES => 0; use constant OUTBYTES => 1; use constant INPACKETS => 2; use constant OUTPACKETS => 3; use constant FLOWS => 4; use constant SERVICES => 5; use Socket; use Getopt::Std; my @files; do $FILTERS; # Grrr. Dumb getopt module.. my @ARG_SAVE = @ARGV; if (!getopts('aCf:hi:l:LmnpqQs:St:T:')) { # See if they specified -f alone if (grep(/^-f$/, @ARG_SAVE)) { print_filters: print join("\n", "Filters defined:", map { "\t$_" } keys %Filters); print "\n"; exit 1; } usage(); } sub data_filter { return 1; } sub mac_filter { if ($ismac ne '') { return ($_[0] =~ /0:2:7e:21:8f:a0/); } return 0; } if ($opt_f ne '') { $opt_f = lc($opt_f); if (!defined $Filters{$opt_f}) { print "Unknown filter specified.\n"; goto print_filters; exit 2; } eval $Filters{$opt_f}; } if ($opt_l =~ /^$/) { $opt_l = DEFAULT_LINES; } elsif ($opt_l =~ /^0$/) { $opt_l = -1; } if ($opt_m) { $ismac = '-s mac'; } else { $ismac = ''; } unless (defined $opt_i) { $opt_i = INPUT_FILE_LIMIT; } elsif ($opt_i eq '0') { $opt_i = -1; } if (!$opt_t) { $opt_t = TOPT_FILE_PERIOD; } if ($opt_h) { usage(); } if ($opt_T) { $SIG{ALRM} = "alarm_handler"; alarm $opt_T; } if (@ARGV) { while ($ARGV[0] !~ /^-/ && @ARGV) { @files = (@files, $ARGV[0]); shift; } } else { my $f = getfilename(); @files = ($f); } $num_files = $#files + 1; #---------- main ----------# if ($opt_a) { read_argus_files(); write_topt_file(); } else { read_topt_files(); if (!$opt_Q) { write_top_talkers(); } if ($opt_S) { write_summary(); } } exit; #-------end of main--------# sub read_topt_files() { while ((@files) && !$stop_alarm) { $input_file_limit = $opt_i; my $file = pop(@files); open(FILE, $file) || warn("could not open $file, $!"); while (($data = ) && $input_file_limit-- && !$stop_alarm) { # skip comments and metavars if ($data =~ /^\s*\%5minSlices\%\s+(\d+)/) { $num_files += $1 - 1; } next if ($data =~ /^\s*\#/ || $data =~ /^\s*\%/); chomp($data); ($ipAddr, $outBytes, $inBytes, $outPkts, $inPkts, $flows, $services) = split("\t", $data); next unless (data_filter($ipAddr)); next if (mac_filter($ipAddr)); $hosts{$ipAddr}[INBYTES] += $inBytes; $hosts{$ipAddr}[OUTBYTES] += $outBytes; $hosts{$ipAddr}[OUTPACKETS] += $outPkts; $hosts{$ipAddr}[INPACKETS] += $inPkts; $hosts{$ipAddr}[FLOWS] += $flows; %dports = map { split /,/ } $services; if ($opt_S) { $inBytesTotal += $inBytes; $outBytesTotal += $outBytes; $inPktsTotal += $inPkts; $outPktsTotal += $outPkts; $flowsTotal += $flows; %tdports = map { split /,/ } $services; } foreach $target (sort keys (%dports)) { ($service_name, $count) = split( /\(|\)/, $target); $hosts{$ipAddr}[SERVICES]{$service_name} += $count; if ($opt_S) { $srvTotal{$service_name} += $count; } } } close(FILE); } print "\n"; } sub write_top_talkers() { if (! $opt_q) { if ($opt_p) { $opt_n = 1; printf("Hostname iMB oMB iKp oKp flows iMbs oMbs ips ops IP address \n"); } else { if ($opt_m) { printf("MAC iMB oMB iKp oKp flows iMbs oMbs ips ops Hostname\n"); } else { printf("ipAddr iMB oMB iKp oKp flows iMbs oMbs ips ops services(num flows)\n"); } } } if ($opt_m) { loadBootptab(); } foreach $target ( sort opt_s_args keys(%hosts)) { if ($opt_l--) { if ($opt_n) { if (($ipName = getaddr($target))) { printf("%-16.16s", $ipName); } else { printf("%-16s", $target); } } else { printf("%-16s", $target); } printf(" %5.0f %6.0f %6.0f %6.0f %8d %5.1f %5.1f %5d %5d ", $hosts{$target}[INBYTES]/1000000, $hosts{$target}[OUTBYTES]/1000000, $hosts{$target}[INPACKETS]/1000, $hosts{$target}[OUTPACKETS]/1000, $hosts{$target}[FLOWS], (($hosts{$target}[INBYTES]/($opt_t * $num_files))/1000000) * 8, (($hosts{$target}[OUTBYTES]/($opt_t * $num_files))/1000000) * 8, $hosts{$target}[INPACKETS]/($opt_t * $num_files), $hosts{$target}[OUTPACKETS]/($opt_t * $num_files)); if ($opt_p || $opt_m) { if($opt_p) { printf("%-16.16s\n", $target); } elsif($opt_m) { $target =~ s/\s+//g; my $mhn = $hn_mac{uc($target)}; printf("%-32.32s\n",$mhn); } } else { if (defined %{$hosts{$target}[SERVICES]}) { if ($opt_L) { printf("%s\n", join(",", map { "$_($hosts{$target}[SERVICES]{$_})" } sort { $hosts{$target}[SERVICES]{$b} <=> $hosts{$target}[SERVICES]{$a} } keys %{$hosts{$target}[SERVICES]})); } else { $srv0 = join(",", map { "$_($hosts{$target}[SERVICES]{$_})" } sort { $hosts{$target}[SERVICES]{$b} <=> $hosts{$target}[SERVICES]{$a} } keys %{$hosts{$target}[SERVICES]}); ($srv1, $srv2, $srv3, $srv4) = split(",", $srv0); if ($srv1 =~ /^-/ || $srv2 =~ /^$/) { $srv = "-"; } else { if ($srv2 =~ /^-/ || $srv3 =~ /^$/) { $srv = $srv1; } elsif ($srv3 =~ /^-/ || $srv4 =~ /^$/) { $srv = join(",", $srv1, $srv2); } elsif ($srv4 =~ /^-/) { $srv = join(",", $srv1, $srv2, $srv3); } else { $srv = join(",", $srv1, $srv2, $srv3, "+"); } } printf("%s\n", $srv); } } else { print "-\n" ; } } } else { last; } } } sub write_summary { if (! $opt_q) { print " iMB oMB iKp oKp flows iMbs oMbs ips ops services(%total flows)\n"; } printf("%7d %7d %7d %7d %8d %5.1f %5.1f %5d %5d ", $inBytesTotal/1000000, $outBytesTotal/1000000, $inPktsTotal/1000, $outPktsTotal/1000, $flowsTotal, (($inBytesTotal/($opt_t * $num_files))/1000000) * 8, (($outBytesTotal/($opt_t * $num_files))/1000000) * 8, $inPktsTotal/($opt_t * $num_files), $outPktsTotal/($opt_t * $num_files)); $srv0 = join(",", map { "$_($srvTotal{$_})" } sort { $srvTotal{$b} <=> $srvTotal{$a} } keys %srvTotal); my @TOPSRV = split(",", $srv0); for my $i (0..9) { my ($srvX_n, $srvX_c) = split( /\(|\)/, $TOPSRV[$i]); printf("%s(%2.lf%),", $srvX_n, $srvX_c/$flowsTotal*100); } printf("+\n"); } sub getaddr { my ($name, $aliases, $addrtype, $length, $addrs); ($name, $aliases, $addrtype, $length, $addrs) = gethostbyaddr(inet_aton($_[0]), AF_INET); return($name); } sub usage() { print "Topt Version ", $version, "\n"; print "Usage: topt [options] [file(s)]\n"; print "options: -a convert Argus to single Topt file.\n"; print " -f apply filter (-f alone lists filters)\n"; print " -h print help.\n"; print " -i read summary records, default is 10k, 0 is no limit.\n"; print " -l print of summary output, 0 is no limit.\n"; print " -L print all services for each host.\n"; print " -n resolve IP addrs to names.\n"; print " -m with -a, store MAC instead of IP; else lookup hostname for MAC\n"; print " -p remove services; add IPs (implies -n)\n"; print " -q do not print legend.\n"; print " -Q do not print any records, use with -S.\n"; print " -s <[i|o]b|f|p> sort by total (or in/out) bytes, flows, or packets, default is bytes.\n"; print " -S provide summary.\n"; print " -t use for time period of topt file, default is 300.\n"; print " -T abort reading file(s) after then continue processing\n"; exit; } sub getfilename() { my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst); my ($filename); foreach my $FILE_PREFIX (@FILE_PREFIXES) { ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); $year = $year + 1900; $mon++; if ($mon <= 9) { $mon = "0$mon"; } if ($mday <= 9) { $mday = "0$mday"; } if ($hour <= 9) { $hour = "0$hour"; } if ($min <= 9) { $min = "0$min"; } for ($min) { /^0/ and do { if ($min < 5) { $min = "00"; } else { $min = "05"; } last; }; /^1/ and do { if ($min < 15) { $min = "10"; } else { $min = "15"; } last; }; /^2/ and do { if ($min < 25) { $min = "20"; } else { $min = "25"; } last; }; /^3/ and do { if ($min < 35) { $min = "30"; } else { $min = "35"; } last; }; /^4/ and do { if ($min < 45) { $min = "40"; } else { $min = "45"; } last; }; /^5/ and do { if ($min < 55) { $min = "50"; } else { $min = "55"; } last; }; } $filename = "$ARCHIVEDIR/$year/$mon/$mday/$hour/".$FILE_PREFIX.".$year.$mon.$mday.$hour.$min.toptalkers"; ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat $filename; if (-e $filename && $size) { return $filename; } else { for ($min) { /^0/ and do { if ($min < 5) { if ($hour == 0) { if ($mday == "01") { if ($mon == "01") { $year--; $mon = "12"; $mday = "31"; } else { $mon--; if ($mon <= 9) { $mon = "0$mon"; } if ($mon =~ /01/|/03/|/05/|/07/|/08/|/10/|/12/) { $mday = "31"; } elsif ($mon =~ /02/) { if (($year - 2000) % 4) { $mday = "28"; } else { $mday = "29"; } } else { $mday = "30"; } } } else { $mday--; if ($mday <= 9) { $mday = "0$mday"; } } $hour = "23"; } else { $hour--; if ($hour <= 9) { $hour = "0$hour"; } } $min = "55"; } else { $min = "00"; } last; }; /^1/ and do { if ($min < 15) { $min = "05"; } else { $min = "10"; } last; }; /^2/ and do { if ($min < 25) { $min = "15"; } else { $min = "20"; } last; }; /^3/ and do { if ($min < 35) { $min = "25"; } else { $min = "30"; } last; }; /^4/ and do { if ($min < 45) { $min = "35"; } else { $min = "40"; } last; }; /^5/ and do { if ($min < 55) { $min = "45"; } else { $min = "50"; } last; }; } $filename = "$ARCHIVEDIR/$year/$mon/$mday/$hour/".$FILE_PREFIX.".$year.$mon.$mday.$hour.$min.toptalkers"; ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat $filename; if (-e $filename && $size) { return $filename; } } } die "Cannot find topt file to open"; } sub read_argus_files() { my ($data, $srcPkts, $dstPkts, $srcBytes, $dstBytes, $srcPort, $dstPort,$smac,$dmac ); my ($file, $cmd, $raformat); my $IP_regex = qr/^(\d{1,3}\.){3}\d{1,3}$/; my $MAC_regex = qr/^(\w{1,2}:){5}\w{1,2}$/; while ((@files) && !$stop_alarm) { $file = pop(@files); if ($ismac ne '') { $raformat = "$ismac -s bytes -s spkts -s dpkts -s dport -r $file - "; } else { $raformat = "-s saddr -s daddr -s bytes -s spkts -s dpkts -s dport -r $file - "; } $cmd = $racmd.$raargs.$raformat; open(CMDOUT, "$cmd|") || warn("could not open $cmd, $!"); while (($data = ) && !$stop_alarm) { chomp($data); ($srcAddr, $dstAddr, $srcBytes, $dstBytes, $srcPkts, $dstPkts, $dstPort) = split(" ", $data); if ($ismac eq '') { next unless ($srcAddr =~ /$IP_regex/ && $dstAddr =~ /$IP_regex/ ); } else { next unless($srcAddr =~ /$MAC_regex/ && $dstAddr =~ /$MAC_regex/); } $hosts{$srcAddr}[INBYTES] += $dstBytes; $hosts{$srcAddr}[OUTBYTES] += $srcBytes; $hosts{$dstAddr}[INBYTES] += $srcBytes; $hosts{$dstAddr}[OUTBYTES] += $dstBytes; $hosts{$srcAddr}[INPACKETS] += $dstPkts; $hosts{$srcAddr}[OUTPACKETS] += $srcPkts; $hosts{$dstAddr}[INPACKETS] += $srcPkts; $hosts{$dstAddr}[OUTPACKETS] += $dstPkts; $hosts{$srcAddr}[FLOWS]++; $hosts{$dstAddr}[FLOWS]++; if (defined $dstPort && ( $dstPort =~ /^(\d+\.?\d*|\.\d+)$/ || $dstPort =~ /^([a-zA-Z]+\.?[a-zA-Z]*|\.[a-zA-Z]+)$/) && $ismac eq '' ) { $hosts{$dstAddr}[SERVICES]{$dstPort}++; } } close(CMDOUT); } } sub write_topt_file () { if ($ismac ne '') { print '%Type-MAC% 1',"\n"; } foreach $target ( sort { ($hosts{$b}[INBYTES] + $hosts{$b}[OUTBYTES] ) <=> ($hosts{$a}[INBYTES] + $hosts{$a}[OUTBYTES]) } keys(%hosts)) { printf("%-15s\t%d\t%d\t%d\t%d\t%d\t", $target, $hosts{$target}[OUTBYTES], $hosts{$target}[INBYTES], $hosts{$target}[OUTPACKETS], $hosts{$target}[INPACKETS], $hosts{$target}[FLOWS]); if (defined %{$hosts{$target}[SERVICES]}) { print join(",", map { "$_($hosts{$target}[SERVICES]{$_})" } sort { $hosts{$target}[SERVICES]{$b} <=> $hosts{$target}[SERVICES]{$a} } keys %{$hosts{$target}[SERVICES]}) ; } else { print "-" ; } print "\n"; } } sub opt_s_args () { if ($opt_s =~ /^b$/) { $hosts{$b}[INBYTES] + $hosts{$b}[OUTBYTES] <=> $hosts{$a}[INBYTES] + $hosts{$a}[OUTBYTES]; } elsif ($opt_s =~ /^ob$/) { $hosts{$b}[OUTBYTES] <=> $hosts{$a}[OUTBYTES]; } elsif ($opt_s =~ /^ib$/) { $hosts{$b}[INBYTES] <=> $hosts{$a}[INBYTES]; } elsif ($opt_s =~ /^p$/) { ($hosts{$b}[INPACKETS] + $hosts{$b}[OUTPACKETS] ) <=> ($hosts{$a}[INPACKETS] + $hosts{$a}[OUTPACKETS]); } elsif ($opt_s =~ /^f$/) { ($hosts{$b}[FLOWS] ) <=> ($hosts{$a}[FLOWS]); } else { $hosts{$b}[INBYTES] + $hosts{$b}[OUTBYTES] <=> $hosts{$a}[INBYTES] + $hosts{$a}[OUTBYTES]; } } sub alarm_handler { $stop_alarm = 1; } sub long2dot { return join('.', unpack('C4', pack('N', $_[0]))); } sub dot2long { return unpack('N', pack('C4', split(/\./, $_[0]))); } sub loadBootptab { my $hn = 1; unless(open(B_READ,$BOOTPTAB)) { print STDERR "Can't open $BOOTPTAB $!\n"; $hn = 0; } while() { my @line = split(/\:/,$_); my @maddr = split(/\=/,$line[2]); my @macChar = split(//,$maddr[1]); my $cmac = ""; for(my $i=0;$i<12;$i++) { next if($i%2 == 0 && $macChar[$i] eq '0'); $cmac .= $macChar[$i]; $cmac .= ":" if($i%2 != 0 && $i != 11); } $hn_mac{$cmac} = $line[0]; } close(B_READ); }