2017-10-31 14:01:56 +01:00
|
|
|
#!/usr/bin/perl
|
|
|
|
#
|
|
|
|
# Dynamic route parser
|
|
|
|
# Combines MTR and Ping features
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use POSIX qw(strftime);
|
|
|
|
use Scalar::Util qw(looks_like_number);
|
|
|
|
use Socket;
|
|
|
|
|
|
|
|
my $HELP=<<EO_HELP;
|
|
|
|
Pandora FMS plugin for route parse
|
|
|
|
Usage: $0 -t target [options]
|
|
|
|
|
|
|
|
OPTIONS
|
|
|
|
-c n number of tests (n)
|
|
|
|
--no-ping 1 disable ping mode
|
|
|
|
--no-mtr 1 disable mtr mode
|
|
|
|
-s 1 symmetric routing (1) *default
|
|
|
|
asymmetric routing (0)
|
|
|
|
|
|
|
|
*Warning* in ping mode, the maximum number of steps detected is 9
|
|
|
|
|
|
|
|
EO_HELP
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Imported methods
|
|
|
|
################################################################################
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Mix hashses
|
|
|
|
################################################################################
|
|
|
|
sub merge_hashes {
|
|
|
|
my $_h1 = shift;
|
|
|
|
my $_h2 = shift;
|
|
|
|
|
|
|
|
my %ret = (%{$_h1}, %{$_h2});
|
|
|
|
|
|
|
|
return \%ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Check if a value is in an array
|
|
|
|
################################################################################
|
|
|
|
sub in_array($$){
|
|
|
|
my ($array, $value) = @_;
|
|
|
|
|
|
|
|
if (empty($value)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
my %params = map { $_ => 1 } @{$array};
|
|
|
|
if (exists($params{$value})) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Check if a given variable contents a number
|
|
|
|
################################################################################
|
|
|
|
sub to_number($) {
|
|
|
|
my $n = shift;
|
|
|
|
|
|
|
|
if(empty($n)) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($n =~ /[\d+,]*\d+\.\d+/) {
|
|
|
|
# American notation
|
|
|
|
$n =~ s/,//g;
|
|
|
|
}
|
|
|
|
elsif ($n =~ /[\d+\.]*\d+,\d+/) {
|
|
|
|
# Spanish notation
|
|
|
|
$n =~ s/\.//g;
|
|
|
|
$n =~ s/,/./g;
|
|
|
|
}
|
|
|
|
if(looks_like_number($n)) {
|
|
|
|
return $n;
|
|
|
|
}
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Erase blank spaces before and after the string
|
|
|
|
################################################################################
|
|
|
|
sub trim($){
|
|
|
|
my $string = shift;
|
|
|
|
if (empty ($string)){
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
$string =~ s/\r//g;
|
|
|
|
|
|
|
|
chomp ($string);
|
|
|
|
$string =~ s/^\s+//g;
|
|
|
|
$string =~ s/\s+$//g;
|
|
|
|
|
|
|
|
return $string;
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Empty
|
|
|
|
################################################################################
|
|
|
|
sub empty($){
|
|
|
|
my $str = shift;
|
|
|
|
|
|
|
|
if (! (defined ($str)) ){
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(looks_like_number($str)){
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ref ($str) eq "ARRAY") {
|
|
|
|
return (($#{$str}<0)?1:0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ref ($str) eq "HASH") {
|
|
|
|
my @tmp = keys %{$str};
|
|
|
|
return (($#tmp<0)?1:0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($str =~ /^\ *[\n\r]{0,2}\ *$/) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2017-10-26 20:37:15 +02:00
|
|
|
}
|
2017-10-31 14:01:56 +01:00
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# is Enabled
|
|
|
|
################################################################################
|
|
|
|
sub is_enabled($){
|
|
|
|
my $value = shift;
|
|
|
|
|
|
|
|
if ((defined ($value)) && ($value > 0)){
|
|
|
|
# return true
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#return false
|
|
|
|
return 0;
|
|
|
|
|
2017-10-26 20:37:15 +02:00
|
|
|
}
|
2017-10-31 14:01:56 +01:00
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# print_module
|
|
|
|
################################################################################
|
|
|
|
sub print_module ($$;$){
|
|
|
|
my $config = shift;
|
|
|
|
my $data = shift;
|
|
|
|
my $not_print_flag = shift;
|
|
|
|
|
|
|
|
if ((ref($data) ne "HASH") || (!defined $data->{name})) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $xml_module = "";
|
|
|
|
# If not a string type, remove all blank spaces!
|
|
|
|
if ($data->{type} !~ m/string/){
|
|
|
|
$data->{value} = trim($data->{value});
|
|
|
|
}
|
|
|
|
|
|
|
|
$data->{tags} = $data->{tags}?$data->{tags}:($config->{MODULE_TAG_LIST}?$config->{MODULE_TAG_LIST}:undef);
|
|
|
|
$data->{interval} = $data->{interval}?$data->{interval}:($config->{MODULE_INTERVAL}?$config->{MODULE_INTERVAL}:undef);
|
|
|
|
$data->{module_group} = $data->{module_group}?$data->{module_group}:($config->{MODULE_GROUP}?$config->{MODULE_GROUP}:undef);
|
|
|
|
|
|
|
|
# Global instructions (if defined)
|
|
|
|
$data->{unknown_instructions} = $config->{unknown_instructions} unless (defined($data->{unknown_instructions}) || (!defined($config->{unknown_instructions})));
|
|
|
|
$data->{warning_instructions} = $config->{warning_instructions} unless (defined($data->{warning_instructions}) || (!defined($config->{warning_instructions})));
|
|
|
|
$data->{critical_instructions} = $config->{critical_instructions} unless (defined($data->{critical_instructions}) || (!defined($config->{critical_instructions})));
|
|
|
|
|
|
|
|
$xml_module .= "<module>\n";
|
|
|
|
$xml_module .= "\t<name><![CDATA[" . $data->{name} . "]]></name>\n";
|
|
|
|
$xml_module .= "\t<type>" . $data->{type} . "</type>\n";
|
|
|
|
|
|
|
|
if (ref ($data->{value}) eq "ARRAY") {
|
|
|
|
$xml_module .= "\t<datalist>\n";
|
|
|
|
foreach (@{$data->{value}}) {
|
|
|
|
$xml_module .= "\t<data><![CDATA[" . $data->{value} . "]]></data>\n";
|
|
|
|
}
|
|
|
|
$xml_module .= "\t</datalist>\n";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$xml_module .= "\t<data><![CDATA[" . $data->{value} . "]]></data>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !(empty($data->{desc}))) {
|
|
|
|
$xml_module .= "\t<description><![CDATA[" . $data->{desc} . "]]></description>\n";
|
|
|
|
}
|
|
|
|
if ( !(empty ($data->{unit})) ) {
|
|
|
|
$xml_module .= "\t<unit><![CDATA[" . $data->{unit} . "]]></unit>\n";
|
|
|
|
}
|
|
|
|
if (! (empty($data->{interval})) ) {
|
|
|
|
$xml_module .= "\t<module_interval><![CDATA[" . $data->{interval} . "]]></module_interval>\n";
|
|
|
|
}
|
|
|
|
if (! (empty($data->{tags})) ) {
|
|
|
|
$xml_module .= "\t<tags>" . $data->{tags} . "</tags>\n";
|
|
|
|
}
|
|
|
|
if (! (empty($data->{module_group})) ) {
|
|
|
|
$xml_module .= "\t<module_group>" . $data->{module_group} . "</module_group>\n";
|
|
|
|
}
|
|
|
|
if (! (empty($data->{module_parent})) ) {
|
|
|
|
$xml_module .= "\t<module_parent>" . $data->{module_parent} . "</module_parent>\n";
|
|
|
|
}
|
|
|
|
if (! (empty($data->{wmin})) ) {
|
|
|
|
$xml_module .= "\t<min_warning><![CDATA[" . $data->{wmin} . "]]></min_warning>\n";
|
|
|
|
}
|
|
|
|
if (! (empty($data->{wmax})) ) {
|
|
|
|
$xml_module .= "\t<max_warning><![CDATA[" . $data->{wmax} . "]]></max_warning>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{cmin})) ) {
|
|
|
|
$xml_module .= "\t<min_critical><![CDATA[" . $data->{cmin} . "]]></min_critical>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{cmax})) ){
|
|
|
|
$xml_module .= "\t<max_critical><![CDATA[" . $data->{cmax} . "]]></max_critical>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{wstr}))) {
|
|
|
|
$xml_module .= "\t<str_warning><![CDATA[" . $data->{wstr} . "]]></str_warning>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{cstr}))) {
|
|
|
|
$xml_module .= "\t<str_critical><![CDATA[" . $data->{cstr} . "]]></str_critical>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{cinv}))) {
|
|
|
|
$xml_module .= "\t<critical_inverse><![CDATA[" . $data->{cinv} . "]]></critical_inverse>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{winv}))) {
|
|
|
|
$xml_module .= "\t<warning_inverse><![CDATA[" . $data->{winv} . "]]></warning_inverse>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{max}))) {
|
|
|
|
$xml_module .= "\t<max><![CDATA[" . $data->{max} . "]]></max>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{min}))) {
|
|
|
|
$xml_module .= "\t<min><![CDATA[" . $data->{min} . "]]></min>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{post_process}))) {
|
|
|
|
$xml_module .= "\t<post_process><![CDATA[" . $data->{post_process} . "]]></post_process>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{disabled}))) {
|
|
|
|
$xml_module .= "\t<disabled><![CDATA[" . $data->{disabled} . "]]></disabled>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{min_ff_event}))) {
|
|
|
|
$xml_module .= "\t<min_ff_event><![CDATA[" . $data->{min_ff_event} . "]]></min_ff_event>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{status}))) {
|
|
|
|
$xml_module .= "\t<status><![CDATA[" . $data->{status} . "]]></status>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{timestamp}))) {
|
|
|
|
$xml_module .= "\t<timestamp><![CDATA[" . $data->{timestamp} . "]]></timestamp>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{custom_id}))) {
|
|
|
|
$xml_module .= "\t<custom_id><![CDATA[" . $data->{custom_id} . "]]></custom_id>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{critical_instructions}))) {
|
|
|
|
$xml_module .= "\t<critical_instructions><![CDATA[" . $data->{critical_instructions} . "]]></critical_instructions>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{warning_instructions}))) {
|
|
|
|
$xml_module .= "\t<warning_instructions><![CDATA[" . $data->{warning_instructions} . "]]></warning_instructions>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{unknown_instructions}))) {
|
|
|
|
$xml_module .= "\t<unknown_instructions><![CDATA[" . $data->{unknown_instructions} . "]]></unknown_instructions>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{quiet}))) {
|
|
|
|
$xml_module .= "\t<quiet><![CDATA[" . $data->{quiet} . "]]></quiet>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{module_ff_interval}))) {
|
|
|
|
$xml_module .= "\t<module_ff_interval><![CDATA[" . $data->{module_ff_interval} . "]]></module_ff_interval>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{crontab}))) {
|
|
|
|
$xml_module .= "\t<crontab><![CDATA[" . $data->{crontab} . "]]></crontab>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{min_ff_event_normal}))) {
|
|
|
|
$xml_module .= "\t<min_ff_event_normal><![CDATA[" . $data->{min_ff_event_normal} . "]]></min_ff_event_normal>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{min_ff_event_warning}))) {
|
|
|
|
$xml_module .= "\t<min_ff_event_warning><![CDATA[" . $data->{min_ff_event_warning} . "]]></min_ff_event_warning>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{min_ff_event_critical}))) {
|
|
|
|
$xml_module .= "\t<min_ff_event_critical><![CDATA[" . $data->{min_ff_event_critical} . "]]></min_ff_event_critical>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{ff_timeout}))) {
|
|
|
|
$xml_module .= "\t<ff_timeout><![CDATA[" . $data->{ff_timeout} . "]]></ff_timeout>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{each_ff}))) {
|
|
|
|
$xml_module .= "\t<each_ff><![CDATA[" . $data->{each_ff} . "]]></each_ff>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{parent_unlink}))) {
|
|
|
|
$xml_module .= "\t<module_parent_unlink><![CDATA[" . $data->{parent_unlink} . "]]></module_parent_unlink>\n";
|
|
|
|
}
|
|
|
|
if (! (empty ($data->{alerts}))) {
|
|
|
|
foreach my $alert (@{$data->{alerts}}){
|
|
|
|
$xml_module .= "\t<alert_template><![CDATA[" . $alert . "]]></alert_template>\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (defined ($config->{global_alerts})){
|
|
|
|
foreach my $alert (@{$config->{global_alerts}}){
|
|
|
|
$xml_module .= "\t<alert_template><![CDATA[" . $alert . "]]></alert_template>\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$xml_module .= "</module>\n";
|
|
|
|
|
|
|
|
if (empty ($not_print_flag)) {
|
|
|
|
print $xml_module;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $xml_module;
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# General arguments parser
|
|
|
|
################################################################################
|
|
|
|
sub parse_arguments($) {
|
|
|
|
my $raw = shift;
|
|
|
|
my @args;
|
|
|
|
if (defined($raw)){
|
|
|
|
@args = @{$raw};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
my %data;
|
|
|
|
for (my $i = 0; $i < $#args; $i+=2) {
|
|
|
|
my $key = trim($args[$i]);
|
|
|
|
|
|
|
|
$key =~ s/^-//;
|
|
|
|
$data{$key} = trim($args[$i+1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return \%data;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# General configuration file parser
|
|
|
|
#
|
|
|
|
# log=/PATH/TO/LOG/FILE
|
|
|
|
#
|
|
|
|
################################################################################
|
|
|
|
sub parse_configuration($;$$){
|
|
|
|
my $conf_file = shift;
|
|
|
|
my $separator;
|
|
|
|
$separator = shift or $separator = "=";
|
|
|
|
my $custom_eval = shift;
|
|
|
|
my $_CFILE;
|
|
|
|
|
|
|
|
my $_config;
|
|
|
|
|
|
|
|
if (empty($conf_file)) {
|
|
|
|
return {
|
|
|
|
error => "Configuration file not specified"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !open ($_CFILE,"<", "$conf_file")) {
|
|
|
|
return {
|
|
|
|
error => "Cannot open configuration file"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
while (my $line = <$_CFILE>){
|
|
|
|
if (($line =~ /^ *\r*\n*$/)
|
|
|
|
|| ($line =~ /^#/ )){
|
|
|
|
# skip blank lines and comments
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
my @parsed = split /$separator/, $line, 2;
|
|
|
|
if ($line =~ /^\s*global_alerts/){
|
|
|
|
push (@{$_config->{global_alerts}}, trim($parsed[1]));
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
if (ref ($custom_eval) eq "ARRAY") {
|
|
|
|
my $f = 0;
|
|
|
|
foreach my $item (@{$custom_eval}) {
|
|
|
|
if ($line =~ /$item->{exp}/) {
|
|
|
|
$f = 1;
|
|
|
|
my $aux;
|
|
|
|
eval {
|
|
|
|
$aux = $item->{target}->($item->{exp},$line);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (empty($_config)) {
|
|
|
|
$_config = $aux;
|
|
|
|
}
|
|
|
|
elsif (!empty($aux) && (ref ($aux) eq "HASH")) {
|
|
|
|
$_config = merge_hashes($_config, $aux);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_enabled($f)){
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$_config->{trim($parsed[0])} = trim($parsed[1]);
|
|
|
|
}
|
|
|
|
close ($_CFILE);
|
|
|
|
|
|
|
|
return $_config;
|
|
|
|
}
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# End of import
|
|
|
|
################################################################################
|
|
|
|
|
|
|
|
|
|
|
|
##########################################################################
|
|
|
|
# Show a message to STDERR
|
|
|
|
##########################################################################
|
|
|
|
sub msg {
|
|
|
|
my $msg = shift;
|
|
|
|
print STDERR strftime ("%Y-%m-%d %H:%M:%S", localtime()) . ": $msg\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub get_next {
|
|
|
|
my ($route, $step) = @_;
|
|
|
|
return $route->{'next'}->{$step};
|
|
|
|
}
|
|
|
|
|
|
|
|
##########################################################################
|
|
|
|
# Extract route steps & timming from mtr output
|
|
|
|
##########################################################################
|
|
|
|
sub get_steps {
|
|
|
|
my ($conf) = @_;
|
|
|
|
my $target = $conf->{'t'};
|
|
|
|
|
|
|
|
return [] if empty($target);
|
|
|
|
|
|
|
|
my $mtr_r = "";
|
|
|
|
my $ping_r = "";
|
|
|
|
|
|
|
|
my @route_raw;
|
|
|
|
my @ping_raw;
|
|
|
|
|
|
|
|
|
|
|
|
if ($^O =~ /win/i){
|
|
|
|
$ping_r = trim(`ping -r 9 $target -n 1 | tr "Routea:->-" " " | gawk "/^[0-9\. ]*\$/ {if (\$1 != \\"\\"){ print \$1\";\"0}}"`) unless is_enabled($conf->{'-no-ping'});
|
|
|
|
|
|
|
|
@ping_raw = split /\n/, $ping_r;
|
|
|
|
|
|
|
|
if ($#ping_raw < 0) {
|
|
|
|
$mtr_r = trim(`mtr -n -o A -c $conf->{'c'} -r $target 2>/NUL | gawk "{print \$2";"\$3}"`) unless is_enabled($conf->{'-no-mtr'});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$ping_r = trim(`for x in \$(ping -n -c 1 -R $target 2>/dev/null | tr -s "R:" " " | awk '/^[0-9\. \t]*\$/ {if (\$1 != ""){print \$1}}'); do echo -n \$x";"; ping -c $conf->{'c'} \$x 2>/dev/null | grep rtt |awk '{print \$4}'| cut -f2 -d"/"; done`) unless is_enabled($conf->{'-no-ping'});
|
|
|
|
|
|
|
|
@ping_raw = split /\n/, $ping_r;
|
|
|
|
|
|
|
|
if ($#ping_raw < 0) {
|
|
|
|
$mtr_r = trim(`mtr -n -c $conf->{'c'} -r $target -o A 2>/dev/null | awk '/^[0-9\\|\\-\\. \\t]*\$/ {print \$2\";\"\$3}'`) unless is_enabled($conf->{'-no-mtr'});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@route_raw = split /\n/, $mtr_r;
|
|
|
|
|
|
|
|
my @modules;
|
|
|
|
my @steps;
|
|
|
|
my $route;
|
|
|
|
|
|
|
|
if ($#ping_raw >= 0) {
|
|
|
|
# PING mode
|
|
|
|
|
|
|
|
my $rng = scalar @ping_raw;
|
|
|
|
my $checked;
|
|
|
|
my $j;
|
|
|
|
|
|
|
|
if (is_enabled($conf->{'s'})) {
|
|
|
|
# Symmetric routing
|
|
|
|
|
|
|
|
if ($^O =~ /win/i){
|
|
|
|
$j = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$j = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (my $i=0; $i< ($rng/2); $i++) {
|
|
|
|
my ($step,$time) = split /;/, $ping_raw[$i];
|
|
|
|
my $_r;
|
|
|
|
|
|
|
|
if (defined($checked->{$step})) {
|
|
|
|
$j-=2;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
$checked->{$step} = 1;
|
|
|
|
|
|
|
|
$_r->{'step'} = $step;
|
|
|
|
if ($^O =~ /win/i) {
|
|
|
|
$_r->{'time'} = trim(`ping -n $conf->{'c'} $_r->{'step'} | grep -e "Av" -e "Me" | gawk "{print \$NF}" | tr -d "ms"`) unless ((!defined($_r->{'step'}) || ($_r->{'step'} eq "")));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$_r->{'time'} = $time;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!defined($_r->{'step'}) || ($_r->{'step'} eq ""))) {
|
|
|
|
$_r->{'step'} = "???";
|
|
|
|
}
|
|
|
|
|
|
|
|
$steps[$j] = $_r;
|
|
|
|
$j+=2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($^O =~ /win/i){
|
|
|
|
$j = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$j = 1;
|
|
|
|
}
|
|
|
|
for (my $i=$rng-1; $i>= ($rng/2); $i--) {
|
|
|
|
my ($step,$time) = split /;/, $ping_raw[$i];
|
|
|
|
my $_r;
|
|
|
|
|
|
|
|
if (defined($checked->{$step})) {
|
|
|
|
$j-=2 if $j>2;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
$_r->{'step'} = $step;
|
|
|
|
if ($^O =~ /win/i) {
|
|
|
|
$_r->{'time'} = trim(`ping -n $conf->{'c'} $_r->{'step'} | grep -e "Av" -e "Me" | gawk "{print \$NF}" | tr -d "ms"`) unless ((!defined($_r->{'step'}) || ($_r->{'step'} eq "")));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$_r->{'time'} = $time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((!defined($_r->{'step'}) || ($_r->{'step'} eq ""))) {
|
|
|
|
$_r->{'step'} = "???";
|
|
|
|
}
|
|
|
|
|
|
|
|
$steps[$j] = $_r;
|
|
|
|
$j+=2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
# Asymmetric routing
|
|
|
|
|
|
|
|
for (my $i=0; $i< $rng; $i++) {
|
|
|
|
my ($step,$time) = split /;/, $ping_raw[$i];
|
|
|
|
my $_r;
|
|
|
|
|
|
|
|
if (defined($checked->{$step})) {
|
|
|
|
# target reached
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
$checked->{$step} = 1;
|
|
|
|
|
|
|
|
$_r->{'step'} = $step;
|
|
|
|
if ($^O =~ /win/i) {
|
|
|
|
$_r->{'time'} = trim(`ping -n $conf->{'c'} $_r->{'step'} | grep -e "Av" -e "Me" | gawk "{print \$NF}" | tr -d "ms"`) unless ((!defined($_r->{'step'}) || ($_r->{'step'} eq "")));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$_r->{'time'} = $time;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!defined($_r->{'step'}) || ($_r->{'step'} eq ""))) {
|
|
|
|
$_r->{'step'} = "???";
|
|
|
|
}
|
|
|
|
|
|
|
|
$steps[$i] = $_r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my $__origin;
|
|
|
|
|
|
|
|
if ($^O !~ /win/i){
|
|
|
|
$__origin = shift @steps;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $gw;
|
|
|
|
|
|
|
|
if ($^O =~ /win/i) {
|
|
|
|
($gw->{'step'},$__origin->{'step'}) = split /;/, trim(`route print -4 | gawk "BEGIN {min=10000} /^\\ *0.0.0.0/ {met=\$NF;if(met<min){min=met; gw=\$3\\\";\\\"\$4}} END {print gw}"`);
|
|
|
|
$gw->{'time'} = trim(`ping -n $conf->{'c'} $gw->{'step'} 2>/NUL | grep ms | grep -v TTL | gawk "{print \$NF}" | tr -d "ms"`);
|
|
|
|
$__origin->{'time'} = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$gw->{'step'} = trim(`route -n | awk 'BEGIN {min=100000} /^0/ {met=\$5; if(min>met){gw=\$2;min=met} } END { print gw}'`);
|
|
|
|
$gw->{'time'} = trim(`ping -c $conf->{'c'} $gw->{'step'} 2>/dev/null | grep rtt |awk '{print \$4}'| cut -f2 -d"/"`);
|
|
|
|
}
|
|
|
|
|
|
|
|
unshift (@steps,($__origin,$gw));
|
|
|
|
|
|
|
|
my $unknown_count = 0;
|
|
|
|
my $previous = undef;
|
|
|
|
|
|
|
|
for(my $i=0; $i <= $#steps; $i++) {
|
|
|
|
my $host = $steps[$i]->{'step'};
|
|
|
|
my $time = to_number($steps[$i]->{'time'});
|
|
|
|
my $preffix = 'RouteStep_';
|
|
|
|
my $desc = '';
|
|
|
|
|
|
|
|
if (!defined($time)) {
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($host eq "???") {
|
|
|
|
$host = "Hidden_" . (++$unknown_count);
|
|
|
|
}
|
|
|
|
if (($i == $#steps) && in_array($conf->{'target_ip'},$host)) {
|
|
|
|
$preffix = 'RouteStepTarget_';
|
|
|
|
}
|
|
|
|
elsif($i == $#steps) {
|
|
|
|
$desc = 'Step unreachable';
|
|
|
|
}
|
|
|
|
|
2020-03-24 09:56:43 +01:00
|
|
|
my $name = $preffix . $host;
|
|
|
|
|
|
|
|
if (defined $previous && $name eq $previous) {
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
2017-10-31 14:01:56 +01:00
|
|
|
push @modules, {
|
|
|
|
name => $preffix . $host,
|
|
|
|
type => "generic_data",
|
|
|
|
value => $time,
|
|
|
|
unit => 'ms',
|
|
|
|
desc => $desc,
|
|
|
|
module_parent => $previous,
|
|
|
|
parent_unlink => (empty($previous)?'1':undef)
|
|
|
|
};
|
|
|
|
|
|
|
|
$previous = $preffix . $host;
|
|
|
|
}
|
|
|
|
|
|
|
|
return \@modules;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
# MTR mode
|
|
|
|
|
|
|
|
if ($#route_raw < 0) {
|
|
|
|
# Empty output
|
|
|
|
msg("Failed to analyze [$target]");
|
|
|
|
return [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (my $i=0; $i <= $#route_raw; $i++) {
|
|
|
|
my $line = $route_raw[$i];
|
|
|
|
if (trim($line) =~ /(.*?);(.*)/) {
|
|
|
|
my $host = $1;
|
|
|
|
my $time = to_number($2);
|
|
|
|
my $preffix = 'RouteStep_';
|
|
|
|
my $desc = '';
|
|
|
|
my $item;
|
|
|
|
my $_r;
|
|
|
|
|
|
|
|
if (!defined($time)) {
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
$_r->{'step'} = $host;
|
|
|
|
$_r->{'time'} = $time;
|
|
|
|
|
|
|
|
push @steps, $_r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my $__origin;
|
|
|
|
|
|
|
|
if ($^O !~ /win/i){
|
|
|
|
$__origin = shift @steps;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $gw;
|
|
|
|
|
|
|
|
if ($^O =~ /win/i) {
|
|
|
|
($gw->{'step'},$__origin->{'step'}) = split /;/, trim(`route print -4 | gawk "BEGIN {min=10000} /^\\ *0.0.0.0/ {met=\$NF;if(met<min){min=met; gw=\$3\\\";\\\"\$4}} END {print gw}"`);
|
|
|
|
$gw->{'time'} = trim(`ping -n $conf->{'c'} $gw->{'step'} 2>/NUL | grep ms | grep -v TTL | gawk "{print \$NF}" | tr -d "ms"`);
|
|
|
|
$__origin->{'time'} = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$gw->{'step'} = trim(`route -n | awk 'BEGIN {min=100000} /^0/ {met=\$5; if(min>met){gw=\$2;min=met} } END { print gw}'`);
|
|
|
|
$gw->{'time'} = trim(`ping -c $conf->{'c'} $gw->{'step'} 2>/dev/null | grep rtt |awk '{print \$4}'| cut -f2 -d"/"`);
|
|
|
|
|
|
|
|
my $__xorigin = trim(`ip a show dev \`route -n | awk 'BEGIN {min=100000} /^0/ {met=\$5; if(min>met){iface=\$NF;min=met} } END { print iface}'\` | grep -w inet | awk '{print \$2}' | cut -d'/' -f1`);
|
|
|
|
|
|
|
|
if ($__xorigin ne $__origin->{'step'}) {
|
|
|
|
unshift(@steps, $__origin);
|
|
|
|
$__origin = {};
|
|
|
|
$__origin->{'step'} = $__xorigin;
|
|
|
|
$__origin->{'time'} = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unshift (@steps,($__origin,$gw));
|
|
|
|
|
|
|
|
my $unknown_count = 0;
|
|
|
|
my $previous = undef;
|
|
|
|
|
|
|
|
for(my $i=0; $i <= $#steps; $i++) {
|
|
|
|
my $host = $steps[$i]->{'step'};
|
|
|
|
my $time = to_number($steps[$i]->{'time'});
|
|
|
|
my $preffix = 'RouteStep_';
|
|
|
|
my $desc = '';
|
|
|
|
|
|
|
|
if (!defined($time)) {
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($host eq "???") {
|
|
|
|
$host = "Hidden_" . (++$unknown_count);
|
|
|
|
}
|
|
|
|
if (($i == $#steps) && in_array($conf->{'target_ip'},$host)) {
|
|
|
|
$preffix = 'RouteStepTarget_';
|
|
|
|
}
|
|
|
|
elsif($i == $#steps) {
|
|
|
|
$desc = 'Step unreachable';
|
|
|
|
}
|
|
|
|
|
2020-03-24 09:56:43 +01:00
|
|
|
my $name = $preffix . $host;
|
|
|
|
|
|
|
|
if (defined $previous && $name eq $previous) {
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
2017-10-31 14:01:56 +01:00
|
|
|
push @modules, {
|
|
|
|
name => $preffix . $host,
|
|
|
|
type => "generic_data",
|
|
|
|
value => $time,
|
|
|
|
unit => 'ms',
|
|
|
|
desc => $desc,
|
|
|
|
module_parent => $previous,
|
|
|
|
parent_unlink => (empty($previous)?'1':undef)
|
|
|
|
};
|
|
|
|
|
|
|
|
$previous = $preffix . $host;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return \@modules;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##########################################################################
|
|
|
|
##########################################################################
|
|
|
|
# MAIN
|
|
|
|
##########################################################################
|
|
|
|
##########################################################################
|
|
|
|
|
|
|
|
if ($#ARGV < 0) {
|
|
|
|
print STDERR $HELP;
|
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $conf;
|
|
|
|
my $file_conf = {};
|
|
|
|
my $args_conf = {};
|
|
|
|
|
|
|
|
if (-e $ARGV[0]) {
|
|
|
|
$file_conf = parse_configuration($ARGV[0]);
|
|
|
|
shift @ARGV;
|
|
|
|
}
|
|
|
|
|
|
|
|
$args_conf = parse_arguments(\@ARGV);
|
|
|
|
$conf = merge_hashes($file_conf,$args_conf);
|
|
|
|
|
|
|
|
if (!defined $conf->{'t'}) {
|
|
|
|
print STDERR $HELP;
|
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
my @targets = gethostbyname($conf->{'t'});
|
|
|
|
@targets = map { inet_ntoa($_) } @targets[4 .. $#targets];
|
|
|
|
if (empty(\@targets)) {
|
|
|
|
print STDERR "Cannot resolve $conf->{'t'} \n";
|
|
|
|
exit 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$conf->{'target_ip'} = \@targets;
|
|
|
|
|
|
|
|
$conf->{'c'} = 4 unless looks_like_number($conf->{'c'});
|
|
|
|
$conf->{'s'} = 1 unless looks_like_number($conf->{'s'});
|
|
|
|
|
|
|
|
|
|
|
|
my $results = get_steps($conf);
|
|
|
|
|
|
|
|
foreach (@{$results}) {
|
|
|
|
print_module($conf, $_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|