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