1package Smokeping::probes::CiscoRTTMonEchoICMP; 2 3=head1 301 Moved Permanently 4 5This is a Smokeping probe module. Please use the command 6 7C<smokeping -man Smokeping::probes::CiscoRTTMonEchoICMP> 8 9to view the documentation or the command 10 11C<smokeping -makepod Smokeping::probes::CiscoRTTMonEchoICMP> 12 13to generate the POD document. 14 15=cut 16 17use strict; 18use base qw(Smokeping::probes::basefork); 19use Symbol; 20use Carp; 21use BER; 22use SNMP_Session; 23use SNMP_util "0.97"; 24use Smokeping::ciscoRttMonMIB "0.2"; 25 26sub pod_hash { 27 my $e = "="; 28 return { 29 name => <<DOC, 30Smokeping::probes::CiscoRTTMonEchoICMP - Probe for SmokePing 31DOC 32 description => <<DOC, 33A probe for smokeping, which uses the ciscoRttMon MIB functionality ("Service Assurance Agent", "SAA") of Cisco IOS to measure ICMP echo ("ping") roundtrip times between a Cisco router and any IP address. 34DOC 35 notes => <<DOC, 36${e}head2 IOS VERSIONS 37 38It is highly recommended to use this probe with routers running IOS 12.0(3)T or higher and to test it on less critical routers first. I managed to crash a router with 12.0(9) quite consistently ( in IOS lingo 12.0(9) is older code than 12.0(3)T ). I did not observe crashes on higher IOS releases, but messages on the router like the one below, when multiple processes concurrently accessed the same router (this case was IOS 12.1(12b) ): 39 40Aug 20 07:30:14: %RTT-3-SemaphoreBadUnlock: %RTR: Attempt to unlock semaphore by wrong RTR process 70, locked by 78 41 42Aug 20 07:35:15: %RTT-3-SemaphoreInUse: %RTR: Could not obtain a lock for RTR. Process 80 43 44 45${e}head2 INSTALLATION 46 47To install this probe copy ciscoRttMonMIB.pm files to (\$SMOKEPINGINSTALLDIR)/lib/Smokeping and CiscoRTTMonEchoICMP.pm to (\$SMOKEPINGINSTALLDIR)/lib/Smokeping/probes. V0.97 or higher of Simon Leinen's SNMP_Session.pm is required. 48 49The router(s) must be configured to allow read/write SNMP access. Sufficient is: 50 51 snmp-server community RTTCommunity RW 52 53If you want to be a bit more restrictive with SNMP write access to the router, then consider configuring something like this 54 55 access-list 2 permit 10.37.3.5 56 snmp-server view RttMon ciscoRttMonMIB included 57 snmp-server community RTTCommunity view RttMon RW 2 58 59The above configuration grants SNMP read-write only to 10.37.3.5 (the smokeping host) and only to the ciscoRttMon MIB tree. The probe does not need access to SNMP variables outside the RttMon tree. 60DOC 61 bugs => <<DOC, 62The probe sends unnecessary pings, i.e. more than configured in the "pings" variable, because the RTTMon MIB only allows to set a total time for all pings in one measurement run (one "life"). Currently the probe sets the life duration to "pings"*5+3 seconds (5 secs is the ping timeout value hardcoded into this probe). 63DOC 64 see_also => <<DOC, 65L<http://oss.oetiker.ch/smokeping/> 66 67L<http://www.switch.ch/misc/leinen/snmp/perl/> 68 69The best source for background info on SAA is Cisco's documentation on L<http://www.cisco.com> and the CISCO-RTTMON-MIB documentation, which is available at: 70L<ftp://ftp.cisco.com/pub/mibs/v2/CISCO-RTTMON-MIB.my> 71DOC 72 authors => <<DOC, 73Joerg.Kummer at Roche.com 74DOC 75 } 76} 77 78my $pingtimeout = 5; 79 80sub new($$$) 81{ 82 my $proto = shift; 83 my $class = ref($proto) || $proto; 84 my $self = $class->SUPER::new(@_); 85 86 # no need for this if we run as a cgi 87 unless ( $ENV{SERVER_SOFTWARE} ) { 88 $self->{pingfactor} = 1000; 89 }; 90 return $self; 91} 92 93sub ProbeDesc($){ 94 my $self = shift; 95 return "CiscoRTTMonEchoICMP"; 96} 97 98sub pingone ($$) { 99 my $self = shift; 100 my $target = shift; 101 102 my $pings = $self->pings($target) || 20; 103 my $tos = $target->{vars}{tos}; 104 my $bytes = $target->{vars}{packetsize}; 105 106 # use the process ID as as row number to make this poll distinct on the router; 107 my $row=$$; 108 109 if (defined 110 StartRttMibEcho($target->{vars}{ioshost}.":::::2", $target->{addr}, 111 $bytes, $pings, $target->{vars}{iosint}, $tos, $row, $target->{vars}{vrf})) 112 { 113 # wait for the series to finish 114 sleep ($pings*$pingtimeout+5); 115 if (my @times=FillTimesFromHistoryTable($target->{vars}{ioshost}.":::::2", $pings, $row)){ 116 DestroyData ($target->{vars}{ioshost}.":::::2", $row); 117 return @times; 118 } 119 else { 120 return(); 121 } 122 } 123 else { 124 return (); 125 } 126} 127 128sub StartRttMibEcho ($$$$$$){ 129 my ($host, $target, $size, $pings, $sourceip, $tos, $row,$vrf) = @_; 130 131 # resolve the target name and encode its IP address 132 $_=$target; 133 if (!/^([0-9]|\.)+/) { 134 (my $name, my $aliases, my $addrtype, my $length, my @addrs) = gethostbyname ($target); 135 $target=join('.',(unpack("C4",$addrs[0]))); 136 } 137 my @octets=split(/\./,$target); 138 my $encoded_target= pack ("CCCC", @octets); 139 140 # resolve the source name and encode its IP address 141 my $encoded_source = undef; 142 if (defined $sourceip) { 143 $_=$sourceip; 144 if (!/^([0-9]|\.)+/) { 145 (my $name, my $aliases, my $addrtype, my $length, my @addrs) = gethostbyname ($sourceip); 146 $sourceip=join('.',(unpack("C4",$addrs[0]))); 147 } 148 my @octets=split(/\./,$sourceip); 149 $encoded_source= pack ("CCCC", @octets); 150 } 151 152 ############################################################# 153 # rttMonCtrlAdminStatus - 1:active 2:notInService 3:notReady 4:createAndGo 5:createAndWait 6:destroy 154 #delete data from former measurements 155 #return undef unless defined 156 # &snmpset($host, "rttMonCtrlAdminStatus.$row",'integer', 6); 157 158 ############################################################# 159 # Check RTTMon version and supported protocols 160 $SNMP_Session::suppress_warnings = 10; # be silent 161 (my $version)=&snmpget ($host, "rttMonApplVersion"); 162 if (! defined $version ) { 163 Smokeping::do_log ("$host doesn't support or allow RTTMon !\n"); 164 return undef; 165 } 166 Smokeping::do_log ("$host supports $version\n"); 167 $SNMP_Session::suppress_warnings = 0; # report errors 168 169 # echo(1), pathEcho(2), fileIO(3), script(4), udpEcho(5), tcpConnect(6), http(7), 170 # dns(8), jitter(9), dlsw(10), dhcp(11), ftp(12) 171 my $udpEchoSupported=0==1; 172 snmpmaptable ($host, 173 sub () { 174 my ($proto, $supported) = @_; 175 # 1 is true , 2 is false 176 $udpEchoSupported=0==0 if ($proto==5 && $supported==1); 177 }, 178 "rttMonApplSupportedRttTypesValid"); 179 180 ############################################################# 181 #setup the new data row 182 183 my @params=(); 184 push @params, 185 "rttMonCtrlAdminStatus.$row", 'integer', 5, 186 "rttMonCtrlAdminRttType.$row", 'integer', 1, 187 "rttMonEchoAdminProtocol.$row", 'integer', 2, 188 "rttMonEchoAdminTargetAddress.$row", 'octetstring', $encoded_target, 189 "rttMonCtrlAdminTimeout.$row", 'integer', $pingtimeout*1000, 190 "rttMonCtrlAdminFrequency.$row", 'integer', $pingtimeout, 191 "rttMonHistoryAdminNumBuckets.$row", 'integer', $pings, 192 "rttMonHistoryAdminNumLives.$row", 'integer', 1, 193 "rttMonHistoryAdminFilter.$row", 'integer', 2, 194 "rttMonEchoAdminPktDataRequestSize.$row",'integer', $size-8, 195 "rttMonScheduleAdminRttStartTime.$row", 'timeticks', 1, 196 "rttMonScheduleAdminRttLife.$row", 'integer', $pings*$pingtimeout+3, 197 "rttMonScheduleAdminConceptRowAgeout.$row",'integer', 60; 198 199 if(defined($vrf)){ 200 push @params,"rttMonEchoAdminVrfName.$row",'octetstring',$vrf; 201 } 202 203 # with udpEcho support (>= 12.0(3)T ) the ICMP ping support was enhanced in the RTTMon SW - we are 204 # NOT using udpEcho, but echo (ICMP echo, ping) 205 if ($udpEchoSupported) { 206 push @params, "rttMonEchoAdminTOS.$row", 'integer', $tos; 207 push @params, "rttMonCtrlAdminNvgen.$row", 'integer', 2; 208 209 # the router (or this script) doesn't check whether the IP address is one of 210 # the router's IP address, i.e. the router might send packets, but never 211 # gets ping replies.. 212 if (defined $sourceip) { 213 push @params, "rttMonEchoAdminSourceAddress.$row", 'octetstring', $encoded_source; 214 } 215 } 216 else { 217 Smokeping::do_log ("Warning this host does not support ToS or iosint\n"); 218 } 219 220 return undef unless defined 221 &snmpset($host, @params); 222 223 ############################################################# 224 # and go ! 225 return undef unless defined 226 &snmpset($host, "rttMonCtrlAdminStatus.$row",'integer',1); 227 228 return 1; 229} 230 231 232# RttResponseSense values 233# 1:ok 2:disconnected 3:overThreshold 4:timeout 5:busy 6:notConnected 7:dropped 8:sequenceError 234# 9:verifyError 10:applicationSpecific 11:dnsServerTimeout 12:tcpConnectTimeout 13:httpTransactionTimeout 235#14:dnsQueryError 15:httpError 16:error 236 237sub FillTimesFromHistoryTable($$$$) { 238 my ($host, $pings, $row) = @_; 239 my @times; 240 241 # snmpmaptable walks two tables (of equal size) 242 # - "rttMonHistoryCollectionCompletionTime.$row", 243 # - "rttMonHistoryCollectionSense.$row" 244 # The code in the sub() argument is executed for each index value snmptable walks 245 snmpmaptable ($host, 246 sub () { 247 my ($index, $rtt, $status) = @_; 248 push @times, (sprintf ("%.10e", $rtt/1000)) 249 if ($status==1); 250 }, 251 "rttMonHistoryCollectionCompletionTime.$row", 252 "rttMonHistoryCollectionSense.$row"); 253 254 return sort { $a <=> $b } @times; 255} 256 257sub DestroyData ($$) { 258 my ($host, $row) = @_; 259 260 &snmpset($host, "rttMonCtrlOperState.$row", 'integer', 3); 261 &snmpset($host, "rttMonCtrlAdminStatus.$row", 'integer', 2); 262 #delete any old config 263 &snmpset($host, "rttMonCtrlAdminStatus.$row", 'integer', 6); 264} 265 266sub targetvars { 267 my $class = shift; 268 return $class->_makevars($class->SUPER::targetvars, { 269 _mandatory => [ 'ioshost' ], 270 ioshost => { 271 _example => 'RTTcommunity@Myrouter.foobar.com.au', 272 _doc => <<DOC, 273The (mandatory) ioshost parameter specifies the Cisco router, which will 274execute the pings, as well as the SNMP community string on the router. 275DOC 276 }, 277 timeout => { 278 _re => '\d+', 279 _example => 15, 280 _default => $pingtimeout+10, 281 _doc => "How long a single RTTMonEcho ICMP 'ping' take at maximum plus 10 seconds to spare. Since we control our own timeout the only purpose of this is to not have us killed by the ping method from basefork.", 282 }, 283 iosint => { 284 _example => '10.33.22.11', 285 _doc => <<DOC, 286The (optional) iosint parameter is the source address for the pings 287sent. This should be one of the active (!) IP addresses of the router to 288get results. IOS looks up the target host address in the forwarding table 289and then uses the interface(s) listed there to send the ping packets. By 290default IOS uses the (primary) IP address on the sending interface as 291source address for a ping. The RTTMon MIB versions before IOS 12.0(3)T 292didn't support this parameter. 293DOC 294 }, 295 tos => { 296 _example => 160, 297 _default => 0, 298 _doc => <<DOC, 299The (optional) tos parameter specifies the value of the ToS byte in 300the IP header of the pings. Multiply DSCP values times 4 and Precedence 301values times 32 to calculate the ToS values to configure, e.g. ToS 160 302corresponds to a DSCP value 40 and a Precedence value of 5. The RTTMon 303MIB versions before IOS 12.0(3)T didn't support this parameter. 304DOC 305 }, 306 vrf => { 307 _example => "INTERNET", 308 _doc => <<DOC, 309The the VPN name in which the RTT operation will be used. For regular RTT 310operation this field should not be configured. The agent 311will use this field to identify the VPN routing Table for 312this operation. 313DOC 314 }, 315 packetsize => { 316 _doc => <<DOC, 317The packetsize parameter lets you configure the packetsize for the pings 318sent. The minimum is 8, the maximum 16392. Use the same number as with 319fping, if you want the same packet sizes being used on the network. 320DOC 321 _default => 56, 322 _re => '\d+', 323 _sub => sub { 324 my $val = shift; 325 return "ERROR: packetsize must be between 8 and 16392" 326 unless $val >= 8 and $val <= 16392; 327 return undef; 328 } 329 } 330 }); 331} 332 3331; 334 335