#!/usr/bin/perl -w # # 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 $ # # mw = Markus Werner mw+nagios@wobcom.de # Remarks (mw): # # I adopted as much as possible the programming style of the origin code. # # There should be a function to exit this programm, # instead of calling print and exit statements all over the place. # # # minor changes by mw # The snmp if_counters on net devices can have overflows. # I wrote this code to address this situation. # It has no automatic detection and which point the overflow # occurs but it will generate a warning state and you # can set the max value by calling this script with an additional # arg. # # minor cosmetic changes by mw # Sorry but I couldn't sustain to clean up some things. # # gj = Greg Frater gregATfraterfactory.com # Remarks (gj): # minor (gj): # # * fixed the performance data, formating was not to spec # * Added a check of the interfaces status (up/down). # If down the check returns a critical status. # * Allow either textual or the numeric index value. # * If the interface speed is not specified on the command line # it gets it automatically from IfSpeed # * Added option for second ifSpeed to allow for asymetrcal links # such as a DSL line or cable modem where the download and upload # speeds are different # * Added -B option to display results in bits/sec instead of Bytes/sec # * Added the current usage in Bytes/s (or bit/s) to the perfdata output # * Added ability for plugin to determine interface to query by matching IP # address of host with entry in ipAdEntIfIndex (.1.3.6.1.2.1.4.20.1.2) # * Added -L flag to list entries found in the ipAdEntIfIndex table # Otherwise, it works as before. # # # # # based on check_traffic from Adrian Wieczorek, # # 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 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 $session; my $error; my $port = 161; my $snmp_version = 1; my @snmpoids; # SNMP OIDs for Traffic my $snmpIfOperStatus = '1.3.6.1.2.1.2.2.1.8'; 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 $snmpIfSpeed = '1.3.6.1.2.1.2.2.1.5'; my $snmpIPAdEntIfIndex = '1.3.6.1.2.1.4.20.1.2'; my $response; # Path to tmp files my $TRAFFIC_FILE = "/tmp/traffic"; # changes sos 20090717 UNKNOWN must bes 3 my %STATUS_CODE = ( 'UNKNOWN' => '3', 'OK' => '0', 'WARNING' => '1', 'CRITICAL' => '2' ); #default values; my $state = "UNKNOWN"; my $if_status = '4'; my ( $in_bytes, $out_bytes ) = 0; my $warn_usage = 85; my $crit_usage = 98; my $COMMUNITY = "public"; my $use_reg = undef; # Use Regexp for name my $output = ""; my $bits = undef; my $suffix = "Bs"; my $label = "MBytes"; #added 20050614 by mw my $max_value; my $max_bytes; #cosmetic changes 20050614 by mw, see old versions for detail # Added options for bits and second max ifspeed 20100202 by gj # Added options for specificy IP addr to match 20100405 by gj my $status = GetOptions( "h|help" => \$opt_h, 'B' => \$bits, 'bits' => \$bits, "C|community=s" => \$COMMUNITY, "w|warning=s" => \$warn_usage, "c|critical=s" => \$crit_usage, "b|bandwidth|I|inBandwidth=i" => \$iface_speed, "O|outBandwidth=i" => \$iface_speedOut, 'r' => \$use_reg, 'noregexp' => \$use_reg, "p|port=i" => \$port, "u|units=s" => \$units, "i|interface=s" => \$iface_descr, "A|address=s" => \$host_ip, "H|hostname=s" => \$host_address, 'L' => \$index_list, 'list' => \$index_list, #added 20050614 by mw "M|max=i" => \$max_value ); if ( $status == 0 ) { print_help(); exit $STATUS_CODE{'OK'}; } # Changed 20091214 gj # Check for missing options #if ( ( !$host_address ) or ( !$iface_descr ) ) { if ( !$host_address ) { print "\nMissing host address!\n\n"; stop(print_usage(),"OK"); } elsif ( ( $iface_speed ) and ( !$units ) ){ print "\nMissing units!\n\n"; stop(print_usage(),"OK"); } elsif ( ( $units ) and ( ( !$iface_speed ) and ( !$iface_speedOut ) ) ) { print "\nMissing interface maximum speed!\n\n"; stop(print_usage(),"OK"); } elsif ( ( $iface_speedOut ) and ( !$units ) ) { print "\nMissing units for Out maximum speed!\n\n"; stop(print_usage(),"OK"); } if ($bits) { $suffix = "bs" } if ( !$iface_speed ) { # Do nothing }else{ #change 20050414 by mw # Added iface_speedOut 20100202 by gj # Convert interface speed to kiloBytes $iface_speed = bits2bytes( $iface_speed, $units ) / 1024; if ( $iface_speedOut ) { $iface_speedOut = bits2bytes( $iface_speedOut, $units ) / 1024; } if ( !$max_value ) { # If no -M Parameter was set, set it to 32Bit Overflow $max_bytes = 4194304 ; # the value is (2^32/1024) } else { $max_bytes = unit2bytes( $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: No support for SNMP v$snmp_version yet\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 ); } } #push( @snmpoids, $snmpIPAdEntIfIndex . "." . $host_address ); # Added 20091209 gj # 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; } push( @snmpoids, $snmpIfSpeed . "." . $iface_number ); push( @snmpoids, $snmpIfOperStatus . "." . $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; stop("WARNING: SNMP error: $answer\n", "WARNING"); } # Added 20091209 gj # Get interface speed from device if not provided on command line # Convert to kiloBytes if ( !$iface_speed ) { $iface_speed = $response->{ $snmpIfSpeed . "." . $iface_number }; $units = "b"; $iface_speed = bits2bytes( $iface_speed, $units ) / 1024; } # Added 20100201 gj # Check if Out max speed was provided, use same if speed for both if not if (!$iface_speedOut) { $iface_speedOut = $iface_speed; } $if_status = $response->{ $snmpIfOperStatus . "." . $iface_number }; $in_bytes = $response->{ $snmpIfInOctets . "." . $iface_number } / 1024; # in kiloBytes $out_bytes = $response->{ $snmpIfOutOctets . "." . $iface_number } / 1024; # in kiloBytes $session->close; my $row; my $last_check_time = time - 1; my $last_in_bytes = $in_bytes; my $last_out_bytes = $out_bytes; if ( open( FILE, "<" . $TRAFFIC_FILE . "_if" . $iface_number . "_" . $host_address ) ) { while ( $row = ) { #cosmetic change 20050416 by mw #Couldn't sustain;-) ## chomp(); ( $last_check_time, $last_in_bytes, $last_out_bytes ) = split( ":", $row ); ### by sos 17.07.2009 check for last_bytes if ( ! $last_in_bytes ) { $last_in_bytes=$in_bytes; } if ( ! $last_out_bytes ) { $last_out_bytes=$out_bytes; } if ($last_in_bytes !~ m/\d/) { $last_in_bytes=$in_bytes; } if ($last_out_bytes !~ m/\d/) { $last_out_bytes=$out_bytes; } } close(FILE); } my $update_time = time; open( FILE, ">" . $TRAFFIC_FILE . "_if" . $iface_number . "_" . $host_address ) or die "Can't open $TRAFFIC_FILE for writing: $!"; printf FILE ( "%s:%.0ld:%.0ld\n", $update_time, $in_bytes, $out_bytes ); close(FILE); my $db_file; #added 20050614 by mw #Check for and correct counter overflow (if possible). #See function counter_overflow. $in_bytes = counter_overflow( $in_bytes, $last_in_bytes, $max_bytes ); $out_bytes = counter_overflow( $out_bytes, $last_out_bytes, $max_bytes ); # Calculate traffic since last check (RX\TX) in kiloBytes my $in_traffic = sprintf( "%.2lf", ( $in_bytes - $last_in_bytes ) / ( time - $last_check_time ) ); my $out_traffic = sprintf( "%.2lf", ( $out_bytes - $last_out_bytes ) / ( time - $last_check_time ) ); # sos 20090717 changed due to rrdtool needs bytes my $in_traffic_absolut = $in_bytes * 1024 ; my $out_traffic_absolut = $out_bytes * 1024; # Calculate usage percentages my $in_usage = sprintf( "%.2f", ( 1.0 * $in_traffic * 100 ) / $iface_speed ); my $out_usage = sprintf( "%.2f", ( 1.0 * $out_traffic * 100 ) / $iface_speedOut ); if ($bits) { # Convert output from Bytes to bits $in_bytes = $in_bytes * 8; $out_bytes = $out_bytes * 8; $in_traffic = $in_traffic * 8; $out_traffic = $out_traffic * 8; $label = "Mbits"; } my $in_prefix = "K"; my $out_prefix = "K"; if ( $in_traffic > 1024 ) { $in_traffic = sprintf( "%.2f", $in_traffic / 1024 ); $in_prefix = "M"; } if ( $out_traffic > 1024 ) { $out_traffic = sprintf( "%.2f", $out_traffic / 1024 ); $out_prefix = "M"; } if ( $in_traffic > 1024 * 1024 ) { $in_traffic = sprintf( "%.2f", $in_traffic / 1024 * 1024 ); $in_prefix = "G"; } if ( $out_traffic > 1024 * 1024 ) { $out_traffic = sprintf( "%.2f",$out_traffic / 1024 * 1024 ); $out_prefix = "G"; } # Convert from kiloBytes to megaBytes $in_bytes = sprintf( "%.2f", $in_bytes / 1024 ); $out_bytes = sprintf( "%.2f", $out_bytes / 1024 ); $state = "OK"; # Added 20091209 by gj if ( $if_status != 1 ) { $output = "Interface $iface_descr is down!"; }else{ $output = "Average IN: " . $in_traffic . $in_prefix . $suffix . " (" . $in_usage . "%), " . "Average OUT: " . $out_traffic . $out_prefix . $suffix . " (" . $out_usage . "%) "; $output .= "Total RX: $in_bytes $label, Total TX: $out_bytes $label"; } # Changed 20091209 gj if ( ( $in_usage > $crit_usage ) or ( $out_usage > $crit_usage ) or ( $if_status != 1 ) ) { $state = "CRITICAL"; } if ( ( $in_usage > $warn_usage ) or ( $out_usage > $warn_usage ) && $state eq "OK" ) { $state = "WARNING"; } # Changed 20091209 gj $output = "$state - $output" if ( $state ne "OK" ); # Changed 20091214 gj - commas should have been semi colons $output .= "|inBandwidth=" . $in_traffic . $in_prefix . $suffix . " outBandwidth=" . $out_traffic . $out_prefix . $suffix; stop($output, $state); sub fetch_Ip2IfIndex { my $state; 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}/ ){ #print "\nI found an IP address\n\n"; } else { $host = get_ip ( $host ); #print "\nWe have a host name $host\n\n"; } # Quit if results not found if ( !defined( $response = $session->get_table($snmpIPAdEntIfIndex) ) ) { $answer = $session->error; $session->close; $state = 'CRITICAL'; $session->close; exit $STATUS_CODE{$state}; } my %resp = %{$response}; # foreach $key ( keys %{$response} ) { if ( $index_list ){ print ("\nInterfaces found:\n"); print (" IP Addr\tIndex\n"); print ("------------------------\n"); } # Check each returned value foreach $key ( keys %resp ) { if ( $index_list ){ my $index_addr = substr $key, 21; print ($index_addr,"\t ",$resp{$key},"\n"); } # Check for ip address mathcin in returned index results if ( $key =~ /$host$/ ) { $snmpkey = $resp{$key}; } } unless ( defined $snmpkey ) { $session->close; $state = 'CRITICAL'; printf "$state: Could not match $host \n"; exit $STATUS_CODE{$state}; } return $snmpkey; } 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} ) { # added 20070816 by oer: 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) { ###if ( $resp =~ /^$ifdescr$/ ) { ###if ( $resp =~ /$ifdescr/ ) { ### print "$resp \n"; ###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; } #added 20050416 by mw #Converts an input value to value in bits sub bits2bytes { return unit2bytes(@_) / 8; } #added 20050416 by mw #Converts an input value to value in bytes sub unit2bytes { my ( $value, $unit ) = @_; if ( $unit eq "g" ) { return $value * 1024 * 1024 * 1024; } elsif ( $unit eq "m" ) { return $value * 1024 * 1024; } elsif ( $unit eq "k" ) { return $value * 1024; } elsif ( $unit eq "b" ) { return $value * 1; } else { print "You have to supply a supported unit\n"; exit $STATUS_CODE{'UNKNOWN'}; } } #added 20050414 by mw #This function detects if an overflow occurs. If so, it returns #a computed value for $bytes. #If there is no counter overflow it simply returns the origin value of $bytes. #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} ); } # Added 20100405 by gj # 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"; #print $rr->address, "\n"; return $rr->address; } } else { stop("Error: IP address not resolved\n","UNKNOWN"); } } #cosmetic changes 20050614 by mw #Couldn't sustain "HERE";-), either. sub print_usage { print <