#!/usr/local/bin/perl # # pi, Tue Mar 12 22:08:07 CET 2013 # check_quagga_bgpd -- status of the quagga bgpd and the BGP4 peers # # tech/tcpip/routing/quagga/nagios-check # use warnings; use strict; use Net::Telnet (); use Config::Tiny; # global variables my($prompt, $hostname, $password, $enable); my($interval) = 300; my($statefn) = ''; my($debugdir) = ''; my($peercnt); my($peerconfcnt) = 0; my(%ip2as); my(%peer); my(%peerprio); my(%peerlvl); my($res); my($failres) = ''; my($saveres) = ''; my($myas) = ''; my($alert); # variables my($pre, $mat); my(@pre); my($t); my($i); my($r); my($err); # init &readconf; #print "prompt: $prompt hostname: $hostname pw: $password ena: $enable\n"; $t = new Net::Telnet ( Timeout => 1, Binmode => 1, Port => 2605, Telnetmode => 0, Errmode => "return", ); if ( $debugdir ne '' ) { $t->output_log($debugdir.'out.log'); $t->input_log ($debugdir.'in.log'); $t->dump_log ($debugdir.'dump.log'); } $r = $t->open($hostname); if ( !defined($r) ) { print "BGP CRITICAL - no-connection\n"; exit(2); } $t->waitfor(String => "Password: "); $err = $t->errmsg; if ( $err ne '' ) { print "BGP CRITICAL - waitforpw: $err\n"; exit(2); }; $t->print($password); $t->waitfor(String => "$prompt> "); $err = $t->errmsg; if ( $err ne '' ) { print "BGP CRITICAL - waitforprompt1: $err\n"; exit(2); }; $t->print("enable"); $t->waitfor(String => "Password: "); $err = $t->errmsg; if ( $err ne '' ) { print "BGP CRITICAL - waitforprompt2: $err\n"; exit(2); }; $t->prompt('/'.$prompt.'# /'); $t->cmd($enable); $err = $t->errmsg; if ( $err ne '' ) { print "BGP CRITICAL - waitforprompt3: $err\n"; exit(2); }; $t->cmd('terminal length 0'); $err = $t->errmsg; if ( $err ne '' ) { print "BGP CRITICAL - waitforterm: $err\n"; exit(2); }; # zeroth, find the peers in the running config @pre = $t->cmd('show running-config'); chop(@pre); chop(@pre); $res = &parseconf(@pre); if ( $res ne '' ) { print "BGP CRITICAL - $res | -\n"; exit(2); } # first, the number of peers @pre = $t->cmd('show bgp mem'); chop(@pre); chop(@pre); $res = &parsemem(@pre); # second, IPv4 @pre = $t->cmd('show ip bgp summary'); chop(@pre); chop(@pre); $res .= &parsebgpsum(@pre); # print "res: $res\n"; # third, IPv6 @pre = $t->cmd('show ipv6 bgp summary'); chop(@pre); chop(@pre); $res .= &parsebgpsum(@pre); # print "res: $res\n"; $t->print("quit"); # Q: how do we detect crit or warn ? # A: one or more of # crit= # warn= # in the results # do we have some alert ? if ( $failres =~ /crit=/ || $peercnt != $peerconfcnt ) { $alert='CRITICAL'; } elsif ( $failres =~ /warn=/ ) { $alert='WARNING'; } else { $alert='OK'; } if ( $statefn ne '' ) { my($t) = open(OUT,'>'.$statefn); if ( defined($t) ) { print OUT "BGPRT $alert - $failres | $saveres\n"; close(OUT); } else { $failres .= "error writing '$statefn': $!" } } if ( $failres ne '' ) { print "BGP $alert - $failres | $res\n"; } else { print "BGP $alert | $res\n"; } if ( $alert eq 'CRITICAL' ) { exit(2); } elsif ( $alert eq 'WARNING' ) { exit(1); } exit 0; ##################################################################### # read the config file, prepare the peer hash sub readconf { my $Config = Config::Tiny->new(); my($t); $Config = Config::Tiny->read( '/usr/local/etc/bmon.conf' ); if ( !defined($Config) ) { print "BGP CRITICAL | Config".Config::Tiny->errstr."\n"; exit(2); } $t=$Config->{_}->{prompt}; if ( defined($t) ) { $prompt = $t; } else { print "BGP CRITICAL | prompt-missing-in-configfile\n"; exit(2); } $t=$Config->{_}->{hostname}; if ( defined($t) ) { $hostname = $t; } else { print "BGP CRITICAL | hostname-missing-in-configfile\n"; exit(2); } $t=$Config->{_}->{pw}; if ( defined($t) ) { $password = $t; } else { print "BGP CRITICAL | password-missing-in-configfile\n"; exit(2); } $t=$Config->{_}->{enable}; if ( defined($t) ) { $enable = $t; } else { print "BGP CRITICAL | enable-missing-in-configfile\n"; exit(2); } # $interval (seconds) or greater $t=$Config->{_}->{interval}; if ( defined($t) && $t > $interval ) { $interval = $t; } # optional $t=$Config->{_}->{statefile}; if ( defined($t) ) { $statefn = $t; } # optional $t=$Config->{_}->{debugdir}; if ( defined($t) ) { $debugdir = $t; } } ##################################################################### sub parseconf { my(@pre)= @_; my($ip, $as, $peer, $prio, $ipas); my(@t); foreach $i (0..$#pre) { # print "$i: $pre[$i]\n"; # find my own AS if ( $pre[$i] =~ /^router bgp \d+$/ ) { if ( $myas eq '' ) { ( $myas ) = ( $pre[$i] =~ /^router bgp (\d+)$/ ); } else { return "parseconf: found second bgp router in line $i"; } next; } # find peers if ( $pre[$i] =~ /^ neighbor [0-9a-f\.:]+ remote-as \d+$/ ) { ( $ip, $as ) = ( $pre[$i] =~ /^ neighbor ([0-9a-f\.:]+) remote-as (\d+)$/ ); if ( !defined($ip) || !defined($as) ) { return "parseconf: did not find IP and AS in line $i"; } if ( defined($ip2as{$ip}) ) { return "parseconf: found IP and AS a second time in line $i"; } else { $ip2as{$ip} = $as; # preset some values, if no description is found $ipas = $ip.' '.$ip2as{$ip}; $peer{$ipas} = 'unknown'.$peerconfcnt; $peerlvl{$ipas} = $peerconfcnt; $peerprio{$ipas} = 'crit'; $peerconfcnt++; } next; } # find description, prio if ( $pre[$i] =~ /^ neighbor [0-9a-f\.:]+ description / ) { @t = split(/\s+/,$pre[$i]); $ip = $t[2]; $peer = $t[4]; $prio = $t[5]; # print "$ip $peer $prio\n"; if ( !defined($ip) || !defined($peer) ) { return "parseconf: did not find IP and peername in line $i"; } if ( !defined($ip2as{$ip}) ) { return "parseconf: did not find AS for IP $ip in line $i"; next; } $ipas = $ip.' '.$ip2as{$ip}; $peer{$ipas} = $peer; $peerlvl{$ipas} = $peerconfcnt; if ( defined($prio) ) { # TODO: check on valid values 'warn, crit, low' $peerprio{$ipas} = $prio; } else { $peerprio{$ipas} = 'crit'; } # print "peer: $peer ip: $ip as: $as\n"; next; } } return ''; } ##################################################################### sub parsemem { my(@pre)= @_; my($ncnt); # search for '\d+ peers, using \d+ \S+ of memory' line foreach $i (@pre) { # print "i: $i\n"; if ( $i =~ /\d+ peers, using \d+ \S+ of memory/ ) { ( $ncnt ) = ( $i =~ /(\d+) peers, using \d+ \S+ of memory/ ); if ( defined($ncnt) ) { $ncnt--; $peercnt=$ncnt; if ( $peercnt == $peerconfcnt ) { return "neigh=$ncnt "; } else { return "neigh=$ncnt;;;$peerconfcnt; "; } } } } } sub parsebgpsum { my(@pre)= @_; my($ip,$as,$upt,$pfx); my($off, $t); my($res)=''; my($mode)=''; if ( $pre[0] eq 'show ip bgp summary' ) { $mode='v4'; } elsif ( $pre[0] eq 'show ipv6 bgp summary' ) { $mode='v6'; } else { # print $pre[0]."\n"; $failres='unknown-mode'; return ''; } foreach $i (0..$#pre) { # print $mode.": $pre[$i]\n"; if ( $pre[$i] =~ /^([0-9a-f\.:]+)/ ) { ( $ip ) = ( $pre[$i] =~ /^([0-9a-f\.:]+)/ ); # which line do we have to parse ? if ( defined($ip) ) { if ( length($ip) > 15 ) { $t = substr($pre[$i+1],18); } else { $t = substr($pre[$i],18); } # parse the line # print $mode."m: $t\n"; my(@t)=split(/\s+/,$t); if ( $t[0] eq '' ) { shift(@t); } $as = $t[0]; $upt= upt2sec($t[6]); # print "ip: $ip as: $as\n"; if ( $upt < $interval ) { $failres .= $mode."age-".$peer{"$ip $as"}."=".$peerlvl{"$ip $as"}.' '; } $pfx= $t[7]; if ( $pfx !~ /^\d+$/ ) { # Active, Idle, OpenSent or something else ? $failres .= $peerprio{"$ip $as"}.'='.$peer{"$ip $as"}. ':'.$pfx.' '; } # print $mode."p: $ip $as $upt $pfx\n"; # output in nagios plugin syntax # if ( $ip =~ /:/ && $pfx != 0 ) { $saveres .= $mode."rt-".$peer{"$ip $as"}."=$pfx "; # } } else { # we can not parse this line, skip it next; } } else { next; } } return $res; } # IN: string with the session uptime in some obscure format # OUT: seconds since epoch sub upt2sec { my($inp) = @_; # examples: never, 01:43:22, 1d00h57m, 05w4d23h # print "inp: $inp\n"; if ( $inp eq 'never' ) { return -1; } if ( $inp =~ /:/ ) { my($h,$m,$s) = split(/:/,$inp); if ( !defined($h) ) { return -1; } return $h * 3600 + $m * 60 + $s; } if ( $inp =~ /\d+d\d+h\d+m/ ) { my($d,$h,$m) = ( $inp =~ /^(\d+)d(\d+)h(\d+)m$/ ); if ( !defined($d) ) { return -1; } return $d * 86400 + $h * 3600 + $m * 60; } if ( $inp =~ /\d+w\d+d\d+h/ ) { my($w,$d,$h) = ( $inp =~ /^(\d+)w(\d+)d(\d+)h$/ ); if ( !defined($w) ) { return -1; } return $w * 604800 + $d * 86400 + $h * 3600; } # if we can not parse it, return -1 return -1; } sub byt2byt { my($byte, $unit) = @_; if ( $unit eq 'bytes' ) { return $byte; } if ( $unit eq 'KiB' ) { return $byte * 1024; } if ( $unit eq 'MiB' ) { return $byte * 1024 * 1024; } }