/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: spxconn.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) 5-July-1995 Bug fixes - tagged [SA] --*/ #include "precomp.h" extern POBJECT_TYPE *IoFileObjectType; #pragma hdrstop #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, SpxConnOpen) #endif // Define module number for event logging entries #define FILENUM SPXCONN VOID SpxFindRouteComplete ( IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, IN BOOLEAN FoundRoute); NTSTATUS SpxConnOpen( IN PDEVICE pDevice, IN CONNECTION_CONTEXT ConnCtx, IN PREQUEST pRequest ) /*++ Routine Description: This routine is used to create a connection object and associate the passed ConnectionContext with it. Arguments: pConnCtx - The TDI ConnectionContext to be associated with object Return Value: STATUS_SUCCESS if connection was successfully opened Error otherwise. --*/ { NTSTATUS status = STATUS_SUCCESS; PSPX_CONN_FILE pSpxConnFile; #ifdef ISN_NT PIRP Irp = (PIRP)pRequest; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); #endif // Allocate memory for a connection object if ((pSpxConnFile = SpxAllocateZeroedMemory(sizeof(SPX_CONN_FILE))) == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } // Initialize values pSpxConnFile->scf_Flags = 0; pSpxConnFile->scf_Type = SPX_CONNFILE_SIGNATURE; pSpxConnFile->scf_Size = sizeof (SPX_CONN_FILE); CTEInitLock (&pSpxConnFile->scf_Lock); pSpxConnFile->scf_ConnCtx = ConnCtx; pSpxConnFile->scf_Device = pDevice; // Initialize list for requests. InitializeListHead(&pSpxConnFile->scf_ReqLinkage); InitializeListHead(&pSpxConnFile->scf_RecvLinkage); InitializeListHead(&pSpxConnFile->scf_RecvDoneLinkage); InitializeListHead(&pSpxConnFile->scf_ReqDoneLinkage); InitializeListHead(&pSpxConnFile->scf_DiscLinkage); #ifdef ISN_NT // easy backlink to file object. pSpxConnFile->scf_FileObject = IrpSp->FileObject; #endif // For connections we go from 0->0 with flags indicating if a close // happened. pSpxConnFile->scf_RefCount = 0; // Insert into a global connection list. spxConnInsertIntoGlobalList(pSpxConnFile); #if DBG // Initialize this to 0xFFFF so we dont hit assert on first packet. pSpxConnFile->scf_PktSeqNum = 0xFFFF; #endif // Set values in the request. REQUEST_OPEN_CONTEXT(pRequest) = (PVOID)pSpxConnFile; REQUEST_OPEN_TYPE(pRequest) = (PVOID)TDI_CONNECTION_FILE; DBGPRINT(CREATE, INFO, ("SpxConnOpen: Opened %lx\n", pSpxConnFile)); ASSERT(status == STATUS_SUCCESS); return(status); } NTSTATUS SpxConnCleanup( IN PDEVICE Device, IN PREQUEST Request ) /*++ Routine Description: Arguments: Request - the close request. Return Value: STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request does not point to a real connection --*/ { NTSTATUS status; CTELockHandle lockHandle; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); // Verify connection file if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { DBGBRK(FATAL); return (status); } DBGPRINT(CREATE, INFO, ("SpxConnFileCleanup: %lx.%lx when %lx\n", pSpxConnFile, Request, pSpxConnFile->scf_RefCount)); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); pSpxConnFile->scf_CleanupReq = Request; CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); // We have a reference, so it wont go to zero until stop returns. Therefore // deref can expect flag to be set. SpxConnStop(pSpxConnFile); SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); // // If this is a connection which is waiting for a local disconnect, // deref it since we dont expect a disconnect after a cleanup. // CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); KdPrint(("Deref for DISCWAIT on connfile: %lx\n", pSpxConnFile)); SpxConnFileDereference (pSpxConnFile, CFREF_DISCWAITSPX); } else { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); } return STATUS_PENDING; } NTSTATUS SpxConnClose( IN PDEVICE Device, IN PREQUEST Request ) /*++ Routine Description: Arguments: Request - the close request. Return Value: STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request does not point to a real connection --*/ { NTSTATUS status; CTELockHandle lockHandle; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); // Verify connection file if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { DBGBRK(FATAL); return (status); } DBGPRINT(CREATE, INFO, ("SpxConnFileClose: %lx when %lx\n", pSpxConnFile, pSpxConnFile->scf_RefCount)); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); pSpxConnFile->scf_CloseReq = Request; SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); return STATUS_PENDING; } VOID SpxConnStop( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: !!!Connection must have a reference when this is called!!! Arguments: Return Value: --*/ { CTELockHandle lockHandle; DBGPRINT(CREATE, INFO, ("SpxConnFileStop: %lx when %lx.%lx\n", pSpxConnFile, pSpxConnFile->scf_RefCount, pSpxConnFile->scf_Flags)); // Call disconnect and disassociate CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) { SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); if (!SPX_CONN_IDLE(pSpxConnFile)) { spxConnAbortiveDisc( pSpxConnFile, STATUS_LOCAL_DISCONNECT, SPX_CALL_TDILEVEL, lockHandle, FALSE); // [SA] Bug #15249 } else { // Disassociate if we are associated. spxConnDisAssoc(pSpxConnFile, lockHandle); } // Lock released at this point. } else { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); } return; } NTSTATUS SpxConnAssociate( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: This routine moves the connection from the device list to the inactive connection list in the address of the address file specified. The address file is pointed to by the connection and is referenced for the associate. Arguments: Return Value: --*/ { NTSTATUS status; PSPX_ADDR_FILE pSpxAddrFile; CTELockHandle lockHandle1, lockHandle2; BOOLEAN derefAddr = FALSE, derefConn = FALSE; PFILE_OBJECT pFileObj = NULL; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); HANDLE AddrObjHandle = ((PTDI_REQUEST_KERNEL_ASSOCIATE)(REQUEST_PARAMETERS(pRequest)))->AddressHandle; do { // Get the handle to the address object from the irp and map it to // the corres. file object. status = ObReferenceObjectByHandle( AddrObjHandle, 0, *IoFileObjectType, pRequest->RequestorMode, (PVOID *)&pFileObj, NULL); if (!NT_SUCCESS(status)) break; if (pFileObj->DeviceObject != SpxDevice->dev_DevObj || pFileObj->FsContext2 != (PVOID)TDI_TRANSPORT_ADDRESS_FILE ) { ObDereferenceObject(pFileObj); status = STATUS_INVALID_HANDLE; break; } pSpxAddrFile = pFileObj->FsContext; // ASSERT(pFileObj->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); // Verify address file/connection file if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) { ObDereferenceObject(pFileObj); break; } derefAddr = TRUE; if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { ObDereferenceObject(pFileObj); break; } derefConn = TRUE; // Grab the addres file lock, then the connection lock for associate. CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle1); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); if (!SPX_CONN_FLAG(pSpxConnFile, (SPX_CONNFILE_CLOSING | SPX_CONNFILE_STOPPING | SPX_CONNFILE_ASSOC)) && !(pSpxAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING)) { derefAddr = FALSE; SpxAddrFileTransferReference( pSpxAddrFile, AFREF_VERIFY, AFREF_CONN_ASSOC); // Queue in the inactive list in the address pSpxConnFile->scf_Next = pSpxAddrFile->saf_Addr->sa_InactiveConnList; pSpxAddrFile->saf_Addr->sa_InactiveConnList = pSpxConnFile; // Queue in the assoc list in the address file pSpxConnFile->scf_AssocNext = pSpxAddrFile->saf_AssocConnList; pSpxAddrFile->saf_AssocConnList = pSpxConnFile; // Remember the addrfile in the connection pSpxConnFile->scf_AddrFile = pSpxAddrFile; SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); status = STATUS_SUCCESS; DBGPRINT(CREATE, INFO, ("SpxConnAssociate: %lx with address file %lx\n", pSpxConnFile, pSpxAddrFile)); } else { status = STATUS_INVALID_PARAMETER; } CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandle2); CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandle1); // Dereference the file object corres. to the address object ObDereferenceObject(pFileObj); } while (FALSE); if (derefAddr) { SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); } if (derefConn) { SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return(status); } NTSTATUS SpxConnDisAssociate( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; CTELockHandle lockHandle; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); // Verify connection file if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) return (status); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); if (!SPX_CONN_IDLE(pSpxConnFile) || (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) { status = STATUS_INVALID_CONNECTION; } CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); // Unlink it if ok. if (NT_SUCCESS(status)) { SpxConnStop(pSpxConnFile); } SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); return(status); } NTSTATUS spxConnDisAssoc( IN PSPX_CONN_FILE pSpxConnFile, IN CTELockHandle LockHandleConn ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status = STATUS_SUCCESS; CTELockHandle lockHandleAddr; PSPX_ADDR_FILE pSpxAddrFile; if (SPX_CONN_IDLE(pSpxConnFile) && (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) { pSpxAddrFile = pSpxConnFile->scf_AddrFile; } else { status = STATUS_INVALID_CONNECTION; } CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); // Unlink it if ok. if (NT_SUCCESS(status)) { CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); // Check again as we had released the lock if (SPX_CONN_IDLE(pSpxConnFile) && (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) { pSpxConnFile->scf_AddrFile = NULL; SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); // Dequeue the connection from the address file spxConnRemoveFromAssocList( &pSpxAddrFile->saf_AssocConnList, pSpxConnFile); // Dequeue the connection file from the address list. It must be // in the inactive list. spxConnRemoveFromList( &pSpxAddrFile->saf_Addr->sa_InactiveConnList, pSpxConnFile); } else { status = STATUS_INVALID_CONNECTION; } CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); DBGPRINT(CREATE, INFO, ("SpxConnDisAssociate: %lx from address file %lx\n", pSpxConnFile, pSpxAddrFile)); if (NT_SUCCESS(status)) { // Remove reference on address for this association. SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); } } return(status); } NTSTATUS SpxConnConnect( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: We need to have another timer that will be started on the connection if the tdi client indicated a timeout value. 0 -> we do not start such a timer, -1 implies, we let our connection timeout values do their thing. Any other value will forcibly shutdown the connect process, when the timer fires. Return Value: --*/ { PTDI_REQUEST_KERNEL_CONNECT pParam; TDI_ADDRESS_IPX UNALIGNED * pTdiAddr; PNDIS_PACKET pCrPkt; NTSTATUS status; PIPXSPX_HDR pIpxSpxHdr; PSPX_FIND_ROUTE_REQUEST pFindRouteReq; CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; PSPX_ADDR pSpxAddr; BOOLEAN locksHeld = TRUE; PNDIS_BUFFER NdisBuf, NdisBuf2; ULONG BufLen =0; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); // Unpack the connect parameters pParam = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(pRequest); pTdiAddr= SpxParseTdiAddress( pParam->RequestConnectionInformation->RemoteAddress); DBGPRINT(CONNECT, DBG, ("SpxConnConnect: Remote SOCKET %lx on %lx.%lx\n", pTdiAddr->Socket, pSpxConnFile, pRequest)); // Check if the connection is in a valid state if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { return(status); } do { if ((pFindRouteReq = (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; } // Check if connection is associated, if so, the association cannot // go away until the reference above is removed. So we are safe in // releasing the lock. CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); status = STATUS_INVALID_ADDRESS; if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) { status = STATUS_SUCCESS; pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; // See if this connection is to be a spx2 connection. SPX_CONN_RESETFLAG(pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG | SPX_CONNFILE_STREAM)); if ((PARAM(CONFIG_DISABLE_SPX2) == 0) && (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2)) { DBGPRINT(CONNECT, DBG, ("SpxConnConnect: SPX2 requested %lx\n", pSpxConnFile)); SPX_CONN_SETFLAG( pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); } if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) { DBGPRINT(CONNECT, DBG, ("SpxConnConnect: SOCK_STREAM requested %lx\n", pSpxConnFile)); SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); } if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) { DBGPRINT(CONNECT, ERR, ("SpxConnConnect: NOACKWAIT requested %lx\n", pSpxConnFile)); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); } if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) { DBGPRINT(CONNECT, ERR, ("spxConnHandleConnReq: IPXHDR requested %lx\n", pSpxConnFile)); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); } } CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); } while (FALSE); if (!NT_SUCCESS(status)) { DBGPRINT(CONNECT, ERR, ("SpxConnConnect: Failed %lx\n", status)); if (pFindRouteReq) { SpxFreeMemory(pFindRouteReq); } return(status); } CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(&pSpxAddr->sa_Lock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); locksHeld = TRUE; status = STATUS_INVALID_CONNECTION; if (SPX_CONN_IDLE(pSpxConnFile) && ((pSpxConnFile->scf_LocalConnId = spxConnGetId()) != 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); } SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_CONNECTING); pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) { PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; } pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); // Move connection from inactive list to non-inactive list. if (!NT_SUCCESS(spxConnRemoveFromList( &pSpxAddr->sa_InactiveConnList, pSpxConnFile))) { // This should never happen! KeBugCheck(0); } // Put connection in the non-inactive list. Connection id must be set. SPX_INSERT_ADDR_ACTIVE( pSpxAddr, pSpxConnFile); // Insert in the global connection tree on device spxConnInsertIntoGlobalActiveList( pSpxConnFile); // Store the remote address in the connection. // !!NOTE!! We get both the network/socket in network form. *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) = *((UNALIGNED ULONG *)(&pTdiAddr->NetworkAddress)); RtlCopyMemory( pSpxConnFile->scf_RemAddr+4, pTdiAddr->NodeAddress, 6); *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)) = *((UNALIGNED USHORT *)(&pTdiAddr->Socket)); // Ok, we are all set, build connect packet, queue it into connection // with the connect request. Ndis buffer already describes this memory // Build IPX header. pCrPkt = NULL; // so it knows to allocate one. SpxPktBuildCr( pSpxConnFile, pSpxAddr, &pCrPkt, SPX_SENDPKT_IDLE, SPX2_CONN(pSpxConnFile)); if (pCrPkt != NULL) { // Remember the request in the connection // // Dont queue for the failure case since we complete it in SpxInternalDispatch. // InsertTailList( &pSpxConnFile->scf_ReqLinkage, REQUEST_LINKAGE(pRequest)); SpxConnQueueSendPktTail(pSpxConnFile, pCrPkt); // // Get the MDL that points to the IPX/SPX header. (the second one) // NdisQueryPacket(pCrPkt, NULL, NULL, &NdisBuf, NULL); NdisGetNextBuffer(NdisBuf, &NdisBuf2); NdisQueryBufferSafe(NdisBuf2, (PUCHAR) &pIpxSpxHdr, &BufLen, HighPagePriority); ASSERT(pIpxSpxHdr != NULL); // Can't fail since it is already mapped #if OWN_PKT_POOLS pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + NDIS_PACKET_SIZE + sizeof(SPX_SEND_RESD) + IpxInclHdrOffset); #endif // Initialize the find route request *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet); // // [SA] Bug #15094 // We need to also pass in the node number to IPX so that IPX can // compare the node addresses to determine the proper WAN NICid // // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pIpxSpxHdr->hdr_DestNode, 6) ; *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNode); *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= *((UNALIGNED USHORT *)(pIpxSpxHdr->hdr_DestNode+4)); DBGPRINT(CONNECT, DBG, ("SpxConnConnect: NETWORK %lx\n", *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet))); DBGPRINT(CONNECT, DBG, ("SpxConnConnect: NODE %02x-%02x-%02x-%02x-%02x-%02x\n", pFindRouteReq->fr_FindRouteReq.Node[0], pFindRouteReq->fr_FindRouteReq.Node[1], pFindRouteReq->fr_FindRouteReq.Node[2], pFindRouteReq->fr_FindRouteReq.Node[3], pFindRouteReq->fr_FindRouteReq.Node[4], pFindRouteReq->fr_FindRouteReq.Node[5])); pFindRouteReq->fr_FindRouteReq.Identifier = IDENTIFIER_SPX; pFindRouteReq->fr_Ctx = pSpxConnFile; // We wont force a rip for every connection. Only if its not // in the IPX database. pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; // Reference for the find route. So that abort connect wont // free up the connection until we return from here. SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); status = STATUS_PENDING; } else { // Abort connect attempt. spxConnAbortConnect( pSpxConnFile, status, lockHandleDev, lockHandleAddr, lockHandleConn); CTEAssert(pSpxConnFile->scf_ConnectReq == NULL); locksHeld = FALSE; status = STATUS_INSUFFICIENT_RESOURCES; } } if (locksHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); CTEFreeLock(&pSpxAddr->sa_Lock, lockHandleAddr); CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); } if (NT_SUCCESS(status)) { // Start off the find route request, We send the packet in completion. // The verify reference is kept until the connect request completes. // If connecting to network 0 we don't do this, proceed to find // route completion which will send the request on very card. if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { SpxFindRouteComplete( &pFindRouteReq->fr_FindRouteReq, TRUE); } else { (*IpxFindRoute)( &pFindRouteReq->fr_FindRouteReq); } } else { DBGPRINT(CONNECT, ERR, ("SpxConnConnect: Failed %lx\n", status)); SpxFreeMemory(pFindRouteReq); SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return(status); } NTSTATUS SpxConnListen( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: We assume the connection passed in is already associated with an address. If it is not, we will die! Is that ok? Return Value: --*/ { NTSTATUS status; CTELockHandle lockHandle1, lockHandle2; PSPX_ADDR pSpxAddr; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); // Check if the connection is in a valid state if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { return(status); } // Check if connection is associated, if so, the association cannot // go away until the reference above is removed. So we are safe in // releasing the lock. CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); status = STATUS_INVALID_ADDRESS; if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) { status = STATUS_SUCCESS; pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; // See if this connection is to be a spx2 connection. SPX_CONN_RESETFLAG(pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG | SPX_CONNFILE_STREAM)); if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2) { SPX_CONN_SETFLAG( pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); } if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) { SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); } if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) { DBGPRINT(CONNECT, ERR, ("SpxConnConnect: NOACKWAIT requested %lx\n", pSpxConnFile)); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); } if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) { DBGPRINT(CONNECT, ERR, ("spxConnHandleConnReq: IPXHDR requested %lx\n", pSpxConnFile)); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); } } CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); if (NT_SUCCESS(status)) { CTEGetLock(&pSpxAddr->sa_Lock, &lockHandle1); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); status = STATUS_INVALID_CONNECTION; if (SPX_CONN_IDLE(pSpxConnFile)) { SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); // Move connection from inactive list to listening list. if (NT_SUCCESS(spxConnRemoveFromList( &pSpxAddr->sa_InactiveConnList, pSpxConnFile))) { // Put connection in the listening list. SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile); InsertTailList( &pSpxConnFile->scf_ReqLinkage, REQUEST_LINKAGE(pRequest)); status = STATUS_PENDING; } else { // This should never happen! KeBugCheck(0); } } CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle1); } if (!NT_SUCCESS(status)) { SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return(status); } NTSTATUS SpxConnAccept( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_ADDR pSpxAddr; NTSTATUS status; CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); DBGPRINT(CONNECT, DBG, ("SpxConnAccept: %lx\n", pSpxConnFile)); // Check if the connection is in a valid state if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { return (status); } // Check if we are in the correct state and associated. CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); status = STATUS_INVALID_CONNECTION; if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) { status = STATUS_SUCCESS; pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; } CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); if (NT_SUCCESS(status)) { // Grab all three locks CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); status = STATUS_INVALID_CONNECTION; if ((SPX_CONN_LISTENING(pSpxConnFile)) && (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_RECDREQ)) { InsertTailList( &pSpxConnFile->scf_ReqLinkage, REQUEST_LINKAGE(pRequest)); // Call acceptcr now. spxConnAcceptCr( pSpxConnFile, pSpxAddr, lockHandleDev, lockHandleAddr, lockHandleConn); DBGPRINT(CONNECT, DBG, ("SpxConnAccept: Accepted\n")); status = STATUS_PENDING; } else { // Free all locks. CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); } } // Remove reference. Note: Listen reference will exist if ok. And that will // be transferred to the fact that the connection is active when accepted. SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); return(status); } NTSTATUS SpxConnDisconnect( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: If active, we do the following. If informative disconnect, just remember the request in the connection. We do not ref for request. Assume it will always be checked for when changing from disconnect to idle. Arguments: Return Value: --*/ { PTDI_REQUEST_KERNEL_DISCONNECT pParam; NTSTATUS status; CTELockHandle lockHandleConn; BOOLEAN lockHeld; SPX_SENDREQ_TYPE reqType; int numDerefs = 0; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); pParam = (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); // Check if the connection is in a valid state if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { return(status); } // Deref unless the disc request gets queued in as a send request. numDerefs++; DBGPRINT(CONNECT, DBG, ("spxConnDisconnect: %lx On %lx when %lx.%lx %lx Params %lx\n", pRequest, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile), SPX_DISC_STATE(pSpxConnFile), SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC), SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC), pParam->RequestFlags)); DBGPRINT(CONNECT, DBG, ("SpxConnDisconnect: %lx\n", pSpxConnFile)); // Check if we are in the correct state and associated. CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); lockHeld = TRUE; switch (pParam->RequestFlags) { case TDI_DISCONNECT_WAIT: // If informative disconnect, just remember in the connection. status = STATUS_INVALID_CONNECTION; if (!SPX_CONN_IDLE(pSpxConnFile)) { InsertTailList( &pSpxConnFile->scf_DiscLinkage, REQUEST_LINKAGE(pRequest)); status = STATUS_PENDING; } break; case TDI_DISCONNECT_ABORT: case TDI_DISCONNECT_RELEASE: // NOTE! We don't honor the async disconnect symantics of tdi // but map them to an abortive disconnect. // NOTE! If our send list is not empty but our client tries to // do a orderly release, we just queue the ord rel as a send // data request. In process ack, we check for the next packet // to not be a ord rel before giving up on window closure. // NOTE! For spx1 connection, map TDI_DISCONNECT_RELEASE to // TDI_DISCONNECT_ABORT (Informed disconnect) if (!SPX2_CONN(pSpxConnFile)) { pParam->RequestFlags = TDI_DISCONNECT_ABORT; } switch (SPX_MAIN_STATE(pSpxConnFile)) { case SPX_CONNFILE_ACTIVE: // Since we are not a timer disconnect, then we need to keep // retrying the disconnect packet. Change state to DISCONN if this // is not an orderly release or we previously received an orderly // release and are now confirming it. // Retry timer will now keep sending out the disconnect packet. reqType = SPX_REQ_DISC; if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) { SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_ORDREL); reqType = SPX_REQ_ORDREL; } else { // Abortive disconnect SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_IDISC); numDerefs++; spxConnAbortSends( pSpxConnFile, STATUS_LOCAL_DISCONNECT, SPX_CALL_TDILEVEL, lockHandleConn); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); // Abort all receives if we are informed disconnect. spxConnAbortRecvs( pSpxConnFile, STATUS_LOCAL_DISCONNECT, SPX_CALL_TDILEVEL, lockHandleConn); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); // Since we released the lock, a remote IDISC could have come // in in which case we really don't want to queue in the disc // request. Instead, we set it as the disc request in the // connection if one is not already there. if (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_IDISC) { DBGPRINT(CONNECT, ERR, ("SpxConnDisconnect: DISC not POST! %lx.%lx\n", pSpxConnFile, SPX_DISC_STATE(pSpxConnFile))); InsertTailList( &pSpxConnFile->scf_DiscLinkage, REQUEST_LINKAGE(pRequest)); status = STATUS_PENDING; break; } } // !NOTE // AbortSends might leave send requests around as packets might // have been with ipx at the time. That is why SendComplete should // never call AbortSends but must call AbortPkt else it may complete // the following disconnect request prematurely. // Creation reference for request. REQUEST_INFORMATION(pRequest) = 1; // If we have no current requests, queue it in and // set it to be the current request, else just queue it in. // There may be other pending requests in queue. if (pSpxConnFile->scf_ReqPkt == NULL) { pSpxConnFile->scf_ReqPkt = pRequest; pSpxConnFile->scf_ReqPktOffset = 0; pSpxConnFile->scf_ReqPktSize = 0; pSpxConnFile->scf_ReqPktType = reqType; } InsertTailList( &pSpxConnFile->scf_ReqLinkage, REQUEST_LINKAGE(pRequest)); // Do not deref the connection, it is taken by the pending request numDerefs--; // We packetize only upto the window we have. if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) { SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); SpxConnPacketize( pSpxConnFile, TRUE, lockHandleConn); lockHeld = FALSE; } status = STATUS_PENDING; break; case SPX_CONNFILE_CONNECTING: case SPX_CONNFILE_LISTENING: spxConnAbortiveDisc( pSpxConnFile, STATUS_INSUFFICIENT_RESOURCES, SPX_CALL_TDILEVEL, lockHandleConn, FALSE); // [SA] Bug #15249 lockHeld = FALSE; status = STATUS_SUCCESS; break; case SPX_CONNFILE_DISCONN: // When we queue in a disconnect as a send request, we expect // to be able to set it into the scf_DiscReq when it is done. // So we don't use scf_DiscReq here. This will be a problem if // the client has a InformDiscReq pending, and a remote disconnect // comes in, *and* the client then does a disc. We will be completing // the request with STATUS_INVALID_CONNECTION. status = STATUS_INVALID_CONNECTION; if (pParam->RequestFlags != TDI_DISCONNECT_RELEASE) { InsertTailList( &pSpxConnFile->scf_DiscLinkage, REQUEST_LINKAGE(pRequest)); status = STATUS_PENDING; // // If this is a disconnect for a connection which was already // disconnected (but AFD's disconnect handler was not called // because the connfile could not be placed in the inactive list), // set this flag so that the disconnect is not called from // ConnInactivate now that the disconnect has occured here. // if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) { SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); } // // If this was an SPXI connection where we indicated TDI_DISCONNECT_RELEASE // to AFD, the ref count was bumped up to indicate a wait for local disconnect // from AFD. Now that we have this disconnect, deref the connection file. Now // we are ready to truly inactivate this connection file. // if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { CTEAssert( (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); lockHeld = FALSE; SpxConnFileDereference(pSpxConnFile, CFREF_DISCWAITSPX); } } break; default: // Should never happen! status = STATUS_INVALID_CONNECTION; } break; default: status = STATUS_INVALID_PARAMETER; break; } if (lockHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); } DBGPRINT(CONNECT, INFO, ("SpxConnDisconnect: returning for %lx.%lx\n", pSpxConnFile, status)); while (numDerefs-- > 0) { SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return(status); } NTSTATUS SpxConnSend( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: Return Value: --*/ { PTDI_REQUEST_KERNEL_SEND pParam; NTSTATUS status; CTELockHandle lockHandleConn; BOOLEAN lockHeld; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); // Check if the connection is in a valid state if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { return(status); } DBGPRINT(SEND, DBG, ("SpxConnSend: %lx.%lx.%lx.%lx\n", pSpxConnFile, pRequest, pParam->SendLength, pParam->SendFlags)); // Check if we are in the correct state and associated. CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); lockHeld = TRUE; DBGPRINT(SEND, INFO, ("Send: %lx.%lx.%lx\n", pParam->SendLength, pParam->SendFlags, pRequest)); status = STATUS_PENDING; do { if (SPX_CONN_ACTIVE(pSpxConnFile) && ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_ORDREL) && (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL) && (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED))) { // Creation reference for request. REQUEST_INFORMATION(pRequest) = 1; // If we have no current requests, queue it in and // set it to be the current request, else just queue it in. // There may be other pending requests in queue. if (pSpxConnFile->scf_ReqPkt == NULL) { DBGPRINT(SEND, INFO, ("%lx\n", pRequest)); pSpxConnFile->scf_ReqPkt = pRequest; pSpxConnFile->scf_ReqPktOffset = 0; pSpxConnFile->scf_ReqPktSize = pParam->SendLength; pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; } InsertTailList( &pSpxConnFile->scf_ReqLinkage, REQUEST_LINKAGE(pRequest)); } else { // // [SA] Bug #14655 // Return the correct error message in case a send fails due to remote disconnect // if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) { status = STATUS_REMOTE_DISCONNECT ; } else { status = STATUS_INVALID_CONNECTION; } break; } // We packetize only upto the window we have. if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) { SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); SpxConnPacketize(pSpxConnFile, TRUE, lockHandleConn); lockHeld = FALSE; } } while (FALSE); if (lockHeld) { CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); } if (!NT_SUCCESS(status)) { SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return(status); } NTSTATUS SpxConnRecv( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; CTELockHandle lockHandle; BOOLEAN fLockHeld; PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); // Check if the connection is in a valid state if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) { return(status); } DBGPRINT(CONNECT, DBG, ("SpxConnReceive: %lx.%lx\n", pSpxConnFile, pRequest)); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); fLockHeld = TRUE; status = STATUS_INVALID_CONNECTION; if (SPX_CONN_ACTIVE(pSpxConnFile) && !(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC))) { status = STATUS_PENDING; // This routine adds its own reference. SpxConnQueueRecv(pSpxConnFile, pRequest); // If recv pkt queue is non-empty then we have buffered data. Call // process pkts/receives. if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) { SpxRecvProcessPkts(pSpxConnFile, lockHandle); fLockHeld = FALSE; } } if (fLockHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); } SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); return(status); } NTSTATUS SpxConnAction( IN PDEVICE pDevice, IN PREQUEST pRequest ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS Status; UINT BufferLength; UINT DataLength; PNDIS_BUFFER NdisBuffer; PNWLINK_ACTION NwlinkAction; CTELockHandle lockHandle; PIPX_SPXCONNSTATUS_DATA pGetStats; PSPX_CONN_FILE pSpxConnFile = NULL; PSPX_ADDR_FILE pSpxAddrFile = NULL; static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this // // To maintain some compatibility with the NWLINK streams- // based transport, we use the streams header format for // our actions. The old transport expected the action header // to be in InputBuffer and the output to go in OutputBuffer. // We follow the TDI spec, which states that OutputBuffer // is used for both input and output. Since IOCTL_TDI_ACTION // is method out direct, this means that the output buffer // is mapped by the MDL chain; for action the chain will // only have one piece so we use it for input and output. // NdisBuffer = REQUEST_NDIS_BUFFER(pRequest); if (NdisBuffer == NULL) { return STATUS_INVALID_PARAMETER; } NdisQueryBufferSafe( REQUEST_NDIS_BUFFER(pRequest), (PVOID *)&NwlinkAction, &BufferLength, LowPagePriority); if (NwlinkAction == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } // Make sure we have enough room for just the header not // including the data. if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { DBGPRINT(ACTION, ERR, ("Nwlink action failed, buffer too small\n")); return STATUS_BUFFER_TOO_SMALL; } if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { return STATUS_NOT_SUPPORTED; } DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); // Make sure that the correct file object is being used. switch (NwlinkAction->OptionType) { case NWLINK_OPTION_CONNECTION: if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_CONNECTION_FILE) { DBGPRINT(ACTION, ERR, ("Nwlink action failed, not connection file\n")); return STATUS_INVALID_HANDLE; } pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); if ((Status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) return(Status); break; case NWLINK_OPTION_ADDRESS: if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { DBGPRINT(ACTION, ERR, ("Nwlink action failed, not address file\n")); return STATUS_INVALID_HANDLE; } pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); if ((Status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) return(Status); break; default: DBGPRINT(ACTION, ERR, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); return STATUS_INVALID_HANDLE; } // Handle the requests based on the action code. For these // requests ActionHeader->ActionCode is 0, we use the // Option field in the streams header instead. Status = STATUS_SUCCESS; DBGPRINT(ACTION, INFO, ("SpxConnAction: Option %x\n", NwlinkAction->Option)); switch (NwlinkAction->Option) { // // This first group support the winsock helper dll. // In most cases the corresponding sockopt is shown in // the comment, as well as the contents of the Data // part of the action buffer. // case MSPX_SETDATASTREAM: if (pSpxConnFile == NULL) { Status = STATUS_INVALID_HANDLE; break; } if (DataLength >= 1) { DBGPRINT(ACTION, INFO, ("%lx: MIPX_SETSENDPTYPE %x\n", pSpxConnFile, NwlinkAction->Data[0])); pSpxConnFile->scf_DataType = NwlinkAction->Data[0]; } else { Status = STATUS_BUFFER_TOO_SMALL; } break; case MSPX_SENDHEADER: DBGPRINT(ACTION, INFO, ("%lx: MSPX_SENDHEADER\n", pSpxAddrFile)); if (pSpxAddrFile == NULL) { Status = STATUS_INVALID_HANDLE; break; } CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_IPXHDR; CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); break ; case MSPX_NOSENDHEADER: DBGPRINT(ACTION, INFO, ("%lx: MSPX_NOSENDHEADER\n", pSpxAddrFile)); if (pSpxAddrFile == NULL) { Status = STATUS_INVALID_HANDLE; break; } CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_IPXHDR; CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); break; case MSPX_GETSTATS: DBGPRINT(ACTION, INFO, ("%lx: MSPX_GETSTATS\n", pSpxConnFile)); if (pSpxConnFile == NULL) { Status = STATUS_INVALID_HANDLE; DBGPRINT(ACTION, INFO, ("pSpxConnFile is NULL. %lx: MSPX_GETSTATS\n", pSpxConnFile)); break; } CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); if (!SPX_CONN_IDLE(pSpxConnFile)) { USHORT TempRetryCount; // // Status fields are returned in network order. // pGetStats = (PIPX_SPXCONNSTATUS_DATA)&NwlinkAction->Data[0]; switch (SPX_MAIN_STATE(pSpxConnFile)) { case SPX_CONNFILE_LISTENING: pGetStats->ConnectionState = 1; break; case SPX_CONNFILE_CONNECTING: pGetStats->ConnectionState = 2; break; case SPX_CONNFILE_ACTIVE: pGetStats->ConnectionState = 3; break; case SPX_CONNFILE_DISCONN: pGetStats->ConnectionState = 4; break; default: pGetStats->ConnectionState = 0; } pGetStats->WatchDogActive = 1; // Always 1 GETSHORT2SHORT( // scf_LocalConnId is in host order &pGetStats->LocalConnectionId, &pSpxConnFile->scf_LocalConnId); pGetStats->RemoteConnectionId = pSpxConnFile->scf_RemConnId; GETSHORT2SHORT(&pGetStats->LocalSequenceNumber, &pSpxConnFile->scf_SendSeqNum); GETSHORT2SHORT(&pGetStats->LocalAckNumber, &pSpxConnFile->scf_RecvSeqNum); GETSHORT2SHORT(&pGetStats->LocalAllocNumber, &pSpxConnFile->scf_SentAllocNum); GETSHORT2SHORT(&pGetStats->RemoteAckNumber, &pSpxConnFile->scf_RecdAckNum); GETSHORT2SHORT(&pGetStats->RemoteAllocNumber, &pSpxConnFile->scf_RecdAllocNum); pGetStats->LocalSocket = pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket; RtlZeroMemory(pGetStats->ImmediateAddress, 6); // Remote network returned in net order. *((ULONG UNALIGNED *)pGetStats->RemoteNetwork) = *((ULONG UNALIGNED *)pSpxConnFile->scf_RemAddr); RtlCopyMemory( pGetStats->RemoteNode, &pSpxConnFile->scf_RemAddr[4], 6); pGetStats->RemoteSocket = *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)); TempRetryCount = (USHORT)pSpxConnFile->scf_WRetryCount; GETSHORT2SHORT(&pGetStats->RetransmissionCount, &TempRetryCount); GETSHORT2SHORT(&pGetStats->EstimatedRoundTripDelay, &pSpxConnFile->scf_BaseT1); pGetStats->RetransmittedPackets = 0; pGetStats->SuppressedPacket = 0; DBGPRINT(ACTION, INFO, ("SSeq %lx RSeq %lx RecdAck %lx RemAllocNum %lx\n", pGetStats->LocalSequenceNumber, pGetStats->LocalAckNumber, pGetStats->RemoteAckNumber, pGetStats->RemoteAllocNumber)); DBGPRINT(ACTION, INFO, ("LocalSkt %lx RemSkt %lx LocConnId %lx RemConnId %lx\n", pGetStats->LocalSocket, pGetStats->RemoteSocket, pGetStats->LocalConnectionId, pGetStats->RemoteConnectionId)); } else { Status = STATUS_INVALID_CONNECTION; } CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); break; case MSPX_NOACKWAIT: DBGPRINT(ACTION, ERR, ("%lx: MSPX_NOACKWAIT\n", pSpxAddrFile)); if (pSpxAddrFile == NULL) { Status = STATUS_INVALID_HANDLE; break; } CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_NOACKWAIT; CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); break; case MSPX_ACKWAIT: DBGPRINT(ACTION, ERR, ("%lx: MSPX_ACKWAIT\n", pSpxAddrFile)); if (pSpxAddrFile == NULL) { Status = STATUS_INVALID_HANDLE; break; } CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_NOACKWAIT; CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); break; // // These are new for ISN (not supported in NWLINK). // // The Option was not supported, so fail. default: Status = STATUS_NOT_SUPPORTED; break; } // end of the long switch on NwlinkAction->Option #if DBG if (Status != STATUS_SUCCESS) { DBGPRINT(ACTION, ERR, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); } #endif if (pSpxConnFile) { SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } if (pSpxAddrFile) { SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); } return Status; } VOID SpxConnConnectFindRouteComplete( IN PSPX_CONN_FILE pSpxConnFile, IN PSPX_FIND_ROUTE_REQUEST pFrReq, IN BOOLEAN FoundRoute, IN CTELockHandle LockHandle ) /*++ Routine Description: This routine is called with the connection lock held and the conn refd. It should deal with both. Arguments: Return Value: --*/ { PNDIS_PACKET pCrPkt; PSPX_SEND_RESD pSendResd; ULONG Timeout; NTSTATUS status = STATUS_BAD_NETWORK_PATH; pSendResd = pSpxConnFile->scf_SendListHead; if (pSendResd == NULL) { CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); // Remove the reference for the call. SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); return; } pCrPkt = (PNDIS_PACKET)CONTAINING_RECORD( pSendResd, NDIS_PACKET, ProtocolReserved); DBGPRINT(CONNECT, INFO, ("SpxConnConnectFindRouteComplete: %lx.%d\n", pSpxConnFile, FoundRoute)); #if defined(_PNP_POWER) Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; #else if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { // Here we are going to send on every NIC ID. We adjust the // timeout down so that a full run through all the NIC IDs will // take one normal timeout. We don't adjust the timer below // 100 ms however. Timeout = (PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR) / SpxDevice->dev_Adapters; if (Timeout < (HALFSEC_TO_MS_FACTOR/5)) { Timeout = HALFSEC_TO_MS_FACTOR / 5; } } else { Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; } #endif // Timeout value is in half-seconds if ((FoundRoute) && ((pSpxConnFile->scf_CTimerId = SpxTimerScheduleEvent( spxConnConnectTimer, Timeout, pSpxConnFile)) != 0)) { // Add a reference for the connect timer SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); // If the mac address in local target is all zeros, fill it with our // destination address. Also if this is a connect to network 0 fill // it in with the destination address, and further down we will loop // through all possible NIC IDs. if (((*((UNALIGNED ULONG *) (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress)) == (ULONG)0) && (*((UNALIGNED USHORT *) (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) || (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) { DBGPRINT(CONNECT, INFO, ("SpxConnConnectFindRouteComplete: LOCAL NET\n")); RtlCopyMemory( pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, pSpxConnFile->scf_RemAddr+4, 6); } // We are all set to go ahead with the connect. // Timer is started on connection status = STATUS_SUCCESS; #if defined(_PNP_POWER) pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); #else if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT) * SpxDevice->dev_Adapters; } else { pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); } #endif _PNP_POWER SPX_CONN_SETFLAG(pSpxConnFile, (SPX_CONNFILE_C_TIMER | SPX_CONNECT_SENTREQ)); pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; pSpxConnFile->scf_AckLocalTarget= pFrReq->fr_FindRouteReq.LocalTarget; if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { #if defined(_PNP_POWER) pSpxConnFile->scf_LocalTarget.NicHandle.NicId = (USHORT)ITERATIVE_NIC_ID; pSpxConnFile->scf_AckLocalTarget.NicHandle.NicId = (USHORT)ITERATIVE_NIC_ID; #else pSpxConnFile->scf_LocalTarget.NicId = 1; pSpxConnFile->scf_AckLocalTarget.NicId = 1; #endif _PNP_POWER } // We will be giving the packet to ipx. pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); // Send the packet SPX_SENDPACKET(pSpxConnFile, pCrPkt, pSendResd); } if (!NT_SUCCESS(status)) { CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); DBGPRINT(CONNECT, ERR, ("SpxConnConnectFindRouteComplete: FAILED on %lx.%d\n", pSpxConnFile, FoundRoute)); spxConnAbortConnect( pSpxConnFile, status, lockHandleDev, lockHandleAddr, lockHandleConn); } // Remove the reference for the call. SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); return; } VOID SpxConnActiveFindRouteComplete( IN PSPX_CONN_FILE pSpxConnFile, IN PSPX_FIND_ROUTE_REQUEST pFrReq, IN BOOLEAN FoundRoute, IN CTELockHandle LockHandle ) /*++ Routine Description: This routine is called with the connection lock held and the conn refd. It should deal with both. Arguments: Return Value: --*/ { BOOLEAN fDisconnect = TRUE; SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); DBGPRINT(CONNECT, DBG, ("SpxConnActiveFindRouteComplete: %lx.%lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); // If we are disconnecting, just remove the reference and exit. if (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE) { fDisconnect = FALSE; // We are here if either the wdog or the retry timer did a find // route. We need to save the info from the find route if it was // successful and just restart the timers. if (FoundRoute) { // If the mac address in local target is all zeros, fill it with our // destination address. if ((*((UNALIGNED ULONG *) (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) && (*((UNALIGNED USHORT *) (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) { DBGPRINT(CONNECT, INFO, ("SpxConnActiveFindRouteComplete: LOCAL NET\n")); RtlCopyMemory( pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, pSpxConnFile->scf_RemAddr+4, 6); } pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; } // Depending on state restart the wdog or retry timer. Add reference // for it. switch (SPX_SEND_STATE(pSpxConnFile)) { case SPX_SEND_RETRY: // Set state to SPX_SEND_RETRYWD SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRYWD); // Start retry timer. if ((pSpxConnFile->scf_RTimerId = SpxTimerScheduleEvent( spxConnRetryTimer, pSpxConnFile->scf_BaseT1, pSpxConnFile)) != 0) { SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); // Reference connection for the timer SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); } else { fDisconnect = TRUE; } break; case SPX_SEND_WD: // Start watchdog 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); } else { fDisconnect = TRUE; } break; case SPX_SEND_IDLE: case SPX_SEND_PACKETIZE: // Do nothing, remove reference and leave. break; default: KeBugCheck(0); } } if (fDisconnect) { DBGPRINT(CONNECT, DBG1, ("SpxConnActiveFindRouteComplete: DISCONNECT %lx.%lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); // Abortive disc will reset the funky state if necessary. spxConnAbortiveDisc( pSpxConnFile, STATUS_INSUFFICIENT_RESOURCES, SPX_CALL_TDILEVEL, LockHandle, FALSE); // [SA] Bug #15249 } else { CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); } SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); return; } ULONG spxConnConnectTimer( IN PVOID Context, IN BOOLEAN TimerShuttingDown ) /*++ Routine Description: We enter this routine during the connection attempt. We could be at any stage of sending either the CR or the SN packet. If we have reached the end of the retry count, we need to know the substate at that point. For a CR, we give up trying to connect, and for a SN we try the next lower packet size or if we have reached the minimum packet size, we give up the connect. Arguments: Return Value: --*/ { PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; PNDIS_PACKET pPkt; PSPX_SEND_RESD pSendResd; CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; BOOLEAN fAbort = FALSE, locksHeld = FALSE, sendPkt = FALSE; PREQUEST pRequest = NULL; // Get all locks CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); locksHeld = TRUE; DBGPRINT(CONNECT, INFO, ("spxConnConnectTimer: Entered\n")); do { if ((!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) || (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile))) { TimerShuttingDown = TRUE; } if (TimerShuttingDown) { break; } if (SPX_CONN_CONNECTING(pSpxConnFile)) { switch (SPX_CONNECT_STATE(pSpxConnFile)) { case SPX_CONNECT_SENTREQ: // There should be only one packet in list, the cr. CTEAssert(pSpxConnFile->scf_SendListHead == pSpxConnFile->scf_SendListTail); pSendResd = pSpxConnFile->scf_SendListHead; pPkt = (PNDIS_PACKET)CONTAINING_RECORD( pSendResd, NDIS_PACKET, ProtocolReserved); if (pSpxConnFile->scf_CRetryCount-- == 0) { // No luck, we need to complete connect request with failure ++SpxDevice->dev_Stat.NotFoundFailures; fAbort = TRUE; break; } // We need to resend the packet if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) { // Try next time. break; } pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; sendPkt = TRUE; break; case SPX_CONNECT_NEG: if (!spxConnGetPktByType( pSpxConnFile, SPX_TYPE_SN, FALSE, &pPkt)) { KeBugCheck(0); } pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) { // Try when we come in next. break; } // If we have exhausted current retries, try next smaller size. // If this was the smallest size, we abort. if (pSpxConnFile->scf_CRetryCount-- == 0) { // Have we tried the smallest size? CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) { // Give up! Remove negotiate packet etc. ++SpxDevice->dev_Stat.SessionTimeouts; fAbort = TRUE; break; } // Set neg pkt size to new lower size spxConnSetNegSize( pPkt, pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); } // We need to resend the packet CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; sendPkt = TRUE; break; case SPX_CONNECT_W_SETUP: default: DBGPRINT(CONNECT, ERR, ("spxConnConnectTimer: state is W_Setup %lx\n", pSpxConnFile)); KeBugCheck(0); } } else { switch (SPX_LISTEN_STATE(pSpxConnFile)) { case SPX_LISTEN_SETUP: if (!spxConnGetPktByType( pSpxConnFile, SPX_TYPE_SS, FALSE, &pPkt)) { KeBugCheck(0); } pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) { // Try when we come in next. break; } // If we have exhausted current retries, try next smaller size. // If this was the smallest size, we abort. if (pSpxConnFile->scf_CRetryCount-- == 0) { // Have we tried the smallest size? if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) { // Give up! Remove negotiate packet etc. Have an abort // kind of routine. ++SpxDevice->dev_Stat.SessionTimeouts; fAbort = TRUE; break; } // Set neg pkt size to new lower size spxConnSetNegSize( pPkt, pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); } // We need to resend the packet CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; sendPkt = TRUE; break; default: KeBugCheck(0); } } } while (FALSE); if (fAbort) { CTEAssert(!sendPkt); DBGPRINT(CONNECT, ERR, ("spxConnConnectTimer: Expired for %lx\n", pSpxConnFile)); spxConnAbortConnect( pSpxConnFile, STATUS_BAD_NETWORK_PATH, 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 (sendPkt) { CTEAssert(!fAbort); #if !defined(_PNP_POWER) if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) { // we are sending to all NICs because this is the initial // connect frame and the remote network is 0. pSpxConnFile->scf_LocalTarget.NicId = (USHORT) ((pSpxConnFile->scf_LocalTarget.NicId % SpxDevice->dev_Adapters) + 1); // we pass this a valid packet in pPkt, so it knows to // just refresh the header and not update the protocol // reserved variables. SpxPktBuildCr( pSpxConnFile, pSpxConnFile->scf_AddrFile->saf_Addr, &pPkt, 0, // state will not be updated SPX2_CONN(pSpxConnFile)); } #endif !_PNP_POWER // Send the packet SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); } if (TimerShuttingDown || fAbort) { // Dereference connection for verify done in connect, for timer. This // should complete any pending disconnects if they had come in in the // meantime. SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); return(TIMER_DONT_REQUEUE); } return(TIMER_REQUEUE_CUR_VALUE); } ULONG spxConnWatchdogTimer( IN PVOID Context, IN BOOLEAN TimerShuttingDown ) /*++ Routine Description: This is started on a connection right after the CR or the CR ack is received. During the connection establishment phase, it does nothing other than decrement the retry count and upon reaching 0, it aborts the connection. When it goes off and finds the connection is active, it sends a probe. Arguments: Return Value: --*/ { PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; CTELockHandle lockHandle; PSPX_SEND_RESD pSendResd; PSPX_FIND_ROUTE_REQUEST pFindRouteReq; PNDIS_PACKET pProbe = NULL; BOOLEAN lockHeld, fSpx2 = SPX2_CONN(pSpxConnFile), fDisconnect = FALSE, fFindRoute = FALSE, fSendProbe = FALSE; DBGPRINT(CONNECT, INFO, ("spxConnWatchdogTimer: Entered\n")); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); lockHeld = TRUE; do { if (TimerShuttingDown || (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) || (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) { #if DBG if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) { CTEAssert(FALSE); } #endif SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); TimerShuttingDown = TRUE; break; } // If the retry timer is active on this connection, and the watchdog // timer happens to fire, just requeue ourselves for spx2. For spx1, // we go ahead with sending a probe. Retry timer does the same things // watchdog does for spx2. switch (SPX_MAIN_STATE(pSpxConnFile)) { case SPX_CONNFILE_ACTIVE: case SPX_CONNFILE_DISCONN: // Squash the race condition where a disconnect request is never // packetized, because the send state was not IDLE. if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC) { DBGPRINT(CONNECT, ERR, ("spxConnWatchdogTimer: POST IDISC %lx\n", pSpxConnFile)); if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) { DBGPRINT(CONNECT, ERR, ("spxConnWatchdogTimer: PKT POST IDISC %lx\n", pSpxConnFile)); SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); SpxConnPacketize( pSpxConnFile, TRUE, lockHandle); lockHeld = FALSE; break; } } if (!fSpx2) { if (pSpxConnFile->scf_WRetryCount-- > 0) { fSendProbe = TRUE; } else { fDisconnect = TRUE; } break; } // SPX2 connection. Watchdog algorithm needs to do lots of goody // stuff. If retry is active, just requeue ourselves. if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) break; // There is a race between watchdog and retry if its started. Who // ever changes the state first gets to go do its thing. switch (SPX_SEND_STATE(pSpxConnFile)) { case SPX_SEND_IDLE: // Enter WD state only if we fired for the second time witout // an ack. This prevents PACKETIZE from blocking due to being // in a non-idle state. CTEAssert(pSpxConnFile->scf_WRetryCount != 0); if ((pSpxConnFile->scf_WRetryCount)-- != (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)) { // We enter the WD state. Build and send a probe. SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_WD); SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); } fSendProbe = TRUE; break; case SPX_SEND_PACKETIZE: // Do nothing. break; case SPX_SEND_RETRY: case SPX_SEND_RETRYWD: case SPX_SEND_RENEG: case SPX_SEND_RETRY2: case SPX_SEND_RETRY3: // Do nothing. Send timer got in first. DBGPRINT(CONNECT, DBG1, ("SpxConnWDogTimer: When retry fired %lx\n", pSpxConnFile)); break; case SPX_SEND_WD: // Decrement count. If not zero, send a probe. If half the // count is reached, stop timer and call find route. if (pSpxConnFile->scf_WRetryCount-- > 0) { if (pSpxConnFile->scf_WRetryCount != (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)/2) { fSendProbe = TRUE; break; } if ((pFindRouteReq = (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) { fDisconnect = TRUE; break; } // Remove timer reference/ Add find route request ref fFindRoute = TRUE; TimerShuttingDown = TRUE; SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); // Initialize the find route request *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network) = *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); // // [SA] Bug #15094 // We need to also pass in the node number to IPX so that IPX can // compare the node addresses to determine the proper WAN NICid // // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6); *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); DBGPRINT(CONNECT, DBG, ("SpxConnWDogTimer: NETWORK %lx\n", *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; pFindRouteReq->fr_Ctx = pSpxConnFile; // Make sure we have IPX re-rip. pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; } else { fDisconnect = TRUE; } break; default: KeBugCheck(0); } break; case SPX_CONNFILE_CONNECTING: if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) || (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_NEG)) { // Do nothing. Connect timer is active. DBGPRINT(CONNECT, ERR, ("SpxConnWDogTimer: CR Timer active %lx\n", pSpxConnFile)); break; } if (!(pSpxConnFile->scf_WRetryCount--)) { // Disconnect! DBGPRINT(CONNECT, ERR, ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", pSpxConnFile->scf_LocalConnId, pSpxConnFile)); fDisconnect = TRUE; } break; case SPX_CONNFILE_LISTENING: if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP) { // Do nothing. Connect timer is active. DBGPRINT(CONNECT, ERR, ("SpxConnWDogTimer: CR Timer active %lx\n", pSpxConnFile)); break; } if (!(pSpxConnFile->scf_WRetryCount--)) { // Disconnect! DBGPRINT(CONNECT, ERR, ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", pSpxConnFile->scf_LocalConnId, pSpxConnFile)); fDisconnect = TRUE; } break; default: // Should never happen! KeBugCheck(0); } } while (FALSE); if (fSendProbe) { CTEAssert(lockHeld); CTEAssert(!fDisconnect); DBGPRINT(CONNECT, DBG1, ("spxConnWatchdogTimer: Send Probe from %lx.%lx\n", pSpxConnFile->scf_LocalConnId, pSpxConnFile)); // Build a probe and send it out to the remote end. SpxPktBuildProbe( pSpxConnFile, &pProbe, (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), fSpx2); if (pProbe != NULL) { SpxConnQueueSendPktTail(pSpxConnFile, pProbe); pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); } } if (fDisconnect) { CTEAssert(lockHeld); CTEAssert(!fSendProbe); // Disconnect! DBGPRINT(CONNECT, ERR, ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", pSpxConnFile->scf_LocalConnId, pSpxConnFile)); TimerShuttingDown = TRUE; SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); // If spx2, check if we need to do anything special. // AbortiveDisc will reset funky state if needed. spxConnAbortiveDisc( pSpxConnFile, STATUS_LINK_TIMEOUT, SPX_CALL_TDILEVEL, lockHandle, FALSE); // [SA] Bug #15249 lockHeld = FALSE; } if (lockHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); } if (fFindRoute) { CTEAssert(!fSendProbe); CTEAssert(!fDisconnect); CTEAssert(TimerShuttingDown); // Start off the find route request (*IpxFindRoute)( &pFindRouteReq->fr_FindRouteReq); } if (pProbe != NULL) { // Send the packet SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); } if (TimerShuttingDown) { // Dereference connection for verify done in connect, for timer. This // should complete any pending disconnects if they had come in in the // meantime. SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return((TimerShuttingDown ? TIMER_DONT_REQUEUE : TIMER_REQUEUE_CUR_VALUE)); } ULONG spxConnRetryTimer( IN PVOID Context, IN BOOLEAN TimerShuttingDown ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; PSPX_SEND_RESD pSendResd; CTELockHandle lockHandleConn; PIPXSPX_HDR pSendHdr; PNDIS_PACKET pPkt; PNDIS_PACKET pProbe = NULL; PSPX_FIND_ROUTE_REQUEST pFindRouteReq; // Compiler warning reenqueueTime = pSpxConnFile->scf_BaseT1; [tingcai] // USHORT reenqueueTime = TIMER_REQUEUE_CUR_VALUE; UINT reenqueueTime = TIMER_REQUEUE_CUR_VALUE; BOOLEAN lockHeld, fResendPkt = FALSE, fDisconnect = FALSE, fFindRoute = FALSE, fBackoffTimer = FALSE; PREQUEST pRequest = NULL; PNDIS_BUFFER NdisBuf, NdisBuf2; ULONG BufLen = 0; DBGPRINT(CONNECT, INFO, ("spxConnRetryTimer: Entered\n")); // Get lock CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); lockHeld = TRUE; do { // If timer is not up, no send pkts, just return. if (TimerShuttingDown || (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) || (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL)) { #if DBG if ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL) { if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) { CTEAssert(FALSE); } } #endif SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); TimerShuttingDown = TRUE; break; } // In all other cases, reenqueue with potentially modified reenqueue // time. reenqueueTime = pSpxConnFile->scf_BaseT1; DBGPRINT(SEND, INFO, ("spxConnRetryTimer: BaseT1 %lx on %lx\n", pSpxConnFile->scf_BaseT1, pSpxConnFile)); // If an ack for a packet was processed while we were out, reset // retry count and return. Or if we are packetizing, return. if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) { break; } else if ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && (pSpxConnFile->scf_RetrySeqNum != pSendResd->sr_SeqNum)) { pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; break; } // If packet is still with IPX, requeue for next time. if (pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) { break; } CTEAssert(pSendResd != NULL); pPkt = (PNDIS_PACKET)CONTAINING_RECORD( pSendResd, NDIS_PACKET, ProtocolReserved); // // Get the MDL that points to the IPX/SPX header. (the second one) // NdisQueryPacket(pPkt, NULL, NULL, &NdisBuf, NULL); NdisGetNextBuffer(NdisBuf, &NdisBuf2); NdisQueryBufferSafe(NdisBuf2, (PUCHAR) &pSendHdr, &BufLen, LowPagePriority); ASSERT(pSendHdr != NULL); #if OWN_PKT_POOLS pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + NDIS_PACKET_SIZE + sizeof(SPX_SEND_RESD) + IpxInclHdrOffset); #endif switch (SPX_SEND_STATE(pSpxConnFile)) { case SPX_SEND_IDLE: // Set ack bit in packet. pSendResd initialized at beginning. pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; // Do we backoff the timer? fBackoffTimer = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_REXMIT) != 0); // We are going to resend this packet pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_ACKREQ | SPX_SENDPKT_REXMIT); ++SpxDevice->dev_Stat.ResponseTimerExpirations; CTEAssert((ULONG)pSpxConnFile->scf_RRetryCount <= PARAM(CONFIG_REXMIT_COUNT)); DBGPRINT(SEND, DBG1, ("spxConnRetryTimer: Retry Count %lx on %lx\n", pSpxConnFile->scf_RRetryCount, pSpxConnFile)); fResendPkt = TRUE; if (pSpxConnFile->scf_RRetryCount-- != 0) { // We dont treat the IDISC packet as a data packet, so none // of the fancy spx2 retry stuff if we are retrying the idisc. if (SPX2_CONN(pSpxConnFile) && (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_IDISC)) { // We enter the RETRY state. Reference conn for this // "funky" state. CTEAssert(SPX2_CONN(pSpxConnFile)); SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY); SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); } } else { DBGPRINT(SEND, ERR, ("spxConnRetryTimer: Retry Count over on %lx\n", pSpxConnFile)); fDisconnect = TRUE; fResendPkt = FALSE; pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; } break; case SPX_SEND_RETRY: // When we have reached retry_count/2 limit, start locate route. Do // not queue ourselves. Handle restarting timer in find route // completion. If timer starts successfully in find route comp, then // it will change our state to RETRYWD. // Decrement count. If half the count is reached, stop timer and call // find route. if (pSpxConnFile->scf_RRetryCount-- != (LONG)PARAM(CONFIG_REXMIT_COUNT)/2) { // We are going to resend this packet pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_ACKREQ | SPX_SENDPKT_REXMIT); fResendPkt = TRUE; fBackoffTimer = TRUE; break; } if ((pFindRouteReq = (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) { DBGPRINT(SEND, ERR, ("spxConnRetryTimer: Alloc Mem %lx\n", pSpxConnFile)); fDisconnect = TRUE; break; } // Remove timer reference/ Add find route request ref fFindRoute = TRUE; TimerShuttingDown = TRUE; SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); // Initialize the find route request *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); // // [SA] Bug #15094 // We need to also pass in the node number to IPX so that IPX can // compare the node addresses to determine the proper WAN NICid // // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6) ; *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4)) = *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); DBGPRINT(CONNECT, DBG, ("SpxConnRetryTimer: NETWORK %lx\n", *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; pFindRouteReq->fr_Ctx = pSpxConnFile; // Make sure we have IPX re-rip. pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; break; case SPX_SEND_RETRYWD: // Retry a watchdog packet WCount times (initialize to RETRY_COUNT). // If process ack receives an ack (i.e. actual ack packet) while in // this state, it will transition the state to RENEG. // // If the pending data gets acked while in this state, we go back // to idle. DBGPRINT(CONNECT, DBG1, ("spxConnRetryTimer: Send Probe from %lx.%lx\n", pSpxConnFile->scf_LocalConnId, pSpxConnFile)); // Use watchdog count here. if (pSpxConnFile->scf_WRetryCount-- > 0) { // Build a probe and send it out to the remote end. SpxPktBuildProbe( pSpxConnFile, &pProbe, (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), TRUE); if (pProbe != NULL) { SpxConnQueueSendPktTail(pSpxConnFile, pProbe); pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); break; } } // Just set state to retry data packet retry_count/2 times. pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY2); break; case SPX_SEND_RENEG: // Renegotiate size. If we give up, goto RETRY3. // 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)) { 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; } // Send reneg packet, if we get the rr ack, then we resend data // on queue. Note that each time we goto a new negotiate size, // we rebuild the data packets. if (pSpxConnFile->scf_RRetryCount-- == 0) { // Reset count. 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, ("SpxConnRetryTimer: %lx MIN RENEG SIZE\n", pSpxConnFile)); } // Are we at the lowest possible reneg pkt size? If not, try // next lower. When we do this, we free all pending send // packets and reset the packetize queue to the first packet. // Process ack will just do packetize and will not do anything // more other than resetting state to proper value. DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: RENEG: %lx - CURRENT %lx\n", pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) { // We tried lowest size and failed to receive ack. Just // retry data packet, and disc if no ack. DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: RENEG(min), RETRY3: %lx - %lx\n", pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); break; } DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: RENEG(!min): %lx - ATTEMPT %lx\n", pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); } DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: %lx.%lx.%lx RENEG SEQNUM %lx ACKNUM %lx\n", pSpxConnFile, pSpxConnFile->scf_RRetryCount, pSpxConnFile->scf_MaxPktSize, (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), pSpxConnFile->scf_SentAllocNum)); // Use first unused data packet sequence number. SpxPktBuildRr( pSpxConnFile, &pPkt, (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY)); if (pPkt != NULL) { SpxConnQueueSendPktTail(pSpxConnFile, pPkt); pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); fResendPkt = TRUE; SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); } break; case SPX_SEND_RETRY2: // Retry the data packet for remaining amount of RRetryCount. If not // acked goto cleanup. If ack received while in this state, goto idle. if (pSpxConnFile->scf_RRetryCount-- > 0) { // We are going to resend this packet pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_ACKREQ | SPX_SENDPKT_REXMIT); DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: 2nd try Resend on %lx\n", pSpxConnFile)); fResendPkt = TRUE; fBackoffTimer = TRUE; } else { DBGPRINT(SEND, ERR, ("spxConnRetryTimer: Retry Count over on %lx\n", pSpxConnFile)); fDisconnect = TRUE; } break; case SPX_SEND_RETRY3: // Send data packet for RETRY_COUNT times initialized in RRetryCount // before state changed to this state. If ok, process ack moves us // back to PKT/IDLE. If not, we disconnect. // We are going to resend this packet if (pSpxConnFile->scf_RRetryCount-- > 0) { DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: 3rd try Resend on %lx\n", pSpxConnFile)); // We are going to resend this packet pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_ACKREQ | SPX_SENDPKT_REXMIT); fResendPkt = TRUE; fBackoffTimer = TRUE; } else { DBGPRINT(SEND, ERR, ("spxConnRetryTimer: Retry Count over on %lx\n", pSpxConnFile)); fDisconnect = TRUE; } break; case SPX_SEND_WD: // Do nothing. Watchdog timer has fired, just requeue. break; default: KeBugCheck(0); } if (fBackoffTimer) { // Increase retransmit timeout by 50% upto maximum indicated by // initial retransmission value. reenqueueTime += reenqueueTime/2; if (reenqueueTime > MAX_RETRY_DELAY) reenqueueTime = MAX_RETRY_DELAY; pSpxConnFile->scf_BaseT1 = pSpxConnFile->scf_AveT1 = reenqueueTime; pSpxConnFile->scf_DevT1 = 0; DBGPRINT(SEND, DBG, ("spxConnRetryTimer: Backed retry on %lx.%lx %lx\n", pSpxConnFile, pSendResd->sr_SeqNum, reenqueueTime)); } if (fDisconnect) { CTEAssert(lockHeld); // Do not requeue this timer. SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); TimerShuttingDown = TRUE; // Disconnect the connection. spxConnAbortiveDisc( pSpxConnFile, STATUS_LINK_TIMEOUT, SPX_CALL_TDILEVEL, lockHandleConn, FALSE); // [SA] Bug #15249 lockHeld = FALSE; } } while (FALSE); if (lockHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); } if (fResendPkt) { DBGPRINT(SEND, DBG, ("spxConnRetryTimer: 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); } else if (fFindRoute) { CTEAssert(!fResendPkt); CTEAssert(!fDisconnect); CTEAssert(TimerShuttingDown); DBGPRINT(SEND, DBG3, ("spxConnRetryTimer: Find route on %lx\n", pSpxConnFile)); // Start off the find route request (*IpxFindRoute)( &pFindRouteReq->fr_FindRouteReq); } else if (pProbe != NULL) { // Send the packet SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); } if (TimerShuttingDown) { // Dereference connection for verify done in connect, for timer. This // should complete any pending disconnects if they had come in in the // meantime. SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); reenqueueTime = TIMER_DONT_REQUEUE; } DBGPRINT(SEND, INFO, ("spxConnRetryTimer: Reenqueue time : %lx on %lx\n", reenqueueTime, pSpxConnFile)); return(reenqueueTime); } ULONG spxConnAckTimer( IN PVOID Context, IN BOOLEAN TimerShuttingDown ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; CTELockHandle lockHandleConn; DBGPRINT(SEND, INFO, ("spxConnAckTimer: Entered\n")); // Get lock CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); if (!TimerShuttingDown && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) { // We didnt have any back traffic, until we do a send from this // end, send acks immediately. Dont try to piggyback. SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); ++SpxDevice->dev_Stat.PiggybackAckTimeouts; DBGPRINT(SEND, DBG, ("spxConnAckTimer: Send ack on %lx.%lx\n", pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); SpxConnSendAck(pSpxConnFile, lockHandleConn); } else { SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); } // Dereference connection for verify done in connect, for timer. This // should complete any pending disconnects if they had come in in the // meantime. SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); return(TIMER_DONT_REQUEUE); } // // DISCONNECT ROUTINES // VOID spxConnAbortiveDisc( IN PSPX_CONN_FILE pSpxConnFile, IN NTSTATUS Status, IN SPX_CALL_LEVEL CallLevel, IN CTELockHandle LockHandleConn, IN BOOLEAN IDiscFlag // [SA] Bug #15249 ) /*++ Routine Description: This is called when: We time out or have insufficient resources - STATUS_LINK_TIMEOUT/STATUS_INSUFFICIENT_RESOURCES - We abort everything. Could be from watchdog or retry. Stop both. We receive a informed disconnect packet - STATUS_REMOTE_DISCONNECT - We abort everything. Ack must be sent by caller as an orphan pkt. We receive a informed disconnect ack pkt STATUS_SUCCESS - We abort everything - Abort is done with status success (this completes our disc req in the send queue) NOTE: CALLED UNDER THE CONNECTION LOCK. Arguments: [SA] Bug #15249: Added IDiscFlag to indicate if this is an Informed Disconnect. If so, indicate TDI_DISCONNECT_RELEASE to AFD so it allows a receive of buffered pkts. This flag is TRUE only if this routine is called from SpxConnProcessIDisc for SPX connections. Return Value: --*/ { int numDerefs = 0; PVOID pDiscHandlerCtx=NULL; PTDI_IND_DISCONNECT pDiscHandler = NULL; BOOLEAN lockHeld = TRUE; DBGPRINT(CONNECT, DBG, ("spxConnAbortiveDisc: %lx - On %lx when %lx\n", Status, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); switch (Status) { case STATUS_LINK_TIMEOUT: ++SpxDevice->dev_Stat.LinkFailures; break; case STATUS_INSUFFICIENT_RESOURCES: ++SpxDevice->dev_Stat.LocalResourceFailures; break; case STATUS_REMOTE_DISCONNECT: ++SpxDevice->dev_Stat.RemoteDisconnects; break; case STATUS_SUCCESS: case STATUS_LOCAL_DISCONNECT: ++SpxDevice->dev_Stat.LocalDisconnects; break; } switch (SPX_MAIN_STATE(pSpxConnFile)) { case SPX_CONNFILE_ACTIVE: // For transition from active to disconn. numDerefs++; case SPX_CONNFILE_DISCONN: // If we are in any state other than idle/packetize, // remove the reference for the funky state, and reset the send state to be // idle. if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) { #if DBG if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) { DBGPRINT(CONNECT, ERR, ("spxConnAbortiveDisc: When DISC STATE %lx.%lx\n", pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); } #endif DBGPRINT(CONNECT, DBG1, ("spxConnAbortiveDisc: 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++; } // This can be called when a idisc is received, or if a timer // disconnect is happening, or if we sent a idisc/ordrel, but the retries // timed out and we are aborting the connection. // So if we are already aborting, never mind. // // [SA] Bug #15249 // SPX_DISC_INACTIVATED indicates a DISC_ABORT'ing connection that has been // inactivated (connfile removed from active conn. list) // if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) { break; } SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ABORT); // Stop 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_R_TIMER)) { if (SpxTimerCancelEvent(pSpxConnFile->scf_RTimerId, FALSE)) { numDerefs++; } SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_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); } #if 0 // // [SA] We need to call AFD after aborting sends since this connection // becomes a candidate for re-use as soon as the disconnect handler is // called. // We call the disconnect handler when the refcount falls to 0 and the // connection transitions to the inactive list. // // NOTE! We indicate disconnect to afd *before* aborting sends to avoid // afd from calling us again with a disconnect. // Get disconnect handler if we have one. And we have not indicated // abortive disconnect on this connection to afd. if (!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_IDISC); } #endif // // [SA] Save the IDiscFlag in the Connection. // (IDiscFlag) ? SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC) : SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC); // Indicate disconnect to afd. if (pDiscHandler != NULL) { CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); DBGPRINT(CONNECT, INFO, ("spxConnAbortiveDisc: Indicating to afd On %lx when %lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); // First complete all requests waiting for receive completion on // this conn before indicating disconnect. spxConnCompletePended(pSpxConnFile); // // [SA] bug #15249 // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD // if (!IDiscFlag) { (*pDiscHandler)( pDiscHandlerCtx, pSpxConnFile->scf_ConnCtx, 0, // Disc data NULL, 0, // Disc info NULL, TDI_DISCONNECT_ABORT); } else { // // [SA] bug #15249 // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets // it has buffered before the remote disconnect took place. // (*pDiscHandler)( pDiscHandlerCtx, pSpxConnFile->scf_ConnCtx, 0, // Disc data NULL, 0, // Disc info NULL, TDI_DISCONNECT_RELEASE); } CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); } // Go through and kill all pending requests. spxConnAbortRecvs( pSpxConnFile, Status, CallLevel, LockHandleConn); CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); spxConnAbortSends( pSpxConnFile, Status, CallLevel, LockHandleConn); lockHeld = FALSE; break; case SPX_CONNFILE_CONNECTING: case SPX_CONNFILE_LISTENING: DBGPRINT(CONNECT, DBG, ("spxConnAbortiveDisc: CONN/LIST Disc On %lx when %lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); lockHeld = FALSE; { CTELockHandle lockHandleAddr, lockHandleDev; CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); // Ensure we are still in connecting/listening, else call abortive // again. switch (SPX_MAIN_STATE(pSpxConnFile)) { case SPX_CONNFILE_CONNECTING: case SPX_CONNFILE_LISTENING: DBGPRINT(CONNECT, DBG, ("spxConnAbortiveDisc: CONN/LIST Disc2 On %lx when %lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); spxConnAbortConnect( pSpxConnFile, Status, lockHandleDev, lockHandleAddr, LockHandleConn); break; case SPX_CONNFILE_ACTIVE: CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); CTEFreeLock( pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); CTEFreeLock( &SpxDevice->dev_Lock, lockHandleDev); CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); DBGPRINT(CONNECT, DBG, ("spxConnAbortiveDisc: CHG ACT Disc2 On %lx when %lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); spxConnAbortiveDisc( pSpxConnFile, Status, CallLevel, LockHandleConn, FALSE); // [SA] Bug #15249 break; default: CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); CTEFreeLock( pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); CTEFreeLock( &SpxDevice->dev_Lock, lockHandleDev); break; } } default: // Already disconnected. break; } if (lockHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); } while (numDerefs-- > 0) { SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return; }