1#!/usr/local/bin/perl -w
2############################## check_snmp_vrrp ##############
3my $Version='1.4';
4# Date : Oct 17 2007
5# Author  : Patrick Proy (patrick at proy.org)
6# Help : http://nagios.manubulon.com/
7# Licence : GPL - http://www.fsf.org/licenses/gpl.txt
8# Contrib : C. Maser (Alteon + Netscreen),  Harm-Jan Blok (Foundry)
9#################################################################
10#
11# Help : ./check_snmp_vrrp.pl -h
12#
13
14use strict;
15use Net::SNMP;
16use Getopt::Long;
17
18# Nagios specific
19
20my $TIMEOUT = 15;
21my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);
22
23# SNMP Datas
24
25######### Nokia (standard ???)
26my $all_vrrp  = "1.3.6.1.2.1.68";
27my $nokia_base_vrrp = "1.3.6.1.2.1.68.1.3.1";   # oid for vrrp
28my $nokia_vrrp_oper = "1.3.6.1.2.1.68.1.3.1.3";   # vrrp operational status
29my $nokia_vrrp_admin ="1.3.6.1.2.1.68.1.3.1.4";   # vrrp admin status
30my $nokia_vrrp_prio = "1.3.6.1.2.1.68.1.3.1.5";   # vrrp vrid priority
31
32######### Nokia Ipso Clustering
33my $nokia_clust_table = "1.3.6.1.4.1.94.1.21.5.1.4.1"; # IpsoclusterEntry
34my $nokia_clust_index = "1.3.6.1.4.1.94.1.21.5.1.4.1.1.1"; #index
35my $nokia_clust_memberid = "1.3.6.1.4.1.94.1.21.5.1.4.1.2.1"; # member ID
36my $nokia_clust_percent = "1.3.6.1.4.1.94.1.21.5.1.4.1.3.1"; #percent assigned
37my $nokia_clust_rating = "1.3.6.1.4.1.94.1.21.5.1.4.1.4.1"; # node rating
38my $nokia_clust_addr = "1.3.6.1.4.1.94.1.21.5.1.4.1.5.1"; # ip address
39
40######### LinkProof
41my $lp_base_vrrp = "1.3.6.1.2.1.68.1.3.1";   # oid for vrrp
42my $lp_vrrp_oper = "1.3.6.1.2.1.68.1.3.1.4";   # vrrp operational status
43my $lp_vrrp_admin ="1.3.6.1.2.1.68.1.3.1.5";   # vrrp admin status
44my $lp_vrrp_prio = "1.3.6.1.2.1.68.1.3.1.6";   # vrrp vrid priority
45
46######### Alteon (AD4 Loadbalancers)
47my $alteon_base_vrrp = "1.3.6.1.4.1.1872.2.1.9.4";
48my $alteon_vrrp_oper = "1.3.6.1.4.1.1872.2.1.9.4.1.1.2";
49my $alteon_vrrp_admin = "";
50my $alteon_vrrp_prio = "1.3.6.1.4.1.1872.2.1.9.4.1.1.3";
51
52######### Netscreen (ScreenOS 5.1)
53### .0 is always the queried device itself
54### so in a cluster every device has its own numbering of members
55my $ns_base_vrrp = "1.3.6.1.4.1.3224.6.2";
56my $ns_vrrp_oper = "1.3.6.1.4.1.3224.6.2.2.1.3";
57my $ns_vrrp_admin = "";
58my $ns_vrrp_prio = "1.3.6.1.4.1.3224.6.2.2.1.4";
59
60######## Foundry
61my $foundry_base_vrrp = "1.3.6.1.4.1.1991.1.2.12.3.1.1";   # oid for vrrp
62my $foundry_vrrp_oper = "1.3.6.1.4.1.1991.1.2.12.3.1.1.10";   # vrrp operational status
63my $foundry_vrrp_admin ="1.3.6.1.4.1.1991.1.2.12.3.1.1.3";   # vrrp admin status
64my $foundry_vrrp_prio = "1.3.6.1.4.1.1991.1.2.12.3.1.1.6";   # vrrp vrid priority
65
66
67######### Make an array
68my %base_vrrp = ("nokia",$nokia_base_vrrp,
69		"lp",$lp_base_vrrp,
70		"alteon",$alteon_base_vrrp,
71		"nsc",$ns_base_vrrp,
72		"foundry",$foundry_base_vrrp
73		);
74my %vrrp_oper = ("nokia",$nokia_vrrp_oper,
75		"lp",$lp_vrrp_oper,
76		"alteon",$alteon_vrrp_oper,
77		"nsc",$ns_vrrp_oper,
78		"foundry",$foundry_vrrp_oper
79		);
80my %vrrp_admin =("nokia",$nokia_vrrp_admin,
81		"lp",$lp_vrrp_admin,
82		"alteon",$alteon_vrrp_admin,
83		"nsc",$ns_vrrp_admin,
84		"foundry",$foundry_vrrp_admin
85		);
86my %vrrp_prio = ("nokia",$nokia_vrrp_prio,
87		"lp",$lp_vrrp_prio,
88		"alteon",$alteon_vrrp_prio,
89		"nsc",$ns_vrrp_prio,
90		"foundry",$foundry_vrrp_oper
91		);
92
93my %state_master=("nokia",3,"alteon",2,"lp",3,"nsc",2,"foundry",1);
94my %state_backup=("nokia",2,"alteon",3,"lp",2,"nsc",3,"foundry",2);
95
96# Globals
97
98my $o_host = 	undef; 		# hostname
99my $o_community = undef; 	# community
100my $o_version2	= undef;	#use snmp v2c
101my $o_port = 	161; 		# port
102my $o_help=	undef; 		# wan't some help ?
103my $o_verb=	undef;		# verbose mode
104my $o_version=	undef;		# print version
105my $o_state=	undef;		# Check master or backup state for ok
106my $o_clustnum=	undef; 		# number of cluster members
107my $o_clustprct=	undef;	# Max % assigned to one cluster.
108my $o_type=	'nokia';	# Check type : nokia|alteon|lp|nsc|foundry
109my $o_long=		undef;		# Make output long
110my $o_timeout=  5;              # Default 5s Timeout
111
112# SNMPv3 specific
113my $o_login=	undef;		# Login for snmpv3
114my $o_passwd=	undef;		# Pass for snmpv3
115my $v3protocols=undef;	# V3 protocol list.
116my $o_authproto='md5';		# Auth protocol
117my $o_privproto='des';		# Priv protocol
118my $o_privpass= undef;		# priv password
119
120# functions
121
122sub p_version { print "check_snmp_vrrp version : $Version\n"; }
123
124sub print_usage {
125    print "Usage: $0 [-v] -H <host> -C <snmp_community> [-2] | (-l login -x passwd [-X pass -L <authp>,<privp>]) -s <master|backup|num,%> [-T <nokia|alteon|lp|nsc|ipsocluster|foundry>] [-p <port>] [-t <timeout>] [-V]\n";
126}
127
128sub isnnum { # Return true if arg is not a number
129  my $num = shift;
130  if ( $num =~ /^(\d+\.?\d*)|(^\.\d+)$/ ) { return 0 ;}
131  return 1;
132}
133
134sub help {
135   print "\nSNMP VRRP Monitor for Nagios version ",$Version,"\n";
136   print "GPL licence, (c)2004-2007 Patrick Proy\n\n";
137   print_usage();
138   print <<EOT;
139-v, --verbose
140   print extra debugging information
141-h, --help
142   print this help message
143-H, --hostname=HOST
144   name or IP address of host to check
145-C, --community=COMMUNITY NAME
146   community name for the host's SNMP agent (implies v1 protocol)
147-2, --v2c
148   Use snmp v2c
149-l, --login=LOGIN ; -x, --passwd=PASSWD
150   Login and auth password for snmpv3 authentication
151   If no priv password exists, implies AuthNoPriv
152-X, --privpass=PASSWD
153   Priv password for snmpv3 (AuthPriv protocol)
154-L, --protocols=<authproto>,<privproto>
155   <authproto> : Authentication protocol (md5|sha : default md5)
156   <privproto> : Priv protocole (des|aes : default des)
157-P, --port=PORT
158   SNMP port (Default 161)
159-T, --type=<nokia|alteon|lp|nsc|ipso>
160   Type of vrrp router to check
161   nokia (default) : Nokia vrrp. Should be working for most vrrp routers
162   alteon : for Alteon AD4 Loadbalancers
163   lp : Radware Linkproof
164   nsc : Nescreen (ScreenOS 5.x NSRP)
165   ipso : Nokia IPSO clustering
166   foundry : Foundry VRRP
167-s, --state=master|backup|num,%
168   Nokia ipso clustering : number of members, max % assigned to nodes.
169   Other : check vrrp interface to be master or backup
170-g, --long
171   Make output long even is all is OK
172-t, --timeout=INTEGER
173   timeout for SNMP in seconds (Default: 5)
174-V, --version
175   prints version number
176EOT
177}
178
179# For verbose output
180sub verb { my $t=shift; print $t,"\n" if defined($o_verb) ; }
181
182# Get the alarm signal (just in case snmp timout screws up)
183$SIG{'ALRM'} = sub {
184     print ("ERROR: Alarm signal (Nagios time-out)\n");
185     exit $ERRORS{"UNKNOWN"};
186};
187
188sub check_options {
189    Getopt::Long::Configure ("bundling");
190    GetOptions(
191   	'v'	=> \$o_verb,		'verbose'	=> \$o_verb,
192        'h'     => \$o_help,    	'help'        	=> \$o_help,
193        'H:s'   => \$o_host,		'hostname:s'	=> \$o_host,
194        'p:i'   => \$o_port,   		'port:i'	=> \$o_port,
195        'C:s'   => \$o_community,	'community:s'	=> \$o_community,
196        't:i'   => \$o_timeout,         'timeout:i'     => \$o_timeout,
197	'V'	=> \$o_version,		'version'	=> \$o_version,
198	'g'	=> \$o_long,		'long'		=> \$o_long,
199	'T:s'	=> \$o_type,		'type:s'		=> \$o_type,
200	'2'	=> \$o_version2,	'v2c'		=> \$o_version2,
201	'l:s'	=> \$o_login,		'login:s'	=> \$o_login,
202	'x:s'	=> \$o_passwd,		'passwd:s'	=> \$o_passwd,
203	'X:s'	=> \$o_privpass,		'privpass:s'	=> \$o_privpass,
204	'L:s'	=> \$v3protocols,		'protocols:s'	=> \$v3protocols,
205	's:s'	=> \$o_state,		'state:s'	=> \$o_state
206    );
207    if (defined ($o_help) ) { help(); exit $ERRORS{"UNKNOWN"}};
208    if (defined($o_version)) { p_version(); exit $ERRORS{"UNKNOWN"}};
209    if ( ! defined($o_host) ) # check host and filter
210	{ print_usage(); exit $ERRORS{"UNKNOWN"}}
211    # check snmp information
212    if ( !defined($o_community) && (!defined($o_login) || !defined($o_passwd)) )
213	  { print "Put snmp login info!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}}
214	if ((defined($o_login) || defined($o_passwd)) && (defined($o_community) || defined($o_version2)) )
215	  { print "Can't mix snmp v1,2c,3 protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}}
216	if (defined ($v3protocols)) {
217	  if (!defined($o_login)) { print "Put snmp V3 login info with protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}}
218	  my @v3proto=split(/,/,$v3protocols);
219	  if ((defined ($v3proto[0])) && ($v3proto[0] ne "")) {$o_authproto=$v3proto[0];	}	# Auth protocol
220	  if (defined ($v3proto[1])) {$o_privproto=$v3proto[1];	}	# Priv  protocol
221	  if ((defined ($v3proto[1])) && (!defined($o_privpass))) {
222	    print "Put snmp V3 priv login info with priv protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}}
223	}
224    # Check state
225    if ($o_type eq "ipso") {
226	  my @state=split(/,/,$o_state);
227	  if ($#state != 1) {
228	    print "On ipso clustering : number of nodes, % assigned\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
229	  $state[1] =~ s/%//;
230	  if (isnnum($state[0]) || isnnum($state[1])) {
231	    print "On ipso clustering (numbers) : number of nodes, % assigned\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
232	  $o_clustnum=	$state[0];
233	  $o_clustprct=	$state[1];
234	} else {
235	  if ( !defined($o_state) || ($o_state ne "master") && ($o_state ne "backup") )
236 	  { print "state must be master or backup\n"; print_usage(); exit $ERRORS{"UNKNOWN"}}
237    }
238	# Check type
239     if ( !defined($o_type) || (($o_type ne "nokia") && ($o_type ne "alteon") && ($o_type ne "lp") && ($o_type ne"nsc") && ($o_type ne"ipso") && ($o_type ne "foundry")) )
240  	{ print "type must be alteon,nokia,lp,nsc or ipso\n"; print_usage(); exit $ERRORS{"UNKNOWN"}}
241
242}
243
244########## MAIN #######
245
246check_options();
247
248# Check gobal timeout if snmp screws up
249if (defined($TIMEOUT)) {
250  verb("Alarm at $TIMEOUT");
251  alarm($TIMEOUT);
252} else {
253  verb("no timeout defined : $o_timeout + 10");
254  alarm ($o_timeout+10);
255}
256
257# Connect to host
258my ($session,$error);
259if ( defined($o_login) && defined($o_passwd)) {
260  # SNMPv3 login
261  verb("SNMPv3 login");
262    if (!defined ($o_privpass)) {
263  verb("SNMPv3 AuthNoPriv login : $o_login, $o_authproto");
264    ($session, $error) = Net::SNMP->session(
265      -hostname   	=> $o_host,
266      -version		=> '3',
267      -username		=> $o_login,
268      -authpassword	=> $o_passwd,
269      -authprotocol	=> $o_authproto,
270	  -port             => $o_port,
271      -timeout          => $o_timeout
272    );
273  } else {
274    verb("SNMPv3 AuthPriv login : $o_login, $o_authproto, $o_privproto");
275    ($session, $error) = Net::SNMP->session(
276      -hostname   	=> $o_host,
277      -version		=> '3',
278      -username		=> $o_login,
279      -authpassword	=> $o_passwd,
280      -authprotocol	=> $o_authproto,
281      -privpassword	=> $o_privpass,
282	  -privprotocol => $o_privproto,
283	  -port             => $o_port,
284      -timeout          => $o_timeout
285    );
286  }
287} else {
288	if (defined ($o_version2)) {
289		# SNMPv2 Login
290		verb("SNMP v2c login");
291		  ($session, $error) = Net::SNMP->session(
292		 -hostname  => $o_host,
293		 -version   => 2,
294		 -community => $o_community,
295		 -port      => $o_port,
296		 -timeout   => $o_timeout
297		);
298  	} else {
299	  # SNMPV1 login
300	  verb("SNMP v1 login");
301	  ($session, $error) = Net::SNMP->session(
302		-hostname  => $o_host,
303		-community => $o_community,
304		-port      => $o_port,
305		-timeout   => $o_timeout
306	  );
307	}
308}
309if (!defined($session)) {
310   printf("ERROR opening session: %s.\n", $error);
311   exit $ERRORS{"UNKNOWN"};
312}
313
314############ Nokia ipso clustering
315
316my $key=undef;
317
318if ($o_type eq "ipso") {
319# Get cluster table
320my $resultat;
321if (Net::SNMP->VERSION < 4) {
322  $resultat = $session->get_table( $nokia_clust_table );
323} else {
324  $resultat = $session->get_table( Baseoid => $nokia_clust_table );
325}
326if (!defined($resultat)) {
327   printf("ERROR: Description table : %s.\n", $session->error);
328   $session->close;
329   exit $ERRORS{"UNKNOWN"};
330}
331$session->close;
332
333my $nclusterindex=0;
334my $output=undef;
335my $overload=0;
336foreach $key ( keys %$resultat) {
337   if ( $key =~ /$nokia_clust_memberid/){
338      # Get rid of the vrrp oper part
339      my $Cindex = $$resultat{$key};
340      $key =~ s/$nokia_clust_memberid\.//;
341	  verb("Found cluster, index $key");
342
343	  my $Pkey= $nokia_clust_percent . "." . $key;
344	  my $percent= $$resultat{$Pkey};
345	  verb("$percent / $Cindex");
346	  if ($percent > $o_clustprct) {$overload=1};
347          if ( defined($output) ) {
348  		$output .= "; Cluster " . $Cindex . " : ".$percent."%";
349 	  } else {
350	         $output = "Cluster " . $Cindex . " : ".$percent."%";
351 	  }
352	  $nclusterindex++;
353   }
354}
355
356if ($nclusterindex==0) {
357  print "No Cluster membre found : CRITICAL\n";
358  exit $ERRORS{"CRITICAL"};
359}
360if ($nclusterindex != $o_clustnum) {
361  print $output," : Not ",$o_clustnum," members : CRITICAL\n";
362  exit $ERRORS{"CRITICAL"};
363}
364if ($overload==1) {
365  print $output," assigned % is > " , $o_clustprct , " : WARNING\n";
366  exit $ERRORS{"WARNING"};
367}
368
369print $output," : OK\n";
370exit $ERRORS{"OK"};
371
372
373}
374
375########### get vrrp table ############
376
377# Get vrrp table
378my $resultat;
379if (Net::SNMP->VERSION < 4) {
380  $resultat = $session->get_table( $base_vrrp{$o_type} );
381} else {
382  $resultat = $session->get_table( Baseoid => $base_vrrp{$o_type} );
383}
384if (!defined($resultat)) {
385   printf("ERROR: Description table : %s.\n", $session->error);
386   $session->close;
387   exit $ERRORS{"UNKNOWN"};
388}
389$session->close;
390
391my @vrid=undef;
392my $nvrid=0;
393my $oid0=undef;
394my @oid=undef;
395
396if ( $o_type eq 'nsc' ) {
397  $nvrid = 1;
398  $vrid[0] = '0';
399} else {
400  foreach $key ( keys %$resultat) {
401     if ( $key =~ /$vrrp_oper{$o_type}/){
402        # Get rid of the vrrp oper part
403        $key =~ s/$vrrp_oper{$o_type}\.//;
404        @oid=split (/\./,$key);
405        $vrid[$nvrid]=pop(@oid);
406        $oid0=pop(@oid);
407        while ( defined ($oid0)) {
408           $vrid[$nvrid] = $oid0 . "." . $vrid[$nvrid];
409           $oid0=pop(@oid);
410        }
411        verb("Added vrid $vrid[$nvrid]");
412        $nvrid++;
413     }
414  }
415}
416
417if ( $nvrid == 0 )
418{ printf("No vrid found : CRITICAL\n");exit $ERRORS{"CRITICAL"};}
419
420my $ok=0;
421my $value;
422my $output=undef;
423my $vrid_out;
424for (my $i=0;$i<$nvrid;$i++) {
425   $output .= ", " if  (defined($output));
426   # Get last part of oid to output the vrid
427   $vrid_out = $vrid[$i];
428   $vrid_out =~ s/.*\.//;
429   $output .= "$vrid_out(";
430   # Get the state
431   $key=$vrrp_oper{$o_type}.".".$vrid[$i];
432
433   $value = ($$resultat{$key} == $state_master{$o_type}) ? "master" :
434	    ($$resultat{$key} == $state_backup{$o_type}) ? "backup" : "initialise : ".$$resultat{$key};
435   $output.=$value . "/";
436   ($value eq $o_state) && $ok++;
437   # Get the administrative status
438   if (($o_type eq 'alteon' )|| ($o_type eq 'nsc') ) {
439     $ok++
440   } else {
441     $key= $vrrp_admin{$o_type} . "." . $vrid[$i];
442     $value = ($$resultat{$key} == 1) ? "up" : "down";
443     $output.= $value . "/";
444     if (($o_type eq 'foundry')  && ($o_state eq 'backup') && ($value eq "down")) {
445        $ok++
446     } else {
447       ($value eq "up") && $ok++;
448     }
449   }
450   # Get the priority
451   $key=$vrrp_prio{$o_type}.".".$vrid[$i];
452   $value = $$resultat{$key};
453   $output.=$value . ")";
454}
455
456verb("verif : $ok");
457
458if ( $ok == (2*$nvrid) ) {
459   if (defined($o_long)) {
460      printf("Vrid : %s : %s %s : OK\n",$output,$nvrid,$o_state) ;
461   } else {
462     printf("%s vrid %s :OK\n",$nvrid,$o_state) ;
463   }
464   exit $ERRORS{"OK"}
465}
466printf("Vrid : %s : not all %s : NOK\n",$output,$o_state);
467exit $ERRORS{"CRITICAL"};
468