#!/usr/bin/perl
#
# findnonpruners.cisco
# Bill Fenner <fenner@parc.xerox.com> 17 April 1997
# Version 1.1
# $Id: findnonpruners.cisco,v 1.2 1997/06/05 21:07:30 fenner Exp $
#
# HOW TO RUN:
# Put the output of "show ip mroute" into a file
# findnonpruners.cisco file
#
# HOW IT WORKS:
# Try to find non-pruning multicast routers by analyzing the output
# of "show ip mroute".  We do this by counting the number of (S,G)
# entries in which an interface is listed against the number of
# (S,G) entries in which that interface is listed as in Forward state.
# A ratio of 100% has one of four meanings:
# 1) The interface is in sparse mode.  This script tries to ignore
#    sparse mode interfaces, but it's untested.  Sparse mode interfaces
#    foil this script's heuristic, so if it points towards a sparse mode
#    interface you can just ignore it.
# 2) The interface has no multicast router neighbors, only group members.
# 3) There are members of every group that traffic is flowing through
#    this router towards that interface.  This is a possibility if the numbers
#    printed are small (e.g. "18/18 (100%)").
# 4) There is a non-pruning router in the direction of that interface.  It's
#    not necessarily the direct neighbor on that interface, 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.
# If they indicate PIM dense-mode flooding, the numbers should not stay
# consistently high, but if they indicate a non-pruner they will continue
# to be flagged.


# Walk through "show ip mroute" output, noting how many times each
# interface is mentioned at all, and how many times it's in Forward
# state.
while (<>) {
	if (/^(\S+) is (up|down), line/) {
		$curint = $1;
		next;
	}
	if (/^\s+Description: (.*)/) {
		$ifinfo{$curint} = $1;
		next;
	}
	if (/^\(([^,]+),\s+([^)]+)/) {
		$cursrc = $1;
		$curgroup = $2;
		next;
	}
	next if ($cursrc eq "*");
#    Ethernet0, Forward/Dense, 00:55:18/00:00:00
	if (/^\s+(\S+), (\S+)\/(\S+),/) {
		next if ($3 eq "Sparse");
		$nchild{$1}++;
		$nforward{$1}++ if ($2 eq "Forward");
	}
}

sub byinterface {
	($c, $d) = ($a =~ /(\D+)(\d+)/);
	($e, $f) = ($b =~ /(\D+)(\d+)/);

	if ($c eq $e) {
		$d <=> $f;
	} else {
		$c cmp $e;
	}
}

foreach $i (sort byinterface keys %nforward) {
	next if ($nforward{$i} == 0 || $nchild{$i} == 0);
	if ($nchild{$i} == 0) {
		print "$i: $nforward{$i}/$nchild{$i}!? $ifinfo{$i}\n";
		next;
	}
	printf "%s: %d/%d (%d%%)", $i, $nforward{$i}, $nchild{$i},
				$nforward{$i} * 100 / $nchild{$i};
	if ($nforward{$i} >= $nchild{$i}) {
		print " <- possible non-pruner $ifinfo{$i}";
	} elsif ($nforward{$i} / $nchild{$i} > 0.95) {
		print " <- suspicious amount of traffic $ifinfo{$i}";
	}
	print "\n";
}
