| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- #!/bin/bash
- shopt -s extglob
- # settings
- readonly LOGDIR='/var/log/tcpdump'
- # Name or location of main log file. If not a path, will be saved to LOGDIR
- MAINLOGFILE='main.log'
- ## File is reduced when this size is reached.
- readonly MAINLOGFILEMAXSIZE=$(( 20 * 1024 * 1024 )) ## bytes
- ## This is the extra space given when file is reduced.
- readonly MAINLOGFILEALLOWANCE=$(( 1 * 1024 * 1024 )) ## bytes
- ## Recommended: >= 300
- readonly MAINLOGCHECKINTERVALS=300 ## seconds
- # Location of tcpdump
- readonly TCPDUMP='/usr/sbin/tcpdump'
- ## customize arguments here e.g. (-C 1 "another with spaces")
- readonly MY_IP=$(host `hostname` | cut -d' ' -f4)
- # Arguments for tcpdump
- readonly TCPDUMPARGS=("(src $MY_IP and dst port 22) or
- (dst $MY_IP and src port 22)")
- # Capture log prefix
- readonly TCPDUMPCAPTUREFILEPREFIX='thesis-capture-'
- # Capture log suffix
- readonly TCPDUMPCAPTUREFILESUFFIX=''
- # Frequency to check that tcpdump is running
- readonly TCPDUMPCHECKINTERVALS=60 ## seconds
- # Frequency to try and re-run tcpdump if it failed
- readonly TCPDUMPTRYTIMEOUT=20 ## seconds
- # How long to keep log files around
- readonly OLD=14 ## days
- # How large of a block to copy at a time
- readonly DDBLOCKSIZE=512 ## bytes
- # Location for temporary files
- readonly TEMPDIR='/var/tmp'
- # other variables
- CURRENTDATE=''
- QUIT=false
- TCPDUMPPID=0
- # functions
- # Print to log file
- function log {
- echo "[$(date '+%F %T')] $1" >> "$MAINLOGFILE"
- echo "$1"
- }
- # Check if tcpdump process is running
- # Returns 1 if not found
- function checktcpdump {
- [[ $TCPDUMPPID -ne 0 ]] && [[ -e /proc/$TCPDUMPPID ]] &&
- kill -s 0 "$TCPDUMPPID" 2>/dev/null
- }
- # Creates a new log file for tcpdump and forks a new process
- # Returns 1 on error
- function starttcpdump {
- log "Starting tcpdump..."
- CURRENTDATE=$(date +%F)
- local BASENAME="${TCPDUMPCAPTUREFILEPREFIX}${CURRENTDATE}${TCPDUMPCAPTUREFILESUFFIX}"
- local -a EXISTINGFILES
- # Generate a list of all files matching pattern and put into array
- {
- if [[ BASH_VERSINFO -ge 4 ]]; then
- readarray -t EXISTINGFILES
- else
- EXISTINGFILES=()
- local -i I=0
- while read LINE; do
- EXISTINGFILES[I++]=$LINE
- done
- fi
- } < <(compgen -G "$LOGDIR/${BASENAME}.+([[:digit:]]).pcap*([[:digit:]])")
- # Finds the lowest log file number higher than all existing files
- local NEXT_SESSION=0
- if [[ ${#EXISTINGFILES[@]} -gt 0 ]]; then
- local SESSION_NUMBER
- for FILE in "${EXISTINGFILES[@]}"; do
- SESSION_NUMBER=${FILE%.pcap*}
- SESSION_NUMBER=${SESSION_NUMBER##*.}
- # If session_number is a gigit and >= next_session, then
- # next_session = session_number + 1
- [[ $SESSION_NUMBER == +([[:digit:]]) &&
- SESSION_NUMBER -ge NEXT_SESSION ]] &&
- NEXT_SESSION=$(( SESSION_NUMBER + 1 ))
- done
- fi
- # Forks tcpdump and checks that it succeeded
- local OUTPUTFILE=$LOGDIR/${BASENAME}.$NEXT_SESSION.pcap
- "$TCPDUMP" "${TCPDUMPARGS[@]}" -w "$OUTPUTFILE" &
- if [[ $? -ne 0 ]]; then
- TCPDUMPPID=0
- return 1
- fi
- TCPDUMPPID=$!
- disown "$TCPDUMPPID"
- checktcpdump
- }
- # Try to start tcpdump. If $QUIT, then exit after a failed attempt
- # otherwise try again
- function starttcpdumploop {
- until starttcpdump; do
- log "Error: Failed to start tcpdump. Waiting for $TCPDUMPTRYTIMEOUT seconds before next attempt..."
- read -t $TCPDUMPTRYTIMEOUT
- [[ $QUIT = true ]] && {
- log "Ending tcpdump manager script."
- exit
- }
- done
- }
- # Kill the current tcpdump process and set QUIT to true
- function stoptcpdump {
- log "Stopping tcpdump..."
- kill "$TCPDUMPPID"
- # If tcpdump is still running, sigkill it
- checktcpdump && kill -s 9 "$TCPDUMPPID"
- TCPDUMPPID=0
- QUIT=true
- }
- # Stop tcpdump and start it again
- function restarttcpdump {
- local TMPQUIT=$QUIT
- log "Restarting tcpdump..."
- checktcpdump && stoptcpdump
- QUIT=$TMPQUIT
- starttcpdumploop
- }
- # Sets QUIT to true
- function catchsignals {
- log "Caught a signal."
- QUIT=true
- }
- function main {
- local CAPTUREFILEPATTERN FILE NEWDATE SIZE TEMPFILE
- local -i I
- # Initializes variables and starts tcpdump child
- CAPTUREFILEPATTERN="${TCPDUMPCAPTUREFILEPREFIX}[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]${TCPDUMPCAPTUREFILESUFFIX}.pcap*"
- [[ $MAINLOGFILE != */* ]] && MAINLOGFILE=$LOGDIR/$MAINLOGFILE
- log "Starting tcpdump manager script..."
- [[ $MAINLOGFILEMAXSIZE == +([[:digit:]]) && MAINLOGFILEMAXSIZE -gt DDBLOCKSIZE ]] || {
- echo "MAINLOGFILEMAXSIZE is not valid."
- return 1
- }
- [[ $MAINLOGFILEALLOWANCE == +([[:digit:]]) && MAINLOGFILEALLOWANCE -gt DDBLOCKSIZE && MAINLOGFILEALLOWANCE -lt MAINLOGFILEMAXSIZE ]] || {
- echo "MAINLOGFILEALLOWANCE is not valid."
- return 1
- }
- trap catchsignals SIGQUIT SIGINT SIGKILL SIGTERM
- mkdir -p "$LOGDIR"
- starttcpdumploop
- #
- for (( I = 1;; I = (I + 1) % 10000 )); do
- # Wait 1 second
- ## we have to separate this from the next statement
- ## to ensure proper handling of signals
- read -t 1
- # If QUIT, exit loop
- [[ $QUIT = true ]] && break
- # Handle daily file rotations
- if (( (I % TCPDUMPCHECKINTERVALS) == 0 )); then
- NEWDATE=$(exec date +%F)
- if [[ ! $NEWDATE = "$CURRENTDATE" ]]; then
- log "A new day has come."
- # Delete files older than $OLD
- if read FILE; then
- log "Deleting $OLD-days old files..."
- while
- log "Deleting $FILE..."
- rm -f "$FILE"
- read FILE
- do
- continue
- done
- fi < <(exec find "$LOGDIR" -name "$CAPTUREFILEPATTERN" -daystart -ctime "+$OLD") # or -mtime?
- # Delete empty files
- find "$LOGDIR" -size 0 -delete
- restarttcpdump
- fi
- fi
- # Truncates main log from front if it exceeds maximum
- if (( (I % MAINLOGCHECKINTERVALS) == 0 )); then
- SIZE=$(exec stat --printf=%s "$MAINLOGFILE")
- if [[ $SIZE == +([[:digit:]]) && SIZE -gt MAINLOGFILEMAXSIZE ]]; then
- log "Reducing log data in $MAINLOGFILE..."
- TEMPFILE=$TEMPDIR/tcpdump-$RANDOM.tmp
- SKIP=$(( (SIZE - (MAINLOGFILEMAXSIZE - MAINLOGFILEALLOWANCE)) /
- DDBLOCKSIZE ))
- dd "bs=$DDBLOCKSIZE" "skip=$SKIP" "if=$MAINLOGFILE" "of=$TEMPFILE"
- ## better than mv
- cat "$TEMPFILE" > "$MAINLOGFILE"; rm -f "$TEMPFILE"
- fi
- fi
- done
- # Stop tcpdump
- checktcpdump && stoptcpdump
- log "Ending tcpdump manager script."
- }
- # start
- main
|