#!/usr/bin/perl -w

#  Try to graph the output of ipacsum from the ipac-ng package

my $ipacsum = "/usr/local/sbin/ipacsum --exact";
my $ipac_cfg = "/etc/ipac-ng/rules.conf";
my $rrd_data_dir = "/var/lib/rrd";
my $graph_dir = "/home/www/htdocs/images/rrd-graphs";	# graph output
my $out_dir = "/home/www/htdocs/hosts";
my $out_main = "index.html";
my $base_url = "http://www.example.com/hosts";	# your URL
my $graph_url = "http://www.example.com/images/rrd-graphs"; # graphs URL
my $rrdtool = "/usr/local/bin/rrdtool";
my $key_send = "send";
my $key_recv = "recv";
my $key_net = "net";
my %ruledata;

my %graph_types = (	"Daily",	" ",
			"Weekly",	"--start -604800",
			"Monthly",	"--start -1m",
			"Yearly",	"--start -1y" );

my $start_time = `date`;	chop $start_time;
print "$0: start $start_time\n";

# Let's try to parse the $ipac_cfg file into memory

open( RULESFILE, "<$ipac_cfg" ) or die "$0: could not open \"$ipac_cfg\": $!\n";

while (<RULESFILE>)
{
	my $line = $_;
	chop $line;

	next if ( ($line eq "") || ($line =~ /^#/ ));
	print "RULE: $line\n";

	my @fields = split( /\|/, $line );
	next if ($#fields != 5);	# skip format violations

	$ruledata{ $fields[0] } = "0";
}

close RULESFILE;

# Let's parse the ipacsum program output and fill in any traffic numbers we find

open( IPACSUM, "$ipacsum |" ) or die "$0: can't run $ipacsum: $!\n";

while (<IPACSUM>)
{
	my $this_key = "";
	my $this_val = "";

	my $line = $_;
	chop $line;

	next if not ($line =~ /^  /);

	my @fields = ($line =~ m/(\w+|[0-9])/g);

	if (($fields[2] =~ /[0-9]/ ) && ($#fields == 2)) # this is a 'total traffic' counter
	{
		$this_key = $fields[0] . " " . $fields[1];
		$this_val = $fields[2];
	}
	else	# this is a 'port traffic' counter
	{
		$this_key = $fields[0] . " " . $fields[1] . " " .
			$fields[2] . " " . $fields[3];
		$this_val = $fields[4];
	}

	$ruledata{ $this_key } = $this_val;
}

close IPACSUM;

# Update (and create, if mssing) the RRD for each rule 

foreach $key ( sort ( keys %ruledata ) )
{
	# change spaces to underlines for the file name and RRD var name
	my $rrd_key = $key;  $rrd_key =~ s/ /_/g;

	my $rrd_filename = "$rrd_data_dir/$rrd_key.rrd";

	if ( not -e $rrd_filename )		# file does not exist
	{
		# create an empty RRD file for this key

		print "Creating \"$rrd_filename\"...";

		my $cmd = "$rrdtool create \"$rrd_filename\" " .
			"DS:$rrd_key:ABSOLUTE:600:0:1073741824 " .
			"RRA:AVERAGE:0.5:1:576 " .	# 2 days of 5-min
			"RRA:AVERAGE:0.5:6:672 " .	# 2 weeks of 1/2 hr
			"RRA:AVERAGE:0.5:24:732 " .	# 2 months of 2-hr
			"RRA:AVERAGE:0.5:144:1460 ";	# 2 years of 12-hr

		`$cmd >/dev/null 2>&1`;

		print "OK.\n";
	}

	# Update the RRD with new data if we have any

	my $cmd = "$rrdtool update \"$rrd_filename\" " .
		"N:$ruledata{$key}";

	`$cmd >/dev/null 2>&1`;
}

# Now for some graphing
# First let's make a main index page with all the hosts' total IO graphs

open( OUTFH, ">$out_dir/$out_main.tmp" );		# write to temp
HTML_HEADER( *OUTFH, "all hosts" );

foreach $key ( sort ( keys %ruledata ) )
{
	my @fields = split( / /, $key );

	next if ( $#fields ne 1 );		# skip port details
	next if ( $fields[1] ne $key_recv );	# skip all but first

	my $hostname = $fields[0];

	# change spaces to underlines for the file name and RRD var name
	my $rrd_key = $key;  $rrd_key =~ s/ /_/g;
	my $rrd_file_recv = "$rrd_data_dir/$fields[0]" . "_" . $key_recv . ".rrd";
	my $rrd_file_send = "$rrd_data_dir/$fields[0]" . "_" . $key_send . ".rrd";

	print OUTFH "<tr><td colspan=4><h3><b>\n";
	print OUTFH "<br>Internet for host: $hostname\n";
	print OUTFH "</b></h3></td></tr>\n";
	print OUTFH "<tr>\n";

	# output 4 graphs horizontally aligned for this host

	foreach $gtype ( "Daily", "Weekly", "Monthly", "Yearly" )
	{
		print OUTFH "<td>\n";
		print OUTFH "<a href=\"$base_url/host-$hostname.html\">\n";
		print OUTFH "<img border=0 src=\"$graph_url/$hostname-internet-";
		print OUTFH "$gtype.png\">\n</a>\n";
		print OUTFH "</td>\n";

		my $cmd = "$rrdtool graph \"$graph_dir/$hostname-internet-$gtype.png\" " .
			"--vertical-label bits " .
			"--imgformat PNG " .
			"$graph_types{$gtype} " .
			"--title \"$gtype internet usage bits/s.  $start_time\" " .
			"DEF:incoming=$rrd_file_recv:$hostname" . "_" . "$key_recv:AVERAGE " .
			"DEF:outgoing=$rrd_file_send:$hostname" . "_" . "$key_send:AVERAGE " .
			"CDEF:incomingbits=incoming,8,\* " .
			"CDEF:outgoingbits=outgoing,8,\* " .
			"AREA:outgoingbits#0086AF:\"Outgoing\" ";

		if (( $gtype eq "Daily" ) || ( $gtype eq "Weekly" ))	# only for recent traffic
		{
			$cmd .=	"GPRINT:outgoingbits:MAX:\"max %6.3lf%s\" " .
				"GPRINT:outgoingbits:LAST:\"last %6.3lf%s\\n\" ";
		}

		$cmd .= "LINE2:incomingbits#dd6633:\"Incoming\" ";

		if (( $gtype eq "Daily" ) || ( $gtype eq "Weekly" ))    # only for recent traffic
		{
			$cmd .=	"GPRINT:incomingbits:MAX:\"max %6.3lf%s\" " .
				"GPRINT:incomingbits:LAST:\"last %6.3lf%s\" ";
		}

		`$cmd 1>/dev/null 2>&1`;
	}

	print OUTFH "</tr>\n";

	# Now let's loop on this host and try to find any port traffic rules

	# open a temp outfile

	open( PORTFH, ">$out_dir/host-$hostname.html.tmp" );
	HTML_HEADER( *PORTFH, $hostname );

	foreach $port_key ( sort ( keys %ruledata ) )
	{
		my @port_fields = split( / /, $port_key );

		next if ( $#port_fields ne 3 );			# skip traffic summaries
		next if ( $port_fields[3] ne $key_recv );	# skip all but first
		next if ( $port_fields[0] ne $hostname );	# skip not on this host

		print "Found port $port_fields[2] for $hostname\n";
		my $port = $port_fields[2];

		# change spaces to underlines for the file name and RRD var name

		my $port_key_recv = $port_key;  $port_key_recv =~ s/ /_/g;
		my $port_key_send = $port_key_recv;  $port_key_send =~ s/recv/send/g;

		my $port_file_recv = "$rrd_data_dir/$port_key_recv.rrd";
		my $port_file_send = "$rrd_data_dir/$port_key_send.rrd";

		my $port_file_rev_recv = "$rrd_data_dir/$port_fields[1]" . "_" .
			"$port_fields[0]" . "_" . "$port_fields[2]" . "_recv.rrd";

		my $port_file_rev_send = "$rrd_data_dir/$port_fields[1]" . "_" .
			"$port_fields[0]" . "_" . "$port_fields[2]" . "_send.rrd";

		my $var_recv =		$hostname . "_" .  $key_net . "_" .
					$port . "_" .  $key_recv;

		my $var_rev_recv =	$key_net . "_" .  $hostname . "_" .
					$port . "_" .  $key_recv;

		my $var_send =		$hostname . "_" .  $key_net . "_" .
					$port . "_" .  $key_send;

		my $var_rev_send =	$key_net . "_" .  $hostname . "_" .
					$port . "_" .  $key_send;

		print PORTFH "<tr><td colspan=4><h3><b>\n";
		print PORTFH "<br>Port traffic for host $hostname:$port\n";
		print PORTFH "</b></h3></td></tr>\n";
		print PORTFH "<tr>\n";

		# output 4 graphs horizontally aligned for this host-port

		foreach $gtype ( "Daily", "Weekly", "Monthly", "Yearly" )
		{
			print PORTFH "<td>\n";
			print PORTFH "<img border=0 src=\"$graph_url/$hostname-$port-";
			print PORTFH "$gtype.png\">\n</a>\n";
			print PORTFH "</td>\n";

			my $cmd = "$rrdtool graph \"$graph_dir/$hostname-$port-$gtype.png\" " .
                        "--vertical-label bits " .
                        "--imgformat PNG " .
                        "$graph_types{$gtype} " .
                        "--title \"$gtype $hostname:$port usage bits/s.  $start_time\" " .
                        "DEF:incoming1=$port_file_recv:$var_recv:AVERAGE " .
                        "DEF:incoming2=$port_file_rev_recv:$var_rev_recv:AVERAGE " .
                        "DEF:outgoing1=$port_file_send:$var_send:AVERAGE " .
                        "DEF:outgoing2=$port_file_rev_send:$var_rev_send:AVERAGE " .
			"CDEF:in_tot=incoming1,incoming2,+ " .
			"CDEF:out_tot=outgoing1,outgoing2,+ " .
                        "CDEF:incomingbits=in_tot,8,\* " .
                        "CDEF:outgoingbits=out_tot,8,\* " .
                        "AREA:outgoingbits#0086AF:\"Outgoing\" ";

			if (( $gtype eq "Daily" ) || ( $gtype eq "Weekly" ))    # only for recent traffic
			{
				$cmd .= "GPRINT:outgoingbits:MAX:\"max %6.3lf%s\" " .
					"GPRINT:outgoingbits:LAST:\"last %6.3lf%s\\n\" ";
			}

			$cmd .= "LINE2:incomingbits#dd6633:\"Incoming\" ";

			if (( $gtype eq "Daily" ) || ( $gtype eq "Weekly" ))    # only for recent traffic
			{
				$cmd .= "GPRINT:incomingbits:MAX:\"max %6.3lf%s\" " .
					"GPRINT:incomingbits:LAST:\"last %6.3lf%s\" ";
			}

			`$cmd 1>/dev/null 2>&1`;
		}

		print PORTFH "</tr>\n";
	}

	HTML_FOOTER( *PORTFH );
	close PORTFH;

	# make live
	`mv $out_dir/host-$hostname.html.tmp $out_dir/host-$hostname.html`;
}

HTML_FOOTER( *OUTFH );
close OUTFH;
`mv $out_dir/$out_main.tmp $out_dir/$out_main`;		# make live

my $end_time =  `date`;		chop $end_time;
print "$0: finished $end_time\n";
exit 0;


##########################################################################

sub HTML_HEADER
{
	local *FH = shift;
	local $hostname = shift;

	print FH "<HTML>\n";
	print FH "<HEAD>\n";
	print FH "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"300\">\n";
	print FH "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">\n";
	print FH "<META HTTP-EQUIV=\"Expires\" CONTENT=\"Sat, 02 Dec 2000 19:35:06 GMT\">\n";
	print FH "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n";
	print FH "<TITLE>Internet traffic by host for $hostname</TITLE>\n";
	print FH "</HEAD>\n";
	print FH "<BODY bgcolor=\"#ffffff\" text=\"#000000\">\n";
	print FH "<table border=0>\n";
}


sub HTML_FOOTER
{
	local *FH = shift;

	print FH "</table>\n";
	print FH "</BODY>\n";
	print FH "</HTML>\n";
}




