|
|
/********************************************************************/ /** 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 "pplasl.h"
#include "tcpdeliv.h"
#include <acd.h>
#include <acdapi.h>
HANDLE TcbPool; HANDLE SynTcbPool; HANDLE IprBufPool = NULL; 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 MAXIMUM_PROCESSORS
#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
#define DQ_PARTITION_TIME 8
uint LastDelayQPartitionTime = 0;
uint MaxHashTableSize = 512; uint DeadmanTicks;
//choose reasonable defaults
uint NumTcbTablePartitions = 4; uint PerPartitionSize = 128; uint LogPerPartitionSize = 7;
CTETimer TCBTimer[MAX_TIMER_PROCS];
ULONGLONG LastTimeoutTime; ULONG CumulativeCarryOver=0;
extern IPInfo LocalNetInfo;
extern SeqNum g_CurISN; extern int g_Credits; extern int g_LastIsnUpdateTime; extern int g_MaxCredits;
//
// 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 );
NTSTATUS 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; 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); CTEGetLockAtDPC(&AbortTCB->tcb_lock); CTEFreeLockFromDPC(&(Conn->tc_ConnBlock->cb_lock));
if (AbortTCB->tcb_state == TCB_CLOSED || CLOSING(AbortTCB)) { CTEFreeLockFromDPC(&AbortTCB->tcb_lock); CTEFreeLock(&tcpContext->EndpointLock, inHandle);
return FALSE; }
if (Rcv && AbortTCB->tcb_rcvhndlr == BufferData) {
// First, search for the IRP in the normal-receive queue.
if (AbortTCB->tcb_rcvhead) { TCPRcvReq *RcvReq = AbortTCB->tcb_rcvhead;
while (RcvReq) { CTEStructAssert(RcvReq, trr); if (RcvReq->trr_context == Irp) { break; } RcvReq = RcvReq->trr_next; }
// If the IRP was found, push it and all of its
// predecessors, then let CompleteRcvs complete them.
if (RcvReq) { TCPRcvReq* TmpReq = AbortTCB->tcb_rcvhead; while (TmpReq != RcvReq) { TmpReq->trr_flags |= TRR_PUSHED; TmpReq = TmpReq->trr_next; } RcvReq->trr_flags |= TRR_PUSHED; RcvReq->trr_status = TDI_CANCELLED;
REFERENCE_TCB(AbortTCB); CTEFreeLockFromDPC(&AbortTCB->tcb_lock); CTEFreeLock(&tcpContext->EndpointLock, inHandle);
CompleteRcvs(AbortTCB);
CTEGetLock(&AbortTCB->tcb_lock, &inHandle); DerefTCB(AbortTCB, inHandle); return FALSE; } }
// Next, search for the IRP in the expedited-receive 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) { PrevReq->trr_next = RcvReq->trr_next; FreeRcvReq(RcvReq); CTEFreeLockFromDPC(&AbortTCB->tcb_lock);
//
// 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]; CTEFreeLockFromDPC(&tcpContext->EndpointLock);
REFERENCE_TCB(AbortTCB);
if (!Rcv) {
CTEFreeLockFromDPC(&AbortTCB->tcb_lock);
// Call ndis cancel packets routine to free up
// queued send packets
(*LocalNetInfo.ipi_cancelpackets) (CancelContext, CancelID); CTEGetLockAtDPC(&AbortTCB->tcb_lock); }
AbortTCB->tcb_flags |= NEED_RST; // send a reset if connected
TryToCloseTCB(AbortTCB, TCB_CLOSE_ABORTED, DISPATCH_LEVEL); RemoveTCBFromConn(AbortTCB);
IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPAbortAndIndicateDisconnect, indicating discon\n" )); }
NotifyOfDisc(AbortTCB, NULL, TDI_CONNECTION_ABORTED, NULL); CTEGetLockAtDPC(&AbortTCB->tcb_lock); DerefTCB(AbortTCB, inHandle); return FALSE;
} else CTEFreeLockFromDPC(&(Conn->tc_ConnBlock->cb_lock)); } else CTEFreeLockFromDPC(&(Conn->tc_ConnBlock->cb_lock)); } CTEFreeLock(&tcpContext->EndpointLock, inHandle);
return FALSE; }
//* AddHalfOpenTCB
//
// Called to update the count of half-open connections and, if necessary,
// adjust retransmission thresholds.
//
// Input: None.
//
// Returns: Nothing.
//
void AddHalfOpenTCB(void) { ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); CTEGetLockAtDPC(&SynAttLock.Lock); if (++TCPHalfOpen >= TCPMaxHalfOpen && TCPHalfOpenRetried >= TCPMaxHalfOpenRetried && MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT) { MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT; } CTEFreeLockFromDPC(&SynAttLock.Lock); }
//* AddHalfOpenRetry
//
// Called to update the count of half-open connections which have reached our
// threshold for the number of SYN-ACK retransmissions. If necessary,
// the routine adjusts overall retransmission thresholds.
//
// Input: RexmitCnt - number of rexmits on the TCB for whom we're called.
//
// Returns: Nothing.
//
void AddHalfOpenRetry(uint RexmitCnt) { ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); if (RexmitCnt == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT) { CTEGetLockAtDPC(&SynAttLock.Lock); if (++TCPHalfOpenRetried >= TCPMaxHalfOpenRetried && TCPHalfOpen >= TCPMaxHalfOpen && MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT) { MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT; } CTEFreeLockFromDPC(&SynAttLock.Lock); } }
//* DropHalfOpenTCB
//
// Called to update the count of half-open connections and, if necessary,
// adjust retransmission thresholds.
//
// Input: RexmitCnt - number of rexmits on the TCB for whom we're called.
//
// Returns: Nothing.
//
void DropHalfOpenTCB(uint RexmitCnt) { ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
CTEGetLockAtDPC(&SynAttLock.Lock);
// Drop the count of half-open connections. If this one's retransmissions
// reached the adaptation threshold, drop the count of retries too.
--TCPHalfOpen; if (RexmitCnt >= ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT) { --TCPHalfOpenRetried; }
if ((TCPHalfOpen < TCPMaxHalfOpen || TCPHalfOpenRetried < TCPMaxHalfOpenRetriedLW) && MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT) { MaxConnectResponseRexmitCountTmp = MAX_CONNECT_RESPONSE_REXMIT_CNT; } CTEFreeLockFromDPC(&SynAttLock.Lock); }
//* ProcessSynTcbs
//
// Called from timeout routine to handle syntcbs in syntcb table
// Retransmits SYN if rexmitcnt 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; 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); CTEStructAssert(SynTCB, syntcb);
CTEGetLockAtDPC(&SynTCB->syntcb_lock);
Scan = QNEXT(Scan);
// This TCB needs to be processed further only if it is in the
// table, which means it is active still.
// (N.B. This is a more restrictive check than ref-count != 0).
if ((SynTCB->syntcb_flags & IN_SYNTCB_TABLE) && (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) { uint RexmitCnt = SynTCB->syntcb_rexmitcnt;
AddHalfOpenRetry(RexmitCnt);
SynTCB->syntcb_refcnt++; CTEFreeLockFromDPC(&SynTCB->syntcb_lock); CTEGetLockAtDPC(&pSynTCBTableLock[SynTCB->syntcb_partition]); CTEGetLockAtDPC(&SynTCB->syntcb_lock);
if (SynTCB->syntcb_flags & IN_SYNTCB_TABLE) { REMOVEQ(&SynTCB->syntcb_link); SynTCB->syntcb_flags &= ~IN_SYNTCB_TABLE;
// We indeed removed the Syn TCB; so, notify.
TcpInvokeCcb(TCP_CONN_SYN_RCVD, TCP_CONN_CLOSED, &SynTCB->syntcb_addrbytes, 0); DropHalfOpenTCB(RexmitCnt); SynTCB->syntcb_refcnt--; } CTEFreeLockFromDPC( &pSynTCBTableLock[SynTCB->syntcb_partition]);
DerefSynTCB(SynTCB, DISPATCH_LEVEL);
} else { AddHalfOpenRetry(SynTCB->syntcb_rexmitcnt);
--SynTCB->syntcb_sendnext; SendSYNOnSynTCB(SynTCB, DISPATCH_LEVEL); } } else { CTEFreeLockFromDPC(&SynTCB->syntcb_lock); } } else { CTEFreeLockFromDPC(&SynTCB->syntcb_lock); } } } }
__inline void InsertIntoTimerWheel(TCB *InsertTCB, ushort Slot) { PTIMER_WHEEL WheelPtr; Queue* TimerSlotPtr;
WheelPtr = &TimerWheel[InsertTCB->tcb_partition]; TimerSlotPtr = &WheelPtr->tw_timerslot[Slot];
ASSERT(InsertTCB->tcb_timerslot == DUMMY_SLOT);
CTEGetLockAtDPC(&(WheelPtr->tw_lock));
InsertTCB->tcb_timerslot = Slot; PUSHQ(TimerSlotPtr, &InsertTCB->tcb_timerwheelq);
CTEFreeLockFromDPC(&(WheelPtr->tw_lock)); }
__inline void RemoveFromTimerWheel(TCB *RemoveTCB) { PTIMER_WHEEL WheelPtr;
WheelPtr = &TimerWheel[RemoveTCB->tcb_partition]; ASSERT(RemoveTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
CTEGetLockAtDPC(&(WheelPtr->tw_lock));
RemoveTCB->tcb_timerslot = DUMMY_SLOT; RemoveTCB->tcb_timertime = 0;
REMOVEQ(&RemoveTCB->tcb_timerwheelq);
CTEFreeLockFromDPC(&(WheelPtr->tw_lock));
}
__inline void RemoveAndInsertIntoTimerWheel(TCB *RemInsTCB, ushort InsertSlot) { PTIMER_WHEEL WheelPtr; Queue* InsertSlotPtr;
ASSERT(RemInsTCB->tcb_timerslot < TIMER_WHEEL_SIZE);
WheelPtr = &TimerWheel[RemInsTCB->tcb_partition]; InsertSlotPtr = &WheelPtr->tw_timerslot[InsertSlot];
CTEGetLockAtDPC(&WheelPtr->tw_lock);
REMOVEQ(&RemInsTCB->tcb_timerwheelq); RemInsTCB->tcb_timerslot = InsertSlot; PUSHQ(InsertSlotPtr, &RemInsTCB->tcb_timerwheelq);
CTEFreeLockFromDPC(&WheelPtr->tw_lock); }
__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;
// The value of the delta ticks has to be atleast 2.
if (Value < 2) { Value = 2; }
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; }
//* 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 TCBHandle; uint i, j; TCB *CurrentTCB; uint Delayed = FALSE; uint ProcessDelayQ = FALSE; ULONGLONG CurrentTime; ULONG TimeDiff; 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;
Processor = KeGetCurrentProcessorNumber();
// Update our free running counter.
if (Processor == 0) { int Delta; ULONG CarryOver; int ElapsedTicks, ExtraTicks;
CurrentTime = KeQueryInterruptTime();
// This is time between when we processed the timer last and now.
TimeDiff = (ULONG)(CurrentTime - LastTimeoutTime); LastTimeoutTime = CurrentTime; // This is the extra time we have to carry forward.
ElapsedTicks = TimeDiff / 1000000; CarryOver = TimeDiff % 1000000;
// This is the total extra time we have seen so far:
CumulativeCarryOver = CumulativeCarryOver + CarryOver;
// If that exceeds 1 tick, we remember that.
if(CumulativeCarryOver > 1000000) { ExtraTicks = CumulativeCarryOver / 1000000; CumulativeCarryOver = CumulativeCarryOver % 1000000; } else { ExtraTicks = 0; }
// Preserved for historic reasons. I can't think of a case where
// this is required!
CPUTCPTime[0] += MIN(200, ElapsedTicks); LocalTime = TCPTime = CPUTCPTime[0];
if (ExtraTicks) { // This makes the timer process 1 (or more) extra tick(s) when the
// timer fires next time. However, this will not make us process
// 2 ticks immediately after TCPTime is incremented by 2 ticks,
// which is going to take care of delayed-ack timers.
CPUTCPTime[0] += ExtraTicks; TCPTime = CPUTCPTime[0]; }
// Set credits so that some more connections can increment the
// Initial Sequence Number, during the next 100 ms.
InterlockedExchange((PLONG)&g_Credits, g_MaxCredits);
Delta = GetDeltaTime();
// The increment made is (256)*(Time in milliseconds). This is really
// close to 25000 increment made originally every 100 ms.
if(Delta > 0) { Delta *= 0x100; InterlockedExchangeAdd((PLONG)&g_CurISN, Delta); } } else { // This is the tick upto which we are going to process this time.
LocalTime = TCPTime; }
CTEInterlockedAddUlong((PULONG)&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++; }
// 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) { Queue MarkerElement; uint maxRexmitCnt; 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); PUSHQ(&TimerWheel[i].tw_timerslot[CurrentSlot], &MarkerElement); CTEFreeLockFromDPC(&TimerWheel[i].tw_lock);
for (; ;) { // First remove the tcb at the head of the list
CTEGetLockAtDPC(&TimerWheel[i].tw_lock);
// 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); break; }
CurrentTCB = STRUCT_OF(TCB, QNEXT(&MarkerElement), tcb_timerwheelq);
CTEFreeLockFromDPC(&TimerWheel[i].tw_lock);
CTEStructAssert(CurrentTCB, tcb); CTEGetLockAtDPC(&CurrentTCB->tcb_lock);
// 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); 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); 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); 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); 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); if (SynAttackProtect) { AddHalfOpenRetry(CurrentTCB->tcb_rexmitcnt); } } 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, NULL);
CTEGetLockAtDPC(&CurrentTCB->tcb_lock); DerefTCB(CurrentTCB, TCBHandle); continue; } #if TRACE_EVENT
if ((CurrentTCB->tcb_state == TCB_SYN_SENT) || (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; if (CurrentTCB->tcb_state == TCB_ESTAB) { GroupType = EVENT_TRACE_GROUP_TCPIP + EVENT_TRACE_TYPE_RETRANSMIT; } else { GroupType = EVENT_TRACE_GROUP_TCPIP + EVENT_TRACE_TYPE_RECONNECT; }
(*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)) && (CurrentTCB->tcb_rce != NULL)) { 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;
if (--(CurrentTCB->tcb_slowcount) == 0) { CurrentTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; } }
// 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, FALSE); 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 (SYNC_STATE(CurrentTCB->tcb_state) && (CurrentTCB->tcb_flags & KEEPALIVE) && CurrentTCB->tcb_conn != NULL) { 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;
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); fEnabled = AcdDriverG.fEnabled; CTEFreeLockFromDPC(&AcdDriverG.SpinLock); if (fEnabled) TCPNoteNewConnection(CurrentTCB, TCBHandle); else CTEFreeLockFromDPC(&CurrentTCB->tcb_lock);
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); } } TimerWheel[i].tw_starttick = LocalTime + 1; }
if (SynAttackProtect) { 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]);
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]);
} //for
} //proc == 0
// See if we need to process the delay queues as part of deadman
// processing. We do this now because we want to restart the timer before
// processing the delay queues, 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.
if (Processor == 0) { if (DeadmanTicks <= LocalTime) { ProcessDelayQ = 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);
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);
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);
// Do AddrCheckTable cleanup
if (AddrCheckTable) {
TCPAddrCheckElement *Temp;
CTEGetLockAtDPC(&AddrObjTableLock.Lock);
for (Temp = AddrCheckTable; Temp < AddrCheckTable + NTWMaxConnectCount; Temp++) { if (Temp->TickCount > 0) { if ((--(Temp->TickCount)) == 0) { Temp->SourceAddress = 0; } } }
CTEFreeLockFromDPC(&AddrObjTableLock.Lock); } }
CTEInterlockedAddUlong((PULONG)&TCBWalkCount, (ULONG)-1, &PendingFreeLock.Lock);
if ((Processor == 0) && TCPTIME_LTE((LastDelayQPartitionTime + DQ_PARTITION_TIME), TCPTime)) { LastDelayQPartitionTime = TCPTime; PartitionDelayQProcessing(TRUE); ProcessDelayQ = TRUE; }
if (ProcessDelayQ) { ProcessTCBDelayQ(DISPATCH_LEVEL, TRUE); } else if (Delayed) { ProcessPerCpuTCBDelayQ(Processor, DISPATCH_LEVEL, NULL, NULL); } }
//* 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, NULL);
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); } }
void DerefSynTCB(SYNTCB * SynTCB, CTELockHandle TCBHandle) { ASSERT(SynTCB->syntcb_refcnt != 0);
if (--SynTCB->syntcb_refcnt == 0) {
CTEGetLockAtDPC(&PendingFreeLock.Lock); 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); CTEFreeLock(&SynTCB->syntcb_lock, TCBHandle);
} else {
CTEFreeLockFromDPC(&PendingFreeLock.Lock); 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;
NewTCB = AllocTCB();
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"reminsertsyn: %x %x\n",SynTCB,NewTCB));
if (NewTCB) { // Initialize the full TCB to replace this SYN 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_sendnext - 1; NewTCB->tcb_sendmax = SynTCB->syntcb_sendnext; 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 = 0; NewTCB->tcb_smrtt = 0; NewTCB->tcb_delta = MS_TO_TICKS(6000); NewTCB->tcb_rexmit = MS_TO_TICKS(3000);
NewTCB->tcb_mss = SynTCB->syntcb_mss; NewTCB->tcb_remmss = SynTCB->syntcb_remmss; NewTCB->tcb_rcvwin = SynTCB->syntcb_defaultwin; 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 |= (SynTCB->syntcb_flags & SYNTCB_SHARED_FLAGS); NewTCB->tcb_tsrecent = SynTCB->syntcb_tsrecent; NewTCB->tcb_tsupdatetime = SynTCB->syntcb_tsupdatetime; NewTCB->tcb_rexmitcnt = 0;
ClassifyPacket(NewTCB); (*LocalNetInfo.ipi_initopts) (&NewTCB->tcb_opt); NewTCB->tcb_opt.ioi_ttl = SynTCB->syntcb_ttl; CTEFreeLockFromDPC(&SynTCB->syntcb_lock);
Inserted = InsertTCB(NewTCB, FALSE);
CTEGetLockAtDPC(&NewTCB->tcb_lock);
if (!Inserted || CLOSING(NewTCB)) { if (!CLOSING(NewTCB)) { TryToCloseTCB(NewTCB, TCB_CLOSE_ABORTED, DISPATCH_LEVEL); CTEGetLockAtDPC(&NewTCB->tcb_lock); } DerefTCB(NewTCB, DISPATCH_LEVEL); NewTCB = NULL; CTEGetLockAtDPC(&SynTCB->syntcb_lock); } else { // No deletion is pending on the TCB, so just drop its refcnt
// and return with its lock held.
DEREFERENCE_TCB(NewTCB);
// Start the timer on the TCB that just got created.
// The only timer that needs to be transfered across is the
// retransmit timer. The number of ticks remaining till the
// next retransmission of SYN+ACK on the SYN TCB is used
// to start the TCB timer.
CTEGetLockAtDPC(&SynTCB->syntcb_lock); START_TCB_TIMER_R(NewTCB, RXMIT_TIMER, (SynTCB->syntcb_rexmittimer < 2) ? 2 : SynTCB->syntcb_rexmittimer); } } else { // Could not convert it to a regular TCB, hence notify.
TcpInvokeCcb(TCP_CONN_SYN_RCVD, TCP_CONN_CLOSED, &SynTCB->syntcb_addrbytes, 0); DropHalfOpenTCB(SynTCB->syntcb_rexmitcnt); }
// Drop the initial reference to the SYN TCB.
DerefSynTCB(SynTCB, TCBHandle); return NewTCB; }
//* FindSynTCB - Find a SynTCB in the syntcb table. Convert to
// full TCB if necessary and indicates what actions should
// be taken with the returned TCB
//
// 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.
// RcvInfo - Received segment information.
// Size - Size of payload in TCP segment.
// index - The index for the SynTCBTable.
// Output: Action - Action upon return of this function that
// should be performed. Flags are
// SYN_PKT_SEND_RST
// SYN_PKT_RST_RCVD
// SYN_PKT_DROP
// Flags are defined in tcb.h
//
// Returns: Pointer to synTCB found, or NULL if none.
//
TCB * FindSynTCB(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort, TCPRcvInfo RcvInfo, uint Size, uint index, PUCHAR Action) { ulong Partition; SYNTCB *SynTCB; Queue *Scan; TCB * RcvTCB; uchar rexmitCnt; Partition = GET_PARTITION(index);
*Action = 0; CTEGetLockAtDPC(&pSynTCBTableLock[Partition]);
for (Scan = QHEAD(&SYNTCBTable[index]); Scan != QEND(&SYNTCBTable[index]); Scan = QNEXT(Scan)) {
SynTCB = QSTRUCT(SYNTCB, Scan, syntcb_link); CTEStructAssert(SynTCB, syntcb);
if (IP_ADDR_EQUAL(SynTCB->syntcb_daddr, Dest) && SynTCB->syntcb_dport == DestPort && IP_ADDR_EQUAL(SynTCB->syntcb_saddr, Src) && SynTCB->syntcb_sport == SrcPort) { SeqNum SendUna = SynTCB->syntcb_sendnext - 1; SeqNum SendMax = SynTCB->syntcb_sendnext;
CTEGetLockAtDPC(&SynTCB->syntcb_lock);
rexmitCnt = SynTCB->syntcb_rexmitcnt; // 1st, we need to verify the sequence number.
// RcvWindow should be 0 here.
if (!SEQ_EQ(RcvInfo.tri_seq, SynTCB->syntcb_rcvnext)) { CTEFreeLockFromDPC(&SynTCB->syntcb_lock); CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]);
// Normally, we should send an ACK back but we
// will wait for the retransmit timer to expire.
*Action = SYN_PKT_DROP; return NULL; } // 2nd, we check for RST, if set, we terminate connection.
if (RcvInfo.tri_flags & TCP_FLAG_RST) { *Action = SYN_PKT_RST_RCVD;
// 3rd, security and precedence check, does not apply
// 4th, check for SYN
} else if (RcvInfo.tri_flags & TCP_FLAG_SYN) { *Action = SYN_PKT_SEND_RST; // 5th, check for ACK
} else if (!(RcvInfo.tri_flags & TCP_FLAG_ACK)) {
// We return right here because in this case we
// actually do not want to change the SynTCB
CTEFreeLockFromDPC(&SynTCB->syntcb_lock); CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]);
*Action = SYN_PKT_DROP; return NULL;
// 5th, continue to check for validity of ACK
} else if (SEQ_GTE(SendUna, RcvInfo.tri_ack) || SEQ_GT(RcvInfo.tri_ack, SendMax)) {
*Action = SYN_PKT_SEND_RST; }
// At this point, if some action needs to be taken
// the SynTCB needs to be killed
if (*Action) { SynTCB->syntcb_flags &= ~IN_SYNTCB_TABLE; REMOVEQ(&SynTCB->syntcb_link); CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]);
TcpInvokeCcb(TCP_CONN_SYN_RCVD, TCP_CONN_CLOSED, &SynTCB->syntcb_addrbytes, 0); DropHalfOpenTCB(SynTCB->syntcb_rexmitcnt);
DerefSynTCB(SynTCB, DISPATCH_LEVEL); return NULL; }
// If we got here we know we have to convert the SynTCB to
// a full TCB
SynTCB->syntcb_flags &= ~IN_SYNTCB_TABLE; REMOVEQ(&SynTCB->syntcb_link); CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]);
RcvTCB = RemoveAndInsertSynTCB(SynTCB, DISPATCH_LEVEL); if (RcvTCB == NULL) { // Could not allocate a TCB.
*Action = SYN_PKT_SEND_RST; return NULL; } return RcvTCB; } // end of if we found a matching SynTCB
} // end of for loop scanning for SynTCBs
CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]); // 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; ulong index, Partition;
*hash = index = TCB_HASH(Dest, Src, DestPort, SrcPort);
Partition = GET_PARTITION(index);
if (AtDispatch) { CTEGetLockAtDPC(&pTCBTableLock[Partition]); } 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); CTEFreeLockFromDPC(&pTCBTableLock[Partition]);
return FoundTCB;
} else FoundTCB = FoundTCB->tcb_next; }
if (AtDispatch) { CTEFreeLockFromDPC(&pTCBTableLock[Partition]); } 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) { Queue *NextLink, *PrevLink; TWTCB *AdjacentTwTcb;
CTEStructAssert(Twtcb, twtcb); ASSERT(Twtcb->twtcb_flags & IN_TWQUEUE);
// Update the delta queue elements. If this element is not the last one,
// update the retransmit ticks of the next element. Otherwise, if this is
// not the only element, update the delta ticks of the previous element.
NextLink = QNEXT(&Twtcb->twtcb_TWQueue); PrevLink = QPREV(&Twtcb->twtcb_TWQueue);
if (NextLink != QEND(&TWQueue[Partition])) {
AdjacentTwTcb = STRUCT_OF(TWTCB, NextLink, twtcb_TWQueue); AdjacentTwTcb->twtcb_rexmittimer += Twtcb->twtcb_rexmittimer; } else if (PrevLink != &TWQueue[Partition]) {
AdjacentTwTcb = STRUCT_OF(TWTCB, PrevLink, twtcb_TWQueue); AdjacentTwTcb->twtcb_delta = AdjacentTwTcb->twtcb_delta - (MAX_REXMIT_TO - Twtcb->twtcb_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; TCB *PrevTCB; TWTCB *TWTcb; uint Partition = TimWaitTCB->tcb_partition; Queue* Scan;
#if DBG
uint Found = FALSE; #endif
CTEStructAssert(TimWaitTCB, tcb);
if (!(TimWaitTCB->tcb_flags & IN_TCB_TABLE)) { CTELockHandle TcbHandle; CTEGetLock(&TimWaitTCB->tcb_lock, &TcbHandle); TryToCloseTCB(TimWaitTCB, TCB_CLOSE_ABORTED, TcbHandle); CTEGetLock(&TimWaitTCB->tcb_lock, &TcbHandle); DerefTCB(TimWaitTCB, TcbHandle); return FALSE; }
CTEGetLock(&pTCBTableLock[Partition], &TableHandle);
CTEGetLockAtDPC(&TimWaitTCB->tcb_lock);
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) {
// Could not allocate Time-wait TCB; so, notify.
TcpInvokeCcb(TCP_CONN_TIME_WAIT, TCP_CONN_CLOSED, &TimWaitTCB->tcb_addrbytes, 0); DerefTCB(TimWaitTCB, DISPATCH_LEVEL); 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_rcvnext = TimWaitTCB->tcb_rcvnext; TWTcb->twtcb_sendnext = TimWaitTCB->tcb_sendnext;
ASSERT(TimWaitTCB->tcb_sendnext == TimWaitTCB->tcb_senduna); #if DBG
TWTcb->twtcb_flags = 0; #endif
CTEGetLockAtDPC(&pTWTCBTableLock[Partition]);
// Free the parent tcb for this connection
DerefTCB(TimWaitTCB, DISPATCH_LEVEL);
// 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]);
InsertInTimewaitQueue(TWTcb, Partition);
CTEFreeLock(&pTWTCBTableLock[Partition], TableHandle);
InterlockedIncrement((PLONG)&numtwqueue); //debug purpose
return TRUE; }
//* 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);
// The time-wait expired and we have to notify.
TcpInvokeCcb(TCP_CONN_TIME_WAIT, TCP_CONN_CLOSED, &RemovedTCB->twtcb_addrbytes, 0);
REMOVEQ(&RemovedTCB->twtcb_link); InterlockedDecrement((PLONG)&TStats.ts_numconns);
RemoveFromTimewaitQueue(RemovedTCB, Partition); InterlockedDecrement((PLONG)&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); }
//* AssassinateTWTCB - Assassinate the time wait TCB, if possible.
//
// A TCB in time wait can be assasinated if (a) the new TCB being created is
// because of passive open and (b) the incoming sequence number on the SYN is
// greater or equal to the next expected sequence number on the connection.
// The sequence number to be used to send the outgoing SYN is derived from the
// next expected send sequence number. If these conditions are satisfied, the
// Time-wait TCB is removed and freed.
//
//
// Input: TwTcb - Time wait TCB to be replaced.
// RecvNext - Next Sequence number after the SYN's sequence.
// SendNext - [OUT] ISN to be used on the new connection.
// Partition - Partition to which the TCB belongs to.
//
// Returns: TRUE if the assassination was possible, FALSE otherwise.
//
__inline BOOLEAN AssassinateTWTCB(TWTCB *TwTcb, SeqNum RecvNext, SeqNum *SendNext, uint Partition) { if (SEQ_GT(RecvNext, TwTcb->twtcb_rcvnext)) { *SendNext= TwTcb->twtcb_sendnext + 128000; RemoveTWTCB(TwTcb, Partition); FreeTWTCB(TwTcb); return TRUE; } return FALSE; }
//* 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; TCB *CurrentTCB; uint Partition; Queue *Scan, *WhereToInsert;
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;
// Begin by looking for duplicates of the new SYNTCB in the TCB table.
// If we find one, bail out.
CurrentTCB = TCBTable[TCBIndex];
while (CurrentTCB != NULL) { 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 { CurrentTCB = CurrentTCB->tcb_next; } }
// Retain our lock on the TCB table, and look next for a duplicate
// in the TWTCB table.
CTEGetLockAtDPC(&pTWTCBTableLock[Partition]); for (Scan = QHEAD(&TWTCBTable[TCBIndex]); Scan != QEND(&TWTCBTable[TCBIndex]); Scan = QNEXT(Scan)) { TWTCB* CurrentTWTCB = STRUCT_OF(TWTCB, Scan, twtcb_link);
if (IP_ADDR_EQUAL(CurrentTWTCB->twtcb_daddr, NewTCB->syntcb_daddr) && (CurrentTWTCB->twtcb_dport == NewTCB->syntcb_dport) && IP_ADDR_EQUAL(CurrentTWTCB->twtcb_saddr, NewTCB->syntcb_saddr) && (CurrentTWTCB->twtcb_sport == NewTCB->syntcb_sport)) {
if (AssassinateTWTCB(CurrentTWTCB, NewTCB->syntcb_rcvnext, &NewTCB->syntcb_sendnext, Partition)) { break; } else { CTEFreeLockFromDPC(&pTWTCBTableLock[Partition]); CTEFreeLock(&pTCBTableLock[Partition], *TableHandle); return FALSE; } } } CTEFreeLockFromDPC(&pTWTCBTableLock[Partition]);
// Finally, check for duplicates in the SYNTCB table, and at the same time
// find the insertion point for the new entry.
CTEGetLockAtDPC(&pSynTCBTableLock[Partition]); CTEGetLockAtDPC(&NewTCB->syntcb_lock);
WhereToInsert = NULL; for (Scan = QHEAD(&SYNTCBTable[TCBIndex]); Scan != QEND(&SYNTCBTable[TCBIndex]); Scan = QNEXT(Scan)) {
SYNTCB *CurrentSynTCB = STRUCT_OF(SYNTCB, Scan, syntcb_link);
if (IP_ADDR_EQUAL(CurrentSynTCB->syntcb_daddr, NewTCB->syntcb_daddr) && (CurrentSynTCB->syntcb_dport == NewTCB->syntcb_dport) && IP_ADDR_EQUAL(CurrentSynTCB->syntcb_saddr, NewTCB->syntcb_saddr) && (CurrentSynTCB->syntcb_sport == NewTCB->syntcb_sport)) {
CTEFreeLockFromDPC(&NewTCB->syntcb_lock); CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]); CTEFreeLock(&pTCBTableLock[Partition], *TableHandle); return FALSE; }
if (WhereToInsert == NULL && Scan > &NewTCB->syntcb_link) { WhereToInsert = Scan; } }
if (WhereToInsert == NULL) { WhereToInsert = Scan; }
ENQUEUE(WhereToInsert, &NewTCB->syntcb_link);
NewTCB->syntcb_flags |= IN_SYNTCB_TABLE;
CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]); CTEFreeLockFromDPC(&pTCBTableLock[Partition]);
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.
// ForceInsert - If there is a time wait TCB, it can be replaced.
//
// Returns: TRUE if we inserted, false if we didn't.
//
uint InsertTCB(TCB * NewTCB, BOOLEAN ForceInsert) { uint TCBIndex; CTELockHandle TableHandle; TCB *PrevTCB, *CurrentTCB; TCB *WhereToInsert; 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); NewTCB->tcb_partition = Partition;
// Look first for a duplicate in the SYNTCB table.
if (SynAttackProtect) { CTEGetLockAtDPC(&pSynTCBTableLock[Partition]); for (Scan = QHEAD(&SYNTCBTable[TCBIndex]); Scan != QEND(&SYNTCBTable[TCBIndex]); Scan = QNEXT(Scan)) { SYNTCB *CurrentSynTCB = STRUCT_OF(SYNTCB, Scan, syntcb_link); if (IP_ADDR_EQUAL(CurrentSynTCB->syntcb_daddr, NewTCB->tcb_daddr) && (CurrentSynTCB->syntcb_dport == NewTCB->tcb_dport) && IP_ADDR_EQUAL(CurrentSynTCB->syntcb_saddr, NewTCB->tcb_saddr) && (CurrentSynTCB->syntcb_sport == NewTCB->tcb_sport)) { CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]); CTEFreeLockFromDPC(&NewTCB->tcb_lock); CTEFreeLock(&pTCBTableLock[Partition], TableHandle); return FALSE; } } CTEFreeLockFromDPC(&pSynTCBTableLock[Partition]); }
// Next, look for a duplicate in the TWTCB table.
CTEGetLockAtDPC(&pTWTCBTableLock[Partition]);
for (Scan = QHEAD(&TWTCBTable[TCBIndex]); Scan != QEND(&TWTCBTable[TCBIndex]); Scan = QNEXT(Scan)) {
TWTCB *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)) {
if (ForceInsert && AssassinateTWTCB(CurrentTWTCB, NewTCB->tcb_rcvnext, &NewTCB->tcb_sendnext, Partition)) { break; } else { CTEFreeLockFromDPC(&pTWTCBTableLock[Partition]); CTEFreeLockFromDPC(&NewTCB->tcb_lock); CTEFreeLock(&pTCBTableLock[Partition], TableHandle); return FALSE; } } }
CTEFreeLockFromDPC(&pTWTCBTableLock[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); 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.
if (WhereToInsert == NULL) { WhereToInsert = PrevTCB; } NewTCB->tcb_next = WhereToInsert->tcb_next; WhereToInsert->tcb_next = NewTCB; NewTCB->tcb_flags |= IN_TCB_TABLE;
// Perform early insertion of the new TCB, since it's likely to have
// a timer scheduled right away in any case.
EarlyInsertTime = TCPTime + 2; if (EarlyInsertTime == 0) { EarlyInsertTime = 1; }
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); CTEFreeLock(&pTCBTableLock[Partition], TableHandle);
InterlockedIncrement((PLONG)&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.
// PreviousState - Previous state of the TCB.
//
// Returns: TRUE if it's OK to free it, FALSE otherwise.
//
uint RemoveTCB(TCB * RemovedTCB, uint PreviousState) { uint TCBIndex; TCB *PrevTCB; #if DBG
uint Found = FALSE; #endif
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) {
TcpInvokeCcb(CONN_STATE(PreviousState), TCP_CONN_CLOSED, &RemovedTCB->tcb_addrbytes, 0); 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((PLONG)&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); 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); return FALSE;
} else {
CTEFreeLockFromDPC(&PendingFreeLock.Lock); 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. We start with the assumption that all types of offloads
// are permitted.
NewTCB->tcb_slowcount = 1; NewTCB->tcb_fastchk = TCP_FLAG_ACK | TCP_FLAG_SLOW; NewTCB->tcb_delackticks = DEL_ACK_TICKS; NewTCB->tcb_allowedoffloads = TCP_IP_OFFLOAD_TYPES; NewTCB->tcb_partition = 0;
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); }
NTSTATUS GetTCBInfo(PTCP_FINDTCB_RESPONSE TCBInfo, IPAddr Dest, IPAddr Src, ushort DestPort, ushort SrcPort) { TCB *FoundTCB; CTELockHandle Handle, 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); FoundTCB = (TCB*)FindTCBTW(Src, Dest, DestPort, SrcPort, Index); if (!FoundTCB) { CTEFreeLock(&pTWTCBTableLock[Partition], TwHandle); return STATUS_NOT_FOUND; } else { timedwait = TRUE; } } else { TwHandle = Handle; } // 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);
LastTimeoutTime = 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
|