#!/usr/bin/perl -w
#
# check_trafficlimit - Nagios(r) network trafficaccounting monitor plugin
#
# Copyright (c) 2004 NETWAYS GmbH, < http://www.netways.de >
# Author: Marius Hein < mhein(at)netways.de >, Gerd Müller < gmueller(at)netways.de
# $Id: check_trafficlimit.pl 1134 2006-02-23 12:17:36Z gmueller $
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307

# Main Stuff...
use strict;
use Net::SNMP;
use vars qw (
    $VERSION
    %ENV
    $TRAFFIC_FILE
    $SNMPWALK
    $SNMPGET
    $COMMUNITY
    $host_address
    $iface_number
    $iface_description
    $row
    @last_values
    $last_check_time
    $last_in_bytes
    $last_out_bytes
    $update_time
    $opt_help
    $opt_reset
    $count
    $opt_period
    $last_day_count
    $day_different
    @date_now
    @date_last
    $PROGNAME
    $OUT
);


sub print_usage();
use File::Basename;
#use Date::Manip;
use Getopt::Long;

# Main configuration
$VERSION      = "0.01a";
$PROGNAME = basename($0);
$ENV{TZ} = 'EST';
$ENV{'PATH'}='';
$ENV{'BASH_ENV'}=''; 
$ENV{'ENV'}=''; 

my $session;
my $error;
my $snmp_version = 1;

# File Prefix
$TRAFFIC_FILE = "/tmp/tmp_check_traffic_limit";

# SNMP stuff:
$SNMPWALK     = "/usr/bin/snmpwalk";
$SNMPGET      = "/usr/bin/snmpget";
$COMMUNITY    = "public";

# Nagios Status Values
my %STATUS_CODE = (
                    'UNKNOWN'  => '-1',
                    'OK'       => '0',
                    'WARNING'  => '1',
                    'CRITICAL' => '2'
);

my @snmpoids;

# SNMP OIDs for Traffic
my $snmpIfInOctets  = '1.3.6.1.2.1.2.2.1.10';
my $snmpIfOutOctets = '1.3.6.1.2.1.2.2.1.16';
my $snmpIfDescr     = '1.3.6.1.2.1.2.2.1.2';

my $response;

# Default values
my ($in_bytes,$out_bytes) = 0;
my $warn_usage = 1.0;
my $crit_usage = 2.0;
my $max_bytes = 419304;    # the value is (2^32/1024)


# Help/Usage sub...
sub print_usage()
{
 print "Usage: $PROGNAME -H host -i if_number -p period [ -w warn ] [ -c crit ]\n\n";
 print "Options:\n";
 print " -H, --host STRING or IPADDRESS\n";
 print "    Check interface on the indicated host.\n\n";
 print " -i, --interface INTEGER\n";
 print "    Interface number assigned by SNMP agent.\n\n";
 print " -w, --warning INTEGER\n";
 print "    Gb of total period traffic for warning results\n\n";
 print " -c, --critical INTEGER\n";
 print "    Gb of total period traffic for critical results\n\n";
 print " -p  --period CHARACTER\n";
 print "    timeperiod to check (d=per day,m=per month)\n\n";
 print " -h, --help\n";
 print "    Display this screen.\n\n";
 print " --reset\n";
 print "    Resets the counter manualy!\n\n";
 exit($STATUS_CODE{"UNKNOWN"});
}

sub fetch_ifdescr {
	my $state;
	my $response;

	my $snmpkey;
	my $answer;
	my $key;

	my ( $session, $ifdescr ) = @_;

	if ( !defined( $response = $session->get_table($snmpIfDescr) ) ) {
		$answer = $session->error;
		$session->close;
		$state = 'CRITICAL';
		$session->close;
		exit $STATUS_CODE{$state};
	}

	foreach $key ( keys %{$response} ) {
		if ( $response->{$key} =~ /^$ifdescr$/ ) {
			$key =~ /.*\.(\d+)$/;
			$snmpkey = $1;

			# print "$ifdescr = $key / $snmpkey \n";  #debug
		}
	}
	unless ( defined $snmpkey ) {
		$session->close;
		$state = 'CRITICAL';
		printf "$state: Could not match $ifdescr \n";
		exit $STATUS_CODE{$state};
	}
	return $snmpkey;
}

