#!/usr/bin/perl -w
#
# check_iftraffic5.pl - Nagios(r) network traffic monitor plugin
# Copyright (C) 2012 Mike Pagano
# Based on Gerd Mueller's check_iftraffic.pl
# Contributions by: Adrian Wieczorek, Ektanoor, Markus Werner, Greg Frater and Richard Walraven
#
# Remarks:
#
#	* Supports all Cisco standard interfaces: TenGigabitEthernet, GigabitEthernet, FastEthernet, 
#		Ethernet, Port-channel, Vlan. Also works on standard NIC adaptors.
#	* Checks input/output traffic, unicast pkts, multicast pkts, broadcast pkts, errors and discards on a single interface.
#	* Checks Port-channel member status by comparing port-channel speed to number.
#	* Performance data formatting meets Nagios standards.
#		Provides stats for bandwidth by percentage and integer values.
#		Packets in/out for unicast, multicast & broadcast.
#		Number of in/out packet discards & errors.
#		Designed specifically for charting with "Splunk for Nagios". 
#		Other reporting programs can use perfdata. IE RRD.
# 	* Checks for interface administrative and operational status up/down. 
#		Down on either returns a critical state.
# 	* Allows either textual or the numeric index value of an interface.
#	* If the interface speed is not specified on the command line it gets it automatically from IfSpeed.
#	* Bandwidth options allow checks of: 
#		Asymmetrical links such as a DSL line or cable modem where the download and upload speeds are different.
#		Minimum input bandwidth useful for IPSEC tunnels.
#	* Can determine interface to query by matching IP address of host with entry
#		 in ipAdEntIfIndex (.1.3.6.1.2.1.4.20.1.2).
#	* Bits per second are the default now. Use -B for bytes.
#	* RX, TX and absolute values show bytes/octets only.
#	* SNMP version 2 as we need 64-bit counters. The 32-bit overflows too fast and too frequently.
# 	* Automatic detection of snmp overflows generates a warning state. Max value used -M.
#	* Perl's given-where "switch" in some places. Look for the correct "use" for your version of perl.
# 	* Supports Perl 5.10
#	* -h displays help information to see avaliable options.
#
#	Final note:
#	Version 5 of check_iftraffic.pl works but it can be made a lot better. 
#	Send us bug reports, questions and comments about this plugin.
#	Latest version of this software: http://www.nagiosexchange.org
#
#	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
#
#For perl 5.10
#use feature qw(switch say);
#For older versions of perl
#use Switch;

use strict;

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

use Data::Dumper;

my $host_ip;
my $host_address;
my $iface_number;
my $iface_descr;
my $iface_speed;
my $iface_speedOut;
my $index_list;
my $opt_h;
my $units;
my $pcstate = "";
my $iface_pcspeed = 0;
my $iface_pctest = 0;
my $session;
my $error;
my $port         = 161;
my $snmp_version = 2;

my @snmpoids;

# SNMP OIDs for Admin & Operational Status, Traffic, Discard & Error counters
#Older 32-bit counters:
#my $snmpIfHCOutOctets 	= '1.3.6.1.2.1.2.2.1.16'; 32-bit ifOutOctets. No longer used.
#my $snmpIfHCInOctets  	= '1.3.6.1.2.1.2.2.1.10'; 32-bit ifInOctets. No longer used.
#my $snmpIfSpeed     	= '1.3.6.1.2.1.2.2.1.5';  32-bit ifSpeed. No longer used.
my $snmpIfAdminStatus 	= '1.3.6.1.2.1.2.2.1.7';
my $snmpIfOperStatus 	= '1.3.6.1.2.1.2.2.1.8';
my $snmpIfDescr     	= '1.3.6.1.2.1.2.2.1.2';
my $snmpIfInDiscards   	= '1.3.6.1.2.1.2.2.1.13';
my $snmpIfInErrors     	= '1.3.6.1.2.1.2.2.1.14';
my $snmpIfOutDiscards  	= '1.3.6.1.2.1.2.2.1.19';
my $snmpIfOutErrors    	= '1.3.6.1.2.1.2.2.1.20';
my $snmpIPAdEntIfIndex 	= '1.3.6.1.2.1.4.20.1.2';
# Newer 64-bit counters:
my $snmpIfHCInOctets			= '1.3.6.1.2.1.31.1.1.1.6';
my $snmpIfHCInUcastPkts			= '1.3.6.1.2.1.31.1.1.1.7';
my $snmpIfHCInMulticastPkts		= '1.3.6.1.2.1.31.1.1.1.8';
my $snmpIfHCInBroadcastPkts		= '1.3.6.1.2.1.31.1.1.1.9';
my $snmpIfHCOutOctets			= '1.3.6.1.2.1.31.1.1.1.10';
my $snmpIfHCOutUcastPkts		= '1.3.6.1.2.1.31.1.1.1.11';
my $snmpIfHCOutMulticastPkts	= '1.3.6.1.2.1.31.1.1.1.12';
my $snmpIfHCOutBroadcastPkts	= '1.3.6.1.2.1.31.1.1.1.13';
my $snmpIfSpeed  				= '1.3.6.1.2.1.31.1.1.1.15';

