windows-nt/Source/XPSP1/NT/net/tcpip/tpipv6/tcpip6/tcp/tcpdeliv.c
2020-09-26 16:20:57 +08:00

1944 lines
73 KiB
C

// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// TCP deliver data code.
//
// This file contains the code for delivering data to the user, including
// putting data into recv. buffers and calling indication handlers.
//
#include "oscfg.h"
#include "ndis.h"
#include "ip6imp.h"
#include "ip6def.h"
#include "tdi.h"
#include "tdint.h"
#include "tdistat.h"
#include "queue.h"
#include "transprt.h"
#include "addr.h"
#include "tcp.h"
#include "tcb.h"
#include "tcprcv.h"
#include "tcpsend.h"
#include "tcpconn.h"
#include "tcpdeliv.h"
#include "route.h"
extern KSPIN_LOCK AddrObjTableLock;
extern uint
PutOnRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPv6Packet *Packet, uint Size);
extern IPv6Packet *
TrimPacket(IPv6Packet *Packet, uint AmountToTrim);
SLIST_HEADER TCPRcvReqFree; // Rcv req. free list.
KSPIN_LOCK TCPRcvReqFreeLock; // Protects rcv req free list.
uint NumTCPRcvReq = 0; // Current number of RcvReqs in system.
uint MaxRcvReq = 0xffffffff; // Maximum allowed number of SendReqs.
NTSTATUS TCPPrepareIrpForCancel(PTCP_CONTEXT TcpContext, PIRP Irp,
PDRIVER_CANCEL CancelRoutine);
ULONG TCPGetMdlChainByteCount(PMDL Mdl);
void TCPDataRequestComplete(void *Context, unsigned int Status,
unsigned int ByteCount);
VOID TCPCancelRequest(PDEVICE_OBJECT Device, PIRP Irp);
VOID CompleteRcvs(TCB *CmpltTCB);
//* FreeRcvReq - Free a rcv request structure.
//
// Called to free a rcv request structure.
//
void // Returns: Nothing.
FreeRcvReq(
TCPRcvReq *FreedReq) // Rcv request structure to be freed.
{
PSLIST_ENTRY BufferLink;
CHECK_STRUCT(FreedReq, trr);
BufferLink = CONTAINING_RECORD(&(FreedReq->trr_next), SLIST_ENTRY,
Next);
ExInterlockedPushEntrySList(&TCPRcvReqFree, BufferLink,
&TCPRcvReqFreeLock);
}
//* GetRcvReq - Get a recv. request structure.
//
// Called to get a rcv. request structure.
//
TCPRcvReq * // Returns: Pointer to RcvReq structure, or NULL if none.
GetRcvReq(
void) // Nothing.
{
TCPRcvReq *Temp;
PSLIST_ENTRY BufferLink;
BufferLink = ExInterlockedPopEntrySList(&TCPRcvReqFree,
&TCPRcvReqFreeLock);
if (BufferLink != NULL) {
Temp = CONTAINING_RECORD(BufferLink, TCPRcvReq, trr_next);
CHECK_STRUCT(Temp, trr);
} else {
if (NumTCPRcvReq < MaxRcvReq)
Temp = ExAllocatePool(NonPagedPool, sizeof(TCPRcvReq));
else
Temp = NULL;
if (Temp != NULL) {
ExInterlockedAddUlong(&NumTCPRcvReq, 1, &TCPRcvReqFreeLock);
#if DBG
Temp->trr_sig = trr_signature;
#endif
}
}
return Temp;
}
//* FindLastPacket - Find the last packet in a chain.
//
// A utility routine to find the last packet in a packet chain.
//
IPv6Packet * // Returns: Pointer to last packet in chain.
FindLastPacket(
IPv6Packet *Packet) // Pointer to packet chain.
{
ASSERT(Packet != NULL);
while (Packet->Next != NULL)
Packet = Packet->Next;
return Packet;
}
//* CovetPacketChain - Take owership of a chain of IP packets.
//
// Called to seize ownership of a chain of IP packets. We copy any
// packets that are not already owned by us. We assume that all packets
// not belonging to us start before those that do, so we quit copying
// when we reach a packet we own.
//
IPv6Packet * // Returns: Pointer to new packet chain.
CovetPacketChain(
IPv6Packet *OrigPkt, // Packet chain to copy from.
IPv6Packet **LastPkt, // Where to return pointer to last packet in chain.
uint Size) // Maximum size in bytes to seize.
{
IPv6Packet *FirstPkt, *EndPkt;
uint BytesToCopy;
ASSERT(OrigPkt != NULL);
ASSERT(Size > 0);
if (!(OrigPkt->Flags & PACKET_OURS)) {
BytesToCopy = MIN(Size, OrigPkt->TotalSize);
FirstPkt = ExAllocatePoolWithTagPriority(NonPagedPool,
sizeof(IPv6Packet) +
BytesToCopy, TCP6_TAG,
LowPoolPriority);
if (FirstPkt != NULL) {
EndPkt = FirstPkt;
FirstPkt->Next = NULL;
FirstPkt->Position = 0;
FirstPkt->FlatData = (uchar *)(FirstPkt + 1);
FirstPkt->Data = FirstPkt->FlatData;
FirstPkt->ContigSize = BytesToCopy;
FirstPkt->TotalSize = BytesToCopy;
FirstPkt->NdisPacket = NULL;
FirstPkt->AuxList = NULL;
FirstPkt->Flags = PACKET_OURS;
CopyPacketToBuffer(FirstPkt->Data, OrigPkt, BytesToCopy,
OrigPkt->Position);
Size -= BytesToCopy;
OrigPkt = OrigPkt->Next;
while (OrigPkt != NULL && !(OrigPkt->Flags & PACKET_OURS)
&& Size != 0) {
IPv6Packet *NewPkt;
BytesToCopy = MIN(Size, OrigPkt->TotalSize);
NewPkt = ExAllocatePoolWithTagPriority(NonPagedPool,
sizeof(IPv6Packet) +
BytesToCopy, TCP6_TAG,
LowPoolPriority);
if (NewPkt != NULL) {
NewPkt->Next = NULL;
NewPkt->Position = 0;
NewPkt->FlatData = (uchar *)(NewPkt + 1);
NewPkt->Data = NewPkt->FlatData;
NewPkt->ContigSize = BytesToCopy;
NewPkt->TotalSize = BytesToCopy;
NewPkt->Flags = PACKET_OURS;
NewPkt->NdisPacket = NULL;
NewPkt->AuxList = NULL;
CopyPacketToBuffer(NewPkt->Data, OrigPkt, BytesToCopy,
OrigPkt->Position);
EndPkt->Next = NewPkt;
EndPkt = NewPkt;
Size -= BytesToCopy;
OrigPkt = OrigPkt->Next;
} else {
FreePacketChain(FirstPkt);
return NULL;
}
}
EndPkt->Next = OrigPkt;
} else
return NULL;
} else {
FirstPkt = OrigPkt;
EndPkt = OrigPkt;
if (Size < OrigPkt->TotalSize) {
OrigPkt->TotalSize = Size;
OrigPkt->ContigSize = Size;
}
Size -= OrigPkt->TotalSize;
}
//
// Now walk down the chain, until we run out of Size.
// At this point, Size is the bytes left to 'seize' (it may be 0),
// and the sizes in packets FirstPkt...EndPkt are correct.
//
while (Size != 0) {
EndPkt = EndPkt->Next;
ASSERT(EndPkt != NULL);
if (Size < EndPkt->TotalSize) {
EndPkt->TotalSize = Size;
EndPkt->ContigSize = Size;
}
Size -= EndPkt->TotalSize;
}
// If there's anything left in the chain, free it now.
if (EndPkt->Next != NULL) {
FreePacketChain(EndPkt->Next);
EndPkt->Next = NULL;
}
*LastPkt = EndPkt;
return FirstPkt;
}
//* PendData - Pend incoming data to a client.
//
// Called when we need to buffer data for a client because there's no receive
// down and we can't indicate.
//
// The TCB lock is held throughout this procedure. If this is to be changed,
// make sure consistency of tcb_pendingcnt is preserved. This routine is
// always called at DPC level.
//
uint // Returns: Number of bytes of data taken.
PendData(
TCB *RcvTCB, // TCB on which to receive the data.
uint RcvFlags, // TCP flags for the incoming packet.
IPv6Packet *InPacket, // Input buffer of packet.
uint Size) // Size in bytes of data in InPacket.
{
IPv6Packet *NewPkt, *LastPkt;
CHECK_STRUCT(RcvTCB, tcb);
ASSERT(Size > 0);
ASSERT(InPacket != NULL);
ASSERT(RcvTCB->tcb_refcnt != 0);
ASSERT(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV);
ASSERT(RcvTCB->tcb_currcv == NULL);
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
CheckPacketList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
NewPkt = CovetPacketChain(InPacket, &LastPkt, Size);
if (NewPkt != NULL) {
//
// We have a duplicate chain. Put it on the end of the pending q.
//
if (RcvTCB->tcb_pendhead == NULL) {
RcvTCB->tcb_pendhead = NewPkt;
RcvTCB->tcb_pendtail = LastPkt;
} else {
RcvTCB->tcb_pendtail->Next = NewPkt;
RcvTCB->tcb_pendtail = LastPkt;
}
RcvTCB->tcb_pendingcnt += Size;
} else {
FreePacketChain(InPacket);
Size = 0;
}
CheckPacketList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
return Size;
}
//* BufferData - Put incoming data into client's buffer.
//
// Called when we believe we have a buffer into which we can put data. We put
// it in there, and if we've filled the buffer or the incoming data has the
// push flag set we'll mark the TCB to return the buffer. Otherwise we'll
// get out and return the data later.
//
// In NT, this routine is called with the TCB lock held, and holds it for
// the duration of the call. This is important to ensure consistency of
// the tcb_pendingcnt field. If we need to change this to free the lock
// partway through, make sure to take this into account. In particular,
// TdiReceive zeros pendingcnt before calling this routine, and this routine
// may update it. If the lock is freed in here there would be a window where
// we really do have pending data, but it's not on the list or reflected in
// pendingcnt. This could mess up our windowing computations, and we'd have
// to be careful not to end up with more data pending than our window allows.
//
uint // Returns: Number of bytes of data taken.
BufferData(
TCB *RcvTCB, // TCB on which to receive the data.
uint RcvFlags, // TCP rcv flags for the incoming packet.
IPv6Packet *InPacket, // Input buffer of packet.
uint Size) // Size in bytes of data in InPacket.
{
uchar *DestPtr; // Destination pointer.
uchar *SrcPtr; // Src pointer.
uint SrcSize; // Size of current source buffer.
uint DestSize; // Size of current destination buffer.
uint Copied; // Total bytes to copy.
uint BytesToCopy; // Bytes of data to copy this time.
TCPRcvReq *DestReq; // Current receive request.
IPv6Packet *SrcPkt; // Current source packet.
PNDIS_BUFFER DestBuf; // Current receive buffer.
uint RcvCmpltd;
uint Flags;
CHECK_STRUCT(RcvTCB, tcb);
ASSERT(Size > 0);
ASSERT(InPacket != NULL);
ASSERT(RcvTCB->tcb_refcnt != 0);
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
//
// In order to copy the received data to the application's buffers,
// we now need to map those buffers into the system's address space.
// Rather than attempting to map them below, where the going gets rough,
// we do it up-front where errors may be more readily handled.
//
// N.B. We map one buffer beyond what we need, since the code below
// will update the current receive-request to point beyond the data copied.
//
Copied = 0;
for (DestReq = RcvTCB->tcb_currcv; DestReq; DestReq = DestReq->trr_next) {
uint DestAvail = DestReq->trr_size - DestReq->trr_amt;
for (DestBuf = DestReq->trr_buffer, DestSize = DestReq->trr_offset;
DestBuf && DestAvail && Copied < Size;
DestBuf = NDIS_BUFFER_LINKAGE(DestBuf), DestSize = 0) {
if (!NdisBufferVirtualAddressSafe(DestBuf, NormalPagePriority)) {
return 0;
}
DestSize = MIN(NdisBufferLength(DestBuf) - DestSize, DestAvail);
DestAvail -= DestSize;
Copied += DestSize;
}
if (Copied >= Size) {
//
// We've mapped the space into which we'll copy;
// now map the space immediately beyond that.
//
if (DestAvail) {
//
// We believe space remains in the current receive-request;
// DestBuf should point to the current buffer.
//
ASSERT(DestBuf);
} else if ((DestReq = DestReq->trr_next) != NULL) {
//
// No more space in that receive-request, but there's another;
// Move to this next one, and map the start of that.
//
DestBuf = DestReq->trr_buffer;
} else {
break;
}
if (!NdisBufferVirtualAddressSafe(DestBuf, NormalPagePriority)) {
return 0;
}
break;
}
}
Copied = 0;
RcvCmpltd = 0;
DestReq = RcvTCB->tcb_currcv;
ASSERT(DestReq != NULL);
CHECK_STRUCT(DestReq, trr);
DestBuf = DestReq->trr_buffer;
DestSize = MIN(NdisBufferLength(DestBuf) - DestReq->trr_offset,
DestReq->trr_size - DestReq->trr_amt);
DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf) + DestReq->trr_offset;
SrcPkt = InPacket;
SrcSize = SrcPkt->TotalSize;
Flags = (RcvFlags & TCP_FLAG_PUSH) ? TRR_PUSHED : 0;
RcvCmpltd = Flags;
DestReq->trr_flags |= Flags;
do {
BytesToCopy = MIN(Size - Copied, MIN(SrcSize, DestSize));
CopyPacketToBuffer(DestPtr, SrcPkt, BytesToCopy, SrcPkt->Position);
Copied += BytesToCopy;
DestReq->trr_amt += BytesToCopy;
// Update our source pointers.
if ((SrcSize -= BytesToCopy) == 0) {
IPv6Packet *TempPkt;
// We've copied everything in this packet.
TempPkt = SrcPkt;
SrcPkt = SrcPkt->Next;
if (Size != Copied) {
ASSERT(SrcPkt != NULL);
SrcSize = SrcPkt->TotalSize;
}
TempPkt->Next = NULL;
FreePacketChain(TempPkt);
} else {
if (BytesToCopy < SrcPkt->ContigSize) {
//
// We have a contiguous region, easy to skip forward.
//
AdjustPacketParams(SrcPkt, BytesToCopy);
} else {
//
// REVIEW: This method isn't very efficient.
//
PositionPacketAt(SrcPkt, SrcPkt->Position + BytesToCopy);
}
}
// Now check the destination pointer, and update it if we need to.
if ((DestSize -= BytesToCopy) == 0) {
uint DestAvail;
// Exhausted this buffer. See if there's another one.
DestAvail = DestReq->trr_size - DestReq->trr_amt;
DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
if (DestBuf != NULL && (DestAvail != 0)) {
// Have another buffer in the chain. Update things.
DestSize = MIN(NdisBufferLength(DestBuf), DestAvail);
DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf);
} else {
// No more buffers in the chain. See if we have another buffer
// on the list.
DestReq->trr_flags |= TRR_PUSHED;
// If we've been told there's to be no back traffic, get an ACK
// going right away.
if (DestReq->trr_flags & TDI_RECEIVE_NO_RESPONSE_EXP)
DelayAction(RcvTCB, NEED_ACK);
RcvCmpltd = TRUE;
DestReq = DestReq->trr_next;
if (DestReq != NULL) {
DestBuf = DestReq->trr_buffer;
DestSize = MIN(NdisBufferLength(DestBuf),
DestReq->trr_size);
DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf);
// If we have more to put into here, set the flags.
if (Copied != Size)
DestReq->trr_flags |= Flags;
} else {
// All out of buffer space. Reset the data handler pointer.
break;
}
}
} else
// Current buffer not empty yet.
DestPtr += BytesToCopy;
// If we've copied all that we need to, we're done.
} while (Copied != Size);
//
// We've finished copying, and have a few more things to do. We need to
// update the current rcv. pointer and possibly the offset in the
// recv. request. If we need to complete any receives we have to schedule
// that. If there's any data we couldn't copy we'll need to dispose of it.
//
RcvTCB->tcb_currcv = DestReq;
if (DestReq != NULL) {
DestReq->trr_buffer = DestBuf;
DestReq->trr_offset = (uint) (DestPtr - (uchar *) NdisBufferVirtualAddress(DestBuf));
RcvTCB->tcb_rcvhndlr = BufferData;
} else
RcvTCB->tcb_rcvhndlr = PendData;
RcvTCB->tcb_indicated -= MIN(Copied, RcvTCB->tcb_indicated);
if (Size != Copied) {
IPv6Packet *NewPkt, *LastPkt;
ASSERT(DestReq == NULL);
// We have data to dispose of. Update the first buffer of the chain
// with the current src pointer and size, and copy it.
ASSERT(SrcSize <= SrcPkt->TotalSize);
NewPkt = CovetPacketChain(SrcPkt, &LastPkt, Size - Copied);
if (NewPkt != NULL) {
// We managed to copy the chain. Push it on the pending queue.
if (RcvTCB->tcb_pendhead == NULL) {
RcvTCB->tcb_pendhead = NewPkt;
RcvTCB->tcb_pendtail = LastPkt;
} else {
LastPkt->Next = RcvTCB->tcb_pendhead;
RcvTCB->tcb_pendhead = NewPkt;
}
RcvTCB->tcb_pendingcnt += Size - Copied;
Copied = Size;
CheckPacketList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
} else
FreePacketChain(SrcPkt);
} else {
// We copied Size bytes, but the chain could be longer than that. Free
// it if we need to.
if (SrcPkt != NULL)
FreePacketChain(SrcPkt);
}
if (RcvCmpltd != 0) {
DelayAction(RcvTCB, NEED_RCV_CMPLT);
} else {
START_TCB_TIMER(RcvTCB->tcb_pushtimer, PUSH_TO);
}
return Copied;
}
//* IndicateData - Indicate incoming data to a client.
//
// Called when we need to indicate data to an upper layer client. We'll pass
// up a pointer to whatever we have available, and the client may take some
// or all of it.
//
uint // Returns: Number of bytes of data taken.
IndicateData(
TCB *RcvTCB, // TCB on which to receive the data.
uint RcvFlags, // TCP receive flags for the incoming packet.
IPv6Packet *InPacket, // Input buffer of packet.
uint Size) // Size in bytes of data in InPacket.
{
TDI_STATUS Status;
PRcvEvent Event;
PVOID EventContext, ConnContext;
uint BytesTaken = 0;
EventRcvBuffer *ERB = NULL;
PTDI_REQUEST_KERNEL_RECEIVE RequestInformation;
PIO_STACK_LOCATION IrpSp;
TCPRcvReq *RcvReq;
ulong IndFlags;
CHECK_STRUCT(RcvTCB, tcb);
ASSERT(Size > 0);
ASSERT(InPacket != NULL);
ASSERT(RcvTCB->tcb_refcnt != 0);
ASSERT(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV);
ASSERT(RcvTCB->tcb_rcvind != NULL);
ASSERT(RcvTCB->tcb_rcvhead == NULL);
ASSERT(RcvTCB->tcb_rcvhndlr == IndicateData);
RcvReq = GetRcvReq();
if (RcvReq != NULL) {
//
// The indicate handler is saved in the TCB. Just call up into it.
//
Event = RcvTCB->tcb_rcvind;
EventContext = RcvTCB->tcb_ricontext;
ConnContext = RcvTCB->tcb_conncontext;
RcvTCB->tcb_indicated = Size;
RcvTCB->tcb_flags |= IN_RCV_IND;
KeReleaseSpinLockFromDpcLevel(&RcvTCB->tcb_lock);
//
// If we're at the end of a contigous data region,
// move forward to the next one. This prevents us
// from making nonsensical zero byte indications.
//
if (InPacket->ContigSize == 0) {
PacketPullupSubr(InPacket, 0, 1, 0);
}
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
"Indicating %lu bytes, %lu available\n",
MIN(InPacket->ContigSize, Size), Size));
}
#if TCP_FLAG_PUSH >= TDI_RECEIVE_ENTIRE_MESSAGE
IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
TDI_RECEIVE_AT_DISPATCH_LEVEL |
((RcvFlags & TCP_FLAG_PUSH) >>
((TCP_FLAG_PUSH / TDI_RECEIVE_ENTIRE_MESSAGE) - 1));
#else
IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
TDI_RECEIVE_AT_DISPATCH_LEVEL |
((RcvFlags & TCP_FLAG_PUSH) <<
((TDI_RECEIVE_ENTIRE_MESSAGE / TCP_FLAG_PUSH) - 1));
#endif
Status = (*Event)(EventContext, ConnContext, IndFlags,
MIN(InPacket->ContigSize, Size), Size, &BytesTaken,
InPacket->Data, &ERB);
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
"%lu bytes taken, status %lx\n", BytesTaken, Status));
}
//
// See what the client did. If the return status is MORE_PROCESSING,
// we've been given a buffer. In that case put it on the front of the
// buffer queue, and if all the data wasn't taken go ahead and copy
// it into the new buffer chain.
//
// Note that the size and buffer chain we're concerned with here is
// the one that we passed to the client. Since we're in a recieve
// handler, any data that has come in would have been put on the
// reassembly queue.
//
if (Status == TDI_MORE_PROCESSING) {
ASSERT(ERB != NULL);
IrpSp = IoGetCurrentIrpStackLocation(ERB);
Status = TCPPrepareIrpForCancel(
(PTCP_CONTEXT) IrpSp->FileObject->FsContext, ERB,
TCPCancelRequest);
if (NT_SUCCESS(Status)) {
RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE)
&(IrpSp->Parameters);
RcvReq->trr_rtn = TCPDataRequestComplete;
RcvReq->trr_context = ERB;
RcvReq->trr_buffer = ERB->MdlAddress;
RcvReq->trr_size = RequestInformation->ReceiveLength;
RcvReq->trr_uflags = (ushort *)
&(RequestInformation->ReceiveFlags);
RcvReq->trr_flags = (uint)(RequestInformation->ReceiveFlags);
RcvReq->trr_offset = 0;
RcvReq->trr_amt = 0;
KeAcquireSpinLockAtDpcLevel(&RcvTCB->tcb_lock);
RcvTCB->tcb_flags &= ~IN_RCV_IND;
ASSERT(RcvTCB->tcb_rcvhndlr == IndicateData);
// Push him on the front of the rcv. queue.
ASSERT((RcvTCB->tcb_currcv == NULL) ||
(RcvTCB->tcb_currcv->trr_amt == 0));
if (RcvTCB->tcb_rcvhead == NULL) {
RcvTCB->tcb_rcvhead = RcvReq;
RcvTCB->tcb_rcvtail = RcvReq;
RcvReq->trr_next = NULL;
} else {
RcvReq->trr_next = RcvTCB->tcb_rcvhead;
RcvTCB->tcb_rcvhead = RcvReq;
}
RcvTCB->tcb_currcv = RcvReq;
RcvTCB->tcb_rcvhndlr = BufferData;
ASSERT(BytesTaken <= Size);
RcvTCB->tcb_indicated -= BytesTaken;
if ((Size -= BytesTaken) != 0) {
//
// Not everything was taken.
// Adjust the buffer chain to point beyond what was taken.
//
InPacket = TrimPacket(InPacket, BytesTaken);
ASSERT(InPacket != NULL);
//
// We've adjusted the buffer chain.
// Call the BufferData handler.
//
BytesTaken += BufferData(RcvTCB, RcvFlags, InPacket, Size);
} else {
// All of the data was taken. Free the buffer chain.
FreePacketChain(InPacket);
}
return BytesTaken;
} else {
//
// The IRP was cancelled before it was handed back to us.
// We'll pretend we never saw it. TCPPrepareIrpForCancel
// already completed it. The client may have taken data,
// so we will act as if success was returned.
//
ERB = NULL;
Status = TDI_SUCCESS;
}
}
KeAcquireSpinLockAtDpcLevel(&RcvTCB->tcb_lock);
RcvTCB->tcb_flags &= ~IN_RCV_IND;
//
// Status is not more processing. If it's not SUCCESS, the client
// didn't take any of the data. In either case we now need to
// see if all of the data was taken. If it wasn't, we'll try and
// push it onto the front of the pending queue.
//
FreeRcvReq(RcvReq); // This won't be needed.
if (Status == TDI_NOT_ACCEPTED)
BytesTaken = 0;
ASSERT(BytesTaken <= Size);
RcvTCB->tcb_indicated -= BytesTaken;
ASSERT(RcvTCB->tcb_rcvhndlr == IndicateData);
// Check to see if a rcv. buffer got posted during the indication.
// If it did, reset the receive handler now.
if (RcvTCB->tcb_rcvhead != NULL)
RcvTCB->tcb_rcvhndlr = BufferData;
// See if all of the data was taken.
if (BytesTaken == Size) {
ASSERT(RcvTCB->tcb_indicated == 0);
FreePacketChain(InPacket);
return BytesTaken; // It was all taken.
}
//
// It wasn't all taken. Adjust for what was taken, and push
// on the front of the pending queue. We also need to check to
// see if a receive buffer got posted during the indication. This
// would be weird, but not impossible.
//
InPacket = TrimPacket(InPacket, BytesTaken);
if (RcvTCB->tcb_rcvhead == NULL) {
IPv6Packet *LastPkt, *NewPkt;
RcvTCB->tcb_rcvhndlr = PendData;
NewPkt = CovetPacketChain(InPacket, &LastPkt, Size - BytesTaken);
if (NewPkt != NULL) {
// We have a duplicate chain. Push it on the front of the
// pending q.
if (RcvTCB->tcb_pendhead == NULL) {
RcvTCB->tcb_pendhead = NewPkt;
RcvTCB->tcb_pendtail = LastPkt;
} else {
LastPkt->Next = RcvTCB->tcb_pendhead;
RcvTCB->tcb_pendhead = NewPkt;
}
RcvTCB->tcb_pendingcnt += Size - BytesTaken;
BytesTaken = Size;
} else {
FreePacketChain(InPacket);
}
return BytesTaken;
} else {
//
// Just great. There's now a receive buffer on the TCB.
// Call the BufferData handler now.
//
ASSERT(RcvTCB->tcb_rcvhndlr = BufferData);
BytesTaken += BufferData(RcvTCB, RcvFlags, InPacket,
Size - BytesTaken);
return BytesTaken;
}
} else {
//
// Couldn't get a receive request. We must be really low on resources,
// so just bail out now.
//
FreePacketChain(InPacket);
return 0;
}
}
//* IndicatePendingData - Indicate pending data to a client.
//
// Called when we need to indicate pending data to an upper layer client,
// usually because data arrived when we were in a state that it couldn't
// be indicated.
//
// Many of the comments in the BufferData header about the consistency of
// tcb_pendingcnt apply here also.
//
void // Returns: Nothing.
IndicatePendingData(
TCB *RcvTCB, // TCB on which to indicate the data.
TCPRcvReq *RcvReq, // Receive request to use to indicate it.
KIRQL PreLockIrql) // IRQL prior to acquiring TCB lock.
{
TDI_STATUS Status;
PRcvEvent Event;
PVOID EventContext, ConnContext;
uint BytesTaken = 0;
EventRcvBuffer *ERB = NULL;
PTDI_REQUEST_KERNEL_RECEIVE RequestInformation;
PIO_STACK_LOCATION IrpSp;
IPv6Packet *NewPkt;
uint Size;
uint BytesIndicated;
uint BytesAvailable;
uchar* DataBuffer;
CHECK_STRUCT(RcvTCB, tcb);
ASSERT(RcvTCB->tcb_refcnt != 0);
ASSERT(RcvTCB->tcb_rcvind != NULL);
ASSERT(RcvTCB->tcb_rcvhead == NULL);
ASSERT(RcvTCB->tcb_pendingcnt != 0);
ASSERT(RcvReq != NULL);
for (;;) {
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
// The indicate handler is saved in the TCB. Just call up into it.
Event = RcvTCB->tcb_rcvind;
EventContext = RcvTCB->tcb_ricontext;
ConnContext = RcvTCB->tcb_conncontext;
BytesIndicated = RcvTCB->tcb_pendhead->ContigSize;
BytesAvailable = RcvTCB->tcb_pendingcnt;
DataBuffer = RcvTCB->tcb_pendhead->Data;
RcvTCB->tcb_indicated = RcvTCB->tcb_pendingcnt;
RcvTCB->tcb_flags |= IN_RCV_IND;
KeReleaseSpinLock(&RcvTCB->tcb_lock, PreLockIrql);
IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
"Indicating pending %d bytes, %d available\n",
BytesIndicated,
BytesAvailable));
}
Status = (*Event)(EventContext, ConnContext,
TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
TDI_RECEIVE_ENTIRE_MESSAGE,
BytesIndicated, BytesAvailable, &BytesTaken,
DataBuffer, &ERB);
IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
"%d bytes taken\n", BytesTaken));
}
//
// See what the client did. If the return status is MORE_PROCESSING,
// we've been given a buffer. In that case put it on the front of the
// buffer queue, and if all the data wasn't taken go ahead and copy
// it into the new buffer chain.
//
if (Status == TDI_MORE_PROCESSING) {
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
"more processing on receive\n"));
}
ASSERT(ERB != NULL);
IrpSp = IoGetCurrentIrpStackLocation(ERB);
Status = TCPPrepareIrpForCancel(
(PTCP_CONTEXT) IrpSp->FileObject->FsContext, ERB,
TCPCancelRequest);
if (NT_SUCCESS(Status)) {
RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE)
&(IrpSp->Parameters);
RcvReq->trr_rtn = TCPDataRequestComplete;
RcvReq->trr_context = ERB;
RcvReq->trr_buffer = ERB->MdlAddress;
RcvReq->trr_size = RequestInformation->ReceiveLength;
RcvReq->trr_uflags = (ushort *)
&(RequestInformation->ReceiveFlags);
RcvReq->trr_flags = (uint)(RequestInformation->ReceiveFlags);
RcvReq->trr_offset = 0;
RcvReq->trr_amt = 0;
KeAcquireSpinLock(&RcvTCB->tcb_lock, &PreLockIrql);
RcvTCB->tcb_flags &= ~IN_RCV_IND;
// Push him on the front of the receive queue.
ASSERT((RcvTCB->tcb_currcv == NULL) ||
(RcvTCB->tcb_currcv->trr_amt == 0));
if (RcvTCB->tcb_rcvhead == NULL) {
RcvTCB->tcb_rcvhead = RcvReq;
RcvTCB->tcb_rcvtail = RcvReq;
RcvReq->trr_next = NULL;
} else {
RcvReq->trr_next = RcvTCB->tcb_rcvhead;
RcvTCB->tcb_rcvhead = RcvReq;
}
RcvTCB->tcb_currcv = RcvReq;
RcvTCB->tcb_rcvhndlr = BufferData;
//
// Have to pick up the new size and pointers now, since things
// could have changed during the upcall.
//
Size = RcvTCB->tcb_pendingcnt;
NewPkt = RcvTCB->tcb_pendhead;
RcvTCB->tcb_pendingcnt = 0;
RcvTCB->tcb_pendhead = NULL;
ASSERT(BytesTaken <= Size);
RcvTCB->tcb_indicated -= BytesTaken;
if ((Size -= BytesTaken) != 0) {
//
// Not everything was taken. Adjust the buffer chain to
// point beyond what was taken.
//
NewPkt = TrimPacket(NewPkt, BytesTaken);
ASSERT(NewPkt != NULL);
//
// We've adjusted the buffer chain.
// Call the BufferData handler.
//
(void)BufferData(RcvTCB, TCP_FLAG_PUSH, NewPkt, Size);
KeReleaseSpinLock(&RcvTCB->tcb_lock, PreLockIrql);
} else {
//
// All of the data was taken. Free the buffer chain.
// Since we were passed a buffer chain which we put on the
// head of the list, leave the rcvhandler pointing at
// BufferData.
//
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
ASSERT(RcvTCB->tcb_indicated == 0);
ASSERT(RcvTCB->tcb_rcvhead != NULL);
KeReleaseSpinLock(&RcvTCB->tcb_lock, PreLockIrql);
FreePacketChain(NewPkt);
}
return;
} else {
//
// The IRP was cancelled before it was handed back to us.
// We'll pretend we never saw it. TCPPrepareIrpForCancel
// already completed it. The client may have taken data,
// so we will act as if success was returned.
//
ERB = NULL;
Status = TDI_SUCCESS;
}
}
KeAcquireSpinLock(&RcvTCB->tcb_lock, &PreLockIrql);
RcvTCB->tcb_flags &= ~IN_RCV_IND;
//
// Status is not more processing. If it's not SUCCESS, the client
// didn't take any of the data. In either case we now need to
// see if all of the data was taken. If it wasn't, we're done.
//
if (Status == TDI_NOT_ACCEPTED)
BytesTaken = 0;
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
RcvTCB->tcb_indicated -= BytesTaken;
Size = RcvTCB->tcb_pendingcnt;
NewPkt = RcvTCB->tcb_pendhead;
ASSERT(BytesTaken <= Size);
// See if all of the data was taken.
if (BytesTaken == Size) {
// It was all taken. Zap the pending data information.
RcvTCB->tcb_pendingcnt = 0;
RcvTCB->tcb_pendhead = NULL;
ASSERT(RcvTCB->tcb_indicated == 0);
if (RcvTCB->tcb_rcvhead == NULL) {
if (RcvTCB->tcb_rcvind != NULL)
RcvTCB->tcb_rcvhndlr = IndicateData;
} else
RcvTCB->tcb_rcvhndlr = BufferData;
KeReleaseSpinLock(&RcvTCB->tcb_lock, PreLockIrql);
FreePacketChain(NewPkt);
break;
}
//
// It wasn't all taken. Adjust for what was taken; we also need to
// check to see if a receive buffer got posted during the indication.
// This would be weird, but not impossible.
//
NewPkt = TrimPacket(NewPkt, BytesTaken);
ASSERT(RcvTCB->tcb_rcvhndlr == PendData);
if (RcvTCB->tcb_rcvhead == NULL) {
RcvTCB->tcb_pendhead = NewPkt;
RcvTCB->tcb_pendingcnt -= BytesTaken;
if (RcvTCB->tcb_indicated != 0 || RcvTCB->tcb_rcvind == NULL) {
KeReleaseSpinLock(&RcvTCB->tcb_lock, PreLockIrql);
break;
}
// From here, we'll loop around and indicate the new data that
// presumably came in during the previous indication.
} else {
//
// Just great. There's now a receive buffer on the TCB.
// Call the BufferData handler now.
//
RcvTCB->tcb_rcvhndlr = BufferData;
RcvTCB->tcb_pendingcnt = 0;
RcvTCB->tcb_pendhead = NULL;
BytesTaken += BufferData(RcvTCB, TCP_FLAG_PUSH, NewPkt,
Size - BytesTaken);
KeReleaseSpinLock(&RcvTCB->tcb_lock, PreLockIrql);
break;
}
} // for (;;)
FreeRcvReq(RcvReq); // This isn't needed anymore.
}
//* DeliverUrgent - Deliver urgent data to a client.
//
// Called to deliver urgent data to a client. We assume the input
// urgent data is in a buffer we can keep. The buffer can be NULL, in
// which case we'll just look on the urgent pending queue for data.
//
void // Returns: Nothing.
DeliverUrgent(
TCB *RcvTCB, // TCB to deliver on.
IPv6Packet *RcvPkt, // Packet for urgent data.
uint Size, // Number of bytes of urgent data to deliver.
KIRQL *pTCBIrql) // Location of KIRQL prior to acquiring TCB lock.
{
KIRQL Irql1, Irql2, Irql3; // One per lock nesting level.
TCPRcvReq *RcvReq, *PrevReq;
uint BytesTaken = 0;
IPv6Packet *LastPkt;
EventRcvBuffer *ERB;
PRcvEvent ExpRcv;
PVOID ExpRcvContext;
PVOID ConnContext;
TDI_STATUS Status;
CHECK_STRUCT(RcvTCB, tcb);
ASSERT(RcvTCB->tcb_refcnt != 0);
CheckPacketList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
//
// See if we have new data, or are processing old data.
//
if (RcvPkt != NULL) {
//
// We have new data. If the pending queue is not NULL, or we're
// already in this routine, just put the buffer on the end of the
// queue.
//
if (RcvTCB->tcb_urgpending != NULL ||
(RcvTCB->tcb_flags & IN_DELIV_URG)) {
IPv6Packet *PrevRcvPkt;
// Put him on the end of the queue.
PrevRcvPkt = CONTAINING_RECORD(&RcvTCB->tcb_urgpending, IPv6Packet,
Next);
while (PrevRcvPkt->Next != NULL)
PrevRcvPkt = PrevRcvPkt->Next;
PrevRcvPkt->Next = RcvPkt;
return;
}
} else {
//
// The input buffer is NULL. See if we have existing data, or are in
// this routine. If we have no existing data or are in this routine
// just return.
//
if (RcvTCB->tcb_urgpending == NULL ||
(RcvTCB->tcb_flags & IN_DELIV_URG)) {
return;
} else {
RcvPkt = RcvTCB->tcb_urgpending;
Size = RcvTCB->tcb_urgcnt;
RcvTCB->tcb_urgpending = NULL;
RcvTCB->tcb_urgcnt = 0;
}
}
ASSERT(RcvPkt != NULL);
ASSERT(!(RcvTCB->tcb_flags & IN_DELIV_URG));
//
// We know we have data to deliver, and we have a pointer and a size.
// Go into a loop, trying to deliver the data. On each iteration, we'll
// try to find a buffer for the data. If we find one, we'll copy and
// complete it right away. Otherwise we'll try and indicate it. If we
// can't indicate it, we'll put it on the pending queue and leave.
//
RcvTCB->tcb_flags |= IN_DELIV_URG;
RcvTCB->tcb_slowcount++;
RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
CheckTCBRcv(RcvTCB);
do {
CheckPacketList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
BytesTaken = 0;
// First check the expedited queue.
if ((RcvReq = RcvTCB->tcb_exprcv) != NULL)
RcvTCB->tcb_exprcv = RcvReq->trr_next;
else {
//
// Nothing in the expedited receive queue. Walk down the ordinary
// receive queue, looking for a buffer that we can steal.
//
PrevReq = CONTAINING_RECORD(&RcvTCB->tcb_rcvhead, TCPRcvReq,
trr_next);
RcvReq = PrevReq->trr_next;
while (RcvReq != NULL) {
CHECK_STRUCT(RcvReq, trr);
if (RcvReq->trr_flags & TDI_RECEIVE_EXPEDITED) {
// This is a candidate.
if (RcvReq->trr_amt == 0) {
ASSERT(RcvTCB->tcb_rcvhndlr == BufferData);
//
// And he has nothing currently in him.
// Pull him out of the queue.
//
if (RcvTCB->tcb_rcvtail == RcvReq) {
if (RcvTCB->tcb_rcvhead == RcvReq)
RcvTCB->tcb_rcvtail = NULL;
else
RcvTCB->tcb_rcvtail = PrevReq;
}
PrevReq->trr_next = RcvReq->trr_next;
if (RcvTCB->tcb_currcv == RcvReq) {
RcvTCB->tcb_currcv = RcvReq->trr_next;
if (RcvTCB->tcb_currcv == NULL) {
//
// We've taken the last receive from the list.
// Reset the rcvhndlr.
//
if (RcvTCB->tcb_rcvind != NULL &&
RcvTCB->tcb_indicated == 0)
RcvTCB->tcb_rcvhndlr = IndicateData;
else
RcvTCB->tcb_rcvhndlr = PendData;
}
}
break;
}
}
PrevReq = RcvReq;
RcvReq = PrevReq->trr_next;
}
}
//
// We've done our best to get a buffer. If we got one, copy into it
// now, and complete the request.
//
if (RcvReq != NULL) {
// Got a buffer.
KeReleaseSpinLock(&RcvTCB->tcb_lock, *pTCBIrql);
BytesTaken = CopyPacketToNdis(RcvReq->trr_buffer, RcvPkt,
Size, 0, RcvPkt->Position);
(*RcvReq->trr_rtn)(RcvReq->trr_context, TDI_SUCCESS, BytesTaken);
FreeRcvReq(RcvReq);
KeAcquireSpinLock(&RcvTCB->tcb_lock, pTCBIrql);
RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind, BytesTaken);
} else {
// No posted buffer. If we can indicate, do so.
if (RcvTCB->tcb_urgind == 0) {
TCPConn *Conn;
// See if he has an expedited rcv handler.
ConnContext = RcvTCB->tcb_conncontext;
KeReleaseSpinLock(&RcvTCB->tcb_lock, *pTCBIrql);
KeAcquireSpinLock(&AddrObjTableLock, &Irql1);
KeAcquireSpinLock(
&ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)]->cb_lock,
&Irql2);
KeAcquireSpinLock(&RcvTCB->tcb_lock, pTCBIrql);
if ((Conn = RcvTCB->tcb_conn) != NULL) {
CHECK_STRUCT(Conn, tc);
ASSERT(Conn->tc_tcb == RcvTCB);
KeReleaseSpinLock(&RcvTCB->tcb_lock, *pTCBIrql);
if (Conn->tc_ao != NULL) {
AddrObj *AO;
AO = Conn->tc_ao;
KeAcquireSpinLock(&AO->ao_lock, &Irql3);
if (AO_VALID(AO) && (ExpRcv = AO->ao_exprcv) != NULL) {
ExpRcvContext = AO->ao_exprcvcontext;
KeReleaseSpinLock(&AO->ao_lock, Irql3);
// We're going to indicate.
RcvTCB->tcb_urgind = Size;
KeReleaseSpinLock(&Conn->tc_ConnBlock->cb_lock,
Irql2);
KeReleaseSpinLock(&AddrObjTableLock, Irql1);
Status = (*ExpRcv)(ExpRcvContext, ConnContext,
TDI_RECEIVE_COPY_LOOKAHEAD |
TDI_RECEIVE_ENTIRE_MESSAGE |
TDI_RECEIVE_EXPEDITED,
MIN(RcvPkt->ContigSize, Size),
Size, &BytesTaken, RcvPkt->Data,
&ERB);
KeAcquireSpinLock(&RcvTCB->tcb_lock, pTCBIrql);
// See what he did with it.
if (Status == TDI_MORE_PROCESSING) {
uint CopySize;
// He gave us a buffer.
if (BytesTaken == Size) {
//
// He gave us a buffer, but took all of
// it. We'll just return it to him.
//
CopySize = 0;
} else {
// We have some data to copy in.
RcvPkt = TrimPacket(RcvPkt, BytesTaken);
ASSERT(RcvPkt->TotalSize != 0);
CopySize = CopyPacketToNdis(
ERB->MdlAddress, RcvPkt,
MIN(Size - BytesTaken,
TCPGetMdlChainByteCount(
ERB->MdlAddress)), 0,
RcvPkt->Position);
}
BytesTaken += CopySize;
RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind,
BytesTaken);
KeReleaseSpinLock(&RcvTCB->tcb_lock,
*pTCBIrql);
ERB->IoStatus.Status = TDI_SUCCESS;
ERB->IoStatus.Information = CopySize;
IoCompleteRequest(ERB, 2);
KeAcquireSpinLock(&RcvTCB->tcb_lock, pTCBIrql);
} else {
// No buffer to deal with.
if (Status == TDI_NOT_ACCEPTED)
BytesTaken = 0;
RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind,
BytesTaken);
}
goto checksize;
} else {
// No receive handler.
KeReleaseSpinLock(&AO->ao_lock, Irql3);
}
}
// Conn->tc_ao == NULL.
KeReleaseSpinLock(&Conn->tc_ConnBlock->cb_lock, Irql2);
KeReleaseSpinLock(&AddrObjTableLock, Irql1);
KeAcquireSpinLock(&RcvTCB->tcb_lock, pTCBIrql);
} else {
// RcvTCB has invalid index.
KeReleaseSpinLock(
&ConnTable[CONN_BLOCKID(RcvTCB->tcb_connid)]->cb_lock,
*pTCBIrql);
KeReleaseSpinLock(&AddrObjTableLock, Irql2);
*pTCBIrql = Irql1;
}
}
//
// For whatever reason we couldn't indicate the data. At this
// point we hold the lock on the TCB. Push the buffer onto the
// pending queue and return.
//
CheckPacketList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
LastPkt = FindLastPacket(RcvPkt);
LastPkt->Next = RcvTCB->tcb_urgpending;
RcvTCB->tcb_urgpending = RcvPkt;
RcvTCB->tcb_urgcnt += Size;
break;
}
checksize:
//
// See how much we took. If we took it all, check the pending queue.
// At this point, we should hold the lock on the TCB.
//
if (Size == BytesTaken) {
// Took it all.
FreePacketChain(RcvPkt);
RcvPkt = RcvTCB->tcb_urgpending;
Size = RcvTCB->tcb_urgcnt;
} else {
//
// We didn't manage to take it all. Free what we did take,
// and then merge with the pending queue.
//
RcvPkt = TrimPacket(RcvPkt, BytesTaken);
Size = Size - BytesTaken + RcvTCB->tcb_urgcnt;
if (RcvTCB->tcb_urgpending != NULL) {
//
// Find the end of the current Packet chain, so we can merge.
//
LastPkt = FindLastPacket(RcvPkt);
LastPkt->Next = RcvTCB->tcb_urgpending;
}
}
RcvTCB->tcb_urgpending = NULL;
RcvTCB->tcb_urgcnt = 0;
} while (RcvPkt != NULL);
CheckPacketList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
RcvTCB->tcb_flags &= ~IN_DELIV_URG;
if (--(RcvTCB->tcb_slowcount) == 0) {
RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
CheckTCBRcv(RcvTCB);
}
}
//* PushData - Push all data back to the client.
//
// Called when we've received a FIN and need to push data to the client.
//
void // Returns: Nothing.
PushData(
TCB *PushTCB) // TCB to be pushed.
{
TCPRcvReq *RcvReq;
CHECK_STRUCT(PushTCB, tcb);
RcvReq = PushTCB->tcb_rcvhead;
while (RcvReq != NULL) {
CHECK_STRUCT(RcvReq, trr);
RcvReq->trr_flags |= TRR_PUSHED;
RcvReq = RcvReq->trr_next;
}
RcvReq = PushTCB->tcb_exprcv;
while (RcvReq != NULL) {
CHECK_STRUCT(RcvReq, trr);
RcvReq->trr_flags |= TRR_PUSHED;
RcvReq = RcvReq->trr_next;
}
if (PushTCB->tcb_rcvhead != NULL || PushTCB->tcb_exprcv != NULL)
DelayAction(PushTCB, NEED_RCV_CMPLT);
}
//* SplitPacket - Split an IPv6Packet into three pieces.
//
// This function takes an input IPv6Packet and splits it into three pieces.
// The first piece is the input buffer, which we just skip over. The second
// and third pieces are actually copied, even if we already own them, so
// that they may go to different places.
//
// Note: *SecondBuf and *ThirdBuf are set to NULL if we can't allocate
// memory for them.
//
void // Returns: Nothing.
SplitPacket(
IPv6Packet *Packet, // Packet chain to be split.
uint Size, // Total size in bytes of packet chain.
uint Offset, // Offset to skip over.
uint SecondSize, // Size in bytes of second piece.
IPv6Packet **SecondPkt, // Where to return second packet pointer.
IPv6Packet **ThirdPkt) // Where to return third packet pointer.
{
IPv6Packet *Temp;
uint ThirdSize;
ASSERT(Offset < Size);
ASSERT(((Offset + SecondSize) < Size) ||
(((Offset + SecondSize) == Size) && ThirdPkt == NULL));
ASSERT(Packet != NULL);
//
// Packet points at the packet to copy from, and Offset is the offset into
// this packet to copy from.
//
if (SecondPkt != NULL) {
//
// We need to allocate memory for a second packet.
//
Temp = ExAllocatePoolWithTagPriority(NonPagedPool,
sizeof(IPv6Packet) + SecondSize,
TCP6_TAG, LowPoolPriority);
if (Temp != NULL) {
Temp->Next = NULL;
Temp->Position = 0;
Temp->FlatData = (uchar *)(Temp + 1);
Temp->Data = Temp->FlatData;
Temp->ContigSize = SecondSize;
Temp->TotalSize = SecondSize;
Temp->NdisPacket = NULL;
Temp->AuxList = NULL;
Temp->Flags = PACKET_OURS;
CopyPacketToBuffer(Temp->Data, Packet, SecondSize,
Packet->Position + Offset);
*SecondPkt = Temp;
} else {
*SecondPkt = NULL;
if (ThirdPkt != NULL)
*ThirdPkt = NULL;
return;
}
}
if (ThirdPkt != NULL) {
//
// We need to allocate memory for a third buffer.
//
ThirdSize = Size - (Offset + SecondSize);
Temp = ExAllocatePoolWithTagPriority(NonPagedPool,
sizeof(IPv6Packet) + ThirdSize,
TCP6_TAG, LowPoolPriority);
if (Temp != NULL) {
Temp->Next = NULL;
Temp->Position = 0;
Temp->FlatData = (uchar *)(Temp + 1);
Temp->Data = Temp->FlatData;
Temp->ContigSize = ThirdSize;
Temp->TotalSize = ThirdSize;
Temp->NdisPacket = NULL;
Temp->AuxList = NULL;
Temp->Flags = PACKET_OURS;
CopyPacketToBuffer(Temp->Data, Packet, ThirdSize,
Packet->Position + Offset + SecondSize);
*ThirdPkt = Temp;
} else
*ThirdPkt = NULL;
}
}
//* HandleUrgent - Handle urgent data.
//
// Called when an incoming segment has urgent data in it. We make sure there
// really is urgent data in the segment, and if there is we try to dispose
// of it either by putting it into a posted buffer or calling an exp. rcv.
// indication handler.
//
// This routine is called at DPC level, and with the TCP locked.
//
// Urgent data handling is a little complicated. Each TCB has the starting
// and ending sequence numbers of the 'current' (last received) bit of urgent
// data. It is possible that the start of the current urgent data might be
// greater than tcb_rcvnext, if urgent data came in, we handled it, and then
// couldn't take the preceding normal data. The urgent valid flag is cleared
// when the next byte of data the user would read (rcvnext - pendingcnt) is
// greater than the end of urgent data - we do this so that we can correctly
// support SIOCATMARK. We always seperate urgent data out of the data stream.
// If the urgent valid field is set when we get into this routing we have
// to play a couple of games. If the incoming segment starts in front of the
// current urgent data, we truncate it before the urgent data, and put any
// data after the urgent data on the reassemble queue. These gyrations are
// done to avoid delivering the same urgent data twice. If the urgent valid
// field in the TCB is set and the segment starts after the current urgent
// data the new urgent information will replace the current urgent
// information.
//
void // Returns: Nothing.
HandleUrgent(
TCB *RcvTCB, // TCB to recv the data on.
TCPRcvInfo *RcvInfo, // RcvInfo structure for the incoming segment.
IPv6Packet *RcvPkt, // Packet chain containing the incoming segment.
uint *Size) // Size in bytes of data in the segment.
{
uint BytesInFront; // Bytes in front of the urgent data.
uint BytesInBack; // Bytes in back of the urgent data.
uint UrgSize; // Size in bytes of urgent data.
SeqNum UrgStart, UrgEnd;
IPv6Packet *EndPkt, *UrgPkt;
TCPRcvInfo NewRcvInfo;
KIRQL TCBIrql;
CHECK_STRUCT(RcvTCB, tcb);
ASSERT(RcvTCB->tcb_refcnt != 0);
ASSERT(RcvInfo->tri_flags & TCP_FLAG_URG);
ASSERT(SEQ_EQ(RcvInfo->tri_seq, RcvTCB->tcb_rcvnext));
// First, validate the urgent pointer.
if (RcvTCB->tcb_flags & BSD_URGENT) {
//
// We're using BSD style urgent data. We assume that the urgent
// data is one byte long, and that the urgent pointer points one
// after the urgent data instead of at the last byte of urgent data.
// See if the urgent data is in this segment.
//
if (RcvInfo->tri_urgent == 0 || RcvInfo->tri_urgent > *Size) {
//
// Not in this segment. Clear the urgent flag and return.
//
RcvInfo->tri_flags &= ~TCP_FLAG_URG;
return;
}
UrgSize = 1;
BytesInFront = RcvInfo->tri_urgent - 1;
} else {
//
// This is not BSD style urgent. We assume that the urgent data
// starts at the front of the segment and the last byte is pointed
// to by the urgent data pointer.
//
BytesInFront = 0;
UrgSize = MIN(RcvInfo->tri_urgent + 1, *Size);
}
BytesInBack = *Size - BytesInFront - UrgSize;
//
// UrgStart and UrgEnd are the first and last sequence numbers of the
// urgent data in this segment.
//
UrgStart = RcvInfo->tri_seq + BytesInFront;
UrgEnd = UrgStart + UrgSize - 1;
if (!(RcvTCB->tcb_flags & URG_INLINE)) {
EndPkt = NULL;
// Now see if this overlaps with any urgent data we've already seen.
if (RcvTCB->tcb_flags & URG_VALID) {
//
// We have some urgent data still around. See if we've advanced
// rcvnext beyond the urgent data. If we have, this is new urgent
// data, and we can go ahead and process it (although anyone doing
// an SIOCATMARK socket command might get confused). If we haven't
// consumed the data in front of the existing urgent data yet,
// we'll truncate this seg. to that amount and push the rest onto
// the reassembly queue. Note that rcvnext should never fall
// between tcb_urgstart and tcb_urgend.
//
ASSERT(SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart) ||
SEQ_GT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgend));
if (SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart)) {
//
// There appears to be some overlap in the data stream.
// Split the buffer up into pieces that come before the current
// urgent data and after the current urgent data, putting the
// latter on the reassembly queue.
//
UrgSize = RcvTCB->tcb_urgend - RcvTCB->tcb_urgstart + 1;
BytesInFront = MIN(RcvTCB->tcb_urgstart - RcvTCB->tcb_rcvnext,
(int) *Size);
if (SEQ_GT(RcvTCB->tcb_rcvnext + *Size, RcvTCB->tcb_urgend)) {
// We have data after this piece of urgent data.
BytesInBack = RcvTCB->tcb_rcvnext + *Size -
RcvTCB->tcb_urgend;
} else
BytesInBack = 0;
SplitPacket(RcvPkt, *Size, BytesInFront, UrgSize, NULL,
(BytesInBack ? &EndPkt : NULL));
if (EndPkt != NULL) {
NewRcvInfo.tri_seq = RcvTCB->tcb_urgend + 1;
if (UrgEnd > RcvTCB->tcb_urgend) {
NewRcvInfo.tri_flags = RcvInfo->tri_flags;
NewRcvInfo.tri_urgent = UrgEnd - NewRcvInfo.tri_seq;
if (RcvTCB->tcb_flags & BSD_URGENT)
NewRcvInfo.tri_urgent++;
} else {
NewRcvInfo.tri_flags = RcvInfo->tri_flags &
~TCP_FLAG_URG;
}
NewRcvInfo.tri_ack = RcvInfo->tri_ack;
NewRcvInfo.tri_window = RcvInfo->tri_window;
PutOnRAQ(RcvTCB, &NewRcvInfo, EndPkt, BytesInBack);
FreePacketChain(EndPkt);
}
*Size = BytesInFront;
RcvInfo->tri_flags &= ~TCP_FLAG_URG;
return;
}
}
//
// We have urgent data we can process now. Split it into its component
// parts, the first part, the urgent data, and the stuff after the
// urgent data.
//
SplitPacket(RcvPkt, *Size, BytesInFront, UrgSize, &UrgPkt,
(BytesInBack ? &EndPkt : NULL));
//
// If we managed to split out the end stuff, put it on the queue now.
//
if (EndPkt != NULL) {
NewRcvInfo.tri_seq = RcvInfo->tri_seq + BytesInFront + UrgSize;
NewRcvInfo.tri_flags = RcvInfo->tri_flags & ~TCP_FLAG_URG;
NewRcvInfo.tri_ack = RcvInfo->tri_ack;
NewRcvInfo.tri_window = RcvInfo->tri_window;
PutOnRAQ(RcvTCB, &NewRcvInfo, EndPkt, BytesInBack);
FreePacketChain(EndPkt);
}
if (UrgPkt != NULL) {
// We succesfully split the urgent data out.
if (!(RcvTCB->tcb_flags & URG_VALID)) {
RcvTCB->tcb_flags |= URG_VALID;
RcvTCB->tcb_slowcount++;
RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
CheckTCBRcv(RcvTCB);
}
RcvTCB->tcb_urgstart = UrgStart;
RcvTCB->tcb_urgend = UrgEnd;
TCBIrql = DISPATCH_LEVEL;
DeliverUrgent(RcvTCB, UrgPkt, UrgSize, &TCBIrql);
}
*Size = BytesInFront;
} else {
//
// Urgent data is to be processed inline. We just need to remember
// where it is and treat it as normal data. If there's already urgent
// data, we remember the latest urgent data.
//
RcvInfo->tri_flags &= ~TCP_FLAG_URG;
if (RcvTCB->tcb_flags & URG_VALID) {
//
// There is urgent data. See if this stuff comes after the
// existing urgent data.
//
if (SEQ_LTE(UrgEnd, RcvTCB->tcb_urgend)) {
//
// The existing urgent data completely overlaps this stuff,
// so ignore this.
//
return;
}
} else {
RcvTCB->tcb_flags |= URG_VALID;
RcvTCB->tcb_slowcount++;
RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
CheckTCBRcv(RcvTCB);
}
RcvTCB->tcb_urgstart = UrgStart;
RcvTCB->tcb_urgend = UrgEnd;
}
return;
}
//* TdiReceive - Process a receive request.
//
// This is the main TDI receive request handler. We validate the connection
// and make sure that we have a TCB in the proper state, then we try to
// allocate a receive request structure. If that succeeds, we'll look and
// see what's happening on the TCB - if there's pending data, we'll put it
// in the buffer. Otherwise we'll just queue the receive for later.
//
TDI_STATUS // Returns: TDI_STATUS of request.
TdiReceive(
PTDI_REQUEST Request, // TDI_REQUEST structure for this request.
ushort *Flags, // Pointer to flags word.
uint *RcvLength, // Pointer to length in bytes of receive buffer.
PNDIS_BUFFER Buffer) // Pointer to buffer to take data.
{
TCPConn *Conn;
TCB *RcvTCB;
TCPRcvReq *RcvReq;
KIRQL Irql0, Irql1; // One per lock nesting level.
TDI_STATUS Error;
ushort UFlags;
Conn = GetConnFromConnID(PtrToUlong(Request->Handle.ConnectionContext),
&Irql0);
if (Conn != NULL) {
CHECK_STRUCT(Conn, tc);
RcvTCB = Conn->tc_tcb;
if (RcvTCB != NULL) {
CHECK_STRUCT(RcvTCB, tcb);
KeAcquireSpinLock(&RcvTCB->tcb_lock, &Irql1);
KeReleaseSpinLock(&Conn->tc_ConnBlock->cb_lock, Irql1);
UFlags = *Flags;
//
// Verify that the cached RCE is still valid.
//
RcvTCB->tcb_rce = ValidateRCE(RcvTCB->tcb_rce);
ASSERT(RcvTCB->tcb_rce != NULL);
//
// Fail new receive requests for TCBs in an invalid state
// and for TCBs with a disconnected outgoing interface
// (except when a loopback route is used).
//
if ((DATA_RCV_STATE(RcvTCB->tcb_state) ||
(RcvTCB->tcb_pendingcnt != 0 && (UFlags & TDI_RECEIVE_NORMAL)) ||
(RcvTCB->tcb_urgcnt != 0 && (UFlags & TDI_RECEIVE_EXPEDITED)))
&& !CLOSING(RcvTCB)
&& !IsDisconnectedAndNotLoopbackRCE(RcvTCB->tcb_rce)) {
//
// We have a TCB, and it's valid. Get a receive request now.
//
CheckPacketList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
RcvReq = GetRcvReq();
if (RcvReq != NULL) {
RcvReq->trr_rtn = Request->RequestNotifyObject;
RcvReq->trr_context = Request->RequestContext;
RcvReq->trr_buffer = Buffer;
RcvReq->trr_size = *RcvLength;
RcvReq->trr_uflags = Flags;
RcvReq->trr_offset = 0;
RcvReq->trr_amt = 0;
RcvReq->trr_flags = (uint)UFlags;
if ((UFlags & (TDI_RECEIVE_NORMAL | TDI_RECEIVE_EXPEDITED))
!= TDI_RECEIVE_EXPEDITED) {
//
// This is not an expedited only receive.
// Put it on the normal receive queue.
//
RcvReq->trr_next = NULL;
if (RcvTCB->tcb_rcvhead == NULL) {
// The receive queue is empty.
// Put it on the front.
RcvTCB->tcb_rcvhead = RcvReq;
RcvTCB->tcb_rcvtail = RcvReq;
} else {
RcvTCB->tcb_rcvtail->trr_next = RcvReq;
RcvTCB->tcb_rcvtail = RcvReq;
}
//
// If this receive is for zero length, complete this
// and indicate pending data again, if any.
//
if (RcvReq->trr_size == 0) {
RcvTCB->tcb_refcnt++;
RcvReq->trr_flags |= TRR_PUSHED;
KeReleaseSpinLock(&RcvTCB->tcb_lock, Irql0);
CompleteRcvs(RcvTCB);
KeAcquireSpinLock(&RcvTCB->tcb_lock, &Irql0);
DerefTCB(RcvTCB, Irql0);
return TDI_PENDING;
}
//
// If this receive can't hold urgent data or there
// isn't any pending urgent data continue processing.
//
if (!(UFlags & TDI_RECEIVE_EXPEDITED) ||
RcvTCB->tcb_urgcnt == 0) {
//
// If tcb_currcv is NULL, there is no currently
// active receive. In this case, check to see if
// there is pending data and that we are not
// currently in a receive indication handler. If
// both of these are true then deal with the
// pending data.
//
if (RcvTCB->tcb_currcv == NULL) {
RcvTCB->tcb_currcv = RcvReq;
// No currently active receive.
if (!(RcvTCB->tcb_flags & IN_RCV_IND)) {
// Not in a rcv. indication.
RcvTCB->tcb_rcvhndlr = BufferData;
if (RcvTCB->tcb_pendhead == NULL) {
KeReleaseSpinLock(&RcvTCB->tcb_lock,
Irql0);
return TDI_PENDING;
} else {
IPv6Packet *PendPacket;
uint PendSize;
uint OldRcvWin;
// We have pending data to deal with.
PendPacket = RcvTCB->tcb_pendhead;
PendSize = RcvTCB->tcb_pendingcnt;
RcvTCB->tcb_pendhead = NULL;
RcvTCB->tcb_pendingcnt = 0;
RcvTCB->tcb_refcnt++;
//
// We assume that BufferData holds
// the lock (does not yield) during
// this call. If this changes for some
// reason, we'll have to fix the code
// below that does the window update
// check. See the comments in the
// BufferData() routine for more info.
//
(void)BufferData(RcvTCB, TCP_FLAG_PUSH,
PendPacket, PendSize);
CheckTCBRcv(RcvTCB);
//
// Now we need to see if the window
// has changed. If it has, send an
// ACK.
//
OldRcvWin = RcvTCB->tcb_rcvwin;
if (OldRcvWin != RcvWin(RcvTCB)) {
// The window has changed, so send
// an ACK.
DelayAction(RcvTCB, NEED_ACK);
}
DerefTCB(RcvTCB, Irql0);
ProcessTCBDelayQ();
return TDI_PENDING;
}
}
//
// In a receive indication. The receive request
// is now on the queue, so just fall through
// to the return.
//
}
//
// A receive is currently active. No need to do
// anything else.
//
KeReleaseSpinLock(&RcvTCB->tcb_lock, Irql0);
return TDI_PENDING;
} else {
//
// This buffer can hold urgent data and we have
// some pending. Deliver it now.
//
RcvTCB->tcb_refcnt++;
DeliverUrgent(RcvTCB, NULL, 0, &Irql0);
DerefTCB(RcvTCB, Irql0);
return TDI_PENDING;
}
} else {
TCPRcvReq *Temp;
//
// This is an expedited only receive. Just put it
// on the end of the expedited receive queue.
//
Temp = CONTAINING_RECORD(&RcvTCB->tcb_exprcv,
TCPRcvReq, trr_next);
while (Temp->trr_next != NULL)
Temp = Temp->trr_next;
RcvReq->trr_next = NULL;
Temp->trr_next = RcvReq;
if (RcvTCB->tcb_urgpending != NULL) {
RcvTCB->tcb_refcnt++;
DeliverUrgent(RcvTCB, NULL, 0, &Irql0);
DerefTCB(RcvTCB, Irql0);
return TDI_PENDING;
} else
Error = TDI_PENDING;
}
} else {
// Couldn't get a receive request.
Error = TDI_NO_RESOURCES;
}
} else {
// The TCB is in an invalid state.
Error = TDI_INVALID_STATE;
}
KeReleaseSpinLock(&RcvTCB->tcb_lock, Irql0);
return Error;
} else {
// No TCB for connection.
KeReleaseSpinLock(&Conn->tc_ConnBlock->cb_lock, Irql0);
Error = TDI_INVALID_STATE;
}
} else {
// No connection.
Error = TDI_INVALID_CONNECTION;
}
return Error;
}