I enhanced the check_bgp.pl plugin provided by Indiana University (see the "IU Nagios Plugins" project at SourceForge).  The old check_bgp.pl plugin checks that BGP sessions on a router are up.  The attached check_bgp_new.pl plugin does the same thing, and is upward-compatible with the old check_bgp.pl.  The new plugin has a new optional feature that works only on Juniper routers.  For each BGP session on a Juniper router, the new plugin can check that the number of active or received prefixes falls between given minimum and maximum values.

We use check_bgp_new at our site to make Nagios notify us when our routing with the outside world gets "funny", like when one ISP suddenly becomes less preferred than another.

-- Pete

#!/usr/bin/perl -w
#
# This Nagios plugin checks BGP sessions on a router.  It checks that
# the BGP sessions in a given list are in "established" state, and on
# Juniper routers it can check that the number of prefixes learned
# from each BGP neighbor falls within a given range.
#
# This is a rewrite of the Nagios plugin named check_bgp.pl, available
# at SourceForge under the project named "IU Nagios Plugins".  That
# plugin checks that BGP sessions are in "established" state.  This
# plugin does the same, but allows passing in an optional set of
# threshold values with each BGP neighbor.  When the optional
# thresholds are supplied on the command line, this plugin checks the
# number of prefixes learned via each BGP session against the
# thresholds.
#
# TODO:
# Make it check prefixes on Cisco as well as Juniper routers.
#
# HISTORY:
#  version 2.0, 2006-08-09, named check_bgp_new.pl
#  Pete Siemsen, [EMAIL PROTECTED]
#  version 1.0, 2005-10-11, named check_bgp.pl
#  see SourceForge project "IU Nagios Plugins"

use strict;
use Getopt::Std;
use Net::SNMP qw(:snmp);
#use Data::Dumper;		# debugging
use vars qw/ %opt /;

my $lastScriptModification = '2006-08-09';

# Standard Nagios plug-in return codes.
my $NAGIOS_OK       = 0;	# success
my $NAGIOS_WARNING  = 1;	# something is a little bit wrong
my $NAGIOS_CRITICAL = 2;	# something is very wrong
my $NAGIOS_UNKNOWN  = 3;	# bad command-line arguments


#=============================================================================
# Do SNMP with the router and set %NeighborStatusTable.
sub GetNeighborStatusTable($$$) {
  my $RouterName = shift;
  my $community = shift;
  my $NeighborStatusTableRef = shift;

  # Open an SNMPv2 session with the router
  my ($Session, $Error) = Net::SNMP->session(
					     -version   => 'snmpv2c',
					     -timeout   => 2,
					     -hostname  => $RouterName,
					     -community => $community
					    );
  return $Error if !defined($Session);

  my $BaseOid = '1.3.6.1.2.1.15.3.1.2';
  my $Result = $Session->get_table($BaseOid);
  if (!defined($Result)) {
    $Session->close;
    return $Session->error();
  }

  foreach (keys %$Result) {
    my $newkey = $_;
    $newkey =~ s/^$BaseOid\.//;
    $$NeighborStatusTableRef{$newkey} = $$Result{$_};
  }
  return 'success';
}