my $response;

# Path to traffic files.  
my $TRAFFIC_FILE = "/var/cache/nagios/ifspeeds/traffic";

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

#default values;
my $state = "UNKNOWN";
my $if_status = '4';
my $if_admin = '4';
my ( $in_bits, $out_bits, $in_ucast, $in_mcast, $in_bcast, $out_ucast, $out_mcast, $out_bcast, $in_discards, $in_errors, $out_discards, $out_errors ) = 0;
my $warn_usage = 75;
my $crit_usage = 90;
my $COMMUNITY  = "public";
my $output = "";
my $bytes = undef; 
my $suffix = "bps";
my $label = "Bytes";
my $max_value;
my $max_bits;
my $min_value = undef;
my $if_in_discards = 0;
my $if_in_errors = 0;
my $if_out_discards = 0;
my $if_out_errors = 0;
my $if_in_ucast = 0;
my $if_in_mcast = 0;
my $if_in_bcast = 0;
my $if_out_ucast = 0;
my $if_out_mcast = 0;
my $if_out_bcast = 0;


# Need to check this out.  Not sure how or if it works.
my $use_reg    =  undef;  # Use Regexp for name

my $status = GetOptions(
 "A|address=s"   => \$host_ip,
 "B|Bytes"       => \$bytes,
 "b|bandwidth|I|inBandwidth=i" => \$iface_speed,
 "P|portchannel=i"  => \$iface_pcspeed,
 "C|community=s" => \$COMMUNITY,
 "c|critical=s"  => \$crit_usage,
 "H|hostname=s"  => \$host_address,
 "h|help"        => \$opt_h,
 "i|interface=s" => \$iface_descr,
 "M|max=i"       => \$max_value,
 "O|outBandwidth=i" => \$iface_speedOut,
 "p|port=i"      => \$port,
 "r|noregexp"    => \$use_reg,
 "u|units=s"     => \$units,
 "w|warning=s"   => \$warn_usage,
 "n|min_value=i" => \$min_value
);

sub bytes2bits {
 return unit2bytes(@_)*8;
};

#Converts an input value to value in bytes
sub unit2bytes {
 my ( $value, $unit ) = @_;
 #given ($unit) {
 # when ('G') { return $value*1073741824; }
 # when ('M') { return $value*1048576; }
 # when ('K') { return $value*1024; }
 # default { return $value }
 #};
 if ($unit eq 'G') {
   return $value*1073741824;
 } 
 elsif ($unit eq 'M') {
   return $value*1048576;
 }
 elsif ($unit eq 'K') {
   return $value*1024;
 }
 else { return $value }

};

sub unit2bits {
 my ( $value, $unit ) = @_;
 #given ($unit) {
 # when ('g') { return $value*1000000000; }
 # when ('m') { return $value*1000000; }
 # when ('k') { return $value*1000; }
 # default { return $value }
 #};
 if ($unit eq 'g') {
   return $value*1_000_000_000;
 }
 elsif ( $unit eq 'm' ) {
   return $value*1_000_000;
 }
 elsif ( $unit eq 'k' ) {
   return $value*1000
 } 
 else { return $value }

};

