#!/usr/local/bin/perl
#
# check interfaces are up
# 
# by Doke Scott, doke@udel.edu, 2004.Aug.4 
#
# $Header: /home/doke/work/nagios/RCS/check_ifs_up,v 1.8 2015/12/15 21:45:09 doke Exp $

use strict;
use warnings;
no warnings 'redefine';
no warnings 'uninitialized';
use Socket;
use Getopt::Long;

$ENV{PATH} = "/usr/bin:/usr/local/bin:/usr/local/net-snmp/bin:/usr/sfw/bin";

use vars qw( $use_ifNames $use_snmpv2 $community $host $verbose $help
    %ifDescr %ifAdminStatus %ifOperStatus %ifIndex_by_ifDescr %opts
    $ifDescr @crit_errors @warn_errors @unknown_errors $rc $upcount );

$host = '';
$community = 'public';
$use_ifNames = 0;
$use_snmpv2 = 1;
$verbose = 0;
$help = 0;

sub usage { 
    my( $rc ) = @_;
    print qq{Usage: $0 -C <community> -H <host> [-nv]
    -n   use ifName instead of ifDescr
    -v   verbose
};
    exit $rc;
    }


Getopt::Long::Configure ("bundling");
GetOptions(
    'H=s' => \$host,
    'C=s' => \$community,
    'n' => \$use_ifNames,
    'v+' => \$verbose,
    'h' => \$help,
    );
&usage( 0 ) if ( $help );
&usage( 1 ) unless ( $host && $community );

$upcount = 0;
&get_info( $host, $community ); 

if ( scalar( @ARGV ) < 1 ) { 
    &check_ifs();
    }
else { 
    foreach $ifDescr ( @ARGV ) { 
	&check_if( $ifDescr );
	}
    }


$rc = 0;   # nagios ok exit code
$" = ", ";
if ( scalar( @crit_errors ) ) { 
    print "CRITICAL: @crit_errors ";
    $rc = 2;
    }
if ( scalar( @warn_errors ) ) { 
    print "; " if ( $rc != 0 );
    print "Warning: @warn_errors ";
    $rc = 1 if ( $rc == 0 );
    }
if ( scalar( @unknown_errors ) ) { 
    print "; " if ( $rc != 0 );
    print "Unknown: @unknown_errors ";
    $rc = 3 if ( $rc == 0 );
    }
elsif ( $rc == 0 ) { 
    print "OK -- $upcount interfaces up"; 
    }
print "\n";
exit $rc;   


##########################






sub get_info { 
    my( $host, $community ) = @_;
    my( $col, $slot, $val, $interval );

    if ( $use_ifNames ) { 
	&snmpwalk( *fH,  $host, $community, "ifName" );
	while ( <fH> ) { 
	    $verbose >= 2 && print "< $_";
	    next unless ( m/ifName\.(\d+) (\S+)/ );
	    $ifDescr{ $1 } = $2;
	    $ifIndex_by_ifDescr{ $2 } = $1;
	    }
	close fH;
	if ( scalar( keys %ifDescr ) < 1 ) { 
	    push @unknown_errors, "couldn't get ifName";
	    return;
	    }
	}
    else { 
	&snmpwalk( *fH,  $host, $community, "ifDescr" );
	while ( <fH> ) { 
	    $verbose >= 2 && print "< $_";
	    next unless ( m/ifDescr\.(\d+) (\S+)/i );
	    $verbose >= 2 && print "> $1 $2\n";
	    $ifDescr{ $1 } = $2;
	    $ifIndex_by_ifDescr{ $2 } = $1;
	    }
	close fH;
	if ( scalar( keys %ifDescr ) < 1 ) { 
	    push @unknown_errors, "couldn't get ifDescr";
	    return;
	    }
	}

    &snmpwalk( *fH,  $host, $community, "ifAdminStatus" );
    while ( <fH> ) { 
	$verbose >= 2 && print "< $_";
	next unless ( m/ifAdminStatus\.(\d+) (\S+)/ );
	$ifAdminStatus{ $1 } = $2;
	}
    close fH;
    if ( scalar( keys %ifAdminStatus ) < 1 ) { 
	push @unknown_errors, "couldn't get ifAdminStatus";
	return;
	}

    &snmpwalk( *fH,  $host, $community, "ifOperStatus" );
    while ( <fH> ) { 
	$verbose >= 2 && print "< $_";
	next unless ( m/ifOperStatus\.(\d+) (\S+)/ );
	$ifOperStatus{ $1 } = $2;
	}
    close fH;
    if ( scalar( keys %ifOperStatus ) < 1 ) { 
	push @unknown_errors, "couldn't get ifOperStatus";
	return;
	}

    }



sub check_ifs { 
    my( $ifx, $ifDescr, %down );
    
    foreach $ifx ( sort { $ifDescr{ $a } cmp $ifDescr{ $b } } keys %ifDescr ) { 
	$ifDescr = $ifDescr{ $ifx };
	$verbose && print "$ifDescr $ifAdminStatus{ $ifx } $ifOperStatus{$ifx}\n";

	next if ( $ifDescr =~ m/^(bme\d|cbp\d|demux\d|dsc|dse|em\d|gre|ip-|ipip|irb|lc-|lo\d|lsi|me\d|mt-|mtun|Null\d|pc-|pd-|pe-|pimd|pime|sp-|tap|vlan\.|vme)|\.32767$|\.32768$|\.32769$/i );

	if ( $ifAdminStatus{ $ifx } eq "up" ) { 
	    if ( $ifOperStatus{ $ifx } eq "up" ) { 
		$upcount++;
		}
	    else { 
		if ( $ifDescr =~ m/(.*)[:\.]\d+$/ && $down{ $1 } ) { 
		    # this is a subinterface, and we already marked the parent down
		    # skip it.
		    next;
		    }
		push @crit_errors, "$ifDescr $ifOperStatus{ $ifx }";
		$down{ $ifDescr } = 1;
		}
	    }
	else { 
	    $down{ $ifDescr } = 1;
	    }
	}
    }



sub check_if { 
    my( $ifDescr ) = @_;
    my( $ifx );

    $verbose && print "check_if( $ifDescr )\n";
    
    return if ( ! $ifDescr );

    if ( ! defined( $ifIndex_by_ifDescr{ $ifDescr } ) ) { 
	push @unknown_errors, "int $ifDescr";
	return;
	}
    $ifx = $ifIndex_by_ifDescr{ $ifDescr };
    $verbose && print "$ifDescr{ $ifx } $ifAdminStatus{ $ifx } $ifOperStatus{$ifx}\n";

    if ( $ifOperStatus{ $ifx } eq "up" ) { 
	$upcount++;
	}
    elsif ( $ifOperStatus{ $ifx } eq "lowerLayerDown" ) { 
	# ignore it
	}
    else { 
	push @crit_errors, "$ifDescr $ifOperStatus{ $ifx }";
	}
    }





sub snmpwalk { 
    my( $fH, $host, $community, $oid ) = @_;
    my( $cmd );

    if ( $use_snmpv2 ) { 
	$cmd = "snmpbulkwalk -v 2c -Oq -c $community $host $oid";
	}
    else { 
	$cmd = "snmpwalk -v 1 -Oq -c $community $host $oid";
	}
    $verbose && print "cmd = '$cmd'\n";
    open( $fH, '-|', $cmd ) || die "can't snmpwalk: $!\n";
    }






