#!/usr/bin/perl
# script to remotely ping machine(s) from (cisco) routerX while sitting on box A.
# Requirements:
# 1. routerX needs to be a cisco box running snmp 
# 2. you as a user should know the RW community string for X
# 3. need perl on boxA
# Example:
# rping -c guess 10.3.1.15 amazon.com
# The above asks cisco router 10.3.1.15 to ping amazon.com, using 
# (RW) community "guess"
#		
#			Made by Omer Ansari omer@ansari.com 10/24
#			New feature -f added 11/07/00
		
use Socket;
use Net::SNMP;
use Getopt::Std;
$|++; # for the dramatic dots ;-)
getopt('fcwtgnsh');

$source=shift;
$dest=shift if (!($opt_f));
#Duuh:
$checkforsrcdest=(!(($source)|($dest)));
$checkforall=(!(($opt_c)|($opt_w)|($opt_f)|($opt_t)|($opt_g)|($opt_n)|($opt_s)|($opt_h)));
$checkforall=($checkforsrcdest&&$checkforall);

if (($opt_h)|($checkforall)) { &help();} else {};
#the following defines the input file for the destinations
if ($opt_f) {$input_file="$opt_f";undef $dest;} else { undef $inputfile};
#The following is the Community_string:
if ($opt_c) {$cs="$opt_c"} else {$cs='private'};
#This defines waiting until the src would have the data:
if ($opt_w) {$sleep_time="$opt_w"} else {$sleep_time='2'};
#This defines the timeout value after a packet is considered dropped:
if ($opt_t) {$ping_timeout="$opt_t"} else {$ping_timeout='1000'};
#This is the gaps between the successive probes:
if ($opt_g) {$ping_gap="$opt_g"} else {$ping_gap='3'};
#This is the number of packets to be sent:
if ($opt_n) {$packet_count="$opt_n"} else {$packet_count='5'};
#This is the ping packet size:
if ($opt_s) {$packet_size="$opt_s"} else {$packet_size='64'};


sub ss	{
#	select undef, undef, undef, 0.3; 
	}
sub help {
	print STDERR <<EOF;
rping v0.1 RemotePing Facility
Lets you ping machine(s) from router X while sitting on box A!
Freeware by Omer Ansari <omer\@ansari.com>

Syntax:
rping	[-c comm_string] [-w wait_time] [-t timeout] [-g gap]\
	[-n probe_count] [-s probe_size] [-h] <Source> <Destination>

rping	[-c comm_string] [-w wait_time] [-t timeout] [-g gap]\
	[-n probe_count] [-s probe_size] [-h] <-f input_file> <Source> 

Options:
	-c comm_string:	The RW community string on the cisco router
			(i.e. the <Source>). Default: private
	-w wait_time:	The amount of time the script waits before
			polling the <Source> for the ping results
			[Note: Increase this if you're not 
			getting results] Default: 2s
	-t timeout:	Defines the timeout value after which the 
			packet is considered dropped. Default: 3000ms	
	-g gap:		The gap between successive ping probes
			Can be set to zero.  Default: 3ms
	-n probe_count:	The number of packets to be sent. Default: 5
	-s probe_size:	The size of each ping packet. Default: 64bytes
	-f input_file:	The file which has multiple destinations to ping
	-h help:	Help (this dump)

EOF
exit;
			}
sub Host2IP	{
	$host = shift;
(undef,undef,undef,undef,$thisaddr)=gethostbyname($host);
	$ip=inet_ntoa($thisaddr);
		return $ip;
		}

sub ResponseCheck	{
$response = shift;
if (!defined($response)) { 
           printf("ERROR: %s\n", $session->error);
           $session->close;
           exit 1;
        }
			}

sub Hex	{
#takes Remote_Dest's ip & convert each octet to hex
$IP= shift;
undef @hex_IP;
@IP=split(/\./, $IP);

foreach $IP_octet (@IP)	{
	$hex_octet = sprintf("%02x",$IP_octet);
	push @hex_IP, $hex_octet;
			}
$hex_IP=join("", @hex_IP);
#hardcoding cisco.com's IP ( 198.133.219.25 ) in hex format just to check
#code functionality...$hex_IP='c685db19';
return $hex_IP;
	}

sub DramaticDots	{
	select undef, undef, undef, 0.25; print("\.");
	select undef, undef, undef, 0.25; print("\.");
	select undef, undef, undef, 0.25; print("\.");
	select undef, undef, undef, 0.25; print("\.");
			}	

	

