/********************************************************************/
/**                     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