#!/usr/local/bin/perl5
#
# $Id: routeflap,v 1.1 1996/04/09 15:50:43 labovit Exp $
#
# Name: flap.pl
# Author: Craig Labovitz (Merit Network, Inc. 1995)
# Description: Munge route_btoa machine parseable output and
#   calculate Curtis-like route flap statistics
#
#
#
# RIB Format:
# status time_last_change total_time_up total_time_down up down change! \
#   [status,time_last_change,time_up,time_down,num_trans,aspath;]+
#

$THRESHOLD = $5;

if ($#ARGV != 1) {
    print "\nUsage: flap.pl input_file output_file\n";
    exit;
}

$INPUT_FILE = $ARGV[0];
$OUTPUT_FILE = $ARGV[1];
open (INPUT, "$ARGV[0]") || die "\nCould not open $ARGV[0]\n";
open (OUTPUT, "> $ARGV[1]") || die "\nCould not open $ARGV[1]\n";

$count = 0;
$START_TIME = 0;

while (<INPUT>) {
    if (!(/^BGP/)) {next;}


    if (($count > 0) && (($count % 10000) == 0)) {
	print "Processed $count routing table changes\n";
    }
    $count++;

    chop ();
    @fields = split (/\|/, $_);
    
    $time = $fields[1];
    if ($START_TIME == 0) {$START_TIME = $time;}

    $TLAST = $time;
    $operation = $fields[2];
    $prefix = $fields[3];
    $aspath = $fields[4];

    &update_rib ($time, $operation, $prefix, $aspath);
    #print "$prefix|$rib{$prefix}\n";
}

&report;




#
# Update routing statistics
#
sub update_rib {
    local ($time, $operation, $prefix, $aspath) = @_;
    local ($entry, @fields);

    #print "$prefix|$rib{$prefix}\n";

    $entry=$rib{$prefix};

    if ($entry eq "") {
	if ($operation eq "W") {
	    print "Error -- withdraw before announce\n";
	    print "$prefix\n";
	    print "$entry\n";
	    #exit;
	}
	$rib{$prefix} = "$operation $time 0 0 0 0 0!$operation,$time,0,0,0,$aspath;";
	return;
    }

    ($main, $as_specific)  = split (/\!/, $entry);

    ($status, $time_last_change, $total_time_up, 
     $total_time_down, $up, $down, $change) = split (/\s+/, $main);

    # route going down
    if (($operation eq "W") && ($status eq "A")) {
	$down += 1;
	$total_time_up += ($time - $time_last_change);
	$total_num_trans += 1;
    }

    elsif (($operation eq "A") && ($status eq "W")) {
	$up += 1;
	$total_time_down += ($time - $time_last_change);
	$total_num_trans += 1;
    }
    elsif (($operation eq "A") && ($status eq "A")) {
	$total_time_up += ($time - $time_last_change);
	$change += 1;
    }
    elsif (($operation eq "W") && ($status eq "W")) {
	print "BOGUS\n$prefix/$entry\n"; 
    }

    $as_specific = 
	&update_as ($as_specific, $time, $time_last_change, $operation, $prefix, $aspath);
    
    $rib{$prefix} = "$operation $time $total_time_up $total_time_down $up $down $change!$as_specific";
}


