#!/usr/local/bin/perl5 -- -*-perl-*-
##################################################################
# Multi Router Traffic Grapher
##################################################################
# Created by Tobias Oetiker <oetiker@dmu.ac.uk>
# Distributed under the GNU copyleft
#
# $Id: mrtg,v 1.24 1995/05/11 13:09:04 oetiker Exp $
# $Log: mrtg,v $
# Revision 1.24  1995/05/11  13:09:04  oetiker
# added refresh tag so that the document reloads after 5 minutes
#
# Revision 1.23  1995/04/28  13:35:53  oetiker
# Fixed config problem
#
# Revision 1.22  1995/04/28  12:42:32  oetiker
# Made Pulicly Presentable
# ,
#
#
###################################################################

# Colors for the graphics

$c_blank = "\xbf\xbf\xbf";
$c_major = "\xff\x00\x00";
$c_in =  "\x00\x00\xff";
$c_out = "\x00\xff\x00";
$c_grid ="\x00\x00\x00";



&readcfg;
&cfgcheck;

$ENV{'MIBFILE'} = $cfg{"mibfile"};
chdir ($cfg{"workdir"});

setpriority (0,0,10); # set nice 10

foreach $router (keys %routers) {

  &getcurrent;
  &readlast;
  &addoldtraffic;
  &writegraphics;
  &writehtml;
  &savetraffic;
  &savenew;

}

exit(0);

sub get {
    local(@output,$out,$in);
    @output= split("\n",
    `$cfg{'snmpget'} -v 1 $rcfg{'ip'}{$router} public $rcfg{'ifout'}{$router} $rcfg{'ifin'}{$router}`);
    if (! $? == 256) { 
	die "$cfg{'snmpget'} not successfull\n$output[0]\n$output[1]\n"; }
    $output[0] =~ /\s(\d+)\s*$/;
    $out=$1;                    
    $output[1] =~ /\s(\d+)\s*$/;
    $in=$1;                    
    return($in,$out);
}

