#!/bin/bash
# This file is released to the public domain

# To use this file you will need:
# bash, and various Unix like commands - for Windows use Cygwin: http://www.cygwin.com/
# neato and dot - part of the graphviz package: http://www.research.att.com/sw/tools/graphviz/
# twireshark - the network analyser: http://www.wireshark.org

fmt=graph
opfmt=ps
show=0
pass=
usage="usage: `basename $0` [ -dh ] [-g type] [-f format] [-P pass] cap_file [ output-file ]"

while getopts dhg:f:P: opt
do
  case $opt in
  d) show=1;;
  g) fmt=$OPTARG;;
  f) opfmt=$OPTARG;;
  P) pass=-$OPTARG;;
  h) echo $usage
  	echo "-d	display output (only if op format is ps)"
	echo "-h	display this help"
	echo "-g type"
	echo "	set graph type: 'graph' or 'digraph' (default is graph)"
	echo "-f format"
	echo "	set output format - see dot/neato output formats. (default is ps)"
	echo "	eg 'ps', 'gif', 'jpg'"
	echo "-P pass"
	echo "	pass through the given options to dot/neato. A leading '-' is added"
	echo "cap-file"
	echo "	network traffic capture file, in a format readable by wireshark"
	echo "output-file"
	echo "	name of the file in which to write the graph (default is cap-file"
	echo "	with '.cap' removed and format added)"
	exit ;;
  *) echo $usage; exit 1;;
  esac
done


ipf=${!OPTIND}
opfn=$(($OPTIND+1))
opf=${!opfn}
	
if [ "$ipf" == "" ]; then echo $usage&& exit; fi

if [ "$opf" == "" ]; then opf=`basename $ipf .cap`.$opfmt; fi

# twireshark creates a list of packets
# cut pulls off the two addresses
# sed removes the arrow to protect it from later munges
# sort puts duplicates next to each other
# uniq removes adjacent duplicates

twireshark -r $ipf -N mnt | awk '$4=="->"{print $3,"###",$5;}' | sort |uniq > raw

# Create the connections list:
#  sed munges the names of the nodes
#  sed prefixes node names that start with a digit
sed 's/[-\.:()]/_/g' < raw | sed 's/\(^[0-9_][0-9_]*\)/IP\1/g;s/ \([0-9_][0-9_]*\)/IP\1/g'  > cons

# Create the nodes list:
#  sed puts all node names on seperate lines
#  sort | uniq removes duplicates
#  sed duplicates the names on the same line, with one inside a label attribute
#  sed munges the names in an identical manner to the connection list munge above, but only in the first name
#    including prefixing names that start with a digit
sed 's/ ### /\n/' < raw | sort | uniq | sed 's/\(.\+\)/\1 [label=\"\1\"]/' | sed ':loop;s/[-\.:()]\(.* \[lab\)/_\1/;t loop;s/\(^[0-9]\)/IP\1/g' > labels

# this section builds the neato/dot command file
# If neato then the file has to start "graph" if dot then "digraph"
# "overlap" is redundant in dot.
# The connection seperator in neato is "--", in dot it's "->"
# If the Broadcast box is required then we make it rectangular

if [ "$fmt" == "graph" ]; then
	cat > cmd.dot <<END
	graph matrix {
	overlap=false
	size="11,8.5"
END
	if grep -q Broadcast < cons; then
		cat >> cmd.dot <<END
		Broadcast [shape=box]
END
	fi
	cat labels >> cmd.dot
	sed 's/###/--/' < cons >> cmd.dot
	cmd="neato"
else
	cat > cmd.dot <<END
	digraph matrix {
	size="11,8.5"
END
	if grep -q Broadcast < cons; then
		cat >> cmd.dot <<END
		Broadcast [shape=box]
END
	fi
	cat labels >> cmd.dot
	sed 's/###/->/' < cons >> cmd.dot
	cmd="dot"
fi

cat >> cmd.dot <<END
}
END

rm -rf raw cons labels

# create the graph
$cmd -T$opfmt $pass <cmd.dot >$opf

# show the graph, if required and if it's postscript
if [ $show == 1 ]; then
	if [ $opfmt == ps ]; then
		ghostview $opf &
	fi
fi

