#!/usr/bin/perl

#maybe call this analyze_traffic

# todo: add a "max" setting, for example, if goes up over
# 1406 KB in one hour or more than one hour (plus few moments) between,
# then it must be too much!
# todo: show total for month, show average month
# todo: show average hour (and total hours)
# todo: show average day (and total days)
# todo: show last 24 hours totals
# todo: doesn't show real ending time; need to make separate function for times

# reed@reedmedia.net
# started 03/Oct/2001

# 04/Oct/2001 For per day results show average per day instead of
#             just accumulatives.
# 05/Oct/2001 Fixed average bytes so it wouldn't show large fraction,
#             like 983.726495726496.
#             Show per day results and totals for everything.
#             Clean up formatting.
#             New logfile format.
# 09/Oct/2001 Made sure that hourly or daily amounts aren't showing previous
#             hour or day by using the time in between the previous time
#             and current time.
#             Added options to choose if you want to analyze new data.

#timestamp interface received transmitted

$debug = '0';

# These two options (if set to 1) will include the amounts
# from the first time it was logged and if the transfer amounts are reset.
# This can scew the results, i.e. the averages may be lower than 100%
# or one hour or one day may have huge amounts because they represent
# previous transfers (that were not logged)
$allow_first_entry_in_reports = '0'; # for hourly, daily
$allow_first_entry_in_totals = '0';

$bytes_only = '0'; # don't show in kilo, mega, or gigabytes

@months = ("January","February","March","April","May","June",
    "July","August","September","October","November","December");

while ($line = <>) {
  chomp ($line);

  $interface = '';

  ($timestamp, $interface, $t_rec_bytes, $t_trans_bytes) = split (/\s+/, $line);

  if ($debug) {
    print "t: $timestamp i: $interface r: $t_rec_bytes t: $t_trans_bytes\n";
  }

  if ($timestamp !~ /^\d+$/) {
    print "Invalid timestamp! $timestamp\n";
    next; # or should I die ?
  }

  if (! $prev_time{$interface}) {
    $prev_time{$interface} = $timestamp;
    $prev_day{$interface} = $timestamp;
    $prev_hour{$interface} = $hour;
    $this_time = $timestamp;
  }
  else {

    # Need to report for correct hour or day; i.e. if the timestamp is
    # for midnight, then it will report for the 0 hour of the current day
    # for the traffic that was really from previous hour and day.
    # maybe this will help:

    $this_time = $timestamp - int (($timestamp - $prev_time{$interface}) / 2);
  }

  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($this_time);
  if ($sec < 10) {
     $ssec = "0$sec";
  }
  else {
     $ssec = $sec;
  }
  if ($min < 10) {
     $smin = "0$min";
  }
  else {
     $smin = $min;
  }
  if ($hour < 10) {
     $shour = "0$hour";
  }
  else {
     $shour = $hour;
  }
  if ($mon < 10) {
     $smon = "0$mon";
  }
  else {
     $smon = $mon;
  }
  if ($mday < 10) {
     $smday = "0$mday";
  }
  else {
     $smday = $mday;
  }

  $month = ($mon + 1);
  $year = $year + 1900;

  if (! $first_date) {
    $first_date ="$months[$mon] $smday, $year $shour\:$smin\:$ssec $timezone";
    $first_mon = $mon;
    $first_mday = $mday;
    $first_year = $year;
  }

  # basically this is a check to make sure the format was correct
  # but doesn't check if valid
  if (! $interface) {
    print "Bad log -- no interface name!\n";
    next; # or should I die
  }

  if ($t_rec_bytes >= $prev_rec_bytes{$interface}) {
    if ($allow_first_entry_in_totals || $prev_rec_bytes{$interface} != 0) {
      $rec_bytes = $t_rec_bytes - $prev_rec_bytes{$interface};
    }
    else {
      $rec_bytes = 0; # start at 0
    }
    $went_back = 0;
  }
  else {
    if ($debug) {
      print "went back on ",
       "$months[$mon] $smday, $year $shour\:$smin\:$ssec $timezone ",
       "new: $t_rec_bytes last: $prev_rec_bytes{$interface}\n";
    }
    $rec_bytes = $t_rec_bytes;
    $prev_day{$interface} = $timestamp;
    if (! $allow_first_entry_in_reports) {
      $went_back = 1;
    }
  }

  if ($t_trans_bytes >= $prev_trans_bytes{$interface}) {
    if ($allow_first_entry_in_totals || $prev_trans_bytes{$interface} != 0) {
      $trans_bytes = $t_trans_bytes - $prev_trans_bytes{$interface};
    }
    else {
      $trans_bytes = 0; # start at zero
    }
    $went_back = 0;
  }
  else {
    if ($debug) {
      print "went back on ",
       "$months[$mon] $smday, $year $shour\:$smin\:$ssec $timezone ",
       "new: $t_rec_bytes last: $prev_rec_bytes{$interface}\n";
    }
    $trans_bytes = $t_trans_bytes;
    $prev_day{$interface} = $timestamp;
    if (! $allow_first_entry_in_reports) {
      $went_back = 1;
    }
  }

  $total_rec_bytes{$interface} += $rec_bytes;
  $total_trans_bytes{$interface} += $trans_bytes;

  if (! $went_back) {
    $rec_hour{$interface}{$hour} += $rec_bytes;
    $trans_hour{$interface}{$hour} += $trans_bytes;

    # count each so we can show averages per one day
    if ($timestamp >= $prev_day{$interface} + 86400) {
      if ($debug) {
        print "prev day: $prev_day{$interface} this_time: $this_time cur: $timestamp\n";
      }
      $count_mday{$interface}{$mday}++;
      $count_wday{$interface}{$wday}++;
      $prev_day{$interface} = $timestamp;
    } 
    if (($timestamp >= $prev_time{$interface} + 3600) ||
        ($prev_hour{$interface} != $hour)) {
      if ($debug) {
        print "prev: $prev_time{$interface} cur: $timestamp int: $interface h: $hour\n";
      }
      $count_hour{$interface}{$hour}++; # hour is really between prev and current
      $prev_time{$interface} = $timestamp;
      $prev_hour{$interface} = $hour;
    }

    $rec_mday{$interface}{$mday} += $rec_bytes;
    $trans_mday{$interface}{$mday} += $trans_bytes;
    $rec_wday{$interface}{$wday} += $rec_bytes;
    $trans_wday{$interface}{$wday} += $trans_bytes;

  }

  $prev_rec_bytes{$interface} = $t_rec_bytes;
  $prev_trans_bytes{$interface} = $t_trans_bytes;

} # looping through each line in log file