# Lookup hosts ip address
sub get_ip {
 use Net::DNS;

 my ( $host_name ) = @_;
 my $res = Net::DNS::Resolver->new;
 my $query = $res->search($host_name);

 if ($query) {
  foreach my $rr ($query->answer) {
   next unless $rr->type eq "A";
   return $rr->address;
  }
 } else {
  stop("Error: IP address not resolved\n","UNKNOWN");
 }
};

sub fetch_Ip2IfIndex {
 my $response;
 my $snmpkey;
 my $answer;
 my $key;

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

 # Determine if we have a host name or IP addr
 if ( $host !~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ ) {
  $host = get_ip ( $host );
 };

 # Quit if results not found
 if ( !defined( $response = $session->get_table($snmpIPAdEntIfIndex) ) ) {
  $answer = $session->error;
  $session->close;
  stop("CRITICAL: SNMP error: $answer\n", "CRITICAL");
 };

 my %resp = %{$response};
 foreach $key ( keys %resp ) {
  # Check for ip address matching in returned index results
  if ( $key =~ /$host$/ ) {
   $snmpkey = $resp{$key};
  }
 }
 unless ( defined $snmpkey ) {
  $session->close;
  stop("CRITICAL: Could not match $host\n", "CRITICAL");
 }
 return $snmpkey;
};

sub fetch_ifdescr {
 my $response;
 my $snmpkey;
 my $answer;
 my $key;

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

 if ( !defined( $response = $session->get_table($snmpIfDescr) ) ) {
  $answer = $session->error;
  $session->close;
  stop("UNKNOWN: SNMP error: $answer\n", "UNKNOWN");
 };

 foreach $key ( keys %{$response} ) {

  # remove trailing 0 Byte for Windows
  my $resp=$response->{$key};
  $resp =~ s/\x00//;

  my $test = defined($use_reg) ? $resp =~ /$ifdescr/ : $resp eq $ifdescr; 

  if ($test) {
   $key =~ /.*\.(\d+)$/;
   $snmpkey = $1;
  }
 }
 unless ( defined $snmpkey ) {
  $session->close;
  stop("CRITICAL: Could not match $ifdescr\n", "CRITICAL");
 }
 return $snmpkey;
};

sub format_volume {
 my $prefix_x="";
 my ($x)=@_;

 if ( $x>1000000000000000000 ) {
  $x=$x/1000000000000000000;
  $prefix_x="E";
 };
 if ( $x>1000000000000000 ) {
  $x=$x/1000000000000000;
  $prefix_x="P";
 };
 if ( $x>1000000000000 ) {
  $x=$x/1000000000000;
  $prefix_x="T";
 };
 if ( $x>1000000000 ) {
  $x=$x/1000000000;
  $prefix_x="G";
 };
 if ( $x>1000000 ) {
  $x=$x/1000000;
  $prefix_x="M";
 };
 if ( $x>1000 ) {
  $x=$x/1000;
  $prefix_x="K";
 };
 $x=sprintf("%.2f",$x);
 return $x.$prefix_x;
};

sub format_volume_bytes {
 my $prefix_x;
 my ($x)=@_;

 if ( $x>1152921504606846976 ) {
  $x=$x/1152921504606846976;
  $prefix_x="E";
 };
 if ( $x>1125899906842624 ) {
  $x=$x/1125899906842624;
  $prefix_x="P";
 };
 if ( $x>1099511627776 ) {
  $x=$x/1099511627776;
  $prefix_x="T";
 };
 if ( $x>1073741824 ) {
  $x=$x/1073741824;
  $prefix_x="G";
 };
 if ( $x>1048576 ) {
  $x=$x/1048576;
  $prefix_x="M";
 };
 if ( $x>1024 ) {
  $x=$x/1024;
  $prefix_x="K";
 };
 $x=sprintf("%.2f",$x);
 return $x.$prefix_x;
};

