#!/usr/bin/perl -w
#
# check_iftraffic_oid.pl - Interface monitoring plug-in written by Nicolas MOREAU
# 
# v1.01 : very minor code change
# v1.0: Initial release
#
# Adapted from:
# ###############################################################
# #check_iftraffic.pl - Nagios(r) network traffic monitor plugin#
# #Copyright (C) 2004 Gerd Mueller / Netways GmbH               #
# #$Id: check_iftraffic.pl 1119 2006-02-09 10:30:09Z gmueller $ #
# ###############################################################
#
#

use strict;

use Net::SNMP;
use Getopt::Long;
&Getopt::Long::config('bundling');
use Data::Dumper;

my $host_address;
my $iface_Txspeed;
my $iface_Rxspeed;
my $opt_h;
my $session;
my $error;
my @snmpoids;
my $response;

# Path to tmp files
my $TRAFFIC_FILE = "/tmp/traffic";

my %STATUS_CODE =
  ( 'UNKNOWN' => '3', 'OK' => '0', 'WARNING' => '1', 'CRITICAL' => '2' );

#default values;
my $port						= 161;
my $snmp_version				= 1;
my $COMMUNITY					= "public";
my $state						= "UNKNOWN";
my ( $in_value, $out_value )	= 0;
my $warn_usage					= 85;
my $crit_usage					= 98;
my $output						= "";
my $snmpIfInValue				= undef; #Total octets received on interface
my $snmpIfOutValue				= undef; #Total octets transmitted by interface
my $snmpRxSpeed					= undef; #Interface Rx speed
my $snmpTxSpeed					= undef; #Interface Tx speed
my $counterName					= undef;
my $oidDescr					= undef;
my $bits						= undef; 
my $inputUnits					= "B"; #Default in Bytes for counters
my $outputUnits					= "M";
my $RxTxUnit					= "b"; #Default in Bits for interface speed
my $outputScale					= "M";


#added 20050614 by mw
my $max_value;
my $max_bitss;

my $status = GetOptions(
	"h|help"        => \$opt_h,
	"H|hostname=s"  => \$host_address,	
	"n|counterName=s" => \$counterName,
	"s|oidDescr=s" => \$oidDescr,###oid
	"C|community=s" => \$COMMUNITY,
	"v|version=s" => \$snmp_version,
	"p|port=i"      => \$port,
	'b'		=> \$bits,
	'bits'		=> \$bits,	
	"R|snmpIfInOctets=s"	=> \$snmpIfInValue,###oid REQUIRED
	"T|snmpIfOutOctets=s"	=> \$snmpIfOutValue,###oid REQUIRED
	"I|inBandwidth=s" => \$snmpRxSpeed,###oid REQUIRED
	"O|outBandwidth=s" => \$snmpTxSpeed,###oid
	"m|RxTxUnit=s" => \$RxTxUnit,
	"e|inputUnits=s"	=> \$inputUnits,
	"f|outputScale=s"	=> \$outputScale,
	"w|warning=s"   => \$warn_usage,
	"c|critical=s"  => \$crit_usage
);

if ($opt_h) {
    print_usage();
    exit $STATUS_CODE{'OK'};
}
if (!$counterName && !$oidDescr){
	print "-n or -s option required \n";
	exit;
}
if ( !$snmpIfInValue or !$snmpIfOutValue or !$snmpTxSpeed){
	print "Missing some options. Check -R, -T and -I \n";
	exit;
}
if(!$snmpTxSpeed){
	$snmpTxSpeed = $snmpRxSpeed;
}
if (  $RxTxUnit ne ( "b" or "B" ) ){
	print "incorrect value provided for -m \n";
	exit;
}


# if($oidDescr){
	# if($oidDescr !~ "^\.([0-9]+\.)*([0-9])+" ){
		# stop("$state: $oidDescr is not an oid\n",$state);
	# }
# }

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

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

#snmp request
if($oidDescr){ #if -s provided
	push( @snmpoids, $oidDescr );
}
push( @snmpoids, $snmpIfInValue );
push( @snmpoids, $snmpIfOutValue );
push( @snmpoids, $snmpRxSpeed );
push( @snmpoids, $snmpTxSpeed );

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

	stop("WARNING: SNMP error: $answer\n", "WARNING");
}

