#!/usr/bin/perl
#
# findnonpruners.mrouted
# Bill Fenner <fenner@parc.xerox.com> 17 April 1997
# Version 1.1
# $Id$
#
# HOW TO RUN:
# kill -USR1 `cat /etc/mrouted.pid`; sleep 2; kill -USR2 `cat /etc/mrouted.pid`
# cp /usr/tmp/mrouted.{cache,dump} .
# ./findnonpruners.mrouted
#
# HOW IT WORKS:
# Try to find non-pruning multicast routers by analyzing the
# mrouted.{dump,cache} files.  We do this by reading mrouted.dump
# to find out which vifs are the children for each route (e.g. on
# which vif's a broadcast would flow), and then read the mrouted.cache
# to see out which vif's we're actually forwarding each active source.
# We count the number of active source/group pairs for which we would
# be forwarding on a vif if we were broadcasting, and the number for
# which we are currently forwarding.  A ratio of 100% has one of two
# meanings:
# 1) There are members of every group that traffic is flowing through
#    this router towards that vif.  This is a possibility if the numbers
#    printed are small (e.g. "18/18 (100%)").
# 2) There is a non-pruning router in the direction of that vif.  It's
#    not necessarily the direct neighbor on that vif, but it's somewhere
#    downstream of that neighbor.
#
# A count of over 95% is also marked, as "suspicious".  These may be
# measurement errors and "should" be 100%, or may be normal.
# If you have any "suspicious" entries, you should rerun the script
# at various times of the day and see if/how the percentages change.



# First, read mrouted.dump to find all source subnets and their
# children.  Put list of outgoing vifs in $outvifs{$origin},
# ignoring leaves.

open(DUMP, "mrouted.dump") || die "Can't open mrouted.dump: $!\n";
$skipping = 1;
while (<DUMP>) {
	if (/Origin-Subnet/) {
		$skipping = 0;
		next;
	}
# 6   fta0  204.70.158.61   tunnel: 166.48.205.250      1  64      0   leaf
	if (/^\s?(\d+)\s+(\S+)\s+\S+\s+(\S+):\s+(\S+)/) {
		$vif = $1;
		if ($3 eq "tunnel") {
			$vifinfo[$vif] = "tunnel to " . $4;
			@h = gethostbyaddr(pack(C4,split(/\./, $4)),1);
			if (@h) {
				$vifinfo[$vif] .= " ($h[0])";
			}
		} else {
			$vifinfo[$vif] = $2;
		}
	}
	next if ($skipping);
# Origin-Subnet      From-Gateway    Metric Tmr In-Vif  Out-Vifs
# 207.10.165.247/32  192.203.230.241    8    40   7    1  2  3  4  5  6* 8*
# 204.162.116/24                        1    50   0    1 
	if (/^\s(\S+)\s+(\d+\.[\d\.]+)?\s+[^\.\s]+\s+\d+\s+\S+\s+(.*)$/) {
		$origin = $1;
		$vifs=$3;
		# ignore children (*)
		@vifs = grep(!/\*/, split(/\s+/,$vifs));
		$outvifs{$origin} = join(",", @vifs);
	}
}
close(DUMP);

#
# Now, read mrouted.cache .  Count the number of entries for which
# a router is a child, and the number for which we are forwarding
# to that router.

open(CACHE, "mrouted.cache") || die "Can't open mrouted.cache: $!\n";
$skipping = 1;
while (<CACHE>) {
	if (/Origin/) {
		$skipping = 0;
		next;
	}
	next if ($skipping);
# 131.179.49.100/30  224.0.1.1         8m   1d    -  2    1p 3  4  5p 7 
	if (/^\s(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(.*)$/) {
		$origin = $1;
		# Remember boundary-split neighbors
		@bounded = grep(s/b$//, split(/\s+/, $2));
		# ignore pruned (p) and boundary-split (b) neighbors
		@vifs = grep(!/[pb]/, split(/\s+/, $2));
		foreach $v (@vifs) {
			$nforward[$v]++;
		}
		foreach $v (split(/,/, $outvifs{$origin})) {
			if (grep($v == $_, @bounded)) {
				next;
			}
			$nchild[$v]++;
		}
	}
}
close(CACHE);

for ($i = 0; $i <= $#nforward; $i++) {
	next if ($nforward[$i] == 0 || $nchild[$i] == 0);
	if ($nchild[$i] == 0) {
		print "vif $i: $nforward[$i]/$nchild[$i]!? $vifinfo[$i]\n";
		next;
	}
	printf "vif %d: %d/%d (%d%%)", $i, $nforward[$i], $nchild[$i],
				$nforward[$i] * 100 / $nchild[$i];
	if ($nforward[$i] >= $nchild[$i]) {
		print " <- possible non-pruner towards ", $vifinfo[$i];
	} elsif ($nforward[$i] / $nchild[$i] > 0.95) {
		print " <- suspicious amount of traffic towards ", $vifinfo[$i];
	}
	print "\n";
}
