There are (at least) two traceroute.monitors. Mine is attached, Jim has 
another one.

Jon

On Sat, 8 Mar 2003, Jonathan B. Bayer wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> Hello mon,
> 
> At a talk that Jim gave last month, he said that one of the included
> monitors was a traceroute monitor, which would keep track of when a
> route would change.
> 
> I haven't been able to find it, can someone point me in the right
> direction?
> 
> 
> Thanks.
> 
> 
> 
> JBB
> - ---
>  Jonathan B. Bayer                          mailto:[EMAIL PROTECTED]
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.2.0 (MingW32)
> 
> iD8DBQE+aopi8frsPBZFgFcRApoaAKCc8Le1F4MuWGIjav1XnCl4TvlNugCgunsQ
> ziKqrUGvRLlKH6w3WyXukEY=
> =B5HD
> -----END PGP SIGNATURE-----
> 
> _______________________________________________
> mon mailing list
> [EMAIL PROTECTED]
> http://linux.kernel.org/mailman/listinfo/mon
> 
#!/usr/bin/perl
#
# mon monitor to watch for route changes
#

# There is currently a hardcoded path to the traceroute binary, see $TRACEROUTE
# but it can be overriden in the config file.

#
# Jon Meek - 31-May-1999 (original code)
#

#
# Jon Meek
# Lawrenceville, NJ
# [EMAIL PROTECTED]
#
# $Id: traceroute.monitor,v 1.3 2002/10/12 04:06:44 meekj Exp meekj $
#
#    Copyright (C) 2001, Jon Meek
#
#    This program 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 2 of the License, or
#    (at your option) any later version.
#
#    This program 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 this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

=head1 NAME

B<traceroute.monitor> - Route monitor for mon.

=head1 DESCRIPTION

Monitor routes from monitor machine to a remote system using traceroute. Alarm and log
when changes are detected.

=head1 SYNOPSIS

B<traceroute.monitor -d -t 20 -c /path/to/traceroute.cf -l 
/usr/local/mon/logs/routes_YYYYMM.log>

The logfile template is usually specified in the configuration file.

=head1 OPTIONS

=over 5

=item B<-d> Debug/Test

=item B<-c config.cfg> Configuration file for this monitor, see example below

=item B<-t timeout> Timeout for traceroute to run in seconds default is 20s

=item B<-l log_file_template> /path/to/logs/internet_web_YYYYMM.log
Current year & month are substituted for YYYYMM, that is the only
possible template at this time.

=back

=head1 MON CONFIGURATION EXAMPLE

 hostgroup route1 rt-tb-paris-26 rt-tb-london-18 rt-tta-pr01r00-4
                  rt-cam-cer001-5 rt-tta-pn01r00-4

 watch route1
        service traceroute
        interval 15m
        monitor traceroute.monitor -c /usr/local/mon/traceroute.cf
        period wd {Sun-Sat}
            alert mail.alert meekj
            alertevery 1h summary

=head1 CONFIGURATION FILE EXAMPLE

 # tracreoute.monitor Config File
 RouteLogFile /usr/local/mon/logs/routes_YYYYMM.log
 RouterList /usr/local/mon/rt.list
 Traceroute /usr/sbin/traceroute
 StateDir /usr/local/mon/state.d
 EquivIP 10.22.4.254 10.22.5.254 10.22.6.254
 EquivIP 10.28.4.254 10.28.5.254 10.28.6.254

Lines with '#' in the first column are ignored.

RouteLogFile - A new log file will be created each month in the above
example the files will be of the form routes_199810.log The YYYYMM
format is the only date string possible in the current version The logs contain
time stamped route changes.

RouterList - Optional IP address to router name translation in
/etc/hosts format (IP_address router_bame). Supplying this list will
provide considerably more meaningful alarm messages, especially if the
router names contain geographical information. Without this list the
extended alarm is just a list of interface IP addresses.

Traceroute - Overrides the default of /usr/sbin/traceroute

StateDir - Overrides the default path of the mon environment variable MON_STATEDIR.
Files named F<lastroute.router_name> contain the last observed route.

EquivIP - A space separated list of IP addresses that should be
considered equivalent for the purposes of determining route
changes. Likely used where there are secondary addresses on router or
switch interfaces.

=head1 BUGS

There probably are some.

=head1 AUTHOR

Jon Meek, [EMAIL PROTECTED]

=head1 SEE ALSO

F<traceroute.anal> - A CGI script to display route change information.

=cut

use Getopt::Std;
use POSIX qw(:signal_h WNOHANG);
use POSIX qw(strftime);

getopts ("vdt:l:c:");

# -l file  Log file name with optional YYYYMM part that will be transformed to current 
month

$TimeOut = $opt_t || 20;     # Set default timeout in seconds

# Usual Linux config
$TRACEROUTE = '/usr/sbin/traceroute';
#$STATE_DIR = '/usr/local/mon/state.d';

