#!/usr/bin/perl -w
# -*- perl -*-

#------------------------------------------------------------------------
# Interactive Animated Traffic Analysis (INANNA) software, Version 0.
#
# Written by Mark Meiss (mmeiss@indiana.edu).
# Copyright 1999, 2000 by the Trustees of Indiana University.
# All Rights Reserved.  See LICENSE for details.
#------------------------------------------------------------------------

use SNMP;

# present usage information if necessary
if (@ARGV != 3) {
    print STDERR << "END";
Usage: $0 [in error sources] [out error sources] [name of file]

Both the "in error sources" and "out error sources" consist of an IPv4
address, followed by a slash, followed by the community name, followed
by another slash, followed by 'in' or 'out'.  (This final parameter
selects between polling input information and output information.)
Additional sources are separated with asterisks.  The information
for the last polling period is kept in "name of file".

Example: $0 10.0.101.254/public/in*10.0.101.253/public/in 10.0.101.254/public/out foo-error
END
    exit;
}

#------------------------------------------------------------------------
# file construction and setup
#------------------------------------------------------------------------

# set the base directory name
$base = '/home/traffic/wan/error-data';

# construct an initial file if necessary
unless (-r "$base/$ARGV[2]") {
    open ERROR, "> $base/$ARGV[2]";
    for ($i = 0; $i < 10; ++$i) {
	print ERROR "-2 -2 -2 -2 0 [just initialized]\n";
    }
    close ERROR;
}

# open the error file for future reading
open ERROR, "$base/$ARGV[2]";

# open the next-generation error file for writing
open NEXT, "> /tmp/error-collector.$$";


#------------------------------------------------------------------------
# process the sources
#------------------------------------------------------------------------

# first input, then output
foreach $i (0, 1) {

    # reinitialize our counters
    $badPackets = $goodPackets = 0;
    $status = 'good';

    # for each source
    foreach $source (split /\*/, $ARGV[$i]) {

	# skip it if it's 'x'
	if ($source eq 'x') {
	    $srcAddress[$i] = '';
	    $newUptime[$i]  = '0';
	    $newName[$i]    = '[N/A]';
	    next;
	}

	# get our parameters
	($srcAddress[$i], $srcCommunity, $srcDirection) = split /\//, $source;

	# split the address on - if necessary
	if ($srcAddress[$i] =~ /-/o) {
	    ($interfaceAddress, $routerAddress) = split /-/o, $srcAddress[$i];
	} else {
	    $interfaceAddress = $routerAddress = $srcAddress[$i];
	}

	# set up an SNMP session
	$session = new SNMP::Session(DestHost => $routerAddress, Community => $srcCommunity);

	# get the interface number for the given address
	($ifNumber) = $session->get(['ipAdEntIfIndex', $interfaceAddress]);

	# create our variables depending on direction
	if ($srcDirection eq 'in') {
	    $vars = new SNMP::VarList(['ifInUcastPkts',   $ifNumber],
				      ['ifInNUcastPkts',  $ifNumber],
				      ['ifInErrors',      $ifNumber],
				      ['ifInDiscards',    $ifNumber],
				      ['sysUptime',       0],
				      ['sysName',         0]);
	} else {
	    $vars = new SNMP::VarList(['ifOutUcastPkts',  $ifNumber],
				      ['ifOutNUcastPkts', $ifNumber],
				      ['ifOutErrors',     $ifNumber],
				      ['ifOutDiscards',   $ifNumber],
				      ['sysUptime',       0],
				      ['sysName',         0]);
	}

	# query the variables
	($newUcastPkts, $newNUcastPkts, $newErrors, $newDiscards, $newUptime[$i], $newName[$i]) = $session->get($vars);

	# if the values aren't defined, define them and mark an error condition
	$newNUcastPkts = 0 unless defined $newNUcastPkts;
	if ((not defined $newUcastPkts) || ($newUcastPkts eq '')) {
	    $newUcastPkts  = -1;
	    $newNUcastPkts = -1;
	    $newErrors     = -1;
	    $newDiscards   = -1;
	    $newUptime[$i] = 0;
	    $newName[$i]   = '[link down]';
	    $status = 'error';
	}

	# save the new values
	print NEXT "$newUcastPkts $newNUcastPkts $newErrors $newDiscards $newUptime[$i] $newName[$i]\n";

	# retrieve the file values
	chomp($line = <ERROR>);
	($oldUcastPkts, $oldNUcastPkts, $oldErrors, $oldDiscards, $oldUptime) = split /\s+/, $line;

	# if the file values are dummies or we had a reboot, mark a spoof condition
	$status = 'spoof' if ($oldUcastPkts == -2) || ($newUptime[$i] < $oldUptime);

	# tally the values
	$goodPackets += ($newUcastPkts  >= $oldUcastPkts)
	    ? ($newUcastPkts  - $oldUcastPkts)  : (2**32 - ($oldUcastPkts  - $newUcastPkts));
	$goodPackets += ($newNUcastPkts >= $oldNUcastPkts)
	    ? ($newNUcastPkts - $oldNUcastPkts) : (2**32 - ($oldNUcastPkts - $newNUcastPkts));
	$badPackets  += ($newErrors     >= $oldErrors) 
            ? ($newErrors     - $oldErrors)     : (2**32 - ($oldErrors     - $newErrors));
	$badPackets  += ($newDiscards   >= $oldDiscards)
            ? ($newDiscards   - $oldDiscards)   : (2**32 - ($oldDiscards   - $newDiscards));
    }

    # report based on status
    if ($status eq 'spoof') {
	print "0\n";
    } elsif ($status eq 'error') {
	print "100000\n";  # 100% * 1000
    } else {
	if (($badPackets + $goodPackets) > 0) {
	    print int(100000 * ($badPackets / ($badPackets + $goodPackets)) + 0.5), "\n";
	} else {
	    print "0\n";
	}
    }
}


#------------------------------------------------------------------------
# report system name and uptime data
#------------------------------------------------------------------------

if ($srcAddress[0] eq $srcAddress[1]) {
    printf "%.1f days\n", $newUptime[0] / (100 * 3600 * 24);
    print "$newName[0]\n";
} else {
    printf "%.1f and %.1f days\n", $newUptime[0] / (100 * 3600 * 24), $newUptime[1] / (100 * 3600 * 24);
    print "$newName[0] and $newName[1]\n";
}


#------------------------------------------------------------------------
# clean up the file stuff
#------------------------------------------------------------------------

# close the "next" file
close NEXT;

# move it into position
system "mv /tmp/error-collector.$$ $base/$ARGV[2]";

# we're done
exit;


#------------------------------------------------------------------------
# $Log$
