#!/usr/bin/perl
# check_tcptraffic is a nagios Nagios plugin to monitor the amount of TCP traffic
#
# check_tcptraffic uses the /proc/net/dev Linux entry to compute the
# amount of transferred bytes from the last plugin execution (temporary
# data is stored in the /tmp/check_tcptraffic-iface file)
#
# See the INSTALL file for installation instructions
#
# Copyright (c) 2010-2011, ETH Zurich.
#
# This module is free software; you can redistribute it and/or modify it
# under the terms of GNU general public license (gpl) version 3.
# See the LICENSE file for details.
#
# RCS information
# enable substitution with:
# $ svn propset svn:keywords "Id Revision HeadURL Source Date"
#
# $Id: check_tcptraffic 1285 2012-03-13 15:05:08Z corti $
# $Revision: 1285 $
# $HeadURL: https://svn.id.ethz.ch/nagios_plugins/check_tcptraffic/check_tcptraffic $
# $Date: 2012-03-13 16:05:08 +0100 (Tue, 13 Mar 2012) $
use 5.00800;
use strict;
use warnings;
use Carp;
use English qw(-no_match_vars);
use Nagios::Plugin::Threshold;
use Nagios::Plugin;
use Nagios::Plugin::Range;
use Nagios::Plugin::Getopt;
use Readonly;
################################################################################
# constants
our $VERSION = '2.2.4';
Readonly my $MAX_LONG => 2**32;
# IMPORTANT: Nagios plugins could be executed using embedded perl in this case
# the main routine would be executed as a subroutine and all the
# declared subroutines would therefore be inner subroutines
# This will cause all the global lexical variables not to stay shared
# in the subroutines!
#
# All variables are therefore declared as package variables...
#
## no critic (ProhibitPackageVars)
use vars qw(
$help
$log_file
$options
$os_64_bit
$plugin
$result
$status
$status_msg
$threshold
$tmp
);
##############################################################################
# subroutines
use subs qw(verbose);
##############################################################################
# Usage : debug( "message" )
# Purpose : writes a debugging message to the console and optionally to the
# debugging log file
# Returns : n/a
# Arguments : the debugging message
# Throws : n/a
# Comments : n/a
# See also : n/a
sub debug {
my $message = shift;
chomp $message;
if ( $options->debug() ) {
print "[DEBUG] $message\n"; ## no critic (RequireCheckedSyscalls)
}
if ( $options->debug_log() ) {
open $log_file, '>>', $options->debug_log()
or $plugin->nagios_exit( UNKNOWN,
'Cannot open ' . $options->debug_log() . ": $OS_ERROR" );
print {$log_file} time
. " $message\n"
or $plugin->nagios_exit( UNKNOWN,
'Cannot write to ' . $options->debug_log() . ": $OS_ERROR" );
close $log_file
or $plugin->nagios_exit( UNKNOWN,
'Cannot close ' . $options->debug_log() . ": $OS_ERROR" );
}
return;
}
##############################################################################
# Usage : running_on_linux()
# Purpose : check if running on a Linux system
# Returns : true if running on Linux
# Arguments : n/a
# Throws : n/a
# Comments : also checks if the OS is 64 bit capable and sets $os_64_bit
# See also : n/a
sub running_on_linux { ## no critic (RequireFinalReturn)
my $output;
my $pid;
my $linux;
# check the system
$pid = open $output, q{-|}, 'uname'
or
$plugin->nagios_exit( UNKNOWN, "Cannot determine the system: $OS_ERROR" );
while (<$output>) {
chomp;
verbose "running on $_\n";
$linux = $_ eq 'Linux';
close $output
or $plugin->nagios_exit( UNKNOWN, "Cannot close output: $OS_ERROR" );
# check the OS (32 or 64 bit)
$pid = open $output, q{-|}, 'uname -m'; ## no critic (RequireBriefOpen)
if ( !$pid ) {
carp "Cannot determine if the system is 64 bit capable: $OS_ERROR";
return $linux;
}
while (<$output>) {
chomp;
$os_64_bit = ( $_ eq 'x86_64' );
my $message;
if ($os_64_bit) {
$message = '64';
}
else {
$message = '32';
}
debug( $message . 'bit system' );
return $linux;
}
carp "Cannot determine if the system is 64 bit capable: $OS_ERROR";
return $linux;
}
$plugin->nagios_exit( UNKNOWN, 'Cannot determine the system' );
}
##############################################################################
# Usage : whoami()
# Purpose : retrieve the user runnging the process
# Returns : username
# Arguments : n/a
# Throws : n/a
# Comments : n/a
# See also : n/a
sub whoami {
my $output;
my $pid = open $output, q{-|}, 'whoami' ## no critic (RequireBriefOpen)
or
$plugin->nagios_exit( UNKNOWN, "Cannot determine the user: $OS_ERROR" );
while (<$output>) {
chomp;
debug("user $_");
return $_;
}
$plugin->nagios_exit( UNKNOWN, 'Cannot determine the user' );
return;
}
##############################################################################
# Usage : write_timer($data_in, $data_out)
# Purpose : writes the time and transmit data to the temporary file
# Returns : n/a
# Arguments : n/a
# Throws : n/a
# Comments : n/a
# See also : n/a
sub write_timer {
my $in = shift;
my $out = shift;
my $TMP; # file handler
my $dbg_message = "writing to $tmp";
if ( !open $TMP, q{>}, $tmp ) { ## no critic (RequireBriefOpen)
debug("error: $OS_ERROR");
$plugin->nagios_exit( UNKNOWN, "Cannot initialize timer: $OS_ERROR" );
}
my $line = time . " $in $out\n";
print {$TMP} $line; ## no critic (RequireCheckedSyscalls)
debug("$dbg_message: $line");
close $TMP
or $plugin->nagios_exit( UNKNOWN, "Cannot close timer: $OS_ERROR" );
return;
}
##############################################################################
# Usage : read_proc('eth0')
# Purpose : reads information about an interface in the proc file system
# Returns : an hash containing the interface info
# Arguments : iface : interface name
# Throws : n/a
# Comments : n/a
# See also : n/a
sub read_proc {
my $iface = shift;
my %data;
my $found = 0;
my $in;
my $out;
my $time;
my $IN; # file descriptor
my $TMP; # file descriptor
my $dev_file = '/proc/net/dev';
open $IN, q{<}, $dev_file ## no critic (RequireBriefOpen)
or $plugin->nagios_exit( UNKNOWN, "Cannot open $dev_file: $OS_ERROR" );
while (<$IN>) {
chomp;
if (/:/mxs) {
# /proc/net/dev format
#
# bytes: The total number of bytes of data transmitted or received
# by the interface.
# packets: The total number of packets of data transmitted or
# received by the interface.
# errs: The total number of transmit or receive errors detected
# by the device driver.
# drop: The total number of packets dropped by the device driver.
# fifo The number of FIFO buffer errors.
# frame: The number of packet framing errors.
# compressed: The number of compressed packets transmitted or received
# by the device driver.
# multicast: The number of multicast frames transmitted or received by
# the device driver.
# hack: some kernels now put some whitespace between the colon and bytes in
s/:\s+/:/mxs;
my (
$combined, $packets_in, $errs_in,
$drop_in, $fifo_in, $frame_in,
$compressed_in, $multicast_in, $bytes_out,
$packets_out, $errs_out, $drop_out,
$fifo_out, $frame_out, $compressed_out,
$multicast_out
) = split;
my ( $name, $bytes_in ) = split /:/mxs, $combined;
if ( $name eq $options->interface() ) {
$found = 1;
$data{combined} = $combined;
$data{packets_in} = $packets_in;
$data{errs_in} = $errs_in;
$data{drop_in} = $drop_in;
$data{fifo_in} = $fifo_in;
$data{frame_in} = $frame_in;
$data{compressed_in} = $compressed_in;
$data{multicast_in} = $multicast_in;
$data{bytes_out} = $bytes_out;
$data{packets_out} = $packets_out;
$data{errs_out} = $errs_out;
$data{drop_out} = $drop_out;
$data{fifo_out} = $fifo_out;
$data{frame_out} = $frame_out;
$data{compressed_out} = $compressed_out;
$data{multicast_out} = $multicast_out;
$data{bytes_in} = $bytes_in;
# get the time difference
if ( $options->reset() || !open $TMP, q{<}, $tmp )
{ ## no critic (RequireBriefOpen)
write_timer( $bytes_in, $bytes_out );
$plugin->nagios_exit( UNKNOWN, 'Initializing timer' );
}
while (<$TMP>) {
chomp;
( $time, $in, $out ) = split;
$data{diff} = time - $time;
$data{in} = $in;
$data{out} = $out;
debug("reading old data: $time $in $out after $data{diff}");
}
close $TMP
or $plugin->nagios_exit( UNKNOWN,
"Cannot close $tmp: $OS_ERROR" );
write_timer( $bytes_in, $bytes_out );
last;
}
}
}
if ( !$found ) {
$plugin->nagios_exit( UNKNOWN,
'Interface ' . $options->interface() . ' not found' );
}
close $IN
or $plugin->nagios_exit( UNKNOWN, "Cannot close $dev_file: $OS_ERROR" );
return %data;
}
##############################################################################
# Usage : verbose("some message string", $optional_verbosity_level);
# Purpose : write a message if the verbosity level is high enough
# Returns : n/a
# Arguments : message : message string
# level : options verbosity level
# Throws : n/a
# Comments : n/a
# See also : n/a
sub verbose {
# arguments
my $message = shift;
my $level = shift;
if ( !defined $message ) {
$plugin->nagios_exit( UNKNOWN,
q{Internal error: not enough parameters for 'verbose'} );
}
if ( !defined $level ) {
$level = 0;
}
debug($message);
if ( $level < $options->verbose()
|| $options->debug() )
{
print $message; ## no critic (RequireCheckedSyscalls)
}
return;
}
##############################################################################
# main
#
################
# Initialization
$os_64_bit = 0;
$plugin = Nagios::Plugin->new( shortname => 'TCPTRAFFIC' );
$status = 0;
$status_msg = q{};
########################
# Command line arguments
$options = Nagios::Plugin::Getopt->new(
usage => 'Usage: %s [OPTIONS]',
version => $VERSION,
url => 'https://trac.id.ethz.ch/projects/nagios_plugins',
blurb => 'Monitors the amount of traffic on a given interface',
);
$options->arg(
spec => 'critical|c=s',
help =>
'Exit with CRITICAL status if traffic is outside of the specified range',
required => 1,
);
$options->arg(
spec => 'warning|w=s',
help =>
'Exit with WARNING status if traffic is outside of the specified range',
required => 1,
);
$options->arg(
spec => 'interface|i=s',
help => 'network interface to monitor',
required => 1,
);
$options->arg(
spec => 'speed|s=i',
help => 'speed (in Mbit/s)',
required => 1,
);
$options->arg(
spec => 'reset|r',
help => 'initialize counter',
);
$options->arg(
spec => 'debug',
help => 'debugging output',
required => 0,
);
$options->arg(
spec => 'debug_log=s',
help => 'generates a log with debugging information',
required => 0,
);
$options->getopts();
###############
# Sanity checks
if ( $options->debug_log() && ( !-w $options->debug_log() ) ) {
$plugin->nagios_exit( UNKNOWN, $options->debug_log() . ' is not writable' );
}
debug('------------------------------------------');
if ( !running_on_linux() ) {
$plugin->nagios_exit( UNKNOWN, 'Not running on a Linux system' );
}
# parse the critical and warning ranges
my $critical_range =
Nagios::Plugin::Range->parse_range_string( $options->critical() );
if ( !( $critical_range && $critical_range->is_set() ) ) {
$plugin->nagios_exit( UNKNOWN, 'Could not parse "critical"' );
}
my $warning_range =
Nagios::Plugin::Range->parse_range_string( $options->warning() );
if ( !( $warning_range && $warning_range->is_set() ) ) {
$plugin->nagios_exit( UNKNOWN, 'Could not parse "warning"' );
}
# check the speed (depending on the counter size in bits)
my $max_check_time; # in seconds
# size max (bytes) max (bits) max (Mbits)
# -----------------------------------------------------------
# 64 bit 2^32 2^64*8 -> 2^67 2^67 / 1024^2 -> 2^47
# 32 bit 2^32 2^32*8 -> 2^35 2^38 / 1024^2 -> 2^15
#<<<
if ($os_64_bit) {
$max_check_time = int( ( 2**47 - 1 ) / $options->speed() ); ## no critic (ProhibitMagicNumbers)
}
else {
$max_check_time = int( ( 2**15 - 1 ) / $options->speed() ); ## no critic (ProhibitMagicNumbers)
}
#>>>
verbose
"the counters can hold data for $max_check_time seconds before overflow\n";
$threshold = Nagios::Plugin::Threshold->set_thresholds(
warning => $warning_range,
critical => $critical_range,
);
$tmp = '/tmp/check_tcptraffic_status-' . $options->interface() . whoami();
########################
# Check the proc entry
my %data = read_proc( $options->interface() );
if ( $data{diff} > $max_check_time ) {
verbose 'the time from last check ('
. $data{diff}
. 's) is greater than the maximum check time allowed with speed '
. $options->speed() . ' ('
. $max_check_time
. "s): sleeping 1s to gather data again\n";
# time difference is > max_check_time
# since the counter could overflow
# we reeinitilize the timer and
# we perform a 1s check
write_timer( $data{bytes_in}, $data{bytes_out} );
sleep 1;
%data = read_proc( $options->interface() );
}
if ( $data{diff} == 0 ) {
# round up
$data{diff} = 1;
}
my $traffic_in;
my $traffic_out;
# in
if ( $data{bytes_in} >= $data{in} ) {
$traffic_in = int( ( $data{bytes_in} - $data{in} ) / $data{diff} );
debug(
"IN: $traffic_in = int( ( $data{bytes_in} - $data{in} ) / $data{diff} )"
);
}
else {
# the old value is larger than the new one: the counter overflowed
$traffic_in =
int( ( $MAX_LONG - $data{in} + $data{bytes_in} ) / $data{diff} );
debug(
"IN (overflow): $traffic_in = int( ( $MAX_LONG - $data{in} + $data{bytes_in} ) / $data{diff} )"
);
}
# out
if ( $data{bytes_out} >= $data{out} ) {
$traffic_out = int( ( $data{bytes_out} - $data{out} ) / $data{diff} );
debug(
"OUT: $traffic_out = int( ( $data{bytes_out} - $data{out} ) / $data{diff} )"
);
}
else {
# the old value is larger than the new one: the counter overflowed
$traffic_out =
int( ( $MAX_LONG - $data{out} + $data{bytes_out} ) / $data{diff} );
debug(
"OUT (overflow): $traffic_out = int( ( $MAX_LONG - $data{out} + $data{bytes_out} ) / $data{diff} )"
);
}
my $traffic = $traffic_in + $traffic_out;
debug("TOT: $traffic = $traffic_in + $traffic_out");
$plugin->add_perfdata(
label => 'TOTAL',
value => sprintf( '%.0f', $traffic ),
uom => 'B',
threshold => $threshold,
);
$plugin->add_perfdata(
label => 'IN',
value => sprintf( '%.0f', $traffic_in ),
uom => 'B',
);
$plugin->add_perfdata(
label => 'OUT',
value => sprintf( '%.0f', $traffic_out ),
uom => 'B',
);
$plugin->add_perfdata(
label => 'TIME',
value => sprintf( '%.0f', $data{diff} ),
uom => 'B',
);
$plugin->nagios_exit( $threshold->get_status($traffic),
$options->interface() . q{ } . sprintf( '%.0f', $traffic ) . ' bytes/s' );
1;
Copyright 2K16 - 2K18 Indonesian Hacker Rulez