sub Init_Probe_Src	{
$source_IP=shift;
$cs=shift;

print("Initializing $source_IP to send ping probes"); &DramaticDots();print "\n";
($session, $error) = Net::SNMP->session(
				Hostname=>$source_IP,
				#Port=>161,
				#Version=>1,
				Community=>$cs
					);

    if (!defined($session))	{
           printf("ERROR: %s\n", $error);
           exit 1;
	        		}
print("community(RW): $cs, wait_time: ${sleep_time}s, ");
print("timeout: ${ping_timeout}ms, probe_gap: ${ping_gap}ms, ");
print("# of pings: $packet_count, packet_size: ${packet_size}bytes\n");



			} #endsub Init_Probe_Src

sub Init_Probe_Dest	{
$i = shift; #leaf
$dest_IP = shift;

#defining a specific leaf for one destination
#--------------------------------------------
#$i='100';
$ciscoPingEntryStatus_OID='.1.3.6.1.4.1.9.9.16.1.1.1.16.'."$i";
$ciscoPingEntryOwner_OID='.1.3.6.1.4.1.9.9.16.1.1.1.15.'."$i";
$ciscoPingProtocol_OID='.1.3.6.1.4.1.9.9.16.1.1.1.2.'."$i";
$ciscoPingAddress_OID='.1.3.6.1.4.1.9.9.16.1.1.1.3.'."$i";
$ciscoPingPacketCount_OID='.1.3.6.1.4.1.9.9.16.1.1.1.4.'."$i";
$ciscoPingPacketSize_OID='.1.3.6.1.4.1.9.9.16.1.1.1.5.'."$i";
$ciscoPingPacketTimeout_OID='.1.3.6.1.4.1.9.9.16.1.1.1.6.'."$i";
$ciscoPingDelay_OID='.1.3.6.1.4.1.9.9.16.1.1.1.7.'."$i";

# Initialization of the Ping Probe:
# ---------------------------------
#first to destroy any previous results on row $i:
$response = $session->set_request($ciscoPingEntryStatus_OID, INTEGER, '6');
&ResponseCheck($response);
#second, to initialize (createAndWait) the ciscoPingEntryStatus
#builds the row by setting ciscoPingEntryStatus = 5
$response = $session->set_request($ciscoPingEntryStatus_OID, INTEGER, '5');
&ResponseCheck($response);

# Setting ping probe characteristics:
# -----------------------------------
## Now, to set the characteristics of the ping
# Set ciscoPingEntryOwner = <anyname>
print(".");

$response = $session->set_request($ciscoPingEntryOwner_OID, OCTET_STRING, 'ping_src');

&ResponseCheck($response);

#Set ciscoPingProtocol = 1 = ip
	#print("setting ciscoPingProtocol to 1 \(ip\)\n");
$response = $session->set_request($ciscoPingProtocol_OID, INTEGER, '1');
ss;
&ResponseCheck($response);

#Set ciscoPingAddress = Hex($ping_destination)
#note, you have to pass a hex string and not a regular IP,
#for this you'd need to call the hex() subroutine to change it

$hex_IP=&Hex($dest_IP);
	#print("setting ciscoPingAddress to hex($dest_IP)\n");
#the pack solution below was provided by the creator of the SNMP.pm module himself,
#mr. David Town (dtown@pit.comms.marconi.com). Thank you!
$response = $session->set_request($ciscoPingAddress_OID, OCTET_STRING, pack('H*', $hex_IP));
&ResponseCheck($response);

#Set the probe timeout (ciscoPingPacketTimeout)
$response = $session->set_request($ciscoPingPacketTimeout_OID, INTEGER32, $ping_timeout);
ss;
&ResponseCheck($response);

#Set the gaps (in ms) between successive probes (ciscoPingDelay)
$response = $session->set_request($ciscoPingDelay_OID, INTEGER32, $ping_gap);
ss;
&ResponseCheck($response);

#Set the packet count to $packetcount (ciscoPingPacketCount)
	#print("setting ciscoPingPacketCount to $packet_count\n");
$response = $session->set_request($ciscoPingPacketCount_OID, INTEGER, $packet_count);
ss;
&ResponseCheck($response);

#Set the packet size to $packet_size (ciscoPingPacketSize)
	#print("setting ciscoPingPacketSize to $packet_size\n");
$response = $session->set_request($ciscoPingPacketSize_OID, INTEGER, $packet_size);
ss;
&ResponseCheck($response);

			} #endsub Init_Probe_Dest



