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
}