sub update_as {
    local ($as_info, $time, $time_last, $operation, $prefix, $aspath) = @_;
    local ($astatus, $achange, $atime_up, $atime_down, $anum_trans);
    local ($out);

    @as = split (/;/, $as_info);
    $as_match_flag = 0;

    foreach $i (0... $#as) {
	#print "AS=$as[$i] @as\n";
	($astatus, $achange, $atime_up, $atime_down, $anum_trans, $aaspath) =
	    split (/,/, $as[$i]);

	# mark everyone as down
	if ($operation eq "W") {
	    if ($astatus eq "A") {
		$atime_up += $time - $achange;
		#$anum_trans += 1;
	    }
	    else {
		$atime_down += $time - $achange;
	    }
	    $as[$i]="W,$time,$atime_up,$atime_down,$anum_trans,$aaspath";
	}

	elsif ($operation eq "A") {

	    if ($aspath eq $aaspath) {$as_match_flag = 1;}

	    # implicit withdraw
	    if (($aspath ne $aaspath) && ($astatus eq "A")) {
		$atime_up += $time - $achange;
		#print "$aaspath $time - $achange = $atime_up\n";
		#wprint &ntime($atime_up);
		$anum_trans += 1;
		$astatus = "W";
	    }
	    # comming back up
	    elsif (($aspath eq $aaspath) && ($astatus eq "W")) {
		$atime_down += $time - $achange;
		#$anum_trans += 1;
		$astatus = "A";
	    }
	    # other routes that are down
	    elsif (($aspath ne $aaspath) && ($astatus eq "W")) {
		$atime_down += $time - $achange;
		$astatus = "W";
	    }
	    # something else has changed
	    elsif (($aspath eq $aaspath) && ($astatus eq "A")) {
		$atime_up += $time - $achange;
		$astatus = "A";
	    }
	    else {
		print "SHOULD NOT GET HERE 45 \n";
		print "$astatus [$aspath] [$aaspath]\n";
		exit;
	    }
	    $as[$i]="$astatus,$time,$atime_up,$atime_down,$anum_trans,$aaspath";
	}
    }


    $out = join (';', @as);

    # New AS path
    if (($operation eq "A") && ($as_match_flag == 0)) {
	$out .= ";$operation,$time,0,0,0,$aspath";
    }

    #print "ASINFO=$out\n";

    return $out;
}



sub ntime {
    local ($n) = @_;

    $hours = int ($n / 3600);
    $minutes = int (($n - ($hours * 3600))/ 60);
    $seconds = $n - $hours*3600 - $minutes*60;

    $out = "$hours:$minutes:$seconds";
    return $out;
}


sub dtime {
    local ($n) = @_;

    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdist) =
	localtime ($n);

    $out = "$mon\/$mday\/$year $hour:$min:$sec";
    return $out;
}


# generate report
sub report {
    $count = 0;

    $t1=&dtime($START_TIME);
    $t2=&dtime($TLAST);

    print OUTPUT "#\n# Report covers period $t1 to $t2\n#\n";

    while (($key, $value) = each %rib) {
	$count++;
	#print "\n\n$value"; exit;
	($main, $as_specific)  = split (/\!/, $value);

	($status, $time_last_change, $total_time_up, 
	 $total_time_down, $up, $down, $change) = split (/\s+/, $main);

	# skip things that have not flapped
	if (($up == 0) && ($down == 0) && ($change == 0)) {next;}
	
	# do final accounting
	if ($status eq "A") {
	    $sflag = "UP";
	    $total_time_up += ($TLAST - $time_last_change);
	}
	elsif ($status eq "W") {
	    $sflag = "DOWN";
	    $total_time_down += ($TLAST - $time_last_change);
	}

	if (($up + $down + $change) <= 5) {
	    #print "Next $up + $down + $change = 5\n";
	    next;
	}

	#print "$key $sflag\n";
	print OUTPUT "$key $sflag\n";
	$t1=&ntime($total_time_up);
	$t2=&ntime($total_time_down);
	#print "  reachability= [up $t1, down $t2]\n";
	#print "  transitions= [up $up, down $down, change $change]\n";
	print OUTPUT "  reachability= [up $t1, down $t2]\n";
	print OUTPUT "  transitions= [up $up, down $down, change $change]\n";

	@as = split (/;/, $as_specific);

	foreach $as (@as) {
	    $stflag = "";
	    #print "\n\n  $as\n";
	    ($astatus, $achange, $atime_up, $atime_down, $anum_trans, $aaspath) =
		split (/,/, $as);
	    
	    # do final accounting
	    if ($astatus eq "A") {
		$stflag = "*";
		$atime_up += ($TLAST - $achange);
	    }
	    elsif ($astatus eq "W") {
		$stflag = " ";
		$atime_down += ($TLAST - $achange);
	    }
	    
	    #print "UP $atime_up\n";
	    #print "DOWN $atime_down\n";

	    $t1=&ntime($atime_up);
	    $t2=&ntime($atime_down);
	    chop ($aaspath);

	    #print "   $stflag ($aaspath) reachability= [up $t1, down $t2], transitions= $anum_trans\n";
	    print OUTPUT "   $stflag ($aaspath) reachability= [up $t1, down $t2], transitions= $anum_trans\n";
	}
    }

    print OUTPUT "# (Reported on $count prefixes)\n#\n";
}