# Check for missing options
if (!$host_address)  {
 print  "\nMissing host address!\n\n";
 stop(print_usage(),"UNKNOWN");
};
if (($iface_speed) and (!$units) ){
 print "\nMissing units!\n\n";
 stop(print_usage(),"UNKNOWN");
};
if (($units) and ((!$iface_speed) and  (!$iface_speedOut))) {
 print "\nMissing interface maximum speed!\n\n";
 stop(print_usage(),"UNKNOWN");
};
if (($iface_speedOut) and (!$units)) {
 print "\nMissing units for Out maximum speed!\n\n";
 stop(print_usage(),"UNKNOWN");
};
if (!$units) {
 $units="b";
};
if (($iface_speed) and ($bytes)) {
 $iface_speed = bytes2bits( $iface_speed, $units );
 if ( $iface_speedOut ) { $iface_speedOut = bytes2bits( $iface_speedOut, $units ); };
} elsif ($iface_speed) {
 $iface_speed = unit2bits( $iface_speed, $units );
 if ( $iface_speedOut ) { $iface_speedOut = unit2bits( $iface_speedOut, $units ); };
};


# If no -M Parameter was set, set it to 64Bit Overflow
if ( !$max_value ) {
  $max_bits = 18446744073709551616;
} else {
 if (!$bytes) {
  $max_bits = unit2bits( $max_value, $units );
 }
 else {
  $max_bits = bytes2bits( $max_value, $units );
 };
};


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 yet\n",$state);
}
else {
 $state = 'UNKNOWN';
 stop("$state: Unknown SNMP v$snmp_version\n",$state);
};


# Neither Interface Index nor Host IP address were specified 
if ( !$iface_descr ) {
 if ( !$host_ip ) {
 # try to resolve host name and find index from ip addr
 $iface_descr = fetch_Ip2IfIndex( $session, $host_address );
 } else {
  # Use ip addr to find index
  $iface_descr = fetch_Ip2IfIndex( $session, $host_ip );
 }
}

# Detect if a string description was given or a numberic interface index number 
if ( $iface_descr =~ /[^0123456789]+/ ) {
 $iface_number = fetch_ifdescr( $session, $iface_descr );
} else {
 $iface_number = $iface_descr;
}
# snmp gets
push( @snmpoids, $snmpIfSpeed . "." . $iface_number );
push( @snmpoids, $snmpIfOperStatus . "." . $iface_number );
push( @snmpoids, $snmpIfAdminStatus . "." . $iface_number );
# Interface inputs
push( @snmpoids, $snmpIfHCInOctets . "." . $iface_number );
push( @snmpoids, $snmpIfHCInUcastPkts . "." . $iface_number );
push( @snmpoids, $snmpIfHCInMulticastPkts . "." . $iface_number );
push( @snmpoids, $snmpIfHCInBroadcastPkts . "." . $iface_number );
push( @snmpoids, $snmpIfInDiscards . "." . $iface_number );
push( @snmpoids, $snmpIfInErrors . "." . $iface_number );
# Interface outputs
push( @snmpoids, $snmpIfHCOutOctets . "." . $iface_number );
push( @snmpoids, $snmpIfHCOutUcastPkts . "." . $iface_number );
push( @snmpoids, $snmpIfHCOutMulticastPkts . "." . $iface_number );
push( @snmpoids, $snmpIfHCOutBroadcastPkts . "." . $iface_number );
push( @snmpoids, $snmpIfOutDiscards . "." . $iface_number );
push( @snmpoids, $snmpIfOutErrors . "." . $iface_number );



if ( !defined( $response = $session->get_request(@snmpoids) ) ) {
 my $answer = $session->error;
 $session->close;
 stop("WARNING: SNMP error: $answer\n", "WARNING");
}

if ( !$iface_speed ) {
 $iface_speed = $response->{ $snmpIfSpeed . "." . $iface_number }*1000000;
}

