Add live MTR to LookingGlass

This commit is contained in:
Marc Hagen 2022-11-23 11:48:40 +01:00
parent c18884639e
commit f6a470e8f8
No known key found for this signature in database

View file

@ -180,7 +180,7 @@ class LookingGlass
*/
public static function mtr(string $host): bool
{
return self::procExecute('mtr -4 --report --report-wide', $host);
return self::procExecute('mtr --raw -n -4 -c ' . self::MTR_COUNT, $host);
}
/**
@ -191,7 +191,7 @@ class LookingGlass
*/
public static function mtr6(string $host): bool
{
return self::procExecute('mtr -6 --report --report-wide', $host);
return self::procExecute('mtr --raw -n -6 -c ' . self::MTR_COUNT, $host);
}
/**
@ -232,11 +232,11 @@ class LookingGlass
private static function procExecute(string $cmd, string $host, int $failCount = 2): bool
{
// define output pipes
$spec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$spec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
];
// sanitize + remove single quotes
$host = str_replace('\'', '', filter_var($host, FILTER_SANITIZE_URL));
@ -251,6 +251,7 @@ class LookingGlass
// check for mtr/traceroute
if (strpos($cmd, 'mtr') !== false) {
$type = 'mtr';
$parser = new Parser();
} elseif (strpos($cmd, 'traceroute') !== false) {
$type = 'traceroute';
} else {
@ -273,12 +274,14 @@ class LookingGlass
// correct output for mtr
if ($type === 'mtr') {
if ($match < 10 && preg_match('/^[0-9]\. /', $str, $string)) {
$str = preg_replace('/^[0-9]\. /', '&nbsp;&nbsp;' . $string[0], $str);
$match++;
} else {
$str = preg_replace('/^[0-9]{2}\. /', '&nbsp;' . substr($str, 0, 4), $str);
}
// correct output for mtr
$parser->update($str);
echo '---' . PHP_EOL . $parser->__toString() . PHP_EOL . str_pad('', 4096) . PHP_EOL;
// flush output buffering
@ob_flush();
flush();
continue;
}
// correct output for traceroute
elseif ($type === 'traceroute') {
@ -319,7 +322,7 @@ class LookingGlass
}
$status = proc_get_status($process);
if ($status['running'] == true) {
if ($status['running']) {
// close pipes that are still open
foreach ($pipes as $pipe) {
fclose($pipe);
@ -341,3 +344,189 @@ class LookingGlass
return true;
}
}
class Hop
{
/** @var int */
public $idx;
/** @var string */
public $asn = '';
/** @var float */
public $avg = 0.0;
/** @var int */
public $loss = 0;
/** @var float */
public $stdev = 0.0;
/** @var int */
public $sent = 0;
/** @var int */
public $recieved = 0;
/** @var float */
public $last = 0.0;
/** @var float */
public $best = 0.0;
/** @var float */
public $worst = 0.0;
/** @var string[] */
public $ips = [];
/** @var string[] */
public $hosts = [];
/** @var float[] */
public $timings = [];
}
class RawHop
{
/** @var string */
public $dataType;
/** @var int */
public $idx;
/** @var string */
public $value;
}
class Parser
{
/** @var Hop[] */
protected $hopsCollection = [];
/** @var int */
private $hopCount = 0;
/** @var int */
private $outputWidth = 38;
public function __construct()
{
putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
}
public function __toString(): string
{
$str = '';
foreach ($this->hopsCollection as $index => $hop) {
$host = $hop->hosts[0] ?? $hop->ips[0] ?? '???';
if (strlen($host) > $this->outputWidth) {
$this->outputWidth = strlen($host);
}
$hop->recieved = count($hop->timings);
if (count($hop->timings)) {
$hop->last = $hop->timings[count($hop->timings) - 1];
$hop->best = $hop->timings[0];
$hop->worst = $hop->timings[0];
$hop->avg = array_sum($hop->timings) / count($hop->timings);
}
if (count($hop->timings) > 1) {
$hop->stdev = $this->stDev($hop->timings);
}
foreach ($hop->timings as $time) {
if ($hop->best > $time) {
$hop->best = $time;
}
if ($hop->worst < $time) {
$hop->worst = $time;
}
}
$hop->loss = $hop->sent ? (100 * ($hop->sent - $hop->recieved)) / $hop->sent : 100;
$str = sprintf(
"%s%2d.|-- %s%3d.0%% %3d %5.1f %5.1f %5.1f %5.1f %5.1f\n",
$str,
$index,
str_pad($host, $this->outputWidth + 3, ' ', STR_PAD_RIGHT),
$hop->loss,
$hop->sent,
$hop->last,
$hop->avg,
$hop->best,
$hop->worst,
$hop->stdev
);
}
return sprintf(" Host%sLoss%% Snt Last Avg Best Wrst StDev\n%s", str_pad('', $this->outputWidth + 7, ' ', STR_PAD_RIGHT), $str);
}
private function stDev(array $array): float
{
$sdSquare = function ($x, $mean) {
return pow($x - $mean, 2);
};
// square root of sum of squares devided by N-1
return sqrt(array_sum(array_map($sdSquare, $array, array_fill(0, count($array), (array_sum($array) / count($array))))) / (count($array) - 1));
}
public function update($rawMtrInput)
{
//Store each line of output in rawhop structure
$things = explode(' ', $rawMtrInput);
if (count($things) !== 3 && (count($things) !== 4 && $things[0] === 'p')) {
return;
}
$rawHop = new RawHop();
$rawHop->dataType = $things[0];
$rawHop->idx = (int)$things[1];
$rawHop->value = $things[2];
if ($this->hopCount < $rawHop->idx + 1) {
$this->hopCount = $rawHop->idx + 1;
}
if (!isset($this->hopsCollection[$rawHop->idx])) {
$this->hopsCollection[$rawHop->idx] = new Hop();
}
$hop = $this->hopsCollection[$rawHop->idx];
$hop->idx = $rawHop->idx;
switch ($rawHop->dataType) {
case 'h':
$hop->ips[] = $rawHop->value;
$hop->hosts[] = gethostbyaddr($rawHop->value) ? : null;
break;
case 'd':
//Not entirely sure if multiple IPs. Better use -n in mtr and resolve later in summarize.
//out.Hops[data.idx].Host = append(out.Hops[data.idx].Host, data.value)
break;
case 'p':
$hop->sent++;
$hop->timings[] = (float)$rawHop->value / 1000;
break;
}
$this->hopsCollection[$rawHop->idx] = $hop;
$this->filterLastDupeHop();
}
// Function to calculate standard deviation (uses sd_square)
private function filterLastDupeHop()
{
// filter dupe last hop
$finalIdx = 0;
$previousIp = '';
foreach ($this->hopsCollection as $key => $hop) {
if (count($hop->ips) && $hop->ips[0] !== $previousIp) {
$previousIp = $hop->ips[0];
$finalIdx = $key + 1;
}
}
unset($this->hopsCollection[$finalIdx]);
usort($this->hopsCollection, function ($a, $b) {
return $a->idx - $b->idx;
});
}
}