windows-nt/Source/XPSP1/NT/net/nwlink/spx/spxcpkt.c

4237 lines
171 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
spxcpkt.c
Abstract:
This module contains code which implements the CONNECTION object.
Routines are provided to create, destroy, reference, and dereference,
transport connection objects.
Author:
Nikhil Kamkolkar (nikhilk) 11-November-1993
Environment:
Kernel mode
Revision History:
Sanjay Anand (SanjayAn) 14-July-1995
Bug fixes - tagged [SA]
--*/
#include "precomp.h"
#pragma hdrstop
// Define module number for event logging entries
#define FILENUM SPXCPKT
VOID
SpxTdiCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);
NTSTATUS
spxPrepareIrpForCancel(PIRP pIrp)
{
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
CTEAssert(pIrp->CancelRoutine == NULL);
if (!pIrp->Cancel) {
IoMarkIrpPending(pIrp);
// Double check if the routine can handle accept cancel.
IoSetCancelRoutine(pIrp, SpxTdiCancel);
// Do I need to increment any reference count here?
DBGPRINT(CONNECT, INFO,
("spxPrepareIrpForCancel: Prepare IRP %p for cancel.\n", pIrp));
IoReleaseCancelSpinLock(oldIrql);
return(STATUS_SUCCESS);
}
DBGPRINT(CONNECT, INFO,
("spxPrepareIrpForCancel: The IRP %p has already been canceled.\n", pIrp));
IoReleaseCancelSpinLock(oldIrql);
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
return(STATUS_CANCELLED);
}
VOID
spxConnHandleConnReq(
IN PIPXSPX_HDR pIpxSpxHdr,
IN PIPX_LOCAL_TARGET pRemoteAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BOOLEAN fNeg, fSpx2;
TA_IPX_ADDRESS srcIpxAddr;
PTDI_IND_CONNECT connHandler;
USHORT srcConnId, destConnId, destSkt,
pktLen, seqNum, ackNum, allocNum;
PVOID connHandlerCtx;
PREQUEST pListenReq;
PSPX_SEND_RESD pSendResd;
NTSTATUS status;
CTELockHandle lockHandle, lockHandleDev, lockHandleConn;
CONNECTION_CONTEXT connCtx;
PIRP acceptIrp;
PSPX_ADDR pSpxAddr;
PSPX_ADDR_FILE pSpxAddrFile, pSpxRefFile;
PSPX_CONN_FILE pSpxConnFile;
PNDIS_PACKET pCrAckPkt;
BOOLEAN connectAccepted = FALSE, delayAccept = FALSE,
addrLock = FALSE, tdiListen = FALSE;
// Convert hdr to host format as needed.
GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen);
GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId);
GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum);
GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum);
GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->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 *)&pIpxSpxHdr->hdr_SrcConnId;
// Verify Connect Request
if (((pIpxSpxHdr->hdr_ConnCtrl & (SPX_CC_ACK | SPX_CC_SYS)) !=
(SPX_CC_ACK | SPX_CC_SYS)) ||
(pIpxSpxHdr->hdr_DataType != 0) ||
(seqNum != 0) ||
(ackNum != 0) ||
(srcConnId == 0) ||
(srcConnId == 0xFFFF) ||
(destConnId != 0xFFFF))
{
DBGPRINT(RECEIVE, ERR,
("SpxConnSysPacket: VerifyCR Failed %lx.%lx\n",
srcConnId, destConnId));
return;
}
// Get the destination socket from the header
destSkt = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_DestSkt;
SpxBuildTdiAddress(
&srcIpxAddr,
sizeof(srcIpxAddr),
(PBYTE)pIpxSpxHdr->hdr_SrcNet,
pIpxSpxHdr->hdr_SrcNode,
pIpxSpxHdr->hdr_SrcSkt);
// Ok, get the address object this is destined for.
CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev);
pSpxAddr = SpxAddrLookup(SpxDevice, destSkt);
CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev);
if (pSpxAddr == NULL)
{
DBGPRINT(RECEIVE, DBG,
("SpxReceive: No addr for %lx\n", destSkt));
return;
}
fSpx2 = ((PARAM(CONFIG_DISABLE_SPX2) == 0) &&
(BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2));
fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG));
DBGPRINT(CONNECT, DBG,
("spxConnHandleConnReq: Received connect req! %d.%d\n",
fSpx2, fNeg));
CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle);
addrLock = TRUE;
// We use a bit setting in the flag to prevent reentering
// per address file.
//
// We first search the list of non-inactive connections on the address
// this packet came in on to see if it is a duplicate. If it is, we just
// resend ack. Note we dont need to scan the global connection list.
status = SpxAddrConnByRemoteIdAddrLock(
pSpxAddr, srcConnId, pIpxSpxHdr->hdr_SrcNet, &pSpxConnFile);
if (NT_SUCCESS(status))
{
DBGPRINT(CONNECT, ERR,
("spxConnHandleConnReq: Received duplicate connect req! %lx\n",
pSpxConnFile));
if (SPX_CONN_ACTIVE(pSpxConnFile) ||
(SPX_CONN_LISTENING(pSpxConnFile) &&
((SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) ||
(SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP))))
{
DBGPRINT(CONNECT, ERR,
("spxConnHandleConnReq: Sending Duplicate CR - ACK! %lx\n",
pSpxConnFile));
// Build and send an ack
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn);
SpxPktBuildCrAck(
pSpxConnFile,
pSpxAddr,
&pCrAckPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY,
SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG),
SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2));
if (pCrAckPkt != NULL)
{
SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt);
}
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle);
addrLock = FALSE;
// Send the CR Ack packet!
if (pCrAckPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd);
}
}
if (addrLock)
{
CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle);
// We should return in this if, else addrLock should be set to
// FALSE.
}
// Deref the connection
SpxConnFileDereference(pSpxConnFile, CFREF_ADDR);
// Deref the address
SpxAddrDereference (pSpxAddr, AREF_LOOKUP);
return;
}
do
{
// New connection request:
// Assume we will be able to accept it and allocate a packet for the ack.
// Walk list of listening connections if any.
pSpxRefFile = NULL;
if ((pSpxConnFile = pSpxAddr->sa_ListenConnList) != NULL)
{
PTDI_REQUEST_KERNEL_LISTEN pParam;
DBGPRINT(RECEIVE, INFO,
("SpxConnIndicate: Listen available!\n"));
// dequeue connection
pSpxAddr->sa_ListenConnList = pSpxConnFile->scf_Next;
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn);
CTEAssert(!IsListEmpty(&pSpxConnFile->scf_ReqLinkage));
pListenReq = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink);
pParam = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(pListenReq);
// if autoaccept, acceptIrp = listenIrp, get connection id and
// process as we do for an indication. As the connection has a
// listen posted on it, it must have a reference for it.
//
// if !autoaccept, we need to complete the listen irp.
delayAccept = (BOOLEAN)((pParam->RequestFlags & TDI_QUERY_ACCEPT) != 0);
if (delayAccept)
{
// Remove the listen irp and prepare for completion.
// NOTE!! Here we do not remove the listen reference. This will
// be removed if disconnect happens, or if accept
// happens, it is transferred to being ref for connection
// being active.
RemoveEntryList(REQUEST_LINKAGE(pListenReq));
REQUEST_STATUS(pListenReq) = STATUS_SUCCESS;
REQUEST_INFORMATION(pListenReq) = 0;
}
// Are we ok with spx2?
if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)) ||
!fSpx2)
{
// We better use spx only.
SPX_CONN_RESETFLAG(pSpxConnFile,
(SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG));
fSpx2 = fNeg = FALSE;
}
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
connectAccepted = TRUE;
tdiListen = TRUE;
}
else
{
// No listens available. Check for connect handlers.
// Walk list of address files indicating to each until accepted.
for (pSpxAddrFile = pSpxAddr->sa_AddrFileList;
pSpxAddrFile != NULL;
pSpxAddrFile = pSpxAddrFile->saf_Next)
{
if ((pSpxAddrFile->saf_Flags & (SPX_ADDRFILE_CLOSING |
SPX_ADDRFILE_CONNIND)) ||
((connHandler = pSpxAddrFile->saf_ConnHandler) == NULL))
{
continue;
}
// Connect indication in progress, drop all subsequent.
pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_CONNIND;
connHandlerCtx = pSpxAddrFile->saf_ConnHandlerCtx;
SpxAddrFileLockReference(pSpxAddrFile, AFREF_INDICATION);
CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle);
addrLock = FALSE;
if (pSpxRefFile)
{
SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION);
pSpxRefFile = NULL;
}
// Make the indication. We are always returned an accept irp on
// indication. Else we fail to accept the connection.
status = (*connHandler)(
connHandlerCtx,
sizeof(srcIpxAddr),
(PVOID)&srcIpxAddr,
0, // User data length
NULL, // User data
0, // Option length
NULL, // Options
&connCtx,
&acceptIrp);
DBGPRINT(RECEIVE, DBG,
("SpxConn: indicate status %lx.%lx\n",
status, acceptIrp));
CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle);
addrLock = TRUE;
pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_CONNIND;
if (status == STATUS_MORE_PROCESSING_REQUIRED)
{
NTSTATUS retStatus;
CTEAssert(acceptIrp != NULL);
retStatus = spxPrepareIrpForCancel(acceptIrp);
if (!NT_SUCCESS(retStatus)) {
// Copy from the failure case below. [TC]
if (acceptIrp)
{
IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT);
}
pSpxRefFile = pSpxAddrFile;
// Shall we close the connection request and listion object here?
break;
}
// Find the connection and accept the connection using that
// connection object.
SpxConnFileReferenceByCtxLock(
pSpxAddrFile,
connCtx,
&pSpxConnFile,
&status);
if (!NT_SUCCESS(status))
{
// The connection object is closing, or is not found
// in our list. The accept irp must have had the same
// connection object. AFD isnt behaving well.
// KeBugCheck(0);
// The code bugchecked (as commented out above).
// Now, we just return error to the TDI client and return from here.
if (acceptIrp)
{
acceptIrp->IoStatus.Status = STATUS_ADDRESS_NOT_ASSOCIATED;
IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT);
}
pSpxRefFile = pSpxAddrFile;
break;
}
// Only for debugging.
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_BYCTX,
CFREF_VERIFY);
pListenReq = SpxAllocateRequest(
SpxDevice,
acceptIrp);
IF_NOT_ALLOCATED(pListenReq)
{
acceptIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT);
// Setup for dereference
pSpxRefFile = pSpxAddrFile;
break;
}
InsertTailList(
&pSpxConnFile->scf_ReqLinkage,
REQUEST_LINKAGE(pListenReq));
// Setup for dereference
pSpxRefFile = pSpxAddrFile;
connectAccepted = TRUE;
// See if this connection is to be a spx2 connection.
SPX_CONN_RESETFLAG(pSpxConnFile,
(SPX_CONNFILE_SPX2 |
SPX_CONNFILE_NEG |
SPX_CONNFILE_STREAM));
if ((pSpxAddrFile->saf_Flags & SPX_ADDRFILE_SPX2) && fSpx2)
{
SPX_CONN_SETFLAG(
pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG));
}
else
{
fSpx2 = fNeg = FALSE;
}
if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_STREAM)
{
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM);
}
if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT)
{
DBGPRINT(CONNECT, ERR,
("spxConnHandleConnReq: NOACKWAIT requested %lx\n",
pSpxConnFile));
SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT);
}
if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR)
{
DBGPRINT(CONNECT, ERR,
("spxConnHandleConnReq: IPXHDR requested %lx\n",
pSpxConnFile));
SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR);
}
break;
}
else
{
// We are not going to accept the connection on this address.
// Try next one.
pSpxRefFile = pSpxAddrFile;
continue;
}
}
}
} while (FALSE);
if (addrLock)
{
CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle);
// No need for flag from this point on.
// addrLock = FALSE;
}
if (pSpxRefFile)
{
SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION);
pSpxRefFile = NULL;
}
if (connectAccepted)
{
CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev);
CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle);
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn);
if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) ||
((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE))
{
PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE;
}
pSpxConnFile->scf_LocalConnId = spxConnGetId();
pSpxConnFile->scf_RemConnId = srcConnId;
pSpxConnFile->scf_SendSeqNum = 0;
pSpxConnFile->scf_RecvSeqNum = 0;
pSpxConnFile->scf_RecdAckNum = 0;
pSpxConnFile->scf_RetrySeqNum = 0;
pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1);
pSpxConnFile->scf_RecdAllocNum = allocNum;
DBGPRINT(CONNECT, INFO,
("spxConnHandleConnReq: %lx CONN L.R %lx.%lx\n",
pSpxConnFile,
pSpxConnFile->scf_LocalConnId,
pSpxConnFile->scf_RemConnId));
pSpxConnFile->scf_LocalTarget = *pRemoteAddr;
pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr;
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr);
// Set max packet size in connection
SPX_MAX_PKT_SIZE(pSpxConnFile, (fSpx2 && fNeg), fSpx2, pIpxSpxHdr->hdr_SrcNet);
DBGPRINT(CONNECT, DBG,
("spxConnHandleConnReq: Accept connect req on %lx.%lx..%lx.%lx!\n",
pSpxConnFile, pSpxConnFile->scf_LocalConnId,
pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize));
// Aborts must now deal with the lists. Need this as Accept has to
// deal with it.
// Put in non-inactive list. All processing now is equivalent to
// that when a listen is completed on a connection.
if ((!tdiListen) && (!NT_SUCCESS(spxConnRemoveFromList(
&pSpxAddr->sa_InactiveConnList,
pSpxConnFile))))
{
// Should never happen!
KeBugCheck(0);
}
SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile);
// Insert in the global connection tree on device.
spxConnInsertIntoGlobalActiveList(
pSpxConnFile);
SPX_CONN_SETFLAG(pSpxConnFile,
((fNeg ? SPX_CONNFILE_NEG : 0) |
(fSpx2 ? SPX_CONNFILE_SPX2: 0)));
//
// If this was a post-inactivated file, clear the disconnect flags
//
if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) &&
(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) {
SPX_DISC_SETSTATE(pSpxConnFile, 0);
}
#if 0
//
// Make sure that this connection got a local disconnect if it was an SPXI
// connection earlier, in response to a TDI_DISCONNECT_RELEASE.
//
CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX] == 0);
#endif
SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING);
SPX_LISTEN_SETSTATE(pSpxConnFile, (delayAccept ? SPX_LISTEN_RECDREQ : 0));
if (!delayAccept)
{
spxConnAcceptCr(
pSpxConnFile,
pSpxAddr,
lockHandleDev,
lockHandle,
lockHandleConn);
}
else
{
// Release the locks.
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle);
CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev);
// Complete the listen irp. Note reference is not removed. Done when
// accept is posted.
SpxCompleteRequest(pListenReq);
}
} else {
++SpxDevice->dev_Stat.NoListenFailures;
}
// Deref the address
SpxAddrDereference (pSpxAddr, AREF_LOOKUP);
return;
}
VOID
spxConnHandleSessPktFromClient(
IN PIPXSPX_HDR pIpxSpxHdr,
IN PIPX_LOCAL_TARGET pRemoteAddr,
IN PSPX_CONN_FILE pSpxConnFile
)
/*++
Routine Description:
Packet received from the client side of the connection.
Handles:
Session Negotiate
Sends Session Setup, when recd, handles SS Ack
STATE MACHINE:
RR
/ \
/ \ ReceivedAck(SPX1Connection)
/ \
/ \--------> ACTIVE
/ ^
Send / |
ACK / |
/ |
/ RecvNeg/NoNeg |
/ SendSS |
SA--------->SS---------------+
^ | SSAckRecv
| |
+-----+
RecvNeg
RR - Received Connect Request
SA - Sent CR Ack
SS - Sent Session Setup
We move from SA to SS when connection is not negotiatiable and we
immediately send the SS, or when we receive negotiate packet and send the neg
ack and the session setup.
Note we could receive a negotiate packet when in SS, as our ack to the
negotiate could have been dropped. We deal with this.
Arguments:
Return Value:
--*/
{
PNDIS_PACKET pSnAckPkt, pSsPkt = NULL;
PSPX_SEND_RESD pSendResd, pSsSendResd;
USHORT srcConnId, destConnId, pktLen, seqNum = 0, negSize, ackNum, allocNum;
CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev;
BOOLEAN locksHeld = FALSE;
GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen);
GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId);
GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum);
GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum);
GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->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 *)&pIpxSpxHdr->hdr_SrcConnId;
// If spx2 we convert neg size field too
if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2)
{
GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize);
CTEAssert(negSize > 0);
}
// Grab all three locks
CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev);
CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr);
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn);
locksHeld = TRUE;
DBGPRINT(CONNECT, INFO,
("spxConnHandleSessPktFromClient: %lx\n", pSpxConnFile));
// Check substate
switch (SPX_LISTEN_STATE(pSpxConnFile))
{
case SPX_LISTEN_RECDREQ:
// Do nothing.
break;
case SPX_LISTEN_SETUP:
// Is this a setup ack? If so, yippee. Our ack to a negotiate packet
// could have been dropped, and so we could also get a negotiate packet
// in that case. If that happens, fall through.
// Verify Ss Ack
if (!SPX2_CONN(pSpxConnFile) ||
(pktLen != MIN_IPXSPX2_HDRSIZE) ||
((pIpxSpxHdr->hdr_ConnCtrl &
(SPX_CC_SYS | SPX_CC_SPX2)) !=
(SPX_CC_SYS | SPX_CC_SPX2)) ||
(pIpxSpxHdr->hdr_DataType != 0) ||
(srcConnId == 0) ||
(srcConnId == 0xFFFF) ||
(srcConnId != pSpxConnFile->scf_RemConnId) ||
(destConnId == 0) ||
(destConnId == 0xFFFF) ||
(destConnId != pSpxConnFile->scf_LocalConnId) ||
(seqNum != 0))
{
DBGPRINT(RECEIVE, DBG,
("SpxConnSysPacket: VerifySSACK Failed Checking SN %lx.%lx\n",
srcConnId, destConnId));
// Fall through to see if this is a neg packet
if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)))
{
break;
}
}
else
{
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromClient: Recd SSACK %lx\n",
pSpxConnFile));
spxConnCompleteConnect(
pSpxConnFile,
lockHandleDev,
lockHandleAddr,
lockHandleConn);
locksHeld = FALSE;
break;
}
case SPX_LISTEN_SENTACK:
// We expect a negotiate packet.
// We should have asked for SPX2/NEG to begin with.
// Verify Sn
if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) !=
(SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) ||
((pIpxSpxHdr->hdr_ConnCtrl &
(SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) !=
(SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) ||
(pIpxSpxHdr->hdr_DataType != 0) ||
(srcConnId == 0) ||
(srcConnId == 0xFFFF) ||
(srcConnId != pSpxConnFile->scf_RemConnId) ||
(destConnId == 0) ||
(destConnId == 0xFFFF) ||
(destConnId != pSpxConnFile->scf_LocalConnId) ||
(seqNum != 0) ||
((negSize < SPX_NEG_MIN) ||
(negSize > SPX_NEG_MAX)))
{
DBGPRINT(RECEIVE, ERR,
("SpxConnSysPacket: VerifySN Failed %lx.%lx\n",
srcConnId, destConnId));
break;
}
// Remember max packet size in connection.
pSpxConnFile->scf_MaxPktSize = negSize;
CTEAssert(negSize > 0);
// Build sn ack, abort if we fail
SpxPktBuildSnAck(
pSpxConnFile,
&pSnAckPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY);
if (pSnAckPkt == NULL)
{
spxConnAbortConnect(
pSpxConnFile,
STATUS_INSUFFICIENT_RESOURCES,
lockHandleDev,
lockHandleAddr,
lockHandleConn);
locksHeld = FALSE;
break;
}
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromClient: Sending SNACK %lx\n",
pSpxConnFile));
// Queue in the packet.
SpxConnQueueSendPktTail(pSpxConnFile, pSnAckPkt);
// The session packet should already be on queue.
if (!spxConnGetPktByType(
pSpxConnFile,
SPX_TYPE_SS,
FALSE,
&pSsPkt))
{
KeBugCheck(0);
}
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromClient: Sending SS %lx\n",
pSpxConnFile));
pSsSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved);
// We need to resend the packet
if ((pSsSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0)
{
// Try next time.
pSsPkt = NULL;
}
else
{
// Set the size to the neg size indicated in connection.
// This could be lower than the size the packet was build
// with originally. But will never be higher.
pSsSendResd->sr_State |= SPX_SENDPKT_IPXOWNS;
spxConnSetNegSize(
pSsPkt,
pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE);
}
// If we are actually LISTEN_SETUP, then send the ss packet also.
// We need to start the connect timer to resend the ss pkt.
if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK)
{
if ((pSpxConnFile->scf_CTimerId =
SpxTimerScheduleEvent(
spxConnConnectTimer,
PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR,
pSpxConnFile)) == 0)
{
spxConnAbortConnect(
pSpxConnFile,
STATUS_INSUFFICIENT_RESOURCES,
lockHandleDev,
lockHandleAddr,
lockHandleConn);
locksHeld = FALSE;
break;
}
// Reference connection for the timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
SPX_LISTEN_SETSTATE(pSpxConnFile, SPX_LISTEN_SETUP);
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT);
}
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr);
CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev);
locksHeld = FALSE;
// Send ack packet
pSendResd = (PSPX_SEND_RESD)(pSnAckPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pSnAckPkt, pSendResd);
// If we have to send the session setup packet, send that too.
if (pSsPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd);
}
break;
default:
// Ignore
DBGPRINT(RECEIVE, DBG,
("SpxConnSysPacket: UNKNOWN %lx.%lx\n",
srcConnId, destConnId));
break;
}
if (locksHeld)
{
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr);
CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev);
}
return;
}
VOID
spxConnHandleSessPktFromSrv(
IN PIPXSPX_HDR pIpxSpxHdr,
IN PIPX_LOCAL_TARGET pRemoteAddr,
IN PSPX_CONN_FILE pSpxConnFile
)
/*++
Routine Description:
Packet received from the server side of the connection. This will both
release the lock and dereference the connection as it sees fit.
STATE MACHINE:
SR--CTimerExpires-->IDLE
/| \
/ | \ ReceivedAck(SPX1Connection)
/ | \
/ | \--------> ACTIVE
(Neg) / | ^
Send / |RecvAck |
SN / |NoNeg |
/ | |
/ | |
/ v |
SN--------->WS---------------+
RecvSNAck RecvSS
SR - Sent Connect request
SN - Sent Session Negotiate
WS - Waiting for session setup packet
Arguments:
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
BOOLEAN fNeg, fSpx2;
USHORT srcConnId, destConnId,
pktLen, seqNum, negSize = 0, ackNum, allocNum;
CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev;
BOOLEAN cTimerCancelled = FALSE, fAbort = FALSE, locksHeld = FALSE;
PNDIS_PACKET pSsAckPkt, pSnPkt, pPkt = NULL;
// We should get a CR Ack, or if our substate is sent session neg
// we should get a session neg ack, or if we are waiting for session
// setup, we should get one of those.
fSpx2 = (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2);
fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG));
GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen);
GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId);
GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum);
GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum);
GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->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 *)&pIpxSpxHdr->hdr_SrcConnId;
// If spx2 we convert neg size field too
if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2)
{
GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize);
CTEAssert(negSize > 0);
}
// Grab all three locks
CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev);
CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr);
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn);
locksHeld = TRUE;
DBGPRINT(CONNECT, INFO,
("spxConnHandleSessPktFromSrv: %lx\n", pSpxConnFile));
// Check substate
switch (SPX_CONNECT_STATE(pSpxConnFile))
{
case SPX_CONNECT_SENTREQ:
// Check if this qualifies as the ack.
// Verify CR Ack
if ((pIpxSpxHdr->hdr_DataType != 0) ||
(srcConnId == 0) ||
(srcConnId == 0xFFFF) ||
(destConnId == 0) ||
(destConnId == 0xFFFF) ||
(seqNum != 0) ||
(ackNum != 0) ||
((pktLen != MIN_IPXSPX_HDRSIZE) &&
((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) &&
(pktLen != MIN_IPXSPX2_HDRSIZE))) ||
((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) &&
((negSize < SPX_NEG_MIN) ||
(negSize > SPX_NEG_MAX))))
{
DBGPRINT(CONNECT, ERR,
("spxConnHandleSessPktFromSrv: CRAck Invalid %lx %lx.%lx.%lx\n",
pSpxConnFile,
pktLen, negSize, pIpxSpxHdr->hdr_ConnCtrl));
break;
}
// From current spx code base:
// Do we need to send an ack to this ack? In case of SPX only?
// What if this ack is dropped? We need to send an ack, if in future
// we get CONNECT REQ Acks, until we reach active?
// * If they want an ack schedule it. The normal case is for this not
// * to happen, but some Novell mainframe front ends insist on having
// * this. And technically, it is OK for them to do this.
DBGPRINT(CONNECT, INFO,
("spxConnHandleSessPktFromSrv: Recd CRACK %lx\n", pSpxConnFile));
// Grab the remote alloc num/conn id (in net format)
pSpxConnFile->scf_SendSeqNum = 0;
pSpxConnFile->scf_RecvSeqNum = 0;
pSpxConnFile->scf_RecdAckNum = 0;
pSpxConnFile->scf_RemConnId = srcConnId;
pSpxConnFile->scf_RecdAllocNum = allocNum;
// If we have been looking for network 0, which means the
// packets were sent on all NIC IDs, update our local
// target now that we have received a response.
#if defined(_PNP_POWER)
if (pSpxConnFile->scf_LocalTarget.NicHandle.NicId == (USHORT)ITERATIVE_NIC_ID) {
#else
if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) {
#endif _PNP_POWER
pSpxConnFile->scf_LocalTarget = *pRemoteAddr;
pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr;
}
DBGPRINT(CONNECT, INFO,
("spxConnHandleSessPktFromSrv: %lx CONN L.R %lx.%lx\n",
pSpxConnFile,
pSpxConnFile->scf_LocalConnId,
pSpxConnFile->scf_RemConnId));
if (!fSpx2 || !fNeg)
{
cTimerCancelled = SpxTimerCancelEvent(
pSpxConnFile->scf_CTimerId, FALSE);
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
if ((pSpxConnFile->scf_WTimerId =
SpxTimerScheduleEvent(
spxConnWatchdogTimer,
PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR,
pSpxConnFile)) == 0)
{
fAbort = TRUE;
break;
}
// Reference transferred to watchdog timer.
if (cTimerCancelled)
{
cTimerCancelled = FALSE;
}
else
{
// Reference connection for the timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
}
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER);
pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT);
}
// Set max packet size, assume not spx2 or !neg, so pass in FALSE
SPX_MAX_PKT_SIZE(pSpxConnFile, FALSE, FALSE, pIpxSpxHdr->hdr_SrcNet);
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPSrv: Accept connect req on %lx.%lx.%lx.%lx!\n",
pSpxConnFile, pSpxConnFile->scf_LocalConnId,
pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize));
if (!fSpx2)
{
// Reset spx2 flags.
SPX_CONN_RESETFLAG(pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG));
// Complete connect request, this free the lock.
// Cancels tdi timer too. Sets all necessary flags.
spxConnCompleteConnect(
pSpxConnFile,
lockHandleDev,
lockHandleAddr,
lockHandleConn);
locksHeld = FALSE;
break;
}
if (!fNeg)
{
// Goto W_SETUP
// Reset all connect related flags, also spx2/neg flags.
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_NEG);
SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP);
break;
}
// Reset max packet size. SPX2 and NEG.
SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, pIpxSpxHdr->hdr_SrcNet);
CTEAssert(negSize > 0);
CTEAssert(pSpxConnFile->scf_MaxPktSize > 0);
pSpxConnFile->scf_MaxPktSize =
MIN(negSize, pSpxConnFile->scf_MaxPktSize);
pSpxConnFile->scf_MaxPktSize = (USHORT)
MIN(pSpxConnFile->scf_MaxPktSize, PARAM(CONFIG_MAX_PACKET_SIZE));
// For SPX2 with negotiation, we set up sneg packet and move to
// SPX_CONNECT_NEG.
SpxPktBuildSn(
pSpxConnFile,
&pSnPkt,
SPX_SENDPKT_IPXOWNS);
if (pSnPkt == NULL)
{
fAbort = TRUE;
break;
}
// Queue in packet
SpxConnQueueSendPktTail(pSpxConnFile, pSnPkt);
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromSrv: Sending SN %lx\n",
pSpxConnFile));
// Reset retry count for connect timer
pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT);
// Change state.
SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_NEG);
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr);
CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev);
locksHeld = FALSE;
// Send the packet
pSendResd = (PSPX_SEND_RESD)(pSnPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pSnPkt, pSendResd);
break;
case SPX_CONNECT_NEG:
// We expect a session neg ack.
// We should have asked for SPX2/NEG to begin with.
// Verify SN Ack
if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) !=
(SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) ||
(pktLen != MIN_IPXSPX2_HDRSIZE) ||
((pIpxSpxHdr->hdr_ConnCtrl &
(SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) !=
(SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) ||
(pIpxSpxHdr->hdr_DataType != 0) ||
(srcConnId == 0) ||
(srcConnId == 0xFFFF) ||
(srcConnId != pSpxConnFile->scf_RemConnId) ||
(destConnId == 0) ||
(destConnId == 0xFFFF) ||
(destConnId != pSpxConnFile->scf_LocalConnId) ||
(seqNum != 0))
{
DBGPRINT(RECEIVE, ERR,
("SpxConnSysPacket: VerifySNACK Failed %lx.%lx\n",
srcConnId, destConnId));
break;
}
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromSrv: Recd SNACK %lx %lx.%lx\n",
pSpxConnFile, negSize, pSpxConnFile->scf_MaxPktSize));
if (negSize > pSpxConnFile->scf_MaxPktSize)
negSize = pSpxConnFile->scf_MaxPktSize;
// Get the size to use
if (negSize <= pSpxConnFile->scf_MaxPktSize)
{
pSpxConnFile->scf_MaxPktSize = negSize;
if (!spxConnGetPktByType(
pSpxConnFile,
SPX_TYPE_SN,
FALSE,
&pPkt))
{
KeBugCheck(0);
}
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0)
{
// Set abort flag and reference conn for the pkt.
pSendResd->sr_State |= SPX_SENDPKT_ABORT;
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
else
{
// Free the negotiate packet
SpxPktSendRelease(pPkt);
}
CTEAssert(pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER);
cTimerCancelled = SpxTimerCancelEvent(
pSpxConnFile->scf_CTimerId, FALSE);
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
// Start the watchdog timer, if fail, we abort.
if ((pSpxConnFile->scf_WTimerId =
SpxTimerScheduleEvent(
spxConnWatchdogTimer,
PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR,
pSpxConnFile)) == 0)
{
// Complete cr with error.
fAbort = TRUE;
break;
}
// Reference goes to watchdog timer.
if (cTimerCancelled)
{
cTimerCancelled = FALSE;
}
else
{
// Reference connection for the timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
}
// We move to the W_SETUP state.
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER);
pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT);
SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP);
}
break;
case SPX_CONNECT_W_SETUP:
// Does this qualify as a session setup packet?
// Verify SS
if (!SPX2_CONN(pSpxConnFile) ||
((pIpxSpxHdr->hdr_ConnCtrl &
(SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) !=
(SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) ||
(pIpxSpxHdr->hdr_DataType != 0) ||
(srcConnId == 0) ||
(srcConnId == 0xFFFF) ||
(srcConnId != pSpxConnFile->scf_RemConnId) ||
(destConnId == 0) ||
(destConnId == 0xFFFF) ||
(destConnId != pSpxConnFile->scf_LocalConnId) ||
(seqNum != 0) ||
((negSize < SPX_NEG_MIN) ||
(negSize > SPX_NEG_MAX)))
{
DBGPRINT(RECEIVE, ERR,
("SpxConnSysPacket: VerifySS Failed %lx.%lx, %lx %lx.%lx\n",
srcConnId, destConnId, negSize,
pIpxSpxHdr->hdr_ConnCtrl,
(SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)));
break;
}
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromSrv: Recd SS %lx\n", pSpxConnFile));
// Copy remote address over into connection (socket could change)
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr);
// Remember max packet size in connection.
pSpxConnFile->scf_MaxPktSize = negSize;
// Build ss ack, abort if we fail
SpxPktBuildSsAck(
pSpxConnFile,
&pSsAckPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT);
if (pSsAckPkt == NULL)
{
fAbort = TRUE;
break;
}
DBGPRINT(CONNECT, DBG,
("spxConnHandleSessPktFromSrv: Sending SSACK %lx\n",
pSpxConnFile));
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
// We dont queue in the pkt as its already marked as abort.
// Queue in the packet.
// SpxConnQueueSendPktTail(pSpxConnFile, pSsAckPkt);
// Complete connect, this releases lock.
spxConnCompleteConnect(
pSpxConnFile,
lockHandleDev,
lockHandleAddr,
lockHandleConn);
locksHeld = FALSE;
// Send ack packet
pSendResd = (PSPX_SEND_RESD)(pSsAckPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pSsAckPkt, pSendResd);
break;
default:
// Ignore
DBGPRINT(RECEIVE, DBG,
("SpxConnSysPacket: UNKNOWN %lx.%lx\n",
srcConnId, destConnId));
break;
}
if (fAbort)
{
spxConnAbortConnect(
pSpxConnFile,
STATUS_INSUFFICIENT_RESOURCES,
lockHandleDev,
lockHandleAddr,
lockHandleConn);
locksHeld = FALSE;
}
if (locksHeld)
{
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn);
CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr);
CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev);
}
if (cTimerCancelled)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
VOID
spxConnAbortConnect(
IN PSPX_CONN_FILE pSpxConnFile,
IN NTSTATUS Status,
IN CTELockHandle LockHandleDev,
IN CTELockHandle LockHandleAddr,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
This routine abort a connection (both client and server side) in the middle
of a connection establishment.
!!! Called with connection lock held, releases lock before return !!!
Arguments:
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
PNDIS_PACKET pPkt;
PREQUEST pRequest = NULL;
int numDerefs = 0;
DBGPRINT(CONNECT, DBG,
("spxConnAbortConnect: %lx\n", pSpxConnFile));
#if DBG
if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile))
{
KeBugCheck(0);
}
#endif
if (Status == STATUS_INSUFFICIENT_RESOURCES) { // others should be counted elsewhere
++SpxDevice->dev_Stat.LocalResourceFailures;
}
// Free up all the packets
while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL)
{
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)
{
// Free the packet
SpxPktSendRelease(pPkt);
}
else
{
// Set abort flag and reference conn for the pkt.
pSendResd->sr_State |= SPX_SENDPKT_ABORT;
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
}
// Cancel all timers
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER))
{
if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE))
{
numDerefs++;
}
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER);
}
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER))
{
if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE))
{
numDerefs++;
}
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
}
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER))
{
if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE))
{
numDerefs++;
}
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER);
}
// We could be called from disconnect for an accept in which case there
// will be no queued request. But we need to remove the reference if there
// is no request (an accept/listen irp) and listen state is on.
CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage));
if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage))
{
pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink);
RemoveEntryList(REQUEST_LINKAGE(pRequest));
REQUEST_STATUS(pRequest) = Status;
REQUEST_INFORMATION(pRequest) = 0;
// Save req in conn for deref to complete.
pSpxConnFile->scf_ConnectReq = pRequest;
numDerefs++;
}
else if (SPX_CONN_LISTENING(pSpxConnFile))
{
numDerefs++;
}
// Bug #20999
// Race condition was an abort came in from timer, but the connect state
// was left unchanged. Due to an extra ref on the connection from the
// aborted cr, the state remained so, and then the cr ack came in, and
// a session neg was built and queued on the connection. Although it should
// not have been. And we hit the assert in deref where the connection is
// being reinitialized. Since this can be called for both listening and
// connecting connections, do the below.
SPX_LISTEN_SETSTATE(pSpxConnFile, 0);
if (SPX_CONN_CONNECTING(pSpxConnFile))
{
SPX_CONNECT_SETSTATE(pSpxConnFile, 0);
}
CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn);
CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr);
CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev);
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
VOID
spxConnCompleteConnect(
IN PSPX_CONN_FILE pSpxConnFile,
IN CTELockHandle LockHandleDev,
IN CTELockHandle LockHandleAddr,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
This routine completes a connection (both client and server side)
!!! Called with connection lock held, releases lock before return !!!
Arguments:
Return Value:
--*/
{
PREQUEST pRequest;
PSPX_SEND_RESD pSendResd;
PNDIS_PACKET pPkt;
int numDerefs = 0;
DBGPRINT(CONNECT, INFO,
("spxConnCompleteConnect: %lx\n", pSpxConnFile));
#if DBG
if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile))
{
DBGBRK(FATAL);
}
#endif
// Free up all the packets
while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL)
{
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)
{
// Free the packet
SpxPktSendRelease(pPkt);
}
else
{
// Set abort flag and reference conn for the pkt.
pSendResd->sr_State |= SPX_SENDPKT_ABORT;
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
}
// Cancel tdi connect timer if we are connecting.
switch (SPX_MAIN_STATE(pSpxConnFile))
{
case SPX_CONNFILE_CONNECTING:
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER))
{
if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE))
{
numDerefs++;
}
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER);
}
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER))
{
if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE))
{
numDerefs++;
}
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
}
if (pSpxConnFile->scf_CRetryCount == (LONG)(PARAM(CONFIG_CONNECTION_COUNT))) {
++SpxDevice->dev_Stat.ConnectionsAfterNoRetry;
} else {
++SpxDevice->dev_Stat.ConnectionsAfterRetry;
}
// Reset all connect related flags
SPX_MAIN_SETSTATE(pSpxConnFile, 0);
SPX_CONNECT_SETSTATE(pSpxConnFile, 0);
break;
case SPX_CONNFILE_LISTENING:
if (pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER)
{
if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE))
{
numDerefs++;
}
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
}
SPX_MAIN_SETSTATE(pSpxConnFile, 0);
SPX_LISTEN_SETSTATE(pSpxConnFile, 0);
break;
default:
KeBugCheck(0);
}
SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_ACTIVE);
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_IDLE);
++SpxDevice->dev_Stat.OpenConnections;
// Initialize timer values
pSpxConnFile->scf_BaseT1 =
pSpxConnFile->scf_AveT1 = PARAM(CONFIG_INITIAL_RETRANSMIT_TIMEOUT);
pSpxConnFile->scf_DevT1 = 0;
pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT);
pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink);
RemoveEntryList(REQUEST_LINKAGE(pRequest));
REQUEST_STATUS(pRequest) = STATUS_SUCCESS;
REQUEST_INFORMATION(pRequest) = 0;
// When we complete the request, we essentially transfer the reference
// to the fact that the connection is active. This will be taken away
// when a Disconnect happens on the connection and we transition from
// ACTIVE to DISCONN.
// numDerefs++;
CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn);
CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr);
CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev);
// Complete request
SpxCompleteRequest(pRequest);
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
BOOLEAN
spxConnAcceptCr(
IN PSPX_CONN_FILE pSpxConnFile,
IN PSPX_ADDR pSpxAddr,
IN CTELockHandle LockHandleDev,
IN CTELockHandle LockHandleAddr,
IN CTELockHandle LockHandleConn
)
{
PNDIS_PACKET pSsPkt, pCrAckPkt;
PSPX_SEND_RESD pSendResd;
BOOLEAN fNeg = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG);
BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2);
DBGPRINT(CONNECT, DBG,
("spxConnAcceptCr: %lx.%d.%d\n",
pSpxConnFile, fSpx2, fNeg));
// Build and queue in packet.
SpxPktBuildCrAck(
pSpxConnFile,
pSpxAddr,
&pCrAckPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY,
fNeg,
fSpx2);
if ((pCrAckPkt != NULL) &&
(pSpxConnFile->scf_LocalConnId != 0))
{
// Queue in the packet.
SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt);
}
else
{
goto AbortConnect;
}
// Start the timer
if ((pSpxConnFile->scf_WTimerId =
SpxTimerScheduleEvent(
spxConnWatchdogTimer,
PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR,
pSpxConnFile)) != 0)
{
// Reference connection for the timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER);
pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT);
}
else
{
goto AbortConnect;
}
// We start the connect timer for retrying ss which we send out now
// if we are not negotiating.
if (fSpx2)
{
// Build the session setup packet also for spx2.
SpxPktBuildSs(
pSpxConnFile,
&pSsPkt,
(USHORT)(fNeg ? 0 : SPX_SENDPKT_IPXOWNS));
if (pSsPkt != NULL)
{
SpxConnQueueSendPktTail(pSpxConnFile, pSsPkt);
}
else
{
goto AbortConnect;
}
if (!fNeg)
{
if ((pSpxConnFile->scf_CTimerId =
SpxTimerScheduleEvent(
spxConnConnectTimer,
PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR,
pSpxConnFile)) != 0)
{
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER);
pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT);
// Reference connection for the timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
}
else
{
goto AbortConnect;
}
}
}
CTEAssert((fNeg && fSpx2) || (!fSpx2 && !fNeg));
// For a SPX connection, we immediately become active. This happens
// in the completeConnect routine. !!Dont change it here!!
if (!fSpx2)
{
spxConnCompleteConnect(
pSpxConnFile,
LockHandleDev,
LockHandleAddr,
LockHandleConn);
}
else
{
SPX_LISTEN_SETSTATE(
pSpxConnFile, (fNeg ? SPX_LISTEN_SENTACK : SPX_LISTEN_SETUP));
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
CTEFreeLock (&pSpxAddr->sa_Lock, LockHandleAddr);
CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev);
}
// Send the CR Ack packet!
pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd);
if (fSpx2 && !fNeg)
{
pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved);
SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd);
}
return(TRUE);
AbortConnect:
spxConnAbortConnect(
pSpxConnFile,
STATUS_INSUFFICIENT_RESOURCES,
LockHandleDev,
LockHandleAddr,
LockHandleConn);
return (FALSE);
}
BOOLEAN
SpxConnPacketize(
IN PSPX_CONN_FILE pSpxConnFile,
IN BOOLEAN fNormalState,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
The caller needs to set the state to packetize before calling this
routine. This can be called when SEND state is RENEG also.
Arguments:
pSpxConnFile - Pointer to a transport address file object.
fNormalState - If true, it will assume it can release lock to send,
else, it just builds pkts without releasing lock and
releases lock at end. Used after reneg changes size.
Return Value:
--*/
{
PLIST_ENTRY p;
PNDIS_PACKET pPkt;
PSPX_SEND_RESD pSendResd;
USHORT windowSize;
ULONG dataLen;
USHORT sendFlags;
int numDerefs = 0;
BOOLEAN fFirstPass = TRUE, fSuccess = TRUE;
PREQUEST pRequest;
#if DBG
if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) &&
fNormalState)
{
DBGBRK(FATAL);
KeBugCheck(0);
}
#endif
// Build all of the packets. The firsttime flag is used so
// that if we get a 0 byte send, we will send it. The firsttime
// flag will be set and we will build the packet and send it.
//
// FOR SPX1, we cannot trust the remote window size. So we only send
// stuff if window size is greater than 0 *AND* we do not have any pending
// sends. Dont get in here if we are ABORT. Dont want to be handling any
// more requests.
while((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ABORT) &&
((pRequest = pSpxConnFile->scf_ReqPkt) != NULL) &&
((pSpxConnFile->scf_ReqPktSize > 0) || fFirstPass))
{
fFirstPass = FALSE;
windowSize = pSpxConnFile->scf_RecdAllocNum -
pSpxConnFile->scf_SendSeqNum + 1;
DBGPRINT(SEND, DBG,
("SpxConnPacketize: WINDOW %lx for %lx\n",
windowSize, pSpxConnFile));
DBGPRINT(SEND, DBG,
("REMALLOC %lx SENDSEQ %lx\n",
pSpxConnFile->scf_RecdAllocNum,
pSpxConnFile->scf_SendSeqNum));
CTEAssert(windowSize >= 0);
// Disconnect/Orderly release is not subject to window closure.
if ((pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) &&
((windowSize == 0) ||
(!SPX2_CONN(pSpxConnFile) &&
(pSpxConnFile->scf_SendSeqListHead != NULL))))
{
break;
}
if (pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA)
{
CTEAssert(pRequest == pSpxConnFile->scf_ReqPkt);
// Get data length
dataLen = (ULONG)MIN(pSpxConnFile->scf_ReqPktSize,
(pSpxConnFile->scf_MaxPktSize -
((SPX2_CONN(pSpxConnFile) ?
MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE))));
DBGPRINT(SEND, DBG,
("SpxConnPacketize: %lx Sending %lx Size %lx Req %lx.%lx\n",
pSpxConnFile,
pSpxConnFile->scf_SendSeqNum,
dataLen,
pSpxConnFile->scf_ReqPkt,
pSpxConnFile->scf_ReqPktSize));
// Build data packet. Handles 0-length for data. Puts in seq num in
// send resd section of packet also.
sendFlags =
(USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) |
SPX_SENDPKT_REQ |
SPX_SENDPKT_SEQ |
((!SPX2_CONN(pSpxConnFile) || (windowSize == 1)) ?
SPX_SENDPKT_ACKREQ : 0));
if (dataLen == pSpxConnFile->scf_ReqPktSize)
{
// Last packet of send, ask for a ack.
sendFlags |= (SPX_SENDPKT_LASTPKT | SPX_SENDPKT_ACKREQ);
if ((pSpxConnFile->scf_ReqPktFlags & TDI_SEND_PARTIAL) == 0)
sendFlags |= SPX_SENDPKT_EOM;
}
SpxPktBuildData(
pSpxConnFile,
&pPkt,
sendFlags,
(USHORT)dataLen);
}
else
{
dataLen = 0;
DBGPRINT(SEND, DBG,
("Building DISC packet on %lx ReqPktSize %lx\n",
pSpxConnFile, pSpxConnFile->scf_ReqPktSize));
// Build informed disc/orderly rel packet, associate with request
SpxPktBuildDisc(
pSpxConnFile,
pRequest,
&pPkt,
(USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | SPX_SENDPKT_REQ |
SPX_SENDPKT_SEQ | SPX_SENDPKT_LASTPKT),
(UCHAR)((pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) ?
SPX2_DT_ORDREL : SPX2_DT_IDISC));
}
if (pPkt != NULL)
{
// If we were waiting to send an ack, we don't have to as we are
// piggybacking it now. Cancel ack timer, get out.
if (fNormalState && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ))
{
DBGPRINT(SEND, DBG,
("SpxConnPacketize: Piggyback happening for %lx.%lx\n",
pSpxConnFile, pSpxConnFile->scf_RecvSeqNum));
// We are sending data, allow piggybacks to happen.
SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK);
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ);
if (SpxTimerCancelEvent(pSpxConnFile->scf_ATimerId, FALSE))
{
numDerefs++;
}
}
if (pSpxConnFile->scf_ReqPktType != SPX_REQ_DATA)
{
// For a disconnect set the state
if (pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL)
{
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC))
{
SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN);
SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL);
numDerefs++;
}
else if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_ORDREL)
{
CTEAssert((SPX_MAIN_STATE(pSpxConnFile) ==
SPX_CONNFILE_ACTIVE) ||
(SPX_MAIN_STATE(pSpxConnFile) ==
SPX_CONNFILE_DISCONN));
SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL);
}
else
{
CTEAssert(
(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL));
}
}
else
{
CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN);
CTEAssert(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC);
// Note we have send the idisc here.
SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_IDISC);
}
}
}
else
{
fSuccess = FALSE;
break;
}
// Queue in packet, reference request for the packet
SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt);
REQUEST_INFORMATION(pRequest)++;
pSpxConnFile->scf_ReqPktSize -= dataLen;
pSpxConnFile->scf_ReqPktOffset += dataLen;
DBGPRINT(SEND, INFO,
("SpxConnPacketize: Req %lx Size after pkt %lx.%lx\n",
pSpxConnFile->scf_ReqPkt, pSpxConnFile->scf_ReqPktSize,
dataLen));
// Even if window size if zero, setup next request is current one
// is done. We are here only after we have packetized this send req.
if (pSpxConnFile->scf_ReqPktSize == 0)
{
// This request has been fully packetized. Either go on to
// next request or we are done packetizing.
p = REQUEST_LINKAGE(pRequest);
if (p->Flink == &pSpxConnFile->scf_ReqLinkage)
{
DBGPRINT(SEND, INFO,
("SpxConnPacketize: Req %lx done, no more\n",
pRequest));
pSpxConnFile->scf_ReqPkt = NULL;
pSpxConnFile->scf_ReqPktSize = 0;
pSpxConnFile->scf_ReqPktOffset = 0;
pRequest = NULL;
}
else
{
pRequest = LIST_ENTRY_TO_REQUEST(p->Flink);
if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT)
{
PTDI_REQUEST_KERNEL_SEND pParam;
pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest);
DBGPRINT(SEND, DBG,
("SpxConnPacketize: Req done, setting next %lx.%lx\n",
pRequest, pParam->SendLength));
DBGPRINT(SEND, INFO,
("-%lx-\n",
pRequest));
// Set parameters in connection for another go.
pSpxConnFile->scf_ReqPkt = pRequest;
pSpxConnFile->scf_ReqPktOffset = 0;
pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags;
pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA;
if ((pSpxConnFile->scf_ReqPktSize = pParam->SendLength) == 0)
{
// Another zero length send.
fFirstPass = TRUE;
}
}
else
{
PTDI_REQUEST_KERNEL_DISCONNECT pParam;
pParam =
(PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest);
pSpxConnFile->scf_ReqPkt = pRequest;
pSpxConnFile->scf_ReqPktOffset = 0;
pSpxConnFile->scf_ReqPktSize = 0;
fFirstPass = TRUE;
pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC;
if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE)
{
pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL;
}
}
}
}
if (fNormalState)
{
// Send the packet if we are not at the reneg state
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
++SpxDevice->dev_Stat.DataFramesSent;
ExInterlockedAddLargeStatistic(
&SpxDevice->dev_Stat.DataFrameBytesSent,
dataLen);
SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd);
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
}
// Check if retry timer needs to be started.
if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)))
{
if ((pSpxConnFile->scf_RTimerId =
SpxTimerScheduleEvent(
spxConnRetryTimer,
pSpxConnFile->scf_BaseT1,
pSpxConnFile)) != 0)
{
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER);
pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT);
// Reference connection for the timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
}
else
{
DBGPRINT(SEND, ERR,
("SpxConnPacketize: Failed to start retry timer\n"));
fSuccess = FALSE;
break;
}
}
}
// Dont overwrite an error state.
if (((fNormalState) &&
(SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE)) ||
((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) &&
(pSpxConnFile->scf_SendSeqListHead == NULL)))
{
if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3)
{
DBGPRINT(SEND, ERR,
("COULD NOT PACKETIZE AFTER RENEG %lx\n", pSpxConnFile));
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_ERRORSTATE,
CFREF_VERIFY);
numDerefs++;
}
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
}
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return(fSuccess);
}
VOID
SpxConnQueueRecv(
IN PSPX_CONN_FILE pSpxConnFile,
IN PREQUEST pRequest
)
/*++
Routine Description:
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
PTDI_REQUEST_KERNEL_RECEIVE pParam;
NTSTATUS status = STATUS_PENDING;
if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage))
{
pParam = (PTDI_REQUEST_KERNEL_RECEIVE)REQUEST_PARAMETERS(pRequest);
pSpxConnFile->scf_CurRecvReq = pRequest;
pSpxConnFile->scf_CurRecvOffset = 0;
pSpxConnFile->scf_CurRecvSize = pParam->ReceiveLength;
}
DBGPRINT(RECEIVE, DBG,
("spxConnQueueRecv: %lx.%lx\n", pRequest, pParam->ReceiveLength));
// Reference connection for this recv.
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
InsertTailList(
&pSpxConnFile->scf_RecvLinkage,
REQUEST_LINKAGE(pRequest));
// RECV irps have no creation references.
REQUEST_INFORMATION(pRequest) = 0;
REQUEST_STATUS(pRequest) = STATUS_SUCCESS;
// State to receive_posted if we are idle.
if (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE)
{
SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_POSTED);
}
return;
}
VOID
spxConnCompletePended(
IN PSPX_CONN_FILE pSpxConnFile
)
{
CTELockHandle lockHandleInter;
LIST_ENTRY ReqList, *p;
PREQUEST pRequest;
InitializeListHead(&ReqList);
DBGPRINT(RECEIVE, DBG,
("spxConnCompletePended: PENDING RECV REQUESTS IN DONE LIST! %lx\n",
pSpxConnFile));
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
p = pSpxConnFile->scf_RecvDoneLinkage.Flink;
while (p != &pSpxConnFile->scf_RecvDoneLinkage)
{
pRequest = LIST_ENTRY_TO_REQUEST(p);
p = p->Flink;
RemoveEntryList(REQUEST_LINKAGE(pRequest));
InsertTailList(
&ReqList,
REQUEST_LINKAGE(pRequest));
}
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
while (!IsListEmpty(&ReqList))
{
p = RemoveHeadList(&ReqList);
pRequest = LIST_ENTRY_TO_REQUEST(p);
DBGPRINT(TDI, DBG,
("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
#if DBG
if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_RECEIVE)
{
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);
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
VOID
SpxConnQWaitAck(
IN PSPX_CONN_FILE pSpxConnFile
)
/*++
Routine Description:
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
// If we are not already in ack queue, queue ourselves in starting
// ack timer.
if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ))
{
// First start ack timer.
if ((pSpxConnFile->scf_ATimerId =
SpxTimerScheduleEvent(
spxConnAckTimer,
100,
pSpxConnFile)) != 0)
{
// Reference connection for timer
SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY);
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ);
++SpxDevice->dev_Stat.PiggybackAckQueued;
}
}
return;
}
VOID
SpxConnSendAck(
IN PSPX_CONN_FILE pSpxConnFile,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
PNDIS_PACKET pPkt = NULL;
DBGPRINT(SEND, DBG,
("spxConnSendAck: ACKING on %lx.%lx\n",
pSpxConnFile, pSpxConnFile->scf_RecvSeqNum));
// Build an ack packet, queue it in non-sequenced queue. Only if we are
// active.
if (SPX_CONN_ACTIVE(pSpxConnFile))
{
SpxPktBuildAck(
pSpxConnFile,
&pPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY,
FALSE,
0);
if (pPkt != NULL)
{
SpxConnQueueSendPktTail(pSpxConnFile, pPkt);
}
else
{
// Log error
DBGPRINT(SEND, ERR,
("SpxConnSendAck: Could not allocate!\n"));
}
}
#if DBG
else
{
DBGPRINT(SEND, DBG,
("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n"));
}
#endif
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
// Send it.
if (pPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
// Send the packet
SPX_SENDACK(pSpxConnFile, pPkt, pSendResd);
}
return;
}
VOID
SpxConnSendNack(
IN PSPX_CONN_FILE pSpxConnFile,
IN USHORT NumToSend,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
PNDIS_PACKET pPkt = NULL;
DBGPRINT(SEND, DBG,
("spxConnSendNack: NACKING on %lx.%lx\n",
pSpxConnFile, pSpxConnFile->scf_RecvSeqNum));
// Build an nack packet, queue it in non-sequenced queue. Only if we are
// active.
if (SPX_CONN_ACTIVE(pSpxConnFile))
{
SpxPktBuildAck(
pSpxConnFile,
&pPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY,
TRUE,
NumToSend);
if (pPkt != NULL)
{
SpxConnQueueSendPktTail(pSpxConnFile, pPkt);
}
else
{
// Log error
DBGPRINT(SEND, ERR,
("SpxConnSendAck: Could not allocate!\n"));
}
}
#if DBG
else
{
DBGPRINT(SEND, DBG,
("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n"));
}
#endif
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
// Send it.
if (pPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
// Send the packet
SPX_SENDACK(pSpxConnFile, pPkt, pSendResd);
}
return;
}
BOOLEAN
SpxConnProcessAck(
IN PSPX_CONN_FILE pSpxConnFile,
IN PIPXSPX_HDR pIpxSpxHdr,
IN CTELockHandle lockHandle
)
/*++
Routine Description:
!!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!!
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
PNDIS_PACKET pPkt;
PREQUEST pRequest;
PSPX_SEND_RESD pSendResd;
CTELockHandle interLockHandle;
USHORT seqNum = 0, ackNum;
int numDerefs = 0;
BOOLEAN fLastPkt, lockHeld = TRUE, fAbort = FALSE,
fResetRetryTimer, fResendPkt = FALSE, fResetSendQueue = FALSE;
PNDIS_BUFFER NdisBuf, NdisBuf2;
ULONG BufLen = 0;
if (pIpxSpxHdr != NULL)
{
GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum);
GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum);
// Ack numbers should already be set in connection!
if (SPX2_CONN(pSpxConnFile))
{
switch (SPX_SEND_STATE(pSpxConnFile))
{
case SPX_SEND_RETRYWD:
// Did we receive an ack for pending data? If so, we goto
// idle and process the ack.
if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) &&
(UNSIGNED_GREATER_WITH_WRAP(
pSpxConnFile->scf_RecdAckNum,
pSendResd->sr_SeqNum)))
{
DBGPRINT(SEND, ERR,
("SpxConnProcessAck: Data acked RETRYWD %lx.%lx!\n",
pSpxConnFile, pSendResd->sr_SeqNum));
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_ERRORSTATE,
CFREF_VERIFY);
numDerefs++;
}
else
{
// Ok, we received an ack for our probe retry, goto
// renegotiate packet size.
// For this both sides must have negotiated size to begin with.
// If they did not, we go on to retrying the data packet.
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG))
{
pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT;
if ((ULONG)pSpxConnFile->scf_MaxPktSize <=
(SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE))
{
pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT);
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: %lx MIN RENEG SIZE\n",
pSpxConnFile));
}
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG);
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n",
pSpxConnFile));
}
else
{
DBGPRINT(SEND, ERR,
("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n",
pSpxConnFile,
pSpxConnFile->scf_Flags));
// Reset count to be
pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT);
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3);
}
}
break;
case SPX_SEND_RENEG:
// We better have a data packet in the list.
CTEAssert(pSpxConnFile->scf_SendSeqListHead);
#if DBG
if ((pIpxSpxHdr->hdr_ConnCtrl &
(SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) ==
(SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2))
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: %lx.%lx.%lx RENEGACK SEQNUM %lx ACKNUM %lx EXPSEQ %lx\n",
pSpxConnFile,
pIpxSpxHdr->hdr_ConnCtrl,
SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT),
seqNum,
ackNum,
(pSpxConnFile->scf_SendSeqListHead->sr_SeqNum + 1)));
}
#endif
// Verify we received an RR ack. If so, we set state to
// SEND_RETRY3. First repacketize if we need to.
if ((SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT)) &&
((pIpxSpxHdr->hdr_ConnCtrl &
(SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) ==
(SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)))
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: RENEG! NEW %lx.%lx!\n",
pSpxConnFile, pSpxConnFile->scf_MaxPktSize));
// Dont allow anymore reneg packet acks to be looked at.
SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT);
pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT);
// Also set the new send sequence number.
pSpxConnFile->scf_SendSeqNum =
(USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1);
// Get the max packet size we will really use. Retry timer
// could have sent other sizes by now, so we can't depend
// on whats set.
// Remember max packet size in connection.
GETSHORT2SHORT(
&pSpxConnFile->scf_MaxPktSize, &pIpxSpxHdr->hdr_NegSize);
// Basic sanity checking on the max packet size.
if (pSpxConnFile->scf_MaxPktSize < SPX_NEG_MIN)
pSpxConnFile->scf_MaxPktSize = SPX_NEG_MIN;
// Get ready to reset the send queue.
fResetSendQueue = TRUE;
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: RENEG DONE : RETRY3 %lx.%lx MP %lx!\n",
pSpxConnFile,
pSpxConnFile->scf_SendSeqNum,
pSpxConnFile->scf_MaxPktSize));
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3);
}
else
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: DUPLICATE RENEG ACK %lx!\n",
pSpxConnFile));
}
break;
case SPX_SEND_RETRY:
case SPX_SEND_RETRY2:
case SPX_SEND_RETRY3:
if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) &&
(UNSIGNED_GREATER_WITH_WRAP(
pSpxConnFile->scf_RecdAckNum,
pSendResd->sr_SeqNum)))
{
DBGPRINT(SEND, DBG,
("SpxConnProcessAck: Data acked %lx.%lx!\n",
pSpxConnFile, SPX_SEND_STATE(pSpxConnFile)));
#if DBG
if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3)
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: CONN RESTORED %lx.%lx!\n",
pSpxConnFile, pSendResd->sr_SeqNum));
}
#endif
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_ERRORSTATE,
CFREF_VERIFY);
numDerefs++;
}
break;
case SPX_SEND_WD:
// Ok, we received an ack for our watchdog. Done.
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
numDerefs++;
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_ERRORSTATE,
CFREF_VERIFY);
break;
default:
break;
}
#if DBG
if (seqNum != 0)
{
// We have a nack, which contains an implicit ack.
// Instead of nack processing, what we do is we resend a
// packet left unacked after ack processing. ONLY if we
// either enter the loop below (fResetRetryTimer is FALSE)
// or if seqNum is non-zero (SPX2 only NACK)
}
#endif
}
}
// Once our numbers are updated, we check to see if any of our packets
// have been acked.
fResetRetryTimer = TRUE;
while (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) &&
((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) ||
(SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) ||
fResetSendQueue) &&
(UNSIGNED_GREATER_WITH_WRAP(
pSpxConnFile->scf_RecdAckNum,
pSendResd->sr_SeqNum)))
{
// Reset retry timer
if (fResetRetryTimer)
{
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER))
{
// This will either successfully restart or not affect the timer
// if it is currently running.
SpxTimerCancelEvent(
pSpxConnFile->scf_RTimerId,
TRUE);
pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT);
}
fResetRetryTimer = FALSE;
}
// Update the retry seq num.
pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum;
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
pRequest = pSendResd->sr_Request;
#if DBG
if (fResetSendQueue)
{
DBGPRINT(SEND, ERR,
("SpxConnProcessAck: Data acked RENEG %lx.%lx!\n",
pSpxConnFile, SPX_SEND_STATE(pSpxConnFile)));
}
#endif
DBGPRINT(SEND, DBG,
("%lx Acked\n", pSendResd->sr_SeqNum));
DBGPRINT(SEND, DBG,
("SpxConnProcessAck: %lx Seq %lx Acked Sr %lx Req %lx %lx.%lx\n",
pSpxConnFile,
pSendResd->sr_SeqNum,
pSendResd,
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// If this packet is the last one comprising this request, remove request
// from queue. Calculate retry time.
fLastPkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_LASTPKT) != 0);
if ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) &&
((pSendResd->sr_State & SPX_SENDPKT_REXMIT) == 0) &&
((pSendResd->sr_SeqNum + 1) == pSpxConnFile->scf_RecdAckNum))
{
LARGE_INTEGER li, ntTime;
int value;
// This is the packet which is being acked. Adjust round trip
// timer.
li = pSendResd->sr_SentTime;
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;
}
//
// 55280
// calculate only if required.
//
if (0 == PARAM(CONFIG_DISABLE_RTT)) {
// Set new time
SpxCalculateNewT1(pSpxConnFile, value);
}
}
}
if (fLastPkt)
{
// Set status
REQUEST_STATUS(pRequest) = STATUS_SUCCESS;
RemoveEntryList(REQUEST_LINKAGE(pRequest));
// Remove creation reference
--(REQUEST_INFORMATION(pRequest));
DBGPRINT(SEND, DBG,
("SpxConnProcessAck: LASTSEQ # %lx for Req %lx with %lx.%lx\n",
pSendResd->sr_SeqNum,
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
CTEAssert(REQUEST_INFORMATION(pRequest) != 0);
}
// Dequeue the packet
CTEAssert((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0);
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)
{
// Dereference request for the dequeing of the packet
--(REQUEST_INFORMATION(pRequest));
DBGPRINT(SEND, DBG,
("SpxConnProcessAck: Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// Free the packet
SpxPktSendRelease(pPkt);
}
else
{
// Packet owned by IPX. What do we do now? Set acked pkt so request
// gets dereferenced in send completion. Note that the packet is already
// off the queue and is floating at this point.
DBGPRINT(SEND, DBG,
("SpxConnProcessAck: IPXOWNS Pkt %lx with %lx.%lx\n",
pPkt, pRequest, REQUEST_STATUS(pRequest)));
pSendResd->sr_State |= SPX_SENDPKT_ACKEDPKT;
}
if (SPX2_CONN(pSpxConnFile) &&
(REQUEST_MINOR_FUNCTION(pRequest) == TDI_DISCONNECT) &&
(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL))
{
SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ORDREL_ACKED);
// If we had received an ordrel in the meantime, we need
// to disconnect.
if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC))
{
fAbort = TRUE;
}
}
// All packets comprising a request have been acked!
if (REQUEST_INFORMATION(pRequest) == 0)
{
CTELockHandle lockHandleInter;
if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT)
{
PTDI_REQUEST_KERNEL_SEND pParam;
pParam = (PTDI_REQUEST_KERNEL_SEND)
REQUEST_PARAMETERS(pRequest);
REQUEST_INFORMATION(pRequest) = pParam->SendLength;
DBGPRINT(SEND, DBG,
("SpxSendComplete: QForComp Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// Request is done. Move to completion list.
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
InsertTailList(
&pSpxConnFile->scf_ReqDoneLinkage,
REQUEST_LINKAGE(pRequest));
// If connection is not already in recv queue, put it in
// there.
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
}
else
{
DBGPRINT(SEND, DBG,
("SpxSendComplete: DISC Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// Set the request in the connection, and deref for it.
InsertTailList(
&pSpxConnFile->scf_DiscLinkage,
REQUEST_LINKAGE(pRequest));
numDerefs++;
}
}
#if DBG
else if (fLastPkt)
{
DBGPRINT(RECEIVE, DBG,
("spxConnProcessAck: ReqFloating %lx.%lx\n",
pSpxConnFile, pRequest));
}
#endif
}
// See if we reset the send queue and repacketize.
if (fResetSendQueue)
{
// Reset send queue and repacketize only if pkts left unacked.
if (pSpxConnFile->scf_SendSeqListHead)
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: Resetting send queue %lx.%lx!\n",
pSpxConnFile, pSpxConnFile->scf_MaxPktSize));
spxConnResetSendQueue(pSpxConnFile);
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: Repacketizing %lx.%lx!\n",
pSpxConnFile, pSpxConnFile->scf_MaxPktSize));
SpxConnPacketize(pSpxConnFile, FALSE, lockHandle);
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
}
else
{
// We just go back to idle state now.
DBGPRINT(SEND, ERR,
("SpxConnProcessAck: All packets acked reneg ack! %lx.%lx!\n",
pSpxConnFile, pSpxConnFile->scf_MaxPktSize));
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
numDerefs++;
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_ERRORSTATE,
CFREF_VERIFY);
}
}
// See if we resend a packet.
if ((seqNum != 0) &&
!fAbort &&
((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) &&
(SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) &&
((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0))
{
PIPXSPX_HDR pSendHdr;
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
//
// Get the MDL that points to the IPX/SPX header. (the second one)
//
NdisQueryPacket(pPkt, NULL, NULL, &NdisBuf, NULL);
NdisGetNextBuffer(NdisBuf, &NdisBuf2);
NdisQueryBuffer(NdisBuf2, (PUCHAR) &pSendHdr, &BufLen);
#if OWN_PKT_POOLS
pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt +
NDIS_PACKET_SIZE +
sizeof(SPX_SEND_RESD) +
IpxInclHdrOffset);
#endif
// Set ack bit in packet. pSendResd initialized at beginning.
pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK;
// We are going to resend this packet
pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS |
SPX_SENDPKT_ACKREQ |
SPX_SENDPKT_REXMIT);
fResendPkt = TRUE;
}
// Push into packetize only if we received an ack. And if there arent any
// packets already waiting. Probably retransmit happening.
if (!fAbort &&
SPX_CONN_ACTIVE(pSpxConnFile) &&
(SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) &&
(pSpxConnFile->scf_ReqPkt != NULL) &&
(!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_PKTQ)) &&
((pSpxConnFile->scf_SendSeqListHead) == NULL) &&
(!SPX2_CONN(pSpxConnFile) ||
((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED) &&
(SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL))))
{
DBGPRINT(RECEIVE, DBG,
("spxConnProcessAck: Recd ack pktizng\n", pSpxConnFile));
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ);
SpxConnFileLockReference(pSpxConnFile, CFREF_PKTIZE);
CTEGetLock(&SpxGlobalQInterlock, &interLockHandle);
SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile);
CTEFreeLock(&SpxGlobalQInterlock, interLockHandle);
}
else if (fAbort)
{
// Set IDISC flag so Abortive doesnt reindicate.
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC);
spxConnAbortiveDisc(
pSpxConnFile,
STATUS_SUCCESS,
SPX_CALL_RECVLEVEL,
lockHandle,
FALSE); // [SA] bug #15249
lockHeld = FALSE;
}
if (lockHeld)
{
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
}
if (fResendPkt)
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessAck: Resend pkt on %lx.%lx\n",
pSpxConnFile, pSendResd->sr_SeqNum));
++SpxDevice->dev_Stat.DataFramesResent;
ExInterlockedAddLargeStatistic(
&SpxDevice->dev_Stat.DataFrameBytesResent,
pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE));
SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd);
}
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return(TRUE);
}
VOID
SpxConnProcessRenegReq(
IN PSPX_CONN_FILE pSpxConnFile,
IN PIPXSPX_HDR pIpxSpxHdr,
IN PIPX_LOCAL_TARGET pRemoteAddr,
IN CTELockHandle lockHandle
)
/*++
Routine Description:
!!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!!
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
USHORT seqNum, ackNum, allocNum, maxPktSize;
PSPX_SEND_RESD pSendResd;
PNDIS_PACKET pPkt = NULL;
// The remote sent us a renegotiate request. We need to send an ack back
// ONLY if we have not acked a data packet with that same sequence number.
// This is guaranteed by the fact that we will not accept the reneg request
// if we have already acked a data packet with the same seq num, as our
// receive seq number would be incremented already.
//
// Note that if we have pending send packets we may end up doing a reneg
// also.
GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum);
GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum);
GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum);
GETSHORT2SHORT(&maxPktSize, &pIpxSpxHdr->hdr_PktLen);
// If the received seq num is less than the expected receive sequence number
// we ignore this request.
if (!UNSIGNED_GREATER_WITH_WRAP(
seqNum,
pSpxConnFile->scf_RecvSeqNum) &&
(seqNum != pSpxConnFile->scf_RecvSeqNum))
{
DBGPRINT(SEND, DBG3,
("SpxConnProcessRenegReq: %lx ERROR RENSEQ %lx RECVSEQ %lx %lx\n",
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum));
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
return;
}
DBGPRINT(SEND, DBG3,
("SpxConnProcessRenegReq: %lx RENSEQ %lx RECVSEQ %lx MAXPKT %lx\n",
pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, maxPktSize));
// Set ack numbers for connection.
SPX_SET_ACKNUM(
pSpxConnFile, ackNum, allocNum);
SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr);
pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr;
// Set RenegAckAckNum before calling buildrrack. If a previous reneg
// request was received with a greater maxpktsize, send an ack with
// that maxpktsize.
if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD))
{
pSpxConnFile->scf_RenegAckAckNum = pSpxConnFile->scf_RecvSeqNum;
pSpxConnFile->scf_RenegMaxPktSize= maxPktSize;
SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD);
DBGPRINT(SEND, DBG3,
("SpxConnProcessRenegReq: %lx SENT ALLOC NUM CURRENT %lx\n",
pSpxConnFile,
pSpxConnFile->scf_SentAllocNum));
// Adjust sentallocnum now that recvseqnum might have moved up.
pSpxConnFile->scf_SentAllocNum +=
(seqNum - pSpxConnFile->scf_RenegAckAckNum);
DBGPRINT(SEND, DBG3,
("SpxConnProcessRenegReq: %lx SENT ALLOC NUM ADJUSTED %lx\n",
pSpxConnFile,
pSpxConnFile->scf_SentAllocNum));
}
// The recvseqnum for the reneg is always >= the renegackacknum.
pSpxConnFile->scf_RecvSeqNum = seqNum;
DBGPRINT(SEND, DBG3,
("SpxConnProcessRenegReq: %lx RESET RECVSEQ %lx SavedACKACK %lx\n",
pSpxConnFile,
pSpxConnFile->scf_RecvSeqNum,
pSpxConnFile->scf_RenegAckAckNum));
// Build and send an ack.
SpxPktBuildRrAck(
pSpxConnFile,
&pPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY,
pSpxConnFile->scf_RenegMaxPktSize);
if (pPkt != NULL)
{
SpxConnQueueSendPktTail(pSpxConnFile, pPkt);
}
#if DBG
else
{
// Log error
DBGPRINT(SEND, ERR,
("SpxConnSendRenegReqAck: Could not allocate!\n"));
}
#endif
// 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.
SpxConnProcessAck(pSpxConnFile, NULL, lockHandle);
if (pPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
// Send the packet
SPX_SENDACK(pSpxConnFile, pPkt, pSendResd);
}
return;
}
VOID
SpxConnProcessOrdRel(
IN PSPX_CONN_FILE pSpxConnFile,
IN CTELockHandle lockHandle
)
/*++
Routine Description:
!!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!!
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
PVOID pDiscHandlerCtx;
PTDI_IND_DISCONNECT pDiscHandler = NULL;
int numDerefs = 0;
PNDIS_PACKET pPkt = NULL;
BOOLEAN lockHeld = TRUE, fAbort = FALSE;
if (SPX_CONN_ACTIVE(pSpxConnFile))
{
if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ORDREL_ACKED)
{
fAbort = TRUE;
}
// Send an ack if one was asked for. And we are done with this pkt
// Update seq numbers and stuff.
SPX_SET_RECVNUM(pSpxConnFile, FALSE);
// Build and send an ack for this. Ordinary spx2 ack.
SpxPktBuildAck(
pSpxConnFile,
&pPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT,
FALSE,
0);
if (pPkt != NULL)
{
// We don't queue this pkt in as we have the ABORT flag set in
// the packet, which implies the pkt is already dequeued.
// SpxConnQueueSendPktTail(pSpxConnFile, pPkt);
// Reference conn for the pkt.
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
// Get disconnect handler if we have one. And have not indicated
// abortive disconnect on this connection to afd.
//
// Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD
//
if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC) &&
!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC))
{
// Yeah, we set the flag regardless of whether a handler is
// present.
pDiscHandler =pSpxConnFile->scf_AddrFile->saf_DiscHandler;
pDiscHandlerCtx=pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx;
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC);
}
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
// Indicate disconnect to afd.
if (pDiscHandler != NULL)
{
(*pDiscHandler)(
pDiscHandlerCtx,
pSpxConnFile->scf_ConnCtx,
0, // Disc data
NULL,
0, // Disc info
NULL,
TDI_DISCONNECT_RELEASE);
}
// We abort any receives here if !fAbort else we abort conn.
CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle);
if (fAbort)
{
// Set IDISC flag so Abortive doesnt reindicate.
SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC);
spxConnAbortiveDisc(
pSpxConnFile,
STATUS_SUCCESS,
SPX_CALL_RECVLEVEL,
lockHandle,
FALSE); // [SA] bug #15249
lockHeld = FALSE;
}
else
{
// Go through and kill all pending requests.
spxConnAbortRecvs(
pSpxConnFile,
STATUS_REMOTE_DISCONNECT,
SPX_CALL_RECVLEVEL,
lockHandle);
lockHeld = FALSE;
}
}
if (lockHeld)
{
CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle);
}
if (pPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
// Send the packet
SPX_SENDACK(pSpxConnFile, pPkt, pSendResd);
}
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
VOID
SpxConnProcessIDisc(
IN PSPX_CONN_FILE pSpxConnFile,
IN CTELockHandle lockHandle
)
/*++
Routine Description:
!!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!!
Arguments:
pSpxConnFile - Pointer to a transport address file object.
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
PNDIS_PACKET pPkt = NULL;
PNDIS_BUFFER NdisBuf, NdisBuf2;
ULONG BufLen = 0;
SPX_SET_RECVNUM(pSpxConnFile, FALSE);
// Build and send an ack for the idisc. Need to modify data type
// and reset sys bit on ack.
// BUG #12344 - Fixing this led to the problem where we queue in
// the pkt below, but AbortSends could already have been called
// => this packet stays on queue without a ref, conn gets freed
// underneath, and in the sendcomplete we crash when this send
// completes.
//
// Fix is to setup this pkt as a aborted pkt to start with.
SpxPktBuildAck(
pSpxConnFile,
&pPkt,
SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT,
FALSE,
0);
if (pPkt != NULL)
{
PIPXSPX_HDR pSendHdr;
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
//
// Get the MDL that points to the IPX/SPX header. (the second one)
//
NdisQueryPacket(pPkt, NULL, NULL, &NdisBuf, NULL);
NdisGetNextBuffer(NdisBuf, &NdisBuf2);
NdisQueryBuffer(NdisBuf2, (PUCHAR) &pSendHdr, &BufLen);
#if OWN_PKT_POOLS
pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt +
NDIS_PACKET_SIZE +
sizeof(SPX_SEND_RESD) +
IpxInclHdrOffset);
#endif
pSendHdr->hdr_ConnCtrl &= ~SPX_CC_SYS;
pSendHdr->hdr_DataType = SPX2_DT_IDISC_ACK;
// We don't queue this pkt in as we have the ABORT flag set in
// the packet, which implies the pkt is already dequeued.
// SpxConnQueueSendPktTail(pSpxConnFile, pPkt);
// Reference conn for the pkt.
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
// We better not have any received pkts, we ignore disconnect
// pkts when that happens.
CTEAssert(pSpxConnFile->scf_RecvListTail == NULL);
CTEAssert(pSpxConnFile->scf_RecvListHead == NULL);
#if DBG
if (pSpxConnFile->scf_SendSeqListHead != NULL)
{
DBGPRINT(CONNECT, DBG1,
("SpxConnDiscPacket: DATA/DISC %lx.%lx.%lx\n",
pSpxConnFile,
pSpxConnFile->scf_SendListHead,
pSpxConnFile->scf_SendSeqListHead));
}
#endif
// Call abortive disconnect on connection.
//
// [SA] bug #15249
// This is an informed disconnect, hence pass DISCONNECT_RELEASE to AFD (TRUE in last param)
//
//
// We pass true only in the case of an SPX connection. SPX2 connections follow the
// exact semantics of Informed Disconnect.
//
if (!SPX2_CONN(pSpxConnFile)) {
spxConnAbortiveDisc(
pSpxConnFile,
STATUS_REMOTE_DISCONNECT,
SPX_CALL_RECVLEVEL,
lockHandle,
TRUE);
} else {
spxConnAbortiveDisc(
pSpxConnFile,
STATUS_REMOTE_DISCONNECT,
SPX_CALL_RECVLEVEL,
lockHandle,
FALSE);
}
if (pPkt != NULL)
{
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
// Send the packet
SPX_SENDACK(pSpxConnFile, pPkt, pSendResd);
}
return;
}
VOID
spxConnResetSendQueue(
IN PSPX_CONN_FILE pSpxConnFile
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PSPX_SEND_RESD pSendResd;
PREQUEST pRequest;
PNDIS_PACKET pPkt;
pSendResd = pSpxConnFile->scf_SendSeqListHead;
CTEAssert(pSendResd != NULL);
pRequest = pSendResd->sr_Request;
// Reset the current send request values
pSpxConnFile->scf_ReqPkt = pSendResd->sr_Request;
pSpxConnFile->scf_ReqPktOffset = pSendResd->sr_Offset;
pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA;
if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT)
{
PTDI_REQUEST_KERNEL_SEND pParam;
pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest);
DBGPRINT(SEND, DBG3,
("spxConnResetSendQueue: %lx.%lx.%lx Reset SEND Req to %lx.%lx\n",
pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2,
pRequest, pParam->SendLength));
// Set parameters in connection for another go. Size parameter is
// original size - offset at this point.
pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags;
pSpxConnFile->scf_ReqPktSize = pParam->SendLength -
pSpxConnFile->scf_ReqPktOffset;
}
else
{
PTDI_REQUEST_KERNEL_DISCONNECT pParam;
DBGPRINT(SEND, ERR,
("spxConnResetSendQueue: %lx.%lx.%lx Reset DISC Req to %lx\n",
pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2,
pRequest));
DBGPRINT(SEND, ERR,
("spxConnResetSendQueue: DISC Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
pParam =
(PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest);
pSpxConnFile->scf_ReqPktOffset = 0;
pSpxConnFile->scf_ReqPktSize = 0;
pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC;
if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE)
{
pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL;
}
}
DBGPRINT(SEND, DBG3,
("spxConnResetSendQueue: Seq Num for %lx is now %lx\n",
pSpxConnFile, pSpxConnFile->scf_SendSeqNum));
// When we are trying to abort a pkt and it is in use by ipx, we simply let
// it float.
do
{
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0);
pRequest = pSendResd->sr_Request;
CTEAssert(REQUEST_INFORMATION(pRequest) != 0);
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)
{
if (--(REQUEST_INFORMATION(pRequest)) == 0)
{
DBGPRINT(SEND, DBG,
("SpxSendComplete: DISC Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
KeBugCheck(0);
}
// Free the packet
SpxPktSendRelease(pPkt);
}
else
{
// We let send completion know that this packet is to be aborted.
pSendResd->sr_State |= SPX_SENDPKT_ABORT;
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
} while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL);
return;
}
VOID
spxConnAbortSendPkt(
IN PSPX_CONN_FILE pSpxConnFile,
IN PSPX_SEND_RESD pSendResd,
IN SPX_CALL_LEVEL CallLevel,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
Called to abort either a sequenced or a non-sequenced packet ONLY from
send completion.
Arguments:
Return Value:
--*/
{
LIST_ENTRY ReqList, *p;
PREQUEST pRequest;
PNDIS_PACKET pPkt;
int numDerefs = 0;
InitializeListHead(&ReqList);
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
if ((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0)
{
pRequest = pSendResd->sr_Request;
CTEAssert(REQUEST_INFORMATION(pRequest) != 0);
CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0);
if (--(REQUEST_INFORMATION(pRequest)) == 0)
{
// Remove request from list its on
// BUG #11626 - request is already removed from list.
// RemoveEntryList(REQUEST_LINKAGE(pRequest));
if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT)
{
DBGPRINT(SEND, DBG,
("SpxSendAbort: QForComp Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
if (CallLevel == SPX_CALL_RECVLEVEL)
{
CTELockHandle lockHandleInter;
// Request is done. Move to completion list.
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
InsertTailList(
&pSpxConnFile->scf_ReqDoneLinkage,
REQUEST_LINKAGE(pRequest));
// If connection is not already in recv queue, put it in
// there.
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
}
else
{
InsertTailList(
&ReqList,
REQUEST_LINKAGE(pRequest));
}
}
else
{
DBGPRINT(SEND, DBG,
("SpxSendComplete: DISC Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// Set the request in the connection, and deref for it.
InsertTailList(
&pSpxConnFile->scf_DiscLinkage,
REQUEST_LINKAGE(pRequest));
numDerefs++;
}
}
}
// Release
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
// Free the packet
SpxPktSendRelease(pPkt);
SpxConnFileDereference(pSpxConnFile, CFREF_ABORTPKT);
if (!IsListEmpty(&ReqList))
{
p = RemoveHeadList(&ReqList);
pRequest = LIST_ENTRY_TO_REQUEST(p);
SpxCompleteRequest(pRequest);
numDerefs++;
}
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
VOID
spxConnAbortSends(
IN PSPX_CONN_FILE pSpxConnFile,
IN NTSTATUS Status,
IN SPX_CALL_LEVEL CallLevel,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
LIST_ENTRY ReqList, *p;
PSPX_SEND_RESD pSendResd;
PREQUEST pRequest;
PNDIS_PACKET pPkt;
int numDerefs = 0;
InitializeListHead(&ReqList);
// We better be in disconnect state, abortive/informed/orderly initiate.
CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN);
// Reset the current send request values
pSpxConnFile->scf_ReqPkt = NULL;
pSpxConnFile->scf_ReqPktOffset = 0;
pSpxConnFile->scf_ReqPktSize = 0;
pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA;
// First go through the non-seq pkt queue.Just set abort flag if owned by ipx
while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL)
{
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) == 0);
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)
{
// Free the packet
SpxPktSendRelease(pPkt);
}
else
{
// Set abort flag and reference conn for the pkt if its not already.
// We only do this check for the non-sequenced packets.
// BUG #12344 (see SpxRecvDiscPacket())
if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) == 0)
{
pSendResd->sr_State |= SPX_SENDPKT_ABORT;
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
}
}
// When we are trying to abort a pkt and it is in use by ipx, we simply let
// it float.
while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL)
{
pPkt = (PNDIS_PACKET)CONTAINING_RECORD(
pSendResd, NDIS_PACKET, ProtocolReserved);
CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0);
pRequest = pSendResd->sr_Request;
CTEAssert(REQUEST_INFORMATION(pRequest) != 0);
SpxConnDequeueSendPktLock(pSpxConnFile, pPkt);
if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)
{
if (--(REQUEST_INFORMATION(pRequest)) == 0)
{
// Remove request from list its on
RemoveEntryList(REQUEST_LINKAGE(pRequest));
// Set status
REQUEST_STATUS(pRequest) = Status;
REQUEST_INFORMATION(pRequest) = 0;
if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT)
{
DBGPRINT(SEND, DBG,
("SpxSendAbort: QForComp Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
if (CallLevel == SPX_CALL_RECVLEVEL)
{
CTELockHandle lockHandleInter;
// Request is done. Move to completion list.
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
InsertTailList(
&pSpxConnFile->scf_ReqDoneLinkage,
REQUEST_LINKAGE(pRequest));
// If connection is not already in recv queue, put it in
// there.
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
}
else
{
InsertTailList(
&ReqList,
REQUEST_LINKAGE(pRequest));
}
}
else
{
DBGPRINT(SEND, DBG,
("SpxSendComplete: DISC Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// Set the request in the connection, and deref for it.
InsertTailList(
&pSpxConnFile->scf_DiscLinkage,
REQUEST_LINKAGE(pRequest));
numDerefs++;
}
}
// Free the packet
SpxPktSendRelease(pPkt);
}
else
{
// We let send completion know that this packet is to be aborted.
pSendResd->sr_State |= SPX_SENDPKT_ABORT;
SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT);
}
}
// If retry timer state is on, then we need to reset and deref.
if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) &&
(SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) &&
(SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD))
{
DBGPRINT(SEND, DBG1,
("spxConnAbortSends: When SEND ERROR STATE %lx.%lx\n",
pSpxConnFile, SPX_SEND_STATE(pSpxConnFile)));
SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE);
SpxConnFileTransferReference(
pSpxConnFile,
CFREF_ERRORSTATE,
CFREF_VERIFY);
numDerefs++;
}
// Remove creation references on all sends.
if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage))
{
p = pSpxConnFile->scf_ReqLinkage.Flink;
while (p != &pSpxConnFile->scf_ReqLinkage)
{
pRequest = LIST_ENTRY_TO_REQUEST(p);
p = p->Flink;
// Remove request from list its on. Its complete or abort list for it.
RemoveEntryList(REQUEST_LINKAGE(pRequest));
// Set status
REQUEST_STATUS(pRequest) = Status;
DBGPRINT(SEND, DBG1,
("SpxSendAbort: %lx Aborting Send Request %lx with %lx.%lx\n",
pSpxConnFile, pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
if (--(REQUEST_INFORMATION(pRequest)) == 0)
{
if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT)
{
DBGPRINT(SEND, DBG,
("SpxSendAbort: QForComp Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
if (CallLevel == SPX_CALL_RECVLEVEL)
{
CTELockHandle lockHandleInter;
// Request is done. Move to completion list.
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
InsertTailList(
&pSpxConnFile->scf_ReqDoneLinkage,
REQUEST_LINKAGE(pRequest));
// If connection is not already in recv queue, put it in
// there.
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
}
else
{
InsertTailList(
&ReqList,
REQUEST_LINKAGE(pRequest));
}
}
else
{
DBGPRINT(SEND, DBG1,
("SpxSendComplete: DISC Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
// Set the request in the connection, and deref for it.
InsertTailList(
&pSpxConnFile->scf_DiscLinkage,
REQUEST_LINKAGE(pRequest));
numDerefs++;
}
}
#if DBG
else
{
// Let it float,
DBGPRINT(SEND, DBG1,
("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n",
pSpxConnFile, pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
}
#endif
}
}
// Release
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
while (!IsListEmpty(&ReqList))
{
p = RemoveHeadList(&ReqList);
pRequest = LIST_ENTRY_TO_REQUEST(p);
SpxCompleteRequest(pRequest);
numDerefs++;
}
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
VOID
spxConnAbortRecvs(
IN PSPX_CONN_FILE pSpxConnFile,
IN NTSTATUS Status,
IN SPX_CALL_LEVEL CallLevel,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
LIST_ENTRY ReqList, *p;
PREQUEST pRequest;
PSPX_RECV_RESD pRecvResd;
PNDIS_PACKET pNdisPkt;
PNDIS_BUFFER pNdisBuffer;
PBYTE pData;
ULONG dataLen;
int numDerefs = 0;
InitializeListHead(&ReqList);
// We better be in disconnect state, abortive/informed/orderly initiate.
// Reset the current receive request values
pSpxConnFile->scf_CurRecvReq = NULL;
pSpxConnFile->scf_CurRecvOffset = 0;
pSpxConnFile->scf_CurRecvSize = 0;
// If we have any buffered data, abort it.
// Buffered data that is 0 bytes long (only eom) may not have a ndis
// buffer associated with it.
while ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL)
{
if ((pSpxConnFile->scf_RecvListHead = pRecvResd->rr_Next) == NULL)
{
pSpxConnFile->scf_RecvListTail = NULL;
}
pNdisPkt = (PNDIS_PACKET)
CONTAINING_RECORD(pRecvResd, NDIS_PACKET, ProtocolReserved);
DBGPRINT(RECEIVE, DBG1,
("spxConnAbortRecvs: %lx in bufferlist on %lx\n",
pSpxConnFile, pNdisPkt));
NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer);
if (pNdisBuffer != NULL)
{
NdisQueryBuffer(pNdisBuffer, &pData, &dataLen);
CTEAssert(pData != NULL);
CTEAssert((LONG)dataLen >= 0);
SpxFreeMemory(pData);
NdisFreeBuffer(pNdisBuffer);
}
// Packet consumed. Free it up.
numDerefs++;
// Free the ndis packet
SpxPktRecvRelease(pNdisPkt);
}
// If packets are on this queue, they are waiting for transfer data to
// complete. Can't do much about that, just go and remove creation refs
// on the receives.
if (!IsListEmpty(&pSpxConnFile->scf_RecvLinkage))
{
p = pSpxConnFile->scf_RecvLinkage.Flink;
while (p != &pSpxConnFile->scf_RecvLinkage)
{
pRequest = LIST_ENTRY_TO_REQUEST(p);
p = p->Flink;
// Remove request from list its on
RemoveEntryList(REQUEST_LINKAGE(pRequest));
// Set status
REQUEST_STATUS(pRequest) = Status;
DBGPRINT(RECEIVE, DBG1,
("SpxRecvAbort: Aborting Recv Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
if (REQUEST_INFORMATION(pRequest) == 0)
{
DBGPRINT(RECEIVE, DBG,
("SpxRecvAbort: QForComp Request %lx with %lx.%lx\n",
pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
if (CallLevel == SPX_CALL_RECVLEVEL)
{
CTELockHandle lockHandleInter;
// Request is done. Move to completion list.
CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter);
InsertTailList(
&pSpxConnFile->scf_RecvDoneLinkage,
REQUEST_LINKAGE(pRequest));
// If connection is not already in recv queue, put it in
// there.
SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile);
CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter);
}
else
{
InsertTailList(
&ReqList,
REQUEST_LINKAGE(pRequest));
}
}
#if DBG
else
{
// Let it float,
DBGPRINT(SEND, DBG1,
("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n",
pSpxConnFile, pRequest, REQUEST_STATUS(pRequest),
REQUEST_INFORMATION(pRequest)));
}
#endif
}
}
// Release
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
while (!IsListEmpty(&ReqList))
{
p = RemoveHeadList(&ReqList);
pRequest = LIST_ENTRY_TO_REQUEST(p);
numDerefs++;
SpxCompleteRequest(pRequest);
}
while (numDerefs-- > 0)
{
SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY);
}
return;
}
#if 0
VOID
spxConnResendPkts(
IN PSPX_CONN_FILE pSpxConnFile,
IN CTELockHandle LockHandleConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_PACKET pPkt;
PSPX_SEND_RESD pSendResd;
USHORT startSeqNum;
BOOLEAN fLockHeld = TRUE, fDone = FALSE;
pSendResd = pSpxConnFile->scf_SendSeqListHead;
if (pSendResd)
{
startSeqNum = pSendResd->sr_SeqNum;
DBGPRINT(SEND, DBG,
("spxConnResendPkts: StartSeqNum %lx for resend on %lx\n",
startSeqNum, pSpxConnFile));
while (spxConnGetPktBySeqNum(pSpxConnFile, startSeqNum++, &pPkt))
{
CTEAssert(pPkt != NULL);
pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved);
if (!(pSendResd->sr_State & SPX_SENDPKT_IPXOWNS))
{
DBGPRINT(SEND, DBG,
("spxConnResendPkts: Pkt %lx.%lx resent on %lx\n",
pPkt, (startSeqNum - 1), pSpxConnFile));
// We are going to send this packet
pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS |
SPX_SENDPKT_REXMIT);
}
else
{
DBGPRINT(SEND, DBG,
("spxConnResendPkts: Pkt %lx.%lx owned by ipx on %lx\n",
pPkt, (startSeqNum - 1), pSpxConnFile));
break;
}
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
fLockHeld = FALSE;
// If pkt has the ack bit set, we break.
fDone = ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) != 0);
// Send the packet
SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd);
if (fDone)
{
break;
}
CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn);
fLockHeld = TRUE;
}
}
if (fLockHeld)
{
CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn);
}
return;
}
#endif