3544 lines
112 KiB
C
3544 lines
112 KiB
C
|
/********************************************************************/
|
||
|
/** 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
|
||
|
|