@days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");

print "Network Device Traffic Analysis\n";
print " From logging started $first_date\n";
print " and ended $months[$mon] $smday, $year $shour\:$smin\:$ssec $timezone\n";

if ($first_mon != $mon ||
    $first_mday != $mday ||
    $first_year != $year) {

  $accumulated = '1';
  print " These are accumulated results for multiple days.\n";
}

print "\n";

foreach $interface (keys %total_rec_bytes) {
  print "$interface total received: ",
        print_bytes($total_rec_bytes{$interface}),
        " ($total_rec_bytes{$interface} bytes)\n";
  print "$interface total transmitted: ",
        print_bytes($total_trans_bytes{$interface}),
        " ($total_trans_bytes{$interface} bytes)\n";
} # for each interface

foreach $interface (keys %total_rec_bytes) {

  print "\nAverage by hour per day for $interface (accumulated totals)\n",
        "      $interface received",
        "                        $interface transmitted\n";
  for ($h = 0; $h < 24; $h++) {
    if (! $count_hour{$interface}{$h}) {
      $count_hour{$interface}{$h}++; # at least one
    }
    if ($debug) {
      print "$h -- $count_hour{$interface}{$h}\n";
      print "total: $total_rec_bytes{$interface} $total_trans_bytes{$interface}\n";
    }

    if ($count_hour{$interface}{$h} > 0 &&
       $total_rec_bytes{$interface} > 0 &&
       $total_trans_bytes{$interface} ) {

      printf ("%-3s  %6.2f%% %12s %-14s  %6.2f%% %12s %-14s\n",
          $h . ':',
          ($rec_hour{$interface}{$h} / $total_rec_bytes{$interface} * 100),
          print_bytes($rec_hour{$interface}{$h} / $count_hour{$interface}{$h}),
          '(' . print_bytes($rec_hour{$interface}{$h}) . ')',
          ($trans_hour{$interface}{$h} / $total_trans_bytes{$interface} * 100),
          print_bytes($trans_hour{$interface}{$h} /
                      $count_hour{$interface}{$h}),
          '(' . print_bytes($trans_hour{$interface}{$h}) . ')' );
    }
  }

  # don't show results for multiple days, if only one day's logs are available
  if ($accumulated) {

    print "\nAverage by day of the month for $interface (accumulated totals)\n",
          "      $interface received",
          "                        $interface transmitted\n";
    for ($d = 1; $d <= 31; $d++) {

      if (! $count_mday{$interface}{$d}) {
        $count_mday{$interface}{$d}++; # at least one
      }
      if ($debug) {
        print "$h -- count: $count_mday{$interface}{$d}\n";
        print "total: $total_rec_bytes{$interface} $total_trans_bytes{$interface}\n";
      }
      if ($count_mday{$interface}{$d} > 0 &&
          $total_rec_bytes{$interface} > 0 &&
          $total_trans_bytes{$interface} ) {

        printf ("%-3s  %6.2f%% %12s %-14s  %6.2f%% %12s %-14s\n",
             $d . ':',
            ($rec_mday{$interface}{$d} / $total_rec_bytes{$interface} * 100),
            print_bytes($rec_mday{$interface}{$d} /
                        $count_mday{$interface}{$d}),
            '(' . print_bytes($rec_mday{$interface}{$d}) . ')',
            ($trans_mday{$interface}{$d} / $total_trans_bytes{$interface}* 100),
            print_bytes($trans_mday{$interface}{$d} /
                        $count_mday{$interface}{$d}),
            '(' . print_bytes($trans_mday{$interface}{$d}) . ')' );
      }
    }

    print "\nAverage by day of the week for $interface (accumulated totals)\n",
          "      $interface received",
          "                        $interface transmitted\n";
    for ($d = 0; $d < 7; $d++) {

      if (! $count_wday{$interface}{$d}) {
        $count_wday{$interface}{$d}++; # at least one
      }
      if ($debug) {
        print "$h -- $count_wday{$interface}{$d}\n";
        print "total: $total_rec_bytes{$interface} $total_trans_bytes{$interface}\n";
      }

      if ($count_wday{$interface}{$d} > 0 &&
          $total_rec_bytes{$interface} > 0 &&
          $total_trans_bytes{$interface} ) {

        printf ("%-3s: %6.2f%% %12s %-14s  %6.2f%% %12s %-14s\n",
             $days[$d],
             ($rec_wday{$interface}{$d} / $total_rec_bytes{$interface} * 100),
             print_bytes($rec_wday{$interface}{$d} /
                         $count_wday{$interface}{$d}), 
             '(' . print_bytes($rec_wday{$interface}{$d}) . ')', 
             ($trans_wday{$interface}{$d} / $total_trans_bytes{$interface}*100),
             print_bytes($trans_wday{$interface}{$d} /
                         $count_wday{$interface}{$d}), 
             '(' . print_bytes($trans_wday{$interface}{$d}) . ')' );
      }
    }

  } # only show multi-day results if logs contained multiple days

} # for each interface

sub print_bytes {

  local $bytes;

  if ($_[0] == 0) {
    $bytes = '0 bytes';
  }
  elsif ($bytes_only || ($_[0] < 1024)) {
    if (int($_[0]) == $_[0]) {
      $bytes = sprintf ("%.0f bytes", $_[0]);
    }
    else {
      $bytes = sprintf ("%.1f bytes", $_[0]);
    }
  }
  elsif ($_[0] < 1024000) { # was 1048576, but decided to show MB vs 1000+KB
#  elsif ($_[0] < 1048576) {
    $bytes = sprintf ("%.2f KB", ($_[0]/1024));
  }
  elsif ($_[0] < 1048600000 ) { # was 1073741824
    $bytes = sprintf ("%.2f MB", ($_[0]/1048576));
  }
  else {
    $bytes = sprintf ("%.2f GB", ($_[0]/1073741824));
  }

  return ($bytes);

}

print "\n This is free open source software. http://www.reedmedia.net/\n";
