#!/usr/bin/perl -w
# requires perl5
#
# a script to find routing loop in tcpdump output
#   algorithm:
#	- extract TCP SYN packets (to reduce data sets)
#	- find packets with the same initial sequence number but a different
#	  ttl (or hoplimit) value.
#
# usage: tcpdump -n -v 'tcp[13] & 2 != 0' | findloop.pl
#	 "tcpdump -n -v" or "tcpdump -n -v tcp" also works
#	 but it's less efficient
#
# 2000/01/28 initial version written by kjc@csl.sony.co.jp
#
# $Id: findloop.pl,v 1.2 2000/01/28 16:13:25 kjc Exp $
#
use strict;

my(@sessions, $listsize);
my($src, $dst, $flags, $seqno, $ttlname, $ttl, $rest);
my($sref, $ip4id, $i, $offset);

# set the LRU session list size to 100
$listsize = 100;

@sessions = ();

while (<>) {

    # tcpdump output:
    # timestamp src > dst: flags seqno win urgent options (ttl dd, id dd)
    if (/\S+\s+(\S+)\s+>\s+(\S+):\s+(\w+)\s+(\d+).*(ttl|hlim)\s+(\d+)(.*)/) {
	$src     = $1;
	$dst     = $2;
	$flags   = $3;
	$seqno   = $4;
	$ttlname = $5;
	$ttl     = $6;
	$rest    = $7;

	# handle only SYN packets
	next if ($flags ne "S");

	# only ipv4 has id
	$ip4id = 0;
	if ($ttlname eq "ttl") {
	    if ($rest =~ /id\s+(\d+)/) {
		$ip4id = $1;
	    }
	}

	#
	# go through the LRU session list
	#
	$offset = -1;
	for $i (0 .. $#sessions) {
	    $sref = $sessions[$i];
	    if ($sref->[0] == $seqno) {

		next if $sref->[2] ne $src;
		next if $sref->[3] ne $dst;

		# entry matched (same TCP SYN packet)
		$offset = $i;

		if ($ip4id != 0 && $sref->[4] != $ip4id) {
		    # this has a different ipv4 id field value
		    last;
		}

		if ($sref->[1] <= $ttl) {
		    # same or larger ttl; this is retransmission
		    # print "rexmit! $src > $dst: seqno:$seqno ttl:$ttl\n";
		    last;
		}

		if ($sref->[1] == $ttl + 1) {
		    # this is redirect
		    # print "redirect! $src > $dst: seqno:$seqno ttl:$ttl\n";
		    last;
		}

		# warn possible loop
		print "\npossible loop! ttl changed:$sref->[1] to $ttl\n";
		print $sref->[5];
		print $_;
		last;
	    }
	}
    
	#
	# maintain the LRU list
	#
	if ($offset >= 0) {
	    # the matching entry found.  remove this one from the list.
	    splice @sessions, $offset, 1 ;
	}
	elsif ($#sessions >= $listsize) {
	    # discard the least-recently-used entry
	    pop @sessions;
	}
	# append this entry
	unshift(@sessions, [ $seqno, $ttl, $src, $dst, $ip4id, $_ ]);
    }
}





syntax highlighted by Code2HTML, v. 0.9.1