# Check if Out max speed was provided, use same if speed for both if not
if (!$iface_speedOut) {
 $iface_speedOut = $iface_speed;
}
# Interface status
$if_admin = $response->{ $snmpIfAdminStatus . "." . $iface_number };
$if_status = $response->{ $snmpIfOperStatus . "." . $iface_number };
# Interface inputs
$in_bits  = $response->{ $snmpIfHCInOctets . "." . $iface_number }*8;
$in_ucast  = $response->{ $snmpIfHCInUcastPkts . "." . $iface_number };
$in_mcast  = $response->{ $snmpIfHCInMulticastPkts . "." . $iface_number };
$in_bcast = $response->{ $snmpIfHCInBroadcastPkts . "." . $iface_number };
$in_discards  = $response->{ $snmpIfInDiscards . "." . $iface_number };
$in_errors = $response->{ $snmpIfInErrors . "." . $iface_number };
# Interface outputs
$out_bits = $response->{ $snmpIfHCOutOctets . "." . $iface_number }*8;
$out_ucast  = $response->{ $snmpIfHCOutUcastPkts . "." . $iface_number };
$out_mcast  = $response->{ $snmpIfHCOutMulticastPkts . "." . $iface_number };
$out_bcast = $response->{ $snmpIfHCOutBroadcastPkts . "." . $iface_number };
$out_discards  = $response->{ $snmpIfOutDiscards . "." . $iface_number };
$out_errors = $response->{ $snmpIfOutErrors . "." . $iface_number };

#We retain the absolute values in bytes for charting. It doesn't matter that the counter may overflow.
my $in_traffic_absolut=$response->{ $snmpIfHCInOctets . "." . $iface_number };
my $out_traffic_absolut=$response->{ $snmpIfHCOutOctets . "." . $iface_number };

$session->close;

my $update_time = time;
my $last_check_time = $update_time - 1;

# Alert if interface is adminstratively down.
if ( $if_admin != 1 ) {
 stop("CRITICAL: Interface $iface_descr is Administratively down!\n", "CRITICAL");
};
# Alert if interface is operationally down.
if ( $if_status != 1 ) {
 stop("CRITICAL: Interface $iface_descr is Operationally down!\n", "CRITICAL");
};

my $row;
my $last_in_bits   = $in_bits;
my $last_out_bits  = $out_bits;
my $last_in_ucast  = $in_ucast;
my $last_in_mcast  = $in_mcast;
my $last_in_bcast  = $in_bcast;
my $last_out_ucast = $out_ucast;
my $last_out_mcast = $out_mcast;
my $last_out_bcast = $out_bcast;
my $last_in_discards = $in_discards;
my $last_in_errors   = $in_errors;
my $last_out_discards = $out_discards;
my $last_out_errors   = $out_errors;

if ( open(FILE,"<".$TRAFFIC_FILE."_if".$iface_number."_".$host_address)) {
 while ( $row = <FILE> ) {
  ( $last_check_time, $last_in_bits, $last_out_bits, $last_in_ucast, $last_in_mcast, $last_in_bcast, $last_out_ucast, $last_out_mcast, $last_out_bcast, $last_in_discards, $last_in_errors, $last_out_discards, $last_out_errors) = split( ":", $row );
  if (!$last_in_bits) { $last_in_bits=$in_bits;  }
  if (!$last_out_bits) { $last_out_bits=$out_bits; }
  if (!$last_in_ucast) { $last_in_ucast=$in_ucast; }
  if (!$last_in_mcast) { $last_in_mcast=$in_mcast; }
  if (!$last_in_bcast) { $last_in_bcast=$in_bcast; }
  if (!$last_out_ucast) { $last_out_ucast=$out_ucast; }
  if (!$last_out_mcast) { $last_out_mcast=$out_mcast; }
  if (!$last_out_bcast) { $last_out_bcast=$out_bcast; }    
  if (!$last_in_discards) { $last_in_discards=$in_discards; }
  if (!$last_in_errors) { $last_in_errors=$in_errors; }
  if (!$last_out_discards) { $last_out_discards=$out_discards; }
  if (!$last_out_errors) { $last_out_errors=$out_errors; }
  if ($last_in_bits !~ m/\d/) { $last_in_bits=$in_bits; }
  if ($last_out_bits !~ m/\d/) { $last_out_bits=$out_bits; }
 }
 close(FILE);
}

if ( open(FILE,">".$TRAFFIC_FILE."_if".$iface_number."_".$host_address )) {
 printf FILE ("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n", $update_time, $in_bits, $out_bits, $in_ucast, $in_mcast, $in_bcast, $out_ucast, $out_mcast, $out_bcast, $in_discards, $in_errors, $out_discards, $out_errors );
 close(FILE);
};

