summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/tcp/tcb.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcb.c1524
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
+