However there are some issues on that script. one of the most important ones is that because of the way your script handles socket, if you run more than 1 session pinging the same ip, there is a great chance that they end up receiving each others packets and therefore giving mismatch identity error.
I changed your script a bit to check for the matching identities and ignore the packet if they weren't matched. i believe i could make that 'while loop' faster if i knew more about php/icmp.
another thing that i added was calculating jitter(http://www.rfc-editor.org/rfc/rfc1889.txt) on every packet. which is pretty important for me.
Here's the ppping.inc:
Code: Select all
<?php
/* -------------------------------------------------------------
This file is part of PurplePixie Ping (PPPing)
PPPing is (C) Copyright 2010 PurplePixie Systems
PPPing 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 3 of the License, or
(at your option) any later version.
PPPing 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 PPPing. If not, see www.gnu.org/licenses
For more information see www.purplepixie.org/phpping
-------------------------------------------------------------- */
/**
* PPPing PHP Ping Utility
* @package PPPing PHP Ping Utility
* @author David Cutting
* @version 0.01
**/
/**
* Main PPPing Class
* @package PPPing PHP Ping Utility
**/
class PPPing
{
/**
* Time-to-Live for IP Packet (DO NOT USE)
*
* -1 Uses system default (usually 64). Please note that this is currently
* not functional.
**/
var $ttl=-1;
/**
* Hostname to ping (resolvable host or IP address)
**/
var $hostname="";
/**
* Identifier - will fill with random content (16 bits)
**/
var $identity=0;
/**
* Sequence number in decimal (16 bits)
**/
var $sequence=0;
/**
* Timeout in seconds - maximum wait for a response before timeout
**/
var $timeout=10;
/**
* Timer start seconds
**/
var $timer_start_sec=0;
/**
* Timer start mseconds
**/
var $timer_start_msec=0;
/**
* Data package for the ping
**/
var $data_package = "PPPing";
/**
* Debug - prints output to the screen
**/
var $debug=false;
/**
* Holds information on last result
**/
var $Last = array();
/**
* Clears last data
**/
function clearLast()
{
$this->last = array(
"set" => false,
"result" => $this->last["result"],
"ttl" => 0,
"hops" => 0,
"source" => "",
"destination" => "",
"jitter" => $this->last["jitter"] );
}
/**
* Get a padded hex identifier
**/
function getIdentity()
{
if ( (is_numeric($this->identity)) && ($this->identity>=0) && ($this->identity<65535) )
$id=$this->identity;
else $id=0;
$id=dechex($id);
$id=str_pad($id,4,"0",STR_PAD_LEFT);
$id=pack("H*",$id);
return $id;
}
/**
* Get a padded hex sequence
**/
function getSequence()
{
if ( (is_numeric($this->sequence)) && ($this->sequence>=0) && ($this->sequence<65535) )
$seq=$this->sequence;
else $seq=0;
$seq=dechex($seq);
$seq=str_pad($seq,4,"0",STR_PAD_LEFT);
$seq=pack("H*",$seq);
return $seq;
}
/**
* Returns a hex string of the binary data for debug purposes
**/
function getHex($data)
{
$parts=unpack("H*",$data);
return $parts[1];
}
/**
* Randomise identity and/or sequence within 16 bit parameters
**/
function Randomise($identity=true,$sequence=false)
{
mt_srand(microtime()*1000000);
if ($identity) $this->identity=mt_rand(0,65534);
if ($sequence) $this->sequence=mt_rand(0,65534);
}
/**
* Start timer (reset values)
**/
function startTimer()
{
$now=microtime();
$timearray=explode(" ",$now);
$this->timer_start_sec=$timearray[1];
$this->timer_start_msec=$timearray[0];
}
/**
* Stop timer (return result)
**/
function stopTimer()
{
$now=microtime();
$timearray=explode(" ",$now);
$finish_secs=$timearray[1];
$finish_msecs=$timearray[0];
$elapsed_seconds = $finish_secs - $this->timer_start_sec;
$elapsed_time = $elapsed_seconds + $finish_msecs - $this->timer_start_msec;
$elapsed_ms = $elapsed_time * 1000;
$elapsed_ms = round($elapsed_ms,3);
return $elapsed_ms;
}
/**
* Constructor - randomises ID
**/
function PPPing()
{
$this->last["jitter"] = -1;
$this->Randomise();
}
/**
* Returns a dotted quad from hex format IPv4 address
**/
function ipAddress($hexip)
{
$quad="";
for($a=0; $a<=6; $a+=2)
{
$portion=substr($hexip,$a,2);
$decimal=hexdec($portion);
if ($quad!="") $quad.=".";
$quad.=$decimal;
}
return $quad;
}
/**
* Generate an ICMP checksum
**/
function Checksum($data)
{
if (strlen($data)%2)
$data .= "\x00";
$bit = unpack('n*', $data);
$sum = array_sum($bit);
while ($sum >> 16)
$sum = ($sum >> 16) + ($sum & 0xffff);
return pack('n*', ~$sum);
}
/**
* Do a ping of the set hostname
*
* @return float
* Returns a negative number of failure which can be turned into text with
* the strError method. A positive number is a response in milliseconds (ms)
**/
function Ping()
{
$this->clearLast();
$type = "\x08"; // icmp echo
$code = "\x00";
$checksum = "\x00\x00"; // initial
$identifier = $this->getIdentity();
$dec_identity = $this->identity;
//$identifier = "\x00\x00";
//$seqNumber = "\x00\x00";
$seqNumber = $this->getSequence();
$dec_sequence = $this->sequence;
$data = $this->data_package;
$package = $type.$code.$checksum.$identifier.$seqNumber.$data;
$checksum = $this->Checksum($package); // proper checksum
$package = $type.$code.$checksum.$identifier.$seqNumber.$data;
$ip_protocol_code = getprotobyname("ip");
$ip_ttl_code = 7;
// Lookup hostname
$ips=str_replace(".","",$this->hostname);
if (!is_numeric($ips))
{
$host=gethostbyname($this->hostname);
if ($host==$this->hostname) return -5;
}
else $host=$this->hostname;
// Create Socket
$socket = socket_create(AF_INET, SOCK_RAW, 1); // @
//or die(socket_strerror(socket_last_error()));
if (!$socket) return -3;
// Set Non-Blocking
socket_set_nonblock($socket); // @
$socket_ttl = socket_get_option($socket,$ip_protocol_code,$ip_ttl_code);
//for ($a=0; $a<64; $a++)
// echo $a." - ".@socket_get_option($socket,$ip_protocol_code,$a)."\n";
if ($this->ttl>0)
{
socket_set_option($socket,$ip_protocol_code,$ip_ttl_code,128);
$socket_ttl = socket_get_option($socket,$ip_protocol_code,$ip_ttl_code);
//socket_set_option($socket,Socket::IPPROTO_IP,Socket::IP_TTL,128);
//$socket_ttl = socket_get_option($socket,Socket::IPPROTO_IP,Socket::IP_TTL);
}
else $socket_ttl = 64; // standard TTL
// Connect Socket
$sconn=socket_connect($socket, $host, null); // @
if (!$sconn) return 0;
// Package Size
//$package_size = 8+strlen($data);
$package_size = strlen($package);
// Send Data
socket_send($socket, $package, $package_size, 0); // @
// Start Timer
$this->startTimer();
$startTime = microtime(true); // need this for the looping section
// Read Data
$keepon=true;
$check_identity=true;
$ipheader="";
$header=false;
while( $check_identity && $keepon ) // @socket_read
{
if ($echo_reply=socket_read($socket, 255))
{
$rx_parts = unpack("C*",$echo_reply);
if ($rx_parts[1] == 0x45) // IP Header Information
{
$header=true;
$ipheader=substr($echo_reply,0,20);
$echo_reply=substr($echo_reply,20);
}
else $header=false;
$echo_reply_hex = $this->getHex($echo_reply);
$reply_identity = hexdec(substr($echo_reply_hex,8,4));
if ($reply_identity === $dec_identity)
{
$check_identity=false;
$elapsed=$this->stopTimer();
}
//else echo "check point! <br />";
}
if ( (microtime(true) - $startTime) > $this->timeout )
$keepon=false;
}
if ($check_identity === false) // didn't time out and check_identity = false
{
socket_close($socket); // @
if ( $echo_reply === false ) return -4;
else if (strlen($echo_reply)<2) return -2;
if ($header) $rx_parts = unpack("C*",$echo_reply);
$tx_parts = unpack("C*",$package);
$ipheader_hex="";
$ipheader_hex = $this->getHex($ipheader);
if ($this->debug)
{
echo "\n";
echo " TyCoChksIdenSequData\n";
echo "TX: ".$this->getHex($package)."\n";
echo "RX: ".$this->getHex($echo_reply)."\n";
if ($ipheader!="") echo "HR: ".$ipheader_hex."\n";
}
$reply_type = $rx_parts[1];
$reply_code = $rx_parts[2];
$reply_sequence = hexdec(substr($echo_reply_hex,12,4));
$match=true;
if ($ipheader!="")
{
$source=substr($ipheader_hex,24,8);
$dest=substr($ipheader_hex,32,8);
$ttl=hexdec(substr($ipheader_hex,16,2));
if ($this->debug) echo $this->ipAddress($source)." => ".$this->ipAddress($dest)." | ttl: ".$ttl."\n";
if ($source==$dest) $match=true;
else $match=false;
$this->last["set"]=true;
$this->last["source"]=$this->ipAddress($source);
$this->last["destination"]=$this->ipAddress($dest);
$this->last["ttl"]=$ttl;
$this->last["hops"]=$socket_ttl - $ttl;
}
if ( (($rx_parts[1]==0) || (($rx_parts[1]==8)&&($match))) && ($rx_parts[2]==0) )
{ // is echo_reply (0) or is echo_request (8) AND match (from same host)
// and has code of 0
// valid response
if ($reply_identity != $dec_identity) return -8; // ID mismatch
else if ($reply_sequence != $dec_sequence) return -7; // sequence mismatch
else
{
if ($this->last["jitter"] == -1) $this->last["jitter"] = 0; // first run
else $this->last["jitter"] = round($this->last["jitter"] + (abs($elapsed - $this->last["result"]) - $this->last["jitter"]) / 16, 3);
$this->last["result"]=$elapsed;
return $elapsed;
}
}
else
{ // ICMP Error
return -9;
}
}
socket_close($socket); // @
return -1; // timeout
}
/**
* Returns textual error for code
**/
function strError($code)
{
switch($code)
{
case -1: return "Timed Out"; break;
case -2: return "Reply Too Short"; break;
case -3: return "Failed to Open Socket"; break;
case -4: return "Invalid (false) Response"; break;
case -5: return "Hostname Lookup Failed"; break;
case -7: return "Sequence Mismatch"; break;
case -8: return "Identity Mismatch"; break;
case -9: return "ICMP Generic Error"; break;
default: return "Unknown Error"; break;
}
}
}
?>
and my ping.php:
Code: Select all
<html>
<head>
<script type="text/JavaScript">
<!--
function pageScroll() {
window.scrollBy(0,300); // horizontal and vertical scroll increments
scrolldelay = setTimeout('pageScroll()',100); // scrolls every 100 milliseconds
}
function stopScroll() {
clearTimeout(scrolldelay);
}
function timedRefresh(timeoutPeriod) {
setTimeout("location.reload(true);",timeoutPeriod);
}
// -->
</script>
</head>
<body onload="JavaScript:timedRefresh(60000);JavaScript:stopScroll();">
<script type="text/javascript" language="JavaScript">
pageScroll();
</script>
</body>
</html>
<?php
include "ppping.inc";
/**
* Display totals
**/
function DisplayTotals($host,$successes,$failures,$times,$seq_counter)
{
if ( ($successes<=0) || ($seq_counter<=0) ) $perc=100;
else if ($failures<=0) $perc=0;
else
{
$perc = ($failures/$seq_counter)*100;
$perc = round($perc,2);
}
echo "<br/>Ping statistics for ".$host.":<br />";
echo "Packets: Sent = ".$seq_counter.", Received = ".$successes.", Lost = ".$failures." (".$perc."% loss),<br />";
$results=count($times);
$high=0;
$low=999999999;
$total=0;
foreach($times as $time)
{
if ($time>$high) $high=$time;
if ($time<$low) $low=$time;
$total+=$time;
}
if ($total>0) // has something
{
$average = round($total/$results,3);
echo "Approximate round trip times in milli-seconds:<br />";
echo "Minimum = ".$low."ms, Maximum = ".$high."ms, Average = ".$average."ms";
}
}
/** Start the routine **/
$ping = new PPPing();
$host = "192.168.103.100";
$ping->hostname = $host;
$ping->timeout = 2;
$successes=0;
$failures=0;
$times=array();
$seq_counter = 1;
// $ping->debug = true;
// $ping->identity = 1;
ob_implicit_flush();
for($i=0; $i<=99; $i=$i+1)
{
$ping->sequence = $seq_counter++;
$result = $ping->Ping();
// echo $ping->identity." ";
// echo $ping->sequence." ";
if ($result<0) // failed
{
$failures++;
echo "Error: ".$ping->strError($result)."<br />";
}
else
{
$successes++;
$times[]=$result;
if ($ping->last["set"])
echo "Reply[".$ping->sequence."] from ".$ping->last["source"].": time=".$result."ms"." TTL=".$ping->last["ttl"]." jitter=".$ping->last["jitter"]."ms<br />";
else echo $result."ms"."<br />";
}
usleep(1000000);
}
$seq_counter--;
DisplayTotals($host,$successes,$failures,$times,$seq_counter);
usleep(1000000); // for making sure the scrollbar will hit the end
?>
there are a lot of more cool things that could be done. i hope i could find some time for that
so what are your thoughts?