diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/tcpip/tcp/tcb.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/tcp/tcb.c | 1524 |
1 files changed, 1524 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/tcb.c b/private/ntos/tdi/tcpip/tcp/tcb.c new file mode 100644 index 000000000..4b01c66d3 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcb.c @@ -0,0 +1,1524 @@ +/********************************************************************/ +/** 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 "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "info.h" +#include "tcpcfg.h" +#include "tcpdeliv.h" +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL + +DEFINE_LOCK_STRUCTURE(TCBTableLock) + +uint TCPTime; +uint TCBWalkCount; + +TCB *TCBTable[TCB_TABLE_SIZE]; + +TCB *LastTCB; + +TCB *PendingFreeList; + +#ifndef NT +TCB *FreeTCBList = NULL; // TCB free list +#else +SLIST_HEADER FreeTCBList; +#endif + +DEFINE_LOCK_STRUCTURE(FreeTCBListLock) // Lock to protect TCB free list. + +EXTERNAL_LOCK(AddrObjTableLock) + +uint CurrentTCBs = 0; +uint MaxTCBs = 0xffffffff; + +#ifdef NT +#define MAX_FREE_TCBS 1000 +#else +#define MAX_FREE_TCBS 10 +#endif + +#define NUM_DEADMAN_TICKS MS_TO_TICKS(1000) + +uint MaxFreeTCBs = MAX_FREE_TCBS; +uint DeadmanTicks; + +CTETimer TCBTimer; + +extern IPInfo LocalNetInfo; + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int InitTCB(void); +void UnInitTCB(void); + +#pragma alloc_text(INIT, InitTCB) +#pragma alloc_text(INIT, UnInitTCB) + +#endif // ALLOC_PRAGMA +#endif + +#ifdef RASAUTODIAL +extern ACD_DRIVER AcdDriverG; + +VOID +TCPNoteNewConnection( + IN TCB *pTCB, + IN CTELockHandle Handle + ); +#endif // RASAUTODIAL + + +//* 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. +// +// 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; + uint i; + + + 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; + CTEFreeLock(&CurrentTCB->tcb_lock, Handle); + + // We've filled it in. Now update the context. + if (CurrentTCB->tcb_next != NULL) { + TCContext->tcc_tcb = CurrentTCB->tcb_next; + return TRUE; + } else { + // NextTCB is NULL. Loop through the TCBTable looking for a new + // one. + i = TCContext->tcc_index + 1; + while (i < TCB_TABLE_SIZE) { + if (TCBTable[i] != NULL) { + TCContext->tcc_tcb = TCBTable[i]; + TCContext->tcc_index = i; + 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; + + 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); + break; + } + i++; + } while (i < TCB_TABLE_SIZE); + + if (CurrentTCB != NULL) { + TCContext->tcc_index = i; + TCContext->tcc_tcb = CurrentTCB; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + 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; + + CTEAssert(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); + CTEAssert(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). + CTEAssert(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; + + 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); + CTEAssert(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. + CTEAssert((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); + CTEAssert(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 { + CTEAssert(SeqTCB->tcb_cursend == NULL); + CTEAssert(AmtForward == 0); + } + + } + + CheckTCBSends(SeqTCB); + +} + +#ifdef NT + +//* 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. +// +void +TCPAbortAndIndicateDisconnect( + uint ConnectionContext + ) +{ + TCB *AbortTCB; + CTELockHandle ConnTableHandle, TCBHandle; + TCPConn *Conn; + + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID(ConnectionContext); + + 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(&ConnTableLock, TCBHandle); + + AbortTCB->tcb_refcnt++; + AbortTCB->tcb_flags |= NEED_RST; // send a reset if connected + TryToCloseTCB(AbortTCB, TCB_CLOSE_ABORTED, ConnTableHandle); + + 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; + } + } + } + + CTEFreeLock(&ConnTableLock, ConnTableHandle); +} + +#endif // NT + + +//* 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, TCBHandle; + uint i; + TCB *CurrentTCB; + uint Delayed = FALSE; + uint CallRcvComplete; + + + // Update our free running counter. + + TCPTime++; + + CTEInterlockedAddUlong(&TCBWalkCount, 1, &TCBTableLock); + + +#ifndef VXD + TCBHandle = DISPATCH_LEVEL; +#endif + + // Loop through each bucket in the table, going down the chain of + // TCBs on the bucket. + for (i = 0; i < TCB_TABLE_SIZE; i++) { + TCB *TempTCB; + uint maxRexmitCnt; + + CurrentTCB = TCBTable[i]; + + while (CurrentTCB != NULL) { + CTEStructAssert(CurrentTCB, tcb); + CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle); + + // If it's CLOSING or CLOSED, skip it. + if (CurrentTCB->tcb_state == TCB_CLOSED || CLOSING(CurrentTCB)) { + + TempTCB = CurrentTCB->tcb_next; + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + CurrentTCB = TempTCB; + continue; + } + + CheckTCBSends(CurrentTCB); + CheckTCBRcv(CurrentTCB); + + // First check the rexmit timer. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_rexmittimer)) { + // The timer is running. + if (--(CurrentTCB->tcb_rexmittimer) == 0) { + + // And it's fired. Figure out what to do now. + + // 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) { +#ifdef SYN_ATTACK + // + // 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 = MaxConnectResponseRexmitCount; + +#endif + } + else { + maxRexmitCnt = MaxDataRexmitCount; + } + } + + // If we've run out of retransmits or we're in FIN_WAIT2, + // time out. + if (CurrentTCB->tcb_rexmitcnt > maxRexmitCnt) { + + CTEAssert(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: + CurrentTCB->tcb_refcnt++; + TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT, TCBHandle); + + RemoveTCBFromConn(CurrentTCB); + NotifyOfDisc(CurrentTCB, NULL, TDI_TIMED_OUT); + +#ifdef SYN_ATTACK + if (SynAttackProtect) { + + CTELockHandle Handle; + + CTEGetLockAtDPC(&SynAttLock, &Handle); + // + // We have put the connection in the closed state. + // Decrement the counters for keeping track of half + // open connections + // + CTEAssert((TCPHalfOpen > 0) && (TCPHalfOpenRetried > 0)); + TCPHalfOpen--; + TCPHalfOpenRetried--; + CTEFreeLockFromDPC(&SynAttLock, Handle); + } +#endif + + CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle); + DerefTCB(CurrentTCB, TCBHandle); + + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } + + 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); + + CTEAssert(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 && + (CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt+1)/2))) { + (*LocalNetInfo.ipi_checkroute)(CurrentTCB->tcb_daddr, + CurrentTCB->tcb_saddr); + } + + // 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: + SendSYN(CurrentTCB, TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + 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: + TryToCloseTCB(CurrentTCB, TCB_CLOSE_SUCCESS, + TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + default: + break; + } + } + } + + + // Now check the SWS deadlock timer.. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_swstimer)) { + // The timer is running. + if (--(CurrentTCB->tcb_swstimer) == 0) { + // 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_RUNNING(CurrentTCB->tcb_pushtimer)) { + // The timer is running. Decrement it. + if (--(CurrentTCB->tcb_pushtimer) == 0) { + // It's fired. + PushData(CurrentTCB); + Delayed = TRUE; + } + } + + // Check the delayed ack timer. + if (TCB_TIMER_RUNNING(CurrentTCB->tcb_delacktimer)) { + // The timer is running. + if (--(CurrentTCB->tcb_delacktimer) == 0) { + // And it's fired. Set up to send an ACK. + + Delayed = TRUE; + DelayAction(CurrentTCB, NEED_ACK); + } + + } + + // Finally check the keepalive timer. + if (CurrentTCB->tcb_state == TCB_ESTAB) { + if (CurrentTCB->tcb_flags & KEEPALIVE) { + uint Delta; + + Delta = TCPTime - CurrentTCB->tcb_alive; + if (Delta > KeepAliveTime) { + Delta -= KeepAliveTime; + if (Delta > (CurrentTCB->tcb_kacount * KAInterval)) { + if (CurrentTCB->tcb_kacount < MaxDataRexmitCount) { + SendKA(CurrentTCB, TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } else + goto TimeoutTCB; + } + } else + CurrentTCB->tcb_kacount = 0; + } + } + + + + // 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)) { + TCPConnReq *ConnReq = CurrentTCB->tcb_connreq; + + CTEAssert(ConnReq != NULL); + if (TCB_TIMER_RUNNING(ConnReq->tcr_timeout)) { + // Timer is running. + if (--(ConnReq->tcr_timeout) == 0) { + // The connection timer has timed out. + TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT, + TCBHandle); + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } + } + } + +#ifdef RASAUTODIAL + // + // Check to see if we have to notify the + // automatic connection driver about this + // connection. + // + if (CurrentTCB->tcb_flags & ACD_CONN_NOTIF) { + BOOLEAN fEnabled; + CTELockHandle AcdHandle; + + // + // Clear the ACD_CONN_NOTIF flag + // and release the TCB table lock. + // + CurrentTCB->tcb_flags &= ~ACD_CONN_NOTIF; + // + // 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); + // + // Reacquire the TCB table lock + // and get the next TCB to process. + // + CurrentTCB = FindNextTCB(i, CurrentTCB); + continue; + } +#endif // RASAUTODIAL + + // Timer isn't running, or didn't fire. + TempTCB = CurrentTCB->tcb_next; + CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle); + CurrentTCB = TempTCB; + } + } + + + // 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. + if (DeadmanTicks == TCPTime) { + CallRcvComplete = TRUE; + DeadmanTicks += NUM_DEADMAN_TICKS; + } else + CallRcvComplete = FALSE; + + + // 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. + + CTEGetLockAtDPC(&TCBTableLock, &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); + + CurrentTCB->tcb_walkcount--; + if (CurrentTCB->tcb_walkcount <= 1) { + *(TCB **)&PrevTCB->tcb_delayq.q_next = + (TCB *)CurrentTCB->tcb_delayq.q_next; + + if (CurrentTCB->tcb_walkcount == 0) { + FreeTCB(CurrentTCB); + } + } else { + PrevTCB = CurrentTCB; + } + } while (PrevTCB->tcb_delayq.q_next != NULL); + } + + TCBWalkCount--; + CTEFreeLockFromDPC(&TCBTableLock, TableHandle); + + // Do AddrCheckTable cleanup + + if (AddrCheckTable) { + + TCPAddrCheckElement *Temp; + + CTEGetLockAtDPC(&AddrObjTableLock, &TableHandle); + + for (Temp=AddrCheckTable; Temp<AddrCheckTable+NTWMaxConnectCount; Temp++) { + if (Temp->TickCount > 0) { + if ((--(Temp->TickCount)) == 0) { + Temp->SourceAddress = 0; + } + } + } + + CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle); + } + + // Restart the timer again. + CTEStartTimer(&TCBTimer, MS_PER_TICK, TCBTimeout, NULL); + + if (Delayed) + ProcessTCBDelayQ(); + + 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); + + CTEAssert(CheckTCB->tcb_mss > 0); + + // + // 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; + 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. + CTEGetLock(&TCBTableLock, &Handle); + + for (i = 0; i < TCB_TABLE_SIZE; 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); + if (!CLOSING(CurTCB)) { + CurTCB->tcb_refcnt++; + CTEFreeLock(&TCBTableLock, 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(&TCBTableLock, &Handle); + } else + CTEFreeLock(&CurTCB->tcb_lock, TCBHandle); + + CurTCB = FindNextTCB(i, CurTCB); + } else { + CurTCB = CurTCB->tcb_next; + } + } + } + + CTEFreeLock(&TCBTableLock, Handle); +} + + +//* 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. 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. +// +TCB * +FindTCB(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort) +{ + TCB *FoundTCB; + + if (LastTCB != NULL) { + CTEStructAssert(LastTCB, tcb); + if (IP_ADDR_EQUAL(LastTCB->tcb_daddr, Dest) && + LastTCB->tcb_dport == DestPort && + IP_ADDR_EQUAL(LastTCB->tcb_saddr, Src) && + LastTCB->tcb_sport == SrcPort) + return LastTCB; + } + + // Didn't find it in our 1 element cache. + FoundTCB = TCBTable[TCB_HASH(Dest, Src, DestPort, SrcPort)]; + 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; + return FoundTCB; + } else + FoundTCB = FoundTCB->tcb_next; + } + + return FoundTCB; + + +} + + +//* 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; + + CTEAssert(NewTCB != NULL); + CTEStructAssert(NewTCB, tcb); + TCBIndex = TCB_HASH(NewTCB->tcb_daddr, NewTCB->tcb_saddr, + NewTCB->tcb_dport, NewTCB->tcb_sport); + + CTEGetLock(&TCBTableLock, &TableHandle); + CTEGetLockAtDPC(&NewTCB->tcb_lock, &TCBHandle); + + // 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(&TCBTableLock, TableHandle); + return FALSE; + + } else { + + if (WhereToInsert == NULL && CurrentTCB > NewTCB) { + WhereToInsert = PrevTCB; + } + + CTEStructAssert(PrevTCB->tcb_next, tcb); + PrevTCB = PrevTCB->tcb_next; + } + + } + + if (WhereToInsert == NULL) { + WhereToInsert = PrevTCB; + } + + NewTCB->tcb_next = WhereToInsert->tcb_next; + WhereToInsert->tcb_next = NewTCB; + NewTCB->tcb_flags |= IN_TCB_TABLE; + TStats.ts_numconns++; + + CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle); + CTEFreeLock(&TCBTableLock, TableHandle); + 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; +#ifdef DEBUG + uint Found = FALSE; +#endif + + CTEStructAssert(RemovedTCB, tcb); + + 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; + TStats.ts_numconns--; +#ifdef DEBUG + Found = TRUE; +#endif + break; + } + PrevTCB = PrevTCB->tcb_next; +#ifdef DEBUG + if (PrevTCB != NULL) + CTEStructAssert(PrevTCB, tcb); +#endif + } while (PrevTCB != NULL); + + CTEAssert(Found); + + } + + if (LastTCB == RemovedTCB) + LastTCB = NULL; + + if (TCBWalkCount == 0) { + return TRUE; + } else { + RemovedTCB->tcb_walkcount = TCBWalkCount + 1; + *(TCB **)&RemovedTCB->tcb_delayq.q_next = PendingFreeList; + PendingFreeList = RemovedTCB; + return FALSE; + + } + + + +} + + +//* ScavengeTCB - Scavenge a TCB that's in the TIME_WAIT state. +// +// Called when we're running low on TCBs, and need to scavenge one from +// TIME_WAIT state. We'll walk through the TCB table, looking for the oldest +// TCB in TIME_WAIT. We'll remove and return a pointer to that TCB. If we +// don't find any TCBs in TIME_WAIT, we'll return NULL. +// +// Input: Nothing. +// +// Returns: Pointer to a reusable TCB, or NULL. +// +TCB * +ScavengeTCB(void) +{ + CTELockHandle TableHandle, TCBHandle, FoundLock; + uint Now = CTESystemUpTime(); + uint Delta = 0; + uint i; + TCB *FoundTCB = NULL, *PrevFound; + TCB *CurrentTCB, *PrevTCB; + + CTEGetLock(&TCBTableLock, &TableHandle); + + if (TCBWalkCount != 0) { + CTEFreeLock(&TCBTableLock, TableHandle); + return NULL; + } + + for (i = 0; i < TCB_TABLE_SIZE; i++) { + + PrevTCB = STRUCT_OF(TCB, &TCBTable[i], tcb_next); + CurrentTCB = PrevTCB->tcb_next; + + while (CurrentTCB != NULL) { + CTEStructAssert(CurrentTCB, tcb); + + CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle); + if (CurrentTCB->tcb_state == TCB_TIME_WAIT && + (CurrentTCB->tcb_refcnt == 0) && !CLOSING(CurrentTCB)){ + if (FoundTCB == NULL || ((Now - CurrentTCB->tcb_alive) > Delta)) { + // Found a new 'older' TCB. If we already have one, free + // the lock on him and get the lock on the new one. + if (FoundTCB != NULL) + CTEFreeLock(&FoundTCB->tcb_lock, TCBHandle); + else + FoundLock = TCBHandle; + + PrevFound = PrevTCB; + FoundTCB = CurrentTCB; + Delta = Now - FoundTCB->tcb_alive; + } else + CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle); + } else + CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle); + + // Look at the next one. + PrevTCB = CurrentTCB; + CurrentTCB = PrevTCB->tcb_next; + } + } + + // If we have one, pull him from the list. + if (FoundTCB != NULL) { + PrevFound->tcb_next = FoundTCB->tcb_next; + FoundTCB->tcb_flags &= ~IN_TCB_TABLE; + // Close the RCE on this guy. + (*LocalNetInfo.ipi_closerce)(FoundTCB->tcb_rce); + TStats.ts_numconns--; + if (LastTCB == FoundTCB) { + LastTCB = NULL; + } + CTEFreeLock(&FoundTCB->tcb_lock, FoundLock); + } + + CTEFreeLock(&TCBTableLock, TableHandle); + return FoundTCB; +} + +//* 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; + + // First, see if we have one on the free list. The code for doing this + // is a little different between the NT and VxD worlds. +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList(&FreeTCBList, &FreeTCBListLock); + + if (BufferLink != NULL) { + NewTCB = STRUCT_OF(TCB, BufferLink, tcb_next); + CTEStructAssert(NewTCB, tcb); + } +#else // NT + NewTCB = FreeTCBList; + if (NewTCB != NULL) { + CTEStructAssert(NewTCB, tcb); + FreeTCBList = NewTCB->tcb_next; + } +#endif // NT + + else { + + // We have none on the free list. If the total number of TCBs + // outstanding is more than we like to keep on the free list, try + // to scavenge a TCB from time wait. + if (CurrentTCBs < MaxFreeTCBs || ((NewTCB = ScavengeTCB()) == NULL)) { + if (CurrentTCBs < MaxTCBs) { + NewTCB = CTEAllocMem(sizeof(TCB)); + if (NewTCB == NULL) { + return NewTCB; + } + else { + CTEInterlockedAddUlong(&CurrentTCBs, 1, &FreeTCBListLock); + } + } else + return NULL; + } + } + + CTEAssert(NewTCB != NULL); + + CTEMemSet(NewTCB, 0, sizeof(TCB)); +#ifdef DEBUG + NewTCB->tcb_sig = tcb_signature; +#endif + INITQ(&NewTCB->tcb_sendq); + NewTCB->tcb_cursend = NULL; + NewTCB->tcb_alive = TCPTime; + // 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; + +#if FAST_RETRANSMIT + NewTCB->tcb_dup = 0; +#endif + + CTEInitLock(&NewTCB->tcb_lock); + + return NewTCB; +} + +//* 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) +{ +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; +#endif // NT + + +#ifndef NT + // + // Since we've moved to using sequenced lists for these resources, + // it's risky to actually free the memory here, so we won't do this + // for NT unless it becomes a problem. + if (CurrentTCBs > MaxFreeTCBs) { + CTEInterlockedAddUlong(&CurrentTCBs, (ulong) -1, &FreeTCBListLock); + CTEFreeMem(FreedTCB); + return; + } +#endif + +#ifdef NT + + CTEStructAssert(FreedTCB, tcb); + + BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedTCB->tcb_next), Next); + ExInterlockedPushEntrySList( + &FreeTCBList, + BufferLink, + &FreeTCBListLock + ); + +#else // NT + + CTEStructAssert(FreedTCB, tcb); + + FreedTCB->tcb_next = FreeTCBList; + FreeTCBList = FreedTCB; + +#endif // NT +} + +#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) +{ + int i; + + for (i = 0; i < TCB_TABLE_SIZE; i++) + TCBTable[i] = NULL; + + LastTCB = NULL; + +#ifdef NT + ExInitializeSListHead(&FreeTCBList); +#endif + + CTEInitLock(&TCBTableLock); + CTEInitLock(&FreeTCBListLock); + + TCPTime = 0; + TCBWalkCount = 0; + DeadmanTicks = NUM_DEADMAN_TICKS; + CTEInitTimer(&TCBTimer); + CTEStartTimer(&TCBTimer, MS_PER_TICK, TCBTimeout, NULL); + + 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) +{ + CTEStopTimer(&TCBTimer); + return; +} + +#pragma END_INIT + |