#=============================================================================
# Do SNMP with the router and set %NeighborReceivedPrefixTable.
sub GetNeighborPrefixCounts($$$$) {
  my $RouterName = shift;
  my $community = shift;
  my $GetActiveCounts = shift;
  my $NeighborPrefixCountsRef = shift;

  # Open an SNMPv2 session with the router
  my ($Session, $Error) = Net::SNMP->session(
					     -version   => 'snmpv2c',
					     -timeout   => 2,
					     -hostname  => $RouterName,
					     -community => $community
					    );
  return $Error if !defined($Session);

  # .iso.org.dod.internet.private.enterprises.juniperMIB.
  # jnxExperiment.jnkBGPM2Experiment.jnxBgpM2.jnxBgpM2Peer.
  # jnxBgpM2PeerData.jnxBgpM2PeerTable.jnxBgpM2PeerEntry.
  # jnxBgpMpPeerIndex
  my $jnxBgpMpPeerIndexOid = '1.3.6.1.4.1.2636.5.1.1.2.1.1.1.14';
  my $SnmpPeerIndices = $Session->get_table($jnxBgpMpPeerIndexOid);
  if (!defined($SnmpPeerIndices)) {
    $Session->close;
    return $Session->error();
  }
  #  print Dumper $SnmpPeerIndices;

  my %PeerIndexToIp;
  foreach my $ReturnedOid (keys %$SnmpPeerIndices) {
    my $LocalOid = $ReturnedOid;
    $LocalOid =~ s/^$jnxBgpMpPeerIndexOid\.//; # remove the baseoid from the left
    next if $LocalOid !~ /^0\.1\./; # skip any non-ipv4 entries
    $LocalOid =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$)/; # get the IP address from the right
    $PeerIndexToIp{$$SnmpPeerIndices{$ReturnedOid}} = $1;
  }
  #  print Dumper \%PeerIndexToIp;

  my $BaseOid;
  if ($GetActiveCounts) {
    # .iso.org.dod.internet.private.enterprises.juniperMIB.
    # jnxExperiment.jnkBGPM2Experiment.jnxBgpM2.jnxBgpM2Peer.
    # jnxBgpM2PeerCounters.jnxBgpM2PrefixCountersTable.
    # jnxBgpM2PrefixCountersEntry.jnxBgpM2PrefixInPrefixesAccepted
    $BaseOid = '1.3.6.1.4.1.2636.5.1.1.2.6.2.1.8';
  } else {
    # .iso.org.dod.internet.private.enterprises.juniperMIB.
    # jnxExperiment.jnkBGPM2Experiment.jnxBgpM2.jnxBgpM2Peer.
    # jnxBgpM2PeerCounters.jnxBgpM2PrefixCountersTable.
    # jnxBgpM2PrefixCountersEntry.jnxBgpM2PrefixInPrefixes
    $BaseOid = '1.3.6.1.4.1.2636.5.1.1.2.6.2.1.7';
  }
  my $SnmpPrefixCounts = $Session->get_table($BaseOid);
  if (!defined($SnmpPrefixCounts)) {
    $Session->close;
    return $Session->error();
  }
  #  print Dumper $SnmpPrefixCounts;

  foreach my $ReturnedOid (keys %$SnmpPrefixCounts) {
    my $LocalOid = $ReturnedOid;
    $LocalOid =~ s/^$BaseOid\.//; # remove the baseoid
    my ($PeerIndex, $Protocol, $RoutingTable) = split /\./, $LocalOid;
    next if $Protocol != 1;	# skip non-ipv4 entries
    next if $RoutingTable != 1;	# skip non-inet0 entries
    my $ip = $PeerIndexToIp{$PeerIndex};
    $$NeighborPrefixCountsRef{$ip} = $$SnmpPrefixCounts{$ReturnedOid};
  }
  #  print Dumper $NeighborPrefixCountsRef;

  return 'success';
}


#
# Parse the "-n" command-line argument.  Return an array of hashes
# containing one hash for each neighbor.
#
sub ParseNeighborsArg ($$) {
  my $NeighborsArg = shift;
  my $LimitsMustBeCheckedRef = shift;

  my @Neighbors;
  my $Index = -1;
  foreach my $NeighborArg (split(',', $NeighborsArg)) {
    my ($ip, $name, $arg3, $arg4) = split('\%', $NeighborArg);
    $Index++;
    $Neighbors[$Index] = {};	# create a new anonymous hash for the neighbor
    $Neighbors[$Index]{Ip} = $ip;
    if ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
      print "INVALID COMMAND ARGUMENT - $ip isn't standard dotted-decimal notation\n";
      exit $NAGIOS_UNKNOWN;
    }
    $Neighbors[$Index]{Name} = $name;
    my $PercentCount = ($NeighborArg =~ tr/%/%/);
    next if $PercentCount == 0;
    if ($PercentCount > 3) {
      print "INVALID COMMAND ARGUMENT - too many percent signs after $ip\n";
      exit $NAGIOS_UNKNOWN;
    }
    $$LimitsMustBeCheckedRef = 1 if $PercentCount > 1;
    if (defined $arg4) {
      if ($arg3 >= $arg4) {
	print "INVALID COMMAND ARGUMENT - lower bound must be < upper bound for $ip \n";
	exit $NAGIOS_UNKNOWN;
      }
      $Neighbors[$Index]{MinimumPrefixes} = $arg3;
      $Neighbors[$Index]{MaximumPrefixes} = $arg4;
    } elsif (defined $arg3) {
      $Neighbors[$Index]{PrefixCount} = $arg3;
    }
  }
  return @Neighbors;
}


