#!/usr/local/bin/perl
# use:
# route_awStat.pl ../BGU1/updates.20011130.2356.gz
# ../BGP/route_awStat.pl ../BGU1/updates.20011130.2356.gz | more

# see anala: /home/dwm/packages/mrt-1.4.8a/src/obj/solaris2.6/bin 
# for original version

# $Id: route_btoa1.pl,v 1.1 2003/07/14 22:45:40 broido Exp $
#
# Name:    route_btoa.pl
# Author:  Craig Labovitz (Merit Network, Inc.) <labovit@merit.edu>
#  A perl version of route_btoa
#
# CHANGE: 10/10/97 <labovit@merit.edu> -- added IPv6 support
#         implements some varient of draft-ietf-idr-bgp4-multiprotocol-01.txt
#
# changed by Andre Broido 2001/12/06 to parse MRT Zebra BGP updates
# as stored on RouteViews sever # http://archive.routeviews.org/
# http://route-views2.oregon-ix.net/bgpdata/2001.12/UPDATES/

%origin  = (2, 'Incomplete', 1, 'EGP', 0, 'IGP');

($IN) = @ARGV;

open IN or die "Could not open $IN $!\n";

$rec = 0;
while ($buf = &get(12)) {
	($time, $type, $subtype, $length) = unpack ("NnnN", $buf);
	if($type != 16){
		&get ($length);
		print STDERR "### Record Type $type != 16 => skipped $length bytes\n";
		next;
    	}

	($sec, $min, $hour, $mday, $mon, $year, @junk) = localtime ($time);
	$mon++; $year =~ s/^1// if $year > 100;
    
	$year = $year % 1000;
	$yday = sprintf("%2.2d-%2.2d-%2.2d", $year, $mon, $day);
	$time = sprintf("%2.2d:%2.2d:%2.2d", $hour, $min, $sec);


	undef @awpfs;
	$peer = $peeras = $asp = undef;
	$wtdcur = $anncur = 0;

	$debug = 1;
	$med = "-";

	$rec++;
	$ret = decode_bgp ($length);
	next if $ret > 0;

    	$title = sprintf("rec %6d $time $yday $time", $rec);
	print STDERR "$rec) last prefix $pf origin $orig wdr=$wtdcur ann=$anncur\n$tit$str\n" if $rec % 1 == 0;
	$tit = sprintf(
		"$title mess.type $messtype hdr 12 len %d  fr_byte %d to_byte %d\n",
		$length, $tot_bytes, $tot_bytes + $length + 12 -1) if $debug;
	print "$tit$str\n";
	$tot_bytes += $length + 12;

	last if $rec > 2000;
} # end while

$nor = scalar keys %origCn;
printf(STDERR "%-10s %d %-8s %d\n\n", "Total_rec", $rec, "Origins", $nor);

printf(STDERR "%-10s %s\n", "type AS", "Count");
for $tas (sort {$a<=>$b or $a cmp $b} keys %weirdAttPeerCn){
	$cn = $weirdAttPeerCn{$tas};
	$sumwei += $cn;
	printf(STDERR "%-10s %d\n", $tas, $cn);
}
printf(STDERR "%-10s %d\n", "Total", $sumwei);

#=======================================================

