From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/tdi/isnp/spx/spxcpkt.c | 4131 +++++++++++++++++++++++++++++++++++ 1 file changed, 4131 insertions(+) create mode 100644 private/ntos/tdi/isnp/spx/spxcpkt.c (limited to 'private/ntos/tdi/isnp/spx/spxcpkt.c') diff --git a/private/ntos/tdi/isnp/spx/spxcpkt.c b/private/ntos/tdi/isnp/spx/spxcpkt.c new file mode 100644 index 000000000..deb185201 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxcpkt.c @@ -0,0 +1,4131 @@ +/*++ + +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 +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) + { + CTEAssert(acceptIrp != NULL); + + // 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); + } + + // 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, 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, 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; + } + + // !!!BUGBUG!!! + // 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 == 0) { +#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; + + 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; + } + + // 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); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // 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; + + 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); + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + 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(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 -- cgit v1.2.3