# - Initial arguments parsing
Getopt::Long::Configure('bundling');
GetOptions (
                "H=s"           =>  \$host_address,
                "host=s"          =>  \$host_address,
                
                "C=s"           =>  \$COMMUNITY,
                "community=s"   =>  \$COMMUNITY,
                
                "i=s"           =>  \$iface_number,
                "interface=s"   =>  \$iface_number,
                
                "w=s"           =>  \$warn_usage,
                "warning=s"     =>  \$warn_usage,
                
                "c=s"           =>  \$crit_usage,
                "critical=s"    =>  \$crit_usage,
                
                "p=s"           =>  \$opt_period,
                "period=s"      =>  \$opt_period,
                
                "reset"         =>  \$opt_reset,
                
                "h"             =>  \$opt_help,
                "help"          =>  \$opt_help,
           ) or die "Too few arguments. Try '$PROGNAME --help' for more information\n";

# HelpArgument
if ($opt_help) {
    print_usage();
}

# Checking the Arguments
if((!$host_address) or (!$iface_number) or (!$crit_usage>0) or (!$warn_usage>0) or (!$opt_period)) 
{ 
    print "Too few arguments. Try '$PROGNAME --help' for more information\n";
    exit($STATUS_CODE{"UNKNOWN"});
}

if ( $snmp_version =~ /[12]/ ) {
	( $session, $error ) = Net::SNMP->session(
		-hostname  => $host_address,
		-community => $COMMUNITY,
		-port      => 161,
		-version   => $snmp_version
	);

	if ( !defined($session) ) {
		print("UNKNOWN: $error");
		exit $STATUS_CODE{'UNKNOWN'};
	}
}
elsif ( $snmp_version =~ /3/ ) {
	my $state = 'UNKNOWN';
	print("$state: No support for SNMP v3 yet\n");
	exit $STATUS_CODE{$state};
}
else {
	my $state = 'UNKNOWN';
	print("$state: No support for SNMP v$snmp_version yet\n");
	exit $STATUS_CODE{$state};
}

$iface_number = fetch_ifdescr( $session, $iface_number );


push( @snmpoids, $snmpIfInOctets . "." . $iface_number );
push( @snmpoids, $snmpIfOutOctets . "." . $iface_number );

if ( !defined( $response = $session->get_request(@snmpoids) ) ) {
	my $answer = $session->error;
	$session->close;

	print("WARNING: SNMP error: $answer\n");
	exit $STATUS_CODE{'WARNING'};
}

$in_bytes  = $response->{ $snmpIfInOctets . "." . $iface_number } / 1024;
$out_bytes = $response->{ $snmpIfOutOctets . "." . $iface_number } / 1024;


# ReadLastValues from File...

$last_check_time=time;
$last_in_bytes=$in_bytes;
$last_out_bytes=$out_bytes;
$count=0;

if(open(FILE,"<".$TRAFFIC_FILE."_if".$iface_number."_".$host_address)) {
while($row = <FILE>)
{
 @last_values = split(":",$row);
 $last_check_time = $last_values[0];
 $last_in_bytes = $last_values[1];
 $last_out_bytes = $last_values[2];
 $count = $last_values[3];
}
close(FILE);
}

$in_bytes  = counter_overflow( $in_bytes,  $last_in_bytes,  $max_bytes );
$out_bytes = counter_overflow( $out_bytes, $last_out_bytes, $max_bytes );
$count=$count+$in_bytes+$out_bytes;

$update_time = time;
@date_now = localtime(time);
@date_last = localtime($last_check_time);

# Debug the to day values...
# print $date_now[3] . " - " . $date_last[3]. "\n";

my $offset=3;
if($opt_period eq "m") {
	$offset=4;
}

if ($date_now[$offset] ne $date_last[$offset] || $opt_reset) {
    # Debug if reset the lastdaycounter.
    print "Last period count reset, starting new...\n";
}

# writing new configstuff to file...
open(FILE,">".$TRAFFIC_FILE."_if".$iface_number."_".$host_address) or die "Can't open $TRAFFIC_FILE for writing: $!";
print FILE "$update_time:$in_bytes:$out_bytes:$count";
close(FILE);

# Calculating some values for humanreading...
$count = sprintf("%.4f", (($in_bytes+$out_bytes)  / 1024 / 1024) );
$in_bytes = sprintf("%.2f",($in_bytes)/1024);
$out_bytes = sprintf("%.2f",($out_bytes)/1024);

# Making the Pluginoutput
# UUUU Critical...
$OUT="OK";

if ($count >= $crit_usage) {
    $OUT .= "CRITICAL";
}
# MMM Warning...
elsif ($count >= $warn_usage) {
    $OUT .= "WARNING";
}

print $OUT.": $count Gb (WARN=$warn_usage Gb; CRIT=$crit_usage Gb)|counter=$count,$warn_usage,$crit_usage\n";

exit ($STATUS_CODE{$OUT});

sub counter_overflow {
        my ( $bytes, $last_bytes, $max_bytes ) = @_;

        $bytes += $max_bytes if ( $bytes < $last_bytes );
        $bytes = 0 if ( $bytes < $last_bytes );
        return $bytes;
}

