#### ASpath-tree v.4.2 - Released on Thu APR 17 2003, h.16:58:12
#### File: lib/getbgptable.pl Last modified on Tue DEC 10 2002, h.10:00:33

# Routines to collect IPv6 BGP AS path information from the supported
# router platforms. Currently these include: cisco (IOS>12), zebra and juniper.
# The information coming from the router (via RSH or local command) is parsed to
# produce a normalized BGP table and to count route entries and ASs.
# Lines of the normalized table are in the format "flags prefix ASpath".
# Flags is a 5 character field given by:
#     1st char: status code (* valid, s suppressed)
#     2nd char: status code (> best, d damped, h history, r RIB failure, - nothing noted)
#     3rd char: status code (i internal, - nothing noted)
#     4th char: network directly connected (0 no, 1 yes)
#     5th char: origin code (i IGP, e EGP, ? incomplete)
# ASpaths are AS numbers separated by a blanck space.
# Numbers of route entries and ASs are stored in a hash array with possible keys:
#     valid
#     suppressed
#     best
#     damped
#     history
#     RIBfailure
#     internal
#     ASs  (all the ASs in the router BGP table)
# Finally this routine produces a text file containing the raw version of the router table
# and if required log files of both the raw router table and the normalized table.
sub get_bgp_table {
	my ($parser, $command, $line) = ('', '', '');
	my (@lines) = ();

	if ($ROUTER =~ /^JUNIPER$/i) {
		$parser = "juniper";
		$STATUSCODES = "*&gt; active";
		$command = "show route table inet6.0 all terse";
		if ($ROUTERUSER eq 'root') {$command = 'cli '.$command}
		$command = "$RSHDIR/rsh $ROUTERADDR -l $ROUTERUSER \"$command\"";
	} elsif ($ROUTER =~ /^ZEBRA$/i) {
		$parser = "zebra";
		$STATUSCODES = "s suppressed, d damped, h history, * valid, &gt; best";
		$command = "vtysh -e \'show ipv6 bgp\'";
		unless ($LOCAL) {$command = "$RSHDIR/rsh $ROUTERADDR -l $ROUTERUSER \"$command\""}
	} elsif ($ROUTER =~ /^ZEBRA93$/i) {
		$parser = "cisco";
		$STATUSCODES = "s suppressed, d damped, h history, * valid, &gt; best";
		$command = "vtysh -e \'show bgp ipv6\'";
		unless ($LOCAL) {$command = "$RSHDIR/rsh $ROUTERADDR -l $ROUTERUSER \"$command\""}
	} elsif ($ROUTER =~ /^CISCO$/i || !$ROUTER) {
		$ROUTER = "CISCO";
		$parser = "cisco";
		$STATUSCODES = "s suppressed, d damped, h history, * valid, &gt; best, r RIB-failure";
		$command = "$RSHDIR/rsh $ROUTERADDR -l $ROUTERUSER \"show bgp ipv6\"";
	} else {&log_it_and_die("ERROR in configuration file: bad value for parameter ROUTER = $ROUTER!")}

# execute command to get the IPv6 BGP table
	open(OUTPUT,"$command |");
	for (<OUTPUT>) {
		s/\x0d//g;
		push(@lines, $_);
	}
	close(OUTPUT);
	chomp(@lines);
# Write log and for web files (raw table)
	open(TXTFILE,"> $BGPTABLEFILE");
	foreach $line (@lines) {unless(grep(/$ROUTERADDR/,$line)) {print TXTFILE "$line\n"}}
	close(TXTFILE);
	if($DEBUG) {
		open(LOGFILE1,"> $LOGDIR/router-table.txt");
		foreach $line (@lines) {unless(grep(/$ROUTERADDR/,$line)) {print LOGFILE1 "$line\n"}}
		close(LOGFILE1);
	}
# Create array of route entries and hash of summary figures
	my ($irtable, $inum) = &$parser(@lines);
# Write log file (normalized table)
	if($DEBUG) {
		open(LOGFILE1,"> $LOGDIR/normalized-table.txt");
		foreach (@rtable) {print LOGFILE1 "$_\n"}
		close(LOGFILE1);
	}
	return($irtable, $inum);
}