sub decode_bgp {
    local ($size) = @_;
    $read = 0; $origin = 0;
    undef $str;
    
    # standard BGP4
	$buf = &get (8); # note: 4 bytes skipped (interface index, addr.family)
	($srcas, $dstas) = unpack ("SS", $buf);
	$peeras = $srcas;

	$buf = &get (4);
	(@srcip) = unpack ("C4", $buf);
	$srcip = join ('.', @srcip);
	$peer = $srcip;
	$buf = &get (4);
	(@dstip) = unpack ("C4", $buf);
	$dstip = join ('.', @dstip);
	$str .= sprintf("FROM: $srcip AS$srcas\n")
		if $srcas != 0 and $debug;
	$str .= sprintf("TO:   $dstip AS$dstas\n") if $dstas != 0 and $debug;

	$buf = &get (16); # skip header
	$buf = &get (2);
	$bgplen = unpack ("S", $buf);

	if($bgplen + 16 != $length){
		print STDERR
		"rec.$record (at tot.bytes $tot_bytes) bgplen $bgplen + 16 != MRT length $length \n";
         	print STDERR " Skipping message remainder ###### \n";
         	&get($length - $read);
		$ncorrupt++;
        	return 1;
	}
	
	$buf = &get(1);
	$messtype = unpack ("C1", $buf);
#	$str .= sprintf("$title mess.type $messtype last byte $tot_bytes\n") if $debug;
	$str .= sprintf("TYPE: BGP/UPDATE\n") if $messtype == 2 and $debug;
	die "Message type is not than of update, 2\n"  if $messtype != 2;

# --------------- WITHDRAWAL PART OF A PACKET --------------------------

	$buf = &get (2);
	$totalwithbyte = unpack ("S", $buf);

    if ($totalwithbyte > 0) {
	$str .= sprintf("WITHDRAW:\n") if $debug;
    }

    while ($totalwithbyte > 0) {
	$buf = &get(1);
	$bitlen = unpack ("C1", $buf);
	$bytes = &bytes ($bitlen);
	$buf = &get ($bytes);
	@addr = unpack ("C$bytes", $buf);
	$addr = join ('.', @addr);
	$pf = "$addr/$bitlen";
	push @awpfs, "-$pf";
	$wtdcur++;
	$wtdtot++;
#	$pfwCn{$pf}++;
	$str .= " $pf" if $debug;
	$str .= "\n" if $debug;
	$totalwithbyte -= (1 + $bytes);
    }

# ---------------- ATTRIBUTE PART OF A PACKET ---------------------

    # total path attributes field
    $buf = &get (2);
    $total = unpack ('S', $buf);

    while ($total > 0) {
	#attr-flag | attr_type_code | length
	$buf = &get (2);
	($flag, $type) = unpack ("C2", $buf);

	# extended length
	if (vec ($flag, 3, 1)) { # used to be 0 instead of 3, extracting "optional" bit
	    $buf = &get (2);
	    $alen = unpack ("S", $buf);
	    $total -= 4;
	}
	# one byte length
	else {
	    $buf = &get (1);
	    $alen = unpack ("C", $buf);
	    $total -= 3;
	}

#	printf "flags Ox%x type $type attr.len $alen\n", $flag;

	$total -= $alen;

	if ($type == 1) {
	    $buf = &get(1);
	    $org = unpack ("C", $buf);
	    $str .= "ORIGIN: $origin{$org}\n" if $debug;
	}
	elsif ($type == 2) {
		$nas = 0;
		$pas = undef;
		$str .= "ASPATH: " if $debug;
		while ($alen > 0) {
			$buf = &get (2);
			($seg_type, $seg_len) = unpack ("CC", $buf);
	
			$segtypeCn{$seg_type}++;
			$alen -= 2;

			$str .= "[ " if $seg_type == 1 and $debug;   

			while ($seg_len-- > 0) {
				$buf = &get (2);
				$alen -= 2;
				$as = unpack ("S", $buf);
				$nas++;
				$str .= "$as " if $debug;
				$asp .= "$as~";
				$rasp .= "$as~" if $as ne $pas;
				$pas = $as;
			}

			$str .= "] " if $seg_type == 1 and $debug;
		}
		$orig = $pas;
		$origCn{$orig}++;
		$str .= "\n" if $debug;
		chop $asp; chop $rasp;
	}
	elsif ($type == 3) {
	    $buf = &get (4);
	    @addr = unpack ("C4", $buf);
	    $addr = join ('.', @addr);
	    $str .="NEXT_HOP: $addr\n" if $debug;
	}
	elsif ($type == 4) {
	    $buf = &get (4); 
	    $med = unpack ("N", $buf);
	    $str .= "MULTI_EXIT_DISC: $med\n" if $debug;
	}
	elsif ($type == 6) {
	    $str .= "ATOMIC_AGGREGATE\n" if $debug;
	}
	elsif ($type == 7) {
	    $buf = &get (2); 
	    $as = unpack ("S", $buf);
	    $buf = &get (4); 
	    @addr = unpack ("C4", $buf);
	    $addr = join ('.', @addr);
	    $str .= "AGGREGATOR: $as $addr\n" if $debug;
	}
	elsif ($type == 8) {
	    $str .= "COMMUNITIES: " if $debug;
	    while ($alen > 0) {
		$buf = &get (4);
		($com_as, $com_val) = unpack ("SS", $buf);
		$alen -= 4;
	        $str .= "$com_as:$com_val " if $debug;
	   }
	   $str .= "\n" if $debug;
	}
	else {
	    	if($weirdAttCn{$type}++ == 0){
			print STDERR "\n ##### Rec $rec: First instance of attr.type $type\n";
			print STDERR "Unknown attribute code type $type ($weirdAttCn{$type} inst.so far)";
			print STDERR " Skipping message remainder ###### \n";
		}
		$tas = sprintf("%-2d %5d", $type, $peeras);
	    	$weirdAttPeerCn{$tas}++;
	    	&get($length - $read);
	    return $type;
	}
    } # end attributes, loop while($total > 0)

# ---------- ANNOUNCED PREFIXES ----------------

# print "Read $read bytes, size $size\n";

    if ($read < $size) {
	$str.= "ANNOUNCE:\n" if $debug;
    }
    while ($read < $size) {
	$buf = &get (1);
	$bitlen = unpack ("C", $buf);

	$bytes = &bytes ($bitlen);
	$buf = &get ($bytes);
	@addr = unpack ("C$bytes", $buf);
	$addr = join ('.', @addr);
	$pf = "$addr/$bitlen";
	push @awpfs, $pf;
	$str .= " $pf\n" if $debug;
	$anntot++;
	$anncur++;
    }
}