#=============================================================================
# Main.

my $Usage = <<"EOF";
usage:  $0 [-h] -r <hostname> -c <community> -n <neighbors> [-v]

[-h]            :  Display this message and exit
[-r] <router>   :  IP address or DNS name of the router
[-c] <community>:  SNMP community string  (default = "public")
[-n] <neighbors>:  A comma-separated list of BGP neighbors, where each
                   neighbor is an IP address followed by optional %-delimited
                   values.  The first value is a name for the neighbor, which
                   is used to clarify the text messages generated by this
                   script.  The remaining one or two values, if they exist,
                   have meaning only when the -r option specifies a Juniper
                   router.  They define limits on the number of prefixes that
                   the router is learning from the BGP neighbor.  If there
                   is only one value, it defines the exact number of prefixes
                   expected from the BGP neighber.  If there are two values,
                   they define lower and upper bounds on the number of
                   prefixes expected from the neighbor.
                        Examples:
                        10.10.10.1,10.10.20.2%ROUTER_B
                        1.2.3.4%PEER1%3,1.2.3.5%ISP1%100000%200000
[-a]            :  Has meaning only when the -r option specifies a Juniper
                   router.  When -a is specified, this plugin checks the
                   number of active (used) routes instead of the number of
                   received routes.  When you check received routes, you're
                   verifying that a BGP neighbor is sending you the number
                   of routes that you expect.  You might check received
                   routes to verify that a neighbor hasn't screwed up a BGP
                   filter.  When you check active routes, you're verifying
                   that the router is actually using the number of routes
                   that you expect.  You might check active routes to
                   verify that most of your traffic is going out a primary
                   link and not a backup link.

This Nagios plugin uses SNMP to query a router about its BGP sessions.
The -n argument defines the list of BGP sessions to be checked; other
BGP sessions on the router will be ignored.  If any of the listed BGP
sessions is not in "established" state, or if a BGP neighbor isn't
sending the expected number of prefixes, this plugin will return a
non-success status.

This Nagios plugin can check the existence of BGP sessions on any
router that supports the standard BGP SNMP MIB.  It can check prefix
limits only on Juniper routers, since prefix counts are available only
in a proprietary SNMP MIB that is implemented only by Juniper routers.
On other routers, this script checks only whether BGP sessions are in
"established" state.  In other words, if you use this script to check
BGP sessions on a non-Juniper router, don't try to specify any limits
or you'll get an error.

$lastScriptModification

EOF

if (!getopts('har:c:n:', \%opt) or
    (!exists $opt{r}) or
    (!exists $opt{n}) or
    (exists $opt{h})) {
  warn $Usage;
  exit $NAGIOS_UNKNOWN;
}
my $RouterName = $opt{r};
my $Community = (exists $opt{c}) ? $opt{c} : "public";
my $CheckActive = (exists $opt{a}) ? $opt{a} : 0;
my $LimitsMustBeChecked = 0;
my @ExpectedNeighbors = ParseNeighborsArg($opt{n}, \$LimitsMustBeChecked);
#print Dumper @ExpectedNeighbors;

my %NeighborStatusTable;
my $status = GetNeighborStatusTable($RouterName, $Community, \%NeighborStatusTable);
if ($status ne 'success') {
  print "SNMP ERROR - couldn't get BGP table from $RouterName\n";
  exit $NAGIOS_CRITICAL;
}
#print Dumper \%NeighborStatusTable;

