#!/bin/sh ################################################################# ## ## @(#) trace4 v0.5 (c) July 2007 Holger Zuleger ## ## usage: trace4 {|} [-O] [-i int] [-n name] [] ## ## The trace4 command starts a tcpdump for a given amount ## of time or a specified number of packets ## ## The trace is normally stored in a file named ## "name-isotime+timesec.cap", unless the option -O is ## given ## - name defaults to "trace4" but could be specified by ## option -n ## - isotime is the startime (UTC) noted in iso time format ## - timesec is the amount of time in seconds specified ## by ## ## The interface used for the trace could be specified by ## option -i (defaults to eth0) ## The IPv4 address of that interface will be parsed, and ## replaces the string "_myip4_" in the tcpdump expression ## (sorry, no ipv6 support right now) ## ## There are some predefined tcpdump expressions available ## via special options (e.g. --DNSOUT) ## ## EXAMPLES ## ## # trace4 10min --DNS ## # trace4 1h --ALLUDP ## # trace4 20pkt host _myip4_ ## ## BUGS ## There should be a more scalable mechanism to specify ## predefined matching patterns like --ALLUDP ## The sed expression for parsing the ipv4 address of the ## interface contains a character. Make sure that ## the tab char is still there after copying this shell ## skript. ## The skript was written and testet under Linux 2.6.16. ## It runs also successful under FreeBSD 6.1. ## ## History ## (M1) 7. July 2007 (hoz) ## Try to get the real userid out of $USER. On success, ## give up the root privileges after starting tcpdump ## (option -Z of tcpdump) ## ## (M2) 9. July 2007 (hoz) ## trap command added to kill the killafter proc ## on interrupt (required only on freebsd) ## ## (M3) 11. July 2007 (hoz) ## Option -Z and -U of tcdump is available only on ## newer versions ## ################################################################# PATH=/bin:/usr/bin:/sbin/:/usr/sbin LC_ALL=C export LC_ALL progname=`basename $0` # set default values debug=0 time="10m" name="trace4" filesuffix="cap" startdate=`date -u +"%Y%m%dT%H%M%SZ"` int="eth0" snap=0 output=file #(M3) tcpdumpversion=394 test $tcpdumpversion -ge 394 && opt="-U" ################################################################# # time2sec # calculate number of seconds out of a time spec # a time spec could look like e.g. "10min" or "3hours" ################################################################# time2sec() { test "$debug" -ge 1 && echo "time2sec $1" 1>&2 unit=`echo $1 | tr -d "[0-9 ]" | tr "[A-Z]" "[a-z]"` test "$debug" -ge 1 && echo "time2sec: unit = $unit" 1>&2 factor=1 case $unit in m*) factor=60 ;; h*) factor=3600 ;; d*) factor=86400 ;; esac val=`echo $1 | tr -dc "[0-9]"` if test -z "$val" then usage "illegal timespec" else test "$debug" -ge 1 && echo "time2sec: expr $val \* $factor" 1>&2 expr $val \* $factor fi } ################################################################# # killafter # span a process that waits seconds and then sends a ## SIGTERM signal to ################################################################# killafter() { sec=$1 pid=$2 { sleep $sec; kill $pid; } & } ################################################################# # usage [] # prints an (optional) error message and a usage message to stderr ################################################################# usage() { echo "usage: $progname {|} [-d] [-O] [-i int] [-n name] []" 1>&2 echo " := {sec|min|hours|days}" 1>&2 echo " := pkts" 1>&2 echo " -d print out additional debug messages" 1>&2 echo " -O print output to stdout instead of a named file" 1>&2 echo " -i int specify interface (default is $int)" 1>&2 echo " -n name prefix of outputfile (default is $name)" 1>&2 echo " := {--MYUDP|--MYTCP|--ALLUDP|--ALLTCP|--DNSOUT|--DNS|}" 1>&2 echo " := a tcpdump expression (see man tcpdump) " 1>&2 echo " if the expr contains the string \"_myip4_\" it will" 1>&2 echo " be replaced by the ipv4 address of the specified interface" 1>&2 if test "$1" then echo $1 1>&2 fi exit 1 } ## look at time spec if test $# -eq 0 then usage exit 1 fi time=$1 shift ## parse options while test $# -gt 0 do case $1 in -d) debug=1 ;; -i) int=$2; shift ;; -n) name=$2; shift ;; -O) output=stdout ;; --MYUDP) expr="udp and host _myip4_" snap=0; name=myudp ;; --MYTCP) expr="tcp and host _myip4_" snap=0; name=mytcp ;; --ALLUDP) expr="udp" snap=0; name=udp ;; --ALLTCP) expr="tcp" snap=0; name=tcp ;; --DNS) # expr="port domain and host _myip4_" expr="port domain" snap=4096; name=dns ;; --DNSOUT) expr="( dst port domain and src host _myip4_ ) or ( src port domain and dst host _myip4_ )" snap=4096; name=dnsout ;; -*) usage "illegal option $1" ;; *) break ;; esac shift done ## check if expr is already set if test -z "$expr" then expr="$@" fi ## parse time/packet spec tsec=`time2sec $time` || exit 1 pkts="" case $time in *p) pkts="-c $tsec";; esac ## set output file name outfile="" test "$output" = file && outfile="-w $name-$startdate+$tsec.$filesuffix" test $debug -ge 1 && echo "outfile = $outfile " 1>&2 ## check interface ## (be sure that the first brackets in the sed expression contains a and a space!) ipv4=`ifconfig $int | grep inet | sed -n "/inet /s/^[ ]*inet [a-z: ]*\([0-9][0-9.]*\).*/\1/p"` test -z "$ipv4" && usage "couldn't parse ip address of interface $int" ## replace _myip4_ in expr expr=`echo $expr | sed "s/_myip4_/$ipv4/g"` #(M1) (M3) ## try to find out the real user id, and set option -Z of tcpdump on newer versions priv="" if test $tcpdumpversion -ge 394 -a -n "$USER" -a "$USER" != root then priv="-Z $USER" fi ## build up the tcpdump command string cmd="tcpdump -i $int $priv -n -p -s $snap $pkts $opt $outfile $expr" echo "$cmd" 1>&2 ## check permissions case `id -u` in 0) ;; *) usage "must be root to trace packets" ;; esac ## now run tcpdump $cmd & if test -z "$pkts" # if there are no number of packets then killafter $tsec $! # spawn process to kill tcpdump killerpid=$! fi #(M2) trap "kill $killerpid" 2 # kill the killafter proc on interrupt wait # until all childs are dead