#==================================================================

# someday I will learn how to do integer division in Perl... (sigh)
sub bytes {
    local ($length) = @_;

    if ($length <= 8) { return (1);}
    if ($length <= 16) {return (2);}
    if ($length <= 24) {return (3);}
    
    return (4);
}

sub get {
    local ($bytes) = @_;
	if($bytes < 0){ 
		print "Negative #bytes = $bytes\n";
		 exit;
		return 0;
	}
    
    $buf = "";
    if (($n = read (IN, $buf, $bytes)) != $bytes) {
	if (($n == 0) && ($bytes == 12)) {return 0;}
	print "read $n bytes, expected $bytes\n";
	 exit;
	return 0;
    }
    
    $read += $bytes;
    return ($buf);
}


# cat updates.20011130.2356 | ../C/hdump4 2000 | more
# nbyte 2000
#  byte # byte hex        INT               HEX         CHAR        INT2        INT2<<8
#       0 00000000   60   8 141  73     3c 08 8d 49     <..I    15368 36169      60  2189 
#       4 00000004    0  16   0   1     00 10 00 01     ....       16     1   18688  4096 
#       8 00000008    0   0   0  80     00 00 00 50     ...P        0    80     256     0 
#      12 0000000c   13 221  25  47     0d dd 19 2f     .../     3549  6447   20493 56601 
#      16 00000010    0   0   0   1     00 00 00 01     ....        0     1   12032     0 
#      20 00000014  208  51 113 254     d0 33 71 fe     .3q.    53299 29182     464 13169 
#      24 00000018  198  32 162 102     c6 20 a2 66     . .f    50720 41574   65222  8354 
#      28 0000001c  255 255 255 255     ff ff ff ff     ....    65535 65535   26367 65535 
#      32 00000020  255 255 255 255     ff ff ff ff     ....    65535 65535   65535 65535 
#      36 00000024  255 255 255 255     ff ff ff ff     ....    65535 65535   65535 65535 
#      40 00000028  255 255 255 255     ff ff ff ff     ....    65535 65535   65535 65535 
#      44 0000002c    0  64   2   0     00 40 02 00     .@..       64   512   65280 16386 
#      48 00000030    0   0  37  64     00 00 25 40     ..%@        0  9536       0    37 
#      52 00000034    1   1   0  64     01 01 00 40     ...@      257    64   16385   256 
#      56 00000038    2  12   2   5     02 0c 02 05     ....      524   517   16386  3074 
#      60 0000003c   13 221   4 215     0d dd 04 d7     ....     3549  1239    1293 56580 
#      64 00000040   73 117  30  92     49 75 1e 5c     Iu.\    18805  7772   55113 29982 
#      68 00000044   78  53  64   3     4e 35 40 03     N5@.    20021 16387   23630 13632 
#      72 00000048    4 208  51 113     04 d0 33 71     ..3q     1232 13169     772 53299 
#      76 0000004c  254 192   8   8     fe c0 08 08     ....    65216  2056   29182 49160 
#      80 00000050   13 221   8  68     0d dd 08 44     ...D     3549  2116    2061 56584 
#      84 00000054   13 221 120 120     0d dd 78 78     ..xx     3549 30840   17421 56696 
#      88 00000058   21 209  41 184     15 d1 29 b8     ..).     5585 10680   30741 53545 
#      92 0000005c   60   8 141  74     3c 08 8d 4a     <..J    15368 36170   47164  2189 
#      96 00000060    0  16   0   1     00 10 00 01     ....       16     1   18944  4096 
#     100 00000064    0   0   0  76     00 00 00 4c     ...L        0    76     256     0 
#     104 00000068   13 221  25  47     0d dd 19 2f     .../     3549  6447   19469 56601 
#     108 0000006c    0   0   0   1     00 00 00 01     ....        0     1   12032     0 
#     112 00000070  208  51 113 254     d0 33 71 fe     .3q.    53299 29182     464 13169 
#     116 00000074  198  32 162 102     c6 20 a2 66     . .f    50720 41574   65222  8354 
#     120 00000078  255 255 255 255     ff ff ff ff     ....    65535 65535   26367 65535 