if($oidDescr){ #if -s, counter name is oid result
	$counterName = $response->{ $oidDescr };	
}
$iface_Txspeed = $response->{ $snmpTxSpeed };
$iface_Rxspeed = $response->{ $snmpRxSpeed };
$out_value = $response->{ $snmpIfOutValue };
$in_value = $response->{ $snmpIfInValue };

$session->close;

#if interface capacity is provided in bytes convert to bits for calculation
if ($RxTxUnit eq "B"){ 
	$iface_Txspeed = $iface_Txspeed * 8;
	$iface_Rxspeed = $iface_Rxspeed * 8;
}
#if counter values are provided in bytes convert to bits for calculation
if ($inputUnits eq "B"){ 
	$out_value = $out_value * 8;
	$in_value = $in_value * 8;
}

my $row;
my $last_check_time = time - 1;
my $last_in_value   = $in_value;
my $last_out_value  = $out_value;

if (
	open( FILE,
		"<" . $TRAFFIC_FILE . "_if_" . $counterName . "_" . $host_address
	)
  )
{
	while ( $row = <FILE> ) {

		#cosmetic change 20050416 by mw
		#Couldn't sustain;-)
		( $last_check_time, $last_in_value, $last_out_value ) =
		  split( ":", $row );

		### by sos 17.07.2009 check for last_bits
		if ( ! $last_in_value  ) { $last_in_value=$in_value;  }
		if ( ! $last_out_value ) { $last_out_value=$out_value; }

		if ($last_in_value !~ m/\d/) { $last_in_value=$in_value; }
		if ($last_out_value !~ m/\d/) { $last_out_value=$out_value; }
	}
	close(FILE);
}

my $update_time = time;

open( FILE, ">" . $TRAFFIC_FILE . "_if_" . $counterName . "_" . $host_address )
  or die "Can't open $TRAFFIC_FILE for writing: $!";

printf FILE ( "%s:%.0ld:%.0ld\n", $update_time, $in_value, $out_value );
close(FILE);

# my $db_file;

#added 20050614 by mw
#Check for and correct counter overflow (if possible).
#See function counter_overflow.
$in_value  = counter_overflow( $in_value,  $last_in_value,  $max_value );
$out_value = counter_overflow( $out_value, $last_out_value, $max_value );

# Calculate traffic since last check (RX\TX)
my $in_traffic = sprintf( "%.2lf",
	( $in_value - $last_in_value ) / ( time - $last_check_time ) );
my $out_traffic = sprintf( "%.2lf",
	( $out_value - $last_out_value ) / ( time - $last_check_time ) );

# Calculate usage percentages
my $in_usage  = sprintf( "%.2f", ( 1.0 * $in_traffic * 100 ) / $iface_Rxspeed );
my $out_usage = sprintf( "%.2f", ( 1.0 * $out_traffic * 100 ) / $iface_Txspeed );