# parser for the IPv6 BGP table on a Cisco router (zebra >= 0.93)
sub cisco {
	my (@lines) = @_;
	my ($prefix, $line, $ASNs, $element, $aspath) = ('', '', '', '', '', '');
	my ($flag1, $flag2, $flag3) = (' ',  ' ', ' ');
	my $flags = '     ';
	my $status_code = '   ';
	my $onlink = 0;
	my ($iass, $irtable);
	my $nexthop_col_start = 20;
	my $path_col_start = 61;
	my $state = 9;
	my ($iass, $irtable);
	my %num = ();

	foreach $line (@lines) {
		if ($state == 9 && $line =~ /.*LocPrf.*/) {$state = 0}
		elsif ($state == 0 && $line =~ /^[\ssdhr\*]{1}[sdhr\s\>]{1}[i\s]{1}.*/) {
			if ($line =~ /^.{3}(\S+).*/) {$prefix = $1}
			$flag1 = substr($line, 0, 1);
			$flag2 = substr($line, 1, 1);
			$flag3 = substr($line, 2, 1);
			$status_code = $flag1.$flag2.flag3;
			$num{total}++;
			if ($status_code =~ /\*/) {$num{valid}++}
			if ($status_code =~ /s/)  {$num{suppressed}++}
			if ($status_code =~ /\>/) {$num{best}++}
			if ($status_code =~ /d/)  {$num{damped}++}
			if ($status_code =~ /h/)  {$num{history}++}
			if ($status_code =~ /r/)  {$num{RIBfailure}++}
			if ($status_code =~ /i/)  {$num{internal}++}
			if ($flag2 eq '>') {
				if (substr($line, $nexthop_col_start-1, 2) =~ /\s\S/) {
					if (substr($line, $nexthop_col_start-1, 3) eq ' ::') {$onlink = 1}
					if (substr($line, ($path_col_start -1), 2) =~ /\s\S/) {
						$aspath = substr($line, $path_col_start);
					}
					else {$state = 1}
				}
				else {$state = 2}
			}
		}
		elsif ($state == 1 && substr($line, ($path_col_start -1), 2) =~ /\s\S/) {
			$aspath = substr($line, $path_col_start);
		}
		elsif ($state == 2 && substr($line, ($nexthop_col_start -1), 2) =~ /\s\S/) {
			if (substr($line, $nexthop_col_start-1, 3) eq ' ::') {$onlink = 1}
			if (substr($line, ($path_col_start -1), 2) =~ /\s\S/) {
				$aspath = substr($line, $path_col_start);
			}
			else {$state = 1}
		}
		if ($prefix && $aspath) {
			($iass, $irtable) = &format_normaltable_entry($prefix, $aspath, $flag1, $flag2, $flag3, $onlink, $iass, $irtable);
			$prefix = ''; $aspath = ''; $onlink = 0;
			$state = 0;
		}
	}
	$num{ASs} = @$iass;
	return($irtable,\%num);
}

# parser for the IPv6 BGP table on a ZEBRA router (zebra < 0.93x)
sub zebra {
	my (@lines) = @_;
	my ($prefix, $line, $ASNs, $element, $aspath) = ('', '', '', '', '', '');
	my ($flag1, $flag2, $flag3) = (' ',  ' ', ' ');
	my $flags = '     ';
	my $status_code = '   ';
	my $onlink = 0;
	my ($iass, $irtable);
	my $nexthop_col_start = 5;
	my $path_col_start = 65;
	my $state = 9;
	my ($iass, $irtable);
	my %num = ();

	foreach $line (@lines) {
		if ($state == 9 && $line =~ /.*LocPrf.*/) {$state = 0}
		elsif ($state == 0 && $line =~ /^[\ssdh\*]{1}[sdh\s\>]{1}[i\s]{1}.*/) {
			if ($line =~ /^.{3}(\S+).*/) {$prefix = $1}
			$flag1 = substr($line, 0, 1);
			$flag2 = substr($line, 1, 1);
			$flag3 = substr($line, 2, 1);
			$status_code = $flag1.$flag2.flag3;
			$num{total}++;
			if ($status_code =~ /\*/) {$num{valid}++}
			if ($status_code =~ /s/)  {$num{suppressed}++}
			if ($status_code =~ /\>/) {$num{best}++}
			if ($status_code =~ /d/)  {$num{damped}++}
			if ($status_code =~ /h/)  {$num{history}++}
			if ($status_code =~ /r/)  {$num{RIBfailure}++}
			if ($status_code =~ /i/)  {$num{internal}++}
			if ($flag2 eq '>') {
				if (substr($line, ($path_col_start -1), 2) =~ /\s\S/) {
					$aspath = substr($line, $path_col_start);
					$state = 1;
				}
				else {&log_it("ZEBRA Parser: next hop not found where expected in line:\n   $line\n")}
			}
		}
		elsif ($state == 1 && substr($line, ($nexthop_col_start -1), 2) =~ /\s\S/) {
			if (substr($line, $nexthop_col_start-1, 3) eq ' ::') {$onlink = 1}
			$state = 0;
		}
		if ($prefix && $aspath) {
			($iass, $irtable) = &format_normaltable_entry($prefix, $aspath, $flag1, $flag2, $flag3, $onlink, $iass, $irtable);
			$prefix = ''; $aspath = ''; $onlink = 0;
			$state = 0;
		}
	}
	$num{ASs} = @$iass;
	return($irtable,\%num);
}