if (defined $ENV{MON_STATEDIR}) { # Are we running under mon?
  $STATE_DIR = $ENV{MON_STATEDIR};
  $RunningUnderMon = 1;
} else {
  $RunningUnderMon = 0;
}

if ($opt_c) { # Read configuration file

  $ConfigFile = $opt_c;

  if (open(C, $ConfigFile)) {
    while ($in = <C>) {
      last if ($in =~ /^Exit/i);
      next if ($in =~ /^\#/);    # Comments
      chomp $in;

      if ($in =~ /^RouteLogFile/i) {
        ($tag, $LogFile) = split(' ', $in, 2);
        next;
      }

      if ($in =~ /^Traceroute/i) {
        ($tag, $TRACEROUTE) = split(' ', $in, 2);
        next;
      }

      if ($in =~ /^RouterList/i) {
        ($tag, $RouterListFile) = split(' ', $in, 2);
        next;
      }

      if ($in =~ /^StateDir/i) { # If the mon environment variable needs to be 
overriden
        ($tag, $STATE_DIR) = split(' ', $in, 2);
        next;
      }

      if ($in =~ /^EquivIP/i) {
        ($tag, $ips) = split(' ', $in, 2);
        (@ip_list) = split(' ', $ips);
#       $ip_string = " $ips "; # Each IP is surrounded by whitespace
        foreach $ip (@ip_list) {
          $EquivIP{$ip} = [ @ip_list ];
        }
        next;
      }

    }
  } else {
    print "traceroute.monitor: Couldn't open $ConfigFile configuration file\n";
    exit 1;

  }
}

if ($opt_l) { # Command line overrides config file
  $LogFile = $opt_l;
}

if ((defined $RouterListFile) && $opt_v) { # Read the router names now
  open(F, $RouterListFile);
  while ($in = <F>) {
    chomp $in;
    ($ip, $name) = split(' ', $in, 2);
    $RouterByIP{$ip} = $name;
  }
  close F;
}

@Failures = ();
@Hosts = @ARGV; # Host names are left on the command line after Getopt


if ($TestOnly) {
  foreach $h (@Hosts) {
    print "Host: $h\n";

    if (defined $EquivIP{$h}) {
      print "  Has equivalent IP\n";
    }
  }

  $ip1 = $Hosts[0];
  $ip2 = $Hosts[1];

  $equiv_check = grep /^$ip2$/, @{ $EquivIP{$ip1} };
  print "$ip1  $ip2  $equiv_check\n";

  @equiv_arr = grep /^$ip2$/, @{ $EquivIP{$ip1} };
  print "$ip1  $ip2  @equiv_arr\n";
  foreach $ip (@equiv_arr) {
    print "  $ip\n";
  }

  exit;
}

#
# Reap children to avoid defunct processes / zombies
# See "Network Programming with Perl" by Lincoln Stein
#
sub Reaper {
  while ((my $child_pid = waitpid(-1, WNOHANG)) > 0) {
    print "Reaped child: $child_pid\n" if $opt_d;
  }
}

$SIG{CHLD} = \&Reaper;

#
# Run traceroute for each destination, collect route
#
foreach $TargetHost (@Hosts) {

  $TimeOfDay = time;
  $FmtTimeOfDay = strftime("%A %d-%b-%Y  %H:%M:%S %Z", localtime($TimeOfDay));

  $route = '';

  eval {
    $SIG{ALRM} = sub {die "timeout" };
    print "Setting timeout to $TimeOut s\n" if $opt_d;
    alarm($TimeOut);

    eval {

      # discard STDERR data from traceroute
      $pid = open(TR, "$TRACEROUTE -n $TargetHost 2>/dev/null |")
        || die "Couldn't run traceroute\n";
      print "$FmtTimeOfDay Traceroute to $TargetHost  pid: $pid\n" if $opt_d;
      while ($in = <TR>) {
        print $in if $opt_d;
        if ($in =~ /\*\s+\*\s+\*/) { # Get * * * then give up
          $route .= '*';
          kill 13, $pid; # 13 = PIPE, prevents Broken Pipe Error, at least on Solaris
          last;
        }

        # We will only pick up the first IP address listed on a line for now
        # Get IP address into $1
        $in =~ /\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+/;
        $ThisHopIP = $1;
        $route .= $ThisHopIP . '-'; # Build route string

        if ($opt_v) {
          chomp $in;
          print "$in $RouterByIP{$ThisHopIP}\n";
        }
      }
      alarm(0);
    };
    alarm(0);
  };

  if ($@) {                     # Check for SIG
    if ($@ =~ /timeout/) {      # It was a traceroute timeout
      print "Traceroute timeout\n" if $opt_d;
      $route .= '*';
      kill 13, $pid;            # 13 = PIPE, prevents Broken Pipe Error, at least on 
Solaris
    } else {
      print "Exiting due to some other alarm\n" if $opt_d;
      die;                      # Some other problem
    }
  }

  close TR;
  $route =~ s/\-$//;            # Remove trailing '-' from route string
  $ResultString{$TargetHost} = "$TimeOfDay $TargetHost $route";

}

$FmtTimeOfDay = strftime("%A %d-%b-%Y  %H:%M:%S %Z", localtime(time));
print "$FmtTimeOfDay finish $TargetHost  pid: $pid\n\n" if $opt_d;

#
# Compare just measured routes with previous route stored in state file
#  or just make the state file if this is the first time for a destination
#

# TODO: if new destination (no state file), then log route to log file
#       add IP to name translation for mail messages

foreach $k (sort keys %ResultString) {
  print "$ResultString{$k}\n" if $opt_d;
  $state_file = "$STATE_DIR/lastroute.$k";

  if (-e $state_file) { # We have checked this route before, compare current
    ($t2, $host2, $current_route) = split(' ', $ResultString{$k});
    open(S, $state_file) || warn "Can't open $state_file for reading\n";
    $in = <S>;
    chomp $in;
    ($t1, $host1, $prev_route) = split(' ', $in);
    close S;

    if ($opt_d) {
      print "Previous route for $host1 -$prev_route-\n";
      print "Current  route for $host2 -$current_route-\n";
    }

    if (&RouteChanged($current_route, $prev_route)) { # Route changed, alarm and record
      if ($RunningUnderMon) { # Write results
        open(S, ">$state_file") || warn "Can't open $state_file for writing\n";
        print S "$ResultString{$k}\n";
        close S;
      }
      push (@Failures, $k);
      print "  Alarm\n" if $opt_d;
    }

  } else { # The state file does not yet exist, so make it

    if ($RunningUnderMon) { # Write results
      open(S, ">$state_file") || warn "Can't open $state_file for writing\n";
      print S "$ResultString{$k}\n";
      close S;
    }
    push (@Failures, $k); # Call it a failure so it will be logged and notification 
will be sent
    print "  New route added to check: $k\n" if $opt_d;
  }

}

# Write results to logfile, if -l

#if ($RunningUnderMon && $LogFile) {
if ($LogFile) {

  ($sec,$min,$hour,$mday,$Month,$Year,$wday,$yday,$isdst) =
    localtime($TimeOfDay);
  $Month++; $Year += 1900;
  $YYYYMM = sprintf('%04d%02d', $Year, $Month);
  $LogFile =~ s/YYYYMM/$YYYYMM/; # Fill in current year and month

  if (-e $LogFile) { # Check for existing log file
    $NewLogFile = 0;
  } else {
    $NewLogFile = 1;
  }

  if ($NewLogFile || (@Failures > 0)) { # Only log if new log file, or if route changes
    open(LOG, ">>$LogFile") || warn "$0 Can't open logfile: $LogFile\n";

    if ($NewLogFile) { # New log file, record all routes being tested
      foreach $host (sort keys %ResultString) {
        print LOG "$ResultString{$host}\n";
      }
    }

    if (($NewLogFile == 0) && (@Failures > 0)) { # Just record changes
      foreach $host (sort @Failures) {
        print LOG "$ResultString{$host}\n";
      }
    }

    close LOG;
  }
}

if (@Failures == 0) { # Exit if there were no failures
    exit 0;
}


if (defined $RouterListFile) { # Read the router names if we have a failure
  open(F, $RouterListFile);
  while ($in = <F>) {
    chomp $in;
    ($ip, $name) = split(' ', $in, 2);
    $RouterByIP{$ip} = $name;
  }
  close F;
}

@SortedFailures = sort @Failures; # To make summary mode in mon happy

print "@SortedFailures\n";

foreach $host (@SortedFailures) {
  print "$host:\n";
  ($t, $target, $rest) = split(' ', $ResultString{$host});
  (@hop_ips) = split(/\-/, $rest);
  foreach $hop_ip (@hop_ips) {
    printf " %-15s %s\n", $hop_ip, $RouterByIP{$hop_ip};
  }
  print "\n";
}

exit 1;


sub RouteChanged {
  my ($current_route, $prev_route) = @_;
  my(@current_ips, @prev_ips);

  if ($current_route eq $prev_route) { # Simple case, same string, no change
    return 0;
  }

  (@current_ips) = split(/\-/, $current_route);
  (@prev_ips) = split(/\-/, $prev_route);

  if ($#current_ips != $#prev_ips) { # Another simple case, different number of hops
    return 1; # Fail
  }

  for ($i = 0; $i <= $#current_ips; $i++) {
    $ip1 = $current_ips[$i];
    $ip2 = $prev_ips[$i];
    next if ($ip1 eq $ip2);
    $equiv_check = grep /^$ip2$/, @{ $EquivIP{$ip1} };
    if ($equiv_check == 0) { # Not same, or equivalent, route different, fail
      return 1;
    }
    print "$i $ip1  $ip2  $equiv_check\n" if $opt_d;
  }
  return 0; # Good, no route change
}

Reply via email to