sub readcfg {
    local($mode,$first,$second);
    open (CFG, pop(@ARGV)) || &printusage;
    while (<CFG>) {
	if (/^([A-Za-z]+)\[(\S+)\]:\s*(.*\S)\s*$/) {
	    $rcfg{lc($1)}{lc($2)} = $3;
	    $routers{lc($2)} = "";
	    $first = $1;
	    $second = $2;
	    $mode = "R";
	    next;
	}
	if (/^(\S+):\s*(.*\S)\s*$/) {
	    $cfg{lc($1)} = $2;
	    $first = $1;
	    $mode = "C";
	    next;
	}
	if (/^\s+(.*\S)\s*$/) {
	    if ($mode eq "R") {
		$rcfg{lc($first)}{lc($second)} .= " $1";
		next;
	    }
	    if ($mode eq "L") {
		$cfg{lc($first)} .= " $1";
		next;
	    }
	}
	if ((/^\s*\#/ ) || (/^\s*$/)) {
	    next;
	}
	die ( "\nLine $. in CFG file doesn't make sence\n" );
    }
    close (CFG);
}

sub cfgcheck {
    local($error,$rou);
    if (($cfg{"workdir"} eq '') || 
	(not -d $cfg{"workdir"})) {
	warn ("Config Problem: \"workdir\" not specified or not existant\n");
	$error = "yes";
    }
    if (($cfg{"snmpget"} eq '') || 
	(not -x $cfg{"snmpget"})) {
	warn ("Config Problem: \"snmpget\" not pointing to executable\n");
	$error = "yes";
    }
    if (($cfg{"mibfile"} eq '') || 
	(not -f $cfg{"mibfile"})) {
	warn ("Config Problem: \"mibfile\" not pointing to a plain file\n");
	$error = "yes";
    }
    if (($cfg{"ppmtogif"} eq '') || 
	(not -x $cfg{"ppmtogif"})) {
	warn ("Config Problem: \"ppmtogif\" not pointing to executable\n");
	$error = "yes";
    }
    foreach $rou (keys %routers) {
	if ($rcfg{"ip"}{$rou} eq '') {
	    warn ("Config Problem: \"ip[$rou]\" not specified\n");
	    $error = "yes";
	}
	if ($rcfg{"maxbytes"}{$rou} eq '') {
	    warn ("Config Problem: \"MaxBytes[$rou]\" not specified\n");
	    $error = "yes";
	}
	if ($rcfg{"title"}{$rou} eq '') {
	    warn ("Config Problem: \"Title[$rou]\" not specified\n");
	    $error = "yes";
	}
	if ($rcfg{"comment"}{$rou} eq '') {
	    warn ("Config Problem: \"Comment[$rou]\" not specified\n");
	    $error = "yes";
	}
	if ($rcfg{"ifout"}{$rou} eq '') {
	    warn ("Config Problem: \"ifOut[$rou]\" not specified\n");
	    $error = "yes";
	}
	if ($rcfg{"ifin"}{$rou} eq '') {
	    warn ("Config Problem: \"ifin[$rou]\" not specified\n");
	    $error = "yes";
	}
    }
    if ($error eq "yes") {
	die ("\n\nPlease fix the error in your config file\n\n");
    }
}
	

sub getcurrent {
    ($innew,$outnew) = &get;
    $time = time;
    $inlast = $innew;
    $outlast = $outnew;
    &readlast;
    @times = $time;
    @inbytes = $innew - $inlast;
    @outbytes = $outnew - $outlast;
}

sub readlast {
    open (LAST, "$router.last") || return;
    $inlast = <LAST>;
    chop ($inlast);
    $outlast = <LAST>;
    chop ($outlast);
    close(LAST);
}
    
sub savenew {
    open (LAST, ">$router.last") || die ("\nCan't write state");
    print LAST "$innew\n$outnew\n";
    close (LAST);      
}

sub getword {
   local($hand) = @_;
   $dummy = <$hand>;
   chop($dummy);
   return($dummy);
}

sub addoldtraffic {
    open (LOG, "$router.log");
    while (not eof LOG) {
      push(@times, &getword("LOG"));
      push(@inbytes, &getword("LOG"));
      push(@outbytes, &getword("LOG"));
    }
    close (LOG);
    if (($inbytes[0]/($times[0]-$times[1]) > $rcfg{"maxbytes"}{$router}*1.1)||
	($inbytes[0] < 0 )) {
	$inbytes[0] =
	    $inbytes[1]/($times[1]-$times[2])*($times[0]-$times[1]);
    }
    if (($outbytes[0]/($times[0]-$times[1]) > $rcfg{"maxbytes"}{$router}*1.1)||
	($outbytes[0] < 0 )) {
	$outbytes[0] =
	    $outbytes[1]/($times[1]-$times[2])*($times[0]-$times[1]);
    }
}

sub writegraphics {
    @input=();@output=();@io_times=();
    $step=5*60;
    $first = $times[0] - ($times[0] % $step);
    &filllist($first,$step,24*12+2);
    &paint("-day.gif", $step,24*12 ,"h");
    
    if ((not -e "${router}-week.gif") ||(-M "${router}-week.gif" >= 0.5/24))
    {
	@input=();@output=();@io_times=();
	$step=30*60;
	$first = $times[0] - ($times[0] % $step);
	&filllist($first,$step,24*2*7+2);
	&paint("-week.gif", $step,24*2*7 ,"d");
    }

    if ((not -e "${router}-year.gif") || ( -M "${router}-year.gif" >= 1)) {
	@input=();@output=();@io_times=();
	$step=24*60*60;
	$first = $times[0] - ($times[0] % $step);
	&filllist($first,$step,365+2);
	&paint("-year.gif", $step,365 ,"m");
    }
}

sub filllist {
    local($first,$step,$steps) = @_;
    local($last);
    $current = 0;
    $last =  $first-$step*$steps;
    for ($pointer = $first; 
	 $pointer > $last ; 
	 $pointer -= $step) {
	$inslice = 0;
	$outslice = 0;
	$pointend=$pointer-$step;
	while (1) {
	    if ($current >= $#times) {
		last;
	    }
  	    $inrate = $inbytes[$current] /
		($times[$current]-$times[$current+1]);
	    $outrate = $outbytes[$current] /
		($times[$current]-$times[$current+1]);
	    if ($times[$current] > $pointend &&  
		$times[$current+1] < $pointer) {
		$inslice += $inbytes[$current];
		$outslice += $outbytes[$current];
		if ($times[$current] > $pointer) {
		    $inslice -= ($times[$current]-$pointer)*$inrate;
		    $outslice -= ($times[$current]-$pointer)*$outrate;
		}
		if ($times[$current+1] ==  $pointend) {$current++; last; }

		if ($times[$current+1] < $pointend) {
		    $inslice -= ($pointend - $times[$current+1])*$inrate;
		    $outslice -= ($pointend - $times[$current+1])*$outrate;
		    last;
		} 
	    }
	    $current++; 
	}
	push  @input, $inslice ;
	push  @io_times, $pointer ;
	push  @output, $outslice ;
    }
}

sub paint {
    local ($suffix,$step,$xmax,$mark) = @_;
    local (@hmarks,@mmarks,$min,$hour,$mday,$wday,$yday);
    open (PPMTOGIF, 
	  "|$cfg{'ppmtogif'} -interlace -transparent ".
	  "'#bfbfbf' >$router$suffix 2>/dev/null ") ||
	      die ("Can't open $cfg{'ppmtogif'}\n");
    $ysize= 100;
    $ymax = $rcfg{'maxbytes'}{$router} * $step;
    $ystep = $ymax / $ysize;
    $yss =0;
    for ($x=0; $x<$xmax; $x++) {
	($min,$hour,$wday,$mday,$yday) =
	    (localtime($io_times[$x]))[1,2,6,3,7];
 
	if ((($mark eq "h") && 
	     (0 == $min)) ||
	    (($mark eq "d") && 
	     (0 == $min) &&
	     (0 == $hour)) ||
	    (($mark eq "m") && 
	     (1  == $mday))) {
	    $hmarks[$x] = 1;
	}
	
	if ((($mark eq "h") && 
	     (0 == $min) &&
	     (0 == $hour)) ||
	    (($mark eq "d") && 
	     (0 == $min) &&
	     (0 == $hour) &&
	     (1 == $wday)) ||
	    (($mark eq "m") && 
	     (0  == $yday))) {
	    $mmarks[$x] = 1;
	}
    }

    print PPMTOGIF "P6 $xmax $ysize 255\n";
        
    for ($y = $ymax ; $y > 0 ; $y -= $ystep) {
        $yss ++;
	for ($x=0; $x<$xmax; $x++) {
	    $current=$c_blank;
	    if($y <= $input[$x]) {
		$current = $c_in; 
	    }
	    if ((($y < $output[$x]+$ystep) 
		&& ($y >= $output[$x+1])) ||
	       (($y > $output[$x]-$ystep) && ($y <= $output[$x+1]))) 
	    {
		$current = $c_out;
	    }
	    if (($yss % 3 == 0) && 
		($hmarks[$x] == 1)) {
		$current = $c_grid;
	    }
	    if (($yss % 25 == 0) && ($x % 3 == 0)) {
		$current = $c_grid;
	    }
            if ($mmarks[$x] == 1) {
		$current = $c_major;
	    }
	    print PPMTOGIF $current;
	}
    }
    close PPMTOGIF;
}

sub savetraffic {
    @input=();@output=();@io_times=();
    $first = $times[0];
    $last = $times[0] - ($times[0] % (5*60));
    $step = $first - $last;
    &filllist($first,$step,1);
    
    $step = 5*60;
    $first = $times[0] - ($times[0] % $step);
    $rest = ($first % (60*30)) / $step;
    $steps = 12*24+$rest;
    &filllist($first,$step,$steps);

    $first -=  $step*$steps;
    $step = 30*60;
    $rest = ($first % (60*60*24)) / $step;
    $steps = 2*24*6+$rest;
    &filllist($first,30*60,$steps);

    $first -=  $step*$steps;
    $step = 24*60*60;
    &filllist($first,24*60*60,735);

    open (LOG, ">$router.log");
    for (@io_times) {
      print LOG "$_\n";
      print LOG shift(@input),"\n";
      print LOG shift(@output),"\n";
    }
    close (LOG);
}

sub datestr {
    local($time) = shift(@_) || return 0;
    local($wday) = (Sunday,Monday,Tuesday,Wednesday,
		    Thursday,Friday,Saturday)[(localtime($time))[6]];
    local($month) = ('January','February' ,'March' ,'April' ,
                     'May' , 'June' , 'July' , 'August' , 'September' , 
                     'October' ,
                     'November' , 'December' )[(localtime($time))[4]];
    local($mday,$year,$hour,$min) = (localtime($time))[3,5,2,1];
    if ($min<10) {$min = "0$min";}
    return "$wday $mday, $month ".($year+1900)." at $hour:$min";
}

sub writehtml {
    local($rev,$date,$Today);
    $Today=datestr(time);
    '$Revision: 1.24 $ ' =~ /Revision: (\S*)/;
    $rev=$1;
    '$Date: 1995/05/11 13:09:04 $ ' =~ /Date: (\S*)/;
    $date=$1;
    open (HTML,">$router.html") || die ("\nCan't write $router.html");
    print HTML <<TRAFFICPAGE;
<HTML>
<META HTTP-EQUIV="Refresh" CONTENT=300>
<HEAD>
<TITLE>
$rcfg{'title'}{$router}
</TITLE>
<H1>$rcfg{'title'}{$router}</H1>
$rcfg{'comment'}{$router}<P>
The blue part of the graph is the incoming traffic. The green line 
is the outgoing traffic.<P>
The statistics were last updated $Today
<HR>
<DL>
<DT><B>Statistics for the last 24 hours</B><BR>
<DD>
<HR>
$rcfg{'maxbytes'}{$router} Bytes per Second<BR>
<IMG SRC="NOW.gif"><IMG SRC="$router-day.gif"><IMG SRC="24h.gif"><BR>
0 Bytes per Second<BR>
<HR>
The red line marks midnight
</DL><P>

<DL>
<DT><B>Statistics for the last 7 days</B><BR>
<DD>
<HR>
$rcfg{'maxbytes'}{$router} Bytes per Second<BR>
<IMG SRC="NOW.gif"><IMG SRC="$router-week.gif"><IMG SRC="7d.gif"><BR>
0 Bytes per Second<BR>
<HR>
The red line marks the start of the week
</DL><P>

<DL>
<DT><B>Statistics for the last Year</B><BR>
<DD>
<HR>
$rcfg{'maxbytes'}{$router} Bytes per Second<BR>
<IMG SRC="NOW.gif"><IMG SRC="$router-year.gif"><IMG SRC="1y.gif"><BR>
0 Bytes per Second<BR>
<HR>
The red line marks the start of the year
</DL>
<HR>

Generated by mrtg <I>Multi Router Traffic Grapher</I>, version $rev. 
Last update $date.<BR> Created by <A HREF="http://engelberg.dmu.ac.uk">
Tobias Oetiker</A>

</BODY>
</HTML>
TRAFFICPAGE
    close HTML;
}

sub printusage {
    print <<USAGEDESC;
mrtg: Multi Router Traffic Grapher 
------------------------------------------------------------------------
Created by Tobias Oetiker <oetiker\@dmu.ac.uk> 
This program is distributed under the GNU copy-left.

Usage:    mrtg mrtg.cfg

Synopsis: This tool, when started up regularly, through a crontab,
          creates a daily, weekly and yearly graph, representing the 
          incoming and outgoing traffic on a snmp manageable router.
          The tool also produces a HTML document around the graphics
          for easy access on a web server.

Configuration: The operation of the "mrtg" is governed by entries in the 
          configuration file. The filename of the configuration file 
	  must be specified as a command line argument when calling 
          "mrtg"
          
Installation: Adapt the configuration file to your needs. Then put up a 
          crontab which starts "mrtg" every five minutes.
	  If for some reasons mrtg misses on reading, this doesn't matter, 
	  it will automatically take that into account.
 

Example Config File:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

# Multi Router Traffic Grapher -- Configuration File
#######################################################################
 
# General Configuration ###############################################
#######################################################################
 
WorkDir: /usr/local/www/data/dept/admin/dld/lin/netcomm/traffic
snmpget: /home/oetiker/bin/snmpget
mibfile: /home/oetiker/etc/mib.txt
ppmtogif: /usr/local/bin/ppmtogif 
 
# Configuration for the CISCO Gateway #################################
#######################################################################
 
IP[jips]: 146.227.1.248
MaxBytes[jips]: 8000   
Title[jips]: Traffic Analysis for DMU's Internet Gateway
 
Comment[jips]: Our Internet link curently runs over a 64Kb X.25 Link.
  This gives a maximum throughput of 8 KBytes per Second.
 
ifOut[jips]: interfaces.ifTable.ifEntry.ifOutOctets.6
ifIn[jiips]: interfaces.ifTable.ifEntry.ifInOctets.6 

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
USAGEDESC
    exit(1);
}


