|
Packit |
667938 |
# collect.pl
|
|
Packit |
667938 |
#
|
|
Packit |
667938 |
# Collect IP accounting data from a cisco router and summarise it
|
|
Packit |
667938 |
# into a CSV file. The file shows number of bytes sent from each
|
|
Packit |
667938 |
# source device to each destination network.
|
|
Packit |
667938 |
# Optionally, the data is also summarised by source (all destinations added
|
|
Packit |
667938 |
# together) and written out to an mrtg config file.
|
|
Packit |
667938 |
# There are also two input files. "networks" is used to map network
|
|
Packit |
667938 |
# addresses to names. "sources" is optional & is used to map source
|
|
Packit |
667938 |
# addresses to host names.
|
|
Packit |
667938 |
#
|
|
Packit |
667938 |
# Args are:
|
|
Packit |
667938 |
# h help
|
|
Packit |
667938 |
# a path of mrtg config file for data by source
|
|
Packit |
667938 |
# n path of a "networks" file used to map network addresses to names
|
|
Packit |
667938 |
# o path of output CSV file. Defaults to YYYYMMDD-HHMMSS.CSV
|
|
Packit |
667938 |
# s path of a "sources" file mapping source addresses to host names
|
|
Packit |
667938 |
#
|
|
Packit |
667938 |
# v1.0 17/9/98 Tony Farr Original
|
|
Packit |
667938 |
# v1.1 16/12/98 Tony Farr Add horrid kludge to collect Exchange traffic
|
|
Packit |
667938 |
# by source
|
|
Packit |
667938 |
# v1.2 22/3/99 Tony Farr Tidy up
|
|
Packit |
667938 |
# v1.3 25/3/99 Tony Farr Remove v1.1 kludge
|
|
Packit |
667938 |
# Just generate one mrtg line/data set
|
|
Packit |
667938 |
#
|
|
Packit |
667938 |
|
|
Packit |
667938 |
use SNMP_util;
|
|
Packit |
667938 |
use Getopt::Std;
|
|
Packit |
667938 |
use File::Basename;
|
|
Packit |
667938 |
use strict;
|
|
Packit |
667938 |
use Socket;
|
|
Packit |
667938 |
|
|
Packit |
667938 |
use vars qw/$opt_h $opt_a $opt_n $opt_o $opt_s/;
|
|
Packit |
667938 |
|
|
Packit |
667938 |
# CONSTANTS
|
|
Packit |
667938 |
# Note inclusion of the write community string
|
|
Packit |
667938 |
my $HOST= '0ztrad3@canb-wan';
|
|
Packit |
667938 |
# Directory for output logs/csv files.
|
|
Packit |
667938 |
my $LOGPATH= "D:\\logs\\whodo\\";
|
|
Packit |
667938 |
# Directory for MRTG config files for traffic sources
|
|
Packit |
667938 |
my $SOURCEDIR= "D:\\www\\mrtg\\whodo";
|
|
Packit |
667938 |
# Any source generating more than BIGBYTES per poll will be added to the sources config file automatically
|
|
Packit |
667938 |
my $BIGBYTES= 40000000;
|
|
Packit |
667938 |
|
|
Packit |
667938 |
my(@dstdesc, @dstaddr, @dstmask, @srcaddr, @srcdesc, %traffictab);
|
|
Packit |
667938 |
my $progname = basename($0);
|
|
Packit |
667938 |
my $usage = "Usage: $progname [-h] [-a mrtg_config_file] [-n network_file] [-o output_file] [-s source_address_file]\n";
|
|
Packit |
667938 |
# Parse Command Line:
|
|
Packit |
667938 |
die $usage unless getopts('ha:n:o:s:');
|
|
Packit |
667938 |
if ( defined($opt_h) ) {
|
|
Packit |
667938 |
print $usage;
|
|
Packit |
667938 |
exit(0);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
if ( defined($opt_n) ) {
|
|
Packit |
667938 |
load_nets($opt_n);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
unless ( defined($opt_o) ) {
|
|
Packit |
667938 |
my ($sec,$min,$hour,$mday,$month,$year) = localtime;
|
|
Packit |
667938 |
$opt_o= $LOGPATH . sprintf("%d%02d%02d-%02d%02d%02d.csv", $year+1900,++$month,$mday,$hour,$min,$sec);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
if ( defined($opt_s) ) {
|
|
Packit |
667938 |
load_sources($opt_s);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
my $age= checkpoint_stats($HOST);
|
|
Packit |
667938 |
get_stats($HOST);
|
|
Packit |
667938 |
print_stats($opt_o);
|
|
Packit |
667938 |
if ( defined($opt_a) ) {
|
|
Packit |
667938 |
make_sources_config($opt_a);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
exit(0);
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub load_nets {
|
|
Packit |
667938 |
# Loads 3 arrays:
|
|
Packit |
667938 |
# @dstaddr - a list of IP addresses
|
|
Packit |
667938 |
# @dstdesc - a list of the corresponding descriptions
|
|
Packit |
667938 |
# @dstmask - a list of the corresponding bitmasks
|
|
Packit |
667938 |
# The tables are loaded from a list of filenames passed in @_. That file
|
|
Packit |
667938 |
# can be a networks file. The file(s) should have format:
|
|
Packit |
667938 |
# netdescription 10.10.10 /25
|
|
Packit |
667938 |
# The "/25" is the mask & may be preceeded by a "#". It is optional.
|
|
Packit |
667938 |
my(@flist)= @_;
|
|
Packit |
667938 |
my ($desc, $end, $masksz);
|
|
Packit |
667938 |
while (my $fname= shift(@flist)) {
|
|
Packit |
667938 |
open(NETWORKS, "<$fname") || warn "$progname: unable to open $fname; $!";
|
|
Packit |
667938 |
while (<NETWORKS>) {
|
|
Packit |
667938 |
# Process #includes after dealing with the current file
|
|
Packit |
667938 |
if ( /^#\s*include\s+(\S+)/ ) {
|
|
Packit |
667938 |
push(@flist, $1);
|
|
Packit |
667938 |
next;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
if ( /^\s*(\w+)\s+(\S+)(.*)/ ) {
|
|
Packit |
667938 |
$desc= $1;
|
|
Packit |
667938 |
$_= $2;
|
|
Packit |
667938 |
$end= $3;
|
|
Packit |
667938 |
push(@dstdesc, $desc);
|
|
Packit |
667938 |
my $octets = 1 + tr/././;
|
|
Packit |
667938 |
$_ .= ".0" x (4 - $octets);
|
|
Packit |
667938 |
push( @dstaddr, aton($_) );
|
|
Packit |
667938 |
if ( $end =~ /\/(\d+)/ ) {
|
|
Packit |
667938 |
$masksz= $1;
|
|
Packit |
667938 |
} else {
|
|
Packit |
667938 |
$masksz= $octets * 8;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
push( @dstmask, pack("B32", "1" x $masksz . "0" x 32) );
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
close(NETWORKS);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub load_sources {
|
|
Packit |
667938 |
# Loads a pair of arrays: @srcaddr (a list of IP addresses) and @srcdesc
|
|
Packit |
667938 |
# (a list of the corresponding descriptions). The files are loaded from
|
|
Packit |
667938 |
# a list of filenames passed in @_. That file can be a hosts file. However "address" entries
|
|
Packit |
667938 |
# can be perl regular exprs as well literal addresses. For compatibility,
|
|
Packit |
667938 |
# "." when used as a wild card must be expressed "\." - i.e. the reverse of normal.
|
|
Packit |
667938 |
my(@flist)= @_;
|
|
Packit |
667938 |
while (my $fname= shift(@flist)) {
|
|
Packit |
667938 |
open(HOSTS, "<$fname") || warn "$progname: unable to open $fname; $!";
|
|
Packit |
667938 |
while (<HOSTS>) {
|
|
Packit |
667938 |
# Process #includes after dealing with the current file
|
|
Packit |
667938 |
if ( /^#\s*include\s+(\S+)/ ) {
|
|
Packit |
667938 |
push(@flist, $1);
|
|
Packit |
667938 |
next;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
($_)= split(/#/);
|
|
Packit |
667938 |
if ( /(\S+)\s+(\S+)/ ) {
|
|
Packit |
667938 |
push(@srcdesc, $2);
|
|
Packit |
667938 |
$_= $1;
|
|
Packit |
667938 |
s/\./\\\./g; # Replace "." with "\."
|
|
Packit |
667938 |
s/\\\\\././g; # If we now have "\\.", replace with "."
|
|
Packit |
667938 |
push(@srcaddr, $_);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
close(HOSTS);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub checkpoint_stats {
|
|
Packit |
667938 |
# Take a checkpoint on IP accounting on the given router & return the duration of it.
|
|
Packit |
667938 |
# The checkpoint is done by doing a get then a set on actCheckPoint
|
|
Packit |
667938 |
my ($age);
|
|
Packit |
667938 |
|
|
Packit |
667938 |
# Find how long since the last checkpoint
|
|
Packit |
667938 |
($age) = snmpget ($_[0], '1.3.6.1.4.1.9.2.4.8.0');
|
|
Packit |
667938 |
warn "$progname: No actAge returned.\n" unless $age;
|
|
Packit |
667938 |
|
|
Packit |
667938 |
# Check to see if we've lost any data
|
|
Packit |
667938 |
($_) = snmpget ($_[0], '1.3.6.1.4.1.9.2.4.6.0');
|
|
Packit |
667938 |
warn "$progname: Accounting table overflow - $_ bytes lost.\n" if $_ > 0;
|
|
Packit |
667938 |
|
|
Packit |
667938 |
# Do a new checkpoint
|
|
Packit |
667938 |
($_) = snmpget ($_[0], '1.3.6.1.4.1.9.2.4.11.0');
|
|
Packit |
667938 |
die "$progname: No actCheckPoint returned.\n" unless defined;
|
|
Packit |
667938 |
snmpset ($_[0], '1.3.6.1.4.1.9.2.4.11.0', 'integer', $_);
|
|
Packit |
667938 |
$age;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub get_stats {
|
|
Packit |
667938 |
# Summarise the checkpoint by destination network (not host).
|
|
Packit |
667938 |
# Summary is placed into %traffictab - a hash of hashes indexed by
|
|
Packit |
667938 |
# source device & destination network.
|
|
Packit |
667938 |
my($src, $dstnet);
|
|
Packit |
667938 |
my @response = snmpwalk ($_[0], '1.3.6.1.4.1.9.2.4.9.1.4' );
|
|
Packit |
667938 |
foreach $_ (@response) {
|
|
Packit |
667938 |
/(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+):(\d+)/ ||
|
|
Packit |
667938 |
die "$progname: Cannot parse response from walk.\n";
|
|
Packit |
667938 |
$dstnet= addr_to_net($2);
|
|
Packit |
667938 |
$src= addr_to_src($1);
|
|
Packit |
667938 |
$traffictab{$src}{$dstnet} += $3;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub print_stats {
|
|
Packit |
667938 |
# Print out the traffictab in csv format
|
|
Packit |
667938 |
my ($sec,$min,$hour,$mday,$month,$year) = localtime(time());
|
|
Packit |
667938 |
$year += 1900;
|
|
Packit |
667938 |
$month++;
|
|
Packit |
667938 |
open (CSVFILE,">$_[0]") || die "$progname: Could not open file $_[0]; $!\n";
|
|
Packit |
667938 |
printf CSVFILE "End Time:,%d/%02d/%d %d:%02d:%02d\n",$mday,$month,$year,$hour,$min,$sec;
|
|
Packit |
667938 |
print CSVFILE "Duration:,$age\n"; # Bug alert. This breaks if $age > 1 day
|
|
Packit |
667938 |
my($s, $d);
|
|
Packit |
667938 |
foreach $s (sort keys %traffictab) {
|
|
Packit |
667938 |
foreach $d (sort keys %{$traffictab{$s}}) {
|
|
Packit |
667938 |
print CSVFILE "$s,$d,$traffictab{$s}{$d}\n";
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
close(CSVFILE);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub make_sources_config {
|
|
Packit |
667938 |
# Print out an mrtg config file
|
|
Packit |
667938 |
my($cfgfile)= @_;
|
|
Packit |
667938 |
my(%cfgentries, $src, $dst, $t, $misc);
|
|
Packit |
667938 |
# Load current cfg entries
|
|
Packit |
667938 |
if ( open(CFG, "<$cfgfile") ) {
|
|
Packit |
667938 |
while (<CFG>) {
|
|
Packit |
667938 |
if ( /^\s*Target\[([^\]]*)/ && $1 ne "Miscellaneous" ) {
|
|
Packit |
667938 |
$cfgentries{ uc($1) }= 1;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
close(CFG);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
# Write out the header of a new config file
|
|
Packit |
667938 |
open(CFG,">$cfgfile") || die "$progname: Could not open file $cfgfile; $!\n";
|
|
Packit |
667938 |
write_sources_header();
|
|
Packit |
667938 |
# For each traffictab entry, if it's large or there's an existing CFG entry, write out a new CFG entry
|
|
Packit |
667938 |
foreach $src (keys %traffictab) {
|
|
Packit |
667938 |
$t= 0;
|
|
Packit |
667938 |
foreach $dst (keys %{$traffictab{$src}}) {
|
|
Packit |
667938 |
$t += $traffictab{$src}{$dst};
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
if ( $cfgentries{ uc($src) } ) {
|
|
Packit |
667938 |
delete $cfgentries{ uc($src) };
|
|
Packit |
667938 |
write_source_entry($src, $t);
|
|
Packit |
667938 |
} elsif ( $t > $BIGBYTES ) {
|
|
Packit |
667938 |
write_source_entry($src, $t);
|
|
Packit |
667938 |
} else {
|
|
Packit |
667938 |
$misc += $t;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
# Write out new entries for any CFG entries that existed previously but we've
|
|
Packit |
667938 |
# missed because they generated no traffic this time.
|
|
Packit |
667938 |
foreach $src (keys %cfgentries) {
|
|
Packit |
667938 |
write_source_entry($src, 0);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
# Write an entry for the miscellaneous odds & ends
|
|
Packit |
667938 |
write_source_entry("Miscellaneous", $misc);
|
|
Packit |
667938 |
close(CFG);
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub write_sources_header {
|
|
Packit |
667938 |
print CFG <
|
|
Packit |
667938 |
WorkDir: $SOURCEDIR
|
|
Packit |
667938 |
IconDir: /mrtg/
|
|
Packit |
667938 |
Interval: 30
|
|
Packit |
667938 |
|
|
Packit |
667938 |
END_OF_HEADER
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub write_source_entry {
|
|
Packit |
667938 |
print CFG <
|
|
Packit |
667938 |
|
|
Packit |
667938 |
Title[$_[0]]: Traffic from $_[0]
|
|
Packit |
667938 |
PageTop[$_[0]]: Traffic from $_[0]
|
|
Packit |
667938 |
MaxBytes[$_[0]]: 12500000
|
|
Packit |
667938 |
Options[$_[0]]: growright, bits, absolute, nopercent
|
|
Packit |
667938 |
Colours[$_[0]]: w#ffffff,blue#0000e0,w#ffffff,r#ff0000
|
|
Packit |
667938 |
Target[$_[0]]: `perl -e "print \\"0\\n$_[1]\\""`
|
|
Packit |
667938 |
YLegend[$_[0]]: Bits per Second
|
|
Packit |
667938 |
ShortLegend[$_[0]]: bps
|
|
Packit |
667938 |
Legend1[$_[0]]:
|
|
Packit |
667938 |
Legend2[$_[0]]: Traffic from $_[0]
|
|
Packit |
667938 |
LegendI[$_[0]]:
|
|
Packit |
667938 |
LegendO[$_[0]]: Traffic:
|
|
Packit |
667938 |
|
|
Packit |
667938 |
END_OF_ENTRY
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub addr_to_net {
|
|
Packit |
667938 |
# Returns the name/description of the network of the given address.
|
|
Packit |
667938 |
# Addresses are looked up in @dstaddr first. If that fails, the address is returned.
|
|
Packit |
667938 |
my($i, $dst);
|
|
Packit |
667938 |
$dst= aton($_[0]);
|
|
Packit |
667938 |
for ($i=0; $i < @dstaddr; $i++) {
|
|
Packit |
667938 |
if ( ($dst & $dstmask[$i]) eq $dstaddr[$i] ) {
|
|
Packit |
667938 |
return $dstdesc[$i];
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
$_[0] =~ /(.*)\..*/; # Assume Class C & strip off the last octet
|
|
Packit |
667938 |
$1;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub BEGIN {
|
|
Packit |
667938 |
my ($lastaddr, $lastsrc);
|
|
Packit |
667938 |
sub addr_to_src {
|
|
Packit |
667938 |
# Returns the name/description of the given address.
|
|
Packit |
667938 |
# Addresses are looked up in @srcaddr first. If there's no match, a dns lookup is tried.
|
|
Packit |
667938 |
# If that fails, the address is returned.
|
|
Packit |
667938 |
if ( $_[0] eq $lastaddr ) {
|
|
Packit |
667938 |
return $lastsrc;
|
|
Packit |
667938 |
} else {
|
|
Packit |
667938 |
$lastaddr= $_[0];
|
|
Packit |
667938 |
for (my $i=0; $i < @srcaddr; $i++) {
|
|
Packit |
667938 |
if ($_[0] =~ /^$srcaddr[$i]$/ ) {
|
|
Packit |
667938 |
$lastsrc= $srcdesc[$i];
|
|
Packit |
667938 |
return $lastsrc;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
my $addr= aton($_[0]);
|
|
Packit |
667938 |
if ($lastsrc= gethostbyaddr($addr, AF_INET)) {
|
|
Packit |
667938 |
$lastsrc =~ s/\.austrade\.gov\.au$//i;
|
|
Packit |
667938 |
} else {
|
|
Packit |
667938 |
$lastsrc= $_[0];
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
return $lastsrc;
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
}
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
|
|
Packit |
667938 |
sub aton {
|
|
Packit |
667938 |
# I found the standard "inet_aton" very slow (on Windows).
|
|
Packit |
667938 |
# Hence this version. It only handles dotted decimal addresses -
|
|
Packit |
667938 |
# not names.
|
|
Packit |
667938 |
$_[0] =~ /(\d+).(\d+).(\d+).(\d+)/;
|
|
Packit |
667938 |
chr($1).chr($2).chr($3).chr($4);
|
|
Packit |
667938 |
}
|