my %BgpStates = (
		 "1" => "Idle",
		 "2" => "Connect",
		 "3" => "Active",
		 "4" => "OpenSent",
		 "5" => "OpenConfirm",
		 "6" => "Established"
		);

# Check that all sessions are established.  Exit if any aren't.
my $BGP_ESTABLISHED = 6;
my $UnestablishedNeighbors = '';
foreach my $ExpectedNeighbor (@ExpectedNeighbors) {
  my $ExpectedNeighborIp = $$ExpectedNeighbor{Ip};
  if (!exists $NeighborStatusTable{$ExpectedNeighborIp}) {
    print "SNMP ERROR - BGP with $ExpectedNeighborIp is not configured on $RouterName\n";
    exit $NAGIOS_CRITICAL;
  }
  my $SessionState = $NeighborStatusTable{$ExpectedNeighborIp};
  if ($SessionState ne $BGP_ESTABLISHED) {
    my $ExpectedNeighborName = ($$ExpectedNeighbor{Name} eq '') ?
      $ExpectedNeighborIp : $$ExpectedNeighbor{Name};
    $UnestablishedNeighbors .= " $ExpectedNeighborName";
  }
}

if ($UnestablishedNeighbors ne '') {
  print "UNESTABLISHED BGP SESSIONS -$UnestablishedNeighbors\n";
  exit $NAGIOS_CRITICAL;
}

if (!$LimitsMustBeChecked) {
  print "BGP OK - all sessions established\n";
  exit $NAGIOS_OK;
}

my %NeighborPrefixCounts;
$status = GetNeighborPrefixCounts($RouterName, $Community, $CheckActive, \%NeighborPrefixCounts);
if ($status ne 'success') {
  print "SNMP ERROR - couldn't get prefix count table from $RouterName\n";
  exit $NAGIOS_CRITICAL;
}
#print Dumper \%NeighborPrefixCounts;

my $CountType = $CheckActive ? 'active' : 'received';
foreach my $ExpectedNeighbor (@ExpectedNeighbors) {
  my $ExpectedNeighborIp = $$ExpectedNeighbor{Ip};
  my $ExpectedNeighborName = ($$ExpectedNeighbor{Name} eq '') ?
    $ExpectedNeighborIp : $$ExpectedNeighbor{Name};
  my $ActualPrefixCount = $NeighborPrefixCounts{$ExpectedNeighborIp};
  if (exists $$ExpectedNeighbor{PrefixCount}) {
    my $ExpectedPrefixCount = $$ExpectedNeighbor{PrefixCount};
    if ($ActualPrefixCount != $ExpectedPrefixCount) {
      print "PREFIX COUNT WRONG - getting $ActualPrefixCount $CountType prefixes, expecting $ExpectedPrefixCount from neighbor $ExpectedNeighborName at ($ExpectedNeighborIp)\n";
      exit $NAGIOS_WARNING;
    }
  } elsif (exists $$ExpectedNeighbor{MinimumPrefixes}) {
    my $Minimum = $$ExpectedNeighbor{MinimumPrefixes};
    if ($ActualPrefixCount < $Minimum) {
      print "LOW BGP PREFIX COUNT - $ActualPrefixCount prefixes $CountType, minimum is $Minimum for neighbor $ExpectedNeighborName at ($ExpectedNeighborIp)\n";
      exit $NAGIOS_WARNING;
    }
    my $Maximum = $$ExpectedNeighbor{MaximumPrefixes};
    if ($ActualPrefixCount > $Maximum) {
      print "HIGH BGP PREFIX COUNT - $ActualPrefixCount prefixes $CountType, maximum is $Maximum for neighbor $ExpectedNeighborName at ($ExpectedNeighborIp)\n";
      exit $NAGIOS_WARNING;
    }
  }
}

print "BGP OK - all sessions established\n";
exit $NAGIOS_OK;


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Nagios-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/nagios-users
::: Please include Nagios version, plugin version (-v) and OS when reporting 
any issue. 
::: Messages without supporting info will risk being sent to /dev/null

Reply via email to