collect.pl
上传用户:shbosideng
上传日期:2013-05-04
资源大小:1555k
文件大小:9k
源码类别:

SNMP编程

开发平台:

C/C++

  1. # collect.pl
  2. #
  3. # Collect IP accounting data from a cisco router and summarise it
  4. # into a CSV file. The file shows number of bytes sent from each
  5. # source device to each destination network.
  6. # Optionally, the data is also summarised by source (all destinations added
  7. # together) and written out to an mrtg config file.
  8. # There are also two input files. "networks" is used to map network
  9. # addresses to names. "sources" is optional & is used to map source
  10. # addresses to host names.
  11. #
  12. # Args are:
  13. # h help
  14. # a path of mrtg config file for data by source
  15. # n path of a "networks" file used to map network addresses to names
  16. # o path of output CSV file. Defaults to YYYYMMDD-HHMMSS.CSV
  17. # s path of a "sources" file mapping source addresses to host names
  18. #
  19. # v1.0 17/9/98 Tony Farr Original
  20. # v1.1 16/12/98 Tony Farr Add horrid kludge to collect Exchange traffic
  21. # by source
  22. # v1.2 22/3/99 Tony Farr Tidy up
  23. # v1.3 25/3/99 Tony Farr Remove v1.1 kludge
  24. # Just generate one mrtg line/data set
  25. #
  26. use SNMP_util;
  27. use Getopt::Std;
  28. use File::Basename;
  29. use strict;
  30. use Socket;
  31. use vars qw/$opt_h $opt_a $opt_n $opt_o $opt_s/;
  32. # CONSTANTS
  33. # Note inclusion of the write community string
  34. my $HOST= '0ztrad3@canb-wan';
  35. # Directory for output logs/csv files.
  36. my $LOGPATH= "D:\logs\whodo\";
  37. # Directory for MRTG config files for traffic sources
  38. my $SOURCEDIR= "D:\www\mrtg\whodo";
  39. # Any source generating more than BIGBYTES per poll will be added to the sources config file automatically
  40. my $BIGBYTES= 40000000;
  41. my(@dstdesc, @dstaddr, @dstmask, @srcaddr, @srcdesc, %traffictab);
  42. my $progname = basename($0);
  43. my $usage = "Usage: $progname [-h] [-a mrtg_config_file] [-n network_file] [-o output_file] [-s source_address_file]n";
  44. # Parse Command Line:
  45. die $usage unless getopts('ha:n:o:s:');
  46. if ( defined($opt_h) ) {
  47. print $usage;
  48. exit(0);
  49. }
  50. if ( defined($opt_n) ) {
  51. load_nets($opt_n);
  52. }
  53. unless ( defined($opt_o) ) {
  54. my ($sec,$min,$hour,$mday,$month,$year) = localtime;
  55. $opt_o= $LOGPATH . sprintf("%d%02d%02d-%02d%02d%02d.csv", $year+1900,++$month,$mday,$hour,$min,$sec);
  56. }
  57. if ( defined($opt_s) ) {
  58. load_sources($opt_s);
  59. }
  60. my $age= checkpoint_stats($HOST);
  61. get_stats($HOST);
  62. print_stats($opt_o);
  63. if ( defined($opt_a) ) {
  64. make_sources_config($opt_a);
  65. }
  66. exit(0);
  67. sub load_nets {
  68. # Loads 3 arrays:
  69. # @dstaddr - a list of IP addresses
  70. # @dstdesc - a list of the corresponding descriptions
  71. # @dstmask - a list of the corresponding bitmasks
  72. # The tables are loaded from a list of filenames passed in @_. That file
  73. # can be a networks file. The file(s) should have format:
  74. # netdescription 10.10.10 /25
  75. # The "/25" is the mask & may be preceeded by a "#". It is optional.
  76. my(@flist)= @_;
  77. my ($desc, $end, $masksz);
  78. while (my $fname= shift(@flist)) {
  79. open(NETWORKS, "<$fname") || warn "$progname: unable to open $fname; $!";
  80. while (<NETWORKS>) {
  81. # Process #includes after dealing with the current file
  82. if ( /^#s*includes+(S+)/ ) {
  83. push(@flist, $1);
  84. next;
  85. }
  86. if ( /^s*(w+)s+(S+)(.*)/ ) {
  87. $desc= $1;
  88. $_= $2;
  89. $end= $3;
  90. push(@dstdesc, $desc);
  91. my $octets = 1 + tr/././;
  92. $_ .= ".0" x (4 - $octets);
  93. push( @dstaddr, aton($_) );
  94. if ( $end =~ //(d+)/ ) {
  95. $masksz= $1;
  96. } else {
  97. $masksz= $octets * 8;
  98. }
  99. push( @dstmask, pack("B32", "1" x $masksz . "0" x 32) );
  100. }
  101. }
  102. close(NETWORKS);
  103. }
  104. }
  105. sub load_sources {
  106. # Loads a pair of arrays: @srcaddr (a list of IP addresses) and @srcdesc
  107. # (a list of the corresponding descriptions). The files are loaded from
  108. # a list of filenames passed in @_. That file can be a hosts file. However "address" entries
  109. # can be perl regular exprs as well literal addresses. For compatibility,
  110. # "." when used as a wild card must be expressed "." - i.e. the reverse of normal.
  111. my(@flist)= @_;
  112. while (my $fname= shift(@flist)) {
  113. open(HOSTS, "<$fname") || warn "$progname: unable to open $fname; $!";
  114. while (<HOSTS>) {
  115. # Process #includes after dealing with the current file
  116. if ( /^#s*includes+(S+)/ ) {
  117. push(@flist, $1);
  118. next;
  119. }
  120. ($_)= split(/#/);
  121. if ( /(S+)s+(S+)/ ) {
  122. push(@srcdesc, $2);
  123. $_= $1;
  124. s/./\./g; # Replace "." with "."
  125. s/\\././g; # If we now have "\.", replace with "."
  126. push(@srcaddr, $_);
  127. }
  128. }
  129. close(HOSTS);
  130. }
  131. }
  132. sub checkpoint_stats {
  133. # Take a checkpoint on IP accounting on the given router & return the duration of it.
  134. # The checkpoint is done by doing a get  then a set on actCheckPoint
  135. my ($age);
  136. # Find how long since the last checkpoint
  137. ($age) = snmpget ($_[0], '1.3.6.1.4.1.9.2.4.8.0');
  138. warn "$progname: No actAge returned.n" unless $age;
  139. # Check to see if we've lost any data
  140. ($_) = snmpget ($_[0], '1.3.6.1.4.1.9.2.4.6.0');
  141. warn "$progname: Accounting table overflow - $_ bytes lost.n" if $_ > 0;
  142. # Do a new checkpoint
  143. ($_) = snmpget ($_[0], '1.3.6.1.4.1.9.2.4.11.0');
  144. die "$progname: No actCheckPoint returned.n" unless defined;
  145. snmpset ($_[0], '1.3.6.1.4.1.9.2.4.11.0', 'integer', $_);
  146. $age;
  147. }
  148. sub get_stats {
  149. # Summarise the checkpoint by destination network (not host).
  150. # Summary is placed into %traffictab - a hash of hashes indexed by
  151. # source device & destination network.
  152. my($src, $dstnet);
  153. my @response = snmpwalk ($_[0], '1.3.6.1.4.1.9.2.4.9.1.4' );
  154. foreach $_ (@response) {
  155. /(d+.d+.d+.d+).(d+.d+.d+.d+):(d+)/ ||
  156. die "$progname: Cannot parse response from walk.n";
  157. $dstnet= addr_to_net($2);
  158. $src= addr_to_src($1);
  159. $traffictab{$src}{$dstnet} += $3;
  160. }
  161. }
  162. sub print_stats {
  163. # Print out the traffictab in csv format
  164. my ($sec,$min,$hour,$mday,$month,$year) = localtime(time());
  165. $year += 1900;
  166. $month++;
  167. open (CSVFILE,">$_[0]") || die "$progname: Could not open file $_[0]; $!n";
  168. printf CSVFILE "End Time:,%d/%02d/%d %d:%02d:%02dn",$mday,$month,$year,$hour,$min,$sec;
  169. print CSVFILE "Duration:,$agen"; # Bug alert. This breaks if $age > 1 day
  170. my($s, $d);
  171. foreach $s (sort keys %traffictab) {
  172. foreach $d (sort keys %{$traffictab{$s}}) {
  173. print CSVFILE "$s,$d,$traffictab{$s}{$d}n";
  174. }
  175. }
  176. close(CSVFILE);
  177. }
  178. sub make_sources_config {
  179. # Print out an mrtg config file
  180. my($cfgfile)= @_;
  181. my(%cfgentries, $src, $dst, $t, $misc);
  182. # Load current cfg entries
  183. if ( open(CFG, "<$cfgfile") ) {
  184. while (<CFG>) {
  185. if ( /^s*Target[([^]]*)/ && $1 ne "Miscellaneous" ) {
  186. $cfgentries{ uc($1) }= 1;
  187. }
  188. }
  189. close(CFG);
  190. }
  191. # Write out the header of a new config file
  192. open(CFG,">$cfgfile") || die "$progname: Could not open file $cfgfile; $!n";
  193. write_sources_header();
  194. # For each traffictab entry, if it's large or there's an existing CFG entry, write out a new CFG entry
  195. foreach $src (keys %traffictab) {
  196. $t= 0;
  197. foreach $dst (keys %{$traffictab{$src}}) {
  198. $t += $traffictab{$src}{$dst};
  199. }
  200. if ( $cfgentries{ uc($src) } ) {
  201. delete $cfgentries{ uc($src) };
  202. write_source_entry($src, $t);
  203. } elsif ( $t > $BIGBYTES ) {
  204. write_source_entry($src, $t);
  205. } else {
  206. $misc += $t;
  207. }
  208. }
  209. # Write out new entries for any CFG entries that existed previously but we've
  210. # missed because they generated no traffic this time.
  211. foreach $src (keys %cfgentries) {
  212. write_source_entry($src, 0);
  213. }
  214. # Write an entry for the miscellaneous odds & ends
  215. write_source_entry("Miscellaneous", $misc);
  216. close(CFG);
  217. }
  218. sub write_sources_header {
  219. print CFG <<END_OF_HEADER;
  220. WorkDir: $SOURCEDIR
  221. IconDir: /mrtg/
  222. Interval: 30
  223. END_OF_HEADER
  224. }
  225. sub write_source_entry {
  226. print CFG <<END_OF_ENTRY;
  227. Title[$_[0]]: Traffic from $_[0]
  228. PageTop[$_[0]]: <H1>Traffic from $_[0]</H1>
  229. MaxBytes[$_[0]]: 12500000
  230. Options[$_[0]]: growright, bits, absolute, nopercent
  231. Colours[$_[0]]: w#ffffff,blue#0000e0,w#ffffff,r#ff0000
  232. Target[$_[0]]: `perl -e "print \"0\n$_[1]\""`
  233. YLegend[$_[0]]: Bits per Second
  234. ShortLegend[$_[0]]: bps
  235. Legend1[$_[0]]:
  236. Legend2[$_[0]]: Traffic from $_[0]
  237. LegendI[$_[0]]:
  238. LegendO[$_[0]]: &nbsp;Traffic:
  239. END_OF_ENTRY
  240. }
  241. sub addr_to_net {
  242. # Returns the name/description of the network of the given address.
  243. # Addresses are looked up in @dstaddr first. If that fails, the address is returned.
  244. my($i, $dst);
  245. $dst= aton($_[0]);
  246. for ($i=0; $i < @dstaddr; $i++) {
  247. if ( ($dst & $dstmask[$i]) eq $dstaddr[$i] ) {
  248. return $dstdesc[$i];
  249. }
  250. }
  251. $_[0] =~ /(.*)..*/; # Assume Class C & strip off the last octet
  252. $1;
  253. }
  254. sub BEGIN {
  255. my ($lastaddr, $lastsrc);
  256. sub addr_to_src {
  257. # Returns the name/description of the given address.
  258. # Addresses are looked up in @srcaddr first. If there's no match, a dns lookup is tried.
  259. # If that fails, the address is returned.
  260. if ( $_[0] eq $lastaddr ) {
  261. return $lastsrc;
  262. } else {
  263. $lastaddr= $_[0];
  264. for (my $i=0; $i < @srcaddr; $i++) {
  265. if ($_[0] =~ /^$srcaddr[$i]$/ ) {
  266. $lastsrc= $srcdesc[$i];
  267. return $lastsrc;
  268. }
  269. }
  270. my $addr= aton($_[0]);
  271. if ($lastsrc= gethostbyaddr($addr, AF_INET)) {
  272. $lastsrc =~ s/.austrade.gov.au$//i;
  273. } else {
  274. $lastsrc= $_[0];
  275. }
  276. return $lastsrc;
  277. }
  278. }
  279. }
  280. sub aton {
  281. # I found the standard "inet_aton" very slow (on Windows).
  282. # Hence this version. It only handles dotted decimal addresses -
  283. # not names.
  284. $_[0] =~ /(d+).(d+).(d+).(d+)/;
  285. chr($1).chr($2).chr($3).chr($4);
  286. }