#! /bin/sh
# force_close_tcp script
# Use this script to force sockets in FIN_WAIT_2 state to close.
# It works by setting the 2MSL timer in the TCP Protocol Control Block (PCB)
# to a non-zero value. The kernel then begins to decrement this value until
# it reaches zero, at which point the kernel forces a close on the socket and
# deletes the TCP PCB. If both sides of the connection are hung, clearing one
# side will possibly clear the other.
# Changes history:
# Last Change 14th Nov 1995 - Laurent Demailly - dl@hplyot.obspm.fr
# MODIFIED for HPUX by dl (where time_wait is 11 not 10 !!!)
# added a lot of variable for OS dependant configurations,...
# tested on hpux 8.07, but *** NO WARRANTY ***
# used wrongly it can probably CRASH your system...
# check /usr/include/netinet/tcp_fsm.h
# and /usr/include/netinet/tcp_var.h
# many thx to <Steinar.Haug@runit.sintef.no>
# Look at the oooooooooooolddddddd emails of original workers :
# TIMETODEATH expressed in decimal instead of hex
#\t-- mkhaw@teknowledge-vaxc.arpa
# original from cdjohns@NSWC-G.ARPA
# Variable tweaking:
# on HPUX :
# #define TCPS_TIME_WAIT 11 /* in 2*msl quiet wait after close */
TCPS_TIME_WAIT=11
# on sunos comment out that one, other OS check /usr/include/netinet/tcp_fsm.h
# TCPS_TIME_WAIT=10
# you have to change the two "adb" lines according to your OS
# hpux:
ADBREAD="adb /hp-ux /dev/kmem"
ADBWRITE="adb -w /hp-ux /dev/kmem"
# SunOs:
# ADBREAD=adb -k /vmunix /dev/mem
# ADBWRITE=adb -k -w /vmunix /dev/mem
# lastly you'll have to play with the awk below (replace it by grep
# or nothing on sunos,... see below)
# MSLOFFSET is the offset in the tcpcb record for the 2MSL timer.
# <netinet/tcp_var.h> describes the tcpcb record.
# This value is the number of bytes offset, expressed in hexadecimal.
MSLOFFSET=10
# if this change, change the adb dump -- dl
STATEOFFSET=8
# TIMETODEATH is the number of half seconds until the connection is
# closed. This value is expressed in decimal and must be greater
# than zero.
TIMETODEATH=10
# Display netstat to get PCB addresses (first column).
echo 'Active Internet connections
PCB Proto Recv-Q Send-Q Local Address Foreign Address (state)'
# damn hpux' netstat can put the state on a 2nd line :/
# so we dropped the |grep 'FIN_WAIT_[12]|CLOS|LAST_ACK'
# and we use this awk wizardry instead (to be adapted to your OS
# awk and netstat) : -- dl
netstat -An | awk -v ve='FIN_WAIT_[12]|CLOS|LAST_ACK' '$1~ve {print prev,$1} {prev=$0} $7~ve {print $0}'
# (we can also use plain -aAn to all sockets, including the listening
# sockets that this script can close too)
echo
echo 'Choose a PCB to terminate (amongst FIN_WAIT*,CLOS* or LAST_ACK states)'
echo 'Addr = ? \\c'
read addr
echo
# Use adb on kernel to display the PCB of the specified address
$ADBREAD << ADBR_EOF
$addr/"nxt"16t"prev"16t"state"n2Xd
$addr+$MSLOFFSET/"2msl"nd
\\$q
ADBR_EOF
# Check to see if this was the correct address and PCB. state should be
# 8 for LAST_ACK, 9 for FIN_WAIT_2
echo
echo "CHECK: 'nxt' & 'prev' should be both = $addr. '2msl' should be 0."
# change this accordingly with tcp_fsm.h -- dl
echo "'state': >= 6 close in progress, but < 10 waiting for ack or fin"
echo '(6=close_wait, 7=fin_wait_1, 8=closing, 9=last_ack, 10=fin_wait2, 11=timewait)'
echo 'Is this the correct PCB (y/n)? \\c'
read ans
echo
case $ans in
[Yy]*)
\t;;
*)
\techo 'No Changes.'
\texit
\t;;
esac
# Use adb on kernel to set the 2MSL timer for the PCB
# and state=CLOSED (0)
# Eh, use TIME_WAIT instead (10 decimal)
# Eh, use a variable because it is not always 10 !! -- dl
$ADBWRITE << ADBW_EOF
$addr+$MSLOFFSET/w 0t$TIMETODEATH
$addr+$STATEOFFSET/w 0t$TCPS_TIME_WAIT
\\$q
ADBW_EOF
echo
echo "Connection will be terminated in about `expr $TIMETODEATH / 2` seconds."
echo
thanx,
bill