#!/usr/bin/perl -w # # check_sip plugin for nagios # $Revision: 1.4 $ # # Nagios plugin to check SIP servers # # By Sam Bashton, Bashton Ltd # bashton.com/content/nagiosplugins # Michael Hirschbichler, Institute of Broadband Communications, # Vienna University of Technology # Ryanny Lin # Craig Gowing, credativ Ltd # http://www.credativ.co.uk/ # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA use strict; use lib "/usr/lib64/nagios/plugins"; use utils qw($TIMEOUT %ERRORS &print_revision &support); use vars qw($PROGNAME); use IO::Socket::INET; #use Sys::Hostname; use Time::HiRes qw(gettimeofday ualarm); use Net::Domain qw (hostname hostfqdn hostdomain); use Switch; $PROGNAME = "check_sip"; my $VERSION = "1.4.0"; $ENV{'BASH_ENV'}=''; $ENV{'ENV'}=''; $ENV{'PATH'}=''; $ENV{'LC_ALL'}='C'; my ($opt_V,$opt_h,$opt_u,$opt_p,$opt_H, $opt_w, $opt_s, $opt_f); $opt_V = $opt_h = $opt_u = $opt_p = $opt_H = $opt_w = $opt_s = $opt_f = ''; my $state = 'UNKNOWN'; use Getopt::Long; Getopt::Long::Configure('bundling'); GetOptions( "V" => \$opt_V, "version" => \$opt_V, "h" => \$opt_h, "help" => \$opt_h, "s" => \$opt_s, "f=s" => \$opt_f, "fromuri=s" => \$opt_f, "u=s" => \$opt_u, "uri=s" => \$opt_u, "p=s" => \$opt_p, "port=s" => \$opt_p, "H=s" => \$opt_H, "host=s" => \$opt_H, "w=s" => \$opt_w, "warn=s" => \$opt_w ); # -h displays help if ($opt_h) { printHelp(); exit $ERRORS{'OK'}; } # -V display version number if ($opt_V) { print_revision($PROGNAME, $VERSION); exit $ERRORS{'OK'}; }; # Check the sip URI is OK unless ($opt_u) { printHelp(); exit $ERRORS{'UNKNOWN'} } # Port is 5060 unless otherwise specified unless ($opt_p) { $opt_p = 5060 } # Determine the host from the sip URI if it wasn't specified with -H unless ($opt_H) { $opt_H = hostFromURI($opt_u) } # Check the host is valid unless (utils::is_hostname($opt_H)) { print "$opt_H is not a valid hostname\n"; printHelp(); exit $ERRORS{"UNKNOWN"}; } unless ($opt_w) { $opt_w = 5 } # Warn if response takes longer than 5 seconds ### Main code ############################################################### # Timeout if we don't recieve a response within a suitable timeframe.. my ($startsec, $starttime); my $resend_times; my $max_resend; #my $Total_timeout=0.5; my ($finishsec, $finishtime); my $secdiff; my $rtime; $SIG{'ALRM'} = sub { #$Total_timeout+=$TIMEOUT; if($resend_times>=($max_resend)){ ($finishsec, $finishtime) = gettimeofday; $secdiff=$finishsec-$startsec; $rtime = $secdiff + $finishtime/ 1000000 - $starttime / 1000000; print ("SIP timeout: No response from SIP server after $rtime seconds. ($resend_times/$max_resend)\n"); exit $ERRORS{"CRITICAL"}; } }; #alarm($TIMEOUT); my $localhost = hostfqdn(); $opt_f = getFromURI($opt_f,$localhost,$opt_p); my $user=getUserPart($opt_f); my $socket = uconnect($opt_H, $opt_p); my @localinfo = unpack_sockaddr_in($socket->sockname); my $req = buildReq($localinfo[0], $opt_u, $opt_f,$user,$localhost); ($startsec, $starttime) = gettimeofday; my $response; for($TIMEOUT=0,$resend_times=0,$max_resend=11;$resend_times<=$max_resend;){ $socket->send($req); $response=''; $resend_times++; # sip retransmission, totally 35.5 seconds. switch($resend_times){ case 1 { ualarm(500000); } case 2 { $TIMEOUT=1; alarm($TIMEOUT); } case 3 { $TIMEOUT=2; alarm($TIMEOUT); } else { $TIMEOUT=4; alarm($TIMEOUT); } } #print $resend_times."->".$Total_timeout." $TIMEOUT\n"; $socket->recv($response, 1024) or $state = 'CRITICAL'; # TODO: CHECK and show '503 Service Unavailable' when receiving unreachable ICMP packet. #get rid of the 100 Trying - provisional response ... if ($response && getResponseCode($response) eq "100"){ $socket->recv($response, 1024) or $state = 'CRITICAL'; } last if ($response); } ($finishsec, $finishtime) = gettimeofday; $secdiff=$finishsec-$startsec; $rtime = $secdiff + (($finishtime - $starttime) / 1000000); # Time taken in seconds if(checkResponse($response,$rtime,$opt_s)) { if ($rtime > $opt_w) { $state = 'WARNING' } else { $state = 'OK' } } else { $state = 'CRITICAL' } exit $ERRORS{$state}; ### Subroutines ############################################################## sub uconnect { my ($host, $port) = @_; my $socket = new IO::Socket::INET->new(PeerPort=>$port, Proto=>'udp', PeerAddr=>$host); unless ($socket) { print "Unable to connect to $host\n"; exit $ERRORS{'UNKNOWN'} } return $socket; } sub getFromURI{ my ($from, $localhost,$localport) = @_; if (!("$from" eq "")){ return "$from:$localport"; }else { return "sip:checksip\@$localhost:$localport"; } } sub getUserPart{ my ($uri) = @_; my @uris=split(/\@/,$uri); my $user=$uris[0]; return $user; } sub hostFromURI { my ($uri) = @_; $uri =~ s/sip:[^\@]+@//; return $uri; } sub getResponseCode { my ($message) = @_; my @messageparts=split(/\ /,$message); return $messageparts[1]; } sub buildReq { my ($localport, $dsturi, $fromuri,$user,$localhost) = @_; my $req; my $tag = genTag(); my $idtag = genTag(); my $branch = genBranch(); $req.= "OPTIONS $dsturi SIP/2.0\r\n"; $req.= "Via: SIP/2.0/UDP $localhost:$localport;branch=$branch\r\n"; $req.= "Max-Forwards: 70\r\n"; $req.= "To: $dsturi\r\n"; $req.= "From: $fromuri;tag=$tag\r\n"; $req.= "Call-ID: $idtag\@$localhost\r\n"; $req.= "CSeq: 1 OPTIONS\r\n"; $req.= "Contact: <$user\@$localhost:$localport>\r\n"; $req.= "Accept: application/sdp\r\n"; $req.= "Content-Length: 0\r\n\r\n"; return $req; } sub genTag { my $tag; my @chars = ('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8', '9'); for (my $i = 0; $i < 6; $i++) { $tag .= $chars[rand(scalar @chars)]; } return $tag; } sub genBranch { my $branch = "z9hG4bK"; my @chars = ('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8', '9'); for (my $i = 0; $i < 11; $i++) { $branch .= $chars[rand(scalar @chars)]; } return $branch; } sub printHelp { print "This plugin tests the sip service on the specified host.\n\n"; print "Usage: $PROGNAME -u sip:uri\@example.com [-H host -p PORT -f sip:fromuri\@example.com -w WARNTIME -s]\n"; print " $PROGNAME [-h | --help]\n"; print " $PROGNAME [-V | --version]\n\n"; print "Options:\n"; print " -u sip:uri\@example.com\n"; print " Full SIP uri, eg sip:uri\@example.com\n"; print " -h, --help\n"; print " Print this help\n"; print " -V, --version\n"; print " Print version information\n"; print " -H host\n"; print " Host name or IP Address to connect to\n"; print " -p port\n"; print " Port to connect to\n"; print " -f sip:fromuri\@example.com\n"; print " Full SIP uri, will be used for the \"From:\"-Header\n"; print " -s\n"; print " Changes default behavior: all SIP-responses will result in an \"OK\"\n\n"; } sub checkResponse { my ($response, $rtime, $sp_behavior) = @_; my @header=split(/\r/,$response); my $tstring=$header[0]; my $rcode=getResponseCode($response); if (!$sp_behavior){ #in this case, we want to see if the SIP-server is respoding positively to our request # Some SUT respond with 100 Trying - assume everything is OK if we get this if ($response =~ /^SIP.+[12]00/){ print "$tstring, $rtime seconds response time, cnt=".$resend_times."|rtt=".$rtime."s;cnt=".$resend_times.";0.5s;1s;0:10; code=".$rcode."\n"; return 1; } elsif ($response =~ /^SIP.+404 Not Found/) { print "$tstring, $rtime seconds response time, cnt=".$resend_times."|rtt=".$rtime."s;cnt=".$resend_times.";0.5s;1s;0:10; code=".$rcode."\n"; return 0 } else { print "Unknown error: $tstring, $rtime seconds response time, cnt=".$resend_times."|rtt=".$rtime."s;cnt=".$resend_times.";0.5s;1s;0:10; code=".$rcode."\n"; return 0; } }else{ #in this case, we accept every response from the server, as long it is SIP if ($response =~ /^SIP./){ print "$tstring, $rtime seconds response time, cnt=".$resend_times."|rtt=".$rtime."s;cnt=".$resend_times.";0.5s;1s;0:10; code=".$rcode."\n"; return 1; } else { print "Unknown error: $tstring, $rtime seconds response time, cnt=".$resend_times."|rtt=".$rtime."s;cnt=".$resend_times.";0.5s;1s;0:10; code=".$rcode."\n"; return 0; } } }