windows-nt/Source/XPSP1/NT/net/tcpip/driver/tcp/tcb.c

3544 lines
112 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/********************************************************************/
/** Microsoft LAN Manager **/
/** Copyright(c) Microsoft Corp., 1990-1993 **/
/********************************************************************/
/* :ts=4 */
//** TCB.C - TCP TCB management code.
//
// This file contains the code for managing TCBs.
//
#include "precomp.h"
#include "addr.h"
#include "tcp.h"
#include "tcb.h"
#include "tlcommon.h"
#include "tcpconn.h"
#include "tcpsend.h"
#include "tcprcv.h"
#include "info.h"
#include "tcpcfg.h"
#include "tcpdeliv.h"
#include "pplasl.h"
#include <acd.h>
#include <acdapi.h>
HANDLE TcbPool;
HANDLE SynTcbPool;
extern HANDLE TcpRequestPool;
//Special spinlock and table for TIMED_WAIT states.
extern CTELock *pTWTCBTableLock;
extern CTELock *pSynTCBTableLock;
Queue *TWTCBTable;
PTIMER_WHEEL TimerWheel;
// This queue is used for faster scavenging
Queue *TWQueue;
ulong numtwqueue = 0;
ulong numtwtimedout = 0;
extern
void
ClassifyPacket(TCB *SendTCB);
void
TCBTimeoutdpc(
#if !MILLEN
PKDPC Dpc,
#else // !MILLEN
PVOID arg0,
#endif // MILLEN
PVOID DeferredContext,
PVOID arg1,
PVOID arg2
);
extern CTELock *pTCBTableLock;
#if MILLEN
#define MAX_TIMER_PROCS 1
#else // MILLEN
#define MAX_TIMER_PROCS 32
#endif // !MILLEN
uint TCPTime;
uint CPUTCPTime[MAX_TIMER_PROCS];
uint TCBWalkCount;
uint PerTimerSize = 0;
uint Time_Proc = 0;
TCB **TCBTable;
TCB *PendingFreeList=NULL;
SYNTCB *PendingSynFreeList=NULL;
Queue *SYNTCBTable;
CACHE_LINE_KSPIN_LOCK PendingFreeLock;
#define NUM_DEADMAN_TICKS MS_TO_TICKS(1000)
#define NUM_DEADMAN_TIME 1000
uint MaxHashTableSize = 512;
uint DeadmanTicks;
//choose reasonable defaults
uint NumTcbTablePartitions = 4;
uint PerPartitionSize = 128;
uint LogPerPartitionSize = 7;
CTETimer TCBTimer[MAX_TIMER_PROCS];
ULONGLONG systemtime;
ULONGLONG delta=0;
extern IPInfo LocalNetInfo;
extern SeqNum g_CurISN;
//
// All of the init code can be discarded.
//
int InitTCB(void);
void UnInitTCB(void);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, InitTCB)
#pragma alloc_text(INIT, UnInitTCB)
#endif
extern CTEBlockStruc TcpipUnloadBlock;
BOOLEAN fTCBTimerStopping = FALSE;
extern ACD_DRIVER AcdDriverG;
VOID
TCPNoteNewConnection(
IN TCB * pTCB,
IN CTELockHandle Handle
);
uint
GetTCBInfo(PTCP_FINDTCB_RESPONSE TCBInfo,
IPAddr Dest,
IPAddr Src,
ushort DestPort,
ushort SrcPort
);
void
TCBTimeoutdpc(
#if !MILLEN
PKDPC Dpc,
#else // !MILLEN
PVOID arg0,
#endif // MILLEN
PVOID DeferredContext,
PVOID arg1,
PVOID arg2
)
/*++
Routine Description:
System timer dpc wrapper routine for TCBTimeout().
Arguments:
Return Value:
None.
--*/
{
CTETimer *Timer;
Timer = (CTETimer *) DeferredContext;
#if MILLEN
// Set the timer again to make it periodic.
NdisSetTimer(&Timer->t_timer, MS_PER_TICK);
#endif // MILLEN
(*Timer->t_handler)((CTEEvent *)Timer, Timer->t_arg);
}
void
CTEInitTimerEx(
CTETimer *Timer
)
/*++
Routine Description:
Initializes a CTE Timer variable for periodic timer.
Arguments:
Timer - Timer variable to initialize.
Return Value:
None.
--*/
{
Timer->t_handler = NULL;
Timer->t_arg = NULL;
#if !MILLEN
KeInitializeDpc(&(Timer->t_dpc), TCBTimeoutdpc, Timer);
KeInitializeTimerEx(&(Timer->t_timer),NotificationTimer);
#else !MILLEN
NdisInitializeTimer(&Timer->t_timer, TCBTimeoutdpc, Timer);
#endif // MILLEN
}
void *
CTEStartTimerEx(
CTETimer *Timer,
unsigned long DueTime,
CTEEventRtn Handler,
void *Context
)
/*++
Routine Description:
Sets a CTE Timer for expiration for periodic timer.
Arguments:
Timer - Pointer to a CTE Timer variable.
DueTime - Time in milliseconds after which the timer should expire.
Handler - Timer expiration handler routine.
Context - Argument to pass to the handler.
Return Value:
0 if the timer could not be set. Nonzero otherwise.
--*/
{
#if !MILLEN
LARGE_INTEGER LargeDueTime;
ASSERT(Handler != NULL);
//
// Convert milliseconds to hundreds of nanoseconds and negate to make
// an NT relative timeout.
//
LargeDueTime.HighPart = 0;
LargeDueTime.LowPart = DueTime;
LargeDueTime = RtlExtendedIntegerMultiply(LargeDueTime, 10000);
LargeDueTime.QuadPart = -LargeDueTime.QuadPart;
Timer->t_handler = Handler;
Timer->t_arg = Context;
KeSetTimerEx(
&(Timer->t_timer),
LargeDueTime,
MS_PER_TICK,
&(Timer->t_dpc)
);
#else // !MILLEN
ASSERT(Handler != NULL);
Timer->t_handler = Handler;
Timer->t_arg = Context;
NdisSetTimer(&Timer->t_timer, DueTime);
#endif // MILLEN
return((void *) 1);
}
//* ReadNextTCB - Read the next TCB in the table.
//
// Called to read the next TCB in the table. The needed information
// is derived from the incoming context, which is assumed to be valid.
// We'll copy the information, and then update the context value with
// the next TCB to be read.
// The table lock for the given index is assumed to be held when this
// function is called.
//
// Input: Context - Poiner to a TCPConnContext.
// Buffer - Pointer to a TCPConnTableEntry structure.
//
// Returns: TRUE if more data is available to be read, FALSE is not.
//
uint
ReadNextTCB(void *Context, void *Buffer)
{
TCPConnContext *TCContext = (TCPConnContext *) Context;
TCPConnTableEntry *TCEntry = (TCPConnTableEntry *) Buffer;
CTELockHandle Handle;
TCB *CurrentTCB;
TWTCB *CurrentTWTCB;
Queue *Scan;
uint i;
if (TCContext->tcc_index >= TCB_TABLE_SIZE) {
CurrentTWTCB = (TWTCB *) TCContext->tcc_tcb;
CTEStructAssert(CurrentTWTCB, twtcb);
TCEntry->tct_state = TCB_TIME_WAIT + TCB_STATE_DELTA;
TCEntry->tct_localaddr = CurrentTWTCB->twtcb_saddr;
TCEntry->tct_localport = CurrentTWTCB->twtcb_sport;
TCEntry->tct_remoteaddr = CurrentTWTCB->twtcb_daddr;
TCEntry->tct_remoteport = CurrentTWTCB->twtcb_dport;
if (TCContext->tcc_infosize > sizeof(TCPConnTableEntry)) {
((TCPConnTableEntryEx*)TCEntry)->tcte_owningpid = 0;
}
i = TCContext->tcc_index - TCB_TABLE_SIZE;
Scan = QNEXT(&CurrentTWTCB->twtcb_link);
if (Scan != QEND(&TWTCBTable[i])) {
TCContext->tcc_tcb = (TCB *) QSTRUCT(TWTCB, Scan, twtcb_link);
return TRUE;
}
} else {
CurrentTCB = TCContext->tcc_tcb;
CTEStructAssert(CurrentTCB, tcb);
CTEGetLock(&CurrentTCB->tcb_lock, &Handle);
if (CLOSING(CurrentTCB))
TCEntry->tct_state = TCP_CONN_CLOSED;
else
TCEntry->tct_state = (uint) CurrentTCB->tcb_state + TCB_STATE_DELTA;
TCEntry->tct_localaddr = CurrentTCB->tcb_saddr;
TCEntry->tct_localport = CurrentTCB->tcb_sport;
TCEntry->tct_remoteaddr = CurrentTCB->tcb_daddr;
TCEntry->tct_remoteport = CurrentTCB->tcb_dport;
if (TCContext->tcc_infosize > sizeof(TCPConnTableEntry)) {
((TCPConnTableEntryEx*)TCEntry)->tcte_owningpid =
(CurrentTCB->tcb_conn) ? CurrentTCB->tcb_conn->tc_owningpid
: 0;
}
CTEFreeLock(&CurrentTCB->tcb_lock, Handle);
if (CurrentTCB->tcb_next != NULL) {
TCContext->tcc_tcb = CurrentTCB->tcb_next;
return TRUE;
}
}
// NextTCB is NULL. Loop through the TCBTable looking for a new
// one.
i = TCContext->tcc_index + 1;
if (i >= TCB_TABLE_SIZE) {
// If the index is greater than the TCB_TABLE_SIZE,
// Then it must be referring to the TIM_WAIT table.
// Get the correct hash index and search in TW table.
i = i - TCB_TABLE_SIZE;
while (i < TCB_TABLE_SIZE) {
if (!EMPTYQ(&TWTCBTable[i])) {
TCContext->tcc_tcb = (TCB *)
QSTRUCT(TWTCB, QHEAD(&TWTCBTable[i]), twtcb_link);
TCContext->tcc_index = i + TCB_TABLE_SIZE;
return TRUE;
break;
} else
i++;
}
} else {
//Normal table scan
while (i < TCB_TABLE_SIZE) {
if (TCBTable[i] != NULL) {
TCContext->tcc_tcb = TCBTable[i];
TCContext->tcc_index = i;
return TRUE;
break;
} else
i++;
}
// We exhausted normal table move on to TIM_WAIT table
i = i - TCB_TABLE_SIZE;
while (i < TCB_TABLE_SIZE) {
if (!EMPTYQ(&TWTCBTable[i])) {
TCContext->tcc_tcb = (TCB *)
QSTRUCT(TWTCB, QHEAD(&TWTCBTable[i]), twtcb_link);
TCContext->tcc_index = i + TCB_TABLE_SIZE;
return TRUE;
break;
} else
i++;
}
}
TCContext->tcc_index = 0;
TCContext->tcc_tcb = NULL;
return FALSE;
}
//* ValidateTCBContext - Validate the context for reading a TCB table.
//
// Called to start reading the TCB table sequentially. We take in
// a context, and if the values are 0 we return information about the
// first TCB in the table. Otherwise we make sure that the context value
// is valid, and if it is we return TRUE.
// We assume the caller holds the TCB table lock.
//
// Input: Context - Pointer to a TCPConnContext.
// Valid - Where to return information about context being
// valid.
//
// Returns: TRUE if data in table, FALSE if not. *Valid set to true if the
// context is valid.
//
uint
ValidateTCBContext(void *Context, uint * Valid)
{
TCPConnContext *TCContext = (TCPConnContext *) Context;
uint i;
TCB *TargetTCB;
TCB *CurrentTCB;
TWTCB *CurrentTWTCB;
Queue *Scan;
i = TCContext->tcc_index;
TargetTCB = TCContext->tcc_tcb;
// If the context values are 0 and NULL, we're starting from the beginning.
if (i == 0 && TargetTCB == NULL) {
*Valid = TRUE;
do {
if ((CurrentTCB = TCBTable[i]) != NULL) {
CTEStructAssert(CurrentTCB, tcb);
TCContext->tcc_index = i;
TCContext->tcc_tcb = CurrentTCB;
return TRUE;
}
i++;
} while (i < TCB_TABLE_SIZE);
// We have TCBs in time wait table also...
i = 0;
do {
if (!EMPTYQ(&TWTCBTable[i])) {
CurrentTWTCB =
QSTRUCT(TWTCB, QHEAD(&TWTCBTable[i]), twtcb_link);
CTEStructAssert(CurrentTWTCB, twtcb);
TCContext->tcc_index = i + TCB_TABLE_SIZE;
TCContext->tcc_tcb = (TCB *) CurrentTWTCB;
return TRUE;
}
i++;
} while (i < TCB_TABLE_SIZE);
return FALSE;
} else {
// We've been given a context. We just need to make sure that it's
// valid.
if (i >= TCB_TABLE_SIZE) {
// If the index is greater than the TCB_TABLE_SIZE,
// Then it must be referring to the TIM_WAIT table.
// Get the correct hash index and search in TW table.
i = i - TCB_TABLE_SIZE;
if (i < TCB_TABLE_SIZE) {
Scan = QHEAD(&TWTCBTable[i]);
while (Scan != QEND(&TWTCBTable[i])) {
CurrentTWTCB = QSTRUCT(TWTCB, Scan, twtcb_link);
if (CurrentTWTCB == (TWTCB *) TargetTCB) {
*Valid = TRUE;
return TRUE;
break;
} else {
Scan = QNEXT(Scan);
}
}
}
} else {
//Normal table
if (i < TCB_TABLE_SIZE) {
CurrentTCB = TCBTable[i];
while (CurrentTCB != NULL) {
if (CurrentTCB == TargetTCB) {
*Valid = TRUE;
return TRUE;
break;
} else {
CurrentTCB = CurrentTCB->tcb_next;
}
}
}
}
// If we get here, we didn't find the matching TCB.
*Valid = FALSE;
return FALSE;
}
}
//* FindNextTCB - Find the next TCB in a particular chain.
//
// This routine is used to find the 'next' TCB in a chain. Since we keep
// the chain in ascending order, we look for a TCB which is greater than
// the input TCB. When we find one, we return it.
//
// This routine is mostly used when someone is walking the table and needs
// to free the various locks to perform some action.
//
// Input: Index - Index into TCBTable
// Current - Current TCB - we find the one after this one.
//
// Returns: Pointer to the next TCB, or NULL.
//
TCB *
FindNextTCB(uint Index, TCB * Current)
{
TCB *Next;
ASSERT(Index < TCB_TABLE_SIZE);
Next = TCBTable[Index];
while (Next != NULL && (Next <= Current))
Next = Next->tcb_next;
return Next;
}
//* ResetSendNext - Set the sendnext value of a TCB.
//
// Called to set the send next value of a TCB. We do that, and adjust all
// pointers to the appropriate places. We assume the caller holds the lock
// on the TCB.
//
// Input: SeqTCB - Pointer to TCB to be updated.
// NewSeq - Sequence number to set.
//
// Returns: Nothing.
//
void
ResetSendNext(TCB *SeqTCB, SeqNum NewSeq)
{
TCPSendReq *SendReq;
uint AmtForward;
Queue *CurQ;
PNDIS_BUFFER Buffer;
uint Offset;
CTEStructAssert(SeqTCB, tcb);
ASSERT(SEQ_GTE(NewSeq, SeqTCB->tcb_senduna));
// The new seq must be less than send max, or NewSeq, senduna, sendnext,
// and sendmax must all be equal. (The latter case happens when we're
// called exiting TIME_WAIT, or possibly when we're retransmitting
// during a flow controlled situation).
ASSERT(SEQ_LT(NewSeq, SeqTCB->tcb_sendmax) ||
(SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendnext) &&
SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendmax) &&
SEQ_EQ(SeqTCB->tcb_senduna, NewSeq)));
AmtForward = NewSeq - SeqTCB->tcb_senduna;
if ((AmtForward == 1) && (SeqTCB->tcb_flags & FIN_SENT) &&
!((SeqTCB->tcb_sendnext-SeqTCB->tcb_senduna) > 1) &&
(SEQ_EQ(SeqTCB->tcb_sendnext, SeqTCB->tcb_sendmax))) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
"tcpip: trying to set sendnext for FIN_SENT\n"));
//CheckTCBSends(SeqTCB);
//allow retransmits of this FIN
SeqTCB->tcb_sendnext = NewSeq;
SeqTCB->tcb_flags &= ~FIN_OUTSTANDING;
return;
}
if ((SeqTCB->tcb_flags & FIN_SENT) &&
(SEQ_EQ(SeqTCB->tcb_sendnext, SeqTCB->tcb_sendmax)) &&
((SeqTCB->tcb_sendnext - NewSeq) == 1)) {
//There is only FIN that is left beyond sendnext.
//allow retransmits of this FIN
SeqTCB->tcb_sendnext = NewSeq;
SeqTCB->tcb_flags &= ~FIN_OUTSTANDING;
return;
}
SeqTCB->tcb_sendnext = NewSeq;
// If we're backing off send next, turn off the FIN_OUTSTANDING flag to
// maintain a consistent state.
if (!SEQ_EQ(NewSeq, SeqTCB->tcb_sendmax))
SeqTCB->tcb_flags &= ~FIN_OUTSTANDING;
if (SYNC_STATE(SeqTCB->tcb_state) && SeqTCB->tcb_state != TCB_TIME_WAIT) {
// In these states we need to update the send queue.
if (!EMPTYQ(&SeqTCB->tcb_sendq)) {
CurQ = QHEAD(&SeqTCB->tcb_sendq);
SendReq = (TCPSendReq *) STRUCT_OF(TCPReq, CurQ, tr_q);
// SendReq points to the first send request on the send queue.
// Move forward AmtForward bytes on the send queue, and set the
// TCB pointers to the resultant SendReq, buffer, offset, size.
while (AmtForward) {
CTEStructAssert(SendReq, tsr);
if (AmtForward >= SendReq->tsr_unasize) {
// We're going to move completely past this one. Subtract
// his size from AmtForward and get the next one.
AmtForward -= SendReq->tsr_unasize;
CurQ = QNEXT(CurQ);
ASSERT(CurQ != QEND(&SeqTCB->tcb_sendq));
SendReq = (TCPSendReq *) STRUCT_OF(TCPReq, CurQ, tr_q);
} else {
// We're pointing at the proper send req now. Break out
// of this loop and save the information. Further down
// we'll need to walk down the buffer chain to find
// the proper buffer and offset.
break;
}
}
// We're pointing at the proper send req now. We need to go down
// the buffer chain here to find the proper buffer and offset.
SeqTCB->tcb_cursend = SendReq;
SeqTCB->tcb_sendsize = SendReq->tsr_unasize - AmtForward;
Buffer = SendReq->tsr_buffer;
Offset = SendReq->tsr_offset;
while (AmtForward) {
// Walk the buffer chain.
uint Length;
// We'll need the length of this buffer. Use the portable
// macro to get it. We have to adjust the length by the offset
// into it, also.
ASSERT((Offset < NdisBufferLength(Buffer)) ||
((Offset == 0) && (NdisBufferLength(Buffer) == 0)));
Length = NdisBufferLength(Buffer) - Offset;
if (AmtForward >= Length) {
// We're moving past this one. Skip over him, and 0 the
// Offset we're keeping.
AmtForward -= Length;
Offset = 0;
Buffer = NDIS_BUFFER_LINKAGE(Buffer);
ASSERT(Buffer != NULL);
} else
break;
}
// Save the buffer we found, and the offset into that buffer.
SeqTCB->tcb_sendbuf = Buffer;
SeqTCB->tcb_sendofs = Offset + AmtForward;
} else {
ASSERT(SeqTCB->tcb_cursend == NULL);
ASSERT(AmtForward == 0);
}
}
CheckTCBSends(SeqTCB);
}
//* TCPAbortAndIndicateDisconnect
//
// Abortively closes a TCB and issues a disconnect indication up the the
// transport user. This function is used to support cancellation of
// TDI send and receive requests.
//
// Input: ConnectionContext - The connection ID to find a TCB for.
//
// Returns: Nothing.
//
BOOLEAN
TCPAbortAndIndicateDisconnect(
uint ConnectionContext,
PVOID Irp,
uint Rcv,
CTELockHandle inHandle
)
{
TCB *AbortTCB;
CTELockHandle ConnTableHandle, TCBHandle;
TCPConn *Conn;
VOID *CancelContext, *CancelID;
PTCP_CONTEXT tcpContext;
PIO_STACK_LOCATION irpSp;
irpSp = IoGetCurrentIrpStackLocation((PIRP)Irp);
tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
Conn = GetConnFromConnID(ConnectionContext, &ConnTableHandle);
if (Conn != NULL) {
CTEStructAssert(Conn, tc);
AbortTCB = Conn->tc_tcb;
if (AbortTCB != NULL) {
// If it's CLOSING or CLOSED, skip it.
if ((AbortTCB->tcb_state != TCB_CLOSED) && !CLOSING(AbortTCB)) {
CTEStructAssert(AbortTCB, tcb);
CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
CTEFreeLock(&(Conn->tc_ConnBlock->cb_lock), TCBHandle);
if (AbortTCB->tcb_state == TCB_CLOSED || CLOSING(AbortTCB)) {
CTEFreeLock(&AbortTCB->tcb_lock, ConnTableHandle);
CTEFreeLock(&tcpContext->EndpointLock, inHandle);
return FALSE;
}
if (Rcv) {
if ((AbortTCB->tcb_rcvhndlr == BufferData)) {
if (AbortTCB->tcb_rcvhead) {
TCPRcvReq *RcvReq, *PrevReq;
TDI_STATUS Status = TDI_SUCCESS;
PrevReq = STRUCT_OF(TCPRcvReq, &AbortTCB->tcb_rcvhead, trr_next);
RcvReq = PrevReq->trr_next;
ASSERT(AbortTCB->tcb_rcvhead == AbortTCB->tcb_currcv);
while (RcvReq) {
CTEStructAssert(RcvReq, trr);
if (RcvReq->trr_context == Irp) {
PrevReq->trr_next = RcvReq->trr_next;
AbortTCB->tcb_currcv = AbortTCB->tcb_rcvhead;
if (AbortTCB->tcb_rcvtail == RcvReq) {
AbortTCB->tcb_rcvtail = PrevReq;
}
if (AbortTCB->tcb_currcv == NULL) {
if (AbortTCB->tcb_rcvind != NULL &&
AbortTCB->tcb_indicated == 0) {
AbortTCB->tcb_rcvhndlr =
IndicateData;
} else {
AbortTCB->tcb_rcvhndlr = PendData;
}
}
AbortTCB->tcb_flags |= IN_RCV_IND;
CTEFreeLock(&AbortTCB->tcb_lock, ConnTableHandle);
CTEFreeLock(&tcpContext->EndpointLock, inHandle);
(*RcvReq->trr_rtn) (RcvReq->trr_context, TDI_CANCELLED, RcvReq->trr_amt);
CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
AbortTCB->tcb_flags &= ~IN_RCV_IND;
if ((AbortTCB->tcb_rcvind != NULL) &&
(AbortTCB->tcb_indicated == 0) &&
(AbortTCB->tcb_pendingcnt != 0)) {
REFERENCE_TCB(AbortTCB);
IndicatePendingData(AbortTCB, RcvReq, TCBHandle);
SendACK(AbortTCB);
CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
DerefTCB(AbortTCB, TCBHandle);
} else {
if (AbortTCB->tcb_rcvhead == NULL) {
if (AbortTCB->tcb_rcvind != NULL &&
AbortTCB->tcb_indicated == 0) {
AbortTCB->tcb_rcvhndlr =
IndicateData;
} else {
AbortTCB->tcb_rcvhndlr =
PendData;
}
}
CTEFreeLock(&AbortTCB->tcb_lock, TCBHandle);
FreeRcvReq(RcvReq);
}
return FALSE;
}
PrevReq = RcvReq;
RcvReq = RcvReq->trr_next;
}
}
//try to find this irp in exprcv queue
if (AbortTCB->tcb_exprcv) {
TCPRcvReq *RcvReq, *PrevReq;
PrevReq = STRUCT_OF(TCPRcvReq, &AbortTCB->tcb_exprcv, trr_next);
RcvReq = PrevReq->trr_next;
while (RcvReq) {
CTEStructAssert(RcvReq, trr);
if (RcvReq->trr_context == Irp) {
//(*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0);
PrevReq->trr_next = RcvReq->trr_next;
FreeRcvReq(RcvReq);
CTEFreeLock(&AbortTCB->tcb_lock, ConnTableHandle);
//
// Account for reference count
// on endoint, as we are not calling
// TCPDataRequestComplete() for this
// request.
//
tcpContext->ReferenceCount--;
CTEFreeLock(&tcpContext->EndpointLock, inHandle);
return TRUE;
}
PrevReq = RcvReq;
RcvReq = RcvReq->trr_next;
}
}
}
}
CancelContext = ((PIRP)Irp)->Tail.Overlay.DriverContext[0];
CancelID = ((PIRP)Irp)->Tail.Overlay.DriverContext[1];
CTEFreeLock(&tcpContext->EndpointLock, ConnTableHandle);
REFERENCE_TCB(AbortTCB);
if (!Rcv) {
CTEFreeLock(&AbortTCB->tcb_lock,TCBHandle);
//Call ndis cancel packets routine to free up
//queued send packets
(*LocalNetInfo.ipi_cancelpackets) (CancelContext, CancelID);
CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
}
AbortTCB->tcb_flags |= NEED_RST; // send a reset if connected
TryToCloseTCB(AbortTCB, TCB_CLOSE_ABORTED, inHandle);
RemoveTCBFromConn(AbortTCB);
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPAbortAndIndicateDisconnect, indicating discon\n"
));
}
NotifyOfDisc(AbortTCB, NULL, TDI_CONNECTION_ABORTED);
CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
DerefTCB(AbortTCB, TCBHandle);
// TCB lock freed by DerefTCB.
return FALSE;
} else
CTEFreeLock(&(Conn->tc_ConnBlock->cb_lock), ConnTableHandle);
} else
CTEFreeLock(&(Conn->tc_ConnBlock->cb_lock), ConnTableHandle);
}
CTEFreeLock(&tcpContext->EndpointLock, inHandle);
return FALSE;
}
//* ProcessSynTcbs
//
// Called from timeout routine to handle syntcbs in syntcb table
// Retransmits SYN if rexnitcnt has not expired. Else removes the
// syntcb from the table and frees it.
// Input: Processor number that is used to select the syntcb space
// to handle.
//
// Returns: Nothing.
//
void
ProcessSynTcbs(uint Processor)
{
Queue *Scan;
SYNTCB *SynTCB;
uint i,maxRexmitCnt,StartIndex=0;
CTELockHandle TableHandle = DISPATCH_LEVEL,SYNTableHandle = DISPATCH_LEVEL;
CTELockHandle Handle;
StartIndex = Processor * PerTimerSize;
for (i = StartIndex; i < MIN(TCB_TABLE_SIZE, StartIndex+PerTimerSize); i++) {
Scan = QHEAD(&SYNTCBTable[i]);
while (Scan != QEND(&SYNTCBTable[i])){
SynTCB = QSTRUCT(SYNTCB, Scan, syntcb_link);
CTEGetLockAtDPC(&SynTCB->syntcb_lock, &TableHandle);
CTEStructAssert(SynTCB, syntcb);
Scan = QNEXT(Scan);
if (TCB_TIMER_RUNNING(SynTCB->syntcb_rexmittimer)) {
// The timer is running.
if (--(SynTCB->syntcb_rexmittimer) == 0) {
maxRexmitCnt = MIN(MaxConnectResponseRexmitCountTmp, MaxConnectResponseRexmitCount);
// Need a (greater or equal) here, because, we want to stop when the count reaches the max.
if ( SynTCB->syntcb_rexmitcnt++ >= maxRexmitCnt) {
SynTCB->syntcb_refcnt++;
CTEFreeLockFromDPC(&SynTCB->syntcb_lock, TableHandle);
CTEGetLockAtDPC(&pSynTCBTableLock[SynTCB->syntcb_partition], &SYNTableHandle);
CTEGetLockAtDPC(&SynTCB->syntcb_lock, &TableHandle);
if (SynTCB->syntcb_flags & IN_SYNTCB_TABLE) {
REMOVEQ(&SynTCB->syntcb_link);
SynTCB->syntcb_flags &= ~IN_SYNTCB_TABLE;
}
CTEFreeLockFromDPC(&pSynTCBTableLock[SynTCB->syntcb_partition], &SYNTableHandle);
SynTCB->syntcb_refcnt--;
DerefSynTCB(SynTCB,TableHandle);
CTEGetLockAtDPC(&SynAttLock.Lock, &Handle);
//
// We have put the connection in the closed state.
// Decrement the counters for keeping track of half
// open connections
//
ASSERT((TCPHalfOpen != 0) && (TCPHalfOpenRetried != 0));
TCPHalfOpen--;
TCPHalfOpenRetried--;
if (((TCPHalfOpen <= TCPMaxHalfOpen) ||
(TCPHalfOpenRetried <= TCPMaxHalfOpenRetriedLW)) &&
(MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
MaxConnectResponseRexmitCountTmp = MAX_CONNECT_RESPONSE_REXMIT_CNT;
}
CTEFreeLockFromDPC(&SynAttLock.Lock, Handle);
} else {
SynTCB->syntcb_sendnext = SynTCB->syntcb_senduna;
SendSYNOnSynTCB(SynTCB,TableHandle);
// Figure out what our new retransmit timeout should be. We
// double it each time we get a retransmit
SynTCB->syntcb_rexmit = MIN(SynTCB->syntcb_rexmit << 1,
MAX_REXMIT_TO);
}
} else {
CTEFreeLockFromDPC(&SynTCB->syntcb_lock, TableHandle);
}
} else {
CTEFreeLockFromDPC(&SynTCB->syntcb_lock, TableHandle);
}
}
}
}
__inline void
InsertIntoTimerWheel(TCB *InsertTCB, ushort Slot)
{
PTIMER_WHEEL WheelPtr;
Queue* TimerSlotPtr;
CTELockHandle LockHandle;
WheelPtr = &TimerWheel[InsertTCB->tcb_partition];
TimerSlotPtr = &WheelPtr->tw_timerslot[Slot];
ASSERT(InsertTCB->tcb_timerslot == DUMMY_SLOT);
CTEGetLockAtDPC(&(WheelPtr->tw_lock), &LockHandle);
InsertTCB->tcb_timerslot = Slot;
PUSHQ(TimerSlotPtr, &InsertTCB->tcb_timerwheelq);
CTEFreeLockFromDPC(&(WheelPtr->tw_lock), LockHandle);
}
__inline void
RemoveFromTimerWheel(TCB *RemoveTCB)
{
PTIMER_WHEEL WheelPtr;
CTELockHandle LockHandle;
WheelPtr = &TimerWheel[RemoveTCB->tcb_partition];
ASSERT(RemoveTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
CTEGetLockAtDPC(&(WheelPtr->tw_lock), &LockHandle);
RemoveTCB->tcb_timerslot = DUMMY_SLOT;
RemoveTCB->tcb_timertime = 0;
REMOVEQ(&RemoveTCB->tcb_timerwheelq);
CTEFreeLockFromDPC(&(WheelPtr->tw_lock), LockHandle);
}
__inline void
RemoveAndInsertIntoTimerWheel(TCB *RemInsTCB, ushort InsertSlot)
{
PTIMER_WHEEL WheelPtr;
Queue* InsertSlotPtr;
CTELockHandle LockHandle;
ASSERT(RemInsTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
WheelPtr = &TimerWheel[RemInsTCB->tcb_partition];
InsertSlotPtr = &WheelPtr->tw_timerslot[InsertSlot];
CTEGetLockAtDPC(&WheelPtr->tw_lock, &LockHandle);
REMOVEQ(&RemInsTCB->tcb_timerwheelq);
RemInsTCB->tcb_timerslot = InsertSlot;
PUSHQ(InsertSlotPtr, &RemInsTCB->tcb_timerwheelq);
CTEFreeLockFromDPC(&WheelPtr->tw_lock, LockHandle);
}
__inline void
RecomputeTimerState(TCB *TimerTCB)
{
TCP_TIMER_TYPE i;
TimerTCB->tcb_timertype = NO_TIMER;
TimerTCB->tcb_timertime = 0;
for(i = 0; i < NUM_TIMERS; i++) {
if ((TimerTCB->tcb_timer[i] != 0) &&
((TimerTCB->tcb_timertime == 0) ||
(TCPTIME_LTE(TimerTCB->tcb_timer[i], TimerTCB->tcb_timertime)))) {
TimerTCB->tcb_timertime = TimerTCB->tcb_timer[i];
TimerTCB->tcb_timertype = i;
}
}
}
// StartTCBTimerR
// Arguments: A TCB, timer type, and the interval in ticks after which the
// timer is supposed to fire.
// Description:
// Sets the array element in tcb_timer for that particular timer to the
// appropriate value, and recomputes the tcb_timertime and tcb_timertype
// values ONLY if we need to. All shortcuts and optimizations are done to
// avoid recomputing the tcb_timertime and tcb_timertype by going through
// the whole array.
// return value: TRUE if minimum got changed; FALSE if not.
BOOLEAN
StartTCBTimerR(TCB *StartTCB, TCP_TIMER_TYPE TimerType, uint DeltaTime)
{
ASSERT(TimerType < NUM_TIMERS);
StartTCB->tcb_timer[TimerType] = TCPTime + DeltaTime;
// Make a check for the case where TCPTime + DeltaTime is 0
// because of wraparound
if (StartTCB->tcb_timer[TimerType] == 0) {
StartTCB->tcb_timer[TimerType] = 1;
}
// This is really simple logic. Find out if the setting of
// this timer changes the minimum. Don't care whether it was
// already running, or whether it already the minimum...
if ((StartTCB->tcb_timertime == 0 ) ||
(TCPTIME_LT(StartTCB->tcb_timer[TimerType], StartTCB->tcb_timertime))
)
{
// Yup it changed the minimum...
StartTCB->tcb_timertime = StartTCB->tcb_timer[TimerType];
StartTCB->tcb_timertype = TimerType;
return TRUE;
}
// No it did not change the minimum.
// You only have to recompute if it was already the minimum
if (StartTCB->tcb_timertype == TimerType) {
RecomputeTimerState(StartTCB);
}
return FALSE;
}
// StopTCBTimerR
// Arguments: A TCB, and a timer type
// Description:
// Sets the array element for that timer to 0, and recomputes
// tcb_timertime and tcb_timertype. It automatically handles
// the case where the function is called for a timer which has
// already stopped.
void
StopTCBTimerR(TCB *StopTCB, TCP_TIMER_TYPE TimerType)
{
ASSERT(TimerType < NUM_TIMERS);
StopTCB->tcb_timer[TimerType] = 0;
// Is this the lowest timer value we are running?
if (StopTCB->tcb_timertype == TimerType)
{
// Stopping a timer can only push back the firing
// time, so we will never remove a TCB from a slot.
RecomputeTimerState(StopTCB);
}
return;
}
// START_TCB_TIMER_R modifies the timer state and only modifies
// wheel state if the timer that was started was earlier than all the
// other timers on that TCB. This is in accordance with the lazy evaluation
// strategy.
__inline void
START_TCB_TIMER_R(TCB *StartTCB, TCP_TIMER_TYPE Type, uint Value)
{
ushort Slot;
if( StartTCB->tcb_timerslot == DUMMY_SLOT ) {
StartTCBTimerR(StartTCB, Type, Value);
Slot = COMPUTE_SLOT(StartTCB->tcb_timertime);
InsertIntoTimerWheel(StartTCB, Slot);
} else if ( StartTCBTimerR(StartTCB, Type, Value)) {
Slot = COMPUTE_SLOT(StartTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(StartTCB, Slot);
}
}
__inline void
STOP_TCB_TIMER_R(TCB *StopTCB, TCP_TIMER_TYPE Type)
{
StopTCBTimerR(StopTCB, Type);
return;
}
void
MakeTimerStateConsistent(TCB *TimerTCB, uint CurrentTime)
{
uint i;
BOOLEAN TimesChanged = FALSE;
for(i = 0; i < NUM_TIMERS; i++) {
if (TimerTCB->tcb_timer[i] != 0) {
if (TCPTIME_LTE(TimerTCB->tcb_timer[i], CurrentTime)) {
// This should not happen. If it does, we either have a bug in the current code
// or in the TimerWheel code.
// This assert was modified because it was found that, at some point of time
// ACD_CON_NOTIF was ON but not looked at after processing CONN_TIMER. So, it
// alright to advance timers by 1 tick if they were supposed to go off on the
// 'current' tick. Otherwise, assert.
if( TCPTIME_LT(TimerTCB->tcb_timer[i], CurrentTime )) {
ASSERT(0);
}
TimesChanged = TRUE;
TimerTCB->tcb_timer[i] = CurrentTime + 1;
// Code to handle wraparound.
if (TimerTCB->tcb_timer[i] == 0) {
TimerTCB->tcb_timer[i] = 1;
}
}
}
}
return;
}
#if MILLEN
ULONGLONG
my64div(ULONGLONG Divisor, ULONGLONG Dividend)
{
return (Divisor/Dividend);
}
#endif // MILLEN
//* TCBTimeout - Do timeout events on TCBs.
//
// Called every MS_PER_TICKS milliseconds to do timeout processing on TCBs.
// We run throught the TCB table, decrementing timers. If one goes to zero
// we look at it's state to decide what to do.
//
// Input: Timer - Event structure for timer that fired.
// Context - Context for timer (NULL in this case.
//
// Returns: Nothing.
//
void
TCBTimeout(CTEEvent * Timer, void *Context)
{
CTELockHandle TableHandle = DISPATCH_LEVEL, TCBHandle;
CTELockHandle TWTableHandle;
uint i, j;
TCB *CurrentTCB;
uint Delayed = FALSE;
uint CallRcvComplete;
ULONGLONG CurrentTime;
ULONGLONG TimeDiff;
uint tmp[2];
uint Processor, StartIndex, EndIndex;
#if TRACE_EVENT
PTDI_DATA_REQUEST_NOTIFY_ROUTINE CPCallBack;
WMIData WMIInfo;
#endif
// LocalTime must remain a uint. A loop below uses the
// termination condition j != (LocalTime + 1) and that
// works only if LocalTime is a uint so that wraparound
// can be handled correctly.
uint LocalTime = 0;
uint PerProcPartitions, ExtraPartitions;
#if MILLEN
Processor = 0;
#else
Processor = KeGetCurrentProcessorNumber();
#endif // !MILLEN
// Update our free running counter.
if (Processor == 0) {
CurrentTime = KeQueryInterruptTime();
TimeDiff = CurrentTime-systemtime+delta;
systemtime = CurrentTime;
#if !MILLEN
*(ULONGLONG *)&tmp[0] = TimeDiff/1000000i64; //100nsec to 100msec
#else // !MILLEN
//
// N.B. The compiler will optimize the 64-bit division followed by
// the mod to one function call: aulldvrm. This function is unsupported
// on WinME and causes a runtime load failure of tcpip.sys.
// We work around this by causing a function call to get
// the quotient and then mod to get the remainder. This way, the
// compiler generates calls to generate the quotient and remainder
// seperately.
//
*(ULONGLONG *)&tmp[0] = my64div(TimeDiff, 1000000i64); //100nsec to 100msec
#endif // MILLEN
CPUTCPTime[Processor] += MIN(200,tmp[0]);
delta = TimeDiff%1000000;
#ifdef TIMER_TEST
TCPTime = CPUTCPTime[0] + 0xfffff000;
#else
TCPTime = CPUTCPTime[0];
#endif
// Update our ISN generator. 25000 every 100ms...
// (inc. every 4us, 4.55 hour cycle time)
InterlockedExchangeAdd(&g_CurISN, 25000);
}
CTEInterlockedAddUlong(&TCBWalkCount, 1, &PendingFreeLock.Lock);
TCBHandle = DISPATCH_LEVEL;
// First compute the indexes of the timer wheels that we need to
// visit on this processor.
PerProcPartitions = NumTcbTablePartitions / KeNumberProcessors;
ExtraPartitions = NumTcbTablePartitions % KeNumberProcessors;
StartIndex = Processor * PerProcPartitions;
StartIndex += MIN(Processor, ExtraPartitions);
EndIndex = MIN(NumTcbTablePartitions, StartIndex + PerProcPartitions);
if (Processor < ExtraPartitions) EndIndex ++;
// There is the question, whether LocalTime should use the value of
// TCPTime after TCPTime has been incremented or before. On processor
// 0, this may actually make a difference how the TCBs are processed.
// The following code tries to keep the new behavior as close as possible
// the original behavior, by running the timer wheel upto the slot
// corresponding to the TCPTime AFTER the increment.
LocalTime = TCPTime;
// Now loop through the timer wheels.
for (i = StartIndex; i < EndIndex; i++) {
// For each timer wheel, tw_starttick stores the first time tick which
// needs to be checked. We loop from tw_starttick to the current time,
// and each time we figure out the slot corresponding to that tick,
// and visit all the TCBs on that slot's queue.
//
ushort CurrentSlot = COMPUTE_SLOT(TimerWheel[i].tw_starttick);
// In this for-loop, the termination condition is not j <= LocalTime,
// but j != LocalTime + 1. This is because TCPTime can wraparound, and
// tw_starttick may actually be higher that LocalTime.
// It is important to make sure that j is a uint, otherwise this logic
// does not hold.
for (j = TimerWheel[i].tw_starttick;
j != (LocalTime + 1);
j++, CurrentSlot = (CurrentSlot == (TIMER_WHEEL_SIZE - 1)) ? 0 : CurrentSlot + 1) {
TCB *TempTCB = NULL;
Queue MarkerElement;
uint maxRexmitCnt;
CTELockHandle TimerWheelHandle;
ushort Slot;
// Our basic loop is going to be:
// Pull out a TCB from the timer slot queue. Process it. Put it
// back into the timer wheel if we need to, depending on if other
// timers need to be fired. The problem with this is if the TCB
// ends up falling in the same slot, we get into this loop where
// we pull the TCB out, process it, put it back into the current
// slot, pull it out again, process it, ad infinitum.
//
// So we introduce a dummy element called MarkerElement. We begin
// our processing by inserting this element into the head of the
// queue. Now we always pull out a TCB which the MarkerElement points
// to, process it, and then push it to the head of the timer slot
// queue. Since no TCBs are ever added to the timer slot queue after
// the MarkerElement (elements are always added to the head of the
// queue), MarkerElement will eventually point to the head of the
// timer slot queue, at which point we know that we are done.
CTEGetLockAtDPC(&TimerWheel[i].tw_lock, &TimerWheelHandle);
PUSHQ(&TimerWheel[i].tw_timerslot[CurrentSlot], &MarkerElement);
CTEFreeLockFromDPC(&TimerWheel[i].tw_lock, TimerWheelHandle);
while (1) {
// First remove the tcb at the head of the list
CTEGetLockAtDPC(&TimerWheel[i].tw_lock, &TimerWheelHandle);
// The list is empty if the marker points to timer slot
if (QNEXT(&MarkerElement) == &TimerWheel[i].tw_timerslot[CurrentSlot]) {
REMOVEQ(&MarkerElement);
CTEFreeLockFromDPC(&TimerWheel[i].tw_lock, TimerWheelHandle);
break;
}
CurrentTCB = STRUCT_OF(TCB, QNEXT(&MarkerElement), tcb_timerwheelq);
CTEFreeLockFromDPC(&TimerWheel[i].tw_lock, TimerWheelHandle);
CTEStructAssert(CurrentTCB, tcb);
CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle);
// Someone may have removed this TCB before we reacquired the tcb
// lock. Check if it is still in the list and still in the same slot.
if (CurrentTCB->tcb_timerslot != CurrentSlot) {
CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
continue;
}
// This TCB may not be in the TCB table anymore. In that case, it SHOULD NOT be processed.
if( !(CurrentTCB->tcb_flags & IN_TCB_TABLE) ) {
RemoveFromTimerWheel(CurrentTCB);
CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
continue;
}
// Check if this is firing at the current time. In case of keepalive
// timers (which fire after hours), sometimes TCBs may queue to the
// current slot but their firing time is not the current time.
// This if statement also does the lazy evaluation -- if all timers
// have been stopped on this TCB just remove the TCB out.
// Callers of STOP_TCB_TIMER_R never end up removing the TCB. That
// job is left to this routine.
if (CurrentTCB->tcb_timertime != j) {
MakeTimerStateConsistent(CurrentTCB, j);
ASSERT(CurrentTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
RecomputeTimerState( CurrentTCB );
if (CurrentTCB->tcb_timertype == NO_TIMER) {
RemoveFromTimerWheel(CurrentTCB);
} else {
Slot = COMPUTE_SLOT(CurrentTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(CurrentTCB, Slot);
}
CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
continue;
}
// If it's CLOSING or CLOSED, skip it.
if (CurrentTCB->tcb_state == TCB_CLOSED || CLOSING(CurrentTCB)) {
// CloseTCB will handle all outstanding requests.
// So, it is safe to remove the TCB from timer wheel.
RemoveFromTimerWheel(CurrentTCB);
CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
continue;
}
CheckTCBSends(CurrentTCB);
CheckTCBRcv(CurrentTCB);
// First check the rexmit timer.
if (TCB_TIMER_FIRED_R(CurrentTCB, RXMIT_TIMER, j)) {
StopTCBTimerR(CurrentTCB, RXMIT_TIMER);
// And it's fired. Figure out what to do now.
// Remove all SACK rcvd entries (per RFC 2018)
if ((CurrentTCB->tcb_tcpopts & TCP_FLAG_SACK) && CurrentTCB->tcb_SackRcvd) {
SackListEntry *Prev, *Current;
Prev = STRUCT_OF(SackListEntry, &CurrentTCB->tcb_SackRcvd, next);
Current = CurrentTCB->tcb_SackRcvd;
while (Current) {
Prev->next = Current->next;
CTEFreeMem(Current);
Current = Prev->next;
}
}
// If we've had too many retransits, abort now.
CurrentTCB->tcb_rexmitcnt++;
if (CurrentTCB->tcb_state == TCB_SYN_SENT) {
maxRexmitCnt = MaxConnectRexmitCount;
} else {
if (CurrentTCB->tcb_state == TCB_SYN_RCVD) {
//
// Save on locking. Though MaxConnectRexmitCountTmp may
// be changing, we are assured that we will not use
// more than the MaxConnectRexmitCount.
//
maxRexmitCnt = MIN(MaxConnectResponseRexmitCountTmp, MaxConnectResponseRexmitCount);
} else {
maxRexmitCnt = MaxDataRexmitCount;
}
}
// If we've run out of retransmits or we're in FIN_WAIT2,
// time out.
if (CurrentTCB->tcb_rexmitcnt > maxRexmitCnt) {
ASSERT(CurrentTCB->tcb_state > TCB_LISTEN);
// This connection has timed out. Abort it. First
// reference him, then mark as closed, notify the
// user, and finally dereference and close him.
TimeoutTCB:
// This call may not be needed, but I've just added it
// for safety.
MakeTimerStateConsistent(CurrentTCB, j);
RecomputeTimerState( CurrentTCB );
ASSERT(CurrentTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
if (CurrentTCB->tcb_timertype == NO_TIMER) {
RemoveFromTimerWheel(CurrentTCB);
} else {
Slot = COMPUTE_SLOT(CurrentTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(CurrentTCB, Slot);
}
REFERENCE_TCB(CurrentTCB);
TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT, TCBHandle);
RemoveTCBFromConn(CurrentTCB);
NotifyOfDisc(CurrentTCB, NULL, TDI_TIMED_OUT);
if (SynAttackProtect) {
if (CurrentTCB->tcb_state == TCB_SYN_RCVD) {
CTELockHandle Handle;
CTEGetLockAtDPC(&SynAttLock.Lock, &Handle);
//
// We have put the connection in the closed state.
// Decrement the counters for keeping track of half
// open connections
//
ASSERT((TCPHalfOpen != 0) && (TCPHalfOpenRetried != 0));
TCPHalfOpen--;
TCPHalfOpenRetried--;
if (((TCPHalfOpen <= TCPMaxHalfOpen) ||
(TCPHalfOpenRetried <= TCPMaxHalfOpenRetriedLW)) &&
(MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
MaxConnectResponseRexmitCountTmp = MAX_CONNECT_RESPONSE_REXMIT_CNT;
}
CTEFreeLockFromDPC(&SynAttLock.Lock, Handle);
}
}
CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle);
DerefTCB(CurrentTCB, TCBHandle);
continue;
}
#if TRACE_EVENT
if (CurrentTCB->tcb_state == TCB_ESTAB){
CPCallBack = TCPCPHandlerRoutine;
if (CPCallBack != NULL) {
ulong GroupType;
WMIInfo.wmi_destaddr = CurrentTCB->tcb_daddr;
WMIInfo.wmi_destport = CurrentTCB->tcb_dport;
WMIInfo.wmi_srcaddr = CurrentTCB->tcb_saddr;
WMIInfo.wmi_srcport = CurrentTCB->tcb_sport;
WMIInfo.wmi_size = 0;
WMIInfo.wmi_context = CurrentTCB->tcb_cpcontext;
GroupType = EVENT_TRACE_GROUP_TCPIP + EVENT_TRACE_TYPE_RETRANSMIT;
(*CPCallBack) (GroupType, (PVOID)&WMIInfo, sizeof(WMIInfo), NULL);
}
}
#endif
CurrentTCB->tcb_rtt = 0; // Stop round trip time
// measurement.
// Figure out what our new retransmit timeout should be. We
// double it each time we get a retransmit, and reset it
// back when we get an ack for new data.
CurrentTCB->tcb_rexmit = MIN(CurrentTCB->tcb_rexmit << 1,
MAX_REXMIT_TO);
// Reset the sequence number, and reset the congestion
// window.
ResetSendNext(CurrentTCB, CurrentTCB->tcb_senduna);
if (!(CurrentTCB->tcb_flags & FLOW_CNTLD)) {
// Don't let the slow start threshold go below 2
// segments
CurrentTCB->tcb_ssthresh =
MAX(
MIN(
CurrentTCB->tcb_cwin,
CurrentTCB->tcb_sendwin
) / 2,
(uint) CurrentTCB->tcb_mss * 2
);
CurrentTCB->tcb_cwin = CurrentTCB->tcb_mss;
} else {
// We're probing, and the probe timer has fired. We
// need to set the FORCE_OUTPUT bit here.
CurrentTCB->tcb_flags |= FORCE_OUTPUT;
}
// See if we need to probe for a PMTU black hole.
if (PMTUBHDetect &&
CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt + 1) / 2)) {
// We may need to probe for a black hole. If we're
// doing MTU discovery on this connection and we
// are retransmitting more than a minimum segment
// size, or we are probing for a PMTU BH already, turn
// off the DF flag and bump the probe count. If the
// probe count gets too big we'll assume it's not
// a PMTU black hole, and we'll try to switch the
// router.
if ((CurrentTCB->tcb_flags & PMTU_BH_PROBE) ||
((CurrentTCB->tcb_opt.ioi_flags & IP_FLAG_DF) &&
(CurrentTCB->tcb_sendmax - CurrentTCB->tcb_senduna)
> 8)) {
// May need to probe. If we haven't exceeded our
// probe count, do so, otherwise restore those
// values.
if (CurrentTCB->tcb_bhprobecnt++ < 2) {
// We're going to probe. Turn on the flag,
// drop the MSS, and turn off the don't
// fragment bit.
if (!(CurrentTCB->tcb_flags & PMTU_BH_PROBE)) {
CurrentTCB->tcb_flags |= PMTU_BH_PROBE;
CurrentTCB->tcb_slowcount++;
CurrentTCB->tcb_fastchk |= TCP_FLAG_SLOW;
// Drop the MSS to the minimum. Save the old
// one in case we need it later.
CurrentTCB->tcb_mss = MIN(MAX_REMOTE_MSS -
CurrentTCB->tcb_opt.ioi_optlength,
CurrentTCB->tcb_remmss);
ASSERT(CurrentTCB->tcb_mss > 0);
CurrentTCB->tcb_cwin = CurrentTCB->tcb_mss;
CurrentTCB->tcb_opt.ioi_flags &= ~IP_FLAG_DF;
}
// Drop the rexmit count so we come here again,
// and don't retrigger DeadGWDetect.
CurrentTCB->tcb_rexmitcnt--;
} else {
// Too many probes. Stop probing, and allow fallover
// to the next gateway.
//
// Currently this code won't do BH probing on the 2nd
// gateway. The MSS will stay at the minimum size. This
// might be a little suboptimal, but it's
// easy to implement for the Sept. 95 service pack
// and will keep connections alive if possible.
//
// In the future we should investigate doing
// dead g/w detect on a per-connection basis, and then
// doing PMTU probing for each connection.
if (CurrentTCB->tcb_flags & PMTU_BH_PROBE) {
CurrentTCB->tcb_flags &= ~PMTU_BH_PROBE;
if (--(CurrentTCB->tcb_slowcount) == 0)
CurrentTCB->tcb_fastchk &=
~TCP_FLAG_SLOW;
}
CurrentTCB->tcb_bhprobecnt = 0;
}
}
}
// Check to see if we're doing dead gateway detect. If we
// are, see if it's time to ask IP.
if (DeadGWDetect &&
(SYNC_STATE(CurrentTCB->tcb_state) ||
!(CurrentTCB->tcb_fastchk & TCP_FLAG_RST_WHILE_SYN)) &&
(CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt + 1) / 2))) {
uint CheckRouteFlag;
if (SYNC_STATE(CurrentTCB->tcb_state)) {
CheckRouteFlag = 0;
} else {
CheckRouteFlag = CHECK_RCE_ONLY;
}
(*LocalNetInfo.ipi_checkroute) (CurrentTCB->tcb_daddr,
CurrentTCB->tcb_saddr,
CurrentTCB->tcb_rce,
&CurrentTCB->tcb_opt,
CheckRouteFlag);
}
if (CurrentTCB->tcb_fastchk & TCP_FLAG_RST_WHILE_SYN) {
CurrentTCB->tcb_fastchk &= ~TCP_FLAG_RST_WHILE_SYN;
CurrentTCB->tcb_slowcount--;
}
// Now handle the various cases.
switch (CurrentTCB->tcb_state) {
// In SYN-SENT or SYN-RCVD we'll need to retransmit
// the SYN.
case TCB_SYN_SENT:
case TCB_SYN_RCVD:
MakeTimerStateConsistent(CurrentTCB, j);
RecomputeTimerState( CurrentTCB );
if (CurrentTCB->tcb_timertype == NO_TIMER) {
RemoveFromTimerWheel(CurrentTCB);
} else {
Slot = COMPUTE_SLOT(CurrentTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(CurrentTCB, Slot);
}
SendSYN(CurrentTCB, TCBHandle);
continue;
case TCB_FIN_WAIT1:
case TCB_CLOSING:
case TCB_LAST_ACK:
// The call to ResetSendNext (above) will have
// turned off the FIN_OUTSTANDING flag.
CurrentTCB->tcb_flags |= FIN_NEEDED;
case TCB_CLOSE_WAIT:
case TCB_ESTAB:
// In this state we have data to retransmit, unless
// the window is zero (in which case we need to
// probe), or we're just sending a FIN.
CheckTCBSends(CurrentTCB);
Delayed = TRUE;
DelayAction(CurrentTCB, NEED_OUTPUT);
break;
// If it's fired in TIME-WAIT, we're all done and
// can clean up. We'll call TryToCloseTCB even
// though he's already sort of closed. TryToCloseTCB
// will figure this out and do the right thing.
case TCB_TIME_WAIT:
MakeTimerStateConsistent(CurrentTCB, j);
RecomputeTimerState( CurrentTCB );
if (CurrentTCB->tcb_timertype == NO_TIMER) {
RemoveFromTimerWheel(CurrentTCB);
} else {
Slot = COMPUTE_SLOT(CurrentTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(CurrentTCB, Slot);
}
TryToCloseTCB(CurrentTCB, TCB_CLOSE_SUCCESS,
TCBHandle);
continue;
default:
break;
}
}
// Now check the SWS deadlock timer..
if (TCB_TIMER_FIRED_R(CurrentTCB, SWS_TIMER, j)) {
StopTCBTimerR(CurrentTCB, SWS_TIMER);
// And it's fired. Force output now.
CurrentTCB->tcb_flags |= FORCE_OUTPUT;
Delayed = TRUE;
DelayAction(CurrentTCB, NEED_OUTPUT);
}
// Check the push data timer.
if (TCB_TIMER_FIRED_R(CurrentTCB, PUSH_TIMER, j)) {
StopTCBTimerR(CurrentTCB, PUSH_TIMER);
// It's fired.
PushData(CurrentTCB);
Delayed = TRUE;
}
// Check the delayed ack timer.
if (TCB_TIMER_FIRED_R(CurrentTCB, DELACK_TIMER, j)) {
StopTCBTimerR(CurrentTCB, DELACK_TIMER);
// And it's fired. Set up to send an ACK.
Delayed = TRUE;
DelayAction(CurrentTCB, NEED_ACK);
}
if (TCB_TIMER_FIRED_R(CurrentTCB, KA_TIMER, j)) {
StopTCBTimerR(CurrentTCB, KA_TIMER);
// Finally check the keepalive timer.
if ((CurrentTCB->tcb_state == TCB_ESTAB) &&
(CurrentTCB->tcb_flags & KEEPALIVE) &&
(CurrentTCB->tcb_conn)) {
if (CurrentTCB->tcb_kacount < MaxDataRexmitCount) {
MakeTimerStateConsistent(CurrentTCB, j);
RecomputeTimerState( CurrentTCB );
START_TCB_TIMER_R( CurrentTCB, KA_TIMER,
CurrentTCB->tcb_conn->tc_tcbkainterval);
ASSERT(CurrentTCB->tcb_timertype != NO_TIMER);
ASSERT(CurrentTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
SendKA(CurrentTCB, TCBHandle);
continue;
} else
goto TimeoutTCB;
}
}
if (TCB_TIMER_FIRED_R(CurrentTCB, CONN_TIMER, j)) {
StopTCBTimerR(CurrentTCB, CONN_TIMER);
// If this is an active open connection in SYN-SENT or SYN-RCVD,
// or we have a FIN pending, check the connect timer.
if (CurrentTCB->tcb_flags & (ACTIVE_OPEN | FIN_NEEDED | FIN_SENT)) {
if (CurrentTCB->tcb_connreq) {
// The connection timer has timed out.
CurrentTCB->tcb_flags |= NEED_RST;
MakeTimerStateConsistent(CurrentTCB, j);
RecomputeTimerState( CurrentTCB );
START_TCB_TIMER_R(CurrentTCB, RXMIT_TIMER, CurrentTCB->tcb_rexmit);
ASSERT(CurrentTCB->tcb_timertype != NO_TIMER);
ASSERT(CurrentTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT,
TCBHandle);
continue;
}
}
}
//
// Check to see if we have to notify the
// automatic connection driver about this
// connection.
//
if (TCB_TIMER_FIRED_R(CurrentTCB, ACD_TIMER, j)) {
BOOLEAN fEnabled;
CTELockHandle AcdHandle;
StopTCBTimerR(CurrentTCB, ACD_TIMER);
MakeTimerStateConsistent(CurrentTCB, j);
RecomputeTimerState( CurrentTCB );
ASSERT(CurrentTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
if (CurrentTCB->tcb_timertype == NO_TIMER) {
RemoveFromTimerWheel(CurrentTCB);
} else {
Slot = COMPUTE_SLOT(CurrentTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(CurrentTCB, Slot);
}
//
// Determine if we need to notify
// the automatic connection driver.
//
CTEGetLockAtDPC(&AcdDriverG.SpinLock, &AcdHandle);
fEnabled = AcdDriverG.fEnabled;
CTEFreeLockFromDPC(&AcdDriverG.SpinLock, AcdHandle);
if (fEnabled)
TCPNoteNewConnection(CurrentTCB, TCBHandle);
else
CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
continue;
}
// Timer isn't running, or didn't fire.
MakeTimerStateConsistent(CurrentTCB, j);
ASSERT(CurrentTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
RecomputeTimerState( CurrentTCB );
if (CurrentTCB->tcb_timertype == NO_TIMER) {
RemoveFromTimerWheel(CurrentTCB);
} else {
Slot = COMPUTE_SLOT(CurrentTCB->tcb_timertime);
RemoveAndInsertIntoTimerWheel(CurrentTCB, Slot);
}
CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
}
}
TimerWheel[i].tw_starttick = LocalTime + 1;
}
ProcessSynTcbs(Processor);
// Check if it is about time to remove TCBs off TWQueue
if (Processor == 0) {
for (i = 0; i < NumTcbTablePartitions; i++) {
BOOLEAN Done = FALSE, firstime = TRUE;
TWTCB *CurrentTCB = NULL;
Queue *tmp;
CTEGetLockAtDPC(&pTWTCBTableLock[i], &TWTableHandle);
while (!Done) {
if (!EMPTYQ(&TWQueue[i])) {
PEEKQ(&TWQueue[i], CurrentTCB, TWTCB, twtcb_TWQueue);
CTEStructAssert(CurrentTCB, twtcb);
ASSERT(CurrentTCB->twtcb_flags & IN_TWQUEUE);
//Decrement its life time and the last TCB in the queue
//because this is a timer delta queue!
if (firstime) {
TWTCB *PrvTCB;
if (CurrentTCB->twtcb_rexmittimer > 0)
CurrentTCB->twtcb_rexmittimer--;
tmp = TWQueue[i].q_prev;
PrvTCB = STRUCT_OF(TWTCB, tmp, twtcb_TWQueue);
PrvTCB->twtcb_delta--;
firstime = FALSE;
}
} else {
Done = TRUE;
CurrentTCB = NULL;
}
if (CurrentTCB) {
// Check the rexmit timer.
if ((CurrentTCB->twtcb_rexmittimer <= 0)) {
// Timer fired close and remove this tcb
RemoveTWTCB(CurrentTCB, i);
numtwtimedout++;
FreeTWTCB(CurrentTCB);
} else {
Done = TRUE;
}
} else
break;
} //while
CTEFreeLockFromDPC(&pTWTCBTableLock[i], TWTableHandle);
} //for
} //proc == 0
// See if we need to call receive complete as part of deadman processing.
// We do this now because we want to restart the timer before calling
// receive complete, in case that takes a while. If we make this check
// while the timer is running we'd have to lock, so we'll check and save
// the result now before we start the timer.
CallRcvComplete = FALSE;
if (Processor == 0) {
if (DeadmanTicks <= LocalTime) {
CallRcvComplete = TRUE;
DeadmanTicks = NUM_DEADMAN_TIME+LocalTime;
}
}
// Now check the pending free list. If it's not null, walk down the
// list and decrement the walk count. If the count goes below 2, pull it
// from the list. If the count goes to 0, free the TCB. If the count is
// at 1 it'll be freed by whoever called RemoveTCB.
if (Processor == 0) {
CTEGetLockAtDPC(&PendingFreeLock.Lock, &TableHandle);
if (PendingFreeList != NULL) {
TCB *PrevTCB;
PrevTCB = STRUCT_OF(TCB, &PendingFreeList, tcb_delayq.q_next);
do {
CurrentTCB = (TCB *) PrevTCB->tcb_delayq.q_next;
CTEStructAssert(CurrentTCB, tcb);
#ifdef PENDING_FREE_DBG
// If WalkCount is 0 (before decrementing!), then something is wrong..
if( CurrentTCB->tcb_walkcount == 0)
DbgBreakPoint();
// If the TCB is still in the TCB table, sky could fall down on our heads!
if( CurrentTCB->tcb_flags & IN_TCB_TABLE)
DbgBreakPoint();
#endif
CurrentTCB->tcb_walkcount--;
if (CurrentTCB->tcb_walkcount <= 0) {
*(TCB **) & PrevTCB->tcb_delayq.q_next =
(TCB *) CurrentTCB->tcb_delayq.q_next;
FreeTCB(CurrentTCB);
} else {
PrevTCB = CurrentTCB;
}
} while (PrevTCB->tcb_delayq.q_next != NULL);
}
if (PendingSynFreeList != NULL) {
SYNTCB *PrevTCB,*CurrentTCB;
//we use q_prev in link q so that QNEXT will still walk to the
//next syntcb in processsyntcb, while this tcb is on SynFreeLis.t
PrevTCB = STRUCT_OF(SYNTCB, &PendingSynFreeList, syntcb_link.q_prev);
do {
CurrentTCB = (SYNTCB *) PrevTCB->syntcb_link.q_prev;
CTEStructAssert(CurrentTCB, syntcb);
CurrentTCB->syntcb_walkcount--;
if (CurrentTCB->syntcb_walkcount <= 0) {
*(SYNTCB **) & PrevTCB->syntcb_link.q_prev =
(SYNTCB *) CurrentTCB->syntcb_link.q_prev;
FreeSynTCB(CurrentTCB);
} else {
PrevTCB = CurrentTCB;
}
} while (PrevTCB->syntcb_link.q_prev != NULL);
}
CTEFreeLockFromDPC(&PendingFreeLock.Lock, TableHandle);
// Do AddrCheckTable cleanup
if (AddrCheckTable) {
TCPAddrCheckElement *Temp;
CTEGetLockAtDPC(&AddrObjTableLock.Lock, &TableHandle);
for (Temp = AddrCheckTable; Temp < AddrCheckTable + NTWMaxConnectCount; Temp++) {
if (Temp->TickCount > 0) {
if ((--(Temp->TickCount)) == 0) {
Temp->SourceAddress = 0;
}
}
}
CTEFreeLockFromDPC(&AddrObjTableLock.Lock, TableHandle);
}
}
CTEInterlockedAddUlong(&TCBWalkCount, -1, &PendingFreeLock.Lock);
if (Delayed) {
ProcessPerCpuTCBDelayQ(Processor, DISPATCH_LEVEL, NULL, NULL);
}
if (CallRcvComplete) {
TCPRcvComplete();
}
}
//* SetTCBMTU - Set TCB MTU values.
//
// A function called by TCBWalk to set the MTU values of all TCBs using
// a particular path.
//
// Input: CheckTCB - TCB to be checked.
// DestPtr - Ptr to destination address.
// SrcPtr - Ptr to source address.
// MTUPtr - Ptr to new MTU.
//
// Returns: TRUE.
//
uint
SetTCBMTU(TCB * CheckTCB, void *DestPtr, void *SrcPtr, void *MTUPtr)
{
IPAddr DestAddr = *(IPAddr *) DestPtr;
IPAddr SrcAddr = *(IPAddr *) SrcPtr;
CTELockHandle TCBHandle;
CTEStructAssert(CheckTCB, tcb);
CTEGetLock(&CheckTCB->tcb_lock, &TCBHandle);
if (IP_ADDR_EQUAL(CheckTCB->tcb_daddr, DestAddr) &&
IP_ADDR_EQUAL(CheckTCB->tcb_saddr, SrcAddr) &&
(CheckTCB->tcb_opt.ioi_flags & IP_FLAG_DF)) {
uint MTU = *(uint *)MTUPtr - CheckTCB->tcb_opt.ioi_optlength;
CheckTCB->tcb_mss = (ushort) MIN(MTU, (uint) CheckTCB->tcb_remmss);
ASSERT(CheckTCB->tcb_mss > 0);
ValidateMSS(CheckTCB);
//
// Reset the Congestion Window if necessary
//
if (CheckTCB->tcb_cwin < CheckTCB->tcb_mss) {
CheckTCB->tcb_cwin = CheckTCB->tcb_mss;
//
// Make sure the slow start threshold is at least
// 2 segments
//
if (CheckTCB->tcb_ssthresh < ((uint) CheckTCB->tcb_mss * 2)) {
CheckTCB->tcb_ssthresh = CheckTCB->tcb_mss * 2;
}
}
}
CTEFreeLock(&CheckTCB->tcb_lock, TCBHandle);
return TRUE;
}
//* DeleteTCBWithSrc - Delete tcbs with a particular src address.
//
// A function called by TCBWalk to delete all TCBs with a particular source
// address.
//
// Input: CheckTCB - TCB to be checked.
// AddrPtr - Ptr to address.
//
// Returns: FALSE if CheckTCB is to be deleted, TRUE otherwise.
//
uint
DeleteTCBWithSrc(TCB * CheckTCB, void *AddrPtr, void *Unused1, void *Unused3)
{
IPAddr Addr = *(IPAddr *) AddrPtr;
CTEStructAssert(CheckTCB, tcb);
if (IP_ADDR_EQUAL(CheckTCB->tcb_saddr, Addr))
return FALSE;
else
return TRUE;
}
//* TCBWalk - Walk the TCBs in the table, and call a function for each of them.
//
// Called when we need to repetively do something to each TCB in the table.
// We call the specified function with a pointer to the TCB and the input
// context for each TCB in the table. If the function returns FALSE, we
// delete the TCB.
//
// Input: CallRtn - Routine to be called.
// Context1 - Context to pass to CallRtn.
// Context2 - Second context to pass to call routine.
// Context3 - Third context to pass to call routine.
//
// Returns: Nothing.
//
void
TCBWalk(uint(*CallRtn) (struct TCB *, void *, void *, void *), void *Context1,
void *Context2, void *Context3)
{
uint i, j;
TCB *CurTCB;
CTELockHandle Handle, TCBHandle;
// Loop through each bucket in the table, going down the chain of
// TCBs on the bucket. For each one call CallRtn.
for (j = 0; j < NumTcbTablePartitions; j++) {
CTEGetLock(&pTCBTableLock[j], &Handle);
for (i = j * PerPartitionSize; i < (j + 1) * PerPartitionSize; i++) {
CurTCB = TCBTable[i];
// Walk down the chain on this bucket.
while (CurTCB != NULL) {
if (!(*CallRtn) (CurTCB, Context1, Context2, Context3)) {
// He failed the call. Notify the client and close the
// TCB.
CTEGetLock(&CurTCB->tcb_lock, &TCBHandle);
ASSERT(CurTCB->tcb_partition == j);
if (!CLOSING(CurTCB)) {
REFERENCE_TCB(CurTCB);
CTEFreeLock(&pTCBTableLock[j], TCBHandle);
TryToCloseTCB(CurTCB, TCB_CLOSE_ABORTED, Handle);
RemoveTCBFromConn(CurTCB);
if (CurTCB->tcb_state != TCB_TIME_WAIT)
NotifyOfDisc(CurTCB, NULL, TDI_CONNECTION_ABORTED);
CTEGetLock(&CurTCB->tcb_lock, &TCBHandle);
DerefTCB(CurTCB, TCBHandle);
CTEGetLock(&pTCBTableLock[j], &Handle);
} else
CTEFreeLock(&CurTCB->tcb_lock, TCBHandle);
CurTCB = FindNextTCB(i, CurTCB);
} else {
CurTCB = CurTCB->tcb_next;
}
}
}
CTEFreeLock(&pTCBTableLock[j], Handle);
} //outer for numpartitions
}
void
DerefSynTCB(SYNTCB * SynTCB, CTELockHandle TCBHandle)
{
CTELockHandle Handle=DISPATCH_LEVEL;
ASSERT(SynTCB->syntcb_refcnt != 0);
if (--SynTCB->syntcb_refcnt == 0) {
CTEGetLockAtDPC(&PendingFreeLock.Lock, &Handle);
if(TCBWalkCount){
ASSERT(!(SynTCB->syntcb_flags & IN_SYNTCB_TABLE));
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Freeing to synpendinglist %x\n",SynTCB));
//we use q_prev in link q so that QNEXT will still walk to the
//next syntcb in processsyntcb, while this tcb is on SynFreeLis.t
SynTCB->syntcb_walkcount = TCBWalkCount + 1;
*(SYNTCB **) &SynTCB->syntcb_link.q_prev = PendingSynFreeList;
PendingSynFreeList = SynTCB;
CTEFreeLockFromDPC(&PendingFreeLock.Lock, Handle);
CTEFreeLock(&SynTCB->syntcb_lock, TCBHandle);
} else {
CTEFreeLockFromDPC(&PendingFreeLock.Lock, Handle);
CTEFreeLock(&SynTCB->syntcb_lock, TCBHandle);
FreeSynTCB(SynTCB);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"syntcb freed %x\n",SynTCB));
}
} else {
CTEFreeLock(&SynTCB->syntcb_lock, TCBHandle);
}
}
TCB *
RemoveAndInsertSynTCB(SYNTCB *SynTCB, CTELockHandle TCBHandle)
{
TCB *NewTCB;
LOGICAL Inserted;
CTELockHandle Handle=DISPATCH_LEVEL;
NewTCB = AllocTCB();
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"reminsertsyn: %x %x\n",SynTCB,NewTCB));
if (NewTCB) {
//Initialize the full blown TCB
NewTCB->tcb_dport = SynTCB->syntcb_dport;
NewTCB->tcb_sport = SynTCB->syntcb_sport;
NewTCB->tcb_daddr = SynTCB->syntcb_daddr;
NewTCB->tcb_saddr = SynTCB->syntcb_saddr;
NewTCB->tcb_rcvnext = SynTCB->syntcb_rcvnext;
NewTCB->tcb_senduna = SynTCB->syntcb_senduna;
NewTCB->tcb_sendmax = SynTCB->syntcb_sendmax;
NewTCB->tcb_sendnext = SynTCB->syntcb_sendnext;
NewTCB->tcb_sendwin = SynTCB->syntcb_sendwin;
NewTCB->tcb_defaultwin = SynTCB->syntcb_defaultwin;
NewTCB->tcb_phxsum = PHXSUM(SynTCB->syntcb_saddr, SynTCB->syntcb_daddr,
PROTOCOL_TCP, 0);
NewTCB->tcb_rtt = SynTCB->syntcb_rtt;
NewTCB->tcb_smrtt = SynTCB->syntcb_smrtt;
NewTCB->tcb_delta = SynTCB->syntcb_delta;
NewTCB->tcb_rexmit = SynTCB->syntcb_rexmit;
NewTCB->tcb_mss = SynTCB->syntcb_mss;
NewTCB->tcb_remmss = SynTCB->syntcb_remmss;
NewTCB->tcb_rcvwin = SynTCB->syntcb_rcvwin;
NewTCB->tcb_rcvwinscale = SynTCB->syntcb_rcvwinscale;
NewTCB->tcb_sndwinscale = SynTCB->syntcb_sndwinscale;
NewTCB->tcb_tcpopts = SynTCB->syntcb_tcpopts;
NewTCB->tcb_state = TCB_SYN_RCVD;
NewTCB->tcb_connreq = NULL;
NewTCB->tcb_refcnt = 0;
REFERENCE_TCB(NewTCB);
NewTCB->tcb_fastchk |= TCP_FLAG_ACCEPT_PENDING;
NewTCB->tcb_flags |= CONN_ACCEPTED;
NewTCB->tcb_tsrecent = SynTCB->syntcb_tsrecent;
NewTCB->tcb_tsupdatetime = SynTCB->syntcb_tsupdatetime;
NewTCB->tcb_rexmitcnt = SynTCB->syntcb_rexmitcnt;
ClassifyPacket(NewTCB);
(*LocalNetInfo.ipi_initopts) (&NewTCB->tcb_opt);
Inserted = InsertTCB(NewTCB);
CTEGetLockAtDPC(&NewTCB->tcb_lock,&Handle);
DEREFERENCE_TCB(NewTCB);
if (!Inserted) {
TryToCloseTCB(NewTCB, TCB_CLOSE_ABORTED, Handle);
NewTCB = NULL;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"tcb insertion failed %x\n",NewTCB));
}
}
//since we are not refing syntcb, it should goaway now.
DerefSynTCB(SynTCB, TCBHandle);
return NewTCB;
}
//* FindSynTCB - Find a SynTCB in the syntcb table.
//
// Called when we need to find a SynTCB in the synTCB table.
// Note : No locks are held on entry. On exit tcb lock will be held
//
// Input: Src - Source IP address of TCB to be found.
// Dest - Dest. "" "" "" "" "" "" ""
// DestPort - Destination port of TCB to be found.
// SrcPort - Source port of TCB to be found.
//
// Returns: Pointer to synTCB found, or NULL if none.
//
TCB *
FindSynTCB(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort,
CTELockHandle * Handle, BOOLEAN AtDispatch, uint index, BOOLEAN *reset)
{
CTELockHandle TableHandle;
ulong Partition;
SYNTCB *FoundTCB;
TCB *NewTCB;
Queue *Scan;
Partition = GET_PARTITION(index);
CTEGetLock(&pSynTCBTableLock[Partition], Handle);
for (Scan = QHEAD(&SYNTCBTable[index]);
Scan != QEND(&SYNTCBTable[index]);
Scan = QNEXT(Scan)) {
FoundTCB = QSTRUCT(SYNTCB, Scan, syntcb_link);
CTEStructAssert(FoundTCB, syntcb);
if (IP_ADDR_EQUAL(FoundTCB->syntcb_daddr, Dest) &&
FoundTCB->syntcb_dport == DestPort &&
IP_ADDR_EQUAL(FoundTCB->syntcb_saddr, Src) &&
FoundTCB->syntcb_sport == SrcPort) {
CTEGetLock(&FoundTCB->syntcb_lock, &TableHandle);
FoundTCB->syntcb_flags &= ~IN_SYNTCB_TABLE;
REMOVEQ(&FoundTCB->syntcb_link);
CTEFreeLock(&pSynTCBTableLock[Partition], TableHandle);
//replace small syntcb with normal one.
NewTCB = RemoveAndInsertSynTCB(FoundTCB, *Handle);
if (NewTCB == NULL) {
uint maxRexmitCnt;
maxRexmitCnt = MIN(MaxConnectResponseRexmitCountTmp,
MaxConnectResponseRexmitCount);
*reset=TRUE;
if (FoundTCB->syntcb_rexmitcnt >= maxRexmitCnt) {
CTEInterlockedAddUlong(&TCPHalfOpenRetried, -1, &SynAttLock.Lock);
}
}
return NewTCB;
}
}
CTEFreeLock(&pSynTCBTableLock[Partition], *Handle);
// no matching TCB
return NULL;
}
//* FindTCB - Find a TCB in the tcb table.
//
// Called when we need to find a TCB in the TCB table. We take a quick
// look at the last TCB we found, and if it matches we return it, lock held. Otherwise
// we hash into the TCB table and look for it.
// Note : No locks are held on entry. On exit tcb lock will be held
//
// Input: Src - Source IP address of TCB to be found.
// Dest - Dest. "" "" "" "" "" "" ""
// DestPort - Destination port of TCB to be found.
// SrcPort - Source port of TCB to be found.
//
// Returns: Pointer to TCB found, or NULL if none.
//
TCB *
FindTCB(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort,
CTELockHandle * Handle, BOOLEAN AtDispatch, uint * hash)
{
TCB *FoundTCB;
CTELockHandle TableHandle;
ulong index, Partition;
*hash = index = TCB_HASH(Dest, Src, DestPort, SrcPort);
Partition = GET_PARTITION(index);
if (AtDispatch) {
CTEGetLockAtDPC(&pTCBTableLock[Partition], Handle);
} else {
CTEGetLock(&pTCBTableLock[Partition], Handle);
}
// Didn't find it in our 1 element cache.
FoundTCB = TCBTable[index];
while (FoundTCB != NULL) {
CTEStructAssert(FoundTCB, tcb);
if (IP_ADDR_EQUAL(FoundTCB->tcb_daddr, Dest) &&
FoundTCB->tcb_dport == DestPort &&
IP_ADDR_EQUAL(FoundTCB->tcb_saddr, Src) &&
FoundTCB->tcb_sport == SrcPort) {
// Found it. Update the cache for next time, and return.
//LastTCB = FoundTCB;
CTEGetLockAtDPC(&FoundTCB->tcb_lock, &TableHandle);
CTEFreeLockFromDPC(&pTCBTableLock[Partition], TableHandle);
return FoundTCB;
} else
FoundTCB = FoundTCB->tcb_next;
}
if (AtDispatch) {
CTEFreeLockFromDPC(&pTCBTableLock[Partition], *Handle);
} else {
CTEFreeLock(&pTCBTableLock[Partition], *Handle);
}
// no matching TCB
return NULL;
}
//* FindTCBTW - Find a TCB in the time wait tcb table.
//
// Called when we need to find a TCB in the TCB table. We take a quick
// look at the last TCB we found, and if it matches we return it. Otherwise
// we hash into the TCB table and look for it. We assume the TCB table lock
// is held when we are called.
//
// Input: Src - Source IP address of TCB to be found.
// Dest - Dest. "" "" "" "" "" "" ""
// DestPort - Destination port of TCB to be found.
// SrcPort - Source port of TCB to be found.
//
// Returns: Pointer to TCB found, or NULL if none.
//
TWTCB *
FindTCBTW(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort, uint index)
{
TWTCB *FoundTCB;
Queue *Scan;
for (Scan = QHEAD(&TWTCBTable[index]);
Scan != QEND(&TWTCBTable[index]);
Scan = QNEXT(Scan)) {
FoundTCB = QSTRUCT(TWTCB, Scan, twtcb_link);
CTEStructAssert(FoundTCB, twtcb);
if (IP_ADDR_EQUAL(FoundTCB->twtcb_daddr, Dest) &&
FoundTCB->twtcb_dport == DestPort &&
IP_ADDR_EQUAL(FoundTCB->twtcb_saddr, Src) &&
FoundTCB->twtcb_sport == SrcPort) {
return FoundTCB;
}
}
// no matching TCB
return NULL;
}
void
InsertInTimewaitQueue(TWTCB *Twtcb, uint Partition)
{
Queue *PrevLink;
TWTCB *Prev;
CTEStructAssert(Twtcb, twtcb);
ASSERT(!(Twtcb->twtcb_flags & IN_TWQUEUE));
ENQUEUE(&TWQueue[Partition], &Twtcb->twtcb_TWQueue);
PrevLink = QPREV(&Twtcb->twtcb_TWQueue);
if (PrevLink != &TWQueue[Partition]) {
Prev = STRUCT_OF(TWTCB, PrevLink, twtcb_TWQueue);
// Compute this TCB's delta value from the using previous TCB's delta
// Note that prev tcb delta is decremented by timeout routine
// every tick
Twtcb->twtcb_rexmittimer = MAX_REXMIT_TO - Prev->twtcb_delta;
} else {
Twtcb->twtcb_rexmittimer = MAX_REXMIT_TO;
}
Twtcb->twtcb_delta = MAX_REXMIT_TO;
#if DBG
Twtcb->twtcb_flags |= IN_TWQUEUE;
#endif
}
void
RemoveFromTimewaitQueue(TWTCB *Twtcb, uint Partition)
{
ushort Delta;
Queue *NextLink;
TWTCB *Next;
CTEStructAssert(Twtcb, twtcb);
ASSERT(Twtcb->twtcb_flags & IN_TWQUEUE);
Delta = Twtcb->twtcb_rexmittimer;
// Update the time for the delta queue if we are not the last
// item in the queue.
//
NextLink = QNEXT(&Twtcb->twtcb_TWQueue);
if (Delta && (NextLink != QEND(&TWQueue[Partition]))) {
Next = STRUCT_OF(TWTCB, NextLink, twtcb_TWQueue);
Next->twtcb_rexmittimer += Delta;
}
REMOVEQ(&Twtcb->twtcb_TWQueue);
#if DBG
Twtcb->twtcb_flags &= ~IN_TWQUEUE;
#endif
}
//* RemoveAndInsert();
// This routine is called by graceful close routine when the TCB
// needs to be placed in TIM_WAIT state.
// The routine removes the TCB from normal table and inserts a small
// version of it
// in TW table at the same hash index as the previous one.
// Also, it queues the TWTCB in timer delta queue for time out
// processing
//
uint
RemoveAndInsert(TCB * TimWaitTCB)
{
uint TCBIndex;
CTELockHandle TableHandle, TWTableHandle, TcbHandle = DISPATCH_LEVEL;
TCB *PrevTCB;
TWTCB *TWTcb;
uint Partition = TimWaitTCB->tcb_partition;
Queue* Scan;
#if DBG
uint Found = FALSE;
#endif
CTEStructAssert(TimWaitTCB, tcb);
ASSERT(TimWaitTCB->tcb_flags & IN_TCB_TABLE);
ASSERT(TimWaitTCB->tcb_pending == 0);
if (TimWaitTCB->tcb_flags & IN_TCB_TABLE) {
CTEGetLock(&pTCBTableLock[Partition], &TableHandle);
CTEGetLockAtDPC(&TimWaitTCB->tcb_lock, &TcbHandle);
TCBIndex = TCB_HASH(TimWaitTCB->tcb_daddr, TimWaitTCB->tcb_saddr,
TimWaitTCB->tcb_dport, TimWaitTCB->tcb_sport);
PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next);
do {
if (PrevTCB->tcb_next == TimWaitTCB) {
// Found him.
PrevTCB->tcb_next = TimWaitTCB->tcb_next;
#if DBG
Found = TRUE;
#endif
break;
}
PrevTCB = PrevTCB->tcb_next;
#if DBG
CTEStructAssert(PrevTCB, tcb);
#endif
} while (PrevTCB != NULL);
ASSERT(Found);
TimWaitTCB->tcb_flags &= ~IN_TCB_TABLE;
TimWaitTCB->tcb_pending |= FREE_PENDING;
//rce and opts are freed in dereftcb.
//at this point tcb is out of this tcbtable
//nobody should be holding on to this.
//we are free to close this tcb and
//move the state to a smaller twtcb after acquiring
//twtcbtable lock
//get a free twtcb
if ((TWTcb = AllocTWTCB(Partition)) == NULL) {
DerefTCB(TimWaitTCB, TcbHandle);
CTEFreeLock(&pTCBTableLock[Partition], TableHandle);
return TRUE;
//possibaly we should queue this tcb on wait queue
//and service it when we get free twtcb
}
// Initialize twtcb
//
TWTcb->twtcb_daddr = TimWaitTCB->tcb_daddr;
TWTcb->twtcb_saddr = TimWaitTCB->tcb_saddr;
TWTcb->twtcb_dport = TimWaitTCB->tcb_dport;
TWTcb->twtcb_sport = TimWaitTCB->tcb_sport;
TWTcb->twtcb_senduna = TimWaitTCB->tcb_senduna;
TWTcb->twtcb_rcvnext = TimWaitTCB->tcb_rcvnext;
TWTcb->twtcb_sendnext = TimWaitTCB->tcb_sendnext;
#if DBG
TWTcb->twtcb_flags = 0;
#endif
CTEGetLockAtDPC(&pTWTCBTableLock[Partition], &TWTableHandle);
// Free the parent tcb for this connection
DerefTCB(TimWaitTCB, TcbHandle);
// now insert this in to time wait table after locking
if (EMPTYQ(&TWTCBTable[TCBIndex])) {
//
// First item in this bucket.
//
PUSHQ(&TWTCBTable[TCBIndex], &TWTcb->twtcb_link);
} else {
//
// Insert the item in sorted order. The order is based
// on the address of the TWTCB. (In this case, comparison
// is made against the address of the twtcb_link member, but
// the same result is achieved.)
//
for (Scan = QHEAD(&TWTCBTable[TCBIndex]);
Scan != QEND(&TWTCBTable[TCBIndex]);
Scan = QNEXT(Scan)) {
if (Scan > &TWTcb->twtcb_link) {
TWTcb->twtcb_link.q_next = Scan;
TWTcb->twtcb_link.q_prev = Scan->q_prev;
Scan->q_prev->q_next = &TWTcb->twtcb_link;
Scan->q_prev = &TWTcb->twtcb_link;
break;
}
}
//
// If we made it to the end without inserting, insert it
// at the end.
//
if (Scan == QEND(&TWTCBTable[TCBIndex])) {
ENQUEUE(&TWTCBTable[TCBIndex], &TWTcb->twtcb_link);
}
}
//no need to hold on to tcbtablelock beyond this point
#if DBG
TWTcb->twtcb_flags |= IN_TWTCB_TABLE;
#endif
CTEFreeLockFromDPC(&pTCBTableLock[Partition], TWTableHandle);
InsertInTimewaitQueue(TWTcb, Partition);
CTEFreeLock(&pTWTCBTableLock[Partition], TableHandle);
InterlockedIncrement(&numtwqueue); //debug purpose
return TRUE;
} //if tcb in table
return FALSE;
}
//* RemoveTWTCB - Remove a TWTCB from the TWTCB table.
//
// Called when we need to remove a TCB in time-wait from the time wait TCB
// table. We assume the TWTCB table lock is held when we are called.
//
// Input: RemovedTCB - TWTCB to be removed.
//
void
RemoveTWTCB(TWTCB *RemovedTCB, uint Partition)
{
CTEStructAssert(RemovedTCB, twtcb);
ASSERT(RemovedTCB->twtcb_flags & IN_TWTCB_TABLE);
ASSERT(RemovedTCB->twtcb_flags & IN_TWQUEUE);
REMOVEQ(&RemovedTCB->twtcb_link);
InterlockedDecrement(&TStats.ts_numconns);
RemoveFromTimewaitQueue(RemovedTCB, Partition);
InterlockedDecrement(&numtwqueue);
#if DBG
RemovedTCB->twtcb_flags &= ~IN_TWTCB_TABLE;
#endif
}
void
ReInsert2MSL(TWTCB *RemovedTCB)
{
uint Index, Partition;
CTEStructAssert(RemovedTCB, twtcb);
ASSERT(RemovedTCB->twtcb_flags & IN_TWQUEUE);
Index = TCB_HASH(RemovedTCB->twtcb_daddr, RemovedTCB->twtcb_saddr,
RemovedTCB->twtcb_dport, RemovedTCB->twtcb_sport);
Partition = GET_PARTITION(Index);
RemoveFromTimewaitQueue(RemovedTCB, Partition);
InsertInTimewaitQueue(RemovedTCB, Partition);
}
//* InsertSynTCB - Insert a SYNTCB in the tcb table.
//
// This routine inserts a SYNTCB in the SYNTCB table. No locks need to be held
// when this routine is called. We insert TCBs in ascending address order.
// Before inserting we make sure that the SYNTCB isn't already in the table.
//
// Input: NewTCB - SYNTCB to be inserted.
//
// Returns: TRUE if we inserted, false if we didn't.
//
uint
InsertSynTCB(SYNTCB * NewTCB, CTELockHandle *TableHandle)
{
uint TCBIndex;
CTELockHandle TCBHandle=DISPATCH_LEVEL;
TCB *PrevTCB, *CurrentTCB;
CTELockHandle SynTableHandle=DISPATCH_LEVEL;
uint Partition;
Queue *Scan;
SYNTCB *tmpSynTCB;
ASSERT(NewTCB != NULL);
CTEStructAssert(NewTCB, syntcb);
TCBIndex = TCB_HASH(NewTCB->syntcb_daddr, NewTCB->syntcb_saddr,
NewTCB->syntcb_dport, NewTCB->syntcb_sport);
Partition = GET_PARTITION(TCBIndex);
CTEGetLock(&pTCBTableLock[Partition], TableHandle);
NewTCB->syntcb_partition = Partition;
// Find the proper place in the table to insert him. While
// we're walking we'll check to see if a dupe already exists.
// When we find the right place to insert, we'll remember it, and
// keep walking looking for a duplicate.
PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"insertsyn: %x\n",NewTCB));
while (PrevTCB->tcb_next != NULL) {
CurrentTCB = PrevTCB->tcb_next;
if (IP_ADDR_EQUAL(CurrentTCB->tcb_daddr, NewTCB->syntcb_daddr) &&
IP_ADDR_EQUAL(CurrentTCB->tcb_saddr, NewTCB->syntcb_saddr) &&
(CurrentTCB->tcb_sport == NewTCB->syntcb_sport) &&
(CurrentTCB->tcb_dport == NewTCB->syntcb_dport)) {
CTEFreeLock(&pTCBTableLock[Partition], *TableHandle);
return FALSE;
} else {
PrevTCB = PrevTCB->tcb_next;
}
}
CTEGetLockAtDPC(&pSynTCBTableLock[Partition], &SynTableHandle);
CTEGetLockAtDPC(&NewTCB->syntcb_lock, &TCBHandle);
if (EMPTYQ(&SYNTCBTable[TCBIndex])) {
PUSHQ(&SYNTCBTable[TCBIndex], &NewTCB->syntcb_link);
} else {
//Make sure that duplicate syn request has not
//resulted in inserting a dup syntcb
//which can happen on a MP system.
for (Scan = QHEAD(&SYNTCBTable[TCBIndex]);
Scan != QEND(&SYNTCBTable[TCBIndex]);
Scan = QNEXT(Scan)) {
tmpSynTCB = STRUCT_OF(SYNTCB, Scan, syntcb_link);
if (IP_ADDR_EQUAL(tmpSynTCB->syntcb_daddr, NewTCB->syntcb_daddr) &&
(tmpSynTCB->syntcb_dport == NewTCB->syntcb_dport) &&
IP_ADDR_EQUAL(tmpSynTCB->syntcb_saddr, NewTCB->syntcb_saddr) &&
(tmpSynTCB->syntcb_sport == NewTCB->syntcb_sport)) {
CTEFreeLockFromDPC(&NewTCB->syntcb_lock, TCBHandle);
CTEFreeLock(&pSynTCBTableLock[Partition],SynTableHandle);
CTEFreeLock(&pTCBTableLock[Partition], *TableHandle);
return FALSE;
}
}
// Insert the item in sorted order. The order is based
// on the address of the SYNTCB. (In this case, comparison
// is made against the address of the syntcb_link member, but
// the same result is achieved.)
//
for (Scan = QHEAD(&SYNTCBTable[TCBIndex]);
Scan != QEND(&SYNTCBTable[TCBIndex]);
Scan = QNEXT(Scan)) {
if (Scan > &NewTCB->syntcb_link) {
NewTCB->syntcb_link.q_next = Scan;
NewTCB->syntcb_link.q_prev = Scan->q_prev;
Scan->q_prev->q_next = &NewTCB->syntcb_link;
Scan->q_prev = &NewTCB->syntcb_link;
break;
}
}
//
// If we made it to the end without inserting, insert it
// at the end.
//
if (Scan == QEND(&SYNTCBTable[TCBIndex])) {
ENQUEUE(&SYNTCBTable[TCBIndex], &NewTCB->syntcb_link);
}
}
NewTCB->syntcb_flags |= IN_SYNTCB_TABLE;
CTEFreeLockFromDPC(&pSynTCBTableLock[Partition], SynTableHandle);
CTEFreeLockFromDPC(&pTCBTableLock[Partition], TCBHandle);
return TRUE;
}
//* InsertTCB - Insert a TCB in the tcb table.
//
// This routine inserts a TCB in the TCB table. No locks need to be held
// when this routine is called. We insert TCBs in ascending address order.
// Before inserting we make sure that the TCB isn't already in the table.
//
// Input: NewTCB - TCB to be inserted.
//
// Returns: TRUE if we inserted, false if we didn't.
//
uint
InsertTCB(TCB * NewTCB)
{
uint TCBIndex;
CTELockHandle TableHandle, TCBHandle;
TCB *PrevTCB, *CurrentTCB;
TCB *WhereToInsert;
CTELockHandle TWTableHandle;
TWTCB *CurrentTWTCB;
uint Partition;
Queue *Scan;
uint EarlyInsertTime;
ASSERT(NewTCB != NULL);
CTEStructAssert(NewTCB, tcb);
TCBIndex = TCB_HASH(NewTCB->tcb_daddr, NewTCB->tcb_saddr,
NewTCB->tcb_dport, NewTCB->tcb_sport);
Partition = GET_PARTITION(TCBIndex);
CTEGetLock(&pTCBTableLock[Partition], &TableHandle);
CTEGetLockAtDPC(&NewTCB->tcb_lock, &TCBHandle);
NewTCB->tcb_partition = Partition;
// Find the proper place in the table to insert him. While
// we're walking we'll check to see if a dupe already exists.
// When we find the right place to insert, we'll remember it, and
// keep walking looking for a duplicate.
PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next);
WhereToInsert = NULL;
while (PrevTCB->tcb_next != NULL) {
CurrentTCB = PrevTCB->tcb_next;
if (IP_ADDR_EQUAL(CurrentTCB->tcb_daddr, NewTCB->tcb_daddr) &&
IP_ADDR_EQUAL(CurrentTCB->tcb_saddr, NewTCB->tcb_saddr) &&
(CurrentTCB->tcb_sport == NewTCB->tcb_sport) &&
(CurrentTCB->tcb_dport == NewTCB->tcb_dport)) {
CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle);
CTEFreeLock(&pTCBTableLock[Partition], TableHandle);
return FALSE;
} else {
if (WhereToInsert == NULL && CurrentTCB > NewTCB) {
WhereToInsert = PrevTCB;
}
CTEStructAssert(PrevTCB->tcb_next, tcb);
PrevTCB = PrevTCB->tcb_next;
}
}
// there can be timed_wait tcb in the tw tcb table.
// look if there is a tcb with the same address.
// Get lock on TW table too.
CTEGetLockAtDPC(&pTWTCBTableLock[Partition], &TWTableHandle);
for (Scan = QHEAD(&TWTCBTable[TCBIndex]);
Scan != QEND(&TWTCBTable[TCBIndex]);
Scan = QNEXT(Scan)) {
CurrentTWTCB = STRUCT_OF(TWTCB, Scan, twtcb_link);
CTEStructAssert(CurrentTWTCB, twtcb);
if (IP_ADDR_EQUAL(CurrentTWTCB->twtcb_daddr, NewTCB->tcb_daddr) &&
(CurrentTWTCB->twtcb_dport == NewTCB->tcb_dport) &&
IP_ADDR_EQUAL(CurrentTWTCB->twtcb_saddr, NewTCB->tcb_saddr) &&
(CurrentTWTCB->twtcb_sport == NewTCB->tcb_sport)) {
CTEFreeLockFromDPC(&pTWTCBTableLock[Partition], TWTableHandle);
CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle);
CTEFreeLock(&pTCBTableLock[Partition], TableHandle);
return FALSE;
}
}
CTEFreeLockFromDPC(&pTWTCBTableLock[Partition], TWTableHandle);
if (WhereToInsert == NULL) {
WhereToInsert = PrevTCB;
}
NewTCB->tcb_next = WhereToInsert->tcb_next;
WhereToInsert->tcb_next = NewTCB;
NewTCB->tcb_flags |= IN_TCB_TABLE;
// Doing EarlyInsert..
EarlyInsertTime = TCPTime + 2;
// Taking care of looparound..
if( EarlyInsertTime == 0)
EarlyInsertTime = 1;
// Early Insertion of TCB..
if( NewTCB->tcb_timerslot == DUMMY_SLOT ) {
NewTCB->tcb_timertime = EarlyInsertTime;
InsertIntoTimerWheel( NewTCB, COMPUTE_SLOT( EarlyInsertTime ) );
} else if ( (TCPTIME_LT( EarlyInsertTime , NewTCB->tcb_timertime )) ||
(NewTCB->tcb_timertime == 0 ) ) {
NewTCB->tcb_timertime = EarlyInsertTime;
RemoveAndInsertIntoTimerWheel( NewTCB, COMPUTE_SLOT( EarlyInsertTime ));
}
CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle);
CTEFreeLock(&pTCBTableLock[Partition], TableHandle);
InterlockedIncrement(&TStats.ts_numconns);
return TRUE;
}
//* RemoveTCB - Remove a TCB from the tcb table.
//
// Called when we need to remove a TCB from the TCB table. We assume the
// TCB table lock and the TCB lock are held when we are called. If the
// TCB isn't in the table we won't try to remove him.
//
// Input: RemovedTCB - TCB to be removed.
//
// Returns: TRUE if it's OK to free it, FALSE otherwise.
//
uint
RemoveTCB(TCB * RemovedTCB)
{
uint TCBIndex;
TCB *PrevTCB;
#if DBG
uint Found = FALSE;
#endif
CTELockHandle Handle;
CTEStructAssert(RemovedTCB, tcb);
if( RemovedTCB->tcb_timerslot != DUMMY_SLOT) {
ASSERT( RemovedTCB->tcb_timerslot < TIMER_WHEEL_SIZE );
RemoveFromTimerWheel( RemovedTCB );
}
if (RemovedTCB->tcb_flags & IN_TCB_TABLE) {
TCBIndex = TCB_HASH(RemovedTCB->tcb_daddr, RemovedTCB->tcb_saddr,
RemovedTCB->tcb_dport, RemovedTCB->tcb_sport);
PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next);
do {
if (PrevTCB->tcb_next == RemovedTCB) {
// Found him.
PrevTCB->tcb_next = RemovedTCB->tcb_next;
RemovedTCB->tcb_flags &= ~IN_TCB_TABLE;
InterlockedDecrement(&TStats.ts_numconns);
#if DBG
Found = TRUE;
#endif
break;
}
PrevTCB = PrevTCB->tcb_next;
#if DBG
if (PrevTCB != NULL)
CTEStructAssert(PrevTCB, tcb);
#endif
} while (PrevTCB != NULL);
ASSERT(Found);
}
CTEGetLockAtDPC(&PendingFreeLock.Lock, &Handle);
if (TCBWalkCount != 0) {
#ifdef PENDING_FREE_DBG
if( RemovedTCB->tcb_flags & IN_TCB_TABLE )
DbgBreakPoint();
#endif
RemovedTCB->tcb_walkcount = TCBWalkCount + 1;
*(TCB **) & RemovedTCB->tcb_delayq.q_next = PendingFreeList;
PendingFreeList = RemovedTCB;
CTEFreeLockFromDPC(&PendingFreeLock.Lock, Handle);
return FALSE;
} else {
CTEFreeLockFromDPC(&PendingFreeLock.Lock, Handle);
return TRUE;
}
}
//* AllocTWTCB - Allocate a TCB.
//
// Called whenever we need to allocate a TWTCB. We try to pull one off the
// free list, or allocate one if we need one. We then initialize it, etc.
//
// Input: Nothing.
//
// Returns: Pointer to the new TCB, or NULL if we couldn't get one.
//
TWTCB *
AllocTWTCB(uint index)
{
TWTCB *NewTWTCB;
LOGICAL FromList;
// We use the reqeust pool, because its buffers are in the same size
// range as TWTCB. Further, it is a very active and efficient look
// aside list whereas when TWTCBs are on their own look aside list it
// is usually at a very small depth because TWTCBs are not allocated
// very frequently w.r.t. to the update period of the look aside list.
//
NewTWTCB = PplAllocate(TcpRequestPool, &FromList);
if (NewTWTCB) {
NdisZeroMemory(NewTWTCB, sizeof(TWTCB));
#if DBG
NewTWTCB->twtcb_sig = twtcb_signature;
#endif
}
return NewTWTCB;
}
//* AllocTCB - Allocate a TCB.
//
// Called whenever we need to allocate a TCB. We try to pull one off the
// free list, or allocate one if we need one. We then initialize it, etc.
//
// Input: Nothing.
//
// Returns: Pointer to the new TCB, or NULL if we couldn't get one.
//
TCB *
AllocTCB(VOID)
{
TCB *NewTCB;
LOGICAL FromList;
NewTCB = PplAllocate(TcbPool, &FromList);
if (NewTCB) {
NdisZeroMemory(NewTCB, sizeof(TCB));
#if DBG
NewTCB->tcb_sig = tcb_signature;
#endif
INITQ(&NewTCB->tcb_sendq);
// Initially we're not on the fast path because we're not established. Set
// the slowcount to one and set up the fastchk fields so we don't take the
// fast path.
NewTCB->tcb_slowcount = 1;
NewTCB->tcb_fastchk = TCP_FLAG_ACK | TCP_FLAG_SLOW;
NewTCB->tcb_delackticks = DEL_ACK_TICKS;
CTEInitLock(&NewTCB->tcb_lock);
INITQ(&NewTCB->tcb_timerwheelq);
NewTCB->tcb_timerslot = DUMMY_SLOT;
NewTCB->tcb_timertime = 0;
NewTCB->tcb_timertype = NO_TIMER;
}
return NewTCB;
}
//* AllocSynTCB - Allocate a SYNTCB.
//
// Called whenever we need to allocate a synTCB. We try to pull one off the
// free list, or allocate one if we need one. We then initialize it, etc.
//
// Input: Nothing.
//
// Returns: Pointer to the new SYNTCB, or NULL if we couldn't get one.
//
SYNTCB *
AllocSynTCB(VOID)
{
SYNTCB *SynTCB;
LOGICAL FromList;
SynTCB = PplAllocate(SynTcbPool, &FromList);
if (SynTCB) {
NdisZeroMemory(SynTCB, sizeof(SYNTCB));
#if DBG
SynTCB->syntcb_sig = syntcb_signature;
#endif
CTEInitLock(&SynTCB->syntcb_lock);
}
return SynTCB;
}
//* FreeTCB - Free a TCB.
//
// Called whenever we need to free a TCB.
//
// Note: This routine may be called with the TCBTableLock held.
//
// Input: FreedTCB - TCB to be freed.
//
// Returns: Nothing.
//
VOID
FreeTCB(TCB * FreedTCB)
{
CTELockHandle Handle;
CTEStructAssert(FreedTCB, tcb);
if (FreedTCB->tcb_timerslot != DUMMY_SLOT) {
CTEGetLock( &FreedTCB->tcb_lock, &Handle);
// This TCB should not be touched in this stage at all..
ASSERT( FreedTCB->tcb_timerslot != DUMMY_SLOT );
// Even if it does, it has to be handled properly..
if( FreedTCB->tcb_timerslot != DUMMY_SLOT) {
ASSERT( FreedTCB->tcb_timerslot < TIMER_WHEEL_SIZE );
RemoveFromTimerWheel( FreedTCB );
}
CTEFreeLock(&FreedTCB->tcb_lock, Handle);
}
if (FreedTCB->tcb_SackBlock)
CTEFreeMem(FreedTCB->tcb_SackBlock);
if (FreedTCB->tcb_SackRcvd) {
SackListEntry *tmp, *next;
tmp = FreedTCB->tcb_SackRcvd;
while (tmp) {
next = tmp->next;
CTEFreeMem(tmp);
tmp = next;
}
}
PplFree(TcbPool, FreedTCB);
}
//* FreeSynTCB - Free a TCB.
//
// Called whenever we need to free a SynTCB.
//
// Note: This routine may be called with the SYNTCBTableLock held.
//
// Input: SynTCB - SynTCB to be freed.
//
// Returns: Nothing.
//
VOID
FreeSynTCB(SYNTCB * SynTCB)
{
CTEStructAssert(SynTCB, syntcb);
PplFree(SynTcbPool, SynTCB);
}
//* FreeTWTCB - Free a TWTCB.
//
// Called whenever we need to free a TWTCB.
//
// Note: This routine may be called with the TWTCBTableLock held.
//
// Input: FreedTCB - TCB to be freed.
//
// Returns: Nothing.
//
__inline
void
FreeTWTCB(TWTCB * FreedTWTCB)
{
PplFree(TcpRequestPool, FreedTWTCB);
}
uint
GetTCBInfo(PTCP_FINDTCB_RESPONSE TCBInfo, IPAddr Dest, IPAddr Src,
ushort DestPort, ushort SrcPort)
{
TCB *FoundTCB;
CTELockHandle Handle, TCBHandle, TwHandle;
BOOLEAN timedwait = FALSE;
uint Index, Partition;
FoundTCB = FindTCB(Src, Dest, DestPort, SrcPort, &Handle, FALSE, &Index);
Partition = GET_PARTITION(Index);
if (FoundTCB == NULL) {
CTEGetLock(&pTWTCBTableLock[Partition], &TwHandle);
(TWTCB *) FoundTCB = FindTCBTW(Src, Dest, DestPort, SrcPort, Index);
if (!FoundTCB) {
CTEFreeLock(&pTWTCBTableLock[Partition], TwHandle);
return STATUS_NOT_FOUND;
} else {
timedwait = TRUE;
}
}
// okay we now have tcb locked.
// copy the fileds
TCBInfo->tcb_addr = (ULONG_PTR) FoundTCB;
if (!timedwait) {
TCBInfo->tcb_senduna = FoundTCB->tcb_senduna;
TCBInfo->tcb_sendnext = FoundTCB->tcb_sendnext;
TCBInfo->tcb_sendmax = FoundTCB->tcb_sendmax;
TCBInfo->tcb_sendwin = FoundTCB->tcb_sendwin;
TCBInfo->tcb_unacked = FoundTCB->tcb_unacked;
TCBInfo->tcb_maxwin = FoundTCB->tcb_maxwin;
TCBInfo->tcb_cwin = FoundTCB->tcb_cwin;
TCBInfo->tcb_mss = FoundTCB->tcb_mss;
TCBInfo->tcb_rtt = FoundTCB->tcb_rtt;
TCBInfo->tcb_smrtt = FoundTCB->tcb_smrtt;
TCBInfo->tcb_rexmitcnt = FoundTCB->tcb_rexmitcnt;
TCBInfo->tcb_rexmittimer = 0; // FoundTCB->tcb_rexmittimer;
TCBInfo->tcb_rexmit = FoundTCB->tcb_rexmit;
TCBInfo->tcb_retrans = TStats.ts_retranssegs;
TCBInfo->tcb_state = FoundTCB->tcb_state;
CTEFreeLock(&FoundTCB->tcb_lock, Handle);
} else {
//TCBInfo->tcb_state = ((TWTCB *)FoundTCB)->twtcb_state;
TCBInfo->tcb_state = TCB_TIME_WAIT;
CTEFreeLock(&pTWTCBTableLock[Partition], TwHandle);
}
return STATUS_SUCCESS;
}
#if REFERENCE_DEBUG
uint
TcpReferenceTCB(
IN TCB *RefTCB,
IN uchar *File,
IN uint Line
)
/*++
Routine Description:
Increases the reference count of a TCB and records a history of who
made the call to reference.
Arguments:
RefTCB - The TCB to reference.
File - The filename containing the calling fcn (output of the __FILE__ macro).
Line - The line number of the call to this fcn. (output of the __LINE__ macro).
Return Value:
The new reference count.
--*/
{
void *CallersCaller;
TCP_REFERENCE_HISTORY *RefHistory;
RefHistory = &RefTCB->tcb_refhistory[RefTCB->tcb_refhistory_index];
RefHistory->File = File;
RefHistory->Line = Line;
RtlGetCallersAddress(&RefHistory->Caller, &CallersCaller);
RefHistory->Count = ++RefTCB->tcb_refcnt;
RefTCB->tcb_refhistory_index = ++RefTCB->tcb_refhistory_index % MAX_REFERENCE_HISTORY;
return RefTCB->tcb_refcnt;
}
uint
TcpDereferenceTCB(
IN TCB *DerefTCB,
IN uchar *File,
IN uint Line
)
/*++
Routine Description:
Decreases the reference count of a TCB and records a history of who
made the call to dereference.
Arguments:
DerefTCB - The TCB to dereference.
File - The filename containing the calling fcn (output of the __FILE__ macro).
Line - The line number of the call to this fcn. (output of the __LINE__ macro).
Return Value:
The new reference count.
--*/
{
void *Caller;
TCP_REFERENCE_HISTORY *RefHistory;
RefHistory = &DerefTCB->tcb_refhistory[DerefTCB->tcb_refhistory_index];
RefHistory->File = File;
RefHistory->Line = Line;
// Because Dereference is usually called from DerefTCB, we are more
// interested in who called DerefTCB. So for dereference, we
// store the caller's caller in our history. We still retain a history
// of the actually call to this fcn via the file and line fields, so
// we are covered if the call did not come from DerefTCB.
//
RtlGetCallersAddress(&Caller, &RefHistory->Caller);
RefHistory->Count = --DerefTCB->tcb_refcnt;
DerefTCB->tcb_refhistory_index = ++DerefTCB->tcb_refhistory_index % MAX_REFERENCE_HISTORY;
return DerefTCB->tcb_refcnt;
}
#endif // REFERENCE_DEBUG
#pragma BEGIN_INIT
//* InitTCB - Initialize our TCB code.
//
// Called during init time to initialize our TCB code. We initialize
// the TCB table, etc, then return.
//
// Input: Nothing.
//
// Returns: TRUE if we did initialize, false if we didn't.
//
int
InitTCB(void)
{
uint i;
for (i = 0; i < TCB_TABLE_SIZE; i++)
TCBTable[i] = NULL;
CTEInitLock(&PendingFreeLock.Lock);
systemtime = KeQueryInterruptTime();
#ifdef TIMER_TEST
TCPTime = 0xfffff000;
#else
TCPTime = 0;
#endif
TCBWalkCount = 0;
DeadmanTicks = NUM_DEADMAN_TIME;
#if MILLEN
Time_Proc = 1;
PerTimerSize = TCB_TABLE_SIZE;
#else // MILLEN
Time_Proc = KeNumberProcessors;
PerTimerSize = (TCB_TABLE_SIZE + Time_Proc) / Time_Proc;
#endif // !MILLEN
for (i = 0; i < Time_Proc; i++) {
CTEInitTimerEx(&TCBTimer[i]);
#if !MILLEN
KeSetTargetProcessorDpc(&(TCBTimer[i].t_dpc), (CCHAR) i);
#endif // !MILLEN
CTEStartTimerEx(&TCBTimer[i], MS_PER_TICK , TCBTimeout, NULL);
}
TcbPool = PplCreatePool(NULL, NULL, 0, sizeof(TCB), 'TPCT', 0);
if (!TcbPool)
{
return FALSE;
}
SynTcbPool = PplCreatePool(NULL, NULL, 0, sizeof(SYNTCB), 'YPCT', 0);
if (!SynTcbPool)
{
PplDestroyPool(TcbPool);
return FALSE;
}
return TRUE;
}
//* UnInitTCB - UnInitialize our TCB code.
//
// Called during init time if we're going to fail the init. We don't actually
// do anything here.
//
// Input: Nothing.
//
// Returns: Nothing.
//
void
UnInitTCB(void)
{
uint i;
for (i = 0; i < Time_Proc; i++) {
CTEStopTimer(&TCBTimer[i]);
}
}
#pragma END_INIT