#!/usr/bin/env perl
#------------------------------------------------------------------------------
#
#   Programm        : check_snmp_interface
#
#------------------------------------------------------------------------------
#
#   Beschreibung    : Nagios check script to observe the status of a network
#                     interface by SNMP
#
#   Autor           : Marek Zavesicky
#   Copyright (C)   : (2015-2017) Marek Zavesicky
#   Version         : 0.1.3
#   Erstellt        : 2015/10/22
#   Letztes Update  : 2017/01/24
#
#   Change history  : See "git log"
#
#   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.  See <http://www.fsf.org/copyleft/gpl.txt>.
#
#   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.
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#                     Pragma
#-------------------------------------------------------------------------------
use strict;
use warnings;

#-------------------------------------------------------------------------------
#                     Used Modules
#-------------------------------------------------------------------------------
use Monitoring::Plugin;
use Net::SNMP;
use Data::Dumper;

#-------------------------------------------------------------------------------
#   Package         : Interface
#-------------------------------------------------------------------------------
{
    package Interface;

    use strict;
    use warnings;

    use Data::Dumper;

    #-------------------------------------------------------------------------------
    #   Methode         : new
    #-------------------------------------------------------------------------------
    #   Description     : Create a new object of type Interface.
    #
    #   Input           : $class        Reference to this class
    #                     session       Reference to Net::SNMP object
    #                     ifindex       the SNMP index of the interface
    #   Output          : $self         The reference to this object
    #-------------------------------------------------------------------------------
    sub new
    {
        my $class = shift;
        my $self = {};

        bless $self, $class;
        $self->_init( shift, shift );

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _init
    #-------------------------------------------------------------------------------
    #   Description     : Initialisation methode for class Interface.
    #
    #   Input           : $self         Reference to this object
    #   Output          : $self         Reference to this object
    #-------------------------------------------------------------------------------
    sub _init
    {
        my $self = shift;
        my $key;

        $self->{ 'session' } = shift;
        $self->{ 'timegap' } = {
            'value'                     => time(),
            'oldvalue'                  => 0,
            'gap'                       => 0,
            'storable'                  => 1
        };
        $self->{ 'ifindex' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.1',
            'value'                     => shift,
            'type'                      => "numeric"
        };
        $self->{'ifdescr' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.2',
            'value'                     => '',
            'type'                      => "string"
        };
        $self->{ 'ifadminstatus' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.7',
            'value'                     => 0,
            'type'                      => "numeric",
            'status'                    => {
                                                1 => 'up',
                                                2 => 'down',
                                                3 => 'testing'
                                            }
        };
        $self->{ 'ifoperstatus' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.8',
            'value'                     => 0,
            'type'                      => "numeric",
            'status'                    => {
                                                1 => 'up',
                                                2 => 'down',
                                                3 => 'testing',
                                                4 => 'unknown',
                                                5 => 'dormant',
                                                6 => 'notpresent',
                                                7 => 'lowerlayerdown'
                                            }
        };
        $self->{ 'ifindiscards' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.13',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "pkts",
            'type'                      => "numeric",
            'storable'                  => 1
        };
        $self->{ 'ifinerrors' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.14',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "pkts",
            'type'                      => "numeric",
            'storable'                  => 1
        };
        $self->{ 'ifoutdiscards' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.19',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "pkts",
            'type'                      => "numeric",
            'storable'                  => 1
        };
        $self->{ 'ifouterrors' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.20',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "pkts",
            'type'                      => "numeric",
            'storable'                  => 1
        };

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setGap
    #-------------------------------------------------------------------------------
    #   Description     : Calculate the gap between the first and the last measure.
    #
    #   Input           : $self         Reference to this object
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setGap
    {
        my $self = shift;

        $self->setByKey( $self->getByKey( 'timegap', 'value' ) - $self->getByKey( 'timegap', 'oldvalue' ), 'timegap', 'gap' );
    }

    #-------------------------------------------------------------------------------
    #   Methode         : getByKey
    #-------------------------------------------------------------------------------
    #   Description     : Get value by key name.
    #
    #   Input           : $self         Reference to this object
    #                     $key          Key name of the needed value
    #   Output          : $value        The value of the requestet key
    #-------------------------------------------------------------------------------
    sub getByKey
    {
        my $self = shift;
        my $key = shift;
        my $subkey = shift;
        my $subsubkey = shift;

        return $self->{ lc( $key ) }->{ lc( $subkey ) }->{ lc( $subsubkey ) } if ( $subsubkey );
        return $self->{ lc( $key ) }->{ lc( $subkey ) } if ( $subkey );
        return $self->{ lc( $key ) };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setByKey
    #-------------------------------------------------------------------------------
    #   Description     : Set value by key name.
    #
    #   Input           : $self         Reference to this object
    #                     $key          Name of the key that needs to be set
    #                     $value        The value of the given key
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setByKey
    {
        my $self = shift;
        my $value = shift;
        my $key = shift;
        my $subkey = shift;

        if ( $subkey )
        {
            $self->{ lc( $key ) }->{ lc( $subkey ) } = $value;
        }
        else
        {
            $self->{ lc( $key ) } = $value;
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _isNumber
    #-------------------------------------------------------------------------------
    #   Description     : Check if a value is either a number or string and return
    #                     the result.
    #
    #   Input           : $self         Reference to this object
    #                     $value        The value that has to be checked
    #   Output          : 0/1           Return 1 if value is a number else 0
    #-------------------------------------------------------------------------------
    sub _isNumber
    {
        my $self = shift;
        my $value = shift;

        ( $value =~ /^-?\d+\.?\d*$/ ) ? return 1 : return 0;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setKeyByOID
    #-------------------------------------------------------------------------------
    #   Description     : Set value for key by OID.
    #
    #   Input           : $self         Reference to this object
    #                     $key          Key of the OID that needs to be set
    #                     $oid          The OID that needs to be set
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setKeyByOID
    {
        my $self = shift;
        my $key = shift;
        my $oid = shift;
        my $value;

        $value = $self->{ session }->get_request( $oid )->{ $oid };

        if ( $self->getByKey( $key, 'type' ) eq "numeric" and $self->_isNumber( $value ) )
        {
            if ( $self->getByKey( $key, 'factor' ) )
            {
                $self->setByKey( $value * $self->getByKey( $key, 'factor' ), $key, 'value' );
            }
            elsif ( $self->getByKey( $key, 'status' ) )
            {
                $self->setByKey( $self->getByKey( $key, 'status', $value ), $key, 'value' );
            }
            else
            {
                $self->setByKey( $value, $key, 'value' );
            }
        }
        elsif ( $self->getByKey( $key, 'type' ) eq "string" and not $self->_isNumber( $value ) )
        {
            $self->setByKey( $value, $key, 'value' );
        }
        else
        {
            $self->setByKey( 0, $key, 'value' );
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setDiffByKey
    #-------------------------------------------------------------------------------
    #   Description     : Set the diference between to measures for a given key.
    #
    #   Input           : $self         Reference to this object
    #                     $key          The key of the object variable to be set
    #                     $cnt1         The last measured counter
    #                     $cnt2         The counter measured before
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setDiffByKey
    {
        my $self = shift;
        my $key = shift;
        my $cntsize = shift;

        if ( $self->getByKey( $key, 'value' ) ge $self->getByKey( $key, 'oldvalue' ) )
        {
            $self->setByKey( ( $self->getByKey( $key, 'value' ) - $self->getByKey( $key, 'oldvalue' ) ) / $self->getByKey( 'timegap', 'gap' ), $key, 'value' );
        }
        else
        {
            $self->setByKey( 0, $key, 'value' );
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setFileData
    #-------------------------------------------------------------------------------
    #   Description     : Create hash entries for the data stored in the
    #                     file.
    #
    #   Input           : $self         Reference to this object
    #                     $key          The key of the measure
    #                     $value        The value of that key
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setFileData
    {
        my $self = shift;
        my $key = shift;
        my $value = shift;

        if ( defined( $key ) )
        {
            $self->setByKey( $value, $key, 'oldvalue' );
        }
        else
        {
            print "Not defined key " . $key . "\n";
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : getFileData
    #-------------------------------------------------------------------------------
    #   Description     : Create the new content of the file.
    #
    #   Input           : $self         Reference to this object
    #   Output          : $data         The new content of a file
    #-------------------------------------------------------------------------------
    sub getFileData
    {
        my $self = shift;
        my $data = "";
        my $key = "";

        foreach $key ( keys %$self )
        {
            next unless ( $self->getByKey( $key, 'storable' ) );
            $data .= $key . " = " . $self->getByKey( $key, 'value' ) . "\n";
        }
        return $data;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _getFactor
    #-------------------------------------------------------------------------------
    #   Description     : Returns the factor for a unit prefix.
    #                     ''          1000**0
    #                     kilo    k   1000**1
    #                     mega    M   1000**2
    #                     giga    G   1000**3
    #                     tera    T   1000**4
    #                     peta    P   1000**5
    #                     exa     E   1000**6
    #                     zetta   Z   1000**7
    #                     yotta   Y   1000**8
    #
    #   Input           : $self         Reference to this object
    #                     $prefix       The unit prefix
    #   Output          : $factor       The factor
    #-------------------------------------------------------------------------------
    sub _getFactor
    {
        my $self = shift;
        my $prefix = shift;
        my $up = { 'k' => 1, 'M' => 2, 'G' => 3, 'T' => 4, 'P' => 5, 'E' => 6, 'Z' => 7, 'Y' => 8 };

        if ( $prefix )
        {
            return 1000**$up->{ $prefix };
        }
        else
        {
            return 1;
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : toUnit
    #-------------------------------------------------------------------------------
    #   Description     : Calculate from bps to the unit of choice.
    #
    #   Input           : $self         Reference to this object
    #                     $key          The key of the value which has to be
    #                                   converted
    #                     $unit         The unit to which the value has to be
    #                                   converted
    #   Output          : $value        The converted value
    #
    #-------------------------------------------------------------------------------
    sub toUnit
    {
        my $self = shift;
        my $key = shift;
        my $unit = shift;
        my $autoconvert = shift;
        my $value = $self->getByKey( $key, 'value' );
        my $up = { 0 => '', 1 => 'k', 2 => 'M', 3 => 'G', 4 => 'T', 5 => 'P', 6 => 'E', 7 => 'Z', 8 => 'Y' };

        $unit = $self->getByKey( $key, 'unit' ) if ( $self->getByKey( $key, 'unit' ) eq 'pkts' );
        return ( 0.00, $unit ) if ( $value eq 0 );

        if ( $unit =~ /Bps/ )
        {
            $value /= 8;
        }

        if ( $unit =~ /bps|Bps/ )
        {
            $unit = $up->{ int( length( int( $value ) ) / 3 ) } . substr( $unit, -3, 3 ) if ( $autoconvert );
            return ( sprintf( "%.2f", $value / $self->_getFactor( substr( $unit, 0, -3 ) ) ), $unit );
        }
        elsif ( $unit eq "%" )
        {
            my $speed = $self->getByKey( 'ifspeed', 'value' );
            if ( $speed eq 0 )
            {
                return ( 100.00, $unit );
            }
            else
            {
                return ( sprintf( "%.2f", $value *  100 / $speed ), $unit );
            }
        }
        else
        {
            return ( sprintf( "%.2f", $value ), $unit );
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : unitTo
    #-------------------------------------------------------------------------------
    #   Description     : Calculate from unit of choice to bits per second.
    #
    #   Input           : $self         Reference to this object
    #                     $key          The key of the value which has to be
    #                                   converted
    #                     $unit         The unit to which the value has to be
    #                                   converted
    #   Output          : $value        The converted value
    #-------------------------------------------------------------------------------
    sub unitTo
    {
        my $self = shift;
        my $value = shift;
        my $unit = shift;

        if ( $unit =~ /Bps/ )
        {
            $value *= 8;
        }

        if ( $unit =~ /bps|Bps/ )
        {
            return sprintf( "%.2f", $value * $self->_getFactor( substr( $unit, 0, -3 ) ) );
        }
        elsif ( $unit eq "%" )
        {
            my $speed = $self->getByKey( 'ifspeed', 'value' );
            if ( $speed eq 0 )
            {
                return 0.00;
            }
            else
            {
                return sprintf( "%.2f", $speed * $value / 100 );
            }
        }
        else
        {
            return sprintf( "%.2f", $value );
        }
    }
}

#-------------------------------------------------------------------------------
#   Package         : InterfaceV1
#-------------------------------------------------------------------------------
{
    package InterfaceV1;

    use parent -norequire, 'Interface';
    use strict;
    use warnings;

    #-------------------------------------------------------------------------------
    #   Methode         : new
    #-------------------------------------------------------------------------------
    #   Description     : Create a new object of type InterfaceV1.
    #
    #   Input           : $class        Reference to this class
    #                     session       Reference to Net::SNMP object
    #                     ifindex       the SNMP index of the interface
    #   Output          : $self         The reference to this object
    #-------------------------------------------------------------------------------
    sub new
    {
        my $class = shift;
        my $self = {};

        bless $self, $class;
        $self->SUPER::_init( shift, shift );
        $self->_init();

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _init
    #-------------------------------------------------------------------------------
    #   Description     : Initialisation methode for class InterfaceV1.
    #
    #   Input           : $self         Reference to this object
    #   Output          : $self         Reference to this object
    #-------------------------------------------------------------------------------
    sub _init
    {
        my $self = shift;
        my $key;

        $self->{ 'ifspeed' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.5',
            'value'                     => 0,
            'type'                      => "numeric"
        };
        $self->{ 'ifinoctets' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.10',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "bps",
            'type'                      => "numeric",
            'factor'                    => 8,
            'storable'                  => 1
        };
        $self->{ 'ifoutoctets' } = {
            'oid'                       => '.1.3.6.1.2.1.2.2.1.16',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "bps",
            'type'                      => "numeric",
            'factor'                    => 8,
            'storable'                  => 1
        };

        foreach $key ( keys %$self )
        {
            next unless ( $key =~ /^if/ );
            $self->setKeyByOID( $key, $self->getByKey( $key, 'oid' ) . "." . $self->getByKey( 'ifindex', 'value' ) );
        }

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setDiffByKey
    #-------------------------------------------------------------------------------
    #   Description     : Set the diference between to measures for a given key.
    #
    #   Input           : $self         Reference to this object
    #                     $key          The key of the object variable to be set
    #                     $cnt1         The last measured counter
    #                     $cnt2         The counter measured before
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setDiffByKey
    {
        my $self = shift;
        my $key = shift;

        $self->SUPER::setDiffByKey( $key, 2**32 )
    }

    #-------------------------------------------------------------------------------
    #   Methode         : printAll
    #-------------------------------------------------------------------------------
    #   Description     : This methode prints all the key value pairs. Only used
    #                     for verbose output.
    #
    #   Input           : $self         Reference to this object
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub printAll
    {
        my $self = shift;

        print "Values for interface " . $self->getByKey( "ifdescr" ) . "\n";
        print "----------------------------------------------------------------------------------------------\n";
        print "\tTime between measure     = " . ( ( $self->getByKey( 'timegap', 'gap' ) ) ? $self->getByKey( 'timegap', 'gap' ) : '-' ) . "\n";
        print "\tInterface index          = " . $self->getByKey( 'ifindex', 'value' ) . "\n";
        print "\tInterface description    = " . $self->getByKey( 'ifdescr', 'value' ) . "\n";
        print "\tBandwidth                = " . $self->getByKey( 'ifspeed', 'value' ) . "\n";
        print "\tInput traffic            = " . $self->getByKey( 'ifinoctets', 'value' ) . " Bits per Second\n";
        print "\tOutput traffic           = " . $self->getByKey( 'ifoutoctets', 'value' ) . " Bits per Second\n";
        print "\tOperation status         = " . $self->getByKey( 'ifoperstatus', 'value' ) . "\n";
        print "\tAdministration status    = " . $self->getByKey( 'ifadminstatus', 'value' ) . "\n";
        print "\tInput discards           = " . $self->getByKey( 'ifindiscards', 'value' ) . " Packets per Second\n";
        print "\tOutput discards          = " . $self->getByKey( 'ifoutdiscards', 'value' ) . " Packets per Second\n";
        print "\tInput errors             = " . $self->getByKey( 'ifinerrors', 'value' ) . " Packets per Second\n";
        print "\tOutput errors            = " . $self->getByKey( 'ifouterrors', 'value' ) . " Packets per Second\n";
        print "\n";
    }
}

#-------------------------------------------------------------------------------
#   Package         : InterfaceV2
#-------------------------------------------------------------------------------
{
    package InterfaceV2;

    use parent -norequire, 'Interface';
    use strict;
    use warnings;


    #-------------------------------------------------------------------------------
    #   Methode         : new
    #-------------------------------------------------------------------------------
    #   Description     : Create a new object of type InterfaceV2.
    #
    #   Input           : $class        Reference to this class
    #                     session       Reference to Net::SNMP object
    #                     ifindex       The SNMP index of the network interface
    #   Output          : $self         The reference to this object
    #-------------------------------------------------------------------------------
    sub new
    {
        my $class = shift;
        my $self = {};

        bless $self, $class;
        $self->SUPER::_init( shift, shift );
        $self->_init();

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _init
    #-------------------------------------------------------------------------------
    #   Description     : Initialisation methode for class InterfaceV2.
    #
    #   Input           : $self         Reference to this object
    #   Output          : $self         Reference to this object
    #-------------------------------------------------------------------------------
    sub _init
    {
        my $self = shift;
        my $key;

        $self->{ 'ifname' } = {
            'oid'                       => '.1.3.6.1.2.1.31.1.1.1.1',
            'value'                     => '',
            'type'                      => "string"
        };
        $self->{ 'ifinoctets' } = {
            'oid'                       => '.1.3.6.1.2.1.31.1.1.1.6',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "bps",
            'type'                      => "numeric",
            'factor'                    => 8,
            'storable'                  => 1
        };
        $self->{ 'ifoutoctets' } = {
            'oid'                       => '.1.3.6.1.2.1.31.1.1.1.10',
            'value'                     => 0,
            'oldvalue'                  => 0,
            'unit'                      => "bps",
            'type'                      => "numeric",
            'factor'                    => 8,
            'storable'                  => 1
        };
        $self->{ 'ifspeed' } = {
            'oid'                       => '.1.3.6.1.2.1.31.1.1.1.15',
            'value'                     => 0,
            'type'                      => "numeric",
            'factor'                    => 10**6
        };
        $self->{ 'ifpromiscuousmode' } = {
            'oid'                       => '.1.3.6.1.2.1.31.1.1.1.16',
            'value'                     => 0,
            'type'                      => "numeric",
            'status'                    => {
                                                1 => 'on',
                                                2 => 'off'
                                           }
        };
        $self->{ 'ifalias' } = {
            'oid'                       => '.1.3.6.1.2.1.31.1.1.1.18',
            'value'                     => '',
            'type'                      => "string",
        };

        foreach $key ( keys %$self )
        {
            next unless ( $key =~ /^if/ );
            $self->setKeyByOID( $key, $self->getByKey( $key, 'oid' ) . "." . $self->getByKey( 'ifindex', 'value' ) );
        }

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : setDiffByKey
    #-------------------------------------------------------------------------------
    #   Description     : Set the diference between to measures for a given key.
    #
    #   Input           : $self         Reference to this object
    #                     $key          The key of the object variable to be set
    #                     $cnt1         The last measured counter
    #                     $cnt2         The counter measured before
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub setDiffByKey
    {
        my $self = shift;
        my $key = shift;

        $self->SUPER::setDiffByKey( $key, 2**64 )
    }

    #-------------------------------------------------------------------------------
    #   Methode         : printAll
    #-------------------------------------------------------------------------------
    #   Description     : This methode prints all the key value pairs. Only used
    #                     for verbose output.
    #
    #   Input           : $self         Reference to this object
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub printAll
    {
        my $self = shift;
        my $unit = shift;

        print "Values for interface " . $self->getByKey( 'ifdescr', 'value' ) . "\n";
        print "----------------------------------------------------------------------------------------------\n";
        print "\tTime between measure     = " . ( ( $self->getByKey( 'timegap', 'gap' ) ) ? $self->getByKey( 'timegap', 'gap' ) : '-' ) . "\n";
        print "\tInterface index          = " . $self->getByKey( 'ifindex', 'value' ) . "\n";
        print "\tInterface name           = " . $self->getByKey( 'ifname', 'value' ) . "\n";
        print "\tInterface description    = " . $self->getByKey( 'ifdescr', 'value' ) . "\n";
        print "\tInterface alias          = " . $self->getByKey( 'ifalias', 'value' ) . "\n";
        print "\tInput traffic            = " . $self->getByKey( 'ifinoctets', 'value' ) . " Bits per Second\n";
        print "\tOutput traffic           = " . $self->getByKey( 'ifoutoctets', 'value' ) . " Bits per Second\n";
        print "\tOperation status         = " . $self->getByKey( 'ifoperstatus', 'value' ) . "\n";
        print "\tAdministration status    = " . $self->getByKey( 'ifadminstatus', 'value' ) . "\n";
        print "\tInput discards           = " . $self->getByKey( 'ifindiscards', 'value' ) . " Packets per Second\n";
        print "\tOutput discards          = " . $self->getByKey( 'ifoutdiscards', 'value' ) . " Packets per Second\n";
        print "\tInput errors             = " . $self->getByKey( 'ifinerrors', 'value' ) . " Packets per Second\n";
        print "\tOutput errors            = " . $self->getByKey( 'ifouterrors', 'value' ) . " Packets per Second\n";
        print "\tPromiscuous mode         = " . $self->getByKey( 'ifpromiscuousmode', 'value' ) . "\n";
        print "\tBandwidth                = " . $self->getByKey( 'ifspeed', 'value' ) . "\n";
        print "\n";
    }
}


#-------------------------------------------------------------------------------
#   Package         : Interfaces
#-------------------------------------------------------------------------------
{
    package Interfaces;

    #---------------------------------------------------------------------------
    #                     Pragma
    #---------------------------------------------------------------------------
    use strict;
    use warnings;

    use Data::Dumper;

    #-------------------------------------------------------------------------------
    #   Methode         : new
    #-------------------------------------------------------------------------------
    #   Description     : Create a new object of type Interfaces.
    #
    #   Input           : $class        Reference to this class
    #                     session       Reference to a Net::SNMP Session
    #                     type          Type of name like ifName, ifDescr, or
    #                                   ifAlias
    #                     name          The name of the networkinterface
    #                     regex         Flag to use regex
    #                     version       Version of the SNMP protocol
    #   Output          : $self         The reference to this object
    #-------------------------------------------------------------------------------
    sub new
    {
        my $class = shift;
        my $self = {
            'session'                       => shift,
            'type'                          => lc( shift ),
            'name'                          => shift,
            'regex'                         => shift,
            'version'                       => shift,
            'interfaces'                    => {},
            'names'                         => []
        };
        bless $self, $class;

        $self->_init();

        return $self;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _init
    #-------------------------------------------------------------------------------
    #   Description     : Initialisation methode for class Interfaces.
    #
    #   Input           : $self         Reference to this object
    #   Output          : $self         Reference to this object
    #-------------------------------------------------------------------------------
    sub _init
    {
        my $self = shift;
        my $types                       = {
            ifdescr                     => '.1.3.6.1.2.1.2.2.1.2',
            ifname                      => '.1.3.6.1.2.1.31.1.1.1.1',
            ifalias                     => '.1.3.6.1.2.1.31.1.1.1.18',
        };
        my $baseoid;
        my $iflist;

        $baseoid = $types->{ $self->_getType() };
        if ( $baseoid )
        {
            $iflist = $self->_getSession()->get_table( Baseoid => $baseoid );
            die( "Could not read description table" ) unless ( defined( $iflist ) );
        }
        else
        {
            die( "Undefined field: " . $self->_getType() );
        }

        # Iterate over all interfaces and put all matching names in a list
        foreach( keys %$iflist )
        {
            if ( defined( $self->_getRegex() ) )
            {
                my $if = $self->_getName();
                if( $iflist->{ $_ } =~ /$if/ )
                {
                    $self->_updateList( $iflist->{ $_ }, $_ );
                }
            }
            elsif ( defined( $self->_getName() ) )
            {
                if ( $iflist->{ $_ } eq $self->_getName() )
                {
                    $self->_updateList( $iflist->{ $_ }, $_ );
                }
            }
            else
            {
                $self->_updateList( $iflist->{ $_ }, $_ );
            }
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : getNames
    #-------------------------------------------------------------------------------
    #   Description     : Return all names of this object.
    #
    #   Input           : $self         Reference to this object
    #   Output          : $names        list of all names
    #-------------------------------------------------------------------------------
    sub getNames
    {
        my $self = shift;

        return $self->{ names };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : getRefByName
    #-------------------------------------------------------------------------------
    #   Description     : Return the reference to an interface object for a given
    #                     name.
    #
    #   Input           : $self         Reference to this object
    #                     $name         Nema of the needed interface reference
    #   Output          : $name         The reference to a object
    #-------------------------------------------------------------------------------
    sub getRefByName
    {
        my $self = shift;
        my $name = shift;

        return $self->{ interfaces }->{ $name };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : writeData
    #-------------------------------------------------------------------------------
    #   Description     : Write the time gap and traffic informations to a file for
    #                     later use.
    #
    #   Input           : $self         Reference to this object
    #                     $np           Reference to Monitoring::Plugin
    #                     $if           Name of the interface
    #                     $gap          Reference to gap hash
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub writeData
    {
        my $self = shift;
        my $np = shift;
        my $if = shift;
        my $file = "";
        my $iffn = $if;

        $file = $np->opts->directory . "/" . $np->opts->hostname . "_" . $np->opts->gapfile;
        $iffn =~ s/\//_/g;
        $file =~ s/__IF__/$iffn/g;

        open( FILE, ">", $file ) or $np->nagios_die( "cant open file $file for writing" );
        print FILE $self->{ interfaces }->{ $if }->getFileData();
        close( FILE );
    }

    #-------------------------------------------------------------------------------
    #   Methode         : readData
    #-------------------------------------------------------------------------------
    #   Description     : Read the data out of the gap file and store it back to the
    #                     object.
    #
    #   Input           : $self         Reference to this object
    #                     $np           Reference to Monitoring::Plugin
    #                     $if           Name of the interface
    #                     $gap          Reference to gap hash
    #   Output          : $return       Return 0/1 if success or fail
    #-------------------------------------------------------------------------------
    sub readData
    {
        my $self = shift;
        my $np = shift;
        my $if = shift;
        my $file = "";
        my $iffn = $if;

        $file = $np->opts->directory . "/" . $np->opts->hostname . "_" . $np->opts->gapfile;
        $iffn =~ s/\//_/g;
        $file =~ s/__IF__/$iffn/g;

        if ( -e $file )
        {
            open( FILE, "<", $file ) or $np->nagios_die( "cant open file $file for reading" );

            while ( <FILE> )
            {
                chomp;
                my ( $key, $value ) = split( '\s+=\s+', $_ );
                $self->{ interfaces }->{ $if }->setFileData( $key, $value );
            }
            close( FILE );
            $self->{ interfaces }->{ $if }->setGap();
            return 1;
        }
        return 0;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : calcDiff
    #-------------------------------------------------------------------------------
    #   Description     : Calculate the difference between two measurepoints and
    #                     store the result in the related object.
    #
    #   Input           : $self         Reference to this object
    #                     $np           Reference to Monitoring::Plugin
    #                     $if           The name of the network interface
    #                     $gap          Reference to gap hash
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub calcDiff
    {
        my $self = shift;
        my $np = shift;
        my $if = shift;
        my $refif;

        $refif = $self->getRefByName( $if );
        foreach my $key ( keys %{ $refif } )
        {
            next unless $refif->getByKey( $key, 'oldvalue' );;

            if ( $refif->{ $key } )
            {
                $refif->setDiffByKey( $key );
            }
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _updateList
    #-------------------------------------------------------------------------------
    #   Description     : Create a hash list of all related network interfaces.
    #
    #   Input           : $self         Reference to this object
    #                     $ifname       Name of the network interface
    #                     $oid          The OID of the related interface
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub _updateList
    {
        my $self = shift;
        my $ifname = shift;
        my $oid = shift;

        if ( $self->{ version } eq 1 )
        {
            $self->_setInterface( $ifname, new InterfaceV1( $self->_getSession(), $self->_getIndex( $oid ) ) );
        }
        else
        {
            $self->_setInterface( $ifname, new InterfaceV2( $self->_getSession(), $self->_getIndex( $oid ) ) );
        }
        push( @{ $self->{ names } }, $ifname );
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _getSession
    #-------------------------------------------------------------------------------
    #   Description     : Return a reference to the SNMP session.
    #
    #   Input           : $self         Reference to this object
    #   Output          : session       Reference to Net:SNMP
    #-------------------------------------------------------------------------------
    sub _getSession
    {
        my $self = shift;

        return $self->{ session };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _setInterface
    #-------------------------------------------------------------------------------
    #   Description     : Stor the reference to a network interface object to this
    #                     object.
    #
    #   Input           : $self         Reference to this object
    #                     $ifname       The name of the network interface
    #                     $obj          The reference to the network interface
    #                                   object
    #   Output          : -             none
    #-------------------------------------------------------------------------------
    sub _setInterface
    {
        my $self = shift;
        my $ifname = shift;
        my $obj = shift;

        $self->{ interfaces }->{ $ifname } = $obj;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _getRegex
    #-------------------------------------------------------------------------------
    #   Description     : Returns the stored regex.
    #
    #   Input           : $self         Reference to this object
    #   Output          : regex         The regex
    #-------------------------------------------------------------------------------
    sub _getRegex
    {
        my $self = shift;

        return $self->{ regex };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _getName
    #-------------------------------------------------------------------------------
    #   Description     : Returns the name of the network interface.
    #
    #   Input           : $self         Reference to this object
    #   Output          : name          The name of the network interface
    #-------------------------------------------------------------------------------
    sub _getName
    {
        my $self = shift;

        return $self->{ name };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _getType
    #-------------------------------------------------------------------------------
    #   Description     : Return the type of the network interface name.
    #
    #   Input           : $self         Reference to this object
    #   Output          : type          The type of the network interface name
    #-------------------------------------------------------------------------------
    sub _getType
    {
        my $self = shift;

        if ( $self->{ version } == 1 )
        {
            $self->{ type } = "ifdescr";
        }

        return $self->{ type };
    }

    #-------------------------------------------------------------------------------
    #   Methode         : _getIndex
    #-------------------------------------------------------------------------------
    #   Description     : Return the SNMP index of the network interface.
    #
    #   Input           : $self         Reference to this object
    #                     $oid          The OID of the network interface
    #   Output          : index         The SNMP index of the network interface
    #-------------------------------------------------------------------------------
    sub _getIndex
    {
        my $self = shift;
        my $oid = shift;

        return ( split( /\./, $oid ) )[ -1 ];
    }
}


#-------------------------------------------------------------------------------
#   Package         : Main
#-------------------------------------------------------------------------------
{
    package main;

    #---------------------------------------------------------------------------
    #                     Global Variables
    #---------------------------------------------------------------------------
    my $NAME = "check_snmp_interface";
    my $SHORTNAME = "Network Interface";
    my $VERSION = "0.1.3";
    my $LICENSE = "GNU v2 or later, see <http://www.fsf.org/copyleft/gpl.txt>";

    #---------------------------------------------------------------------------
    #                     Sub createSnmpSession
    #---------------------------------------------------------------------------
    #   Function        : This function tries to create the SNMP session.
    #
    #   Input           : $np           Monitoring Plugin reference
    #                     $session      SNMP session reference
    #   Output          : -             none
    #---------------------------------------------------------------------------
    sub createSnmpSession
    {
        my $np          = shift;
        my $session;
        my $error;
        my $options     = {
            Hostname   => $np->opts->hostname,
            Version    => $np->opts->snmpversion,
            Port       => $np->opts->port,
            Timeout    => $np->opts->timeout
        };

        if ( $np->opts->snmpversion == 3 )
        {
            print "Connecting SNMP Version 3\n" if ( $np->opts->verbose );
            $options->{ Username }         = $np->opts->username;
            $options->{ Authpassword }     = $np->opts->authpassword;
            $options->{ Authprotocol }     = $np->opts->authprotocol;
            if ( defined( $np->opts->privpass ) and defined( $np->opts->privproto ) )
            {
                $options->{ Privpass }     = $np->opts->privpass;
                $options->{ PrivProto }    = $np->opts->privproto;
            }
        }
        else
        {
            print "Connecting SNMP Version 1 or 2\n" if ( $np->opts->verbose );
            $options->{ Community }        = $np->opts->community;
        }

        print Dumper( $options ) if ( $np->opts->verbose );

        ( $session, $error ) = Net::SNMP->session( %$options );

        # Exit if session is not defined
        $np->nagios_die( "cant open SNMP Session: $error" ) unless ( defined( $session ) );

        return $session;
    }

    #---------------------------------------------------------------------------
    #                     Sub closeSnmpSession
    #---------------------------------------------------------------------------
    #   Function        : This function tries to close the SNMP session.
    #
    #   Input           : $session      SNMP session reference
    #   Output          : -             none
    #---------------------------------------------------------------------------
    sub closeSnmpSession
    {
        my $session     = shift;

        $session->close();
    }

    #---------------------------------------------------------------------------
    #                     Sub getIfObject
    #---------------------------------------------------------------------------
    #   Function        : This function tries to catch the index of the needed
    #                     network interface.
    #
    #   Input           : $np           Monitoring Plugin reference
    #                     $snmp         SNMP session reference
    #                     $ifobject     the hash into wich we store the data
    #                     $scheme       the hash with all oids we need
    #                     $order        the array with the needed order
    #   Output          : -             none
    #---------------------------------------------------------------------------
    sub getIfObject
    {
        my $np          = shift;
        my $snmp        = shift;
        my $ifobject    = {};

        $ifobject = new Interfaces(
            $snmp,
            $np->opts->field,
            $np->opts->name,
            $np->opts->regex,
            $np->opts->snmpversion
        );
        return $ifobject;
    }

    #---------------------------------------------------------------------------
    #                     Sub createQueryHash
    #---------------------------------------------------------------------------
    #   Function        : This function has the task to make the function
    #                     checkThresholds a bit more readeble. It creates two
    #                     hash references.
    #
    #   Input           : $np           Monitoring Plugin reference
    #                     $if           A reference to the interface
    #                     $thdoptions   A hashreference for the threshold
    #                                   options
    #                     $prfdtptions  A hashreference for the perfdata options
    #                     $key          Name of the key
    #                     $warning      The warning threshold
    #                     $critical     The critical threshold
    #   Output          : -             none
    #---------------------------------------------------------------------------
    sub createQueryHash
    {
        my $np = shift;
        my $if = shift;
        my $thdoptions = shift;
        my $prfdtptions = shift;
        my $key = shift;
        my $warning = shift;
        my $critical = shift;
        my $labeluom = "";
        my $uom = $if->getByKey( $key, 'unit' );
        my $min = "";
        my $max = "";

        if ( $key =~ /octets/ )
        {
            $min = 0;
            $max = $if->getByKey( 'ifspeed', 'value' );
        }

        if ( $warning ne "none" and $critical ne "none" )
        {
            # $warning = $if->unitTo( $warning, $if->getByKey( $key, 'unit' ) );
            # $critical = $if->unitTo( $critical, $if->getByKey( $key, 'unit' ) );
            $warning = $if->unitTo( $warning, $np->opts->unit );
            $critical = $if->unitTo( $critical, $np->opts->unit );
            $thdoptions->{ 'check' }    = $if->getByKey( $key, 'value' );
            $thdoptions->{ 'warning' }  = $warning;
            $thdoptions->{ 'critical' } = $critical;
        }
        else
        {
            $warning = "";
            $critical = "";
        }

        if ( $np->opts->suppressunit )
        {
            $labeluom = "_in_" . $uom;
            $uom = "";
        }

        $prfdtptions->{ 'label' }       = "'" . $if->getByKey( lc( $np->opts->field ), 'value' ) . "_" . $key . $labeluom . "'";
        $prfdtptions->{ 'value' }       = $if->unitTo( $if->getByKey( $key, 'value' ), $uom );
        $prfdtptions->{ 'uom' }         = $uom;
        $prfdtptions->{ 'warning' }     = $warning;
        $prfdtptions->{ 'critical' }    = $critical;
        $prfdtptions->{ 'min' }         = $min;
        $prfdtptions->{ 'max' }         = $max;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : checkThresholds
    #-------------------------------------------------------------------------------
    #   Description     : Check the thresholds of all values and generate the
    #                     message and performance data of the output.
    #
    #   Input           : $np           A reference to Monitoring::Plugin
    #                     $if           A reference to the interface
    #   Output          : $ret          The returned errorlevel
    #                     $string       The message
    #-------------------------------------------------------------------------------
    sub checkThresholds
    {
        my $np = shift;
        my $if = shift;
        my @fields = split(",", $np->opts->show);
        my @warning = split(",", $np->opts->warning) if ( $np->opts->warning );
        my @critical = split(",", $np->opts->critical) if ( $np->opts->critical );
        my @label = split(",", $np->opts->label) if ( $np->opts->label );
        my @alias = split(",", $np->opts->alias) if ( $np->opts->alias );
        my $interface = $if->getByKey( lc( $np->opts->field ), 'value' );
        my $ret = 0;
        my $r;
        my $string = "";

        return ( 1, $interface . " parameters do not align. " ) if ( scalar @fields ne scalar @warning or scalar @fields ne scalar @critical );

        for ( my $i = 0; $i < scalar @fields; $i++ )
        {
            my $thresholdoptions = {};
            my $perfdataoptions = {};

            createQueryHash( $np, $if, $thresholdoptions, $perfdataoptions, $fields[ $i ],$warning[ $i ], $critical[ $i ] );

            if ( %{ $thresholdoptions } )
            {
                my $r =  $np->check_threshold( %{ $thresholdoptions } );
                $ret = $r if ( $ret lt $r );
            }
            $np->add_perfdata( %{ $perfdataoptions } );
        }

        # Generate the status string
        $r = checkFlags( $np, $if, \$string );
        $ret = $r if ( $ret lt $r );
        for ( my $i = 0; $i lt scalar @label; $i++ )
        {
            my $a = $alias[ $i ] ? $alias[ $i ] : $label[ $i ];
            my ( $value, $unit ) = $if->toUnit( $label[ $i ], $np->opts->unit, $np->opts->autoconvert );

            $string .= sprintf( " %s=%3.2f%s ", $a, $value, $unit );
        }

        return $ret, $interface . $string;
    }

    #-------------------------------------------------------------------------------
    #   Methode         : checkFlag
    #-------------------------------------------------------------------------------
    #   Description     : Compare a flag with a given value and color it.
    #
    #   Input           : $letter       A letter
    #                     $flag         A flag
    #                     $expected     A expected value of the flag
    #                     $string       A reference to the status string
    #   Output          : 0/2           Return OK (0) or CRITICAL (2)
    #-------------------------------------------------------------------------------
    sub checkFlag
    {
        my $letter = shift;
        my $flag = shift;
        my $expected = shift;
        my $string = shift;

        return "" if ( $expected eq "none" );

        if ( lc( $flag ) ne lc( $expected ) )
        {
            $$string .= ' ' . $letter . '=<span class="serviceCRITICAL">' . $flag . '</span>';
            return 2;
        }
        else
        {
            $$string .= ' ' . $letter . '=<span class="serviceOK">' . $flag . '</span>';
            return 0;
        }
    }

    #-------------------------------------------------------------------------------
    #   Methode         : checkFlags
    #-------------------------------------------------------------------------------
    #   Description     : Check the flags of a network interface and generate the
    #                     message for the output.
    #
    #   Input           : $np           A reference to Monitoring::Plugin
    #                     $if           A reference to the interface
    #                     $string       A reference to the status string
    #   Output          : 0/2           Return OK (0) or CRITICAL (2)
    #-------------------------------------------------------------------------------
    sub checkFlags
    {
        my $np = shift;
        my $if = shift;
        my $string = shift;
        my $return = 0;
        my $r;

        $return = checkFlag( 'A', $if->getByKey( 'ifadminstatus', 'value' ), $np->opts->adminstat, $string );
        $r = checkFlag( 'O', $if->getByKey( 'ifoperstatus', 'value' ), $np->opts->operstat, $string );
        $return = $r if ( $return lt $r );

        if ( $np->opts->snmpversion > 1 )
        {
            $r = checkFlag( 'P', $if->getByKey( 'ifpromiscuousmode', 'value' ), $np->opts->promiscuous, $string );
            $return = $r if ( $return lt $r );
        }
        return $return;
    }

    #---------------------------------------------------------------------------
    #                     Main
    #---------------------------------------------------------------------------
    my $snmp;
    my $ifobject = {};
    my $gapfile;
    my $gap = {};
    my $ret = 0;
    my $string = "";

    my $np = Monitoring::Plugin->new(
        usage       => "",
        shortname   => $SHORTNAME,
        version     => $VERSION,
        license     => $LICENSE,
        plugin      => $NAME,
        timeout     => 15
    );

    $np->add_arg(
        spec        => 'hostname|H=s',
        help        => "The host name of the requested network interfaces.",
        required    => 1
    );

    # Define SNMP Options
    $np->add_arg(
        spec        => 'community|C=s',
        help        => "The SNMP readonly community string.",
        default     => 'public'
    );

    $np->add_arg(
        spec        => 'username=s',
        help        => "The username, needed for SNMP version 3.",
        required    => 0
    );

    $np->add_arg(
        spec        => 'authpassword=s',
        help        => "The authentication password, needed for SNMP version 3.",
        required    => 0
    );

    $np->add_arg(
        spec        => 'authprotocol=s',
        help        => "The privacy type, needed for SNMP version 3.",
        required    => 0
    );

    $np->add_arg(
        spec        => 'privpass=s',
        help        => "The private key, optional for SNMP version 3.",
        required    => 0
    );

    $np->add_arg(
        spec        => 'privproto=s',
        help        => "The privacy type, optional for SNMP version 3.",
        required    => 0
    );

    $np->add_arg(
        spec        => 'snmpversion=i',
        help        => "Specify the SNMP Protocoll version (one of 1, 2 or 3, default is 2).",
        default     => 2
    );

    $np->add_arg(
        spec        => 'port=i',
        help        => "The service port for SNMP (default is 161).",
        default     => 161
    );

    $np->add_arg(
        spec        => 'directory|d=s',
        help        => "The name of the directory where to stor the gap file (default /tmp)",
        default     => "/tmp",
    );

    $np->add_arg(
        spec        => 'gapfile|g=s',
        help        => "The name of the gap file (default check_snmp_interface___IF__.gap)",
        default     => "check_snmp_interface___IF__.gap",
    );

    # Define interface request arguments
    $np->add_arg(
        spec        => 'name|i=s',
        help        => "The name of the network interface that needs to be observerd.",
        required    => 0
    );

    $np->add_arg(
        spec        => 'adminstat|A=s',
        help        => "Observe the administration status flag of the interface (Available flags are up, down, testing and none, default is none)",
        default     => 'none'
    );

    $np->add_arg(
        spec        => 'operstat|O=s',
        help        => "Observe the operation status flag of the interface (Available flags are up, down, testing, unknown, dormant, notpresent, lowerlayerdown and none, default is none)",
        default     => 'none'
    );

    $np->add_arg(
        spec        => 'promiscuous|P=s',
        help        => "Observe the promiscuous mode status flag of the interface (Available flags are on, off and none, default is none)",
        default     => 'none'
    );

    $np->add_arg(
        spec        => 'regex|r',
        help        => "Do use regex to match name",
    );

    $np->add_arg(
        spec        => 'field|f=s',
        help        => "One of ifName, ifDescr, or ifAlias",
        default     => 'ifName'
    );

    $np->add_arg(
        spec        => 'label|l=s',
        help        => "Which of the defined fields in the show argument shall be shown in the status line",
        default     => 'ifoutoctets,ifinoctets'
    );

    $np->add_arg(
        spec        => 'alias|a=s',
        help        => "replace the label with an alias",
        default     => 'Outbound,Inbound'
    );

    $np->add_arg(
        spec        => 'warning|w=s',
        help        => "List of thresholds for warnings, has to be in order of show arguments",
        default     => 'none,none'
    );

    $np->add_arg(
        spec        => 'critical|c=s',
        help        => "List of thresholds for criticals, has to be in order of show arguments",
        default     => 'none,none'
    );

    $np->add_arg(
        spec        => 'show|s=s',
        help        => "Select which fields shall be shown and observerd (ifoutoctets,ifinoctets,ifoutdiscards,ifindiscards,ifouterrors,ifinerrors)",
        default     => 'ifoutoctets,ifinoctets'
    );

    $np->add_arg(
        spec        => 'unit|u=s',
        help        => "use one of bps, Bps, kbps, kBps, Mbps, MBps, Gbps, GBps or % as unit",
        default     => 'bps'
    );

    $np->add_arg(
        spec        => 'suppressunit',
        help        => "for tools which cant handle unit, suppress them",
    );

    $np->add_arg(
        spec        => 'autoconvert',
        help        => "Show results in status line in human readable way",
    );

    # read the cli options
    $np->getopts;

    # create a SNMP connection to the node
    $snmp = createSnmpSession( $np );

    # collect the interface informations and store them in a hash
    $ifobject = getIfObject( $np, $snmp );

    # throw a "unknown" if we have no interfaces to work with
    $np->nagios_die( 3, "The interface " . $np->opts->name . " is unknown" ) if ( scalar @{ $ifobject->getNames() } eq 0 );

    foreach my $if ( sort @{ $ifobject->getNames() } )
    {
        # get data from last check
        my $file_exist = $ifobject->readData( $np, $if );

        # store data from this check
        $ifobject->writeData( $np, $if );
        print Dumper( $ifobject ) if ( $np->opts->verbose );

        if ( $file_exist )
        {
            # calculte the traffic between this and the last check
            $ifobject->calcDiff( $np, $if, $gap );
            my ( $r, $s ) = checkThresholds( $np, $ifobject->{ interfaces }->{ $if } );
            $ret = $r if ( $ret < $r );
            $string .= $s;
        }

        $ifobject->{ interfaces }->{ $if }->printAll( $np->opts->unit ) if ( $np->opts->verbose );
    }

    # close the SNMP connection
    closeSnmpSession( $snmp );

    # generate output
    $np->nagios_exit( $ret, $string );
}
