#!/bin/sh
#########################################################################
#
# File: check_traffic.sh
# Description: Nagios check plugins to check network interface traffic in *nix.
# Language: GNU Bourne-Again SHell
# Version: 1.1.6
# Date: 2009-02-20
# Corp.: Chenlei
# Author: chnl@163.com (U can msn me with this.)
# WWW: http://bbs.itnms.net
#########################################################################
# Bugs:
# The Latest Version will be released in http://bbs.itnms.net.
# You can send bugs to http://bbs.itnms.net,
# or email to me directly: chnl@163.com
#########################################################################
# Todo:
# Do not use unit at performance data, for pnp/rrd graphing better.
# Use perl Net::SNMP module to rewrite this script(U Can find the perl/Net::SNMP Version in the same site).
# Add the History performance data file support.(maybe)
# Also support the -n args, with interface name to check the traffic.
#########################################################################
# ChangeLog:
#
# Version 1.1.6
# 2009-02-20
# Fix some mistake at perfdata output format with Warning and Critical Value. (thanks for Jiang Shan)
# Remove the redundant code, merge code for --range option.
# Write data before the exit(for the reason of IsFirst), for the next time use.
# When write or read file error, use Unknow instead of Warning severity.
# If get a negative netflow or time interval value here, exit with unknow.
#
# Version 1.1.5
# 2008-09-28
# Fix bug on perfdata output format;
# Fix some spelling mistake;
# Add the Default Value for UseRange as "False";
# Version 1.1.4
# 2008-09-18
# Add -r options, Use Range instead of single value in warning and critical Threshold;
# This option suggestion by zhgypg@hotmail.com at http://www.itnms.net/discuz/thread-1220-1-1.html
#
# Version 1.1.3
# 2008-09-17
# Set the default Interval as 12 seconds;
#
# Version 1.1.2
# 2008-08-19
# Check the snmp agent support the 64 bit counter or not;
# Check the interface status, if not OK, exit with Critical status;
# Get the interface name with Interface Index Value;
#
# Version 1.1.1
# 2008-08-06
# Fix some bugs in version compare.
# Use -6 option, use 64 bit counter.
#
# Version 1.1.0
# 2008-06-11
# In snmp v2c, use counters ifHC* instead of if*;
#
# Version 1.0.9
# 2008-04-22
# More friendly output when getting snmp info error.
#
# Version 1.0.8
# 2008-03-31
# Correct some spelling mistake
#
# Version 1.0.7
# 2008-03-28
# If it's the first time to touch hist_dat, echo OK and Tips out;
# Test the hist_dat can be read and write;
# Use the Vars for $OutPut and $PerfData
# Fix some output format.
#
# Version 1.0.6
# 2008-03-25
# Correct Performance data output with Warning and Critical Value of Total and Interval for pnp graphing.
#
# Version 1.0.5
# 2008-03-24
# Correct Performance data output for pnp graphing.
#
# Version 1.0.4
# 2008-03-21
# Correct Performance data output to "Nagios plug-in development guidelines",
# for Graphing the performance data in the web with PNP.
# The standard is: 'label'=value[UOM];[warn];[crit];[min];[max]
#
# Version 1.0.3
# 2008-03-20
# More friendly output with function list_interface().
#
# Version 1.0.2
# 2008-03-06
# Fix some coding bugs;
# Add the -L support;
# -K/-M to speicify in K or M (bps,B/s);
# -B/-b switch to B/s or bps;
# Add Total traffic value in output
#
# Version 1.0.1
# 2008-02-28
# Fix two cacl bugs at line 212 and 244.
# In print_full_help_msg(), '$$' instead $$.
#
# Version 1.0
# 2008-02-27
# Original Version.
#########################################################################
# Heh, just a ad here :), for my honey.
# http://shop35165045.taobao.com/
############################
#
# Exit values:
# ------------
# 0 OK
# 1 Warning
# 2 Cirital
# 3 Unknown
# Others Unknown
#
# ----------------------------------------------------------------------
# These are Parameters from external
# -h
# Get the help message
#
# -v
# Verbose mode, to debug some messages out to the /tmp directory with log file name check_traffic.$$.
#
# -V 1|2c|3
# Specify the version of snmp
#
# -C Community
# Specify the Community
#
# -H host
# Specify the host
#
# -6 Use 64 bit counter, ifHC* instead of if*.
#
# -r Use Range instead of single value in warning and critical Threshold;
#
# -I interface
# Specify the interface
#
# -L List all Interfaces on specify host
#
# -B/b Switch to B/s or bps, default is -b, bps
#
# -K/M Switch to K or M (bsp,B/s), default is -K
#
# -w Warning value Kbps, in and out
# -c Critical value Kbps, in and out
# Set Warning and Critical Traffic value
unset LANG
Scale=4
Unit_1="K"
Unit_2="bps"
UseRange="False"
ifIn32="ifInOctets"
ifOut32="ifOutOctets"
ifIn64="ifHCInOctets"
ifOut64="ifHCOutOctets"
# Set the Min Interval of Check.
Min_Interval=12
print_help_msg(){
$Echo "Usage: $0 -h to get help."
}
print_full_help_msg(){
$Echo "Usage:"
$Echo "$0 [ -v ] [ -6 ] [ -r ] -V 1|2c|3 -C snmp-community -H host [ -L ] -I interface -w in,out-warning-value -c in,out-critical-value -K/M -B/b "
$Echo "Example:"
$Echo "${0} -V 2c -C public -H 127.0.0.1 -I 4 -w 200,100 -c 300,200 -K -B"
$Echo "Or -r to use Range Value Options:"
$Echo "${0} -V 2c -C public -H 127.0.0.1 -I 4 -r -w 200-300,100-200 -c 100-400,50-250 -K -B"
$Echo "If you don't use -K/M -B/b options, default -K -b, corresponding to Kbps."
$Echo "Make sure that the check interval greater than 5 Seconds."
$Echo "Or modify the Min_Interval var in this file Line 180."
$Echo 'And, if you want in Verbose mode, use -v, to check the debug messages in the file /tmp/check_traffic.$$.'
$Echo "Or use $0 [ -v ] -V 1|2c|3 -C snmp-community -H host -L "
$Echo "To list all interfaces on specify host."
}
print_err_msg(){
$Echo "Error."
print_full_help_msg
}
check_record_cnt(){
echo $2 | awk -F "$1" '{print NF}'
}
list_interface(){
$SNMPWALK -v $Version -c $Community $Host "IF-MIB::ifDescr" |sed 's/IF-MIB::ifDescr./Interface index /g' | sed 's/= STRING:/orresponding to /g'
exit 3
}
#adjust_value(){
# if [ `echo "$1 < 1" | bc` -eq 1 ]; then
# return "0"${$1} # if -lt 1, will error at: return: 0.1: numeric argument required
# else
# return $1
# fi
#}
to_debug(){
if [ "$Debug" = "true" ]; then
$Echo "$*" >> /tmp/check_traffic.log.$$ 2>&1
#$Echo "$*" >> /tmp/check_traffic.log 2>&1
fi
}
case "$(uname -s)"
in
SunOS)
Echo="echo"
;;
Linux)
Echo="echo -e"
;;
*)
Echo="echo"
;;
esac
if [ $# -lt 1 ]; then
print_help_msg
exit 3
else
while getopts :v6rhV:C:H:I:LKMBbw:c: OPTION
do
case $OPTION
in
v)
#$Echo "Verbose mode."
Debug=true
;;
V)
Version=$OPTARG
;;
C)
Community=$OPTARG
;;
6)
Bit64="True"
;;
r)
UseRange="True"
;;
H)
Host=$OPTARG
;;
L)
ListInt="True"
;;
I)
Interface=$OPTARG
;;
w)
WarningV=$OPTARG
;;
c)
CriticalV=$OPTARG
;;
M)
isM="True"
Unit_1="M"
;;
K)
;;
B)
isB="True"
Unit_2="B"
;;
b)
;;
h)
print_full_help_msg
exit 3
;;
?)
$Echo "Error: Illegal Option."
print_help_msg
exit 3
;;
esac
done
fi
if [ -z "$Version" -o -z "$Community" -o -z "$Host" ] ; then
$Echo "Args Error."
print_full_help_msg
exit 3
fi
SNMPWALK=`which snmpwalk 2>&1`
if [ $? -ne 0 ];then
$Echo $SNMPWALK
$Echo "Can not found command snmpwalk in you system PATH: $PATH, pleas check it"
exit 3
fi
to_debug Use $SNMPWALK to check traffic
if [ "$ListInt" = "True" ]; then
$Echo "List Interface for host $Host."
list_interface
exit 3
fi
if [ -z "$Interface" -o -z "$WarningV" -o -z "$CriticalV" ] ; then
$Echo "Args Error."
print_full_help_msg
exit 3
fi
to_debug All Values are \" Warning: "$WarningV" and Critical: "$CriticalV" \".
WVC=`check_record_cnt "," "$WarningV"`
CVC=`check_record_cnt "," "$CriticalV"`
to_debug WVC is $WVC and CVC is $CVC
if [ $UseRange = "True" ] ;then
to_debug UseRange is True
#####################
if [ $WVC -ne 2 -o $CVC -ne 2 ] ; then
$Echo "Warning and Critical Value error."
print_full_help_msg
exit 3
else
W1=`echo $WarningV| awk -F "," '{print $1}'`
W1b=`echo $W1| awk -F "-" '{print $1}'`
W1e=`echo $W1| awk -F "-" '{print $2}'`
W2=`echo $WarningV| awk -F "," '{print $2}'`
W2b=`echo $W2| awk -F "-" '{print $1}'`
W2e=`echo $W2| awk -F "-" '{print $2}'`
Wtb=`echo "$W1b + $W2b"|bc`
Wte=`echo "$W1e + $W2e"|bc`
to_debug Warning Value is $W1 $W2 $Wtb $Wte
C1=`echo $CriticalV| awk -F "," '{print $1}'`
C1b=`echo $C1| awk -F "-" '{print $1}'`
C1e=`echo $C1| awk -F "-" '{print $2}'`
C2=`echo $CriticalV| awk -F "," '{print $2}'`
C2b=`echo $C2| awk -F "-" '{print $1}'`
C2e=`echo $C2| awk -F "-" '{print $2}'`
Ctb=`echo "$C1b + $C2b"|bc`
Cte=`echo "$C1e + $C2e"|bc`
to_debug Critical Value is $C1 $C2 $Ctb $Cte
check_1b=`echo "$C1b < $W1b" | bc`
check_1e=`echo "$C1e > $W1e" | bc`
check_2b=`echo "$C2b < $W2b" | bc`
check_2e=`echo "$C2e > $W2e" | bc`
to_debug check_1 is $check_1b , $check_1e check_2 is $check_2b $check_2e
if [ $check_1b -ne 1 -o $check_1e -ne 1 -o $check_2b -ne 1 -o $check_2e -ne 1 ] ; then
$Echo "Error, the corresponding Critical End value must greater than Warning End value, And Critical Begin value must less than Warning End value"
print_full_help_msg
exit 3
fi
fi
#####################
else
to_debug Use Range is False
if [ $WVC -ne 2 -o $CVC -ne 2 ] ; then
$Echo "Warning and Critical Value error."
print_full_help_msg
exit 3
else
W1=`echo $WarningV| awk -F "," '{print $1}'`
W2=`echo $WarningV| awk -F "," '{print $2}'`
Wt=`echo "$W1 + $W2"|bc`
to_debug Warning Value is $W1 $W2 $Wt
C1=`echo $CriticalV| awk -F "," '{print $1}'`
C2=`echo $CriticalV| awk -F "," '{print $2}'`
Ct=`echo "$C1 + $C2"|bc`
to_debug Critical Value is $C1 $C2 $Ct
check_1=`echo "$C1 > $W1" | bc`
check_2=`echo "$C2 > $W2" | bc`
to_debug check_1 is $check_1 , check_2 is $check_2
if [ $check_1 -ne 1 -o $check_2 -ne 1 ] ; then
$Echo "Error, the corresponding Critical value must greater than Warning value."
print_full_help_msg
exit 3
fi
fi
fi
# This file will save the traffic data from previos check.
# Make sure it will never be deleted.
CF_HIST_DATA="/var/tmp/check_traffic_${Host}_${Interface}.hist_dat"
if [ ! -f $CF_HIST_DATA ]; then
IsFirst="True"
touch $CF_HIST_DATA
if [ $? -ne 0 ];then
Severity=3
Msg="Unknown"
OutPut="Create File $CF_HIST_DATA Error with user `id`."
$Echo "$Msg" "-" $OutPut
exit $Severity
fi
fi
if [ ! -r $CF_HIST_DATA -o ! -w $CF_HIST_DATA ]; then
Severity=3
Msg="Unknown"
OutPut="Read or Write File $CF_HIST_DATA Error with user `id`."
$Echo "$Msg" "-" $OutPut
exit $Severity
fi
Time=`date +%s`
ifName=`$SNMPWALK -v $Version -c $Community $Host IF-MIB::ifDescr.${Interface}| awk -F ":" '{print $4}'`
if [ "$Version" = "2c" -a "$Bit64" = "True" ] ;then
ifIn=$ifIn64
ifOut=$ifOut64
$SNMPWALK -v $Version -c $Community $Host IF-MIB::ifHCOutOctets >/dev/null 2>&1
if [ $? -ne 0 ];then
$Echo "Maybe your Host(device) not support 64 bit counter. Please confirm your ARGS and re-check it with Verbose mode, then to check the log.(If you snmp not support 64 bit counter, do not use -6 option)"
exit 3
fi
else
ifIn=$ifIn32
ifOut=$ifOut32
fi
_result_status=`$SNMPWALK -v $Version -c $Community $Host "IF-MIB::ifOperStatus.${Interface}"| awk '{print $4}' | awk -F '(' '{print $1}'`
if [ "$_result_status" != "up" ]; then
$Echo "The Interface name:${ifName} -- index:${Interface} you checked not up."
exit 2
fi
_result_in=`$SNMPWALK -v $Version -c $Community $Host "IF-MIB::${ifIn}"|grep "IF-MIB::${ifIn}.""$Interface"`
_result_out=`$SNMPWALK -v $Version -c $Community $Host "IF-MIB::${ifOut}"|grep "IF-MIB::${ifOut}.""$Interface" `
to_debug time is $Time, $SNMPWALK check result in is $_result_in, out is $_result_out
_result_in=`echo $_result_in |awk '{print $4}'`
_result_out=`echo $_result_out|awk '{print $4}'`
to_debug time is $Time, $SNMPWALK check result in is $_result_in, out is $_result_out
if [ -z "$_result_in" -o -z "$_result_out" ] ; then
$Echo "No Data been get here. Please confirm your ARGS and re-check it with Verbose mode, then to check the log.(If you snmp not support 64 bit counter, do not use -6 option)"
exit 3
fi
In=`echo "$_result_in * 8 " |bc`
Out=`echo "$_result_out * 8 " |bc`
to_debug Time is $Time, In is $In, Out is $Out
HistData=`cat $CF_HIST_DATA| head -n 1`
HistTime=`echo $HistData| awk -F "|" '{print $1}'|sed 's/ //g'`
HistIn=`echo $HistData| awk -F "|" '{print $2}'|sed 's/ //g'`
HistOut=`echo $HistData| awk -F "|" '{print $3}'|sed 's/ //g'`
to_debug HistTime is $HistTime, HistIn is $HistIn, HistOut is $HistOut
#If there is a null hist file, can write the data before exit(for the reason of IsFirst),
# the data can be used for next time.
echo "$Time|$In|$Out" > $CF_HIST_DATA
if [ $? -ne 0 ];then
Severity=3
Msg="Unknown"
OutPut="Write File $CF_HIST_DATA Error with user `id`."
$Echo "$Msg" "-" $OutPut
exit $Severity
fi
if [ -z "$HistTime" -o -z "$HistIn" -o -z "$HistOut" ] ; then
echo "$Time|$In|$Out" > $CF_HIST_DATA
if [ "$IsFirst" = "True" ]; then
Severity="0"
Msg="OK"
OutPut="It's the first time for this plugins run. We'll get the data from the next time."
else
Severity="3"
Msg="Unknown"
OutPut="Can not found data in the history data file. \
Please to check the file $CF_HIST_DATA ,or use use verbose mode and check the debug file"
fi
$Echo "$Msg" "-" $OutPut
exit $Severity
fi
Interval=`echo "$Time - $HistTime" | bc`
if [ $Interval -lt $Min_Interval ] ; then
$Echo "The check interval must greater than $Min_Interval Seconds. But now it's $Interval. \
Please retry it later."
exit 3
fi
DiffIn=`echo "$In - $HistIn" | bc`
DiffOut=`echo "$Out - $HistOut" | bc`
if [ ` echo " $Interval > 0 " |bc ` -eq 0 ] ; then
$Echo "we got a negative time interval value here."
exit 3
fi
if [ ` echo " $DiffOut > 0 " |bc ` -eq 0 -o ` echo " $DiffIn > 0 " |bc ` -eq 0 ] ; then
$Echo "Maybe 32 bit counter overflow, because we got a negative value here."
exit 3
fi
In=`echo "$DiffIn / $Interval" | bc`
Out=`echo "$DiffOut / $Interval" | bc`
In=`echo "$In / 1024" | bc`
Out=`echo "$Out / 1024" | bc`
if [ "$isB" = "True" ]; then
In=`echo "scale=$Scale; $In / 8" | bc`
Out=`echo "scale=$Scale; $Out / 8" | bc`
fi
if [ "$isM" = "True" ]; then
In=`echo "scale=$Scale; $In / 1024" | bc`
Out=`echo "scale=$Scale; $Out / 1024" | bc`
fi
to_debug Unit_1 is $Unit_1, Unit_2 is $Unit_2
to_debug Interval is $Interval, DiffIn is $DiffIn, DiffOut is $DiffOut, In is $In, Out is $Out
if [ $UseRange = "True" ] ;then
check_w1b=`echo "$In > $W1b" | bc`
check_w1e=`echo "$In < $W1e" | bc`
check_w2b=`echo "$Out > $W2b" | bc`
check_w2e=`echo "$Out < $W2e" | bc`
to_debug check_w1 is $check_w1b $check_w1e , check_w2 is $check_w2b $check_w2e
check_c1b=`echo "$In > $C1b" | bc`
check_c1e=`echo "$In < $C1e" | bc`
check_c2b=`echo "$Out > $C2b" | bc`
check_c2e=`echo "$Out < $C2e" | bc`
to_debug check_c1 is $check_c1b $check_c1e, check_c2 is $check_c2b $check_c2e
if [ $check_w1b -eq 1 -a $check_w1e -eq 1 -a $check_w2b -eq 1 -a $check_w2e -eq 1 ] ; then
Severity="0";
Msg="OK";
to_debug Severity is $Severity , Msg is $Msg
elif [ $check_c1b -eq 1 -a $check_c1e -eq 1 -a $check_c2b -eq 1 -a $check_c2e -eq 1 ] ; then
Severity="1";
Msg="Warning";
to_debug Severity is $Severity , Msg is $Msg
else
Severity="2";
Msg="Critical";
to_debug Severity is $Severity , Msg is $Msg
fi
else
check_w1=`echo "$In < $W1" | bc`
check_w2=`echo "$Out < $W2" | bc`
to_debug check_w1 is $check_w1 , check_w2 is $check_w2
check_c1=`echo "$In < $C1" | bc`
check_c2=`echo "$Out < $C2" | bc`
to_debug check_c1 is $check_c1 , check_c2 is $check_c2
if [ $check_w1 -eq 1 -a $check_w2 -eq 1 ] ; then
Severity="0";
Msg="OK";
to_debug Severity is $Severity , Msg is $Msg
elif [ $check_c1 -eq 1 -a $check_c2 -eq 1 ] ; then
Severity="1";
Msg="Warning";
to_debug Severity is $Severity , Msg is $Msg
else
Severity="2";
Msg="Critical";
to_debug Severity is $Severity , Msg is $Msg
fi
fi
Total=`echo "$In + $Out" | bc`
if [ `echo "$In < 1" | bc` -eq 1 ]; then
In="0"${In}
if [ "$In" = "00" ]; then
In=0.0
fi
fi
if [ `echo "$Out < 1" | bc` -eq 1 ]; then
Out="0"${Out}
if [ "$Out" = "00" ]; then
Out=0.0
fi
fi
if [ `echo "$Total < 1" | bc` -eq 1 ]; then
Total="0"${Total}
if [ "$Total" = "00" ]; then
Total=0.0
fi
fi
if [ $UseRange = "True" ] ;then
$Echo "$Msg" "-" The Traffic In is "$In"${Unit_1}${Unit_2}, Out is "$Out"${Unit_1}${Unit_2}, Total is "$Total"${Unit_1}${Unit_2}. The Check Interval is "$Interval"s \|In\=${In}${Unit_1}${Unit_2}\;\;\;0\;0 Out\=${Out}${Unit_1}${Unit_2}\;\;\;0\;0 Total\=${Total}${Unit_1}${Unit_2}\;\;\;0\;0 Interval\=${Interval}s\;1200\;1800\;0\;0
exit $Severity
else
$Echo "$Msg" "-" The Traffic In is "$In"${Unit_1}${Unit_2}, Out is "$Out"${Unit_1}${Unit_2}, Total is "$Total"${Unit_1}${Unit_2}. The Check Interval is "$Interval"s \|In\=${In}${Unit_1}${Unit_2}\;${W1}\;${C1}\;0\;0 Out\=${Out}${Unit_1}${Unit_2}\;${W2}\;${C2}\;0\;0 Total\=${Total}${Unit_1}${Unit_2}\;${Wt}\;${Ct}\;0\;0 Interval\=${Interval}s\;1200\;1800\;0\;0
exit $Severity
fi
# End of check_traffic.sh