# Traffic Check
my $in_traffic=0;
my $out_traffic=0;

if ( $in_bits < $last_in_bits ) {
  $in_bits = $in_bits + ($max_bits-$last_in_bits);
  $in_traffic = $in_bits/($update_time-$last_check_time);
} else { $in_traffic = ($in_bits-$last_in_bits)/($update_time-$last_check_time); };

if ( $out_bits < $last_out_bits ) {
  $out_bits = $out_bits + ($max_bits-$last_out_bits);
  $out_traffic = $out_bits/($update_time-$last_check_time);
} else { $out_traffic = ($out_bits-$last_out_bits)/($update_time-$last_check_time); };

# Calculate usage percentages
my $in_usage  = ($in_traffic*100)/$iface_speed;
my $out_usage = ($out_traffic*100)/$iface_speedOut;

if ($bytes) {
 # Convert output from bits to bytes
 $in_traffic = $in_traffic/8;
 $out_traffic = $out_traffic/8;
 $suffix = "Bs";
}

# Option -P Check Port-channel speed to determine if all members are operational
# Compares user input to speed reported by switch in Mbps.
if ( $iface_pcspeed != 0 ) { 
	$iface_pctest = $response->{ $snmpIfSpeed . "." . $iface_number };
	if ( $iface_pctest != $iface_pcspeed ) {
	$pcstate = "CRITICAL";
}
}

my $in=format_volume($in_traffic);
my $out=format_volume($out_traffic);

my $rx=format_volume_bytes($in_traffic_absolut);
my $tx=format_volume_bytes($out_traffic_absolut);

# Convert percentages to a more visual format
$in_usage  = sprintf("%.2f", $in_usage);
$out_usage = sprintf("%.2f", $out_usage);

# Convert performance to a more visual format
$in_traffic  = sprintf("%.2f", $in_traffic);
$out_traffic = sprintf("%.2f", $out_traffic);

# Calculate unicast, multicast & broadcast values
	if ( $in_ucast > $last_in_ucast ) {
		$if_in_ucast = $in_ucast - $last_in_ucast; };
		
	if ( $in_mcast > $last_in_mcast ) {
		$if_in_mcast = $in_mcast - $last_in_mcast; };
		
	if ( $in_bcast > $last_in_bcast ) {
		$if_in_bcast = $in_bcast - $last_in_bcast; };	

	if ( $out_ucast > $last_out_ucast ) {
		$if_out_ucast = $out_ucast - $last_out_ucast; };
		
	if ( $out_mcast > $last_out_mcast ) {
		$if_out_mcast = $out_mcast - $last_out_mcast; };
		
	if ( $out_bcast > $last_out_bcast ) {
		$if_out_bcast = $out_bcast - $last_out_bcast; };			
			
# Output results
$state = "OK";

#Check min value for in_traffic
if (defined($min_value)) { 
		if ($in_traffic<$min_value) {
		$output .= "Traffic IN too low for $iface_descr! ";
		$state = "WARNING";}
}

# Check for Discards & Errors
if ( $in_discards > $last_in_discards or $in_errors > $last_in_errors or $out_discards > $last_out_discards or $out_errors > $last_out_errors ) {
	$if_in_discards = $in_discards - $last_in_discards;
	$if_in_errors = $in_errors - $last_in_errors;
	$if_out_discards = $out_discards - $last_out_discards;
	$if_out_errors = $out_errors - $last_out_errors;
	$state = "WARNING";
	$output .= "Interface $iface_descr has Discards\/Errors! inDiscards=$if_in_discards, inErrors=$if_in_errors, outDiscards=$if_out_discards, outErrors=$if_out_errors, ";
};

$output .= "Average IN: ".$in.$suffix.", Bandwidth: ".$in_usage."%, "
        ."Average OUT: ".$out.$suffix.", Bandwidth: ".$out_usage."% ";


if (($in_usage>$warn_usage) or ($out_usage>$warn_usage)) {
 $state = "WARNING";
}