sub Launch_Probe	{
$i = shift ; #leaf
$ciscoPingEntryStatus_OID='.1.3.6.1.4.1.9.9.16.1.1.1.16.'."$i";


#first verify that ping is ready to go (ciscoPingEntryStatus = 2)
#print("verify if ping is ready ciscoPingEntryStatus should be 2 \(notInService\)\n");
$response = $session->get_request($ciscoPingEntryStatus_OID);

&ResponseCheck($response);
if ($response->{$ciscoPingEntryStatus_OID} != 2)	{
		print STDERR "ERROR: ping probe not ready: $!\n";
		exit;
							} else {
	#print "entrystatys: $response->{$ciscoPingEntryStatus_OID}\n";
		#print "ping probes ready to be launched";&DramaticDots();print"\n";
								}

#launch probe, Set ciscoPingEntryStatus = 1 to tell it to activate
#print("Launching $packet_count ping probes \($packet_size bytes\): \n");
print("$source_IP ($source) --\> $leaf_IP{$i} ($IP_name{$leaf_IP{$i}})\n");
$response = $session->set_request($ciscoPingEntryStatus_OID, INTEGER, 1);
#sleep $sleep_time;print "\n";


			} #endsub Launch_Probe

sub Get_Results	{
@leaves = @_;

#doing an snmpwalk [get_table()] to get the data:

$results_OID='.1.3.6.1.4.1.9.9.16.1.1.1';
$response = $session->get_table($results_OID);
&ResponseCheck($response);

foreach $i (@leaves)	{
$ciscoPingSentPackets_OID='.1.3.6.1.4.1.9.9.16.1.1.1.9.'."$i";
$ciscoPingReceivedPackets_OID='.1.3.6.1.4.1.9.9.16.1.1.1.10.'."$i";
$ciscoPingMinRtt_OID='.1.3.6.1.4.1.9.9.16.1.1.1.11.'."$i";
$ciscoPingAvgRtt_OID='.1.3.6.1.4.1.9.9.16.1.1.1.12.'."$i";
$ciscoPingMaxRtt_OID='.1.3.6.1.4.1.9.9.16.1.1.1.13.'."$i";

$sent=$response->{$ciscoPingSentPackets_OID};
$received=$response->{$ciscoPingReceivedPackets_OID};
$percentage=($received/$sent)*100;
$min=$response->{$ciscoPingMinRtt_OID};
$avg=$response->{$ciscoPingAvgRtt_OID};
$max=$response->{$ciscoPingMaxRtt_OID};

$ip="$leaf_IP{$i}";
print("\(target: $IP_name{$ip}\): Success rate :$percentage\% ($received\/$sent), ");
print("round-trip min/avg/max = $min/$avg/$max ms\n");

#last to destroy the results on row $i:
#Commented out for debugging purposes...
	#print("setting ciscoPingEntryStatus to 6 \(destroy\)\n");
#$response = $session->set_request($ciscoPingEntryStatus_OID, INTEGER, '6');

		
			}
		} #endsub Get_Results

     
#MAIN() :
#=======

	($source_IP)=&Host2IP($source);

#first to check if single dest or multiple:
if (!($input_file))	{
	#single dest.
	($dest_IP)=&Host2IP($dest);
	$IP_name{$dest_IP}=$dest;
	push @destinations, $dest_IP;
				} else	{
	open (INPUT_FILE, "<$input_file");
	while (<INPUT_FILE>)	{
		#create an array with all dest. IPs
		$d_host = $_;
		chomp $d_host;
		$d_ip = &Host2IP($d_host);
		$IP_name{$d_ip} = $d_host;
		push @destinations, $d_ip;
				}
	$number_of_dest = @destinations;
					}
$leaf = 90; 
	&Init_Probe_Src($source_IP, $cs);
	print("Setting ping probe attributes"); #&DramaticDots();

foreach $destination (@destinations)	{

	$leaf = $leaf + 10;
	$leaf_IP{$leaf} = $destination;
	&Init_Probe_Dest($leaf, $destination);
		
	
	push @leaves, $leaf;
	
					} #endforeach $dest..
print "\n";
foreach $lif (@leaves)	{
	&Launch_Probe($lif);
			}

sleep $sleep_time;
&Get_Results(@leaves);
	
#close the session
$session->close;
