2840 lines
125 KiB
C
2840 lines
125 KiB
C
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
spxrecv.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Nikhil Kamkolkar (nikhilk) 11-November-1993
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
Sanjay Anand (SanjayAn) 5-July-1995
|
|
Bug fixes - tagged [SA]
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
// Define module number for event logging entries
|
|
#define FILENUM SPXRECV
|
|
|
|
BOOLEAN
|
|
SpxReceive(
|
|
IN NDIS_HANDLE MacBindingHandle,
|
|
IN NDIS_HANDLE MacReceiveContext,
|
|
IN ULONG_PTR FwdAdapterCtx,
|
|
IN PIPX_LOCAL_TARGET RemoteAddress,
|
|
IN ULONG MacOptions,
|
|
IN PUCHAR LookaheadBuffer,
|
|
IN UINT LookaheadBufferSize,
|
|
IN UINT LookaheadBufferOffset,
|
|
IN UINT PacketSize,
|
|
IN PMDL pMdl
|
|
)
|
|
|
|
{
|
|
PIPXSPX_HDR pHdr;
|
|
|
|
// We have a separate routine to process SYS packets. DATA packets are
|
|
// processed within this routine.
|
|
if (LookaheadBufferSize < MIN_IPXSPX_HDRSIZE)
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxReceive: Invalid length %lx\n", LookaheadBufferSize));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
++SpxDevice->dev_Stat.PacketsReceived;
|
|
|
|
pHdr = (PIPXSPX_HDR)LookaheadBuffer;
|
|
if ((pHdr->hdr_ConnCtrl & SPX_CC_SYS) == 0)
|
|
{
|
|
// Check for data packets
|
|
if ((pHdr->hdr_DataType != SPX2_DT_ORDREL) &&
|
|
(pHdr->hdr_DataType != SPX2_DT_IDISC) &&
|
|
(pHdr->hdr_DataType != SPX2_DT_IDISC_ACK))
|
|
{
|
|
// HANDLE DATA PACKET
|
|
SpxRecvDataPacket(
|
|
MacBindingHandle,
|
|
MacReceiveContext,
|
|
RemoteAddress,
|
|
MacOptions,
|
|
LookaheadBuffer,
|
|
LookaheadBufferSize,
|
|
LookaheadBufferOffset,
|
|
PacketSize);
|
|
}
|
|
else
|
|
{
|
|
// The whole packet better be in the lookahead, else we ignore.
|
|
if (LookaheadBufferSize == PacketSize)
|
|
{
|
|
SpxRecvDiscPacket(
|
|
LookaheadBuffer,
|
|
RemoteAddress,
|
|
LookaheadBufferSize);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SpxRecvSysPacket(
|
|
MacBindingHandle,
|
|
MacReceiveContext,
|
|
RemoteAddress,
|
|
MacOptions,
|
|
LookaheadBuffer,
|
|
LookaheadBufferSize,
|
|
LookaheadBufferOffset,
|
|
PacketSize);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxTransferDataComplete(
|
|
IN PNDIS_PACKET pNdisPkt,
|
|
IN NDIS_STATUS NdisStatus,
|
|
IN UINT BytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSPX_CONN_FILE pSpxConnFile;
|
|
PREQUEST pRequest;
|
|
PSPX_RECV_RESD pRecvResd;
|
|
CTELockHandle lockHandle;
|
|
NTSTATUS status;
|
|
BOOLEAN fAck, fEom, fBuffered, fImmedAck, fLockHeld;
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxTransferData: For %lx with status %lx\n", pNdisPkt, NdisStatus));
|
|
|
|
pRecvResd = RECV_RESD(pNdisPkt);
|
|
pSpxConnFile = pRecvResd->rr_ConnFile;
|
|
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
fLockHeld = TRUE;
|
|
|
|
fEom = ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0);
|
|
fImmedAck = ((pRecvResd->rr_State & SPX_RECVPKT_IMMEDACK) != 0);
|
|
fBuffered = ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0);
|
|
fAck = ((pRecvResd->rr_State & SPX_RECVPKT_SENDACK) != 0);
|
|
|
|
// Check if receive is done. If we remove the reference for this
|
|
// packet and it goes to zero, that means the receive was aborted.
|
|
// Move to the completion queue.
|
|
// If receive is filled up, then remove the creation reference
|
|
// i.e. just complete the receive at this point.
|
|
// There can be only one packet per receive, we dont support
|
|
// out of order reception.
|
|
|
|
if (!fBuffered)
|
|
{
|
|
// Get pointer to the buffer descriptor and its memory.
|
|
NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer);
|
|
CTEAssert((pNdisBuffer != NULL) || (BytesTransferred == 0));
|
|
|
|
// BUG #11772
|
|
// On MP-machines scf_CurRecvReq could be set to NULL. Get the req
|
|
// from the recv packet.
|
|
// pRequest = pSpxConnFile->scf_CurRecvReq;
|
|
// CTEAssert(pRequest == pRecvResd->rr_Request);
|
|
pRequest = pRecvResd->rr_Request;
|
|
|
|
// Remove reference for this packet.
|
|
--(REQUEST_INFORMATION(pRequest));
|
|
|
|
if (NdisStatus == NDIS_STATUS_SUCCESS)
|
|
{
|
|
pSpxConnFile->scf_CurRecvOffset += BytesTransferred;
|
|
pSpxConnFile->scf_CurRecvSize -= BytesTransferred;
|
|
|
|
#if DBG
|
|
if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0)
|
|
{
|
|
if (BytesTransferred != 0)
|
|
{
|
|
CTEAssert (pSpxConnFile->scf_IndBytes != 0);
|
|
pSpxConnFile->scf_IndBytes -= BytesTransferred;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (REQUEST_INFORMATION(pRequest) == 0)
|
|
{
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n",
|
|
pRequest, REQUEST_INFORMATION(pRequest),
|
|
REQUEST_STATUS(pRequest),
|
|
pSpxConnFile->scf_CurRecvSize));
|
|
|
|
if (SPX_CONN_STREAM(pSpxConnFile) ||
|
|
(pSpxConnFile->scf_CurRecvSize == 0) ||
|
|
fEom ||
|
|
((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) &&
|
|
(REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL)))
|
|
{
|
|
CTELockHandle lockHandleInter;
|
|
|
|
// We are done with this receive.
|
|
REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset;
|
|
|
|
status = STATUS_SUCCESS;
|
|
if (!SPX_CONN_STREAM(pSpxConnFile) &&
|
|
(pSpxConnFile->scf_CurRecvSize == 0) &&
|
|
!fEom)
|
|
{
|
|
status = STATUS_RECEIVE_PARTIAL;
|
|
}
|
|
|
|
if ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) &&
|
|
(REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL))
|
|
{
|
|
status = REQUEST_STATUS(pRequest);
|
|
}
|
|
|
|
REQUEST_STATUS(pRequest) = status;
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n",
|
|
pRequest, REQUEST_INFORMATION(pRequest),
|
|
REQUEST_STATUS(pRequest),
|
|
pSpxConnFile->scf_CurRecvSize));
|
|
|
|
// Dequeue this request, Set next recv if one exists.
|
|
SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest);
|
|
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
|
|
InsertTailList(
|
|
&pSpxConnFile->scf_RecvDoneLinkage,
|
|
REQUEST_LINKAGE(pRequest));
|
|
|
|
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
|
|
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisFreeBuffer(pNdisBuffer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Buffered receive, queue it in if successful.
|
|
// BUG #18363
|
|
// IF WE DISCONNECTED in the meantime, we need to just dump this
|
|
// packet.
|
|
if (SPX_CONN_ACTIVE(pSpxConnFile) &&
|
|
(NdisStatus == NDIS_STATUS_SUCCESS))
|
|
{
|
|
// Queue packet in connection. Reference connection for this.
|
|
SpxConnQueueRecvPktTail(pSpxConnFile, pNdisPkt);
|
|
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxTransferData: Buffering: %lx Pkt %lx Size %lx F %lx\n",
|
|
pSpxConnFile, pNdisPkt, BytesTransferred, pRecvResd->rr_State));
|
|
|
|
// There could either be queued receives. (This could happen in
|
|
// a partial receive case. Or if a receive got queued in while we
|
|
// were processing this packet (Possible on MP)), or a packet was
|
|
// buffered while we were completing some receives
|
|
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead);
|
|
|
|
if ((pSpxConnFile->scf_CurRecvReq != NULL) ||
|
|
((pSpxConnFile->scf_RecvListHead->rr_State &
|
|
SPX_RECVPKT_INDICATED) == 0))
|
|
{
|
|
CTELockHandle interLockHandle;
|
|
|
|
// Push this connection into a ProcessRecv queue which will be
|
|
// dealt with in receive completion.
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxRecvTransferData: Queueing for recvp %lx.%lx\n",
|
|
pSpxConnFile, pSpxConnFile->scf_Flags));
|
|
|
|
// Get the global q lock, push into recv list.
|
|
CTEGetLock(&SpxGlobalQInterlock, &interLockHandle);
|
|
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
|
|
CTEFreeLock(&SpxGlobalQInterlock, interLockHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PBYTE pData;
|
|
ULONG dataLen;
|
|
|
|
// Get pointer to the buffer descriptor and its memory.
|
|
NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer);
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisQueryBuffer(pNdisBuffer, &pData, &dataLen);
|
|
CTEAssert(pData != NULL);
|
|
CTEAssert((LONG)dataLen >= 0);
|
|
|
|
// Free the data, ndis buffer.
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisFreeBuffer(pNdisBuffer);
|
|
}
|
|
SpxFreeMemory(pData);
|
|
}
|
|
|
|
// Dont send ack, set status to be failure so we free packet/buffer.
|
|
fAck = FALSE;
|
|
NdisStatus = NDIS_STATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
END_PROCESS_PACKET(
|
|
pSpxConnFile, fBuffered, (NdisStatus == NDIS_STATUS_SUCCESS));
|
|
|
|
if (fAck)
|
|
{
|
|
// Rem ack addr should have been copied in receive.
|
|
|
|
// #17564
|
|
if (fImmedAck ||
|
|
SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) ||
|
|
SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK))
|
|
{
|
|
SpxConnSendAck(pSpxConnFile, lockHandle);
|
|
fLockHeld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
SpxConnQWaitAck(pSpxConnFile);
|
|
}
|
|
}
|
|
|
|
if (fLockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
}
|
|
|
|
if (!fBuffered || (NdisStatus != STATUS_SUCCESS))
|
|
{
|
|
// Free the ndis packet/buffer
|
|
SpxPktRecvRelease(pNdisPkt);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxReceiveComplete(
|
|
IN USHORT NicId
|
|
)
|
|
|
|
{
|
|
CTELockHandle lockHandleInter, lockHandle;
|
|
PREQUEST pRequest;
|
|
BOOLEAN fConnLockHeld, fInterlockHeld;
|
|
PSPX_CONN_FILE pSpxConnFile;
|
|
int numDerefs = 0;
|
|
|
|
// See if any connections need recv processing. This will also take
|
|
// care of any acks opening up window so our sends go to the max.
|
|
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
|
|
fInterlockHeld = TRUE;
|
|
|
|
while ((pSpxConnFile = SpxRecvConnList.pcl_Head) != NULL)
|
|
{
|
|
// Reset for each connection
|
|
numDerefs = 0;
|
|
|
|
if ((SpxRecvConnList.pcl_Head = pSpxConnFile->scf_ProcessRecvNext) == NULL)
|
|
SpxRecvConnList.pcl_Tail = NULL;
|
|
|
|
// Reset next field to NULL
|
|
pSpxConnFile->scf_ProcessRecvNext = NULL;
|
|
|
|
DBGPRINT(SEND, DBG,
|
|
("SpxConnRemoveFromRecv: %lx\n", pSpxConnFile));
|
|
|
|
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
|
|
do
|
|
{
|
|
// Complete pending requests.
|
|
while (!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage))
|
|
{
|
|
pRequest =
|
|
LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqDoneLinkage.Flink);
|
|
|
|
RemoveEntryList(REQUEST_LINKAGE(pRequest));
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
|
|
DBGPRINT(TDI, DBG,
|
|
("SpxReceiveComplete: Completing %lx with %lx.%lx\n",
|
|
pRequest, REQUEST_STATUS(pRequest),
|
|
REQUEST_INFORMATION(pRequest)));
|
|
|
|
CTEAssert (REQUEST_MINOR_FUNCTION(pRequest) != TDI_RECEIVE);
|
|
SpxCompleteRequest(pRequest);
|
|
numDerefs++;
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
}
|
|
|
|
// Call process pkts if we have any packets or if any receives to
|
|
// complete. Note this will call even when there are no receives
|
|
// queued and the first packet has already been indicated.
|
|
if ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) &&
|
|
(!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage) ||
|
|
(pSpxConnFile->scf_RecvListHead != NULL)))
|
|
{
|
|
// We have the flag reference on the connection.
|
|
SpxRecvProcessPkts(pSpxConnFile, lockHandle);
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
}
|
|
|
|
#if DBG
|
|
if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage))
|
|
{
|
|
DBGPRINT(TDI, DBG,
|
|
("SpxReceiveComplete: RecvDone left %lx\n",
|
|
pSpxConnFile));
|
|
}
|
|
#endif
|
|
|
|
// Hmm. This check is rather expensive, and essentially we are doing
|
|
// it twice. Should look to see if this can be modified safely.
|
|
} while ((!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) ||
|
|
((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) &&
|
|
((!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) ||
|
|
((pSpxConnFile->scf_RecvListHead != NULL) &&
|
|
((pSpxConnFile->scf_RecvListHead->rr_State &
|
|
(SPX_RECVPKT_BUFFERING | SPX_RECVPKT_INDICATED)) ==
|
|
SPX_RECVPKT_BUFFERING)))));
|
|
|
|
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RECVQ);
|
|
SpxConnFileTransferReference(
|
|
pSpxConnFile,
|
|
CFREF_RECV,
|
|
CFREF_VERIFY);
|
|
|
|
numDerefs++;
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
|
|
while (numDerefs-- > 0)
|
|
{
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
|
|
}
|
|
|
|
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
|
|
}
|
|
|
|
|
|
// First see if we need to packetize.
|
|
while ((pSpxConnFile = SpxPktConnList.pcl_Head) != NULL)
|
|
{
|
|
if ((SpxPktConnList.pcl_Head = pSpxConnFile->scf_PktNext) == NULL)
|
|
SpxPktConnList.pcl_Tail = NULL;
|
|
|
|
// Reset next field to NULL
|
|
pSpxConnFile->scf_PktNext = NULL;
|
|
|
|
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
|
|
|
|
DBGPRINT(SEND, DBG,
|
|
("SpxConnRemoveFromPkt: %lx\n", pSpxConnFile));
|
|
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
fConnLockHeld = TRUE;
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxReceiveComplete: Packetizing %lx\n", pSpxConnFile));
|
|
|
|
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ);
|
|
if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE)
|
|
{
|
|
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE);
|
|
|
|
// 262691 SpxConnPacketize always frees the lock.
|
|
SpxConnPacketize(
|
|
pSpxConnFile,
|
|
TRUE,
|
|
lockHandle);
|
|
fConnLockHeld = FALSE;
|
|
}
|
|
|
|
if (fConnLockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
}
|
|
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_PKTIZE);
|
|
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
|
|
}
|
|
|
|
if (fInterlockHeld)
|
|
{
|
|
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// PACKET HANDLING ROUTINES
|
|
//
|
|
|
|
|
|
VOID
|
|
SpxRecvSysPacket(
|
|
IN NDIS_HANDLE MacBindingHandle,
|
|
IN NDIS_HANDLE MacReceiveContext,
|
|
IN PIPX_LOCAL_TARGET pRemoteAddr,
|
|
IN ULONG MacOptions,
|
|
IN PUCHAR LookaheadBuffer,
|
|
IN UINT LookaheadBufferSize,
|
|
IN UINT LookaheadBufferOffset,
|
|
IN UINT PacketSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called to indicate an incoming system packet.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIPXSPX_HDR pHdr;
|
|
USHORT srcConnId, destConnId,
|
|
pktLen, ackNum, allocNum;
|
|
PSPX_CONN_FILE pSpxConnFile;
|
|
CTELockHandle lockHandle;
|
|
BOOLEAN lockHeld = FALSE;
|
|
|
|
pHdr = (PIPXSPX_HDR)LookaheadBuffer;
|
|
|
|
// check minimum length
|
|
if (PacketSize < MIN_IPXSPX_HDRSIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Convert hdr to host format as needed.
|
|
GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen);
|
|
GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId);
|
|
|
|
if ((pktLen < MIN_IPXSPX_HDRSIZE) ||
|
|
(pktLen > PacketSize) ||
|
|
(pHdr->hdr_PktType != SPX_PKT_TYPE))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxRecvSysPacket: Packet Size %lx.%lx\n",
|
|
pktLen, PacketSize));
|
|
|
|
return;
|
|
}
|
|
|
|
if ((pktLen == SPX_CR_PKTLEN) &&
|
|
(destConnId == 0xFFFF) &&
|
|
(pHdr->hdr_ConnCtrl & SPX_CC_CR))
|
|
{
|
|
spxConnHandleConnReq(
|
|
pHdr,
|
|
pRemoteAddr);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// [SA] Bug #14917
|
|
// Some SPX SYS packets (no extended ack field) may come in with the SPX2 bit set.
|
|
// Make sure we don't discard these packets.
|
|
//
|
|
|
|
// if ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && (pktLen < MIN_IPXSPX2_HDRSIZE))
|
|
// {
|
|
// return;
|
|
// }
|
|
|
|
GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum);
|
|
GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum);
|
|
|
|
// We keep and use the remote id in the net format. This maintains the
|
|
// 0x0 and 0xFFFF to be as in the host format.
|
|
srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId;
|
|
|
|
if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxConnSysPacket: Incorrect conn id %lx.%lx\n",
|
|
srcConnId, destConnId));
|
|
|
|
return;
|
|
}
|
|
|
|
DBGPRINT(CONNECT, DBG,
|
|
("SpxConnSysPacket: packet received dest %lx src %lx\n",
|
|
pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt));
|
|
|
|
// Find the connection this is destined for and reference it.
|
|
SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DBGPRINT(RECEIVE, WARN,
|
|
("SpxConnSysPacket: Id %lx NOT FOUND\n", destConnId));
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
|
|
DBGPRINT(RECEIVE, INFO,
|
|
("SpxConnSysPacket: Id %lx Conn %lx\n",
|
|
destConnId, pSpxConnFile));
|
|
|
|
// This could be one of many packets. Connection ack/Session negotiate/
|
|
// Session setup, Data Ack, Probe/Ack, Renegotiate/Ack. We shunt
|
|
// off all the packets to different routines but process the data
|
|
// ack packets here.
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
//
|
|
// We have the connection. We should update the dest. sock # in
|
|
// it in case it changed. Unix machines do do that sometimes.
|
|
// SCO bug 7676
|
|
//
|
|
SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAddr);
|
|
|
|
lockHeld = TRUE;
|
|
|
|
// Restart watchdog timer if started.
|
|
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER))
|
|
{
|
|
// This will either successfully restart or not affect the timer
|
|
// if it is currently running.
|
|
SpxTimerCancelEvent(
|
|
pSpxConnFile->scf_WTimerId,
|
|
TRUE);
|
|
|
|
pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT);
|
|
}
|
|
|
|
switch (SPX_MAIN_STATE(pSpxConnFile))
|
|
{
|
|
case SPX_CONNFILE_CONNECTING:
|
|
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
lockHeld = FALSE;
|
|
|
|
spxConnHandleSessPktFromSrv(
|
|
pHdr,
|
|
pRemoteAddr,
|
|
pSpxConnFile);
|
|
|
|
break;
|
|
|
|
case SPX_CONNFILE_LISTENING:
|
|
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
lockHeld = FALSE;
|
|
|
|
spxConnHandleSessPktFromClient(
|
|
pHdr,
|
|
pRemoteAddr,
|
|
pSpxConnFile);
|
|
|
|
break;
|
|
|
|
case SPX_CONNFILE_ACTIVE:
|
|
case SPX_CONNFILE_DISCONN:
|
|
|
|
// NOTE: Our ack to a session setup might get dropped.
|
|
// But the SS Ack is similar to a normal SPX2 ack.
|
|
// We dont have to do anything special.
|
|
|
|
// Received ack/nack/reneg/reneg ack/disc associated packet.
|
|
// Disc packets except ordrel ack have non-zero datastream type.
|
|
if ((pHdr->hdr_ConnCtrl &
|
|
(SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) ==
|
|
(SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2))
|
|
{
|
|
// We received a renegotiate packet. Ignore all ack values
|
|
// in a reneg req.
|
|
SpxConnProcessRenegReq(pSpxConnFile, pHdr, pRemoteAddr, lockHandle);
|
|
lockHeld = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Set ack numbers for connection.
|
|
SPX_SET_ACKNUM(
|
|
pSpxConnFile, ackNum, allocNum);
|
|
|
|
// Check if we are an ack/nack packet in which case call process
|
|
// ack. Note that the spx2 orderly release ack is a normal spx2 ack.
|
|
if (((pHdr->hdr_ConnCtrl & SPX_CC_ACK) == 0) &&
|
|
(pHdr->hdr_DataType == 0))
|
|
{
|
|
SpxConnProcessAck(pSpxConnFile, pHdr, lockHandle);
|
|
lockHeld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Just process the numbers we got.
|
|
SpxConnProcessAck(pSpxConnFile, NULL, lockHandle);
|
|
lockHeld = FALSE;
|
|
}
|
|
|
|
// If the remote wants us to send an ack, do it.
|
|
if (pHdr->hdr_ConnCtrl & SPX_CC_ACK)
|
|
{
|
|
// First copy the remote address in connection.
|
|
SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr;
|
|
|
|
if (!lockHeld)
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
lockHeld = TRUE;
|
|
}
|
|
|
|
SpxConnSendAck(pSpxConnFile, lockHandle);
|
|
lockHeld = FALSE;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Ignore this packet.
|
|
DBGPRINT(RECEIVE, WARN,
|
|
("SpxConnSysPacket: Ignoring packet, state is not active\n"));
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (lockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
}
|
|
|
|
// Remove reference added on connection
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_BYID);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxRecvDiscPacket(
|
|
IN PUCHAR LookaheadBuffer,
|
|
IN PIPX_LOCAL_TARGET pRemoteAddr,
|
|
IN UINT LookaheadSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called to indicate an incoming connection.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PIPXSPX_HDR pHdr;
|
|
USHORT srcConnId, destConnId,
|
|
pktLen, seqNum, ackNum, allocNum;
|
|
PSPX_CONN_FILE pSpxConnFile;
|
|
CTELockHandle lockHandle;
|
|
BOOLEAN lockHeld;
|
|
|
|
pHdr = (PIPXSPX_HDR)LookaheadBuffer;
|
|
|
|
// check minimum length
|
|
if (LookaheadSize < MIN_IPXSPX_HDRSIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Convert hdr to host format as needed.
|
|
GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen);
|
|
GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId);
|
|
GETSHORT2SHORT(&seqNum, &pHdr->hdr_SeqNum);
|
|
GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum);
|
|
GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum);
|
|
|
|
if ((pktLen < MIN_IPXSPX_HDRSIZE) ||
|
|
(pHdr->hdr_PktType != SPX_PKT_TYPE))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxRecvDiscPacket: Packet Size %lx\n",
|
|
pktLen));
|
|
|
|
return;
|
|
}
|
|
|
|
// We keep and use the remote id in the net format. This maintains the
|
|
// 0x0 and 0xFFFF to be as in the host format.
|
|
srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId;
|
|
if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxConnDiscPacket: Incorrect conn id %lx.%lx\n",
|
|
srcConnId, destConnId));
|
|
|
|
return;
|
|
}
|
|
|
|
DBGPRINT(CONNECT, DBG,
|
|
("SpxConnDiscPacket: packet received dest %lx src %lx\n",
|
|
pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt));
|
|
|
|
// Find the connection this is destined for and reference it.
|
|
SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DBGPRINT(RECEIVE, WARN,
|
|
("SpxConnDiscPacket: Id %lx NOT FOUND", destConnId));
|
|
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
DBGPRINT(RECEIVE, INFO,
|
|
("SpxConnDiscPacket: Id %lx Conn %lx DiscType %lx\n",
|
|
destConnId, pSpxConnFile, pHdr->hdr_DataType));
|
|
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
lockHeld = TRUE;
|
|
|
|
// Unless we are in the active/disconnecting, but send state = idle
|
|
// and recv state = idle/recv posted, we ignore all disconnect packets.
|
|
if (((SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_ACTIVE) &&
|
|
(SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_DISCONN)) ||
|
|
((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) &&
|
|
(SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) ||
|
|
((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_IDLE) &&
|
|
(SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_POSTED)) ||
|
|
!(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) ||
|
|
(SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)))
|
|
{
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDiscPacket: %lx, %lx, %lx.%lx, %d.%d\n",
|
|
pSpxConnFile,
|
|
SPX_MAIN_STATE(pSpxConnFile),
|
|
SPX_SEND_STATE(pSpxConnFile), SPX_RECV_STATE(pSpxConnFile),
|
|
(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)),
|
|
(SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT))));
|
|
|
|
break;
|
|
}
|
|
|
|
// If we have received a disconnect, process received ack to complete any
|
|
// pending sends before we allow the disconnect. This ack number will be
|
|
// the last word on this session.
|
|
SPX_SET_ACKNUM(
|
|
pSpxConnFile, ackNum, allocNum);
|
|
|
|
SpxConnProcessAck(pSpxConnFile, NULL, lockHandle);
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
|
|
switch (pHdr->hdr_DataType)
|
|
{
|
|
case SPX2_DT_ORDREL:
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDiscPacket: Recd ORDREl!\n"));
|
|
|
|
// Need to deal with all sthe states.
|
|
// Restart watchdog timer if started.
|
|
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER))
|
|
{
|
|
// This will either successfully restart or not affect the timer
|
|
// if it is currently running.
|
|
SpxTimerCancelEvent(
|
|
pSpxConnFile->scf_WTimerId,
|
|
TRUE);
|
|
|
|
pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT);
|
|
}
|
|
|
|
// On receive, we do check the seq num for the orderly release, just
|
|
// like for a data packet.
|
|
// If this was not already indicated, indicate it now. That is all
|
|
// we do for an orderly release. When our client does a orderly rel
|
|
// and we receive the ack for that, call abortive with success.
|
|
|
|
// Verify ord rel packet, this checks if seq nums match also.
|
|
if ((pktLen != MIN_IPXSPX2_HDRSIZE) ||
|
|
((pHdr->hdr_ConnCtrl &
|
|
(SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) !=
|
|
(SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) ||
|
|
(pHdr->hdr_DataType != SPX2_DT_ORDREL) ||
|
|
(srcConnId == 0) ||
|
|
(srcConnId == 0xFFFF) ||
|
|
(srcConnId != pSpxConnFile->scf_RemConnId) ||
|
|
(destConnId == 0) ||
|
|
(destConnId == 0xFFFF) ||
|
|
(destConnId != pSpxConnFile->scf_LocalConnId))
|
|
{
|
|
DBGPRINT(CONNECT, DBG1,
|
|
("SpxConnDiscPacket: OR Failed/Ignored %lx, %lx.%lx.%lx\n",
|
|
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum,
|
|
pSpxConnFile->scf_RecvListTail));
|
|
|
|
break;
|
|
}
|
|
|
|
// If it passed above test, but seq number is incorrect, schedule
|
|
// to send an ack.
|
|
if (seqNum != pSpxConnFile->scf_RecvSeqNum)
|
|
{
|
|
USHORT NumToResend;
|
|
|
|
DBGPRINT(CONNECT, DBG,
|
|
("SpxConnDiscPacket: Unexpected seq on %lx, %lx.%lx\n",
|
|
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum));
|
|
|
|
// Calculate number to be resent. If we expect sequence 1 and receive
|
|
// 2 for eg., we need to send a nack, else we send an ack.
|
|
if (SPX2_CONN(pSpxConnFile) &&
|
|
UNSIGNED_GREATER_WITH_WRAP(
|
|
seqNum,
|
|
pSpxConnFile->scf_RecvSeqNum) &&
|
|
!UNSIGNED_GREATER_WITH_WRAP(
|
|
seqNum,
|
|
pSpxConnFile->scf_SentAllocNum))
|
|
{
|
|
NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1);
|
|
SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle);
|
|
lockHeld = FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Copy address for when ack is to be sent.
|
|
SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr;
|
|
|
|
if (pSpxConnFile->scf_RecvListHead == NULL)
|
|
{
|
|
// No received data, go ahead and process now.
|
|
DBGPRINT(CONNECT, INFO,
|
|
("SpxConnDiscPacket: NO DATA ORDREL %lx.%lx.%lx\n",
|
|
pSpxConnFile,
|
|
pSpxConnFile->scf_RecvListHead,
|
|
pSpxConnFile->scf_SendSeqListHead));
|
|
|
|
SpxConnProcessOrdRel(pSpxConnFile, lockHandle);
|
|
lockHeld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// No received data, go ahead and process now.
|
|
DBGPRINT(CONNECT, DBG1,
|
|
("SpxConnDiscPacket: DATA ORDREL %lx.%lx.%lx\n",
|
|
pSpxConnFile,
|
|
pSpxConnFile->scf_RecvListHead,
|
|
pSpxConnFile->scf_SendSeqListHead));
|
|
|
|
// Set flag in last recd buffer
|
|
pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_ORD_DISC;
|
|
}
|
|
|
|
break;
|
|
|
|
case SPX2_DT_IDISC:
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDiscPacket: %lx Recd IDISC %lx!\n",
|
|
pSpxConnFile, pSpxConnFile->scf_RefCount));
|
|
|
|
DBGPRINT(RECEIVE, INFO,
|
|
("SpxConnDiscPacket: SEND %d. RECV %d.%lx!\n",
|
|
IsListEmpty(&pSpxConnFile->scf_ReqLinkage),
|
|
IsListEmpty(&pSpxConnFile->scf_RecvLinkage),
|
|
pSpxConnFile->scf_RecvDoneLinkage));
|
|
|
|
if (!((pktLen == MIN_IPXSPX_HDRSIZE) ||
|
|
((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) &&
|
|
(pktLen == MIN_IPXSPX2_HDRSIZE))) ||
|
|
!(pHdr->hdr_ConnCtrl & SPX_CC_ACK) ||
|
|
(pHdr->hdr_DataType != SPX2_DT_IDISC) ||
|
|
(srcConnId == 0) ||
|
|
(srcConnId == 0xFFFF) ||
|
|
(srcConnId != pSpxConnFile->scf_RemConnId) ||
|
|
(destConnId == 0) ||
|
|
(destConnId == 0xFFFF) ||
|
|
(destConnId != pSpxConnFile->scf_LocalConnId))
|
|
{
|
|
DBGPRINT(CONNECT, ERR,
|
|
("SpxConnDiscPacket:IDISC Ignored %lx.%lx.%lx.%lx\n",
|
|
pSpxConnFile, seqNum,
|
|
pSpxConnFile->scf_RecvSeqNum,
|
|
pSpxConnFile->scf_RecvListTail));
|
|
break;
|
|
}
|
|
|
|
// Copy address for when ack is to be sent.
|
|
SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr;
|
|
|
|
if (pSpxConnFile->scf_RecvListHead == NULL)
|
|
{
|
|
// No received data, go ahead and process now.
|
|
DBGPRINT(CONNECT, INFO,
|
|
("SpxConnDiscPacket: NO RECV DATA IDISC %lx.%lx.%lx\n",
|
|
pSpxConnFile,
|
|
pSpxConnFile->scf_RecvListHead,
|
|
pSpxConnFile->scf_SendSeqListHead));
|
|
|
|
SpxConnProcessIDisc(pSpxConnFile, lockHandle);
|
|
|
|
lockHeld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Set flag in last recd buffer
|
|
|
|
pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_IDISC;
|
|
}
|
|
|
|
break;
|
|
|
|
case SPX2_DT_IDISC_ACK:
|
|
|
|
// Done with informed disconnect. Call abort connection with
|
|
// status success. That completes the pending disconnect request
|
|
// with status_success.
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDiscPacket: %lx Recd IDISC ack!\n", pSpxConnFile));
|
|
|
|
if (!((pktLen == MIN_IPXSPX_HDRSIZE) ||
|
|
((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) &&
|
|
(pktLen == MIN_IPXSPX2_HDRSIZE))) ||
|
|
(pHdr->hdr_DataType != SPX2_DT_IDISC_ACK) ||
|
|
(srcConnId == 0) ||
|
|
(srcConnId == 0xFFFF) ||
|
|
(srcConnId != pSpxConnFile->scf_RemConnId) ||
|
|
(destConnId == 0) ||
|
|
(destConnId == 0xFFFF) ||
|
|
(destConnId != pSpxConnFile->scf_LocalConnId))
|
|
{
|
|
DBGPRINT(CONNECT, ERR,
|
|
("SpxConnDiscPacket:Ver idisc ack Failed %lx, %lx.%lx\n",
|
|
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum));
|
|
break;
|
|
}
|
|
|
|
// We should be in the right state to accept this.
|
|
if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) &&
|
|
(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_IDISC))
|
|
{
|
|
spxConnAbortiveDisc(
|
|
pSpxConnFile,
|
|
STATUS_SUCCESS,
|
|
SPX_CALL_RECVLEVEL,
|
|
lockHandle,
|
|
FALSE); // [SA] bug #15249
|
|
|
|
lockHeld = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
|
|
} while (FALSE);
|
|
|
|
if (lockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
}
|
|
|
|
// Remove reference added on connection
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_BYID);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxRecvBufferPkt(
|
|
IN PSPX_CONN_FILE pSpxConnFile,
|
|
IN NDIS_HANDLE MacBindingHandle,
|
|
IN NDIS_HANDLE MacReceiveContext,
|
|
IN UINT LookaheadOffset,
|
|
IN PIPXSPX_HDR pIpxSpxHdr,
|
|
IN UINT PacketSize,
|
|
IN PIPX_LOCAL_TARGET pRemoteAddr,
|
|
IN CTELockHandle LockHandleConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called to indicate an incoming connection.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PNDIS_PACKET pNdisPkt;
|
|
PSPX_RECV_RESD pRecvResd;
|
|
ULONG bytesCopied;
|
|
BOOLEAN fEom;
|
|
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
|
|
PBYTE pData = NULL;
|
|
PNDIS_BUFFER pNdisBuffer = NULL;
|
|
|
|
if (PacketSize > 0)
|
|
{
|
|
// Allocate memory for this data.
|
|
if (pData = (PBYTE)SpxAllocateMemory(PacketSize))
|
|
{
|
|
// Describe memory with a ndis buffer descriptor.
|
|
NdisAllocateBuffer(
|
|
&ndisStatus,
|
|
&pNdisBuffer,
|
|
SpxDevice->dev_NdisBufferPoolHandle,
|
|
pData,
|
|
PacketSize);
|
|
}
|
|
else
|
|
{
|
|
ndisStatus = NDIS_STATUS_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (ndisStatus == NDIS_STATUS_SUCCESS)
|
|
{
|
|
// Allocate a ndis receive packet.
|
|
SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus);
|
|
if (ndisStatus == NDIS_STATUS_SUCCESS)
|
|
{
|
|
// Queue the buffer into the packet if there is one.
|
|
if (pNdisBuffer)
|
|
{
|
|
NdisChainBufferAtBack(
|
|
pNdisPkt,
|
|
pNdisBuffer);
|
|
}
|
|
|
|
fEom = ((SPX_CONN_MSG(pSpxConnFile) &&
|
|
(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) ||
|
|
SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR));
|
|
|
|
pRecvResd = RECV_RESD(pNdisPkt);
|
|
pRecvResd->rr_DataOffset= 0;
|
|
|
|
#if DBG
|
|
// Store seq number
|
|
GETSHORT2SHORT(&pRecvResd->rr_SeqNum , &pIpxSpxHdr->hdr_SeqNum);
|
|
#endif
|
|
|
|
pRecvResd->rr_State =
|
|
(SPX_RECVPKT_BUFFERING |
|
|
(SPX_CONN_FLAG2(
|
|
pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) |
|
|
(fEom ? SPX_RECVPKT_EOM : 0) |
|
|
((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? SPX_RECVPKT_SENDACK : 0));
|
|
|
|
if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK)
|
|
{
|
|
// copy the remote address in connection.
|
|
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr;
|
|
}
|
|
|
|
pRecvResd->rr_Request = NULL;
|
|
pRecvResd->rr_ConnFile = pSpxConnFile;
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxRecvBufferPkt: %lx Len %lx DataPts %lx F %lx\n",
|
|
pSpxConnFile, PacketSize, pData, pRecvResd->rr_State));
|
|
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
|
|
// Call ndis transfer data. Copy ENTIRE packet. copySize has
|
|
// been modified so use original values.
|
|
ndisStatus = NDIS_STATUS_SUCCESS;
|
|
bytesCopied = 0;
|
|
if (PacketSize > 0)
|
|
{
|
|
(*IpxTransferData)(
|
|
&ndisStatus,
|
|
MacBindingHandle,
|
|
MacReceiveContext,
|
|
LookaheadOffset,
|
|
PacketSize,
|
|
pNdisPkt,
|
|
&bytesCopied);
|
|
}
|
|
|
|
if (ndisStatus != STATUS_PENDING)
|
|
{
|
|
SpxTransferDataComplete(
|
|
pNdisPkt,
|
|
ndisStatus,
|
|
bytesCopied);
|
|
}
|
|
|
|
// BUG: FDDI returns pending which messes us up here.
|
|
ndisStatus = NDIS_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// ASSERT: Lock will be freed in the success case.
|
|
if (ndisStatus != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxRecvBufferPkt: FAILED!\n"));
|
|
|
|
END_PROCESS_PACKET(pSpxConnFile, FALSE, FALSE);
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
|
|
if (pData != NULL)
|
|
{
|
|
SpxFreeMemory(pData);
|
|
}
|
|
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisFreeBuffer(pNdisBuffer);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxRecvDataPacket(
|
|
IN NDIS_HANDLE MacBindingHandle,
|
|
IN NDIS_HANDLE MacReceiveContext,
|
|
IN PIPX_LOCAL_TARGET RemoteAddress,
|
|
IN ULONG MacOptions,
|
|
IN PUCHAR LookaheadBuffer,
|
|
IN UINT LookaheadSize,
|
|
IN UINT LookaheadOffset,
|
|
IN UINT PacketSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called to indicate an incoming connection.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIPXSPX_HDR pIpxSpxHdr;
|
|
USHORT srcConnId, destConnId,
|
|
pktLen, seqNum, ackNum, allocNum;
|
|
ULONG receiveFlags;
|
|
PSPX_CONN_FILE pSpxConnFile;
|
|
PTDI_IND_RECEIVE pRecvHandler;
|
|
PVOID pRecvCtx;
|
|
PIRP pRecvIrp;
|
|
ULONG bytesTaken, iOffset, copySize, bytesCopied;
|
|
CTELockHandle lockHandle;
|
|
PNDIS_PACKET pNdisPkt;
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
PSPX_RECV_RESD pRecvResd;
|
|
NDIS_STATUS ndisStatus;
|
|
PREQUEST pRequest = NULL;
|
|
BOOLEAN fEom,
|
|
fImmedAck = FALSE, fLockHeld = FALSE, fPktDone = FALSE;
|
|
|
|
pIpxSpxHdr = (PIPXSPX_HDR)LookaheadBuffer;
|
|
|
|
// check minimum length
|
|
if (PacketSize < MIN_IPXSPX_HDRSIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Convert hdr to host format as needed.
|
|
GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen);
|
|
GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId);
|
|
GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum);
|
|
GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum);
|
|
GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum);
|
|
|
|
if ((pktLen < MIN_IPXSPX_HDRSIZE) ||
|
|
(pktLen > PacketSize) ||
|
|
(pIpxSpxHdr->hdr_PktType != SPX_PKT_TYPE))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxConnDataPacket: Packet Size %lx.%lx\n",
|
|
pktLen, PacketSize));
|
|
|
|
return;
|
|
}
|
|
|
|
// We keep and use the remote id in the net format.
|
|
srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId;
|
|
|
|
if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxConnDataPacket: Incorrect conn id %lx.%lx\n",
|
|
srcConnId, destConnId));
|
|
|
|
return;
|
|
}
|
|
|
|
DBGPRINT(CONNECT, DBG,
|
|
("SpxConnDataPacket: packet received dest %lx src %lx seq %lx\n",
|
|
pIpxSpxHdr->hdr_DestSkt, pIpxSpxHdr->hdr_SrcSkt, seqNum));
|
|
|
|
if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) &&
|
|
(pktLen < MIN_IPXSPX2_HDRSIZE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Find the connection this is destined for and reference it.
|
|
SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DBGPRINT(RECEIVE, WARN,
|
|
("SpxConnDataPacket: Id %lx NOT FOUND", destConnId));
|
|
return;
|
|
}
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
|
|
#if 0
|
|
//
|
|
// We have the connection. We should update the dest. sock # in
|
|
// it in case it changed. Unix machines do do that sometimes.
|
|
// SCO bug 7676
|
|
//
|
|
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr);
|
|
#endif
|
|
|
|
fLockHeld = TRUE;
|
|
do
|
|
{
|
|
DBGPRINT(RECEIVE, INFO,
|
|
("SpxConnDataPacket: Id %lx Conn %lx\n",
|
|
destConnId, pSpxConnFile));
|
|
|
|
// Restart watchdog timer if started.
|
|
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER))
|
|
{
|
|
// This will either successfully restart or not affect the timer
|
|
// if it is currently running.
|
|
SpxTimerCancelEvent(
|
|
pSpxConnFile->scf_WTimerId,
|
|
TRUE);
|
|
|
|
pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT);
|
|
}
|
|
|
|
if (SPX_CONN_ACTIVE(pSpxConnFile))
|
|
{
|
|
// Verify data packet, this checks if seq nums match also.
|
|
if ((pIpxSpxHdr->hdr_SrcConnId != pSpxConnFile->scf_RemConnId) ||
|
|
(destConnId != pSpxConnFile->scf_LocalConnId) ||
|
|
!((pktLen >= MIN_IPXSPX_HDRSIZE) ||
|
|
((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) &&
|
|
(pktLen >= MIN_IPXSPX2_HDRSIZE))))
|
|
{
|
|
DBGPRINT(CONNECT, DBG,
|
|
("SpxConnDataPacket: Failed %lx, %lx.%lx\n",
|
|
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum));
|
|
|
|
break;
|
|
}
|
|
|
|
// If it passed above test, but seq number is incorrect, schedule
|
|
// to send an ack.
|
|
if (seqNum != pSpxConnFile->scf_RecvSeqNum)
|
|
{
|
|
USHORT NumToResend;
|
|
|
|
DBGPRINT(CONNECT, DBG,
|
|
("SpxConnDataPacket: Unexpected seq on %lx, %lx.%lx\n",
|
|
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum));
|
|
|
|
++SpxDevice->dev_Stat.DataFramesRejected;
|
|
ExInterlockedAddLargeStatistic(
|
|
&SpxDevice->dev_Stat.DataFrameBytesRejected,
|
|
pktLen - (SPX2_CONN(pSpxConnFile) ?
|
|
MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE));
|
|
|
|
//
|
|
// Bug #16975: Set the remote ack addr for use in SpxConnSendAck()
|
|
//
|
|
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *RemoteAddress;
|
|
|
|
// Calculate number to be resent. If we expect sequence 1 and receive
|
|
// 2 for eg., we need to send a nack, else we send an ack.
|
|
if (SPX2_CONN(pSpxConnFile) &&
|
|
UNSIGNED_GREATER_WITH_WRAP(
|
|
seqNum,
|
|
pSpxConnFile->scf_RecvSeqNum) &&
|
|
!UNSIGNED_GREATER_WITH_WRAP(
|
|
seqNum,
|
|
pSpxConnFile->scf_SentAllocNum))
|
|
{
|
|
NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1);
|
|
SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle);
|
|
fLockHeld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
SpxConnSendAck(pSpxConnFile, lockHandle);
|
|
fLockHeld = FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// If we have received an orderly release, we accept no more data
|
|
// packets.
|
|
if (SPX_CONN_FLAG(
|
|
pSpxConnFile,
|
|
(SPX_CONNFILE_IND_IDISC |
|
|
SPX_CONNFILE_IND_ODISC))
|
|
|
|
||
|
|
|
|
((pSpxConnFile->scf_RecvListTail != NULL) &&
|
|
((pSpxConnFile->scf_RecvListTail->rr_State &
|
|
SPX_RECVPKT_DISCMASK) != 0)))
|
|
{
|
|
DBGPRINT(CONNECT, ERR,
|
|
("SpxConnDataPacket: After ord rel %lx, %lx.%lx\n",
|
|
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum));
|
|
|
|
break;
|
|
}
|
|
|
|
// We are processing a packet OR a receive is about to complete.
|
|
if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT))
|
|
{
|
|
BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum);
|
|
}
|
|
else
|
|
{
|
|
// Already processing a packet. Or a receive is waiting to
|
|
// complete. Get out.
|
|
break;
|
|
}
|
|
|
|
// Set ack numbers for connection.
|
|
SPX_SET_ACKNUM(
|
|
pSpxConnFile, ackNum, allocNum);
|
|
|
|
SpxConnProcessAck(pSpxConnFile, NULL, lockHandle);
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
|
|
iOffset = MIN_IPXSPX2_HDRSIZE;
|
|
if (!SPX2_CONN(pSpxConnFile))
|
|
{
|
|
iOffset = 0;
|
|
if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR))
|
|
{
|
|
iOffset = MIN_IPXSPX_HDRSIZE;
|
|
}
|
|
}
|
|
|
|
copySize = pktLen - iOffset;
|
|
fEom = ((SPX_CONN_MSG(pSpxConnFile) &&
|
|
(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) ||
|
|
SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR));
|
|
|
|
// Do we attempt to piggyback? If not, fImmedAck is true.
|
|
// For SPX1 we dont piggyback.
|
|
// Bug #18253
|
|
fImmedAck = (!SPX2_CONN(pSpxConnFile) ||
|
|
((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM) == 0));
|
|
|
|
// If we do not have EOM to indicate AND we are a zero-sized packet
|
|
// then just consume this packet.
|
|
if (!fEom && (copySize == 0))
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("SpxConnDataPacket: ZERO LENGTH PACKET NO EOM %lx.%lx\n",
|
|
pSpxConnFile, seqNum));
|
|
|
|
fPktDone = TRUE;
|
|
break;
|
|
}
|
|
|
|
receiveFlags = TDI_RECEIVE_NORMAL;
|
|
receiveFlags |= ((fEom ? TDI_RECEIVE_ENTIRE_MESSAGE : 0) |
|
|
(((MacOptions &
|
|
NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ?
|
|
TDI_RECEIVE_COPY_LOOKAHEAD : 0));
|
|
|
|
++SpxDevice->dev_Stat.DataFramesReceived;
|
|
ExInterlockedAddLargeStatistic(
|
|
&SpxDevice->dev_Stat.DataFrameBytesReceived,
|
|
copySize);
|
|
|
|
// Ok, we accept this packet. Depending on our state.
|
|
switch (SPX_RECV_STATE(pSpxConnFile))
|
|
{
|
|
case SPX_RECV_PROCESS_PKTS:
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDataPacket: recv completions on %lx\n",
|
|
pSpxConnFile));
|
|
|
|
goto BufferPacket;
|
|
|
|
case SPX_RECV_IDLE:
|
|
|
|
// If recv q is non-empty we are buffering data.
|
|
// Also, if no receive handler goto buffer data. Also, if receives
|
|
// are being completed, buffer this packet.
|
|
if ((pSpxConnFile->scf_RecvListHead != NULL) ||
|
|
!(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) ||
|
|
!(pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler))
|
|
{
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDataPacket: RecvListHead non-null %lx\n",
|
|
pSpxConnFile));
|
|
|
|
goto BufferPacket;
|
|
}
|
|
|
|
if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND))
|
|
{
|
|
pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx;
|
|
|
|
// Don't indicate this packet again.
|
|
SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND);
|
|
|
|
#if DBG
|
|
CTEAssert(pSpxConnFile->scf_CurRecvReq == NULL);
|
|
|
|
// Debug code to ensure we dont reindicate data/indicate
|
|
// when previously indicated data waiting with afd.
|
|
|
|
//
|
|
// Comment this out for Buf # 10394. we'r hitting this assert
|
|
// even when there was no data loss.
|
|
//
|
|
// CTEAssert(pSpxConnFile->scf_IndBytes == 0);
|
|
CTEAssert(pSpxConnFile->scf_PktSeqNum != seqNum);
|
|
|
|
pSpxConnFile->scf_PktSeqNum = seqNum;
|
|
pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags;
|
|
pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2;
|
|
|
|
pSpxConnFile->scf_IndBytes = copySize;
|
|
pSpxConnFile->scf_IndLine = __LINE__;
|
|
|
|
|
|
#endif
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
|
|
bytesTaken = 0;
|
|
status = (*pRecvHandler)(
|
|
pRecvCtx,
|
|
pSpxConnFile->scf_ConnCtx,
|
|
receiveFlags,
|
|
LookaheadSize - iOffset,
|
|
copySize,
|
|
&bytesTaken,
|
|
LookaheadBuffer + iOffset,
|
|
&pRecvIrp);
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDataPacket: IND Flags %lx.%lx ConnID %lx,\
|
|
%lx Ctx %lx SEQ %lx Size %lx . %lx .%lx IND Status %lx\n",
|
|
pIpxSpxHdr->hdr_ConnCtrl,
|
|
receiveFlags,
|
|
destConnId,
|
|
pSpxConnFile,
|
|
pSpxConnFile->scf_ConnCtx,
|
|
seqNum,
|
|
LookaheadSize - iOffset,
|
|
copySize,
|
|
bytesTaken,
|
|
status));
|
|
|
|
DBGPRINT(RECEIVE, INFO,
|
|
("SpxConnDataPacket: %x %x %x %x %x %x %x %x %x %x %x %x\n",
|
|
*(LookaheadBuffer+iOffset),
|
|
*(LookaheadBuffer+iOffset+1),
|
|
*(LookaheadBuffer+iOffset+2),
|
|
*(LookaheadBuffer+iOffset+3),
|
|
*(LookaheadBuffer+iOffset+4),
|
|
*(LookaheadBuffer+iOffset+5),
|
|
*(LookaheadBuffer+iOffset+6),
|
|
*(LookaheadBuffer+iOffset+7),
|
|
*(LookaheadBuffer+iOffset+8),
|
|
*(LookaheadBuffer+iOffset+9),
|
|
*(LookaheadBuffer+iOffset+10),
|
|
*(LookaheadBuffer+iOffset+11)));
|
|
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
// Assume all data accepted.
|
|
CTEAssert((bytesTaken != 0) || fEom);
|
|
fPktDone = TRUE;
|
|
|
|
#if DBG
|
|
// Set this to 0, since we just indicated, there could
|
|
// not have been other data.
|
|
pSpxConnFile->scf_IndBytes = 0;
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
|
|
// Queue irp into connection, change state to receive
|
|
// posted and fall thru.
|
|
pRequest = SpxAllocateRequest(
|
|
SpxDevice,
|
|
pRecvIrp);
|
|
|
|
IF_NOT_ALLOCATED(pRequest)
|
|
{
|
|
pRecvIrp->IoStatus.Status =
|
|
STATUS_INSUFFICIENT_RESOURCES;
|
|
IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT);
|
|
break;
|
|
}
|
|
|
|
// If there was indicated but not received data waiting
|
|
// (which in this path there will never be, the request
|
|
// could be completed given the data filled it up, and
|
|
// the lock released.
|
|
SpxConnQueueRecv(
|
|
pSpxConnFile,
|
|
pRequest);
|
|
|
|
CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq);
|
|
}
|
|
else if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage))
|
|
{
|
|
// Data was not accepted. Need to buffer data and
|
|
// reduce window.
|
|
goto BufferPacket;
|
|
}
|
|
|
|
// Fall through to recv_posted.
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(RECEIVE, WARN,
|
|
("SpxConnDataPacket: !!!Ignoring %lx Seq %lx\n",
|
|
pSpxConnFile,
|
|
seqNum));
|
|
|
|
break;
|
|
}
|
|
|
|
case SPX_RECV_POSTED:
|
|
|
|
if (pSpxConnFile->scf_RecvListHead != NULL)
|
|
{
|
|
// This can happen also. Buffer packet if it does.
|
|
goto BufferPacket;
|
|
}
|
|
|
|
// If a receive irp is posted, then process the receive irp. If
|
|
// we fell thru we MAY already will have an irp.
|
|
if (pRequest == NULL)
|
|
{
|
|
CTEAssert(!IsListEmpty(&pSpxConnFile->scf_RecvLinkage));
|
|
CTEAssert(pSpxConnFile->scf_CurRecvReq != NULL);
|
|
pRequest = pSpxConnFile->scf_CurRecvReq;
|
|
}
|
|
|
|
// Process receive. Here we do not need to worry about
|
|
// indicated yet not received data. We just deal with
|
|
// servicing the current packet.
|
|
CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq);
|
|
if ((LookaheadSize == PacketSize) &&
|
|
(pSpxConnFile->scf_CurRecvSize >= copySize))
|
|
{
|
|
bytesCopied = 0;
|
|
status = STATUS_SUCCESS;
|
|
if (copySize > 0)
|
|
{
|
|
status = TdiCopyBufferToMdl(
|
|
LookaheadBuffer,
|
|
iOffset,
|
|
copySize,
|
|
REQUEST_TDI_BUFFER(pRequest),
|
|
pSpxConnFile->scf_CurRecvOffset,
|
|
&bytesCopied);
|
|
|
|
CTEAssert(NT_SUCCESS(status));
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Abort request with this status. Reset request
|
|
// queue to next request if one is available.
|
|
}
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("BytesCopied %lx CopySize %lx, Recv Size %lx.%lx\n",
|
|
bytesCopied, copySize,
|
|
pSpxConnFile->scf_CurRecvSize,
|
|
pSpxConnFile->scf_CurRecvOffset));
|
|
}
|
|
|
|
// Update current request values and see if this request
|
|
// is to be completed. Either zero or fEom.
|
|
pSpxConnFile->scf_CurRecvOffset += bytesCopied;
|
|
pSpxConnFile->scf_CurRecvSize -= bytesCopied;
|
|
|
|
#if DBG
|
|
// Decrement indicated data count
|
|
if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND))
|
|
{
|
|
if (bytesCopied != 0)
|
|
{
|
|
CTEAssert (pSpxConnFile->scf_IndBytes != 0);
|
|
pSpxConnFile->scf_IndBytes -= bytesCopied;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (SPX_CONN_STREAM(pSpxConnFile) ||
|
|
(pSpxConnFile->scf_CurRecvSize == 0) ||
|
|
fEom)
|
|
{
|
|
CTELockHandle lockHandleInter;
|
|
|
|
// Set status
|
|
REQUEST_STATUS(pRequest) = STATUS_SUCCESS;
|
|
REQUEST_INFORMATION(pRequest)=
|
|
pSpxConnFile->scf_CurRecvOffset;
|
|
|
|
if (!SPX_CONN_STREAM(pSpxConnFile) &&
|
|
(pSpxConnFile->scf_CurRecvSize == 0) &&
|
|
!fEom)
|
|
{
|
|
REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL;
|
|
}
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnData: Completing recv %lx with %lx.%lx\n",
|
|
pRequest, REQUEST_STATUS(pRequest),
|
|
REQUEST_INFORMATION(pRequest)));
|
|
|
|
// Dequeue this request, Set next recv if one exists.
|
|
SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest);
|
|
|
|
// Request is done. Move to completion list.
|
|
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
|
|
InsertTailList(
|
|
&pSpxConnFile->scf_RecvDoneLinkage,
|
|
REQUEST_LINKAGE(pRequest));
|
|
|
|
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
|
|
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
|
|
}
|
|
|
|
fPktDone = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Need to allocate a ndis receive packet for transfer
|
|
// data.
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnDataPacket: %lx.%lx Tranfer data needed!\n",
|
|
copySize, pSpxConnFile->scf_CurRecvSize));
|
|
|
|
if (copySize > pSpxConnFile->scf_CurRecvSize)
|
|
{
|
|
// Partial receive. Buffer and then deal with it.
|
|
goto BufferPacket;
|
|
}
|
|
|
|
// Allocate a ndis receive packet.
|
|
SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus);
|
|
if (ndisStatus != NDIS_STATUS_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Describe the receive irp's data with a ndis buffer
|
|
// descriptor.
|
|
if (copySize > 0)
|
|
{
|
|
SpxCopyBufferChain(
|
|
&ndisStatus,
|
|
&pNdisBuffer,
|
|
SpxDevice->dev_NdisBufferPoolHandle,
|
|
REQUEST_TDI_BUFFER(pRequest),
|
|
pSpxConnFile->scf_CurRecvOffset,
|
|
copySize);
|
|
|
|
if (ndisStatus != NDIS_STATUS_SUCCESS)
|
|
{
|
|
// Free the recv packet
|
|
SpxPktRecvRelease(pNdisPkt);
|
|
break;
|
|
}
|
|
|
|
// Queue the buffer into the packet
|
|
// Link the buffer descriptor into the packet descriptor
|
|
NdisChainBufferAtBack(
|
|
pNdisPkt,
|
|
pNdisBuffer);
|
|
}
|
|
|
|
// Don't care about whether this is indicated or not here
|
|
// as it is not a buffering packet.
|
|
pRecvResd = RECV_RESD(pNdisPkt);
|
|
pRecvResd->rr_Id = IDENTIFIER_SPX;
|
|
pRecvResd->rr_State =
|
|
((fEom ? SPX_RECVPKT_EOM : 0) |
|
|
(SPX_CONN_FLAG2(
|
|
pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) |
|
|
(fImmedAck ? SPX_RECVPKT_IMMEDACK : 0) |
|
|
((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ?
|
|
SPX_RECVPKT_SENDACK : 0));
|
|
|
|
if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK)
|
|
{
|
|
// copy the remote address in connection.
|
|
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *RemoteAddress;
|
|
}
|
|
|
|
pRecvResd->rr_Request = pRequest;
|
|
pRecvResd->rr_ConnFile = pSpxConnFile;
|
|
|
|
// reference receive request
|
|
REQUEST_INFORMATION(pRequest)++;
|
|
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
fLockHeld = FALSE;
|
|
|
|
// Call ndis transfer data.
|
|
ndisStatus = NDIS_STATUS_SUCCESS;
|
|
bytesCopied = 0;
|
|
if (copySize > 0)
|
|
{
|
|
(*IpxTransferData)(
|
|
&ndisStatus,
|
|
MacBindingHandle,
|
|
MacReceiveContext,
|
|
iOffset + LookaheadOffset,
|
|
copySize,
|
|
pNdisPkt,
|
|
&bytesCopied);
|
|
}
|
|
|
|
if (ndisStatus != STATUS_PENDING)
|
|
{
|
|
SpxTransferDataComplete(
|
|
pNdisPkt,
|
|
ndisStatus,
|
|
bytesCopied);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KeBugCheck(0);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
BufferPacket:
|
|
|
|
SpxRecvBufferPkt(
|
|
pSpxConnFile,
|
|
MacBindingHandle,
|
|
MacReceiveContext,
|
|
iOffset + LookaheadOffset,
|
|
pIpxSpxHdr,
|
|
copySize,
|
|
RemoteAddress,
|
|
lockHandle);
|
|
|
|
fLockHeld = FALSE;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
// Here we process a received ack.
|
|
if (!fLockHeld)
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
fLockHeld = TRUE;
|
|
}
|
|
|
|
// Send an ack if one was asked for. And we are done with this packet.
|
|
if (fPktDone)
|
|
{
|
|
END_PROCESS_PACKET(pSpxConnFile, FALSE, TRUE);
|
|
}
|
|
|
|
if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) && fPktDone)
|
|
{
|
|
if (!fLockHeld)
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
|
|
fLockHeld = TRUE;
|
|
}
|
|
|
|
// First copy the remote address in connection.
|
|
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr);
|
|
pSpxConnFile->scf_AckLocalTarget = *RemoteAddress;
|
|
|
|
// #17564
|
|
if (fImmedAck ||
|
|
SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) ||
|
|
SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK))
|
|
{
|
|
SpxConnSendAck(pSpxConnFile, lockHandle);
|
|
fLockHeld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
SpxConnQWaitAck(pSpxConnFile);
|
|
}
|
|
}
|
|
|
|
if (fLockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
|
|
}
|
|
|
|
// Deref the connection
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_BYID);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxRecvFlushBytes(
|
|
IN PSPX_CONN_FILE pSpxConnFile,
|
|
IN ULONG BytesToFlush,
|
|
IN CTELockHandle LockHandleConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
pSpxConnFile - Pointer to a transport address file object.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PNDIS_PACKET pNdisPkt;
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
PSPX_RECV_RESD pRecvResd;
|
|
PBYTE pData;
|
|
ULONG dataLen, copyLen;
|
|
BOOLEAN fLockHeld = TRUE, fWdwOpen = FALSE;
|
|
USHORT discState = 0;
|
|
int numPkts = 0, numDerefs = 0;
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxRecvFlushBytes: %lx Flush %lx\n",
|
|
pSpxConnFile, BytesToFlush));
|
|
|
|
while (((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) &&
|
|
((BytesToFlush > 0) ||
|
|
((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0)))
|
|
{
|
|
// A buffering recv packet will have ATMOST one ndis buffer descriptor
|
|
// queued in, which will describe a segment of memory we have
|
|
// allocated. An offset will also be present indicating the data
|
|
// to start reading from (or to indicate from to AFD).
|
|
CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0);
|
|
pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD(
|
|
pRecvResd, NDIS_PACKET, ProtocolReserved);
|
|
|
|
NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL);
|
|
|
|
// Initialize pData
|
|
pData = NULL;
|
|
dataLen = 0;
|
|
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisQueryBuffer(pNdisBuffer, &pData, &dataLen);
|
|
CTEAssert(pData != NULL);
|
|
CTEAssert((LONG)dataLen >= 0);
|
|
}
|
|
|
|
if ((BytesToFlush == 0) && (dataLen != 0))
|
|
{
|
|
// Don't flush this packet.
|
|
break;
|
|
}
|
|
|
|
// Allow for zero data, eom only packets.
|
|
copyLen = MIN((dataLen - pRecvResd->rr_DataOffset), BytesToFlush);
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxRecvFlushBytes: %lx Pkt %lx DataLen %lx Copy %lx Flush %lx\n",
|
|
pSpxConnFile, pNdisPkt, dataLen, copyLen, BytesToFlush));
|
|
|
|
// Adjust various values to see whats done whats not
|
|
pRecvResd->rr_DataOffset += (USHORT)copyLen;
|
|
BytesToFlush -= (ULONG)copyLen;
|
|
|
|
#if DBG
|
|
if (copyLen != 0)
|
|
{
|
|
CTEAssert (pSpxConnFile->scf_IndBytes != 0);
|
|
pSpxConnFile->scf_IndBytes -= copyLen;
|
|
}
|
|
#endif
|
|
|
|
if (pRecvResd->rr_DataOffset == dataLen)
|
|
{
|
|
// Packet consumed. Free it up. Check if disc happened.
|
|
discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK);
|
|
CTEAssert((discState == 0) ||
|
|
(pRecvResd == pSpxConnFile->scf_RecvListTail));
|
|
|
|
numDerefs++;
|
|
SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt);
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer);
|
|
CTEAssert(pNdisBuffer != NULL);
|
|
NdisFreeBuffer(pNdisBuffer);
|
|
SpxFreeMemory(pData);
|
|
}
|
|
|
|
SpxPktRecvRelease(pNdisPkt);
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxRecvFlushBytes: !!!ALL INDICATED on %lx.%lx.%lx.%lx\n",
|
|
pSpxConnFile, pNdisPkt, pNdisBuffer, pData));
|
|
|
|
INCREMENT_WINDOW(pSpxConnFile);
|
|
fWdwOpen = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Took only part of this packet. Get out.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL))
|
|
{
|
|
// Send an ack as our windows probably opened up. Dont wait to
|
|
// piggyback here...
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxRecvFlushBytes: Send ACK %lx\n",
|
|
pSpxConnFile));
|
|
|
|
#if DBG_WDW_CLOSE
|
|
// If packets been indicated we have started buffering. Also
|
|
// check if window is now zero.
|
|
{
|
|
LARGE_INTEGER li, ntTime;
|
|
int value;
|
|
|
|
li = pSpxConnFile->scf_WdwCloseTime;
|
|
if (li.LowPart && li.HighPart)
|
|
{
|
|
KeQuerySystemTime(&ntTime);
|
|
|
|
// Get the difference
|
|
ntTime.QuadPart = ntTime.QuadPart - li.QuadPart;
|
|
|
|
// Convert to milliseconds. If the highpart is 0, we
|
|
// take a shortcut.
|
|
if (ntTime.HighPart == 0)
|
|
{
|
|
value = ntTime.LowPart/10000;
|
|
}
|
|
else
|
|
{
|
|
ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime);
|
|
value = ntTime.LowPart << 4;
|
|
}
|
|
|
|
// Set new average close time
|
|
pSpxConnFile->scf_WdwCloseAve += value;
|
|
pSpxConnFile->scf_WdwCloseAve /= 2;
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("V %ld AVE %ld\n",
|
|
value, pSpxConnFile->scf_WdwCloseAve));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SpxConnSendAck(pSpxConnFile, LockHandleConn);
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
}
|
|
|
|
// Check if disconnect happened
|
|
switch (discState)
|
|
{
|
|
case SPX_RECVPKT_IDISC:
|
|
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
|
|
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("spxRecvFlushBytes: Buffered IDISC %lx\n",
|
|
pSpxConnFile));
|
|
|
|
SpxConnProcessIDisc(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
break;
|
|
|
|
case SPX_RECVPKT_ORD_DISC:
|
|
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
|
|
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("spxRecvFlushBytes: Buffered ORDREL %lx\n",
|
|
pSpxConnFile));
|
|
|
|
SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
break;
|
|
|
|
case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC):
|
|
|
|
// IDISC has more priority.
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
|
|
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("spxRecvFlushBytes: Buffered IDISC *AND* ORDREL %lx\n",
|
|
pSpxConnFile));
|
|
|
|
SpxConnProcessIDisc(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
if (fLockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
}
|
|
|
|
while (numDerefs-- > 0)
|
|
{
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
SpxRecvIndicatePendingData(
|
|
IN PSPX_CONN_FILE pSpxConnFile,
|
|
IN CTELockHandle LockHandleConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
pSpxConnFile - Pointer to a transport address file object.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - Receive was queued => TRUE
|
|
|
|
--*/
|
|
{
|
|
ULONG indicateFlags;
|
|
PNDIS_PACKET pNdisPkt;
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
PREQUEST pRequest;
|
|
PIRP pRecvIrp;
|
|
ULONG bytesTaken, totalSize, bufSize;
|
|
PTDI_IND_RECEIVE pRecvHandler;
|
|
PVOID pRecvCtx;
|
|
PSPX_RECV_RESD pRecvResd;
|
|
NTSTATUS status;
|
|
PBYTE lookaheadData;
|
|
ULONG lookaheadSize;
|
|
BOOLEAN fLockHeld = TRUE, fRecvQueued = FALSE;
|
|
|
|
|
|
while ((pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler) &&
|
|
((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) &&
|
|
(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) &&
|
|
((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0) &&
|
|
((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) == 0))
|
|
{
|
|
// Once a receive is queued we better get out.
|
|
CTEAssert(!fRecvQueued);
|
|
|
|
// Initialize lookahead values
|
|
lookaheadData = NULL;
|
|
lookaheadSize = 0;
|
|
|
|
// We have no indicated but pending data, and there is some data to
|
|
// indicate. Figure out how much. Indicate upto end of message or as
|
|
// much as we have.
|
|
|
|
// A buffering recv packet will have ATMOST one ndis buffer descriptor
|
|
// queued in, which will describe a segment of memory we have
|
|
// allocated. An offset will also be present indicating the data
|
|
// to start reading from (or to indicate from to AFD).
|
|
CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0);
|
|
pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD(
|
|
pRecvResd, NDIS_PACKET, ProtocolReserved);
|
|
|
|
NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL);
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisQueryBuffer(pNdisBuffer, &lookaheadData, &lookaheadSize);
|
|
CTEAssert(lookaheadData != NULL);
|
|
CTEAssert((LONG)lookaheadSize >= 0);
|
|
}
|
|
|
|
// Allow for zero data, eom only packets.
|
|
lookaheadSize -= pRecvResd->rr_DataOffset;
|
|
totalSize = lookaheadSize;
|
|
lookaheadData += pRecvResd->rr_DataOffset;
|
|
|
|
// If this packet contained data then eom must also have been
|
|
// indicated at the time all the data was consumed.
|
|
CTEAssert((lookaheadSize > 0) ||
|
|
((pRecvResd->rr_DataOffset == 0) &&
|
|
((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0)));
|
|
|
|
#if DBG
|
|
CTEAssert (pSpxConnFile->scf_CurRecvReq == NULL);
|
|
|
|
// Debug code to ensure we dont reindicate data/indicate
|
|
// when previously indicated data waiting with afd.
|
|
CTEAssert(pSpxConnFile->scf_IndBytes == 0);
|
|
CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum);
|
|
|
|
pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum;
|
|
pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags;
|
|
pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2;
|
|
#endif
|
|
|
|
pRecvResd->rr_State |= SPX_RECVPKT_INDICATED;
|
|
|
|
// Go ahead and walk the list of waiting packets. Get total size.
|
|
while ((pRecvResd->rr_Next != NULL) &&
|
|
((pRecvResd->rr_State & SPX_RECVPKT_EOM) == 0))
|
|
{
|
|
// Check next packet.
|
|
pRecvResd = pRecvResd->rr_Next;
|
|
|
|
#if DBG
|
|
CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum);
|
|
|
|
pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum;
|
|
pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags;
|
|
pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2;
|
|
#endif
|
|
|
|
pRecvResd->rr_State |= SPX_RECVPKT_INDICATED;
|
|
|
|
pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD(
|
|
pRecvResd, NDIS_PACKET, ProtocolReserved);
|
|
|
|
NdisQueryPacket(pNdisPkt, NULL, NULL, NULL, &bufSize);
|
|
CTEAssert((LONG)bufSize >= 0);
|
|
|
|
// Allow for zero data, eom only packets.
|
|
totalSize += bufSize;
|
|
}
|
|
|
|
#if DBG
|
|
pSpxConnFile->scf_IndBytes = totalSize;
|
|
pSpxConnFile->scf_IndLine = __LINE__;
|
|
|
|
// There better not be any pending receives. If so, we have data
|
|
// corruption about to happen.
|
|
if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage))
|
|
{
|
|
DBGBRK(FATAL);
|
|
KeBugCheck(0);
|
|
}
|
|
#endif
|
|
|
|
indicateFlags = TDI_RECEIVE_NORMAL | TDI_RECEIVE_COPY_LOOKAHEAD;
|
|
if ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0)
|
|
{
|
|
indicateFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
|
}
|
|
|
|
pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx;
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
|
|
bytesTaken = 0;
|
|
status = (*pRecvHandler)(
|
|
pRecvCtx,
|
|
pSpxConnFile->scf_ConnCtx,
|
|
indicateFlags,
|
|
lookaheadSize,
|
|
totalSize,
|
|
&bytesTaken,
|
|
lookaheadData,
|
|
&pRecvIrp);
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("SpxConnIndicatePendingData: IND Flags %lx Size %lx .%lx IND Status %lx\n",
|
|
indicateFlags,
|
|
totalSize,
|
|
bytesTaken,
|
|
status));
|
|
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
// Assume all data accepted. Free bytesTaken worth of data packets.
|
|
// Sometimes AFD returns STATUS_SUCCESS to just flush the data, so
|
|
// we can't assume it took only one packet (since lookahead only
|
|
// had that information).
|
|
CTEAssert(bytesTaken == totalSize);
|
|
SpxRecvFlushBytes(pSpxConnFile, totalSize, LockHandleConn);
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
continue;
|
|
}
|
|
else if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
|
|
// Queue irp into connection, change state to receive
|
|
// posted and fall thru.
|
|
pRequest = SpxAllocateRequest(
|
|
SpxDevice,
|
|
pRecvIrp);
|
|
|
|
IF_NOT_ALLOCATED(pRequest)
|
|
{
|
|
pRecvIrp->IoStatus.Status =
|
|
STATUS_INSUFFICIENT_RESOURCES;
|
|
IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT);
|
|
return (FALSE);
|
|
}
|
|
|
|
SpxConnQueueRecv(
|
|
pSpxConnFile,
|
|
pRequest);
|
|
|
|
fRecvQueued = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (fLockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
}
|
|
|
|
return fRecvQueued;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpxRecvProcessPkts(
|
|
IN PSPX_CONN_FILE pSpxConnFile,
|
|
IN CTELockHandle LockHandleConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle buffered data, complete irp if necessary. Set state to idle
|
|
if list becomes empty.
|
|
|
|
Arguments:
|
|
|
|
pSpxConnFile - Pointer to a transport address file object.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN: More data left to indicate => TRUE
|
|
|
|
--*/
|
|
{
|
|
ULONG remainingDataLen, copyLen, bytesCopied;
|
|
PREQUEST pRequest;
|
|
NTSTATUS status;
|
|
BOOLEAN fEom;
|
|
PNDIS_PACKET pNdisPkt;
|
|
PNDIS_BUFFER pNdisBuffer;
|
|
PSPX_RECV_RESD pRecvResd;
|
|
ULONG dataLen;
|
|
PBYTE pData;
|
|
LIST_ENTRY *p;
|
|
BOOLEAN fLockHeld = TRUE, fMoreData = TRUE, fWdwOpen = FALSE;
|
|
USHORT discState = 0;
|
|
int numDerefs = 0;
|
|
|
|
if (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS)
|
|
{
|
|
SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_PROCESS_PKTS);
|
|
|
|
ProcessReceives:
|
|
|
|
while ((pSpxConnFile->scf_CurRecvReq != NULL) &&
|
|
((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL))
|
|
{
|
|
// A buffering recv packet will have one ndis buffer descriptor
|
|
// queued in, which will describe a segment of memory we have
|
|
// allocated. An offset will also be present indicating the data
|
|
// to start reading from (or to indicate from to AFD).
|
|
CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0);
|
|
|
|
pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD(
|
|
pRecvResd, NDIS_PACKET, ProtocolReserved);
|
|
|
|
NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL);
|
|
|
|
|
|
// Initialize pData
|
|
pData = NULL;
|
|
dataLen = 0;
|
|
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisQueryBuffer(pNdisBuffer, &pData, &dataLen);
|
|
CTEAssert(pData != NULL);
|
|
CTEAssert((LONG)dataLen >= 0);
|
|
}
|
|
|
|
// Allow for zero data, eom only packets.
|
|
remainingDataLen = dataLen - pRecvResd->rr_DataOffset;
|
|
|
|
// If this packet contained data then eom must also have been
|
|
// indicated at the time all the data was consumed.
|
|
CTEAssert((remainingDataLen > 0) ||
|
|
((pRecvResd->rr_DataOffset == 0) &&
|
|
((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0)));
|
|
|
|
status = STATUS_SUCCESS;
|
|
copyLen = 0;
|
|
if (remainingDataLen > 0)
|
|
{
|
|
copyLen = MIN(remainingDataLen, pSpxConnFile->scf_CurRecvSize);
|
|
status = TdiCopyBufferToMdl(
|
|
pData,
|
|
pRecvResd->rr_DataOffset,
|
|
copyLen,
|
|
REQUEST_TDI_BUFFER(pSpxConnFile->scf_CurRecvReq),
|
|
pSpxConnFile->scf_CurRecvOffset,
|
|
&bytesCopied);
|
|
|
|
CTEAssert(NT_SUCCESS(status));
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Abort request with this status. Reset request
|
|
// queue to next request if one is available.
|
|
copyLen = pSpxConnFile->scf_CurRecvSize;
|
|
}
|
|
}
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx Size %lx F %lx\n",
|
|
pSpxConnFile, pNdisPkt, pData, copyLen, pRecvResd->rr_State));
|
|
|
|
// Adjust various values to see whats done whats not
|
|
pRecvResd->rr_DataOffset += (USHORT)copyLen;
|
|
pSpxConnFile->scf_CurRecvSize -= (USHORT)copyLen;
|
|
pSpxConnFile->scf_CurRecvOffset += (USHORT)copyLen;
|
|
|
|
#if DBG
|
|
// If this packet was part of indicated data count, decrement.
|
|
if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0)
|
|
{
|
|
if (copyLen != 0)
|
|
{
|
|
CTEAssert (pSpxConnFile->scf_IndBytes != 0);
|
|
pSpxConnFile->scf_IndBytes -= copyLen;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Set fEom/discState (init to 0) only if all of packet was consumed.
|
|
fEom = FALSE;
|
|
if (pRecvResd->rr_DataOffset == dataLen)
|
|
{
|
|
fEom = (BOOLEAN)((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0);
|
|
|
|
// Remember if disconnect needed to happen. If set, this better be
|
|
// last packet received. Again, only if entire pkt was consumed.
|
|
discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK);
|
|
CTEAssert((discState == 0) ||
|
|
(pRecvResd == pSpxConnFile->scf_RecvListTail));
|
|
|
|
// Packet consumed. Free it up.
|
|
numDerefs++;
|
|
|
|
SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt);
|
|
INCREMENT_WINDOW(pSpxConnFile);
|
|
|
|
fWdwOpen = TRUE;
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx DEQUEUED\n",
|
|
pSpxConnFile, pNdisPkt, pData));
|
|
|
|
if (pNdisBuffer != NULL)
|
|
{
|
|
NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer);
|
|
NdisFreeBuffer(pNdisBuffer);
|
|
SpxFreeMemory(pData);
|
|
}
|
|
|
|
SpxPktRecvRelease(pNdisPkt);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: %lx Pkt %lx PARTIAL USE %lx.%lx\n",
|
|
pSpxConnFile, pNdisPkt, pRecvResd->rr_DataOffset, dataLen));
|
|
}
|
|
|
|
// Don't complete until we are out of all packets and stream mode or...
|
|
if (((pSpxConnFile->scf_RecvListHead == NULL) &&
|
|
SPX_CONN_STREAM(pSpxConnFile)) ||
|
|
(pSpxConnFile->scf_CurRecvSize == 0) ||
|
|
fEom)
|
|
{
|
|
// Done with receive, move to completion or complete depending on
|
|
// call level.
|
|
pRequest = pSpxConnFile->scf_CurRecvReq;
|
|
|
|
// Set status. Complete with error from TdiCopy if so.
|
|
REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset;
|
|
REQUEST_STATUS(pRequest) = status;
|
|
|
|
// Ensure we dont overwrite an error status.
|
|
if (!SPX_CONN_STREAM(pSpxConnFile) &&
|
|
(pSpxConnFile->scf_CurRecvSize == 0) &&
|
|
!fEom &&
|
|
NT_SUCCESS(status))
|
|
{
|
|
REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL;
|
|
}
|
|
|
|
// Dequeue this request, set next recv if one exists.
|
|
SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest);
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: %lx Recv %lx with %lx.%lx\n",
|
|
pSpxConnFile, pRequest, REQUEST_STATUS(pRequest),
|
|
REQUEST_INFORMATION(pRequest)));
|
|
|
|
#if DBG
|
|
if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) &&
|
|
(REQUEST_INFORMATION(pRequest) == 0))
|
|
{
|
|
DBGPRINT(TDI, DBG,
|
|
("SpxReceiveComplete: Completing %lx with %lx.%lx\n",
|
|
pRequest, REQUEST_STATUS(pRequest),
|
|
REQUEST_INFORMATION(pRequest)));
|
|
}
|
|
#endif
|
|
|
|
// Request is done. Move to receive completion list. There
|
|
// could already be previously queued requests in here.
|
|
InsertTailList(
|
|
&pSpxConnFile->scf_RecvDoneLinkage,
|
|
REQUEST_LINKAGE(pRequest));
|
|
}
|
|
|
|
CTEAssert((discState == 0) ||
|
|
(pSpxConnFile->scf_RecvListHead == NULL));
|
|
}
|
|
|
|
// Complete any completed receives
|
|
while ((p = pSpxConnFile->scf_RecvDoneLinkage.Flink) !=
|
|
&pSpxConnFile->scf_RecvDoneLinkage)
|
|
{
|
|
pRequest = LIST_ENTRY_TO_REQUEST(p);
|
|
RemoveEntryList(REQUEST_LINKAGE(pRequest));
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
|
|
DBGPRINT(TDI, DBG,
|
|
("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n",
|
|
pRequest, REQUEST_STATUS(pRequest),
|
|
REQUEST_INFORMATION(pRequest)));
|
|
|
|
#if DBG
|
|
if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) &&
|
|
(REQUEST_INFORMATION(pRequest) == 0))
|
|
{
|
|
DBGPRINT(TDI, DBG,
|
|
("SpxReceiveComplete: Completing %lx with %lx.%lx\n",
|
|
pRequest, REQUEST_STATUS(pRequest),
|
|
REQUEST_INFORMATION(pRequest)));
|
|
}
|
|
#endif
|
|
|
|
SpxCompleteRequest(pRequest);
|
|
numDerefs++;
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
}
|
|
|
|
fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) &&
|
|
((pSpxConnFile->scf_RecvListHead ->rr_State &
|
|
SPX_RECVPKT_BUFFERING) != 0) &&
|
|
((pSpxConnFile->scf_RecvListHead->rr_State &
|
|
SPX_RECVPKT_INDICATED) == 0));
|
|
|
|
while (fMoreData)
|
|
{
|
|
// Bug #21036
|
|
// If there is a receive waiting to be processed, we better not
|
|
// indicate data before we finish it.
|
|
if (pSpxConnFile->scf_CurRecvReq != NULL)
|
|
goto ProcessReceives;
|
|
|
|
// If a receive was queued the goto beginning again.
|
|
if (SpxRecvIndicatePendingData(pSpxConnFile, LockHandleConn))
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
goto ProcessReceives;
|
|
}
|
|
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) &&
|
|
((pSpxConnFile->scf_RecvListHead ->rr_State &
|
|
SPX_RECVPKT_BUFFERING) != 0) &&
|
|
((pSpxConnFile->scf_RecvListHead->rr_State &
|
|
SPX_RECVPKT_INDICATED) == 0));
|
|
}
|
|
|
|
// Set state
|
|
SPX_RECV_SETSTATE(
|
|
pSpxConnFile,
|
|
(pSpxConnFile->scf_CurRecvReq == NULL) ?
|
|
SPX_RECV_IDLE : SPX_RECV_POSTED);
|
|
}
|
|
#if DBG
|
|
else
|
|
{
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("spxConnProcessRecdPkts: Already processing pkts %lx\n",
|
|
pSpxConnFile));
|
|
}
|
|
#endif
|
|
|
|
if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL))
|
|
{
|
|
// Send an ack as our windows probably opened up. Dont wait to
|
|
// piggyback here...
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: Send ACK %lx\n",
|
|
pSpxConnFile));
|
|
|
|
#if DBG_WDW_CLOSE
|
|
// If packets been indicated we have started buffering. Also
|
|
// check if window is now zero.
|
|
{
|
|
LARGE_INTEGER li, ntTime;
|
|
int value;
|
|
|
|
li = pSpxConnFile->scf_WdwCloseTime;
|
|
if (li.LowPart && li.HighPart)
|
|
{
|
|
KeQuerySystemTime(&ntTime);
|
|
|
|
// Get the difference
|
|
ntTime.QuadPart = ntTime.QuadPart - li.QuadPart;
|
|
|
|
// Convert to milliseconds. If the highpart is 0, we
|
|
// take a shortcut.
|
|
if (ntTime.HighPart == 0)
|
|
{
|
|
value = ntTime.LowPart/10000;
|
|
}
|
|
else
|
|
{
|
|
ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime);
|
|
value = ntTime.LowPart << 4;
|
|
}
|
|
|
|
// Set new average close time
|
|
pSpxConnFile->scf_WdwCloseAve += value;
|
|
pSpxConnFile->scf_WdwCloseAve /= 2;
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("V %ld AVE %ld\n",
|
|
value, pSpxConnFile->scf_WdwCloseAve));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SpxConnSendAck(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
}
|
|
|
|
// Check if disconnect happened
|
|
switch (discState)
|
|
{
|
|
case SPX_RECVPKT_IDISC:
|
|
|
|
CTEAssert(!fMoreData);
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
|
|
|
|
if (!fLockHeld)
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
}
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: Buffered IDISC %lx\n",
|
|
pSpxConnFile, fMoreData));
|
|
|
|
SpxConnProcessIDisc(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
break;
|
|
|
|
case SPX_RECVPKT_ORD_DISC:
|
|
|
|
CTEAssert(!fMoreData);
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
|
|
|
|
if (!fLockHeld)
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
}
|
|
|
|
DBGPRINT(RECEIVE, DBG,
|
|
("spxConnProcessRecdPkts: Buffered ORDREL %lx\n",
|
|
pSpxConnFile, fMoreData));
|
|
|
|
SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
break;
|
|
|
|
case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC):
|
|
|
|
// IDISC has more priority.
|
|
CTEAssert(!fMoreData);
|
|
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
|
|
|
|
if (!fLockHeld)
|
|
{
|
|
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
|
|
}
|
|
|
|
DBGPRINT(RECEIVE, ERR,
|
|
("spxConnProcessRecdPkts: Buffered IDISC *AND* ORDREL %lx\n",
|
|
pSpxConnFile, fMoreData));
|
|
|
|
SpxConnProcessIDisc(pSpxConnFile, LockHandleConn);
|
|
fLockHeld = FALSE;
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
if (fLockHeld)
|
|
{
|
|
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
|
|
}
|
|
|
|
while (numDerefs-- > 0)
|
|
{
|
|
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
|
|
}
|
|
|
|
return;
|
|
}
|