Network Link monitor

I came up with this simple script to monitor my WAN links, and to notify me when a link goes down, and when it is available again. It does this by connecting to a port of a remote router. If the router responds, the link is alive. This script can be extended to support servers as well.

The script was tested on AIX 4.3.3 and ActiveState Perl for Windows on Windows 2000 server.

The data file

The data file is a text file, containing site information, and an IP address. They are seperated with the pipe symbol.

SITE-A|192.168.1.1
SITE-B|10.1.1.1

The config file

data=data.txt
port=23
sleep=120
systemlog=system.log
historylog=history.log
statustxt=status.txt
statushtml=c:\status.htm
title=My own network monitor
external=mailit.bat

The ini file is straight forward: which data file to use, which port to connect to, how long to sleep between intervals, the system log file name, the history log file name, the status log file name, the output HTML file name, a title, and the external application to call when the status changes.

One thing worth noting, is that all files will be stored in the local directory, all except the statushtml file, which have to be specified absolutely.

The external application will be called with the site, IP and status as parameters. You may use these in your email applications, etc.

Run the script as a daemon on Unix, or load it as a service with FireDaemon on Windows.

The code

#!/usr/bin/perl use IO::Socket; $slash = &getslash; $curdir = substr($0,0,rindex($0,$slash)+1); if($curdir eq "") { $curdir = ".$slash"; } $ini = "siteping.ini"; open(INI,"$curdir$ini"); foreach $i (<INI>) { chomp($i); ($v,$a) = split(/\=/,$i); $inih{$v} = $a; } close INI; #====================================================== $data = $inih{data}; $port = $inih{port}; $sleep = $inih{sleep}; $systemlog = $inih{systemlog}; $historylog = $inih{historylog}; $statustxt = $inih{statustxt}; $statushtml = $inih{statushtml}; $title = $inih{title}; $external = $inih{external}; #====================================================== open(DAT,"$curdir$data"); foreach $i (<DAT>) { chomp($i); ($site,$ip) = split(/\|/,$i); $hashsite{$site} = $ip; $currentstatus{$site} = "Ok!"; } close DAT; while(1) { unlink "$curdir$systemlog"; &log("Starting scanning","$systemlog"); foreach $site (sort keys %hashsite) { $host = $hashsite{$site}; $alive = &check_port($host,$port); if($alive == 1) { $status = "Ok!"; } else { $status = "FAILED!!"; $error = 1; } &log("$site = $status","$systemlog"); if($currentstatus{$site} ne $status) { &log("$site => $status","$historylog"); `$external $site $host $status`; } $currentstatus{$site} = $status; } #write realtime status files #============================ $time = localtime(time); open(STATUS,">$curdir$statustxt"); print STATUS "$time\n"; foreach $site (sort keys %hashsite) { $status = $currentstatus{$site}; print STATUS "$site=$status\n"; } close STATUS; #write html file from status file #=================================== open(STATUS,"$curdir$statustxt"); open(HTML,">$statushtml"); print HTML "<html><title>$title</title><h1>$title</h1><meta HTTP-E +QUIV=\"Refresh\" CONTENT=\"$sleep;\">\n"; print HTML &style; print HTML <<HTML; <table border=1> HTML ; $date = <STATUS>; foreach $l (<STATUS>) { chomp($l); ($site,$status) = split(/\=/,$l); if($status eq "Ok!") { $st = &htmlcell($status,"GREEN"); } else { $st = &htmlcell($status,"RED"); } print HTML "<tr><th>$site</th>$st</tr>\n"; } print HTML "</table>\n"; $d = &timeformat(time,"dd MMM YYYY hh:mm:ss"); print HTML "<hr><i>Last updated on $d, and will refresh in $sl +eep seconds</i></html>"; close STATUS; close HTML; &log("Sleeping for $sleep seconds","$systemlog"); sleep($sleep); } #===================================================================== +============ sub log { $txt = $_[0]; $file = $_[1]; $time = localtime(time); open(FOO,">>$curdir$file"); print FOO "$time $txt\n"; close FOO; } ################################################# # Get name or IP, do lookup ################################################# sub name { my ($host) = @_; ($name,$alias,$addrtype,$length,$new_addr) = gethostbyaddr(inet_aton($host),AF_INET); $ipaddr = inet_ntoa(scalar($new_addr)); return $ipaddr; } ################################################# # Check to see if a port is open ################################################# sub check_port { my ($host,$port) = @_; $remote = IO::Socket::INET -> new ( Proto => "tcp", PeerAddr => $host, PeerPort => $port ) ; if ($remote) { close $remote; return 1; } else { return 0; } } sub cl2 { local $cl_var = $ARGV[$_[0]]; local $cl_default = $_[1]; if($cl_var eq "") { $cl_var = $cl_default; } return $cl_var; } sub cl { ################################################################## +################### # CL ################################################################## +################### # Input 0 : Command line variable (0,1,2,3, etc) # Input 1 : Default text to display (if the command line var is bl +ank) # Input 2 : Default answer to the question # # Similar to the ASK procedure, except this proc reads from the co +mmand line, eg. # # $myvar = &cl(0,"What is your name","Bob"); # ################################################################## +################### local $cl_var = $ARGV[$_[0]]; local $cl_txt = $_[1]; local $cl_default = $_[2]; if($cl_var eq "") { $cl_var = &ask($cl_txt,$cl_default); } return $cl_var; } sub ask { ################################################################## +################### # ASK ################################################################## +################### # Input 0 : Message to display # Input 1 : Default answer # # This procedure works a lot like the input keyword in BASIC. You + can specify # a default answer to the question, eg. # # $myvar = &ask("What is your name","Bob"); # ################################################################## +################### local $ask_msg = $_[0]; local $ask_default = $_[1]; if($ask_default eq "") { $ask_ifdefault = ""; } else { $ask_ifdefault = " [$ask_default] "; } local $ask_val = ""; while($ask_val eq "") { print "$ask_msg$ask_ifdefault --> "; $ask_val = <STDIN>; chomp($ask_val); if($ask_val eq "") { $ask_val = $ask_default; } } return $ask_val; } sub getslash { $r = $ENV{PATH}; if(index($r,"\\") != -1) { return "\\"; } if(index($r,"/") != -1) { return "/"; } return "UNKNOWN"; } sub style { return <<END; <style> .parent {font-family: Verdana; font-size: 8pt; margin-top: 5; text-indent: 0; margin-left: 0; cursor: hand;} .child {font-family: Verdana; font-size: 8pt; margin-left: 10; margin-bottom:20; font-weight: normal;} .image {} BODY { BACKGROUND-COLOR : white; COLOR : black; FONT-FAMILY : verdana, arial; FONT-SIZE : 8pt; } H1 { COLOR : #000363; FONT-SIZE : 14pt; font-family : Verdana; border-style : solid; border-color : #000363; border-width : 0pt; border-bottom-width : 1pt; font-weight : bold; } H2 { COLOR : #000363; FONT-SIZE : 13pt; font-weight : bold; font-family : Verdana, arial; } H5 { COLOR : #000363; FONT-SIZE : 9pt; font-family : Verdana, arial; } H6 { COLOR : #000363; FONT-SIZE : 8pt; font-family : Verdana, arial; } P { COLOR : black; FONT-FAMILY : verdana, arial; FONT-SIZE : 8pt; } TD { COLOR : black; FONT-FAMILY : verdana, arial; FONT-SIZE : 8pt; } TH { BACKGROUND-COLOR : #000363; COLOR : white; FONT-FAMILY : verdana, arial; FONT-SIZE : 8pt; FONT-WEIGHT : bold; } H3 { font-size : 11pt; font-family : Verdana, arial; color : #000363; font-weight : bold; } H4 { font-size : 10pt; font-family : Verdana, arial; color : #000363; } A:hover { background-color : #00FFFF; color : #000000; } </style> END ; } #================================================================= sub timeformat { $tf_timesent = $_[0]; $tf_format = $_[1]; $timeformat_longmonth[1] = "January"; $timeformat_longmonth[2] = "February"; $timeformat_longmonth[3] = "March"; $timeformat_longmonth[4] = "April"; $timeformat_longmonth[5] = "May"; $timeformat_longmonth[6] = "June"; $timeformat_longmonth[7] = "July"; $timeformat_longmonth[8] = "August"; $timeformat_longmonth[9] = "September"; $timeformat_longmonth[10] = "October"; $timeformat_longmonth[11] = "November"; $timeformat_longmonth[12] = "December"; $timeformat_shortmonth[1] = "Jan"; $timeformat_shortmonth[2] = "Feb"; $timeformat_shortmonth[3] = "Mar"; $timeformat_shortmonth[4] = "Apr"; $timeformat_shortmonth[5] = "May"; $timeformat_shortmonth[6] = "Jun"; $timeformat_shortmonth[7] = "Jul"; $timeformat_shortmonth[8] = "Aug"; $timeformat_shortmonth[9] = "Sep"; $timeformat_shortmonth[10] = "Oct"; $timeformat_shortmonth[11] = "Nov"; $timeformat_shortmonth[12] = "Dec"; #FORMAT KEYS # hh -- 24 hour, no leading zero -- Done # HH -- 24 hour, leading zero -- Done # mm -- minute, leading zero -- Done # ss -- seconds, leading zero -- Done # DD -- Day (leading zero) -- Done # dd -- Day (no leading zero) -- Done # m -- Month (no leading zero) -- Done # MM -- Month (01 - Jan, 12 - Dec) -- Done # MMM -- Month (Jan, Feb) -- Done # MMMM -- Month (January, February) -- Done # YY -- Year (Leading zero, eg. 02) -- Done # YYYY -- Year (full year, eg 2002) -- Done ($tf_sec,$tf_min,$tf_hour,$tf_mday,$tf_mon,$tf_year,$tf_wday,$ +tf_yday,$tf_isdst) = localtime($tf_timesent); $tf_HH = &timeformat_leadingzero($tf_hour); $tf_hh = $tf_hour; $tf_mm = &timeformat_leadingzero($tf_min); $tf_ss = &timeformat_leadingzero($tf_sec); $tf_DD = &timeformat_leadingzero($tf_mday); $tf_dd = $tf_mday; $tf_m = $tf_mon + 1; $tf_MM = &timeformat_leadingzero($tf_m); $tf_MMM = $timeformat_shortmonth[$tf_m]; $tf_MMMM = $timeformat_longmonth[$tf_m]; $tf_YYYY = $tf_year + 1900; $tf_YY = substr($tf_YYYY,2,2); $tf_format =~ s/HH/$tf_HH/g; $tf_format =~ s/hh/$tf_hh/g; $tf_format =~ s/mm/$tf_mm/g; $tf_format =~ s/ss/$tf_ss/g; $tf_format =~ s/DD/$tf_DD/g; $tf_format =~ s/dd/$tf_dd/g; $tf_format =~ s/MMMM/$tf_MMMM/g; $tf_format =~ s/MMM/$tf_MMM/g; $tf_format =~ s/MM/$tf_MM/g; $tf_format =~ s/m/$tf_m/g; $tf_format =~ s/YYYY/$tf_YYYY/g; $tf_format =~ s/YY/$tf_YY/g; return $tf_format; } sub timeformat_leadingzero { $tflz_var = $_[0]; if(length($tflz_var) == 1) { $tflz_var = "0$tflz_var"; } return $tflz_var; } sub htmlcell { $txt = @_[0]; $col = $_[1]; if($col eq "RED") { return "<td bgcolor=\"#FF0000\">$txt</td>\n"; } if($col eq "GREEN") { return "<td bgcolor=\"#00FF00\">$txt</td>\n"; } }

