#!/usr/bin/perl
#
# Looking Glass CGI with ssh, telnet, rexec and remote LG support
# with IPv4 and IPv6 support
#
# Copyright (C) 2000-2002 Cougar
# http://www.version6.net/
#
# 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
#
use strict qw(subs vars);
$ENV{HOME} = "."; # SSH needs access for $HOME/.ssh
use XML::Parser;
my $SYS_progid = '$Id: lg.cgi,v 1.30 2004/11/25 14:12:42 cougar Exp $';
my $default_ostype = "ios";
my $lgurl;
my $logfile;
my $asfile;
my $logoimage;
my $logoalign;
my $logolink;
my $title;
my $favicon;
my $email;
my $rshcmd;
my $ipv4enabled;
my $ipv6enabled;
my $httpmethod = "POST";
my $timeout;
my $disclaimer;
my $securemode = 1;
my %router_list;
my @routers;
my %namemap;
my %ostypes;
my %cmdmap;
my $default_router;
my $xml_current_router_name = "";
my $xml_current_cgi_name = "";
my $xml_current_replace_name = "";
my $xml_current_replace_proto = "";
my %valid_query = (
"ios" => {
"ipv4" => {
"bgp" => "show ip bgp %s",
"advertised-routes" => "show ip bgp neighbors %s advertised-routes",
"summary" => "show ip bgp summary",
"ping" => "ping %s",
"trace" => "traceroute %s"
},
"ipv6" => {
"bgp" => "show bgp ipv6 %s",
"advertised-routes" => "show bgp ipv6 neighbors %s advertised-routes",
"summary" => "show bgp ipv6 summary",
"ping" => "ping ipv6 %s",
"trace" => "traceroute ipv6 %s"
}
},
"zebra" => {
"ipv4" => {
"bgp" => "show ip bgp %s",
"advertised-routes" => "show ip bgp neighbors %s advertised-routes",
"summary" => "show ip bgp summary",
"ping" => "ping %s",
"trace" => "traceroute %s"
},
"ipv6" => {
"bgp" => "show bgp ipv6 %s",
"advertised-routes" => "show bgp ipv6 neighbors %s advertised-routes",
"summary" => "show bgp ipv6 summary",
"ping" => "ping ipv6 %s",
"trace" => "traceroute ipv6 %s"
}
},
"junos" => {
"ipv4" => {
"trace" => "traceroute %s as-number-lookup"
},
"ipv6" => {
"trace" => "traceroute %s"
},
"ipv46" => {
"bgp" => "show bgp %s",
"advertised-routes" => "show route advertising-protocol bgp %s %s",
"summary" => "show bgp summary",
"ping" => "ping count 5 %s"
}
}
);
my %whois = (
"RIPE" => "http://www.ripe.net/perl/whois?AS%s",
"ARIN" => "http://www.arin.net/cgi-bin/whois.pl?queryinput=%s",
"APNIC" => "http://www.apnic.net/apnic-bin/whois.pl?searchtext=AS%s",
"default" => "http://www.sixxs.net/tools/whois/?AS%s"
);
$| = 1;
&read_config;
# grab CGI data
my $incoming;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $incoming, $ENV{'CONTENT_LENGTH'});
} else {
$incoming = $ENV{'QUERY_STRING'};
}
my %FORM = &cgi_decode($incoming);
my $date = localtime;
if ($logfile ne "") {
open(LOG, ">>$logfile");
($ENV{REMOTE_HOST}) && ( print LOG "$ENV{'REMOTE_HOST'} ");
($ENV{REMOTE_ADDR}) && ( print LOG "$ENV{'REMOTE_ADDR'} ");
print LOG "- - [$date]";
($ENV{HTTP_REFERER}) && ( print LOG " $ENV{'HTTP_REFERER'}");
}
my $query_cmd = "";
if (defined $valid_query{$ostypes{$FORM{router}}}{"ipv46"}{$FORM{query}}) {
$query_cmd = $valid_query{$ostypes{$FORM{router}}}{"ipv46"}{$FORM{query}};
} elsif (defined $valid_query{$ostypes{$FORM{router}}}{lc($FORM{protocol})}{$FORM{query}}) {
$query_cmd = $valid_query{$ostypes{$FORM{router}}}{lc($FORM{protocol})}{$FORM{query}};
} elsif (($FORM{router} ne "") || ($FORM{protocol} ne "") || ($FORM{query})) {
if ($logfile ne "") {
print LOG " \"$FORM{router}\" \"ILLEGAL QUERY: [$ostypes{$FORM{router}}] [$FORM{protocol}] [$FORM{query}]\"\n";
close(LOG);
}
&print_head;
&print_form;
&print_tail;
exit;
}
if ((! defined $router_list{$FORM{router}}) ||
($query_cmd eq "")) {
if ($logfile ne "") {
print LOG "\n";
close(LOG);
}
&print_head;
&print_form;
&print_tail;
exit;
}
if ($router_list{$FORM{router}} =~ /^http[s]{0,1}:/) {
if ($logfile ne "") {
print LOG " \"$FORM{router}\" \"$FORM{query}" . ($FORM{addr} ne "" ? " $FORM{addr}" : "") . "\"\n";
close LOG;
}
if ($router_list{$FORM{router}} =~ /\?/) {
$incoming = "&$incoming";
} else {
$incoming = "?$incoming";
}
my $remote = $router_list{$FORM{router}};
if (defined $cmdmap{$remote}{lc($FORM{protocol})}) {
$incoming .= "&";
my $mapref = $cmdmap{$remote}{lc($FORM{protocol})};
foreach my $key (keys (%{$mapref})) {
next if ($key eq "DEFAULT");
(my $urlkey = $key) =~ s/([+*\/\\])/\\$1/g;
if (${$mapref}{$key} eq "") {
$incoming =~ s/([\?\&])($urlkey)=[^\&]*\&/$1/g;
} elsif (${$mapref}{$key} =~ /=/) {
$incoming =~ s/([\?\&])($urlkey)\&/"${1}${$mapref}{$2}&"/e;
}
}
foreach my $key (keys (%{$mapref})) {
next if ($key eq "DEFAULT");
$incoming =~ s/([\?\&])($key)=/"${1}${$mapref}{$2}="/e;
}
$incoming =~ s|&$||g;
if (defined ${$mapref}{DEFAULT}) {
$incoming .= "&${$mapref}{DEFAULT}";
}
}
print "Location: $router_list{$FORM{router}}${incoming}\n\n";
exit;
}
my $command = sprintf($query_cmd, $FORM{addr});
print LOG " \"$FORM{router}\" \"$command\"\n";
close LOG;
&print_head($command);
if ($FORM{addr} !~ /^[\w\.\^\$\-\/ ]*$/) {
if ($FORM{addr} =~ /^[\w\.\^\$\-\:\/ ]*$/) {
if (($FORM{protocol} ne "IPv6") && ($ostypes{$FORM{router}} ne "junos")){
&print_error("ERROR: IPv6 address for IPv4 query");
}
} else {
&print_error("Illegal characters in parameter string");
}
}
$FORM{addr} = "" if ($FORM{addr} =~ /^[ ]*$/);
if ($query_cmd =~ /%s/) {
&print_error("Parameter missing") if ($FORM{addr} eq "");
} else {
&print_warning("No parameter needed") if ($FORM{addr} ne "");
}
my %AS;
if ($asfile =~ /\.db$/) {
use DB_File;
tie (%AS, 'DB_File', $asfile, O_RDONLY, 0644, $DB_HASH) or
print STDERR "Can\'t read AS database $asfile: $!\n";
} else {
%AS = &read_as_list($asfile);
}
if ($ostypes{$FORM{router}} eq "junos") {
if ($command =~ /^show bgp n\w*\s+([\d\.A-Fa-f:]+)$/) {
# show bgp n.. ---> show bgp neighbor
$command = "show bgp neighbor $1";
} elsif ($command =~ /^show bgp n\w*\s+([\d\.A-Fa-f:]+) ro\w*$/) {
# show bgp n.. ro.. ---> show route receive-protocol bgp
$command = "show route receive-protocol bgp $1";
} elsif ($command =~ /^show bgp neighbors ([\d\.A-Fa-f:]+) routes all$/) {
# show bgp neighbors routes all ---> show route receive-protocol bgp all
$command = "show route receive-protocol bgp $1 all";
} elsif ($command =~ /^show bgp neighbors ([\d\.A-Fa-f:]+) routes damping suppressed$/) {
# show bgp neighbors routes damping suppressed ---> show route receive-protocol bgp damping suppressed
$command = "show route receive-protocol bgp $1 damping suppressed";
} elsif ($command =~ /^show bgp n\w*\s+([\d\.A-Fa-f:]+) advertised-routes ([\d\.A-Fa-f:\/]+)$/) {
# show ip bgp n.. advertised-routes ---> show route advertising-protocol bgp exact detail
$command = "show route advertising-protocol bgp $1 $2 exact detail";
} elsif ($command =~ /^show bgp n\w*\s+([\d\.A-Fa-f:]+) receive-protocol ([\d\.A-Fa-f:\/]+)$/) {
# show ip bgp n.. receive-protocol ---> show route receive-protocol bgp exact detail
$command = "show route receive-protocol bgp $1 $2 exact detail";
} elsif ($command =~ /^show bgp n\w*\s+([\d\.A-Fa-f:]+) a[\w\-]*$/) {
# show ip bgp n.. a.. ---> show route advertising-protocol bgp
$command = "show route advertising-protocol bgp $1";
} elsif ($command =~ /^show bgp\s+([\d\.A-Fa-f:]+\/\d+)$/) {
# show bgp /mask ---> show route protocol bgp all
$command = "show route protocol bgp $1 terse exact";
} elsif ($command =~ /^show bgp\s+([\d\.A-Fa-f:]+)$/) {
# show bgp ---> show route protocol bgp all
$command = "show route protocol bgp $1 terse";
} elsif ($command =~ /^show bgp\s+([\d\.A-Fa-f:\/]+) exact$/) {
# show bgp exact ---> show route protocol bgp exact detail all
$command = "show route protocol bgp $1 exact detail all";
} elsif ($command =~ /^show bgp re\s+(.*)$/) {
# show ip bgp re ---> show route aspath-regex all
my $re = $1;
$re = "^.*${re}" if ($re !~ /^\^/);
$re = "${re}.*\$" if ($re !~ /\$$/);
$re =~ s/_/ /g;
$command = "show route aspath-regex \"$re\" all";
}
}
&print_results($FORM{router}, $router_list{$FORM{router}}, $command);
&print_tail;
exit;
sub read_config {
my $xp = new XML::Parser(ProtocolEncoding => "ISO-8859-1", Handlers => {Char => \&xml_charparse, Start => \&xml_startparse, End => \&xml_endparse});
$xp->parsefile("lg.conf");
undef($xp);
}
sub xml_charparse {
my ($xp,$str) = @_;
return if $str =~ /^\s*$/m;
my $elem = lc($xp->current_element);
if ($xml_current_router_name ne "") {
if ($elem eq "url") {
$router_list{$xml_current_router_name} = $str;
push @routers, $xml_current_router_name;
} elsif ($elem eq "title") {
$namemap{$xml_current_router_name} .= $str;
} else {
die("Illegal value for configuration tag \"" . $xp->current_element . "\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif (($xml_current_cgi_name ne "") && ($xml_current_replace_name ne "")) {
if (($elem eq "replace") ||
($elem eq "default")) {
$cmdmap{$xml_current_cgi_name}{"ipv4"}{$xml_current_replace_name} .= $str if ($xml_current_replace_proto ne "ipv6");
$cmdmap{$xml_current_cgi_name}{"ipv6"}{$xml_current_replace_name} .= $str if ($xml_current_replace_proto ne "ipv4");
} else {
die("Illegal value for configuration tag \"" . $xp->current_element . "\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "lgurl") {
$lgurl = $str;
} elsif ($elem eq "logfile") {
$logfile = $str;
} elsif ($elem eq "aslist") {
$asfile = $str;
} elsif ($elem eq "logoimage") {
$logoimage = $str;
} elsif ($elem eq "htmltitle") {
$title = $str;
} elsif ($elem eq "favicon") {
$favicon = $str;
} elsif ($elem eq "contactmail") {
$email = $str;
} elsif ($elem eq "rshcmd") {
$rshcmd = $str;
} elsif ($elem eq "httpmethod") {
$httpmethod = $str;
} elsif ($elem eq "timeout") {
$timeout = $str;
} elsif ($elem eq "disclaimer") {
$disclaimer = "\n";
} elsif ($elem eq "securemode") {
if ($str =~ /^(0|off|no)$/i) {
$securemode = 0;
} elsif ($str =~ /^(1|on|yes)$/i) {
$securemode = 1;
} else {
die("Illegal securemode \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "separator") {
push @routers, "---- $str ----";
} else {
print "\n";
}
}
sub xml_startparse {
my ($xp,$str,@attrval) = @_;
my $elem = lc($xp->current_element);
my $str2 = lc($str);
if ($elem eq "") {
if ($str2 ne "lg_conf_file") {
die("Illegal configuration tag \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "lg_conf_file") {
if ($str2 eq "logoimage") {
for (my $i = 0; $i <= $#attrval; $i += 2) {
if (lc($attrval[$i]) eq "align") {
$logoalign = " Align=\"" . $attrval[$i+1] . "\"";
} elsif (lc($attrval[$i]) eq "link") {
$logolink = $attrval[$i+1];
} else {
die("Illegal parameter for LogoImage \"" . $attrval[$i] . "\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
}
} elsif (($str2 ne "lgurl") &&
($str2 ne "logfile") &&
($str2 ne "aslist") &&
($str2 ne "htmltitle") &&
($str2 ne "favicon") &&
($str2 ne "contactmail") &&
($str2 ne "rshcmd") &&
($str2 ne "httpmethod") &&
($str2 ne "timeout") &&
($str2 ne "disclaimer") &&
($str2 ne "securemode") &&
($str2 ne "router_list") &&
($str2 ne "argument_list")) {
die("Illegal configuration tag \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "router_list") {
if ($str2 eq "router") {
for (my $i = 0; $i <= $#attrval; $i += 2) {
if (lc($attrval[$i]) eq "name") {
$xml_current_router_name = $attrval[$i+1];
$ostypes{$xml_current_router_name} = lc($default_ostype);
$ipv4enabled ++;
} elsif (lc($attrval[$i]) eq "default") {
if (lc($attrval[$i+1]) eq "yes") {
$default_router = $xml_current_router_name;
}
} elsif (lc($attrval[$i]) eq "enableipv6") {
if (lc($attrval[$i+1]) eq "yes") {
$ipv4enabled--;
$ipv6enabled++;
}
} elsif (lc($attrval[$i]) eq "ostype") {
$ostypes{$xml_current_router_name} = lc($attrval[$i+1]);
}
}
if ($xml_current_router_name eq "") {
die("Variable \"Name\" missing at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($str2 eq "separator") {
} else {
die("Illegal configuration tag \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "router") {
if (($str2 ne "title") &&
($str2 ne "url")) {
die("Illegal configuration tag \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "argument_list") {
if ($str2 eq "lg") {
for (my $i = 0; $i <= $#attrval; $i += 2) {
if (lc($attrval[$i]) eq "url") {
$xml_current_cgi_name = $attrval[$i+1];
}
}
if ($xml_current_cgi_name eq "") {
die("Variable \"URL\" missing at line " . $xp->current_line . ", column " . $xp->current_column);
}
} else {
die("Illegal configuration tag \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} elsif ($elem eq "lg") {
if ($str2 eq "replace") {
for (my $i = 0; $i <= $#attrval; $i += 2) {
if (lc($attrval[$i]) eq "param") {
$xml_current_replace_name = $attrval[$i+1];
} elsif (lc($attrval[$i]) eq "proto") {
$xml_current_replace_proto = lc($attrval[$i+1]);
}
}
if ($xml_current_replace_name eq "") {
die("Variable \"Param\" missing at line " . $xp->current_line . ", column " . $xp->current_column);
}
$cmdmap{$xml_current_cgi_name}{"ipv4"}{$xml_current_replace_name} = "" if ($xml_current_replace_proto ne "ipv6");
$cmdmap{$xml_current_cgi_name}{"ipv6"}{$xml_current_replace_name} = "" if ($xml_current_replace_proto ne "ipv4");
} elsif ($str2 eq "default") {
$xml_current_replace_name = "DEFAULT";
} else {
die("Illegal configuration tag \"$str\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
} else {
die("ASSERT str=\"$str\" elem=\"" . $xp->current_element . "\" at line " . $xp->current_line . ", column " . $xp->current_column);
}
}
sub xml_endparse {
my ($xp,$str) = @_;
my $elem = lc($xp->current_element);
my $str2 = lc($str);
if ($elem eq "router_list") {
if ($str2 eq "router") {
$xml_current_router_name = "";
}
} elsif ($elem eq "lg") {
if (($str2 eq "replace") ||
($str2 eq "default")) {
$xml_current_replace_name = "";
$xml_current_replace_proto = "";
}
}
}
sub print_head {
my ($arg) = @_;
my ($titlestr) = $title;
$titlestr .= " - $arg" if ($arg ne "");
print "Content-type: text/html\n\n";
print "\n";
print "\n";
print "\n";
print "\n";
print "$titlestr\n";
if ($favicon ne "") {
print "\n";
}
print <
EOT
print "\n";
print "\n";
if ($logoimage ne "") {
print "\n";
}
print "\n";
print "$titlestr
\n";
print "\n";
print "
\n";
}
sub print_form {
print <
\n\n";
}
print <
|
|
|
|
|
EOT
}
sub print_tail {
print <
$disclaimer
Please email questions or comments to
$email.
EOT
}
sub print_error
{
print "" . join(" ", @_) . "\n";
&print_tail;
exit 1;
}
sub print_warning
{
print "WARNING! " . join(" ", @_) . "\n";
print <
EOT
}
sub print_results
{
my ($hostname, $host, $command) = @_;
my $best = 0;
my $count = 0;
my $telnet;
my $ssh;
my @output;
# This regexp is from RFC 2396 - URI Generic Syntax
if ($host !~ /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/) {
die ("Illegal URI: \"$host\"");
}
my $scheme = $2;
$host = $4;
if ($host !~ /^((([^:\@\[]+)(:([^\@]+))?)\@)?([^\/?#]*)$/) {
die ("Can't extract login/pass from host: \"$host\"");
}
my $login = $3;
my $password = $5;
$host = $6;
my $port;
if ($host =~ /^\[(.+)\](:([\d,]+))?$/) {
$host = $1;
$port = $3;
} elsif ($host =~ /^([^:]+)(:([\d,]+))?$/) {
$host = $1;
$port = $3;
} else {
die ("Illegal host address \"$host\"");
}
print "Router: " . html_encode($hostname) . "\n";
print "
\n";
print "Command: " . html_encode($command) . "\n";
print "\n";
if ($scheme eq "rsh") {
print_error("Configuration error, missing rshcmd") if ($rshcmd eq "");
open(P, "$rshcmd $host \'$command\' |");
} elsif ($scheme eq "ssh") {
eval "
use IO::Handle;
use Net::SSH::Perl;
use Net::SSH::Perl::Cipher;
";
die $@ if $@;
$port = 22 if ($port eq "");
$ssh = Net::SSH::Perl->new($host, port => $port);
$ssh->login($login, $password);
my ($out, $err) = $ssh->cmd("$command");
@output = split (/\n/, $out);
} elsif ($scheme eq "telnet") {
eval "
use Net::Telnet;
";
die $@ if $@;
if ($ostypes{$FORM{router}} eq "zebra") {
if (($command =~ /^ping /) || ($command =~ /^traceroute /)) {
$port = $1 if ($port =~ /^(\d+),\d*$/);
$port = 2601 if ($port eq "");
} else {
$port = $1 if ($port =~ /^\d*,(\d+)$/);
$port = 2605 if ($port eq "");
}
}
$port = 23 if ($port eq "");
$telnet = new Net::Telnet;
$telnet->errmode( sub { print "ERROR:" . join('|', @_) . "\n"; } );
$telnet->timeout($timeout);
$telnet->option_callback( sub { return; } );
$telnet->option_accept(Do => 31); # TELOPT_NAWS
$telnet->open(Host => $host,
Port => $port);
if ($login ne "") {
$telnet->waitfor('/(ogin|name|word):.*$/');
$telnet->print("$login");
}
if ($password ne "") {
$telnet->waitfor('/word:.*$/');
$telnet->print("$password");
}
$telnet->waitfor(Match => '/.*[\$%>] {0,1}$/',
Match => '/^[^#]*[\$%#>] {0,1}$/');
$telnet->telnetmode(0);
$telnet->put(pack("C9",
255, # TELNET_IAC
250, # TELNET_SB
31, 0, 200, 0, 0, # TELOPT_NAWS
255, # TELNET_IAC
240)); # TELNET_SE
$telnet->telnetmode(1);
my $telnetcmd = $command;
$telnetcmd .= " | no-more" if ($ostypes{$FORM{router}} eq "junos");
$telnet->print("$telnetcmd");
$telnet->getline; # read out command line
} else {
print_error("Configuration error, no such scheme: $scheme\n");
}
my $lastip = "";
my $inemptyheader = 1;
while (1) {
if ($scheme eq "telnet") {
if ($#output >= 0) {
$_ = shift (@output);
} elsif (! $telnet->eof) {
my ($prematch, $match) = $telnet->waitfor(
Match => '/\n/',
Match => '/[\$%#>] {0,1}$/',
Errmode => "return")
or do {
};
if ($match =~ /[\$%#>] {0,1}$/) {
$telnet->print("quit");
$telnet->close;
last;
}
push @output, $prematch . $match;
next;
} else {
last;
}
} elsif ($scheme eq "ssh") {
last if ($#output < 0);
$_ = shift (@output);
} else {
last if (eof(P));
$_ = ;
}
next if (/Type escape sequence to abort./);
next if (/Translating .*\.\.\.domain server/);
next if (($inemptyheader) && (/^$/));
$inemptyheader = 0;
$_ = html_encode($_);
if ($command eq "show ip bgp summary") {
s/( local AS number )(\d+)/($1 . as2link($2))/e;
s/^([\d\.]+\s+\d+\s+)(\d+)/($1 . as2link($2))/e;
s/^(\d+\.\d+\.\d+\.\d+)(\s+.*\s+)([1-9]\d*)$/($1 . $2 . bgplink($3, "neighbors+$1+routes"))/e;
s/^(\d+\.\d+\.\d+\.\d+)(\s+)/(bgplink($1, "neighbors+$1") . $2)/e;
# Zebra IPv6 neighbours
s/^(.{15} 4\s+)(\d+)/($1 . as2link($2))/e;
s/^([\dA-Fa-f]*:[\dA-Fa-f:]*)(\s+)/(bgplink($1, "neighbors+$1") . $2)/e;
s/^([\dA-Fa-f]*:[\dA-Fa-f:]*)$/bgplink($1, "neighbors+$1")/e;
} elsif ($command eq "show bgp ipv6 summary") {
s/^(.{15} 4\s+)(\d+)/($1 . as2link($2))/e;
if (/^([\dA-Fa-f]*:[\dA-Fa-f:]*)\s+4\s+/) {
$lastip = $1;
s/^([\dA-Fa-f:]+)(\s+.*\s+)([1-9]\d*)$/($1 . $2 . bgplink($3, "neighbors+${lastip}+routes"))/e;
s/^([\dA-Fa-f:]+)(\s+)/(bgplink($1, "neighbors+$1") . $2)/e;
$lastip = "";
}
if (/^([\dA-Fa-f:]+)$/) {
$lastip = $1;
s/^([\dA-Fa-f:]+)$/bgplink($1, "neighbors+$1")/e;
}
if (($lastip ne "") && (/^(\s+.*\s+)([1-9]\d*)$/)) {
s/^(\s+.*\s+)([1-9]\d*)$/($1 . bgplink($2, "neighbors+${lastip}+routes"))/e;
$lastip = "";
}
} elsif ($command eq "show bgp summary") {
# JunOS
if (/^([\dA-Fa-f:][\d\.A-Fa-f:]+)\s+/) {
$lastip = $1;
# IPv4
s/^(\d+\.\d+\.\d+\.\d+)(\s+.*\s+)([1-9]\d*)(\s+\d+\s+\d+\s+\d+\s+\d+\s+[\d:]+\s+)(\d+)\/(\d+)\/(\d+)(\s+)/($1 . $2 . bgplink($3, "neighbors+$1+routes") . $4 . bgplink($5, "neighbors+$1+routes") . "\/" . bgplink($6, "neighbors+$1+routes+all") . "\/" . bgplink($7, "neighbors+$1+routes+damping+suppressed") . $8)/e;
# IPv4/IPv6
s/^([\dA-Fa-f:][\d\.A-Fa-f:]+\s+)(\d+)(\s+)/($1 . as2link($2) . $3)/e;
s/^([\dA-Fa-f:][\d\.A-Fa-f:]+)(\s+)/(bgplink($1, "neighbors+$1") . $2)/e;
}
if (($lastip ne "") && (/( [^:]+: )(\d+)\/(\d+)\/(\d+)$/)) {
s/^( [^:]+: )(\d+)\/(\d+)\/(\d+)$/($1 . bgplink($2, "neighbors+${lastip}+routes") . "\/" . bgplink($3, "neighbors+${lastip}+routes+all") . "\/" . bgplink($4, "neighbors+${lastip}+routes+damping+suppressed"))/e;
$lastip = "";
}
} elsif (($command =~ /^show ip bgp\s+n\w*\s+[\d\.]+\s+(ro|re|a)/i) ||
($command =~ /^show bgp ipv6\s+n\w*\s+[\dA-Fa-f:]+\s+(ro|re|a)/i) ||
($command =~ /^show ip bgp\s+re/i) ||
($command =~ /^show bgp ipv6\s+re/i) ||
($command =~ /^show ip bgp\s+[\d\.]+\s+[\d\.]+\s+(l|s)/i) ||
($command =~ /^show (ip bgp|bgp ipv6) prefix-list/i) ||
($command =~ /^show (ip bgp|bgp ipv6) route-map/i)) {
s/^([\*r ](>|d|h| ).{59})([\d\s,\{\}]+)([ie\?])$/($1 . as2link($3) . $4)/e;
s/^([\*r ](>|d|h| )[i ])([\d\.A-Fa-f:\/]+)(\s+)/($1 . bgplink($3, $3) . $4)/e;
s/^([\*r ](>|d|h| )[i ])([\d\.A-Fa-f:\/]+)$/($1 . bgplink($3, $3))/e;
s/^(( ){20}.{41})([\d\s,\{\}]+)([ie\?])$/($1 . as2link($3) . $4)/e;
s/(, remote AS )(\d+)(,)/($1 . as2link($2) . $3)/e;
} elsif ($command =~ /^show route receive-protocol bgp\s+([\d\.A-Fa-f:]+)/i) {
my $ip = $1;
s/(Community: )([\d: ]+)/($1 . community2link($2))/e;
s/(Communities: )([\d: ]+)/($1 . community2link($2))/e;
s/(^\s+AS path: )([\d ]+)/($1 . as2link($2))/e;
s/^([\d\.\s].{24})([\d\.]+)(\s+)/($1 . bgplink($2, "neighbors+$2") . $3)/e;
s/^([\d\.\/]+)(\s+)/(bgplink($1, $1) . $2)/e;
s/^([\d\.A-Fa-f:\/]+)(\s+)/(bgplink($1, "$1+exact") . $2)/e;
s/^([\d\.A-Fa-f:\/]+)\s*$/(bgplink($1, "$1+exact"))/e;
s/^([ \*] )([\d\.A-Fa-f:\/]+)(\s+)/($1 . bgplink($2, "$2+exact") . $3)/e;
} elsif ($command =~ /^show route advertising-protocol bgp\s+([\d\.A-Fa-f:]+)$/i) {
my $ip = $1;
s/^([\d\.\s].{64})([\d\s,\{\}]+)([I\?])$/($1 . as2link($2) . $3)/e;
s/^([\d\.\s].{24})([\d\.]+)(\s+)/($1 . bgplink($2, "neighbors+$2") . $3)/e;
s/^([\d\.\/]+)(\s+)/(bgplink($1, $1) . $2)/e;
s/^([\d\.A-Fa-f:\/]+)(\s+)/(bgplink($1, "$1+exact") . $2)/e;
s/^([\d\.A-Fa-f:\/]+)\s*$/(bgplink($1, "$1+exact"))/e;
s/^([ \*] )([\d\.A-Fa-f:\/]+)(\s+)/($1 . bgplink($2, "neighbors+$ip+advertised-routes+$2") . $3)/e;
} elsif (($command =~ /^show ip bgp n\w*\s+([\d\.]+)/i) ||
($command =~ /^show ip bgp n\w*$/i)) {
$lastip = $1 if ($1 ne "");
$lastip = $1 if (/^BGP neighbor is ([\d\.]+),/);
if ($securemode) {
s/((Local|Foreign) port: )\d+/${1}???/g;
}
s/(Prefix )(advertised)( [1-9]\d*)/($1 . bgplink($2, "neighbors+$lastip+advertised-routes") . $3)/e;
s/( Prefixes Total: )(\d+)( )/($1 . bgplink($2, "neighbors+$lastip+advertised-routes") . $3)/e;
s/(prefixes )(received)( [1-9]\d*)/($1 . bgplink($2, "neighbors+$lastip+routes") . $3)/e;
s/^( Prefixes Current: \s+)(\d+)(\s+)(\d+)/($1 . bgplink($2, "neighbors+$lastip+advertised-routes") . $3 . bgplink($4, "neighbors+$lastip+routes"))/e;
s/(\s+)(Received)( prefixes:\s+[1-9]\d*)/($1 . bgplink($2, "neighbors+$lastip+routes") . $3)/e;
s/^( Saved \(soft-reconfig\):\s+)(\d+|n\/a)(\s+)(\d+)/($1 . $2 . $3 . bgplink($4, "neighbors+$lastip+received-routes"))/e;
s/( [1-9]\d* )(accepted)( prefixes)/($1 . bgplink($2, "neighbors+$lastip+routes") . $3)/e;
s/^( [1-9]\d* )(accepted|denied but saved)( prefixes consume \d+ bytes)/($1 . bgplink($2, "neighbors+$lastip+received-routes") . $3)/e;
s/^(BGP neighbor is )(\d+\.\d+\.\d+\.\d+)(,)/($1 . pinglink($2) . $3)/e;
s/^( Description: )(.*)$/$1$2<\/B>/;
s/(,\s+remote AS )(\d+)(,)/($1 . as2link($2) . $3)/e;
s/(, local AS )(\d+)(,)/($1 . as2link($2) . $3)/e;
s/( update prefix filter list is )(\S+)/($1 . bgplink($2, "prefix-list+$2"))/e;
s/(Route map for \S+ advertisements is\s+)(\S+)/($1 . bgplink($2, "route-map+$2"))/e;
} elsif ($command =~ /^show bgp ipv6 n\w*\s+([\dA-Fa-f:]+)/i) {
my $ip = $1;
if ($securemode) {
s/((Local|Foreign) port: )\d+/${1}???/g;
}
s/(Prefix )(advertised)( [1-9]\d*)/($1 . bgplink($2, "neighbors+$ip+advertised-routes") . $3)/e;
s/^( [1-9]\d* )(accepted)( prefixes)/($1 . bgplink($2, "neighbors+$ip+routes") . $3)/e;
s/^( Description: )(.*)$/$1$2<\/B>/;
s/(\s+remote AS )(\d+)(,)/($1 . as2link($2) . $3)/e;
s/(\s+local AS )(\d+)(,)/($1 . as2link($2) . $3)/e;
s/( update prefix filter list is )(\S+)/($1 . bgplink($2, "prefix-list+$2"))/e;
s/(Route map for \S+ advertisements is\s+)(\S+)/($1 . bgplink($2, "route-map+$2"))/e;
} elsif ($command =~ /^show bgp n\w*\s+([\d\.A-Fa-f:]+)/i) {
my $ip = $1;
if ($securemode) {
s/^(Peer:\s+[\d\.:]+\+)\d+(\s+AS\s+\d+\s+Local:\s+[\d\.:]+\+)\d+(\s+AS\s+\d+)/${1}???${2}???${3}/g;
}
s/(\s+AS )(\d+)/($1 . as2link($2))/eg;
s/(\s+AS: )(\d+)/($1 . as2link($2))/eg;
s/^( Active prefixes:\s+)(\d+)/($1 . bgplink($2, "neighbors+$ip+routes"))/e;
s/^( Received prefixes:\s+)(\d+)/($1 . bgplink($2, "neighbors+$ip+routes+all"))/e;
s/^( Suppressed due to damping:\s+)(\d+)/($1 . bgplink($2, "neighbors+$ip+routes+damping+suppressed"))/e;
s/^( )(Export)(: )/($1 . bgplink($2, "neighbors+$ip+advertised-routes") . $3)/e;
s/( )(Import)(: )/($1 . bgplink($2, "neighbors+$ip+routes+all") . $3)/e;
} elsif ($command =~ /^show route protocol bgp .* terse/i) {
s/^(.{20} B .{25} >.{15}[^ ]*)( [\d\s,\{\}]+)(.*)$/($1 . as2link($2) . $3)/e;
s/^([\* ] )([\d\.A-Fa-f:\/]+)(\s+)/($1 . bgplink($2, "$2+exact") . $3)/e;
} elsif (($command =~ /^show route protocol bgp /i) || ($command =~ /^show route aspath-regex /i)) {
if ($securemode) {
s/(Task: BGP_[\d\.A-Fa-f:]+\+)\d+/${1}???/g;
}
if (/^ (.)BGP /) {
if ($1 eq "*") {
$best = "\#FF0000";
} else {
$best = "";
}
} elsif (/^[\d\.A-Fa-f:\/\s]{19}([\*\+\- ])\[BGP\//) {
if ($1 =~ /[\*\+]/) {
$best = "\#FF0000";
} elsif ($1 eq "-") {
$best = "\#008800";
} else {
$best = "";
}
} elsif (/^$/) {
$best = "";
}
s/( from )([0-9\.A-Fa-f:]+)/($1 . bgplink($2, "neighbors+$2"))/e;
s/( Source: )([0-9\.A-Fa-f:]+)/($1 . bgplink($2, "neighbors+$2"))/e;
s/(\s+AS: )([\d ]+)/($1 . as2link($2))/eg;
s/(Community: )([\d: ]+)/($1 . community2link($2))/e;
s/(Communities: )([\d: ]+)/($1 . community2link($2))/e;
s/(^\s+AS path: )([\d ]+)/($1 . as2link($2))/e;
s/^([\dA-Fa-f:]+[\d\.A-Fa-f:\/]+)(\s*)/("" . bgplink($1, "$1+exact") . "<\/B>$2")/e;
$_ = "$_" if ($best ne "");
} elsif ($command =~ /bgp/) {
s|^(BGP routing table entry for) (\S+)|$1 $2|;
s|^(Paths:\ .*)\ best\ \#(\d+)
|$1\ best\ \#$2|x
&& do { $best = $2; };
# Fix for IPv6 route output where there are no addional 3 spaces before addresses
if ((/^ Advertised to non peer-group peers:$/) &&
($command =~ / ipv6 /)) {
$count--;
}
if ((/^ (\d+.*)/ && ! /^ \d+\./) || (/^ Local/)) {
$count++;
$_ = as2link($_);
}
$_ = "$_" if $best && $best == $count;
s/( from )([0-9\.A-Fa-f:]+)( )/($1 . bgplink($2, "neighbors+$2") . $3)/e;
s/(Community: )([\d: ]+)/($1 . community2link($2))/e;
s/(Communities: )([\d: ]+)/($1 . community2link($2))/e;
s/(^\s+AS path: )([\d ]+)/($1 . as2link($2))/e;
} elsif ($command =~ /^trace/i) {
s/(\[AS\s+)(\d+)(\])/($1 . as2link($2) . $3)/e;
}
print "$_\n";
}
close(P);
print "
\n";
}
######## Portion of code is borrowed from NCSA WebMonitor "mail" code
sub cgi_decode {
my ($incoming) = @_;
my %FORM;
my $ref = "FORM";
my @pairs = split(/&/, $incoming);
foreach (@pairs) {
my ($name, $value) = split(/=/, $_);
$name =~ tr/+/ /;
$value =~ tr/+/ /;
$name =~ s/%([A-F0-9][A-F0-9])/pack("C", hex($1))/gie;
$value =~ s/%([A-F0-9][A-F0-9])/pack("C", hex($1))/gie;
#### Strip out semicolons unless for special character
$value =~ s/;/$$/g;
$value =~ s/&(\S{1,6})$$/&\1;/g;
$value =~ s/$$/ /g;
$value =~ s/\|/ /g;
$value =~ s/^!/ /g; ## Allow exclamation points in sentences
$FORM{$name} .= $value;
}
return (%FORM);
}
sub read_as_list {
my ($fn) = @_;
local *F;
my %AS;
if (! open(F, $fn)) {
print "\n";
return;
}
while () {
chop;
if (/^#include\s+(.+)$/) {
my %AS2 = &read_as_list($1);
foreach my $key (keys (%AS2)) {
$AS{$key} = $AS2{$key};
}
undef %AS2;
next;
}
next if (/^$/ || /^\s*#/);
my ($asnum, $descr) = split /\t+/;
$asnum =~ s/^[^\d]*(\d+)[^\d]*$/$1/;
$AS{$asnum} = $descr;
}
close(F);
return (%AS);
}
sub as2link {
my ($line) = @_;
my $prefix;
my $suffix;
if ($line =~ /^([^\d]*)([\d\s]*\d)(.*)$/) {
$prefix = $1;
$line = $2;
$suffix = $3;
}
if ($line =~ /:/) {
return($prefix . $line . $suffix);
}
my @aslist = split(/[^\d]+/, $line);
my @separators = split(/[\d]+/, $line);
$line = "";
for (my $i = 0; $i <= $#aslist; $i++) {
my $as = $aslist[$i];
my $sep = "";
$sep = $separators[$i + 1] if ($i <= $#separators);
my $rep;
if (! defined $AS{$as}) {
$rep = $as;
} else {
my $link = "";
if ($AS{$as} =~ /(\w+):/) {
if (defined $whois{$1}) {
$link = sprintf(" HREF=\"$whois{$1}\" TARGET=_lookup", $as);
} elsif (defined $whois{default}) {
$link = sprintf(" HREF=\"$whois{default}\" TARGET=_lookup", $as);
}
}
my $descr = $AS{$as};
$descr = "$2 ($1)" if ($descr =~ /^([^:]+):(.*)$/);
$rep = "$as";
}
$line .= $rep . $sep;
}
$suffix =~ s/(aggregated by )(\d+)( )/($1 . as2link($2) . $3)/e;
return($prefix . $line . $suffix);
}
sub community2link {
my ($line) = @_;
my $prefix;
my $suffix;
my @communitylist = split(/[^\d:]+/, $line);
my @separators = split(/[\d:]+/, $line);
$line = "";
for (my $i = 0; $i <= $#communitylist; $i++) {
my $community = $communitylist[$i];
my $sep = "";
$sep = $separators[$i + 1] if ($i <= $#separators);
my $rep;
if (! defined $AS{$community}) {
$rep = $community;
} else {
my $link = "";
my $descr = $AS{$community};
my $asnum = $1 if ($community =~ /^(\d+):/);
if (defined $AS{$asnum . ":URL"}) {
$rep = "$community (" . html_encode($descr) . ")";
} else {
$rep = html_encode("$community ($descr)");
}
}
$line .= $rep . $sep;
}
return($line);
}
sub bgplink {
my ($txt, $cmd) = @_;
my $link = $lgurl;
my $router = $FORM{router};
$router =~ s/\+/%2B/;
$router =~ s/=/%3D/;
$router =~ s/\&/%26/g;
$link .= "?query=bgp";
$link .= "&protocol=" . $FORM{protocol};
$link .= "&addr=$cmd";
$link .= "&router=$router";
$link =~ s/ /+/g;
return("$txt");
}
sub pinglink {
my ($ip) = @_;
my $link = $lgurl;
my $router = $FORM{router};
$router =~ s/\+/%2B/;
$router =~ s/=/%3D/;
$router =~ s/\&/%26/g;
$link .= "?query=ping";
$link .= "&protocol=" . $FORM{protocol};
$link .= "&addr=$ip";
$link .= "&router=$router";
$link =~ s/ /+/g;
return("$ip");
}
sub html_encode {
($_) = @_;
s|[\r\n]||g;
s|&|&|g;
s|<|<|g;
s|>|>|g;
return $_;
}