#calculate dynamic scale for screen output
my $in_usage_display;
my $out_usage_display;
my $in_autoscale;
my $out_autoscale;
if ( !$bits ){
	if ( $in_traffic < 1024 ){
		$in_usage_display = sprintf( "%.0f", $in_traffic / 8 );
		$in_autoscale = "octets/s";
	}
	if ( $out_traffic < 1024 ){
		$out_usage_display = sprintf( "%.0f", $out_traffic / 8 );
		$out_autoscale = "octets/s";
	}
	if ( $in_traffic >= 1024 && $in_traffic < 1048576 ){
		$in_usage_display = sprintf( "%.2f", $in_traffic / 8192 );
		$in_autoscale = "Kio/s";
	}
	if ( $out_traffic >= 1024 && $out_traffic < 1048576){
		$out_usage_display = sprintf( "%.2f", $out_traffic / 8192 );
		$out_autoscale = "Kio/s";
	}
		if ( $in_traffic >= 1048576 && $in_traffic < 1073741824 ){
		$in_usage_display = sprintf( "%.2f", $in_traffic / 8388608 );
		$in_autoscale = "Mio/s";
	}
	if ( $out_traffic >= 1048576 && $out_traffic < 1073741824){
		$out_usage_display = sprintf( "%.2f", $out_traffic / 8388608 );
		$out_autoscale = "Mio/s";
	}
	if ( $in_traffic >= 1073741824 ){
		$in_usage_display = sprintf( "%.2f", $in_traffic / 8589934592 );
		$in_autoscale = "Gio/s";
	}
	if ( $out_traffic >= 1073741824){
		$out_usage_display = sprintf( "%.2f", $out_traffic / 8589934592 );
		$out_autoscale = "Gio/s";
	}
}
else {
	if ( $in_traffic < 1024 ){
		$in_usage_display = sprintf( "%.0f", $in_traffic );
		$in_autoscale = "Bits/s";
	}
	if ( $out_traffic < 1024 ){
		$out_usage_display = sprintf( "%.0f", $out_traffic );
		$out_autoscale = "Bits/s";
	}
	if ( $in_traffic >= 1024 && $in_traffic < 1048576 ){
		$in_usage_display = sprintf( "%.2f", $in_traffic / 1024 );
		$in_autoscale = "Kib/s";
	}
	if ( $out_traffic >= 1024 && $out_traffic < 1048576){
		$out_usage_display = sprintf( "%.2f", $out_traffic / 1024 );
		$out_autoscale = "Kib/s";
	}
	if ( $in_traffic >= 1048576 && $in_traffic < 1073741824 ){
		$in_usage_display = sprintf( "%.2f", $in_traffic / 1048576 );
		$in_autoscale = "Mib/s";
	}
	if ( $out_traffic >= 1048576 && $out_traffic < 1073741824){
		$out_usage_display = sprintf( "%.2f", $out_traffic / 1048576 );
		$out_autoscale = "Mib/s";
	}
	if ( $in_traffic > 1073741824 ){
		$in_usage_display = sprintf( "%.2f", $in_traffic / 1073741824 );
		$in_autoscale = "Gib/s";
	}
	if ( $out_traffic > 1073741824){
		$out_usage_display = sprintf( "%.2f", $out_traffic / 1073741824 );
		$out_autoscale = "Gib/s";
	}
}

#Calculate perfdatas 
my $scale;
if ( $outputScale eq "B" ) {
	if ( !$bits ){
		$in_traffic = sprintf( "%.2f", $in_traffic / 8 );
		$out_traffic = sprintf( "%.2f", $out_traffic / 8 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 8 );
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 8 );
		$scale = "Bytes/s";
	}
	else{
		$scale = "Bits/s";
	}
}
if ( $outputScale eq "K" ) {
	if ( !$bits ){
		$in_traffic = sprintf( "%.2f", $in_traffic / 8192 ); #/8 /1024
		$out_traffic = sprintf( "%.2f", $out_traffic / 8192 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 8192 ); 
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 8192 );
		$scale = "Kio/s";
	}
	else{
		$in_traffic = sprintf( "%.2f", $in_traffic / 1024 );
		$out_traffic = sprintf( "%.2f", $out_traffic  / 1024 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 1024 );
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 1024 );
		$scale = "Kib/s";
	}
}
if ( $outputScale eq "M" ) {
	if ( !$bits ){
		$in_traffic = sprintf( "%.2f", $in_traffic / 8388608 ); #/8 / 1024 / 1024
		$out_traffic = sprintf( "%.2f", $out_traffic / 8388608 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 8388608 );
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 8388608 );
		$scale = "Mio/s";
	}
	else{
		$in_traffic = sprintf( "%.2f", $in_traffic / 1048576 ); # /1024 /1024
		$out_traffic = sprintf( "%.2f", $out_traffic / 1048576 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 1048576 );
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 1048576 );
		$scale = "Mib/s";
	}
}
if ( $outputScale eq "G" ) {
	if ( !$bits ){
		$in_traffic = sprintf( "%.2f", $in_traffic / 8589934592 ); #/8 / 1024 / 1024 /1024
		$out_traffic = sprintf( "%.2f", $out_traffic / 8589934592 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 8589934592 );
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 8589934592 );
		$scale = "Gio/s";
	}
	else{
		$in_traffic = sprintf( "%.2f", $in_traffic / 1073741824 ); # /1024 /1024 /1024
		$out_traffic = sprintf( "%.2f", $out_traffic / 1073741824 );
		$iface_Txspeed = sprintf( "%.2f", $iface_Txspeed / 1073741824 );
		$iface_Rxspeed = sprintf( "%.2f", $iface_Rxspeed / 1073741824 );
		$scale = "Gib/s";
	}
}