Edited by footpad, ~ Tue Jul 30 11:07:47 2002 (UTC): Added <READMORE> tag, per Consideration.

Replies are listed 'Best First'.
Re: Simple Network Availability Monitor
by Tomte (Priest) on Jul 30, 2002 at 12:18 UTC
    neat & nifty :-)
    FWIW: I'd put the initialization stuff in a function and put a HUP signal handler in place to reread the ini on HUP.
    Could that way, and with a bit of rc-script theft and rewriting, be easily used as a surveillance-daemon on a security-auditors machine.
    Well, just my 0.02EUR

    regards,
    tomte

    UPDATE: just in case you think I'm stupid: a fullfledged IDS isn't feasible for all of us for several reasons, and therefore a script like this, maybe extended to check vital services (like http), is IMHO a great help if you're an underpaid coder with the additional burden of security/availabilty checking....;-) Update_II: s/your/you're/
Re: Simple Network Availability Monitor
by cybear (Monk) on Aug 22, 2002 at 14:02 UTC
    I have a similar script that I posted, in a less refined form, some time ago.
    The script that I wrote tests connectivity to a host outside of your
    companies firewall, using an FTP proxy server to make the connection.

    There are still imporvements that could be made, but this is not bad.

    #!/bin/perl -w use Net::Telnet(); use Net::FTP(); use strict; unless (@ARGV) { print "\n\n\tUsage: pingtest.pl <name of calling script>\n\n"; exit(0); } chomp (my $CALLING_SCRIPT = "$ARGV[0]"); ###################################################################### +######### ### Note: this script only tests for hosts external to the Boeing netw +ork ### ### that are reached through a firewall + ### ###################################################################### +######### my $FIREWALL; my @FIREWALLS = qw (gate.somecompany.com othergate.somecompany.com); my @REACHABLE_FIREWALLS; my $REACHABLE_FIREWALL; my @USEABLE_FIREWALLS; my $USEABLE_FIREWALL; my @TOUCHABLE_FIREWALLS; my $TOUCHABLE_FIREWALL; my $FTP_SESSION; my $SUCCESSFUL_PROXY_CONNECTION = "none"; my $EXTERNAL_HOST = "192.xxx.185.xxx"; my $KEY = "default"; my @MESSAGE; umask 027; ###################################################################### +######### ### Beginning of main + ### ###################################################################### +######### @REACHABLE_FIREWALLS = pingFireWall(@FIREWALLS); @USEABLE_FIREWALLS = pingExternalHost(@REACHABLE_FIREWALLS); testFtpConnectivity(@USEABLE_FIREWALLS); print "$KEY $SUCCESSFUL_PROXY_CONNECTION"; if (($CALLING_SCRIPT eq "manualtest") or ($KEY ne "Working")) { `/opt/ECXpert_custom/scripts/notify.pl -k $KEY -s $CALLING_SCRIPT +\"@MESSAGE\" `; exit(2); } ###################################################################### +######### ### Beginning of subroutine sections + ### ###################################################################### +######### sub pingFireWall { @FIREWALLS = @_; foreach $FIREWALL (@FIREWALLS) { my $PINGFIREWALL = `/usr/sbin/ping \"$FIREWALL\"`; if ($PINGFIREWALL =~ /alive/) { push @MESSAGE, "Ping of $FIREWALL successful..."; $KEY = "Working"; push (@REACHABLE_FIREWALLS, $FIREWALL); } else { push @MESSAGE, "Ping of $FIREWALL failed!..."; $KEY = "Serious"; } } ### error check and notify section unless (@REACHABLE_FIREWALLS >= 1) { push @MESSAGE, "Unable to reach any Firewall servers!!..."; $KEY = "Critical"; } return(@REACHABLE_FIREWALLS); } sub pingExternalHost { my @LINES; my $LINE; my $T; @REACHABLE_FIREWALLS = @_; foreach $REACHABLE_FIREWALL (@REACHABLE_FIREWALLS) { $T = new Net::Telnet (Timeout => 20, Prompt => '/tination/'); $T->open("$REACHABLE_FIREWALL"); $T->waitfor('/tination/'); if ($T) { push @TOUCHABLE_FIREWALLS, $REACHABLE_FIREWALL; push @MESSAGE, "Telnet to $REACHABLE_FIREWALL successful.. +."; @LINES = $T->cmd("!ping $EXTERNAL_HOST"); $LINE ="@LINES"; if ($LINE =~ /alive/) { push @MESSAGE, "Ping of $EXTERNAL_HOST from $R +EACHABLE_FIREWALL sucessful..."; $KEY = "Working"; push @USEABLE_FIREWALLS, $REACHABLE_FIREWALL; } else { push @MESSAGE, "Ping of $EXTERNAL_HOST from $REACHABLE +_FIREWALL failed!..."; } } else { push @MESSAGE, "Telnet to $REACHABLE_FIREWALL failed!..."; push @MESSAGE, $LINE; $KEY = "Serious"; } } ### error check and notify section unless (@TOUCHABLE_FIREWALLS >= 1) { push @MESSAGE, "Cannot Telnet to either Firewall!!..." +; $KEY = "Critical"; } unless (@USEABLE_FIREWALLS >= 1) { $KEY = "Critical"; } return(@USEABLE_FIREWALLS); } sub testFtpConnectivity { @USEABLE_FIREWALLS = @_; foreach $USEABLE_FIREWALL (@USEABLE_FIREWALLS) { if ($FTP_SESSION = Net::FTP-> new("$USEABLE_FIREWALL", Debug = +> 0)) { $SUCCESSFUL_PROXY_CONNECTION = $USEABLE_FIREWALL; push @MESSAGE, "Ftp connection to $USEABLE_FIREWALL succes +sful..."; $KEY = "Working"; last; } else { push @MESSAGE, "Ftp connection to $USEABLE_FIREWALL failed +!!!..."; $KEY = "Serious"; } } ### error check and notify section unless ($SUCCESSFUL_PROXY_CONNECTION ne "none") { push @MESSAGE, "No FTP connections could be made!!!..."; $KEY = "Critical"; } return ($SUCCESSFUL_PROXY_CONNECTION); }
    - cybear