11.8. Pinging with CiscoUsing ICMP messages (also know as "pinging") to determine if a host is up is a common and simple network monitoring technique. Pinging a small number of hosts is not that challenging. But if you have many hosts that are distributed all over the place, things could get ugly. If the polling interval used is short enough, you could run into the problem where the nth poll hasn't finished before the nth+1 poll begins. Another problem could be that the machine you want to ping from doesn't have proper routing to the host or hosts you want to monitor. Cisco routers and some switches support the Cisco ping MIB (download from ftp://ftp.cisco.com/pub/mibs/v2/CISCO-PING-MIB.my). Basically, this feature allows you to have routers perform ICMP operations on your behalf. In effect, you can have a distributed ping system. In this script,[*] we'll use SNMP to configure a Cisco router to perform pings on our behalf:
#!/usr/bin/perl use SNMP; # # This script was adapted from the one that comes with Net-SNMP # my %ipsToPing = ( "192.168.0.48" => 333, ); my $router = "192.168.0.130"; my $community = "public"; my $version = 1; my $sess = new SNMP::Session (DestHost => $router, Community => $community, Retries => 1, Version => $version); my $ciscoPingEntry = ".1.3.6.1.4.1.9.9.16.1.1.1"; my $ciscoPingEntryStatus = "$ciscoPingEntry.16"; my $ciscoPingEntryOwner = "$ciscoPingEntry.15"; my $ciscoPingProtocol = "$ciscoPingEntry.2"; my $ciscoPingPacketCount = "$ciscoPingEntry.4"; my $ciscoPingPacketSize = "$ciscoPingEntry.5"; my $ciscoPingAddress = "$ciscoPingEntry.3"; my $ciscoPingSentPackets = "$ciscoPingEntry.9"; my $ciscoPingReceivedPackets = "$ciscoPingEntry.10"; my $ciscoPingMinRtt = "$ciscoPingEntry.11"; my $ciscoPingAvgRtt = "$ciscoPingEntry.12"; my $ciscoPingMaxRtt = "$ciscoPingEntry.13"; my $ciscoPingCompleted = "$ciscoPingEntry.14"; # # Set up Cisco Ping table with targets we want to ping # foreach my $target (sort keys %ipsToPing){ my $row = $ipsToPing{$target}; # We must encode the IP we want to ping to HEX my $dec = pack("C*",split /\./, $target); $sess->set([ # First we clear the entry for this target [$ciscoPingEntryStatus, $row, 6, "INTEGER"], # Now we create a new entry for this target [$ciscoPingEntryStatus, $row, 5, "INTEGER"], # Set the owner of this entry [$ciscoPingEntryOwner, $row, "kjs", "OCTETSTR"], # Set the protocol to use, in this case "1" is IP [$ciscoPingProtocol, $row, 1, "INTEGER"], # Set the number of packets to send [$ciscoPingPacketCount, $row, 20, "INTEGER"], # Set the packet size [$ciscoPingPacketSize, $row, 150, "INTEGER"], # Finally set the target we want to ping [$ciscoPingAddress, $row, $dec, "OCTETSTR"]]); # This enables this target and causes the router to start pinging $sess->set([[$ciscoPingEntryStatus, $row, 1, "INTEGER"]]); if($sess->{ErrorStr}){ print "An Error Occurred: $sess->{ErrorStr}\n"; exit; } } # Give router time to do its thing... sleep 30; # # Get results # foreach my $target (sort keys %ipsToPing){ my $row = $ipsToPing{$target}; my ($sent, $received, $low, $avg, $high, $completed) = $sess->get([ [$ciscoPingSentPackets, $row], [$ciscoPingReceivedPackets, $row], [$ciscoPingMinRtt, $row], [$ciscoPingAvgRtt, $row], [$ciscoPingMaxRtt, $row], [$ciscoPingCompleted, $row]]); printf "($target)Packet loss: %d% (%d/%d)\n", (100 * ($sent-$received)) / $sent, $received, $sent; print "Average delay $avg (low: $low high: $high)\n"; # Here we remove this target's entry from the Cisco Ping Table $sess->set([$ciscoPingEntryStatus, $row, 6, "INTEGER"]); } Let's look at some details of this script:
Here's sample output from a run of this script: $ ./pingmib.pl (192.168.0.48)Packet loss: 0% (20/20) Average delay 1 (low: 1 high: 1) Now here's a look at an SNMP walk of the Cisco ping MIB (note that we ftp'ed the Cisco ping MIB and placed it in the same folder with the rest of the Net-SNMP MIBs): $ snmpwalk -m ALL -IR -v1 -c public 192.168.0.130 ciscoPingMIB CISCO-PING-MIB::ciscoPingProtocol.333 = INTEGER: ip(1) CISCO-PING-MIB::ciscoPingAddress.333 = STRING: c0:a8:0:30 CISCO-PING-MIB::ciscoPingPacketCount.333 = INTEGER: 20 CISCO-PING-MIB::ciscoPingPacketSize.333 = INTEGER: 150 CISCO-PING-MIB::ciscoPingPacketTimeout.333 = INTEGER: 2000 milliseconds CISCO-PING-MIB::ciscoPingDelay.333 = INTEGER: 0 milliseconds CISCO-PING-MIB::ciscoPingTrapOnCompletion.333 = INTEGER: false(2) CISCO-PING-MIB::ciscoPingSentPackets.333 = Counter32: 20 CISCO-PING-MIB::ciscoPingReceivedPackets.333 = Counter32: 20 CISCO-PING-MIB::ciscoPingMinRtt.333 = INTEGER: 1 milliseconds CISCO-PING-MIB::ciscoPingAvgRtt.333 = INTEGER: 1 milliseconds CISCO-PING-MIB::ciscoPingMaxRtt.333 = INTEGER: 1 milliseconds CISCO-PING-MIB::ciscoPingCompleted.333 = INTEGER: true(1) CISCO-PING-MIB::ciscoPingEntryOwner.333 = STRING: kjs CISCO-PING-MIB::ciscoPingEntryStatus.333 = INTEGER: active(1) To summarize, this script does the following:
One enhancement that can be made is to persist each row to the table and let the router continually ping the target or targets. This could be accomplished by creating new entries on the router and setting the following OID: ciscoPingDelay OBJECT-TYPE SYNTAX Integer32 (0..3600000) UNITS "milliseconds" MAX-ACCESS read-create STATUS current DESCRIPTION "Specifies the minimum amount of time to wait before sending the next packet in a sequence after receiving a response or declaring a timeout for a previous packet. The actual delay may be greater due to internal task scheduling." DEFVAL { 0 } ::= { ciscoPingEntry 7 } As the DESCRIPTION notes, ciscoPingDelay specifies a sort of sleep. The router will wait for the specified amount of time (in milliseconds) before it tries to ping the target again. Of course, you would also not want to destroy each row after you get the statistics from the routers, as is done in the earlier script. Here are a couple of things to watch out for if you use this method:
|