There's probably a few of you that have done similar things, but I thought I'd post my code here anyway for comparison/reference purposes (and to give the better programmers here a laugh).
The first script grabs the data produced by http://<routeros ip>/accounting/ip.cgi and dumps it into a DB_File (basically permanent hash of hashes). It picks out the relevant destination hosts (private networks such as 192.168, 10.2, 172.16 for example) so it doesn't record the zillion public hosts etc, you can easily change it to do so if you wish. I run this every so many minutes using cron, as long as you run it at least once an hour and before the MT router buffer fills, so for high traffic/many hosts you should run it often.
The smallest 'unit' is hours, you could make it minutes etc but expect the file to grow far more quickly.
The second script produces graphs based upon the data, I've attached an example output image.
I'll be adding more scripts to work with the data in different ways. Like I'll be making one to dump the contents of the DB_File into a spreadsheet so manual reports can be done, like finding the most heaviest users/hosts.
gather.pl:
Code: Select all
#!/usr/bin/perl -w
use strict;
use LWP::Simple;
use MLDBM 'DB_File';
use Time::Local;
my $arg0 = $ARGV[0];
my $arg1 = $ARGV[1];
my $ip_accounting_url="http://<routeros ip>/accounting/ip.cgi";
my $accounting_mldbm_data_db = "~/accounting_data.mldbm";
tie my %h, 'MLDBM', $accounting_mldbm_data_db or die $!;
my ($timestamp) = &time_stamp();
my $epoch = time();
# print "\n Epoch set to: $epoch\n";
&gather_ip_accounting($ip_accounting_url);
sub gather_ip_accounting {
my $url = $_[0];
my ($src, $dst, $bytes, $packets, $src_usr, $dst_usr);
foreach my $line (split(/\n/, get($url))) {
($src, $dst, $bytes, $packets, $src_usr, $dst_usr) = split(" ", $line);
# Change the following regex to suit your networks, should make this a var or something...
if ($dst && $dst =~ /(192\.168\.)|(10\.2\.)|(172\.16\.)/){
my $h_dst = $h{$dst . "_" . $timestamp};
$h_dst->{dst} = $dst;
# $h_dst->{src} = $src;
$h_dst->{bytes} += $bytes;
$h_dst->{packets} += $packets;
# $h_dst->{src_usr} = $src_usr;
$h_dst->{dst_usr} = $dst_usr;
$h_dst->{epoch} = $epoch;
$h{$dst . "_" . $timestamp} = $h_dst;
}
}
}
# use Data::Dumper;
# print Dumper(%h);
untie %h;
sub time_stamp {
my ($d_t);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon++;
$d_t = sprintf("%4d-%2.2d-%2.2d %2.2d:00:00",$year,$mon,$mday,$hour,$min,$sec);
return($d_t);
}
Code: Select all
#!/usr/bin/perl -w
use strict;
use LWP::Simple;
use MLDBM 'DB_File';
use Time::Local;
use GD::Graph::bars;
my ($num_values, $period_type);
if ($ARGV[0] && $ARGV[1]) {
if ($ARGV[0] =~ /\d+/) {
$num_values = $ARGV[0];
}
else {
print "\nIncorrect value supplied for number of units\n";
exit;
}
if ($ARGV[1] =~ /(hours)|(days)|(months)/) {
$period_type = $ARGV[1];
}
else {
print "\nIncorrect value supplied for type of units\n";
exit;
}
}
else {
print "\nUsage: period units\nPeriod: The number of values\nUnits: Hours, Days, Months\n\n";
exit;
}
print "\n Gathering $num_values $period_type worth of data from db!\n";
my $epoch = time();
my $accounting_mldbm_data_db = "~/accounting_data.mldbm";
my $graph_image_file = "~/accounting_data_" . $num_values . "_" . $period_type . "_" . $epoch . ".png";
tie my %h, 'MLDBM', $accounting_mldbm_data_db or die $!;
#else {
# &print_period_summary($arg0, $arg1);
#}
my($graphvalues, @graphvalues_tmp);
my $period_total = 0;
my $i = 0;
while ($i <= $num_values) {
# print "$i\n";
@graphvalues_tmp = &print_total($i, $period_type);
my $data = $graphvalues_tmp[0];
my $epoch = $graphvalues_tmp[1];
my $HMS = &epoch_to_MDHMS($epoch);
push @{$graphvalues->[0]}, $HMS;
push @{$graphvalues->[1]}, $data;
$period_total = $period_total + $data;
$i++;
}
my $graph = GD::Graph::bars->new(85*$num_values, 300);
$graph->set(
x_label => "$period_type (latest towards the left) Period Total: $period_total",
y_label => 'Mbytes',
title => "Total Mbytes (Over $num_values $period_type)",
transparent => '0',
show_values => '1',
bar_spacing => '2',
) or warn $graph->error;
my $image = $graph->plot($graphvalues) or die $graph->error;
open(IMG, ">$graph_image_file") or die $!;
binmode IMG;
print IMG $image->png;
# use Data::Dumper;
# print Dumper($graphvalues);
untie %h;
sub print_total {
my $h_total=0;
my ($h_row, $h_column, $h_bytes, $h_dst);
my ($num, $period) = @_;
my ($epoch_start, $epoch_end) = &epoch_period($num, $period);
for my $h_row ( keys %h ) {
if ($h{$h_row}{epoch} >= $epoch_start && $h{$h_row}{epoch} <= $epoch_end) {
$h_bytes = $h{$h_row}{bytes};
$h_dst = $h{$h_row}{dst};
$h_total = $h_total + $h_bytes;
}
}
my $formatted_total = sprintf("%.3f", $h_total/1024/1024);
return($formatted_total, $epoch_start);
}
sub epoch_period {
my ($past_count, $period) = @_;
my ($epoch_period_start, $epoch_period_end);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my ($start_hour, $end_hour);
if ($period eq "hours") {
$start_hour = $hour-$past_count;
$end_hour = $hour-$past_count;
}
elsif ($period eq "days") {
$mday = $mday-$past_count;
$start_hour = '00';
$end_hour = '23';
}
elsif ($period eq "months") {
$mon = $mon-$past_count;
# $mday = '00';
# $hour = '00';
}
$epoch_period_start = timelocal(00,00,$start_hour,$mday,$mon,$year);
# print "Start: $epoch_period_start\n";
$epoch_period_end = timelocal(59,59,$end_hour,$mday,$mon,$year);
# print "End: $epoch_period_end\n";
# print "SUB EPOCH_PERIOD: $epoch_period_start, $epoch_period_end\n";
return($epoch_period_start, $epoch_period_end);
}
sub epoch_to_MDHMS {
my $epoch = $_[0];
my ($sec, $min, $hour, $mday, $mon) = (localtime($epoch))[0,1,2,3,4];
my $mdhms = $mon+1 . "-" . $mday . " " . sprintf("%02d", $hour) . ":" . sprintf("%02d", $min) . ":" . sprintf("%02d", $sec);
return($mdhms);
}