|
|
/*++
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; }
|