###############################################################
VERSION="1.6.0"
IFConFIG=/sbin/ifconfig
GREP=/bin/grep
CUT=/usr/bin/cut
INTERFACE=""
LEVEL_WARN="0"
LEVEL_CRIT="0"
RESULT=""
EXIT_STATUS=3
USE_IFConFIG=false
IFERRORS=false
TCPSTATS=false
ERRORTMPFILE=/tmp/check_netio
export LANG=en_EN.UTF-8 # We need ifconfig in English
###############################################################
## FUNCTIONS
## Print usage
usage() {
echo " check_netio $VERSION - Monitoring plugin to check network interface and I/O"
echo ""
echo " USAGE: check_netio.sh -i INTERFACE [-l] [-e] [-t] [-r] [-h]"
echo ""
echo " -i Interface to check (e.g. eth0)"
echo " -l Use legacy mode (use ifconfig command)"
echo " -e Enable check of interface errors"
echo " -t Enable tcp statistics (system-wide, not limited to chosen interface)"
echo " -r Comma-separated list of strings for regular expression lookup in tcp statistics (in combination with -t)"
echo " -h Show this page"
echo ""
}
## Process command line options
doopts() {
if ( `test 0 -lt $#` ); then
while getopts i:letr:h myarg "$@"; do
case $myarg in
h|\?) usage; exit;;
i) INTERFACE=$OPTARG;;
l) USE_IFConFIG=true;;
e) IFERRORS=true;;
r) REGEX=$OPTARG;;
t) TCPSTATS=true;;
*) usage; exit;;
esac
done
else
usage; exit
fi
}
# Write output and return result
theend() {
echo $RESULT
exit $EXIT_STATUS
}
## END FUNCTIONS
###############################################################
## MAIN
# Handle command line options
doopts $@
# Get the full output from /proc/net/dev
INTERFACES_FULL="`cat /proc/net/dev`"
# Verify that interface exists
if ! [ -L /sys/class/net/$INTERFACE ]; then
RESULT="NETIO UNKNOWN - No interface $INTERFACE found"; EXIT_STATUS=3
theend
fi
if [ $USE_IFConFIG = true ]; then
# Get the full ifconfig output from the selected interface
IFCONFIG_FULL=`$IFCONFIG $INTERFACE`
fi
if [ $TCPSTATS = true ]; then
# Collect netstat stats
NETSTAT_FULL=`cat /proc/net/netstat`
fi
# For legacy reasons we keep this information here:
# Check what kind of ifconfig response we get. Here are a few examples.
#
# Typical Linux -2017:
#eth0 Link encap:Ethernet HWaddr 00:50:56:99:35:34
# inet addr:10.161.204.204 Bcast:10.161.204.255 Mask:255.255.255.0
# inet6 addr: fe80::250:56ff:fe99:3534/64 Scope:Link
# UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
# RX packets:13244146360 errors:0 dropped:47729 overruns:0 frame:0
# TX packets:12690444622 errors:0 dropped:0 overruns:0 carrier:0
# collisions:0 txqueuelen:1000
# RX bytes:10473813937684 (10.4 TB) TX bytes:1956197200532 (1.9 TB)
#
# Starting in 2017, first seen in RHEL/Centos 7:
#eno16777984: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
# inet 10.162.215.71 netmask 255.255.255.0 broadcast 10.162.215.255
# inet6 fe80::250:56ff:fe8d:5c15 prefixlen 64 scopeid 0x20<link>
# ether 00:50:56:8d:5c:15 txqueuelen 1000 (Ethernet)
# RX packets 1419523582 bytes 5884437221627 (5.3 TiB)
# RX errors 0 dropped 130904 overruns 0 frame 0
# TX packets 771824547 bytes 252382597591 (235.0 GiB)
# TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
if [ $USE_IFConFIG = true ]; then
# So user dediced to use ifconfig
if [[ -n $(echo $IFCONFIG_FULL | grep "RX packets:") ]]; then
# This is the old ifconfig output
BYTES_RX=`$IFCONFIG $INTERFACE | $GREP 'bytes' | $CUT -d":" -f2 | $CUT -d" " -f1`
BYTES_TX=`$IFCONFIG $INTERFACE | $GREP 'bytes' | $CUT -d":" -f3 | $CUT -d" " -f1`
else
# This is the new ifconfig output 2017 and newer
BYTES_RX=`$IFCONFIG $INTERFACE | $GREP 'bytes' | $GREP 'RX packets' | awk '{print $5}'`
BYTES_TX=`$IFCONFIG $INTERFACE | $GREP 'bytes' | $GREP 'TX packets' | awk '{print $5}'`
fi
else
# Hurray, we directly parse the /proc/net/dev output and save time
BYTES_RX=$( echo "$INTERFACES_FULL" | awk "/${INTERFACE}:/ {print \$2}")
ERRORS_RX=$( echo "$INTERFACES_FULL" | awk "/${INTERFACE}:/ {print \$4}")
DROPS_RX=$( echo "$INTERFACES_FULL" | awk "/${INTERFACE}:/ {print \$5}")
BYTES_TX=$( echo "$INTERFACES_FULL" | awk "/${INTERFACE}:/ {print \$10}")
ERRORS_TX=$( echo "$INTERFACES_FULL" | awk "/${INTERFACE}:/ {print \$12}")
DROPS_TX=$( echo "$INTERFACES_FULL" | awk "/${INTERFACE}:/ {print \$13}")
fi
# Handle netstat stats
if [ $TCPSTATS = true ]; then
IFS=' '
read -r -a key <<< `echo $NETSTAT_FULL|sed -ne "1p"|$CUT -d ' ' -f 1 --complement`
read -r -a value <<< `echo $NETSTAT_FULL|sed -ne "2p"|$CUT -d ' ' -f 1 --complement`
i=0
for key in ${key[*]}; do
if [[ -n ${REGEX} ]]; then
declare -a regex_list=($(echo "$REGEX" | sed 's/,/ /g'))
shopt -s nocasematch
for regex in ${regex_list[*]}; do
if [[ ${key} =~ "${regex}" ]]; then
tcpperfdata[$i]="${key[$i]}=${value[$i]};;;; "
fi
done
else
tcpperfdata[$i]="${key[$i]}=${value[$i]};;;; "
fi
let i++
done
else tcpperfdata=""
fi
# Handle interface errors
if [ $IFERRORS = true ]; then
if [ -f ${ERRORTMPFILE}_${INTERFACE} ]; then
# Oh, we already saw errors before, compare values
PREVIOUS_ERRORS_RX=$(tail -n 2 ${ERRORTMPFILE}_${INTERFACE} | awk "/ERRORS_RX/ {print \$3}")
PREVIOUS_ERRORS_TX=$(tail -n 2 ${ERRORTMPFILE}_${INTERFACE} | awk "/ERRORS_TX/ {print \$3}")
PACKET_COUNT=4
if [[ $ERRORS_RX -gt $(echo "$PREVIOUS_ERRORS_RX + $PACKET_COUNT" |bc) || $ERRORS_TX -gt $(echo "$PREVIOUS_ERRORS_TX + $PACKET_COUNT" |bc) ]]; then
echo "$(date +%s) ERRORS_RX $ERRORS_RX" >> ${ERRORTMPFILE}_${INTERFACE}
echo "$(date +%s) ERRORS_TX $ERRORS_TX" >> ${ERRORTMPFILE}_${INTERFACE}
RESULT="NETIO WARNING - Errors on $INTERFACE: $ERRORS_RX Receive errors (previous check: $PREVIOUS_ERRORS_RX), $ERRORS_TX Transmit errors (previous check: $PREVIOUS_ERRORS_TX)|NET_${INTERFACE}_RX=${BYTES_RX}B;;;; NET_${INTERFACE}_TX=${BYTES_TX}B;;;; NET_${INTERFACE}_ERR_RX=${ERRORS_RX};;;; NET_${INTERFACE}_ERR_TX=${ERRORS_TX};;;; NET_${INTERFACE}_DROP_RX=${DROPS_RX};;;; NET_${INTERFACE}_DROP_TX=${DROPS_TX};;;; ${tcpperfdata[*]}"
EXIT_STATUS=1
else
# output ok with hint that no change in error count
RESULT="NETIO OK - $INTERFACE: Receive $BYTES_RX Bytes, Transmit $BYTES_TX Bytes - Hint: Previously detected errors (Receive: $PREVIOUS_ERRORS_RX, Transmit: $PREVIOUS_ERRORS_TX) but no change since last check|NET_${INTERFACE}_RX=${BYTES_RX}B;;;; NET_${INTERFACE}_TX=${BYTES_TX}B;;;; NET_${INTERFACE}_ERR_RX=${ERRORS_RX};;;; NET_${INTERFACE}_ERR_TX=${ERRORS_TX};;;; NET_${INTERFACE}_DROP_RX=${DROPS_RX};;;; NET_${INTERFACE}_DROP_TX=${DROPS_TX};;;; ${tcpperfdata[*]}"
EXIT_STATUS=0
fi
else # Check if we got errors
if [[ $ERRORS_RX -gt 0 || $ERRORS_TX -gt 0 ]]; then
echo "$(date +%s) ERRORS_RX $ERRORS_RX" >> ${ERRORTMPFILE}_${INTERFACE}
echo "$(date +%s) ERRORS_TX $ERRORS_TX" >> ${ERRORTMPFILE}_${INTERFACE}
RESULT="NETIO WARNING - Errors on $INTERFACE: $ERRORS_RX Receive errors, $ERRORS_TX Transmit errors|NET_${INTERFACE}_RX=${BYTES_RX}B;;;; NET_${INTERFACE}_TX=${BYTES_TX}B;;;; NET_${INTERFACE}_ERR_RX=${ERRORS_RX};;;; NET_${INTERFACE}_ERR_TX=${ERRORS_TX};;;; NET_${INTERFACE}_DROP_RX=${DROPS_RX};;;; NET_${INTERFACE}_DROP_TX=${DROPS_TX};;;; ${tcpperfdata[*]}"
EXIT_STATUS=1
else
RESULT="NETIO OK - $INTERFACE: Receive $BYTES_RX Bytes, Transmit $BYTES_TX Bytes|NET_${INTERFACE}_RX=${BYTES_RX}B;;;; NET_${INTERFACE}_TX=${BYTES_TX}B;;;; NET_${INTERFACE}_ERR_RX=${ERRORS_RX};;;; NET_${INTERFACE}_ERR_TX=${ERRORS_TX};;;; NET_${INTERFACE}_DROP_RX=${DROPS_RX};;;; NET_${INTERFACE}_DROP_TX=${DROPS_TX};;;; ${tcpperfdata[*]}"
EXIT_STATUS=0
fi
fi
else # No error handling, just output the stats
RESULT="NETIO OK - $INTERFACE: Receive $BYTES_RX Bytes, Transmit $BYTES_TX Bytes|NET_${INTERFACE}_RX=${BYTES_RX}B;;;; NET_${INTERFACE}_TX=${BYTES_TX}B;;;; NET_${INTERFACE}_ERR_RX=${ERRORS_RX};;;; NET_${INTERFACE}_ERR_TX=${ERRORS_TX};;;; NET_${INTERFACE}_DROP_RX=${DROPS_RX};;;; NET_${INTERFACE}_DROP_TX=${DROPS_TX};;;; ${tcpperfdata[*]}"
EXIT_STATUS=0
fi
# Quit and return information and exit status
theend