if (($in_usage>$crit_usage) or ($out_usage>$crit_usage)) {
 $state = "CRITICAL";
}

#Output results of Port-Channel test with Critical
if ($pcstate eq "CRITICAL" ) {
	$state = "CRITICAL";
	$output = "Interface $iface_descr has a member interface down! "
	."Average IN: ".$in.$suffix.", Bandwidth: ".$in_usage."%, "
    ."Average OUT: ".$out.$suffix.", Bandwidth: ".$out_usage."% ";
}

$output = "$state: $output |inUse=$in_usage, outUse=$out_usage, Warn=$warn_usage, Crit=$crit_usage,"
  ." inBW=$in_traffic, outBW=$out_traffic," 
  ." inUcast=$if_in_ucast, inMcast=$if_in_mcast, inBcast=$if_in_bcast, outUcast=$if_out_ucast, outMcast=$if_out_mcast, outBcast=$if_out_bcast,"
  ." inDis=$if_in_discards, inErr=$if_in_errors, outDis=$if_out_discards, outErr=$if_out_errors";

stop($output, $state);

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

# Print help information
sub print_usage {
	print <<EOU;
    Usage: check_iftraffic5.pl -H host [ -C community_string ] [ -i if_index|if_descr ] [ -r ]
    [ -b if_max_speed_in | -I if_max_speed_in ] [ -O if_max_speed_out ] [ -P port_channel_speed ]
    [ -u ] [ -B ] [ -A IP Address ] [ -n ] [ -M ] [ -w warn ] [ -c crit ]

    Example 1: check_iftraffic3.pl -H hostname -C public
    Example 2: check_iftraffic3.pl -H hostname -C public -i "Intel Pro" -r -B  
    Example 3: check_iftraffic3.pl -H hostname -C public -i 5
    Example 4: check_iftraffic3.pl -H hostname -C public -i 5 -B -b 100 -u m 
    Example 5: check_iftraffic3.pl -H hostname -C public -i 5 -B -b 20 -O 5 -u m 
    Example 6: check_iftraffic3.pl -H hostname -C public -A 192.168.1.1 -B -b 100 -u m 
	Example 7: check_iftraffic3.pl -H hostname -C public -i Port-channel50 -b 2 -u g -P 2000

    Options:

    -H, --host STRING or IPADDRESS
        Hostname or IP address of device.
    -B, --bytes
        Display results in Bytes per second B/s. (default: bits/s)
    -C, --community STRING 
        SNMP Community. (Version 2 only!)
    -i, --interface STRING
        Interface Name.
    -b, --bandwidth INTEGER
        Enter interface's bandwidth in bps as defined by -u.
        Required if -u is used.  
    -I, --inBandwidth INTEGER
        Interface maximum speed in bps as defined by -u.
        Applied to both IN and OUT if option (-O) is not provided.
    -O, --outBandwidth INTEGER
        Interface maximum speed in bps as defined by -u.
        Applied to OUT traffic.
    -u, --units STRING
        g=gigabits/s,m=megabits/s,k=kilobits/s,b=bits/s.
        Required if -b, -I, or -O are used.
    -P, --portchannel INTEGER
        Checks Port-channel member status by comparing port-channel speed to number.
        Enter Port-channel's total bandwidth using Mbps.
        Supports 10G interfaces using 64-bit counters.
    -n, --Minimum InBandwidth INTEGER
        % of bandwidth minimum usage necessary to result in a warning status.
        Used for IPSEC tunnels where minimum value is needed.
    -w, --warning Bandwidth INTEGER
        % of bandwidth usage necessary to result in warning status. (default: 75%)
    -c, --critical Bandwidth INTEGER
        % of bandwidth usage necessary to result in critical status. (default: 90%)
    -M, --max INTEGER
        Max Counter Value of net devices in kilo/mega/giga/bytes.
    -A, --address STRING (IP Address)
        IP Address to use when determining the interface index to use.  Can be 
        used when the index changes frequently or as in the case of Windows 
        servers the index is different depending on the NIC installed.
    -r, --regexp STRING
        Use regexp to match NAME in description OID.  Not sure if this works!
		
EOU

}