#!/usr/bin/perl
# nagios: -epn
# Don't run this plugin in the embedded perl interpreter
# It hasn't been tested that way
#

# This script is intended to be used to check the age of routes in a device's
# routing table.
###############################################################################
# sgichk_routetable.pl -- SGI custom Nagios plugin to check route tables
#
# History:
#   200901013	Created plugin. Brent Bice (bbice@sgi.com)
###############################################################################

use strict;
use warnings;
use Storable;

# Core modules
use Getopt::Long;
use Pod::Usage;

# SGI specific modules
use lib "/usr/local/nagios/libexec";
use lib "/usr/local/nagios/nrpe/libexec";
use lib "/usr/local/nagios/common-files/libexec";
use SgiPlugs;

#####
# Secure thyself
####
# Remove ENV vars that shells will use
delete @ENV{qw(IFS CDPATH ENV BASH_ENV PATH)};

# General vars
my $progname = 'sgichk_routetable.pl';
my %rcs_info = ('SOURCE' => '$Source: /source/Development/SGI-Plugins/RCS/sgichk_routetable.pl,v $',
                'NAME' => '$Name:  $',
                'REVISION' => '$Revision: 1.2 $',
                'DATE' => '$Date: 2012/04/21 18:22:53 $');

#####
# Get the command line arguments
####
my $help = 0;
#my $snmpwalk = "/usr/bin/snmpwalk";    # snmpwalk is mega slow
my $snmpwalk = "/usr/bin/snmpbulkwalk";
my $RouteMIB = ".1.3.6.1.2.1.4.21.1";
my $Host = "";
my $snmpvers = "";
my $snmpcomm = "public";
my $snmpuser = "";
my $snmppass = "";
my $version = 1.0;
my $timeout = 30;   # It can take a while to download a whole route table
my $verbose = 0;
my $warning;
my $critical;
my $PersistPath;

   if (! stat $snmpwalk) {
      print "Can't find $snmpwalk\n";
   }

# Configure Getopt::Long to be case-sensitive and not use abbreviated option names.
Getopt::Long::Configure('no_ignore_case', 'no_auto_abbrev');

my $arg_count = scalar(@ARGV);

# Parse the command line
GetOptions('help|h' => \$help,
		'host|H=s' => \$Host,
                'timeout|t=i' => \$timeout,
                'verbose|v' => \$verbose,
                'snmpvers|V=s' => \$snmpvers,
                'snmpcomm|C=s' => \$snmpcomm,
                'snmpuser|u=s' => \$snmpuser,
                'snmppass|p=s' => \$snmppass,
                'warning|w' => \$warning,
                'critical|c' => \$critical
           ) or &SgiPlugs::print_syntax_error("Use option --help for help");
# No more options on the command line
&SgiPlugs::print_syntax_error("Use option --help for help") if ($ARGV[0]);

# Print help if asked to
if ($help) {
    if ($arg_count == 1) {
        pod2usage(-exitstatus => $SgiPlugs::ERRORS{'UNKNOWN'}, -verbose => 2) ;
    } else {
        &SgiPlugs::print_syntax_error("Use option --help for help");
    }
}

# Check for the thresholds
if ((! defined($warning)) && (! defined($critical))) {
   &SgiPlugs::exit_unknown( "You must use either -w or -c" );
}

# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
    #&SgiPlugs::exit_warning("Taking too long to fetch route table");
    &SgiPlugs::exit_ok("Taking too long to fetch route table will check back");
};
alarm($timeout);

my $snmpcmd = $snmpwalk;
my %dest = ();
my %nexthop = ();
my %type = ();
my %proto = ();
my %age = ();
my %mask = ();
my %Odest = ();
my %Onexthop = ();
my %Otype = ();
my %Oproto = ();
my %Oage = ();
my %Omask = ();

if (length($snmpcomm) > 0) { $snmpcmd .= " -c $snmpcomm"; }
if (length($snmpvers) > 0) { $snmpcmd .= " -v $snmpvers"; }
if (length($Host) > 0) {
   $snmpcmd .= " $Host $RouteMIB";
   $PersistPath = "/tmp/route-table.$Host";
}

# HACK for debugging
#$snmpcmd = "cat /tmp/route-table.txt";

# Retrieve old routes if $PersistPath exists and is readable
if (defined($PersistPath) && -s $PersistPath) {
   if (open P, "<$PersistPath") {
      %Odest = %{Storable::fd_retrieve (\*P)};
      %Onexthop = %{Storable::fd_retrieve (\*P)};
      %Otype = %{Storable::fd_retrieve (\*P)};
      %Oproto = %{Storable::fd_retrieve (\*P)};
      %Oage = %{Storable::fd_retrieve (\*P)};
      %Omask = %{Storable::fd_retrieve (\*P)};
      close P;
   }
}

open SNMP, "$snmpcmd |"   || die ("Couldn't run $snmpcmd");
while (<SNMP>) {
   chomp;
   if (/ipRouteDest\.(.+) = IpAddress: (.+)/) {
      $dest{$1} = $2;
   }
   if (/ipRouteNextHop\.(.+) = IpAddress: (.+)/) {
      $nexthop{$1} = $2;
   }
   if (/ipRouteType\.(.+) = INTEGER: (.+)/) {
      $type{$1} = $2;
   }
   if (/ipRouteProto\.(.+) = INTEGER: (.+)/) {
      $proto{$1} = $2;
   }
   if (/ipRouteAge\.(.+) = INTEGER: (.+)/) {
      $age{$1} = int($2);
   }
   if (/ipRouteMask\.(.+) = IpAddress: (.+)/) {
      $mask{$1} = $2;
   }
}
close SNMP;