# parser for the IPv6 BGP table on a JUNIPER router
sub juniper {
	my (@lines) = @_;
	my ($prefix, $line, $ASNs, $element, $aspath) = ('', '', '', '', '', '');
	my ($flag1, $flag2, $flag3) = (' ',  ' ', ' ');
	my $flags = '     ';
	my $status_code = '   ';
	my $onlink = 0;
	my ($iass, $irtable);
	my ($iass, $irtable);
	my %num = ();

	foreach $line (@lines) {
		if ($line =~ /^[\s\*]{1}\s{1}(.*)/) {
			@elements = grep(/\S/, split(/\s+/, $1));
			$flag2 = substr($line, 0, 1);
			if ($elements[0] =~ /\//) {
				$prefix = $elements[0];
				if ($flag2 eq '*') {
					$num{total}++;
					$num{best}++;
					$flag1 = '*';
					$flag2 = '>';
					$flag3 = '-';
					if ($elements[1] eq 'D' && $prefix !~ /^fe80/i) {#to be checked
						$onlink = 1;
						$aspath = '?';
					}
					elsif ($elements[1] eq 'B') {
						$line =~ /\>{1}\S+\s+(.*)$/;
						$aspath = $1;
					}
				}
			}
			elsif ($flag2 eq '*') {
				$num{total}++;
				$num{best}++;
				$flag1 = '*';
				$flag2 = '>';
				$flag3 = '-';
				if ($elements[0] eq 'D' && $prefix !~ /^fe80/i) {#to be checked
					$onlink = 1;
					$aspath = '?';
				}
				elsif ($elements[0] eq 'B') {
					$line =~ /\>{1}\S+\s+(.*)$/;
					$aspath = $1;
				}
			}
			else {
				$num{total}++;
			}
		}
		if ($prefix && $aspath) {
			($iass, $irtable) = &format_normaltable_entry($prefix, $aspath, $flag1, $flag2, $flag3, $onlink, $iass, $irtable);
			$prefix = ''; $aspath = ''; $onlink = 0;
		}
	}
	$num{valid} = $num{total};
	$num{ASs} = @$iass;
	return($irtable,\%num);
}

# format entry for normalized table, deals with AS confederations and AS sets (all routers)
sub format_normaltable_entry {
	my ($prefix, $aspath, $flag1, $flag2, $flag3, $onlink, $iass, $irtable) = @_;
	my ($elemet, $ASNs, $origincode, $flags);
	my (@elements);

	if ($aspath =~ /\([\d\s]+\)\s*(\S+.*)/) {$aspath = $1} # hide AS confederation info
	@elements = grep(/\S/, split(/\s+/, $aspath));
	foreach $element (@elements) {
		if ($element =~ /^\d+$/) {
			unless (grep(/^$element$/, @$iass)) {push(@$iass, $element)}
			$ASNs .= " $element";
		}
		elsif ($element =~ /^\{/) {
			my @tmp = split(/\,/, substr($element, 1, length($element)-2));
			my $newelement = '';
			foreach my $el (@tmp) {
				$newelement .= "$el\#";
				unless (grep(/$el/, @$iass)) {push(@$iass, $el)}
			}
			chop($newelement);
			$ASNs .= " $newelement";
		}
		elsif ($element =~ /[\?ie]{1}/) {$origincode = $element}
	}
	if ($flag3 eq ' ') {$flag3 = '-'}
	$flags = $flag1.$flag2.$flag3.$onlink.$origincode;
	push (@$irtable, "$flags $prefix $ASNs");

	return($iass, $irtable);
}

(1);

#### ASpath-tree v.4.2 - Released on Thu APR 17 2003, h.16:58:12
#### File: lib/getbgptable.pl Last modified on Tue DEC 10 2002, h.10:00:33
