summaryrefslogtreecommitdiffstats
path: root/util/lg/lg.cgi.in
diff options
context:
space:
mode:
Diffstat (limited to 'util/lg/lg.cgi.in')
-rwxr-xr-xutil/lg/lg.cgi.in449
1 files changed, 449 insertions, 0 deletions
diff --git a/util/lg/lg.cgi.in b/util/lg/lg.cgi.in
new file mode 100755
index 0000000..0320b7d
--- /dev/null
+++ b/util/lg/lg.cgi.in
@@ -0,0 +1,449 @@
+#!@PERLV_PATH@
+# Looking glass
+# vars: query, router, args
+
+BEGIN {
+$me = $0;
+$me =~ s/.*\/(\S+)$/$1/;
+}
+
+use CGI qw/:standard/;
+use POSIX qw(strftime);
+use Sys::Syslog;
+use LockFile::Simple qw(lock trylock unlock);
+
+my($BASEDIR) = "@prefix@";
+my($pingcmd) = "@LG_PING_CMD@";
+
+my($query, $max_time_diff, $cache_dir, $cloginrc, @results);
+my($type, $router_param, $remote_user, $arg, $router, $mfg);
+
+if (!defined($ENV{HOME})) { $ENV{HOME} = "."; }
+
+# read LG configuration file
+sub readconf
+{
+ my($conffile, $cmds);
+ local(*CONF);
+ if (defined($env{LG_CONF})) {
+ $conffile = $env{LG_CONF};
+ } else {
+ $conffile = "$BASEDIR/util/lg/lg.conf";
+ }
+
+ if (! -f $conffile) {
+ return;
+ }
+
+ if (open(CONF, "< $conffile")) {
+ while (<CONF>) {
+ next if (/^\s*(#|$)/);
+ $cmds .= $_;
+ }
+ close(CONF);
+ eval $cmds;
+ } else {
+ printf(STDERR "ERROR: couldn\'t open the configuration file: $conffile: $!\n");
+ exit(255);
+ }
+
+ return;
+}
+# logging
+sub dolog
+{
+ my($level, $msg) = @_;
+
+ if (defined($LG_LOG) && $LG_LOG !~ /\//) {
+ openlog($me, "pid", $LG_LOG);
+ syslog($level, "%s", $msg);
+ closelog;
+ } else {
+ local(*LOG);
+ my($file);
+ if (defined($LG_LOG)) {
+ $file = $LG_LOG;
+ } else {
+ $file = "$cache_dir/lg.log";
+ }
+ # log date, hostname, query, addr
+ open(LOG,">>$file") ;
+ printf(LOG "$msg");
+ close(LOG);
+ }
+ return;
+}
+# run commands on the router
+sub DoRsh
+{
+ my ($router, $mfg, $type, $arg) = @_;
+ my($ctime) = time();
+
+ my($lckobj) = LockFile::Simple->make(-delay => $lock_int,
+ -max => $max_lock_wait, -hold => $max_lock_hold);
+
+ if ($pingcmd =~ /\d$/) {
+ `$pingcmd $router`;
+ } else {
+ `$pingcmd $router 56 1`;
+ }
+ if ($?) {
+ return ("$router is unreachable. Try again later.\n");
+ }
+ if (! $lckobj->lock("$cache_dir/$router")) {
+ return ("$router is busy. Try again later.\n");
+ }
+ @results = &DoCmd($router, $mfg, $type, $arg);
+ $lckobj->unlock("$cache_dir/$router");
+ return (@results);
+}
+sub DoCmd
+{
+ my($rtr, $mfg, $type, $arg) = @_;
+ local(*CMD);
+
+ if ($mfg =~ /juniper/i) {
+ open(CMD, "jlogin -f $cloginrc -c \'$juniperCmd{$type} $arg\' $rtr |");
+ $cmd = $juniperCmd{$type};
+ } else {
+ open(CMD, "clogin -noenable -f $cloginrc -c \'$ciscoCmd{$type} $arg\' $rtr |");
+ $cmd = $ciscoCmd{$type};
+ }
+ while (<CMD>) {
+ tr/\015//d;
+ if (/^error:/i) {
+ dolog(LOG_ERR, $_);
+ return ($_);
+ }
+# push(@results, $_);
+ if (/$cmd/) {
+ ($prompt) = /^(\S*)[\#>]/;
+# push(@results, "prompt = $prompt\n\n");
+ last;
+ }
+ }
+ while (<CMD>) {
+ last if /^$prompt[\#>]/;
+ tr/\015//d;
+ push(@results, $_);
+ }
+ while (<CMD>) {}
+ close(CMD);
+ return @results;
+}
+# convert a ipv4 address mask to prefix length
+sub mask2len {
+ my($mask) = shift;
+ my($a, $b, $c, $d) = split('\.', $mask);
+ my($p, $len);
+
+ $p = ~ (($a << 24) + ($b << 16) + ($c << 8) + $d);
+ for ($len = 32; $p > 0; $len --) {
+ $p = $p >> 1;
+ }
+
+ return($len);
+}
+# create the page and log the transaction...
+sub print_results {
+
+ my($timestr) = strftime("%a %b %e %H:%M:%S %Y %Z", gmtime);
+ dolog(LOG_INFO, sprintf("%s %s %s %s\n",
+ $ENV{REMOTE_HOST}, $ENV{REMOTE_ADDR}, $ENV{REMOTE_USER},
+ "- - [$timestr] $type $router $arg"));
+ print $query->header;
+ print $query->start_html("Looking Glass Results - $router");
+
+ $timestr = strftime("%a %b %e %H:%M:%S %Y %Z", gmtime);
+
+ # add the company image, LG_IMAGE
+ print $LG_IMAGE;
+
+ print <<HEAD ;
+ </b></font>
+ <font size=+3><b><h1>Looking Glass Results - $router
+ </b></h1></font>
+ <hr>
+
+ <center>
+ <b>Date:</b> $timestr
+ <p>
+ <b>Query:</b> $cmdDisp{$type}
+ <br>
+HEAD
+
+ if ($arg) { print "<b>Argument(s):</b> $arg\n"; }
+ print "</center>\n";
+
+ print <<END ;
+ <!--$cached-->
+ </center>
+ <p>
+ <pre>
+END
+
+ if ($seconds) { print "<b>From cache (number of seconds old (max $max_time_diff)):</b> $seconds\n\n" ; }
+
+ print @results;
+
+ print <<END ;
+ </pre>
+ <!--- end page content --->
+ </body>
+END
+
+ print $query->end_html;
+ exit;
+
+} #end sub print_results
+
+
+# read the configuration file if it exists.
+readconf();
+
+## The script will now cache the results as simple files in the $cache_dir,
+## named after the type of query (queries must, of course, be one word no
+## spaces). Modify $max_time_diff to set the lifetime for each cache.
+## Currently, cache lifetime is the same for all queries.
+# for most web servers, cache_dir must be writable by uid nobody
+if (defined($LG_CACHE_DIR)) {
+ $cache_dir = $LG_CACHE_DIR;
+} else {
+ $cache_dir = "./tmp";
+}
+
+# when to display cache? max time difference (in seconds)
+if (defined($LG_CACHE_TIME)) {
+ $max_time_diff = $LG_CACHE_TIME;
+} else {
+ $max_time_diff = "600" ;
+}
+
+# max seconds to wait for a 'router' lock to free up
+$max_lock_wait = 30;
+$lock_int = 5;
+$max_lock_hold = 300;
+
+# clogin setup
+if (defined($LG_CLOGINRC)) {
+ $cloginrc = $LG_CLOGINRC;
+} else {
+ $cloginrc = "$BASEDIR/.cloginrc";
+}
+
+$query = new CGI;
+
+# get form data and validate
+$type = ($query->param('query'))[0];
+$router_param = ($query->param('router'))[0];
+$remote_user = $ENV{REMOTE_USER};
+$arg = ($query->param('args'))[0];
+# handle multiple args
+@arg = split(' ', $arg);
+
+# verify commands, arguments, etc.
+($router, $mfg) = split(':', $router_param);
+if (!defined($type) || !defined($router)) {
+ $results[0] = "You must at least choose a Query and a router. Try buying a clue.\n";
+ &print_results;
+}
+
+# conversion of command "type" passed from lgform.cgi to the vendor's syntax.
+%ciscoCmd = (
+ #acl => "sh access-list",
+ #aspath => "sh ip as-path-access-list",
+ #communitylist => "sh ip community-list",
+ damp => "sh ip bgp dampened-paths",
+ framerelay => "sh frame-relay pvc",
+ interface => "sh interface",
+ intbrief => "sh ip interface",
+ log => "sh logging",
+ mbgp => "sh ip mbgp",
+ mbgpsum => "sh ip mbgp summary",
+ regex => "sh ip bgp regex",
+ route => "sh ip route",
+ #routemap => "sh route-map",
+ ping => "ping",
+ prefix => "sh ip bgp",
+ prefixlist => "sh ip prefix-list",
+ summary => "sh ip bgp summary",
+ trace => "traceroute"
+ );
+%juniperCmd = (
+ #acl => "sh access-list",
+ #aspath => "sh ip as-path-access-list",
+ #communitylist => "sh ip community-list",
+ damp => "show route damping suppressed terse table inet.0",
+ interface => "sh interface",
+ log => "show log messages",
+ mbgp => "sh route table inet.2 terse",
+ mbgpsum => "sh bgp summary",
+ #regex => "sh route table inet.0 aspath-regex",
+ route => "sh route table inet.0",
+ #routemap => "sh route-map",
+ ping => "ping rapid count 5",
+ prefix => "sh route table inet.0",
+ #prefixlist => "sh ip prefix-list",
+ summary => "sh bgp summary",
+ trace => "traceroute"
+ );
+%cmdDisp = (
+ #acl => "show access-list",
+ #aspath => "show ip as-path-access-list",
+ #communitylist => "show ip community-list",
+ damp => "show ip bgp dampened-paths",
+ log => "show logging",
+ mbgp => "show ip mbgp",
+ mbgpsum => "show ip mbgp summary",
+ regex => "show ip bgp regex",
+ route => "show ip route",
+ #routemap => "show route-map",
+ ping => "ping",
+ prefix => "show ip bgp",
+ prefixlist => "show ip prefix-list",
+ summary => "show ip bgp summary",
+ trace => "traceroute"
+ );
+
+# not all cmds/queries are implemented for junipers
+if ($mfg =~ /juniper/ && ! defined($juniperCmd{$type})) {
+ $results[0] = "$cmdDisp{$type} not implemented for junipers. sorry.\n";
+ &print_results;
+}
+
+
+if ($type eq "prefix" || $type eq "mbgp" || $type eq "route" ) {
+ if ($arg[0] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+ $results[0] = "The IP address \"$arg[0]\" is not valid and lacking an address would over-burden our router.\n";
+ &print_results;
+ } elsif (defined($arg[1]) && $arg[1] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+ $results[0] = "The IP netmask \"$arg[1]\" is not valid.\n";
+ &print_results;
+ }
+ if ($mfg =~ /juniper/i && defined($arg[1])) {
+ $arg = $arg[0] . "/" . mask2len($arg[1]);
+ }
+} elsif ($type eq "framerelay") {
+ if ($arg[0] > 15 && $arg[0] < 1024) {
+ $arg = $arg[0];
+ } else {
+ undef($arg);
+ }
+} elsif ($type eq "interface") {
+ if ($arg[1] =~ /[-\/0-9:.]+/) {
+ $arg = $arg[0] . " " . $arg[1];
+ } else {
+ if ($arg[0] =~ /^b/i && $mfg =~ /cisco/i) {
+ $type = "intbrief";
+ $arg = "brief";
+ } else {
+ $arg = $arg[0];
+ }
+ }
+} elsif ($type eq "log") {
+ $arg[0] =~ s/^\s*|//;
+ if ($arg[0] =~ /^\s*$/) {
+ shift(@arg);
+ }
+ if ($arg[0] !~ /^\s*$/) {
+ if ($mfg =~ /cisco/i) {
+ $arg = " | include " . join(' ', @arg);
+ } elsif ($mfg =~ /juniper/i) {
+ $arg = " | match \"" . join(' ', @arg) . "\"";
+ }
+ } else {
+ undef($arg);
+ }
+} elsif ($type eq "ping" || $type eq "trace") {
+ if ($arg[0] !~ /^\d+\.\d+\.\d+\.\d+$/) {
+ if ($arg[0] !~ /([A-Za-z0-9-]*.)*[A-Za-z0-9-]*.(com|edu|net|org)/) {
+ $results[0] = "That argument ($arg[0]) is not valid.\n";
+ &print_results;
+ }
+ }
+ $arg = $arg[0];
+} elsif ($type eq "aspath" || $type eq "communitylist") {
+ if ($arg[0] !~ /^\d+$/ || ($arg[0] < 1 && $arg[0] > 199)) {
+ $results[0] = "That argument ($arg[0]) is not valid.\n";
+ &print_results;
+ }
+ $arg = $arg[0];
+} elsif ($type eq "acl") {
+ if ($arg[0] !~ /^\d+$/ || ($arg[0] < 100 && $arg[0] > 199) ||
+ ($arg[0] < 1300 && $arg[0] > 2699)) {
+ $results[0] = "That argument ($arg[0]) is not valid.\n";
+ &print_results;
+ }
+ $arg = $arg[0];
+ # don't show the jewels
+ &print_results if ($arg == 98 || $arg == 99);
+} elsif ($type eq "prefixlist" || $type eq "routemap") {
+ if ($arg[0] !~ /^[A-Za-z][^\s\"]*$/) {
+ $results[0] = "That argument ($arg[0]) is not valid.\n";
+ &print_results;
+ }
+ $arg = $arg[0];
+} elsif ($type eq "regex") {
+ $arg = $arg[0];
+ if ($#arg > 1) {
+ for ($n = 1; $n <= $#arg - 1; $n++) { $arg .= " " . $arg[$n]; }
+ }
+ if ($arg !~ /^[0-9_ ^.*+?[\])\(-]*\$?$/ || $arg =~ /^\s*$/) {
+ $results[0] = "That argument ($arg[0]) is not valid.\n";
+ &print_results;
+ }
+ # pathetic excuses for lookups
+ if ($arg =~ /^[_.* ^]*(\*|1|701|1239|1280|1740|3561|5462|10303)+[_\$]*$/ ||
+ $arg =~ /^[_.* ^]*(1|701|1239|1280|1740|3561|5462|10303)+[_ .]*[\[*.]/) {
+ $results[0] = "Get real. Such a query has potential to over-burden our router.\nLook that up on your own router.\n";
+ &print_results;
+ }
+ # escape any ()
+ $arg =~ s/([\(\)])/\\$1/g;
+} elsif ($type eq "damp" || $type eq "summary" || $type eq "mbgpsum") {
+ undef($arg);
+}
+
+# cache the following
+if ($type eq "summary" || $type eq "mbgpsu" || $type eq "damp" || $type eq "log") {
+ if (!$arg) {
+ # cache requests with no addr/argument
+ local(*CACHE);
+
+ my($file) = "$cache_dir/$type" ;
+ $file =~ s/\s+/_/g;
+ $file .= "_$router";
+
+ if (-e $file) {
+ # see if cache exists
+ @stat = stat($file);
+ $ftime = $stat[9] ;
+ $dtime = time - $stat[9] ;
+
+ # see if we are within cache time
+ if ($dtime <= $max_time_diff) {
+ open(CACHE,"$file") ;
+ while (<CACHE>) { push(@results, $_); }
+ close CACHE ;
+ $seconds = $dtime;
+ &print_results ;
+ }
+ }
+
+ # else, execute command and save to a new cache file
+ open(CACHE,">$file") || die "couldnt create cache file $file" ;
+
+ @results = &DoRsh($router, $mfg, $type, $arg);
+
+ print CACHE "@results";
+ close CACHE ;
+ } else {
+ @results = &DoRsh($router, $mfg, $type, $arg);
+ }
+ &print_results ;
+} # end dampened-paths/flap-statistics
+
+@results = &DoRsh($router, $mfg, $type, $arg);
+&print_results ;
+
+exit ;