diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/tcp/tcpsend.c | 2666 |
1 files changed, 2666 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/tcpsend.c b/private/ntos/tdi/tcpip/tcp/tcpsend.c new file mode 100644 index 000000000..6c97e9f8a --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpsend.c @@ -0,0 +1,2666 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPSEND.C - TCP send protocol code. +// +// This file contains the code for sending Data and Control segments. +// + +#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 "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" +#include "secfltr.h" + +#if FAST_RETRANSMIT +void +TCPFastSend(TCB *SendTCB, + PNDIS_BUFFER in_SendBuf, + uint in_SendOfs, + TCPSendReq *in_SendReq, + uint in_SendSize); +#endif + + + +#ifdef NT +SLIST_HEADER TCPSendFree; +#else +PNDIS_BUFFER TCPSendFree; +#endif + +DEFINE_LOCK_STRUCTURE(TCPSendFreeLock); + +ulong TCPCurrentSendFree; +ulong TCPMaxSendFree = TCP_MAX_HDRS; + +void *TCPProtInfo; // TCP protocol info for IP. + +#ifdef NT +SLIST_HEADER TCPSendReqFree; // Send req. free list. +#else +TCPSendReq *TCPSendReqFree; // Send req. free list. +#endif + +DEFINE_LOCK_STRUCTURE(TCPSendReqFreeLock); +DEFINE_LOCK_STRUCTURE(TCPSendReqCompleteLock); +uint NumTCPSendReq; // Current number of SendReqs in system. +uint MaxSendReq = 0xffffffff; // Maximum allowed number of SendReqs. + + +NDIS_HANDLE TCPSendBufferPool; + +typedef struct TCPHdrBPoolEntry { + struct TCPHdrBPoolEntry *the_next; + NDIS_HANDLE the_handle; + uchar *the_buffer; +} TCPHdrBPoolEntry; + +TCPHdrBPoolEntry *TCPHdrBPoolList; + +extern IPInfo LocalNetInfo; + +#ifdef CHICAGO +extern uchar TransportName[]; +#endif // CHICAGO + +EXTERNAL_LOCK(TCBTableLock) + +// +// All of the init code can be discarded. +// +#ifdef NT + +int InitTCPSend(void); +void UnInitTCPSend(void); + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, InitTCPSend) +#pragma alloc_text(INIT, UnInitTCPSend) + +#endif // ALLOC_PRAGMA +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context, + uint Added); +#endif + +extern void ResetSendNext(TCB *SeqTCB, SeqNum NewSeq); + + +//* GrowTCPHeaderList - Try to grow the tcp header list. +// +// Called when we run out of buffers on the TCP header list, and need +// to grow it. We look to see if we're already at the maximum size, and +// if not we'll allocate the need structures and free them to the list. +// +// Input: Nothing. +// +// Returns: A pointer to a new TCP header buffer if we have one, or NULL. +// +PNDIS_BUFFER +GrowTCPHeaderList(void) +{ + TCPHdrBPoolEntry *NewEntry; + CTELockHandle Handle; + NDIS_STATUS Status; + uint HeaderSize; + uchar *TCPSendHP; + uint i; + PNDIS_BUFFER Buffer; + PNDIS_BUFFER ReturnBuffer; + + CTEGetLock(&TCPSendFreeLock, &Handle); + + if (TCPCurrentSendFree < TCPMaxSendFree) { + + // Still room to grow the list. + NewEntry = CTEAllocMem(sizeof(TCPHdrBPoolEntry)); + + if (NewEntry == NULL) { + // Couldn't get the memory. + CTEFreeLock(&TCPSendFreeLock, Handle); + return NULL; + } + + + NdisAllocateBufferPool(&Status, &NewEntry->the_handle, + NUM_TCP_HEADERS); + + if (Status != NDIS_STATUS_SUCCESS) { + // Couldn't get a new set of buffers. Fail. + CTEFreeMem(NewEntry); + CTEFreeLock(&TCPSendFreeLock, Handle); + return NULL; + } + + HeaderSize = sizeof(TCPHeader) + + MAX(MSS_OPT_SIZE, sizeof(SendCmpltContext)) + + LocalNetInfo.ipi_hsize; + + TCPSendHP = CTEAllocMem(HeaderSize * NUM_TCP_HEADERS); + + if (TCPSendHP == NULL) { + NdisFreeBufferPool(NewEntry->the_handle); + CTEFreeMem(NewEntry); + CTEFreeLock(&TCPSendFreeLock, Handle); + return NULL; + } + + NewEntry->the_buffer = TCPSendHP; + TCPCurrentSendFree += NUM_TCP_HEADERS; + NewEntry->the_next = TCPHdrBPoolList; + TCPHdrBPoolList = NewEntry; + ReturnBuffer = NULL; + CTEFreeLock(&TCPSendFreeLock, Handle); + + for (i = 0; i < NUM_TCP_HEADERS; i++) { + NdisAllocateBuffer(&Status, &Buffer, NewEntry->the_handle, + TCPSendHP + (i * HeaderSize), HeaderSize); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); // This is probably harmless, but check. + break; + } + + NdisBufferLength(Buffer) = sizeof(TCPHeader); + + if (i != 0) + FreeTCPHeader(Buffer); + else + ReturnBuffer = Buffer; + } + + // Update the count with what we didn't allocate, if any. + CTEInterlockedAddUlong(&TCPCurrentSendFree, i - NUM_TCP_HEADERS, + &TCPSendFreeLock); + + return ReturnBuffer; + + } else { + // At the limit already. It's possible someone snuck in and grew + // the list before we got to, so check and see if it's still empty. +#ifdef VXD + if (TCPSendFree != NULL) { + ReturnBuffer = TCPSendFree; + TCPSendFree = NDIS_BUFFER_LINKAGE(ReturnBuffer); + +#else + PSINGLE_LIST_ENTRY BufferLink; + + CTEFreeLock(&TCPSendFreeLock, Handle); + + BufferLink = ExInterlockedPopEntrySList( + &TCPSendFree, + &TCPSendFreeLock + ); + + if (BufferLink != NULL) { + ReturnBuffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + } else + ReturnBuffer = NULL; + + return ReturnBuffer; +#endif +#ifdef VXD + } else + ReturnBuffer = NULL; +#endif + + } + + CTEFreeLock(&TCPSendFreeLock, Handle); + return ReturnBuffer; + + +} + +//* GetTCPHeader - Get a TCP header buffer. +// +// Called when we need to get a TCP header buffer. This routine is +// specific to the particular environment (VxD or NT). All we +// need to do is pop the buffer from the free list. +// +// Input: Nothing. +// +// Returns: Pointer to an NDIS buffer, or NULL is none. +// +PNDIS_BUFFER +GetTCPHeader(void) +{ + PNDIS_BUFFER NewBuffer; + +#ifdef VXD + NewBuffer = TCPSendFree; + if (NewBuffer != NULL) { + TCPSendFree = NDIS_BUFFER_LINKAGE(NewBuffer); + return NewBuffer; + } + +#else + + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList( + &TCPSendFree, + &TCPSendFreeLock + ); + if (BufferLink != NULL) { + NewBuffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + return NewBuffer; + } +#endif + else + return GrowTCPHeaderList(); +} + +//* FreeTCPHeader - Free a TCP header buffer. +// +// Called to free a TCP header buffer. +// +// Input: Buffer to be freed. +// +// Returns: Nothing. +// +void +FreeTCPHeader(PNDIS_BUFFER FreedBuffer) +{ + + CTEAssert(FreedBuffer != NULL); + + NdisBufferLength(FreedBuffer) = sizeof(TCPHeader); + +#ifdef VXD + + NDIS_BUFFER_LINKAGE(FreedBuffer) = TCPSendFree; + TCPSendFree = FreedBuffer; + +#else + + ExInterlockedPushEntrySList( + &TCPSendFree, + STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedBuffer->Next), Next), + &TCPSendFreeLock + ); + +#endif +} + +//* FreeSendReq - Free a send request structure. +// +// Called to free a send request structure. +// +// Input: FreedReq - Connection request structure to be freed. +// +// Returns: Nothing. +// +void +FreeSendReq(TCPSendReq *FreedReq) +{ +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + + CTEStructAssert(FreedReq, tsr); + + BufferLink = STRUCT_OF( + SINGLE_LIST_ENTRY, + &(FreedReq->tsr_req.tr_q.q_next), + Next + ); + + ExInterlockedPushEntrySList( + &TCPSendReqFree, + BufferLink, + &TCPSendReqFreeLock + ); + +#else // NT + + TCPSendReq **Temp; + + CTEStructAssert(FreedReq, tsr); + + Temp = (TCPSendReq **)&FreedReq->tsr_req.tr_q.q_next; + *Temp = TCPSendReqFree; + TCPSendReqFree = FreedReq; + +#endif // NT +} + +//* GetSendReq - Get a send request structure. +// +// Called to get a send request structure. +// +// Input: Nothing. +// +// Returns: Pointer to SendReq structure, or NULL if none. +// +TCPSendReq * +GetSendReq(void) +{ + TCPSendReq *Temp; + +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + Queue *QueuePtr; + TCPReq *ReqPtr; + + + BufferLink = ExInterlockedPopEntrySList( + &TCPSendReqFree, + &TCPSendReqFreeLock + ); + + if (BufferLink != NULL) { + QueuePtr = STRUCT_OF(Queue, BufferLink, q_next); + ReqPtr = STRUCT_OF(TCPReq, QueuePtr, tr_q); + Temp = STRUCT_OF(TCPSendReq, ReqPtr, tsr_req); + CTEStructAssert(Temp, tsr); + } + else { + if (NumTCPSendReq < MaxSendReq) + Temp = CTEAllocMem(sizeof(TCPSendReq)); + else + Temp = NULL; + + if (Temp != NULL) { + ExInterlockedAddUlong(&NumTCPSendReq, 1, &TCPSendReqFreeLock); +#ifdef DEBUG + Temp->tsr_req.tr_sig = tr_signature; + Temp->tsr_sig = tsr_signature; +#endif + } + } + +#else // NT + + Temp = TCPSendReqFree; + if (Temp != NULL) + TCPSendReqFree = (TCPSendReq *)Temp->tsr_req.tr_q.q_next; + else { + if (NumTCPSendReq < MaxSendReq) + Temp = CTEAllocMem(sizeof(TCPSendReq)); + else + Temp = NULL; + + if (Temp != NULL) { + NumTCPSendReq++; +#ifdef DEBUG + Temp->tsr_req.tr_sig = tr_signature; + Temp->tsr_sig = tsr_signature; +#endif + } + } + +#endif // NT + + return Temp; +} + + + +//* TCPSendComplete - Complete a TCP send. +// +// Called by IP when a send we've made is complete. We free the buffer, +// and possibly complete some sends. Each send queued on a TCB has a ref. +// count with it, which is the number of times a pointer to a buffer +// associated with the send has been passed to the underlying IP layer. We +// can't complete a send until that count it 0. If this send was actually +// from a send of data, we'll go down the chain of send and decrement the +// refcount on each one. If we have one going to 0 and the send has already +// been acked we'll complete the send. If it hasn't been acked we'll leave +// it until the ack comes in. +// +// NOTE: We aren't protecting any of this with locks. When we port this to +// NT we'll need to fix this, probably with a global lock. See the comments +// in ACKSend() in TCPRCV.C for more details. +// +// Input: Context - Context we gave to IP. +// BufferChain - BufferChain for send. +// +// Returns: Nothing. +// +void +TCPSendComplete(void *Context, PNDIS_BUFFER BufferChain) +{ + CTELockHandle SendHandle; + PNDIS_BUFFER CurrentBuffer; + + + if (Context != NULL) { + SendCmpltContext *SCContext = (SendCmpltContext *)Context; + TCPSendReq *CurrentSend; + uint i; + + CTEStructAssert(SCContext, scc); + + // First, loop through and free any NDIS buffers here that need to be. + // freed. We'll skip any 'user' buffers, and then free our buffers. We + // need to do this before decrementing the reference count to avoid + // destroying the buffer chain if we have to zap tsr_lastbuf->Next to + // NULL. + + CurrentBuffer = NDIS_BUFFER_LINKAGE(BufferChain); + for (i = 0; i < (uint)SCContext->scc_ubufcount; i++) { + CTEAssert(CurrentBuffer != NULL); + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + } + + for (i = 0; i < (uint)SCContext->scc_tbufcount; i++) { + PNDIS_BUFFER TempBuffer; + + CTEAssert(CurrentBuffer != NULL); + + TempBuffer = CurrentBuffer; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + NdisFreeBuffer(TempBuffer); + } + + CurrentSend = SCContext->scc_firstsend; + + i = 0; + while (i < SCContext->scc_count) { + Queue *TempQ; + long Result; + + + TempQ = QNEXT(&CurrentSend->tsr_req.tr_q); + + CTEStructAssert(CurrentSend, tsr); + + Result = CTEInterlockedDecrementLong( + &(CurrentSend->tsr_refcnt) + ); + + CTEAssert(Result >= 0); + + if (Result <= 0) { + + // Reference count has gone to 0 which means the send has + // been ACK'd or cancelled. Complete it now. + + // If we've sent directly from this send, NULL out the next + // pointer for the last buffer in the chain. + if (CurrentSend->tsr_lastbuf != NULL) { + NDIS_BUFFER_LINKAGE(CurrentSend->tsr_lastbuf) = NULL; + CurrentSend->tsr_lastbuf = NULL; + } + + CTEGetLock(&RequestCompleteLock, &SendHandle); + ENQUEUE(&SendCompleteQ, &CurrentSend->tsr_req.tr_q); + RequestCompleteFlags |= SEND_REQUEST_COMPLETE; + CTEFreeLock(&RequestCompleteLock, SendHandle); + } + + CurrentSend = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q), + tsr_req); + + i++; + } + + } + + FreeTCPHeader(BufferChain); + + if (RequestCompleteFlags & SEND_REQUEST_COMPLETE) + TCPRcvComplete(); + +} + +//* RcvWin - Figure out the receive window to offer in an ack. +// +// A routine to figure out what window to offer on a connection. We +// take into account SWS avoidance, what the default connection window is, +// and what the last window we offered is. +// +// Input: WinTCB - TCB on which to perform calculations. +// +// Returns: Window to be offered. +// +uint +RcvWin(TCB *WinTCB) +{ + int CouldOffer; // The window size we could offer. + + CTEStructAssert(WinTCB, tcb); + + CheckRBList(WinTCB->tcb_pendhead, WinTCB->tcb_pendingcnt); + + CTEAssert(WinTCB->tcb_rcvwin >= 0); + + CouldOffer = WinTCB->tcb_defaultwin - WinTCB->tcb_pendingcnt; + + CTEAssert(CouldOffer >= 0); + CTEAssert(CouldOffer >= WinTCB->tcb_rcvwin); + + if ((CouldOffer - WinTCB->tcb_rcvwin) >= (int) MIN(WinTCB->tcb_defaultwin/2, + WinTCB->tcb_mss)) + WinTCB->tcb_rcvwin = CouldOffer; + + return WinTCB->tcb_rcvwin; +} + +//* SendSYN - Send a SYN segment. +// +// This is called during connection establishment time to send a SYN +// segment to the peer. We get a buffer if we can, and then fill +// it in. There's a tricky part here where we have to build the MSS +// option in the header - we find the MSS by finding the MSS offered +// by the net for the local address. After that, we send it. +// +// Input: SYNTcb - TCB from which SYN is to be sent. +// TCBHandle - Handle for lock on TCB. +// +// Returns: Nothing. +// +void +SendSYN(TCB *SYNTcb, CTELockHandle TCBHandle) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *SYNHeader; + uchar *OptPtr; + IP_STATUS SendStatus; + + CTEStructAssert(SYNTcb, tcb); + + HeaderBuffer = GetTCPHeader(); + + // Go ahead and set the retransmission timer now, in case we didn't get a + // buffer. In the future we might want to queue the connection for + // when we free a buffer. + START_TCB_TIMER(SYNTcb->tcb_rexmittimer, SYNTcb->tcb_rexmit); + if (HeaderBuffer != NULL) { + ushort TempWin; + ushort MSS; + uchar FoundMSS; + + SYNHeader = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + NdisBufferLength(HeaderBuffer) = sizeof(TCPHeader) + MSS_OPT_SIZE; + SYNHeader->tcp_src = SYNTcb->tcb_sport; + SYNHeader->tcp_dest = SYNTcb->tcb_dport; + SYNHeader->tcp_seq = net_long(SYNTcb->tcb_sendnext); + SYNTcb->tcb_sendnext++; + + if (SEQ_GT(SYNTcb->tcb_sendnext, SYNTcb->tcb_sendmax)) { + TStats.ts_outsegs++; + SYNTcb->tcb_sendmax = SYNTcb->tcb_sendnext; + } else + TStats.ts_retranssegs++; + + SYNHeader->tcp_ack = net_long(SYNTcb->tcb_rcvnext); + if (SYNTcb->tcb_state == TCB_SYN_RCVD) { + SYNHeader->tcp_flags = MAKE_TCP_FLAGS(6, TCP_FLAG_SYN | TCP_FLAG_ACK); +#ifdef SYN_ATTACK + // + // if this is the second time we are trying to send the SYN-ACK, + // increment the count of retried half-connections + // + if (SynAttackProtect && (SYNTcb->tcb_rexmitcnt == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) { + CTEInterlockedAddUlong(&TCPHalfOpenRetried, 1, &SynAttLock); + } +#endif + } else { + SYNHeader->tcp_flags = MAKE_TCP_FLAGS(6, TCP_FLAG_SYN); + } + + TempWin = (ushort)SYNTcb->tcb_rcvwin; + SYNHeader->tcp_window = net_short(TempWin); + SYNHeader->tcp_xsum = 0; + OptPtr = (uchar *)(SYNHeader + 1); + FoundMSS = (*LocalNetInfo.ipi_getlocalmtu)(SYNTcb->tcb_saddr, &MSS); + + if (!FoundMSS) { + DEBUGCHK; + CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle); + FreeTCPHeader(HeaderBuffer); + return; + } + + MSS -= sizeof(TCPHeader); + + *OptPtr++ = TCP_OPT_MSS; + *OptPtr++ = MSS_OPT_SIZE; + **(ushort **)&OptPtr = net_short(MSS); + + SYNTcb->tcb_refcnt++; + SYNHeader->tcp_xsum = ~XsumSendChain(SYNTcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader) + MSS_OPT_SIZE), HeaderBuffer); + CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle); + + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader) + MSS_OPT_SIZE, SYNTcb->tcb_daddr, + SYNTcb->tcb_saddr, &SYNTcb->tcb_opt, SYNTcb->tcb_rce, + PROTOCOL_TCP); + + SYNTcb->tcb_error = SendStatus; + if (SendStatus != IP_PENDING) { + FreeTCPHeader(HeaderBuffer); + } + + CTEGetLock(&SYNTcb->tcb_lock, &TCBHandle); + DerefTCB(SYNTcb, TCBHandle); + + } else { + CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle); + return; + } + +} + +//* SendKA - Send a keep alive segment. +// +// This is called when we want to send a keep alive. +// +// Input: KATcb - TCB from which keep alive is to be sent. +// Handle - Handle for lock on TCB. +// +// Returns: Nothing. +// +void +SendKA(TCB *KATcb, CTELockHandle Handle) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *Header; + IP_STATUS SendStatus; + + CTEStructAssert(KATcb, tcb); + + HeaderBuffer = GetTCPHeader(); + + if (HeaderBuffer != NULL) { + ushort TempWin; + SeqNum TempSeq; + + Header = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + NdisBufferLength(HeaderBuffer) = sizeof(TCPHeader) + 1; + Header->tcp_src = KATcb->tcb_sport; + Header->tcp_dest = KATcb->tcb_dport; + TempSeq = KATcb->tcb_senduna - 1; + Header->tcp_seq = net_long(TempSeq); + + TStats.ts_retranssegs++; + + Header->tcp_ack = net_long(KATcb->tcb_rcvnext); + Header->tcp_flags = MAKE_TCP_FLAGS(5, TCP_FLAG_ACK); + + TempWin = (ushort)RcvWin(KATcb); + Header->tcp_window = net_short(TempWin); + Header->tcp_xsum = 0; + + Header->tcp_xsum = ~XsumSendChain(KATcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader) + 1), HeaderBuffer); + + KATcb->tcb_kacount++; + CTEFreeLock(&KATcb->tcb_lock, Handle); + + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader) + 1, KATcb->tcb_daddr, + KATcb->tcb_saddr, &KATcb->tcb_opt, KATcb->tcb_rce, PROTOCOL_TCP); + + if (SendStatus != IP_PENDING) { + FreeTCPHeader(HeaderBuffer); + } + + + } else { + CTEFreeLock(&KATcb->tcb_lock, Handle); + } + +} + +//* SendACK - Send an ACK segment. +// +// This is called whenever we need to send an ACK for some reason. Nothing +// fancy, we just do it. +// +// Input: ACKTcb - TCB from which ACK is to be sent. +// +// Returns: Nothing. +// +void +SendACK(TCB *ACKTcb) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *ACKHeader; + IP_STATUS SendStatus; + CTELockHandle TCBHandle; + SeqNum SendNext; + + CTEStructAssert(ACKTcb, tcb); + + if ((ACKTcb->tcb_state == TCB_TIME_WAIT) && + (ACKTcb->tcb_flags & TW_PENDING)) { + CTEGetLock(&ACKTcb->tcb_lock, &TCBHandle); + STOP_TCB_TIMER(ACKTcb->tcb_delacktimer); + ACKTcb->tcb_flags &= ~(NEED_ACK | ACK_DELAYED); + ACKTcb->tcb_error = IP_SUCCESS; + CTEFreeLock(&ACKTcb->tcb_lock, TCBHandle); + return; + } + + HeaderBuffer = GetTCPHeader(); + + + if (HeaderBuffer != NULL) { + ushort TempWin; + + CTEGetLock(&ACKTcb->tcb_lock, &TCBHandle); + + ACKHeader = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + + ACKHeader->tcp_src = ACKTcb->tcb_sport; + ACKHeader->tcp_dest = ACKTcb->tcb_dport; + ACKHeader->tcp_ack = net_long(ACKTcb->tcb_rcvnext); + + // If the remote peer is advertising a window of zero, we need to + // send this ack with a seq. number of his rcv_next (which in that case + // should be our senduna). We have code here ifdef'd out that makes + // sure that we don't send outside the RWE, but this doesn't work. We + // need to be able to send a pure ACK exactly at the RWE. + + if (ACKTcb->tcb_sendwin != 0) { + SeqNum MaxValidSeq; + + SendNext = ACKTcb->tcb_sendnext; +#if 0 + MaxValidSeq = ACKTcb->tcb_senduna + ACKTcb->tcb_sendwin - 1; + + SendNext = (SEQ_LT(SendNext, MaxValidSeq) ? SendNext : MaxValidSeq); +#endif + + } else + SendNext = ACKTcb->tcb_senduna; + + if ((ACKTcb->tcb_flags & FIN_SENT) && + SEQ_EQ(SendNext, ACKTcb->tcb_sendmax - 1)) { + ACKHeader->tcp_flags = MAKE_TCP_FLAGS(5, + TCP_FLAG_FIN | TCP_FLAG_ACK); + } else + ACKHeader->tcp_flags = MAKE_TCP_FLAGS(5, TCP_FLAG_ACK); + + ACKHeader->tcp_seq = net_long(SendNext); + + TempWin = (ushort)RcvWin(ACKTcb); + ACKHeader->tcp_window = net_short(TempWin); + ACKHeader->tcp_xsum = 0; + + ACKHeader->tcp_xsum = ~XsumSendChain(ACKTcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader)), HeaderBuffer); + + STOP_TCB_TIMER(ACKTcb->tcb_delacktimer); + ACKTcb->tcb_flags &= ~(NEED_ACK | ACK_DELAYED); + + if (ACKTcb->tcb_flags & TW_PENDING) { + ACKTcb->tcb_state = TCB_TIME_WAIT; + } + + CTEFreeLock(&ACKTcb->tcb_lock, TCBHandle); + + TStats.ts_outsegs++; + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader), ACKTcb->tcb_daddr, ACKTcb->tcb_saddr, + &ACKTcb->tcb_opt, ACKTcb->tcb_rce, PROTOCOL_TCP); + + ACKTcb->tcb_error = SendStatus; + if (SendStatus != IP_PENDING) + FreeTCPHeader(HeaderBuffer); + } + + return; + + +} + +//* SendRSTFromTCB - Send a RST from a TCB. +// +// This is called during close when we need to send a RST. +// +// Input: RSTTcb - TCB from which RST is to be sent. +// +// Returns: Nothing. +// +void +SendRSTFromTCB(TCB *RSTTcb) +{ + PNDIS_BUFFER HeaderBuffer; + TCPHeader *RSTHeader; + IP_STATUS SendStatus; + + CTEStructAssert(RSTTcb, tcb); + + CTEAssert(RSTTcb->tcb_state == TCB_CLOSED); + + HeaderBuffer = GetTCPHeader(); + + + if (HeaderBuffer != NULL) { + SeqNum RSTSeq; + + RSTHeader = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(HeaderBuffer) + + LocalNetInfo.ipi_hsize); + NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL; + + RSTHeader->tcp_src = RSTTcb->tcb_sport; + RSTHeader->tcp_dest = RSTTcb->tcb_dport; + + // If the remote peer has a window of 0, send with a seq. # equal + // to senduna so he'll accept it. Otherwise send with send max. + if (RSTTcb->tcb_sendwin != 0) + RSTSeq = RSTTcb->tcb_sendmax; + else + RSTSeq = RSTTcb->tcb_senduna; + + RSTHeader->tcp_seq = net_long(RSTSeq); + RSTHeader->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong), + TCP_FLAG_RST); + + RSTHeader->tcp_window = 0; + RSTHeader->tcp_xsum = 0; + + RSTHeader->tcp_xsum = ~XsumSendChain(RSTTcb->tcb_phxsum + + (uint)net_short(sizeof(TCPHeader)), HeaderBuffer); + + TStats.ts_outsegs++; + TStats.ts_outrsts++; + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer, + sizeof(TCPHeader), RSTTcb->tcb_daddr, RSTTcb->tcb_saddr, + &RSTTcb->tcb_opt, RSTTcb->tcb_rce, PROTOCOL_TCP); + + if (SendStatus != IP_PENDING) + FreeTCPHeader(HeaderBuffer); + } + + return; + + +} +//* SendRSTFromHeader - Send a RST back, based on a header. +// +// Called when we need to send a RST, but don't necessarily have a TCB. +// +// Input: TCPH - TCP header to be RST. +// Length - Length of the incoming segment. +// Dest - Destination IP address for RST. +// Src - Source IP address for RST. +// OptInfo - IP Options to use on RST. +// +// Returns: Nothing. +// +void +SendRSTFromHeader(TCPHeader UNALIGNED *TCPH, uint Length, IPAddr Dest, + IPAddr Src, IPOptInfo *OptInfo) +{ + PNDIS_BUFFER Buffer; + TCPHeader *RSTHdr; + IPOptInfo NewInfo; + IP_STATUS SendStatus; + + if (TCPH->tcp_flags & TCP_FLAG_RST) + return; + + Buffer = GetTCPHeader(); + + if (Buffer != NULL) { + // Got a buffer. Fill in the header so as to make it believable to + // the remote guy, and send it. + + RSTHdr = (TCPHeader *)((uchar *)NdisBufferVirtualAddress(Buffer) + + LocalNetInfo.ipi_hsize); + + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + + if (TCPH->tcp_flags & TCP_FLAG_SYN) + Length++; + + if (TCPH->tcp_flags & TCP_FLAG_FIN) + Length++; + + if (TCPH->tcp_flags & TCP_FLAG_ACK) { + RSTHdr->tcp_seq = TCPH->tcp_ack; + RSTHdr->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong), + TCP_FLAG_RST); + } else { + SeqNum TempSeq; + + RSTHdr->tcp_seq = 0; + TempSeq = net_long(TCPH->tcp_seq); + TempSeq += Length; + RSTHdr->tcp_ack = net_long(TempSeq); + RSTHdr->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong), + TCP_FLAG_RST | TCP_FLAG_ACK); + } + + RSTHdr->tcp_window = 0; + RSTHdr->tcp_dest = TCPH->tcp_src; + RSTHdr->tcp_src = TCPH->tcp_dest; + RSTHdr->tcp_xsum = 0; + + RSTHdr->tcp_xsum = ~XsumSendChain(PHXSUM(Src, Dest, PROTOCOL_TCP, + sizeof(TCPHeader)), Buffer); + + (*LocalNetInfo.ipi_initopts)(&NewInfo); + + if (OptInfo->ioi_options != NULL) + (*LocalNetInfo.ipi_updateopts)(OptInfo, &NewInfo, Dest, NULL_IP_ADDR); + + TStats.ts_outsegs++; + TStats.ts_outrsts++; + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, Buffer, + sizeof(TCPHeader), Dest, Src, &NewInfo, NULL, PROTOCOL_TCP); + + if (SendStatus != IP_PENDING) + FreeTCPHeader(Buffer); + + (*LocalNetInfo.ipi_freeopts)(&NewInfo); + } + +} +//* GoToEstab - Transition to the established state. +// +// Called when we are going to the established state and need to finish up +// initializing things that couldn't be done until now. We assume the TCB +// lock is held by the caller on the TCB we're called with. +// +// Input: EstabTCB - TCB to transition. +// +// Returns: Nothing. +// +void +GoToEstab(TCB *EstabTCB) +{ + + // Initialize our slow start and congestion control variables. + EstabTCB->tcb_cwin = 2 * EstabTCB->tcb_mss; + EstabTCB->tcb_ssthresh = 0xffffffff; + + EstabTCB->tcb_state = TCB_ESTAB; + + // We're in established. We'll subtract one from slow count for this fact, + // and if the slowcount goes to 0 we'll move onto the fast path. + + if (--(EstabTCB->tcb_slowcount) == 0) + EstabTCB->tcb_fastchk &= ~TCP_FLAG_SLOW; + + TStats.ts_currestab++; + + EstabTCB->tcb_flags &= ~ACTIVE_OPEN; // Turn off the active opening flag. + +} + +//* InitSendState - Initialize the send state of a connection. +// +// Called during connection establishment to initialize our send state. +// (In this case, this refers to all information we'll put on the wire as +// well as pure send state). We pick an ISS, set up a rexmit timer value, +// etc. We assume the tcb_lock is held on the TCB when we are called. +// +// Input: NewTCB - TCB to be set up. +// +// Returns: Nothing. +void +InitSendState(TCB *NewTCB) +{ + CTEStructAssert(NewTCB, tcb); + + NewTCB->tcb_sendnext = CTESystemUpTime(); + NewTCB->tcb_senduna = NewTCB->tcb_sendnext; + NewTCB->tcb_sendmax = NewTCB->tcb_sendnext; + NewTCB->tcb_error = IP_SUCCESS; + + // Initialize pseudo-header xsum. + NewTCB->tcb_phxsum = PHXSUM(NewTCB->tcb_saddr, NewTCB->tcb_daddr, + PROTOCOL_TCP, 0); + + // Initialize retransmit and delayed ack stuff. + NewTCB->tcb_rexmitcnt = 0; + NewTCB->tcb_rtt = 0; + NewTCB->tcb_smrtt = 0; + NewTCB->tcb_delta = MS_TO_TICKS(6000); + NewTCB->tcb_rexmit = MS_TO_TICKS(3000); + STOP_TCB_TIMER(NewTCB->tcb_rexmittimer); + STOP_TCB_TIMER(NewTCB->tcb_delacktimer); + +} + +//* TCPStatus - Handle a status indication. +// +// This is the TCP status handler, called by IP when a status event +// occurs. For most of these we do nothing. For certain severe status +// events we will mark the local address as invalid. +// +// Entry: StatusType - Type of status (NET or HW). NET status +// is usually caused by a received ICMP +// message. HW status indicate a HW +// problem. +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is NET status, the original dest. of +// DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local +// or remote). +// Param - Additional information for status - i.e. the +// param field of an ICMP message. +// Data - Data pertaining to status - for NET status, this +// is the first 8 bytes of the original DG. +// +// Returns: Nothing +// +void +TCPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data) +{ + CTELockHandle TableHandle, TCBHandle; + TCB *StatusTCB; + TCPHeader UNALIGNED *Header = (TCPHeader UNALIGNED *)Data; + SeqNum DropSeq; + + // Handle NET status codes differently from HW status codes. + if (StatusType == IP_NET_STATUS) { + // It's a NET code. Find a matching TCB. + CTEGetLock(&TCBTableLock, &TableHandle); + StatusTCB = FindTCB(OrigSrc, OrigDest, Header->tcp_dest, + Header->tcp_src); + if (StatusTCB != NULL) { + // Found one. Get the lock on it, and continue. + CTEStructAssert(StatusTCB, tcb); + + CTEGetLock(&StatusTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&TCBTableLock, TCBHandle); + + + // Make sure the TCB is in a state that is interesting. + if (StatusTCB->tcb_state == TCB_CLOSED || + StatusTCB->tcb_state == TCB_TIME_WAIT || + CLOSING(StatusTCB)) { + CTEFreeLock(&StatusTCB->tcb_lock, TableHandle); + return; + } + + switch (StatusCode) { + // Hard errors - Destination protocol unreachable. We treat + // these as fatal errors. Close the connection now. + case IP_DEST_PROT_UNREACHABLE: + StatusTCB->tcb_error = StatusCode; + StatusTCB->tcb_refcnt++; + TryToCloseTCB(StatusTCB, TCB_CLOSE_UNREACH, TableHandle); + + RemoveTCBFromConn(StatusTCB); + NotifyOfDisc(StatusTCB, NULL, + MapIPError(StatusCode, TDI_DEST_UNREACHABLE)); + CTEGetLock(&StatusTCB->tcb_lock, &TCBHandle); + DerefTCB(StatusTCB, TCBHandle); + return; + break; + + // Soft errors. Save the error in case it time out. + case IP_DEST_NET_UNREACHABLE: + case IP_DEST_HOST_UNREACHABLE: + case IP_DEST_PORT_UNREACHABLE: + case IP_PACKET_TOO_BIG: + case IP_BAD_ROUTE: + case IP_TTL_EXPIRED_TRANSIT: + case IP_TTL_EXPIRED_REASSEM: + case IP_PARAM_PROBLEM: + StatusTCB->tcb_error = StatusCode; + break; + + case IP_SPEC_MTU_CHANGE: + // A TCP datagram has triggered an MTU change. Figure out + // which connection it is, and update him to retransmit the + // segment. The Param value is the new MTU. We'll need to + // retransmit if the new MTU is less than our existing MTU + // and the sequence of the dropped packet is less than our + // current send next. + + Param -= sizeof(TCPHeader) - + StatusTCB->tcb_opt.ioi_optlength; + DropSeq = net_long(Header->tcp_seq); + + if (*(ushort *)&Param <= StatusTCB->tcb_mss && + (SEQ_GTE(DropSeq, StatusTCB->tcb_senduna) && + SEQ_LT(DropSeq, StatusTCB->tcb_sendnext))) { + + // Need to initiate a retranmsit. + ResetSendNext(StatusTCB, DropSeq); + // Set the congestion window to allow only one packet. + // This may prevent us from sending anything if we + // didn't just set sendnext to senduna. This is OK, + // we'll retransmit later, or send when we get an ack. + StatusTCB->tcb_cwin = Param; + DelayAction(StatusTCB, NEED_OUTPUT); + } + + StatusTCB->tcb_mss = (ushort)MIN(Param, + (ulong)StatusTCB->tcb_remmss); + + CTEAssert(StatusTCB->tcb_mss > 0); + + + // + // Reset the Congestion Window if necessary + // + if (StatusTCB->tcb_cwin < StatusTCB->tcb_mss) { + StatusTCB->tcb_cwin = StatusTCB->tcb_mss; + + // + // Make sure the slow start threshold is at least + // 2 segments + // + if ( StatusTCB->tcb_ssthresh < + ((uint) StatusTCB->tcb_mss*2) + ) { + StatusTCB->tcb_ssthresh = StatusTCB->tcb_mss * 2; + } + } + + break; + + // Source quench. This will cause us to reinitiate our + // slow start by resetting our congestion window and + // adjusting our slow start threshold. + case IP_SOURCE_QUENCH: + StatusTCB->tcb_ssthresh = + MAX( + MIN( + StatusTCB->tcb_cwin, + StatusTCB->tcb_sendwin + ) / 2, + (uint) StatusTCB->tcb_mss * 2 + ); + StatusTCB->tcb_cwin = StatusTCB->tcb_mss; + break; + + default: + DEBUGCHK; + break; + } + + CTEFreeLock(&StatusTCB->tcb_lock, TableHandle); + } else { + // Couldn't find a matching TCB. Just free the lock and return. + CTEFreeLock(&TCBTableLock, TableHandle); + } + } else { + uint NewMTU; + + // 'Hardware' or 'global' status. Figure out what to do. + switch (StatusCode) { + case IP_ADDR_DELETED: + // Local address has gone away. OrigDest is the IPAddr which is + // gone. + +#ifndef _PNP_POWER + // + // Delete all TCBs with that as a source address. + // This is done via TDI notifications in the PNP world. + // + TCBWalk(DeleteTCBWithSrc, &OrigDest, NULL, NULL); + +#endif // _PNP_POWER + +#ifdef SECFLTR + // + // Delete any security filters associated with this address + // + DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_TCP); + +#endif // SECFLTR + + break; + + case IP_ADDR_ADDED: + +#ifdef SECFLTR + // + // An address has materialized. OrigDest identifies the address. + // Data is a handle to the IP configuration information for the + // interface on which the address is instantiated. + // + AddProtocolSecurityFilter(OrigDest, PROTOCOL_TCP, + (NDIS_HANDLE) Data); +#endif // SECFLTR + + break; + + case IP_MTU_CHANGE: + NewMTU = Param - sizeof(TCPHeader); + TCBWalk(SetTCBMTU, &OrigDest, &OrigSrc, &NewMTU); + break; +#ifdef CHICAGO + case IP_UNLOAD: + // IP is telling us we're being unloaded. First, deregister + // with VTDI, and then call CTEUnload(). + (void)TLRegisterProtocol(PROTOCOL_TCP, NULL, NULL, NULL, NULL); + TLRegisterDispatch(TransportName, NULL); + (void)RegisterAddrChangeHndlr(AddrChange, FALSE); + CTEUnload(TransportName); + break; +#endif // CHICAGO + default: + DEBUGCHK; + break; + } + } +} + +//* FillTCPHeader - Fill the TCP header in. +// +// A utility routine to fill in the TCP header. +// +// Input: SendTCB - TCB to fill from. +// Header - Header to fill into. +// +// Returns: Nothing. +// +void +FillTCPHeader(TCB *SendTCB, TCPHeader *Header) +{ +#ifdef VXD + +// STUPID FUCKING COMPILER generates incorrect code for this. Put it back on +// the blessed day we get a real compiler. + +#if 0 + _asm { + mov edx, dword ptr SendTCB + mov ecx, dword ptr Header + mov ax, word ptr [edx].tcb_sport + xchg al, ah + mov word ptr [ecx].tcp_src, ax + mov ax, [edx].tcb_dport + xchg ah, al + mov [ecx].tcp_dest, ax + + mov eax, [edx].tcb_sendnext + xchg ah, al + ror eax, 16 + xchg ah, al + mov [ecx].tcp_seq, eax + + mov eax, [edx].tcb_rcvnext + xchg ah, al + ror eax, 16 + xchg ah, al + mov [ecx].tcp_ack, eax + + mov [ecx].tcp_flags, 1050H + mov dword ptr [ecx].tcp_xsum, 0 + + push edx + call near ptr RcvWin + add esp, 4 + + mov ecx, Header + xchg ah, al + mov [ecx].tcp_window, ax + + } +#else + ushort S; + ulong L; + + + Header->tcp_src = SendTCB->tcb_sport; + Header->tcp_dest = SendTCB->tcb_dport; + L = SendTCB->tcb_sendnext; + Header->tcp_seq = net_long(L); + L = SendTCB->tcb_rcvnext; + Header->tcp_ack = net_long(L); + Header->tcp_flags = 0x1050; + *(ulong *)&Header->tcp_xsum = 0; + S = RcvWin(SendTCB); + Header->tcp_window = net_short(S); +#endif +#else + + // + // BUGBUG: Is this worth coding in assembly? + // + ushort S; + ulong L; + + + Header->tcp_src = SendTCB->tcb_sport; + Header->tcp_dest = SendTCB->tcb_dport; + L = SendTCB->tcb_sendnext; + Header->tcp_seq = net_long(L); + L = SendTCB->tcb_rcvnext; + Header->tcp_ack = net_long(L); + Header->tcp_flags = 0x1050; + *(ulong *)&Header->tcp_xsum = 0; + S = RcvWin(SendTCB); + Header->tcp_window = net_short(S); +#endif + + +} + +//* TCPSend - Send data from a TCP connection. +// +// This is the main 'send data' routine. We go into a loop, trying +// to send data until we can't for some reason. First we compute +// the useable window, use it to figure the amount we could send. If +// the amount we could send meets certain criteria we'll build a frame +// and send it, after setting any appropriate control bits. We assume +// the caller has put a reference on the TCB. +// +// Input: SendTCB - TCB to be sent from. +// TCBHandle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +#ifdef VXD +TCPSend(TCB *SendTCB) +#else +TCPSend(TCB *SendTCB, CTELockHandle TCBHandle) +#endif +{ + int SendWin; // Useable send window. + uint AmountToSend; // Amount to send this time. + uint AmountLeft; + TCPHeader *Header; // TCP header for a send. + PNDIS_BUFFER FirstBuffer, CurrentBuffer; + TCPSendReq *CurSend; + SendCmpltContext *SCC; + SeqNum OldSeq; + IP_STATUS SendStatus; + uint AmtOutstanding, AmtUnsent; + int ForceWin; // Window we're force to use. +#ifdef VXD + CTELockHandle TCBHandle; + + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); +#endif + + + CTEStructAssert(SendTCB, tcb); + CTEAssert(SendTCB->tcb_refcnt != 0); + + CTEAssert(*(int *)&SendTCB->tcb_sendwin >= 0); + CTEAssert(*(int *)&SendTCB->tcb_cwin >= SendTCB->tcb_mss); + + CTEAssert(!(SendTCB->tcb_flags & FIN_OUTSTANDING) || + (SendTCB->tcb_sendnext == SendTCB->tcb_sendmax)); + + if (!(SendTCB->tcb_flags & IN_TCP_SEND) && + !(SendTCB->tcb_fastchk & TCP_FLAG_IN_RCV)) { + SendTCB->tcb_flags |= IN_TCP_SEND; + + // We'll continue this loop until we send a FIN, or we break out + // internally for some other reason. + while (!(SendTCB->tcb_flags & FIN_OUTSTANDING)) { + + CheckTCBSends(SendTCB); + + AmtOutstanding = (uint)(SendTCB->tcb_sendnext - + SendTCB->tcb_senduna); + AmtUnsent = SendTCB->tcb_unacked - AmtOutstanding; + + CTEAssert(*(int *)&AmtUnsent >= 0); + + SendWin = (int)(MIN(SendTCB->tcb_sendwin, SendTCB->tcb_cwin) - + AmtOutstanding); + +#if FAST_RETRANSMIT + // if this send is after the fast recovery + // and sendwin is zero because of amt outstanding + // then, at least force 1 segment to prevent delayed + // ack timeouts from the remote + + if (SendTCB->tcb_force) { + SendTCB->tcb_force=0; + if (SendWin < SendTCB->tcb_mss ){ + + SendWin = SendTCB->tcb_mss; + } + } + +#endif + + + + // Since the window could have shrank, need to get it to zero at + // least. + ForceWin = (int)((SendTCB->tcb_flags & FORCE_OUTPUT) >> + FORCE_OUT_SHIFT); + SendWin = MAX(SendWin, ForceWin); + + AmountToSend = MIN(MIN((uint)SendWin, AmtUnsent), SendTCB->tcb_mss); + + CTEAssert(SendTCB->tcb_mss > 0); + + // See if we have enough to send. We'll send if we have at least a + // segment, or if we really have some data to send and we can send + // all that we have, or the send window is > 0 and we need to force + // output or send a FIN (note that if we need to force output + // SendWin will be at least 1 from the check above), or if we can + // send an amount == to at least half the maximum send window + // we've seen. + if (AmountToSend == SendTCB->tcb_mss || + (AmountToSend != 0 && AmountToSend == AmtUnsent) || + (SendWin != 0 && + ((SendTCB->tcb_flags & (FORCE_OUTPUT | FIN_NEEDED)) || + AmountToSend >= (SendTCB->tcb_maxwin / 2)))) { + + // It's OK to send something. Try to get a header buffer now. + FirstBuffer = GetTCPHeader(); + if (FirstBuffer != NULL) { + + // Got a header buffer. Loop through the sends on the TCB, + // building a frame. + CurrentBuffer = FirstBuffer; + CurSend = SendTCB->tcb_cursend; + + Header = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(FirstBuffer) + + LocalNetInfo.ipi_hsize); + + SCC = (SendCmpltContext *)(Header + 1); +#ifdef DEBUG + SCC->scc_sig = scc_signature; +#endif + FillTCPHeader(SendTCB, Header); + + SCC->scc_ubufcount = 0; + SCC->scc_tbufcount = 0; + SCC->scc_count = 0; + + AmountLeft = AmountToSend; + + if (AmountToSend != 0) { + long Result; + + + CTEStructAssert(CurSend, tsr); + SCC->scc_firstsend = CurSend; + + do { + CTEAssert(CurSend->tsr_refcnt > 0); + Result = CTEInterlockedIncrementLong( + &(CurSend->tsr_refcnt) + ); + + CTEAssert(Result > 0); + + SCC->scc_count++; + // If the current send offset is 0 and the current + // send is less than or equal to what we have left + // to send, we haven't already put a transport + // buffer on this send, and nobody else is using + // the buffer chain directly, just use the input + // buffers. We check for other people using them + // by looking at tsr_lastbuf. If it's NULL, + // nobody else is using the buffers. If it's not + // NULL, somebody is. + + if (SendTCB->tcb_sendofs == 0 && + (SendTCB->tcb_sendsize <= AmountLeft) && + (SCC->scc_tbufcount == 0) && + CurSend->tsr_lastbuf == NULL) { + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = + SendTCB->tcb_sendbuf; + do { + SCC->scc_ubufcount++; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + } while (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL); + + CurSend->tsr_lastbuf = CurrentBuffer; + AmountLeft -= SendTCB->tcb_sendsize; + SendTCB->tcb_sendsize = 0; + } else { + uint AmountToDup; + PNDIS_BUFFER NewBuf, Buf; + uint Offset; + NDIS_STATUS NStatus; + uchar *VirtualAddress; + uint Length; + + // Either the current send has more data than + // we want to send, or the starting offset is + // not 0. In either case we'll need to loop + // through the current send, allocating buffers. + Buf = SendTCB->tcb_sendbuf; + Offset = SendTCB->tcb_sendofs; + + do { + CTEAssert(Buf != NULL); + + NdisQueryBuffer(Buf, &VirtualAddress, + &Length); + + CTEAssert((Offset < Length) || + (Offset == 0 && Length == 0)); + + // Adjust the length for the offset into + // this buffer. + + Length -= Offset; + + AmountToDup = MIN(AmountLeft, Length); + + NdisAllocateBuffer(&NStatus, &NewBuf, + TCPSendBufferPool, + VirtualAddress + Offset, + AmountToDup); + if (NStatus == NDIS_STATUS_SUCCESS) { + SCC->scc_tbufcount++; + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = + NewBuf; + + CurrentBuffer = NewBuf; + if (AmountToDup >= Length) { + // Exhausted this buffer. + Buf = NDIS_BUFFER_LINKAGE(Buf); + Offset = 0; + } else { + Offset += AmountToDup; + CTEAssert(Offset < NdisBufferLength(Buf)); + } + + SendTCB->tcb_sendsize -= AmountToDup; + AmountLeft -= AmountToDup; + } else { + // Couldn't allocate a buffer. If + // the packet is already partly built, + // send what we've got, otherwise + // bail out. + if (SCC->scc_tbufcount == 0 && + SCC->scc_ubufcount == 0) { + TCPSendComplete(SCC, FirstBuffer); + goto error_oor; + } + AmountToSend -= AmountLeft; + AmountLeft = 0; + } + } while (AmountLeft && SendTCB->tcb_sendsize); + + SendTCB->tcb_sendbuf = Buf; + SendTCB->tcb_sendofs = Offset; + } + + if (CurSend->tsr_flags & TSR_FLAG_URG) { + ushort UP; + // This send is urgent data. We need to figure + // out what the urgent data pointer should be. + // We know sendnext is the starting sequence + // number of the frame, and that at the top of + // this do loop sendnext identified a byte in + // the CurSend at that time. We advanced CurSend + // at the same rate we've decremented + // AmountLeft (AmountToSend - AmountLeft == + // AmountBuilt), so sendnext + + // (AmountToSend - AmountLeft) identifies a byte + // in the current value of CurSend, and that + // quantity plus tcb_sendsize is the sequence + // number one beyond the current send. + UP = + (ushort)(AmountToSend - AmountLeft) + + (ushort)SendTCB->tcb_sendsize - + ((SendTCB->tcb_flags & BSD_URGENT) ? 0 : 1); + + Header->tcp_urgent = net_short(UP); + + Header->tcp_flags |= TCP_FLAG_URG; + } + + // See if we've exhausted this send. If we have, + // set the PUSH bit in this frame and move on to + // the next send. We also need to check the + // urgent data bit. + if (SendTCB->tcb_sendsize == 0) { + Queue *Next; + uchar PrevFlags; + + // We've exhausted this send. Set the PUSH bit. + Header->tcp_flags |= TCP_FLAG_PUSH; + PrevFlags = CurSend->tsr_flags; + Next = QNEXT(&CurSend->tsr_req.tr_q); + if (Next != QEND(&SendTCB->tcb_sendq)) { + CurSend = STRUCT_OF(TCPSendReq, + QSTRUCT(TCPReq, Next, tr_q), tsr_req); + CTEStructAssert(CurSend, tsr); + SendTCB->tcb_sendsize = CurSend->tsr_unasize; + SendTCB->tcb_sendofs = CurSend->tsr_offset; + SendTCB->tcb_sendbuf = CurSend->tsr_buffer; + SendTCB->tcb_cursend = CurSend; + + // Check the urgent flags. We can't combine + // new urgent data on to the end of old + // non-urgent data. + if ((PrevFlags & TSR_FLAG_URG) && ! + (CurSend->tsr_flags & TSR_FLAG_URG)) + break; + } else { + CTEAssert(AmountLeft == 0); + SendTCB->tcb_cursend = NULL; + SendTCB->tcb_sendbuf = NULL; + } + } + } while (AmountLeft != 0); + + } else { + + // We're in the loop, but AmountToSend is 0. This + // should happen only when we're sending a FIN. Check + // this, and return if it's not true. + CTEAssert(AmtUnsent == 0); + if (!(SendTCB->tcb_flags & FIN_NEEDED)) { + // DEBUGCHK; + FreeTCPHeader(FirstBuffer); + break; + } + + SCC->scc_firstsend = NULL; + NDIS_BUFFER_LINKAGE(FirstBuffer) = NULL; + } + + // Adjust for what we're really going to send. + AmountToSend -= AmountLeft; + + // Update the sequence numbers, and start a RTT measurement + // if needed. + + OldSeq = SendTCB->tcb_sendnext; + SendTCB->tcb_sendnext += AmountToSend; + + if (SEQ_EQ(OldSeq, SendTCB->tcb_sendmax)) { + // We're sending entirely new data. + + // We can't advance sendmax once FIN_SENT is set. + CTEAssert(!(SendTCB->tcb_flags & FIN_SENT)); + SendTCB->tcb_sendmax = SendTCB->tcb_sendnext; + // We've advanced sendmax, so we must be sending some + // new data, so bump the outsegs counter. + TStats.ts_outsegs++; + + if (SendTCB->tcb_rtt == 0) { + // No RTT running, so start one. + SendTCB->tcb_rtt = TCPTime; + SendTCB->tcb_rttseq = OldSeq; + } + } else { + // We have at least some retransmission. + + TStats.ts_retranssegs++; + if (SEQ_GT(SendTCB->tcb_sendnext, SendTCB->tcb_sendmax)) { + // But we also have some new data, so check the + // rtt stuff. + TStats.ts_outsegs++; + CTEAssert(!(SendTCB->tcb_flags & FIN_SENT)); + SendTCB->tcb_sendmax = SendTCB->tcb_sendnext; + + if (SendTCB->tcb_rtt == 0) { + // No RTT running, so start one. + SendTCB->tcb_rtt = TCPTime; + SendTCB->tcb_rttseq = OldSeq; + } + } + } + + // We've built the frame entirely. If we've send everything + // we have and their's a FIN pending, OR it in. + if (AmtUnsent == AmountToSend) { + if (SendTCB->tcb_flags & FIN_NEEDED) { + CTEAssert(!(SendTCB->tcb_flags & FIN_SENT) || + (SendTCB->tcb_sendnext == (SendTCB->tcb_sendmax - 1))); + // See if we still have room in the window for a FIN. + if (SendWin > (int) AmountToSend) { + Header->tcp_flags |= TCP_FLAG_FIN; + SendTCB->tcb_sendnext++; + SendTCB->tcb_sendmax = SendTCB->tcb_sendnext; + SendTCB->tcb_flags |= (FIN_SENT | FIN_OUTSTANDING); + SendTCB->tcb_flags &= ~FIN_NEEDED; + } + } + } + + AmountToSend += sizeof(TCPHeader); + + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + + SendTCB->tcb_flags &= ~(NEED_ACK | ACK_DELAYED | + FORCE_OUTPUT); + STOP_TCB_TIMER(SendTCB->tcb_delacktimer); + STOP_TCB_TIMER(SendTCB->tcb_swstimer); + SendTCB->tcb_alive = TCPTime; + + CTEFreeLock(&SendTCB->tcb_lock, TCBHandle); + + // We're all set. Xsum it and send it. + Header->tcp_xsum = ~XsumSendChain(SendTCB->tcb_phxsum + + (uint)net_short(AmountToSend), FirstBuffer); + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, SCC, + FirstBuffer, AmountToSend, SendTCB->tcb_daddr, + SendTCB->tcb_saddr, &SendTCB->tcb_opt, SendTCB->tcb_rce, + PROTOCOL_TCP); + + SendTCB->tcb_error = SendStatus; + if (SendStatus != IP_PENDING) { + TCPSendComplete(SCC, FirstBuffer); + if (SendStatus != IP_SUCCESS) { + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); + // This packet didn't get sent. If nothing's + // changed in the TCB, put sendnext back to + // what we just tried to send. Depending on + // the error, we may try again. + if (SEQ_GTE(OldSeq, SendTCB->tcb_senduna) && + SEQ_LT(OldSeq, SendTCB->tcb_sendnext)) + ResetSendNext(SendTCB, OldSeq); + + // We know this packet didn't get sent. Start + // the retransmit timer now, if it's not already + // runnimg, in case someone came in while we + // were in IP and stopped it. + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + + // If it failed because of an MTU problem, get + // the new MTU and try again. + if (SendStatus == IP_PACKET_TOO_BIG) { + uint NewMTU; + + // The MTU has changed. Update it, and try + // again. + SendStatus = (*LocalNetInfo.ipi_getpinfo)( + SendTCB->tcb_daddr, SendTCB->tcb_saddr, + &NewMTU, NULL); + + if (SendStatus != IP_SUCCESS) + break; + + // We have a new MTU. Make sure it's big enough + // to use. If not, correct this and turn off + // MTU discovery on this TCB. Otherwise use the + // new MTU. + if (NewMTU <= (sizeof(TCPHeader) + + SendTCB->tcb_opt.ioi_optlength)) { + + // The new MTU is too small to use. Turn off + // PMTU discovery on this TCB, and drop to + // our off net MTU size. + SendTCB->tcb_opt.ioi_flags &= ~IP_FLAG_DF; + SendTCB->tcb_mss = MIN((ushort)MAX_REMOTE_MSS, + SendTCB->tcb_remmss); + } else { + + // The new MTU is adequate. Adjust it for + // the header size and options length, and use + // it. + NewMTU -= sizeof(TCPHeader) - + SendTCB->tcb_opt.ioi_optlength; + SendTCB->tcb_mss = MIN((ushort)NewMTU, + SendTCB->tcb_remmss); + } + + CTEAssert(SendTCB->tcb_mss > 0); + + continue; + } + break; + } + } + + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); + continue; + } else // FirstBuffer != NULL. + goto error_oor; + } else { + // We've decided we can't send anything now. Figure out why, and + // see if we need to set a timer. + if (SendTCB->tcb_sendwin == 0) { + if (!(SendTCB->tcb_flags & FLOW_CNTLD)) { + SendTCB->tcb_flags |= FLOW_CNTLD; + SendTCB->tcb_rexmitcnt = 0; + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + SendTCB->tcb_slowcount++; + SendTCB->tcb_fastchk |= TCP_FLAG_SLOW; + } else + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, + SendTCB->tcb_rexmit); + } else + if (AmountToSend != 0) + // We have something to send, but we're not sending + // it, presumably due to SWS avoidance. + if (!TCB_TIMER_RUNNING(SendTCB->tcb_swstimer)) + START_TCB_TIMER(SendTCB->tcb_swstimer, SWS_TO); + + break; + } + } // while (!FIN_OUTSTANDING) + + // We're done sending, so we don't need the output flags set. + SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT | + SEND_AFTER_RCV); + } else + SendTCB->tcb_flags |= SEND_AFTER_RCV; + + DerefTCB(SendTCB, TCBHandle); + return; + +// Common case error handling code for out of resource conditions. Start the +// retransmit timer if it's not already running (so that we try this again +// later), clean up and return. +error_oor: + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + // We had an out of resource problem, so clear the OUTPUT flags. + SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT); + DerefTCB(SendTCB, TCBHandle); + return; + +} + + +#if FAST_RETRANSMIT +//* ResetSendNextAndFastSend - Set the sendnext value of a TCB. +// +// Called to handle fast retransmit of the segment which the reveiver +// is asking for. +// tcb_lock will be held while entering (called by TCPRcv) +// and will be released in this routine after doing IP xmit. +// +// Input: SeqTCB - Pointer to TCB to be updated. +// NewSeq - Sequence number to set. +// +// Returns: Nothing. +// +void +ResetAndFastSend(TCB *SeqTCB, SeqNum NewSeq) +{ + + TCPSendReq *SendReq; + uint AmtForward; + Queue *CurQ; + PNDIS_BUFFER Buffer; + uint Offset; + uint SendSize; + CTELockHandle TCBHandle; + + 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))); + + + + //KdPrint(("Resetandfastsend TCB %x, seq %x\n", SeqTCB, NewSeq)); + + 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. + // We're pointing at the proper send req now. We need to go down + + // SendReq points to the cursend + // SendSize point to sendsize in the cursend + + SendSize = SendReq->tsr_unasize; + + Buffer = SendReq->tsr_buffer; + Offset = SendReq->tsr_offset; + + // Call the fast retransmit send now + + //KdPrint(("Calling fastsend buf %x, Offset %x\n", Buffer,Offset)); + + TCPFastSend(SeqTCB, Buffer, Offset, SendReq, SendSize); + + + } else { + + CTEAssert(SeqTCB->tcb_cursend == NULL); + } + + } + +#ifndef VXD + TCBHandle = DISPATCH_LEVEL; +#endif + + DerefTCB(SeqTCB, TCBHandle); + return; + +} + +//* TCPFastSend - To send a segment without changing TCB state +// +// Called to handle fast retransmit of the segment +// tcb_lock will be held while entering (called by TCPRcv) +// +// Input: SendTCB - Pointer to TCB +// in_sendBuf - Pointer to ndis_buffer +// in_sendofs - Send Offset +// in_sendreq - current send request +// in_sendsize - size of this send +// +// Returns: Nothing. +// + +void +TCPFastSend(TCB *SendTCB, + PNDIS_BUFFER in_SendBuf, + uint in_SendOfs, + TCPSendReq *in_SendReq, + uint in_SendSize) +{ + + int SendWin; // Useable send window. + uint AmountToSend; // Amount to send this time. + uint AmountLeft; + TCPHeader *Header; // TCP header for a send. + PNDIS_BUFFER FirstBuffer, CurrentBuffer; + TCPSendReq *CurSend; + SendCmpltContext *SCC; + SeqNum OldSeq; + IP_STATUS SendStatus; + uint AmtOutstanding, AmtUnsent; + int ForceWin; // Window we're force to use. + CTELockHandle TCBHandle; + + uint SendOfs = in_SendOfs; + uint SendSize = in_SendSize; + PNDIS_BUFFER SendBuf; + +#ifndef VXD + TCBHandle = DISPATCH_LEVEL; +#endif + + + CTEStructAssert(SendTCB, tcb); + CTEAssert(SendTCB->tcb_refcnt != 0); + + CTEAssert(*(int *)&SendTCB->tcb_sendwin >= 0); + CTEAssert(*(int *)&SendTCB->tcb_cwin >= SendTCB->tcb_mss); + + CTEAssert(!(SendTCB->tcb_flags & FIN_OUTSTANDING) || + (SendTCB->tcb_sendnext == SendTCB->tcb_sendmax)); + + + + AmtOutstanding = (uint)(SendTCB->tcb_sendnext - + SendTCB->tcb_senduna); + + AmtUnsent = SendTCB->tcb_unacked - AmtOutstanding; + + CTEAssert(*(int *)&AmtUnsent >= 0); + + SendWin = SendTCB->tcb_mss; + + + AmountToSend = MIN(in_SendSize, SendTCB->tcb_mss); + + CTEAssert (AmountToSend >= 0); + + + CTEAssert(SendTCB->tcb_mss > 0); + + // See if we have enough to send. We'll send if we have at least a + // segment, or if we really have some data to send and we can send + // all that we have, or the send window is > 0 and we need to force + // output or send a FIN (note that if we need to force output + // SendWin will be at least 1 from the check above), or if we can + // send an amount == to at least half the maximum send window + // we've seen. + + //KdPrint(("In fastsend Sendwin %x, Amttosend %x\n", SendWin,AmountToSend)); + + if (AmountToSend >= 0) { + + + // It's OK to send something. Try to get a header buffer now. + // Mark the TCB for debugging. + // This should be removed for shipping version. + + SendTCB->tcb_fastchk |= TCP_FLAG_FASTREC; + + FirstBuffer = GetTCPHeader(); + + if (FirstBuffer != NULL) { + + // Got a header buffer. Loop through the sends on the TCB, + // building a frame. + + CurrentBuffer = FirstBuffer; + CurSend = in_SendReq; + SendOfs = in_SendOfs; + + Header = (TCPHeader *)( + (uchar *)NdisBufferVirtualAddress(FirstBuffer) + + LocalNetInfo.ipi_hsize); + + SCC = (SendCmpltContext *)(Header + 1); + +#ifdef DEBUG + SCC->scc_sig = scc_signature; +#endif + FillTCPHeader(SendTCB, Header); + { + ulong L = SendTCB->tcb_senduna; + Header->tcp_seq = net_long(L); + + } + + SCC->scc_ubufcount = 0; + SCC->scc_tbufcount = 0; + SCC->scc_count = 0; + + AmountLeft = AmountToSend; + + if (AmountToSend != 0) { + long Result; + + CTEStructAssert(CurSend, tsr); + SCC->scc_firstsend = CurSend; + + do { + + CTEAssert(CurSend->tsr_refcnt > 0); + + Result = CTEInterlockedIncrementLong( + &(CurSend->tsr_refcnt) + ); + + CTEAssert(Result > 0); + + SCC->scc_count++; + + // If the current send offset is 0 and the current + // send is less than or equal to what we have left + // to send, we haven't already put a transport + // buffer on this send, and nobody else is using + // the buffer chain directly, just use the input + // buffers. We check for other people using them + // by looking at tsr_lastbuf. If it's NULL, + // nobody else is using the buffers. If it's not + // NULL, somebody is. + + if (SendOfs == 0 && + (SendSize <= AmountLeft) && + (SCC->scc_tbufcount == 0) && + CurSend->tsr_lastbuf == NULL) { + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = in_SendBuf; + + do { + SCC->scc_ubufcount++; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + } while (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL); + + CurSend->tsr_lastbuf = CurrentBuffer; + AmountLeft -= SendSize; + //KdPrint(("nobody using this CurSend %x\n",CurSend )); + // SendSize = 0; + } else { + uint AmountToDup; + PNDIS_BUFFER NewBuf, Buf; + uint Offset; + NDIS_STATUS NStatus; + uchar *VirtualAddress; + uint Length; + + // Either the current send has more data than + // we want to send, or the starting offset is + // not 0. In either case we'll need to loop + // through the current send, allocating buffers. + + Buf = in_SendBuf; + + Offset = SendOfs; + + do { + CTEAssert(Buf != NULL); + + NdisQueryBuffer(Buf, &VirtualAddress, + &Length); + + CTEAssert((Offset < Length) || + (Offset == 0 && Length == 0)); + + // Adjust the length for the offset into + // this buffer. + + Length -= Offset; + + AmountToDup = MIN(AmountLeft, Length); + + NdisAllocateBuffer(&NStatus, &NewBuf, + TCPSendBufferPool, + VirtualAddress + Offset, + AmountToDup); + + if (NStatus == NDIS_STATUS_SUCCESS) { + + SCC->scc_tbufcount++; + + NDIS_BUFFER_LINKAGE(CurrentBuffer) = NewBuf; + + CurrentBuffer = NewBuf; + + if (AmountToDup >= Length) { + + // Exhausted this buffer. + + Buf = NDIS_BUFFER_LINKAGE(Buf); + Offset = 0; + + } else { + + Offset += AmountToDup; + CTEAssert(Offset < NdisBufferLength(Buf)); + } + + SendSize -= AmountToDup; + AmountLeft -= AmountToDup; + + } else { + + // Couldn't allocate a buffer. If + // the packet is already partly built, + // send what we've got, otherwise + // bail out. + + if (SCC->scc_tbufcount == 0 && + SCC->scc_ubufcount == 0) { + TCPSendComplete(SCC, FirstBuffer); + goto error_oor; + } + + AmountToSend -= AmountLeft; + AmountLeft = 0; + + } + } while (AmountLeft && SendSize); + + SendBuf = Buf; + SendOfs = Offset; + //KdPrint(("Ready to send. SendBuf %x SendOfs %x\n",SendBuf, SendOfs )); + + } + + if (CurSend->tsr_flags & TSR_FLAG_URG) { + ushort UP; + + KdPrint(("Fast send in URG %x\n", CurSend)); + + // This send is urgent data. We need to figure + // out what the urgent data pointer should be. + // We know sendnext is the starting sequence + // number of the frame, and that at the top of + // this do loop sendnext identified a byte in + // the CurSend at that time. We advanced CurSend + // at the same rate we've decremented + // AmountLeft (AmountToSend - AmountLeft == + // AmountBuilt), so sendnext + + // (AmountToSend - AmountLeft) identifies a byte + // in the current value of CurSend, and that + // quantity plus tcb_sendsize is the sequence + // number one beyond the current send. + + UP = + (ushort)(AmountToSend - AmountLeft) + + (ushort)SendTCB->tcb_sendsize - + ((SendTCB->tcb_flags & BSD_URGENT) ? 0 : 1); + + Header->tcp_urgent = net_short(UP); + + Header->tcp_flags |= TCP_FLAG_URG; + } + + // See if we've exhausted this send. If we have, + // set the PUSH bit in this frame and move on to + // the next send. We also need to check the + // urgent data bit. + + if (SendSize == 0) { + Queue *Next; + uchar PrevFlags; + + // We've exhausted this send. Set the PUSH bit. + Header->tcp_flags |= TCP_FLAG_PUSH; + PrevFlags = CurSend->tsr_flags; + Next = QNEXT(&CurSend->tsr_req.tr_q); + if (Next != QEND(&SendTCB->tcb_sendq)) { + CurSend = STRUCT_OF(TCPSendReq, + QSTRUCT(TCPReq, Next, tr_q), tsr_req); + CTEStructAssert(CurSend, tsr); + SendSize = CurSend->tsr_unasize; + SendOfs = CurSend->tsr_offset; + SendBuf = CurSend->tsr_buffer; + CurSend = CurSend; + + // Check the urgent flags. We can't combine + // new urgent data on to the end of old + // non-urgent data. + if ((PrevFlags & TSR_FLAG_URG) && ! + (CurSend->tsr_flags & TSR_FLAG_URG)) + break; + } else { + CTEAssert(AmountLeft == 0); + CurSend = NULL; + SendBuf = NULL; + } + } + + } while (AmountLeft != 0); + + } else { + + // Amt to send is 0. + // Just bail out and strat timer. + + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + FreeTCPHeader(FirstBuffer); + return; + + } + + // Adjust for what we're really going to send. + + AmountToSend -= AmountLeft; + + + TStats.ts_retranssegs++; + + // We've built the frame entirely. If we've send everything + // we have and their's a FIN pending, OR it in. + + AmountToSend += sizeof(TCPHeader); + + + SendTCB->tcb_flags &= ~(NEED_ACK | ACK_DELAYED | + FORCE_OUTPUT); + + + + STOP_TCB_TIMER(SendTCB->tcb_delacktimer); + STOP_TCB_TIMER(SendTCB->tcb_swstimer); + + SendTCB->tcb_alive = TCPTime; + + SendTCB->tcb_fastchk &= ~TCP_FLAG_FASTREC; + + CTEFreeLock(&SendTCB->tcb_lock, TCBHandle); + + //KdPrint (("Going out to IP SendTCB %x, Firstbuf %x\n", SendTCB, FirstBuffer)); + // We're all set. Xsum it and send it. + + Header->tcp_xsum = ~XsumSendChain(SendTCB->tcb_phxsum + + (uint)net_short(AmountToSend), FirstBuffer); + + SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, SCC, + FirstBuffer, AmountToSend, SendTCB->tcb_daddr, + SendTCB->tcb_saddr, &SendTCB->tcb_opt, SendTCB->tcb_rce, + PROTOCOL_TCP); + + SendTCB->tcb_error = SendStatus; + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + + if (SendStatus != IP_PENDING) { + TCPSendComplete(SCC, FirstBuffer); + } + + //Reacquire Lock to keep DerefTCB happy + //Bug #63904 + + CTEGetLock(&SendTCB->tcb_lock, &TCBHandle); + + + } else { // FirstBuffer != NULL. + goto error_oor; + } + } else{ + + SendTCB->tcb_flags |= SEND_AFTER_RCV; + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + } + + + SendTCB->tcb_flags |= NEED_OUTPUT; + + return; + +// Common case error handling code for out of resource conditions. Start the +// retransmit timer if it's not already running (so that we try this again +// later), clean up and return. + +error_oor: + if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer)) + START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit); + + // We had an out of resource problem, so clear the OUTPUT flags. + SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT); + + return; + + +} + +#endif //FAST_RETRANSMIT + + + + +//* TDISend - Send data on a connection. +// +// The main TDI send entry point. We take the input parameters, validate them, +// allocate a send request, etc. We then put the send request on the queue. +// If we have no other sends on the queue or Nagling is disabled we'll +// call TCPSend to send the data. +// +// Input: Request - The TDI request for the call. +// Flags - Flags for this send. +// SendLength - Length in bytes of send. +// SendBuffer - Pointer to buffer chain to be sent. +// +// Returns: Status of attempt to send. +// +TDI_STATUS +TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength, + PNDIS_BUFFER SendBuffer) +{ + TCPConn *Conn; + TCB *SendTCB; + TCPSendReq *SendReq; + CTELockHandle ConnTableHandle, TCBHandle; + TDI_STATUS Error; + uint EmptyQ; + +#ifdef DEBUG + uint RealSendSize; + PNDIS_BUFFER Temp; + + // Loop through the buffer chain, and make sure that the length matches + // up with SendLength. + + Temp = SendBuffer; + RealSendSize = 0; + do { + CTEAssert(Temp != NULL); + + RealSendSize += NdisBufferLength(Temp); + Temp = NDIS_BUFFER_LINKAGE(Temp); + } while (Temp != NULL); + + CTEAssert(RealSendSize == SendLength); + +#endif + + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + SendTCB = Conn->tc_tcb; + if (SendTCB != NULL) { + CTEStructAssert(SendTCB, tcb); + CTEGetLockAtDPC(&SendTCB->tcb_lock, &TCBHandle); + CTEFreeLockFromDPC(&ConnTableLock, TCBHandle); + if (DATA_SEND_STATE(SendTCB->tcb_state) && !CLOSING(SendTCB)) { + // We have a TCB, and it's valid. Get a send request now. + + CheckTCBSends(SendTCB); + + if (SendLength != 0) { + + SendReq = GetSendReq(); + if (SendReq != NULL) { + SendReq->tsr_req.tr_rtn = Request->RequestNotifyObject; + SendReq->tsr_req.tr_context = Request->RequestContext; + SendReq->tsr_buffer = SendBuffer; + SendReq->tsr_size = SendLength; + SendReq->tsr_unasize = SendLength; + SendReq->tsr_refcnt = 1; // ACK will decrement this ref + SendReq->tsr_offset = 0; + SendReq->tsr_lastbuf = NULL; + SendReq->tsr_time = TCPTime; + SendReq->tsr_flags = (Flags & TDI_SEND_EXPEDITED) ? + TSR_FLAG_URG : 0; + SendTCB->tcb_unacked += SendLength; + + EmptyQ = EMPTYQ(&SendTCB->tcb_sendq); + ENQUEUE(&SendTCB->tcb_sendq, &SendReq->tsr_req.tr_q); + if (SendTCB->tcb_cursend == NULL) { + SendTCB->tcb_cursend = SendReq; + SendTCB->tcb_sendbuf = SendBuffer; + SendTCB->tcb_sendofs = 0; + SendTCB->tcb_sendsize = SendLength; + } + if (EmptyQ) { + SendTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&SendTCB->tcb_lock, ConnTableHandle); + TCPSend(SendTCB); +#else + TCPSend(SendTCB, ConnTableHandle); +#endif + } else + if (!(SendTCB->tcb_flags & NAGLING) || + (SendTCB->tcb_unacked - (SendTCB->tcb_sendmax - + SendTCB->tcb_senduna)) >= SendTCB->tcb_mss) { + SendTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&SendTCB->tcb_lock, + ConnTableHandle); + TCPSend(SendTCB); +#else + TCPSend(SendTCB, ConnTableHandle); +#endif + } else + CTEFreeLock(&SendTCB->tcb_lock, + ConnTableHandle); + + return TDI_PENDING; + } else + Error = TDI_NO_RESOURCES; + } else + Error = TDI_SUCCESS; + } else + Error = TDI_INVALID_STATE; + + CTEFreeLock(&SendTCB->tcb_lock, ConnTableHandle); + return Error; + } else + Error = TDI_INVALID_STATE; + } else + Error = TDI_INVALID_CONNECTION; + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return Error; + +} + +#pragma BEGIN_INIT +extern void *TLRegisterProtocol(uchar Protocol, void *RcvHandler, + void *XmitHandler, void *StatusHandler, + void *RcvCmpltHandler); + +extern IP_STATUS TCPRcv(void *IPContext, IPAddr Dest, IPAddr Src, + IPAddr LocalAddr, IPAddr SrcAddr, + IPHeader UNALIGNED *IPH, uint IPHLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, + uchar Protocol, IPOptInfo *OptInfo); +extern void TCPRcvComplete(void); + +uchar SendInited = FALSE; + +//* FreeTCPHeaderList - Free the list of TCP header buffers. +// +// Called when we want to free the list of TCP header buffers. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +FreeTCPHeaderList(void) +{ + CTELockHandle Handle; + TCPHdrBPoolEntry *Entry; + + CTEGetLock(&TCPSendFreeLock, &Handle); + + Entry = TCPHdrBPoolList; + TCPHdrBPoolList = NULL; + + TCPCurrentSendFree = 0; + + while (Entry != NULL) { + TCPHdrBPoolEntry *OldEntry; + + NdisFreeBufferPool(Entry->the_handle); + CTEFreeMem(Entry->the_buffer); + OldEntry = Entry; + Entry = Entry->the_next; + CTEFreeMem(OldEntry); + } + + CTEFreeLock(&TCPSendFreeLock, Handle); + + +} + +//* InitTCPSend - Initialize our send side. +// +// Called during init time to initialize our TCP send state. +// +// Input: Nothing. +// +// Returns: TRUE if we inited, false if we didn't. +// +int +InitTCPSend(void) +{ + PNDIS_BUFFER Buffer; + NDIS_STATUS Status; + + +#ifdef NT + ExInitializeSListHead(&TCPSendFree); + ExInitializeSListHead(&TCPSendReqFree); +#endif + + CTEInitLock(&TCPSendReqFreeLock); + CTEInitLock(&TCPSendFreeLock); + CTEInitLock(&TCPSendReqCompleteLock); + + TCPHdrBPoolList = NULL; + TCPCurrentSendFree = 0; + + Buffer = GrowTCPHeaderList(); + + if (Buffer != NULL) + FreeTCPHeader(Buffer); + else + return FALSE; + + NdisAllocateBufferPool(&Status, &TCPSendBufferPool, NUM_TCP_BUFFERS); + if (Status != NDIS_STATUS_SUCCESS) { + FreeTCPHeaderList(); + return FALSE; + } + + TCPProtInfo = TLRegisterProtocol(PROTOCOL_TCP, TCPRcv, TCPSendComplete, + TCPStatus, TCPRcvComplete); + + if (TCPProtInfo == NULL) { + FreeTCPHeaderList(); + NdisFreeBufferPool(TCPSendBufferPool); + return FALSE; + } + + SendInited = TRUE; + return TRUE; +} + +//* UnInitTCPSend - UnInitialize our send side. +// +// Called during init time if we're going to fail to initialize. +// +// Input: Nothing. +// +// Returns: TRUE if we inited, false if we didn't. +// +void +UnInitTCPSend(void) +{ + if (!SendInited) + return; + + TLRegisterProtocol(PROTOCOL_TCP, NULL, NULL, NULL, NULL); + FreeTCPHeaderList(); + NdisFreeBufferPool(TCPSendBufferPool); + +} +#pragma END_INIT + |