$state = "OK";

$output = 	"Average IN: ". $in_usage_display . $in_autoscale . " (" . $in_usage . "%), " 
			. "Average OUT: " . $out_usage_display . $out_autoscale . " (" . $out_usage . "%)<br>";
$output .= "Rx Capacity: ".$iface_Rxspeed . $scale .", Tx Capacity: ".$iface_Txspeed. $scale ."";


# Changed 20091209 gj
if ( ( $in_usage > $warn_usage ) or ( $out_usage > $warn_usage ) && $state eq "OK" ){
	$state = "WARNING";
}
if ( ( $in_usage > $crit_usage ) or ( $out_usage > $crit_usage ) ) {
	$state = "CRITICAL";
}

# Changed 20091209 gj
# $output = "$state - $output"
# if ( $state ne "OK" );

# Changed 20091214 gj - commas should have been semi colons
$output .= "|Rx_Bandwidth=". $in_traffic . $scale . ";;;; Tx_Bandwidth=".$out_traffic. $scale 
			. ";;;; Rx_Capacity=".$iface_Rxspeed. $scale .";;;; Tx_Capacity=".$iface_Txspeed. $scale .";;;;";

stop($output, $state);



#added 20050414 by mw
#This function detects if an overflow occurs. If so, it returns
#a computed value for $octets.
#If there is no counter overflow it simply returns the origin value of $octets.
#IF there is a Counter reboot wrap, just use previous output.
sub counter_overflow {
	my ( $bytes, $last_bytes, $max_bytes ) = @_;

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

# Added 20100202 by gj
# Print results and exit script
sub stop {
	my $result = shift;
	my $exit_code = shift;
	print $result . "\n";
	exit ( $STATUS_CODE{$exit_code} );
}


sub print_usage {
	print <<EOU;
	
	*******************************************************
	* Nagios interface monitoring plugin                  * 
	* Select speed/bandwith by their OID                  *
	* Originaly developped to check my UBIQUITI backhauls *
	* But you could use it to monitor other materials     *
	*******************************************************
	
    Usage: check_iftraffic_oid.pl -H host [ -C community ] [ -v version] [ -p port] -b [ -n counter_name ] [ -R oid_string ] [ -T oid_string ] [ -e input_unit ] [ -I oid_string ] [ -O oid_string ] [ -m RxTx_unit] [ -f output_scale] [ -w warn ] [ -c crit ]

	Check with a MIB explorer if values provided by -I, -O  -R and -T are in bits or Bytes to set -e andd -m options.

    Options:

    -H, --host STRING or IPADDRESS
        Check interface on the indicated host.
    -b, --bits
        Display results in bits per second b/s (default: Bytes/s)
    -C, --community STRING 
        SNMP Community.
    -v  --version STRING
        default snmp v1
    -p --port INTEGER
        snmp port
    -n, --counterName STRING
        Unique identifier of your choice for this counter. Required if you don't use -s
    -s --oidDescr
        oid where you can find interface description. Required if you don't use -n.
        Overwrite -n option
    -R --snmpIfInOctets STRING
        oid for RX counter
    -T --snmpIfOutOctets STRING
        oid for TX counter
    -e, --inputUnits STRING
        b=bits, B=Bytes
        Units of collected datas by -I and -O options. Default, bits
    -I, --inBandwidth STRING
        SNMP OID in which Rx capacity can be found.  Applied to 
        both IN and OUT if no second (-O) max speed is provided.
    -O, --outBandwidth STRING
        SNMP OID in which Tx capacity can be found		
    -m --RxTxUnit
        b=bits/s, B=Bytes/s
        unit of Rx/Tx capacity (default bits).
    -f, --outputScale
        G=giga,M=mega,K=kilo,B=stay in bits or Bytes (according to -b option). Default, mega.
        Scale of perfdata result (scale of your graph).
    -w, --warning INTEGER
        % of bandwidth usage necessary to result in warning status (default: 85%)
    -c, --critical INTEGER
        % of bandwidth usage necessary to result in critical status (default: 98%)
EOU

}

