#!/import/misc/bin/perl -sw
# mdups - examine a 'tcpdump -v' trace and print more information
# about packets with duplicate ip id's
# based on Van Jacobson's mdups.sh
# - Bill Fenner, 8Nov94
#
# Recommended usage: tcpdump -e -l -v udp and ip multicast | mdups
# use "mdups -t" to make it run tcpdump for you
# sort +1 -2 +10 -11 groups hosts together.
#
# This script apparently tickles a bug in perl's assoc handling, since
# it periodically makes perl dump core.

$|=1;		# line-buffer stdout
$nuketime = 30;
$do_nuke = 0;
$spp = 2;

#
# to quiet perl -w
($n, $v, $t, &nuke_history) if 0;

# If -t flag, or stdin is a tty, then run tcpdump automagically.
if ($t || -t) {
	$args = "-e -l -v \\( udp and ip multicast and not broadcast \\)";
	if ($n) {
		$args = "-n " . $args;
	}
	if ($#ARGV >= $[) {
		$args .= " and \\( " . join(" ", @ARGV) . " \\)";
	}
print "tcpdump $args\n";
	#open(TCPDUMP, "tcpdump ${nflag} -e -l -v udp and ip multicast and not broadcast |") || die "tcpdump: $!\n";
	open(TCPDUMP, "tcpdump ${args} |") || die "tcpdump: $!\n";
} else {
	*TCPDUMP = *STDIN;
}

$SIG{'ALRM'} = "nuke_history";
alarm($nuketime);

while (<TCPDUMP>) {
	if ($do_nuke) {
		%last = ();
		$do_nuke = 0;
	}
	chop;
	next if /frag/;
#	($tm,$host,$ttl,$id) = (split)[0,1,7,9];
# without -e ^^^
#	($tm,$e,$host,$dst,$ttl,$id) = (split)[0,1,5,7,11,13];
	# ether: timestamp mac-src mac-dst proto len src > dst ttl NNN id NNN
	# fddi: timestamp ? mac-src mac-dst len: encap proto src > dst ttl id
	if (/(\S+)\s+(\d+\s+)?(\S+)\s+\S+\s+(ip|0800)?\s*(\d+):\s+(snap ip )?(\S+)\.[^.]+\s+>\s+(\S+)\.[^.]+.*ttl\s+(\d+).*id\s+(\d+)/) {
		($tm, $e, $size, $host, $dst, $ttl, $id) = ($1, $3, $5, $7, $8, $9, $10);
	} else {
		print STDERR "Failed to parse ", $_, "\n";
		next;
	}
	($h,$m,$s) = split(/:/,$tm);
	$s += $m * 60 + $h * 3600;
	$count = 0;
	if ($i = $last{$host . "/" . $id}) {
		($os,$oe,$ottl,$count) = split("/",$i);
		$count++;
		if ($s - $os > $count * $spp) {	# If the rate is < 1 packet
						# per $spp seconds, assume
						# that it is sequence-wrap
						# and not a duplicate.
			$count = 0;
		} else {
		$toprint = $tm . " " . $host;
		$toprint .= "/$e" if ($v);
		$toprint .= sprintf(" %s oTTL %d delta %d deltatime %d ms id %d ndups %d\n",
			$dst,$ottl,$ottl - $ttl,($s - $os) * 1000,$id,$count);
		if ($e ne $oe) {
			$toprint .= ">>> oe $oe e $e\n";
		}
		# Don't print single dups that are more than 300 seconds 
		# apart.  However, if there are multiple dups or there
		# are single dups more closely spaced, print out the
		# original one as well as the future ones.
		$sg = $host . "/" . $dst;
		if ($count == 1 && (!defined($lastdup{$sg}) || ($s - $lastdup{$sg} > 300))) {
			$toprint{$sg} = $toprint;
#			print "<<< saving $toprint";
		} else {
			if ($toprint{$sg}) {
#				print "<<< saved ", $toprint{$sg};
				print $toprint{$sg};
				undef $toprint{$sg};
			}
			print $toprint;
		}
		$lastdup{$sg} = $s;
		$s=$os; $ttl=$ottl;	# all deltas from original packet
		}
	}
	$last{$host . "/" . $id} = join("/",$s,$e,$ttl,$count);
}

sub nuke_history {
	$do_nuke = 1;

	alarm($nuketime);
	return;
}
