mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2666 lines
83 KiB
2666 lines
83 KiB
/********************************************************************/
|
|
/** 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
|
|
|