my $retval = 0;  # Assume all is OK
my $retstr = "";
my $r = "";
my $numnew = 0;
my $nummod = 0;
my $numlost = 0;

foreach $r (keys(%dest)) {

   # Look for any new routes
   if (! defined($Odest{$r})) {   #Not in old route table
      $retstr .= "NEW: $dest{$r} / $mask{$r} -\> $nexthop{$r} : $proto{$r}\n";
      $numnew++;
      if (defined($critical)) {
         if ($retval == 0) { $retval = 2; }
      } else {
         if (defined($warning)) {
            if ($retval == 0) { $retval = 1; }
         }
      }

   } else {   # destination exists in old route table but...

      # If the netmasks or destinations differ, it's a different route
      if (($Omask{$r} ne $mask{$r}) || ($Onexthop{$r} ne $nexthop{$r})) {
         $retstr .= "MODIFIED: ";
         $retstr .= "$dest{$r} / $mask{$r} -\> $nexthop{$r} : $proto{$r}\n";
         $nummod++;
         if (defined($critical)) {
            if ($retval == 0) { $retval = 2; }
         } else {
            if (defined($warning)) {
               if ($retval == 0) { $retval = 1; }
            }
         }
      }
   }
}

# Check to see if any routes disappeared
foreach $r (keys(%Odest)) {  # Odest contains old routes
   if (! defined($dest{$r})) {
      if (defined($critical)) {
         if ($retval == 0) { $retval = 2; }
      } else {
         if (defined($warning)) {
            if ($retval == 0) { $retval = 1; }
         }
      }
      $retstr .= "LOST ROUTE: ";
      $retstr .= "$Odest{$r} / $Omask{$r} -\> $Onexthop{$r} : $Oproto{$r}\n";
      $numlost++;
   }
}

# Save current route table to $PersistPath
if (defined($PersistPath)) {
   if (open P, ">$PersistPath") {
      Storable::store_fd (\%dest, *P);
      Storable::store_fd (\%nexthop, *P);
      Storable::store_fd (\%type, *P);
      Storable::store_fd (\%proto, *P);
      Storable::store_fd (\%age, *P);
      Storable::store_fd (\%mask, *P);
      close P;
   }
}

#HACK   Return just the first 60 characters of the return string
$retstr = substr($retstr,0,400);

if ($retval == 0) {
   my $numroutes = scalar(keys(%dest));
   #print "OK: Route Table unchanged for $numroutes routes\n";
   #exit (0);
   &SgiPlugs::exit_ok("Route Table unchanged for $numroutes routes");
} else {
   my $statstr = "";
   if ($numnew > 0) {
      $statstr .= "$numnew NEW";
   }
   if ($nummod > 0) {
      $statstr .= ", $nummod MODIFIED";
   }
   if ($numlost > 0) {
      $statstr .= ", $numlost LOST";
   }
   $retstr = $statstr . "\n" . $retstr;
   if ($retval == 1) {
      #print "WARNING: $retstr\n";
      &SgiPlugs::exit_warning($retstr);
   } else {
      if ($retval == 2) {
         #print "CRITICAL: $retstr\n";
         &SgiPlugs::exit_critical($retstr);
      }
   }
}

exit($retval);

__END__

=head1 Name

sgichk_routetable.pl -- SGI Nagios plugin to check route tables

=head1 Description

This plugin checks the route table on a given system and alerts if it
finds a new route has appeared or an old one disappeared since the last check

=head1 Usage

sgichk_routetable.pl -h

sgichk_routetable.pl -H Host [-t <timeout interval>] [-v] [-C <snmp_community>] [-V (1|2c|3)] [-u <snmp_user>] [-p <snmp_pass>] [-P <pathname>] [-w <warning>] [-c <critical>]

=head1 Options

=over 2

=item -h, --help

Prints this help

=item -V, --snmpvers

What version of the SNMP protocol to use

=item -t, --timeout=INTEGER

Timeout after specified interval (seconds). The default is 30.

=item -v, --verbose

Run verbose. Only for debugging. Do not use this for Nagios.

=item -C, --snmpcomm

SNMP Community String

=item -u, --snmpuser

SNMP Username

=item -p, --snmppass

SNMP Password

=item -P, --persist

File pathname to save route table to. This is so we can check for lost routes
and alert if the previous route table contains a route not found in the current
route table.

=item -w, --warning=INTEGER

Warning threshold in seconds.

=item -c, --critical=INTEGER

Critical threshold in sedonds.

=back

=head1 Notes

*

=head1 Pre-requisites

This plugin requires the following modules:

=over 2

=item

=back

=head1 Authors

=over 2

=item Brent Bice (bbice@sgi.com)

=item

=back


=head1 Revision

$Id: sgichk_routetable.pl,v 1.2 2012/04/21 18:22:53 root Exp $

=cut


