diff options
Diffstat (limited to '')
42 files changed, 25486 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/spx/dirs b/private/ntos/tdi/isnp/spx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/spx/globals.c b/private/ntos/tdi/isnp/spx/globals.c new file mode 100644 index 000000000..51fc80803 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/globals.c @@ -0,0 +1,87 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Global values +PDEVICE SpxDevice = NULL; +UNICODE_STRING IpxDeviceName = {0}; +HANDLE IpxHandle = NULL; + +LARGE_INTEGER Magic100000 = { + 0x1b478424, + 0xa7c5ac47 + }; +// Line info +IPX_LINE_INFO IpxLineInfo = {0}; +USHORT IpxMacHdrNeeded = 0; +USHORT IpxInclHdrOffset= 0; + +// Entry Points into the IPX stack +IPX_INTERNAL_SEND IpxSendPacket = NULL; +IPX_INTERNAL_FIND_ROUTE IpxFindRoute = NULL; +IPX_INTERNAL_QUERY IpxQuery = NULL; +IPX_INTERNAL_TRANSFER_DATA IpxTransferData = NULL; + +#if DBG +ULONG SpxDebugDump = 0; +LONG SpxDumpInterval = DBG_DUMP_DEF_INTERVAL; +ULONG SpxDebugLevel = DBG_LEVEL_ERR; +ULONG SpxDebugSystems = DBG_COMP_MOST; +#endif + +// Unload event triggered when ref count on device goes to zero. +KEVENT SpxUnloadEvent = {0}; + +// Maximum packet size quanta used during packet size negotiation +ULONG SpxMaxPktSize[] = { + 576 - MIN_IPXSPX2_HDRSIZE, + 1024 - MIN_IPXSPX2_HDRSIZE, + 1474 - MIN_IPXSPX2_HDRSIZE, + 1492 - MIN_IPXSPX2_HDRSIZE, + 1500 - MIN_IPXSPX2_HDRSIZE, + 1954 - MIN_IPXSPX2_HDRSIZE, + 4002 - MIN_IPXSPX2_HDRSIZE, + 8192 - MIN_IPXSPX2_HDRSIZE, + 17314 - MIN_IPXSPX2_HDRSIZE, + 65535 - MIN_IPXSPX2_HDRSIZE + }; + +ULONG SpxMaxPktSizeIndex = sizeof(SpxMaxPktSize)/sizeof(ULONG); + + +// Global interlock +CTELock SpxGlobalInterlock = {0}; + +// Another one, used only for global queues for addr/conn +CTELock SpxGlobalQInterlock = {0}; +PSPX_CONN_FILE SpxGlobalConnList = NULL; +PSPX_ADDR_FILE SpxGlobalAddrList = NULL; + +SPX_CONNFILE_LIST SpxPktConnList = {NULL, NULL}; +SPX_CONNFILE_LIST SpxRecvConnList = {NULL, NULL}; + +// Timer globals +LONG SpxTimerCurrentTime = 0; diff --git a/private/ntos/tdi/isnp/spx/h/fwddecls.h b/private/ntos/tdi/isnp/spx/h/fwddecls.h new file mode 100644 index 000000000..feda4e76b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/fwddecls.h @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + fwddecls.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +struct _SPX_ADDR ; +struct _SPX_ADDR_FILE ; +struct _SPX_CONN_FILE ; +struct _SPX_SEND_RESD ; diff --git a/private/ntos/tdi/isnp/spx/h/globals.h b/private/ntos/tdi/isnp/spx/h/globals.h new file mode 100644 index 000000000..e4fcf39a8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/globals.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +extern PDEVICE SpxDevice; +extern UNICODE_STRING IpxDeviceName; +extern HANDLE IpxHandle; + +extern LARGE_INTEGER Magic100000; + +#if 1 // DBG +extern ULONG SpxDebugDump; +extern LONG SpxDumpInterval; +extern ULONG SpxDebugLevel; +extern ULONG SpxDebugSystems; + +#endif + +// More IPX info. +extern IPX_LINE_INFO IpxLineInfo; +extern USHORT IpxMacHdrNeeded; +extern USHORT IpxInclHdrOffset; + +// Entry Points into the IPX stack +extern IPX_INTERNAL_SEND IpxSendPacket; +extern IPX_INTERNAL_FIND_ROUTE IpxFindRoute; +extern IPX_INTERNAL_QUERY IpxQuery; +extern IPX_INTERNAL_TRANSFER_DATA IpxTransferData; + +// Unload event +extern KEVENT SpxUnloadEvent; + +extern ULONG SpxMaxPktSize[]; +extern ULONG SpxMaxPktSizeIndex; + +extern CTELock SpxGlobalInterlock; + + +extern CTELock SpxGlobalQInterlock; +extern PSPX_CONN_FILE SpxGlobalConnList; +extern PSPX_ADDR_FILE SpxGlobalAddrList; + +extern SPX_CONNFILE_LIST SpxPktConnList; +extern SPX_CONNFILE_LIST SpxRecvConnList; + +extern LONG SpxTimerCurrentTime; diff --git a/private/ntos/tdi/isnp/spx/h/isnspx.h b/private/ntos/tdi/isnp/spx/h/isnspx.h new file mode 100644 index 000000000..6080b0423 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/isnspx.h @@ -0,0 +1,363 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnspx.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include <ntddk.h> +#include <tdikrnl.h> +#include <ndis.h> +#ifndef CTE_TYPEDEFS_DEFINED +#include <cxport.h> +#endif +#include <bind.h> + +#include "wsnwlink.h" + +#define SPX_DEVICE_SIGNATURE (USHORT)(*(PUSHORT)"SD") +#define SPX_ADDRESS_SIGNATURE (USHORT)(*(PUSHORT)"AD") +#define SPX_ADDRESSFILE_SIGNATURE (USHORT)(*(PUSHORT)"AF") +#define SPX_CONNFILE_SIGNATURE (USHORT)(*(PUSHORT)"CF") + +#define SPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + +#define SPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +typedef UCHAR BYTE, *PBYTE; +typedef ULONG DWORD, *PDWORD; + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + +// +// PREQUEST +// SpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define SpxAllocateRequest(_Device,_Irp) \ + (_Irp) + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// SpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define SpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_TDI_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the TDI buffer chain associated with a request. +// + +#define REQUEST_TDI_BUFFER(_Request) \ + ((PVOID)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// PUNICODE_STRING +// REQUEST_OPEN_NAME( +// IN PREQUEST Request +// ); +// +// Used to access the RemainingName field of a request. +// + +#define REQUEST_OPEN_NAME(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->FileObject->FileName)) + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// SpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define SpxCompleteRequest(_Request) \ + { \ + CTELockHandle _CancelIrql; \ + DBGPRINT(TDI, INFO, \ + ("SpxCompleteRequest: Completing %lx with %lx\n", \ + (_Request), REQUEST_STATUS(_Request))); \ + \ + IoAcquireCancelSpinLock( &_CancelIrql ); \ + (_Request)->CancelRoutine = NULL; \ + IoReleaseCancelSpinLock( _CancelIrql ); \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT); \ + } + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +#include "fwddecls.h" + +// BUGBUG: This should go in ntddk.h? +#ifndef _NTIOAPI_ +#include "spxntdef.h" +#endif + +#include "spxreg.h" +#include "spxdev.h" +#include "spxbind.h" +#include "spxtimer.h" +#include "spxpkt.h" +#include "spxerror.h" +#include "spxaddr.h" +#include "spxconn.h" +#include "spxrecv.h" +#include "spxsend.h" +#include "spxquery.h" +#include "spxmem.h" +#include "spxutils.h" + + +// Globals +#include "globals.h" + + + + diff --git a/private/ntos/tdi/isnp/spx/h/spxaddr.h b/private/ntos/tdi/isnp/spx/h/spxaddr.h new file mode 100644 index 000000000..b49a4791e --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxaddr.h @@ -0,0 +1,426 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.h + +Abstract: + + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define DYNSKT_RANGE_START 0x4000 +#define DYNSKT_RANGE_END 0x7FFF +#define SOCKET_UNIQUENESS 1 + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. + +#define AFREF_CREATE 0 +#define AFREF_VERIFY 1 +#define AFREF_INDICATION 2 +#define AFREF_CONN_ASSOC 3 + +#define AFREF_TOTAL 4 + +typedef struct _SPX_ADDR_FILE { + +#if DBG + ULONG saf_RefTypes[AFREF_TOTAL]; +#endif + + CSHORT saf_Type; + CSHORT saf_Size; + + // number of references to this object. + ULONG saf_RefCount; + + // Linkage in address list. + struct _SPX_ADDR_FILE * saf_Next; + struct _SPX_ADDR_FILE * saf_GlobalNext; + + // List of associated connection/active or otherwise + struct _SPX_CONN_FILE * saf_AssocConnList; + + // the current state of the address file structure; this is either open or + // closing + USHORT saf_Flags; + + // address to which we are bound, pointer to its lock. + struct _SPX_ADDR * saf_Addr; + CTELock * saf_AddrLock; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT saf_FileObject; +#endif + + // device to which we are attached. + struct _DEVICE * saf_Device; + + // This holds the request used to close this address file, + // for pended completion. + PREQUEST saf_CloseReq; + + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + PTDI_IND_CONNECT saf_ConnHandler; + PVOID saf_ConnHandlerCtx; + + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. + PTDI_IND_DISCONNECT saf_DiscHandler; + PVOID saf_DiscHandlerCtx; + + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. + PTDI_IND_RECEIVE saf_RecvHandler; + PVOID saf_RecvHandlerCtx; + + // Send possible handler + PTDI_IND_SEND_POSSIBLE saf_SendPossibleHandler; + PVOID saf_SendPossibleHandlerCtx; + + // !!!We do not do datagrams or expedited data!!! + + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. + PTDI_IND_ERROR saf_ErrHandler; + PVOID saf_ErrHandlerCtx; + PVOID saf_ErrHandlerOwner; + + +} SPX_ADDR_FILE, *PSPX_ADDR_FILE; + +#define SPX_ADDRFILE_OPENING 0x0000 // not yet open for business +#define SPX_ADDRFILE_OPEN 0x0001 // open for business +#define SPX_ADDRFILE_CLOSING 0x0002 // closing +#define SPX_ADDRFILE_STREAM 0x0004 // Opened for stream mode operation +#define SPX_ADDRFILE_CONNIND 0x0008 // Connect ind in progress +#define SPX_ADDRFILE_SPX2 0x0010 // Attempt SPX2 address file +#define SPX_ADDRFILE_NOACKWAIT 0x0020 // Dont delay acks on assoc connections +#define SPX_ADDRFILE_IPXHDR 0x0040 // Pass ipx hdr on all assoc connections +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** +// If you are adding any more states to this beyond 0x0080, MAKE SURE to go +// in code and change statements like (Flags & SPX_***) to +// ((Flags & SPX_**) != 0)!!! I dont want to make that change that at this stage. +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** + +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. + +#define AREF_ADDR_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _SPX_ADDR { + +#if DBG + ULONG sa_RefTypes[AREF_TOTAL]; +#endif + + USHORT sa_Size; + CSHORT sa_Type; + + // number of references to this object. + ULONG sa_RefCount; + + // next address/this device object. + struct _SPX_ADDR * sa_Next; + + // The following fields are used to maintain state about this address. + // attributes of the address. + ULONG sa_Flags; + + // Next addressfile for this address + struct _SPX_ADDR_FILE * sa_AddrFileList; + + // List of inactive connections and active connections on this address file. + struct _SPX_CONN_FILE * sa_InactiveConnList; + struct _SPX_CONN_FILE * sa_ActiveConnList; + + // This is the list of connections which have a POST_LISTEN on them. They + // do not have a local connection id at this point. But will, when they move + // from here to the ActiveConnList, when the listen is satisfied (no matter + // if the accept has not been posted yet, in the case of non-autoaccept listens) + struct _SPX_CONN_FILE * sa_ListenConnList; + + CTELock sa_Lock; + + // the socket this address corresponds to. + USHORT sa_Socket; + + // device context to which we are attached. + struct _DEVICE * sa_Device; + CTELock * sa_DeviceLock; + +#ifdef ISN_NT + + // These two can be a union because they are not used + // concurrently. + union { + + // This structure is used for checking share access. + SHARE_ACCESS sa_ShareAccess; + + // Used for delaying NbfDestroyAddress to a thread so + // we can access the security descriptor. + WORK_QUEUE_ITEM sa_DestroyAddrQueueItem; + + } u; + + // This structure is used to hold ACLs on the address. + PSECURITY_DESCRIPTOR sa_SecurityDescriptor; + +#endif + +} SPX_ADDR, *PSPX_ADDR; + +#define SPX_ADDR_CLOSING 0x00000001 + + +// ROUTINE PROTOTYPES + +VOID +SpxAddrRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address); + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile); + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile); + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest); + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile); + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address); + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request); + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT struct _SPX_CONN_FILE **ppSpxConnFile); + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrDestroy( + IN PVOID Parameter); + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device); + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +#if DBG +#define SpxAddrReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type],\ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrRef (_Address); \ + } + +#define SpxAddrLockReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrLockRef (_Address); \ + } + +#define SpxAddrDereference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + }\ + } + + +#define SpxAddrFileReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileRef (_AddressFile); \ + } + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileLockRef (_AddressFile); \ + } + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxAddrFileDeref (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxAddrReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock) + +#define SpxAddrLockReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock); + +#define SpxAddrDereference(_Address, _Type) \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + } + +#define SpxAddrFileReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock) + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock); + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + if (SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + (ULONG)-1, \ + (_AddressFile)->saf_AddrLock) == 1) { \ + SpxAddrFileDestroy (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) + +#endif // DBG diff --git a/private/ntos/tdi/isnp/spx/h/spxbind.h b/private/ntos/tdi/isnp/spx/h/spxbind.h new file mode 100644 index 000000000..81ad6ac58 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxbind.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +NTSTATUS +SpxInitBindToIpx( + VOID); + +VOID +SpxUnbindFromIpx( + VOID); + diff --git a/private/ntos/tdi/isnp/spx/h/spxconn.h b/private/ntos/tdi/isnp/spx/h/spxconn.h new file mode 100644 index 000000000..bb1173432 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxconn.h @@ -0,0 +1,1666 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +// Minimum value for RTT in ms. +// BUGBUG: Have these be a derivate of registry values. +#define SPX_T1_MIN 200 +#define MAX_RETRY_DELAY 5000 // 5 seconds +#define SPX_DEF_RENEG_RETRYCOUNT 1 // All reneg pkts except min sent once + +// Some types +typedef enum +{ + SPX_CALL_RECVLEVEL, + SPX_CALL_TDILEVEL +} SPX_CALL_LEVEL; + +typedef enum +{ + SPX_REQ_DATA, + SPX_REQ_ORDREL, + SPX_REQ_DISC + +} SPX_SENDREQ_TYPE; + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Connection. + +#define CFREF_CREATE 0 +#define CFREF_VERIFY 1 +#define CFREF_INDICATION 2 +#define CFREF_BYCTX 3 +#define CFREF_BYID 4 +#define CFREF_ADDR 5 +#define CFREF_REQ 6 +#define CFREF_TIMER 7 +#define CFREF_PKTIZE 8 +#define CFREF_RECV 9 +#define CFREF_ABORTPKT 10 +#define CFREF_ERRORSTATE 11 +#define CFREF_FINDROUTE 12 + +// +// New state added to reflect an SPXI connection which is waiting for +// a local disconnect after having indicated a RELEASE to AFD. +// +#define CFREF_DISCWAITSPX 13 + +#define CFREF_TOTAL 14 + +#define CFMAX_STATES 20 + +typedef struct _SPX_CONN_FILE +{ + +#if DBG + ULONG scf_RefTypes[CFREF_TOTAL]; + +#if 0 +// +// Disabled for now - to enable logging of states, move this array *after* the Type/Size; +// a change in their offset can cause problems since we assume the offset to be less than +// the size of an AddressFile structure. (see SpxTdiQueryInformation) +// + ULONG scf_StateBuffer[CFMAX_STATES]; + ULONG scf_NextStatePtr; +#endif + +#endif + + CSHORT scf_Type; + CSHORT scf_Size; + + // number of references to this object. + ULONG scf_RefCount; + + // Linkage in device address file list. The connection can be on the device + // connection list, address inactive/listen/active list. + struct _SPX_CONN_FILE * scf_Next; + struct _SPX_CONN_FILE * scf_AssocNext; + struct _SPX_CONN_FILE * scf_GlobalActiveNext; + + // Queued in a global list, stays here from creation to destroy. + struct _SPX_CONN_FILE * scf_GlobalNext; + struct _SPX_CONN_FILE * scf_PktNext; + struct _SPX_CONN_FILE * scf_ProcessRecvNext; + + // the current state of the connection. One main state and multiple substates. + ULONG scf_Flags; + + // More information + ULONG scf_Flags2; + +#if DBG + // Save the state of flags/flags2 before reinit. Overwritten every reinit. + ULONG scf_GhostFlags; + ULONG scf_GhostFlags2; + ULONG scf_GhostRefCount; + PREQUEST scf_GhostDiscReq; +#endif + + // Connection retry counts, or watchdog timer count when the connection goes + // active + union + { + LONG scf_CRetryCount; + LONG scf_WRetryCount; + }; + LONG scf_RRetryCount; + USHORT scf_RRetrySeqNum; + + union + { + ULONG scf_CTimerId; + ULONG scf_RTimerId; // Only after we turn active + }; + + ULONG scf_WTimerId; // Watchdog timer + ULONG scf_TTimerId; // TDI Connect/Disconnect timer + ULONG scf_ATimerId; // Ack timer id + + // Variables used to manage the Retry timer tick value + // Note our timer subsytem fires at 100ms granularity. + int scf_BaseT1; + int scf_AveT1; + int scf_DevT1; + + // Stored in HOST-ORDER + // LOCAL variables + USHORT scf_LocalConnId; + USHORT scf_SendSeqNum; // Debug dw +9a + USHORT scf_SentAllocNum; // dw +9c + + // REMOTE variables + USHORT scf_RecvSeqNum; // dw +9e + USHORT scf_RecdAckNum; // dw +a0 + USHORT scf_RecdAllocNum; // dw +a2 + + // RETRY sequence number + USHORT scf_RetrySeqNum; + + // Saved ack number to be used in building the reneg ack packet. + // Note that our RecvSeqNum which we normally use is overwritten + // when we receive a renegotiate request. + USHORT scf_RenegAckAckNum; + + // Stored in NETWORK-ORDER. scf_RemAckAddr contains the remote address + // for a data packet that had the ack bit set, buildAck will use this + // address. + BYTE scf_RemAddr[12]; + BYTE scf_RemAckAddr[12]; + USHORT scf_RemConnId; // Debug dw +be + + // Maximum packet size (or size of first) reneg packet. + USHORT scf_RenegMaxPktSize; + + // Local target to use in when sending acks. This is set to received + // data's indicated local target. + IPX_LOCAL_TARGET scf_AckLocalTarget; + + // Maximum packet size to use for this connection + USHORT scf_MaxPktSize; + UCHAR scf_DataType; + + // Local target to use in sends, initialized upon connect indication + // or when find_route completes + IPX_LOCAL_TARGET scf_LocalTarget; + + // Connection lock + CTELock scf_Lock; + + // address to which we are bound + struct _SPX_ADDR_FILE * scf_AddrFile; + + // Connection context + CONNECTION_CONTEXT scf_ConnCtx; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT scf_FileObject; +#endif + + // LIST_ENTRY of disconnect irps waiting for completion. There could be + // multiple disconnect inform irps. + LIST_ENTRY scf_DiscLinkage; + + // LIST_ENTRY of send requests (intially contains connect/listen/accept also) + // on this connection. + LIST_ENTRY scf_ReqLinkage; + + // Queue for completed requests awaiting completion + LIST_ENTRY scf_ReqDoneLinkage; + LIST_ENTRY scf_RecvDoneLinkage; + + // Queue for pending receives + LIST_ENTRY scf_RecvLinkage; + PREQUEST scf_CurRecvReq; + ULONG scf_CurRecvOffset; + ULONG scf_CurRecvSize; + + // Current request packetize info + PREQUEST scf_ReqPkt; + ULONG scf_ReqPktOffset; + ULONG scf_ReqPktSize; + ULONG scf_ReqPktFlags; + SPX_SENDREQ_TYPE scf_ReqPktType; + + // Single linked list of sequenced send/disc packets + PSPX_SEND_RESD scf_SendSeqListHead; + PSPX_SEND_RESD scf_SendSeqListTail; + + // Single linked list of send (unsequenced) packets + PSPX_SEND_RESD scf_SendListHead; + PSPX_SEND_RESD scf_SendListTail; + + // Single linked list of buffered recv packets. + PSPX_RECV_RESD scf_RecvListHead; + PSPX_RECV_RESD scf_RecvListTail; + + // Connect request + PREQUEST scf_ConnectReq; + + // This holds the request used to close this address file, + // for pended completion. We also pend cleanup requests for connections. + PREQUEST scf_CleanupReq; + PREQUEST scf_CloseReq; + +#if DBG + + // Packet being indicated, seq num, flags/flags2 + USHORT scf_PktSeqNum; + ULONG scf_PktFlags; + ULONG scf_PktFlags2; + + ULONG scf_IndBytes; + ULONG scf_IndLine; +#endif + +#if DBG_WDW_CLOSE + + // Keep track of how long the window was closed on this connection. + ULONG scf_WdwCloseAve; + LARGE_INTEGER scf_WdwCloseTime; // Time when wdw was closed +#endif + + // device to which we are attached. + struct _DEVICE * scf_Device; + +} SPX_CONN_FILE, *PSPX_CONN_FILE; + + +// Basic states +// Least significant byte of flags is used. +// Mutually exclusive states are coded as numbers, others are bit flags. +// Only main states are currently in form of numbers. Also, send and receive. +// +// Once we go active, we need SEND/RECEIVE/DISC substates to be mutually +// exclusive with each other. As all three could be active at the same time. + +// Connection MAIN states. These are all mutually exclusive. +#define SPX_CONNFILE_MAINMASK 0x00000007 +#define SPX_CONNFILE_ACTIVE 0x00000001 +#define SPX_CONNFILE_CONNECTING 0x00000002 +#define SPX_CONNFILE_LISTENING 0x00000003 +#define SPX_CONNFILE_DISCONN 0x00000004 + +// Connecting states (VALID when CONNFILE_CONNECTING) +#define SPX_CONNECT_MASK 0x000000F0 +#define SPX_CONNECT_SENTREQ 0x00000010 +#define SPX_CONNECT_NEG 0x00000020 +#define SPX_CONNECT_W_SETUP 0x00000030 + +// Listening states (VALID when CONNFILE_LISTENING) +#define SPX_LISTEN_MASK 0x000000F0 +#define SPX_LISTEN_RECDREQ 0x00000010 +#define SPX_LISTEN_SENTACK 0x00000020 +#define SPX_LISTEN_NEGACK 0x00000030 +#define SPX_LISTEN_SETUP 0x00000040 + +// Connection SUB states +// Send machine states (VALID when CONNFILE_ACTIVE) +#define SPX_SEND_MASK 0x000000F0 +#define SPX_SEND_IDLE 0x00000000 +#define SPX_SEND_PACKETIZE 0x00000010 +#define SPX_SEND_RETRY 0x00000020 +#define SPX_SEND_RETRYWD 0x00000030 +#define SPX_SEND_RENEG 0x00000040 +#define SPX_SEND_RETRY2 0x00000050 +#define SPX_SEND_RETRY3 0x00000060 +#define SPX_SEND_WD 0x00000070 // We dont reneg pkt size on wdog + // Also we change to this state only + // 2nd time wdog fires w/out ack. +#define SPX_SEND_NAK_RECD 0x00000080 + +// Receive machine states (VALID when CONNFILE_ACTIVE) +#define SPX_RECV_MASK 0x00000F00 +#define SPX_RECV_IDLE 0x00000000 +#define SPX_RECV_POSTED 0x00000100 +#define SPX_RECV_PROCESS_PKTS 0x00000200 + +// Disconnect states (VALID when CONNFILE_DISCONN/CONNFILE_ACTIVE) +// These are valid when either ACTIVE/DISCONN is set. We use these when +// active for a orderly release, i.e. we receive pkt from remote, but we +// stay active (setting SPX_DISC_RECV_ORDREL) until our client posts a +// disconnect, which is when we move to disconnecting. +#define SPX_DISC_MASK 0x0000F000 +#define SPX_DISC_IDLE 0x00000000 +#define SPX_DISC_ABORT 0x00001000 +#define SPX_DISC_SENT_IDISC 0x00002000 +#define SPX_DISC_POST_ORDREL 0x00003000 +#define SPX_DISC_SENT_ORDREL 0x00004000 +#define SPX_DISC_ORDREL_ACKED 0x00005000 +#define SPX_DISC_POST_IDISC 0x00006000 + +// [SA] bug #14655 added flag to indicate that SpxConnInactivate already called for +// this disconnecting connection +// +#define SPX_DISC_INACTIVATED 0x00007000 + +// The following are not mutually exclusive. +#define SPX_CONNFILE_RECVQ 0x00010000 // Process completed receives/pkts +#define SPX_CONNFILE_RENEG_SIZE 0x00020000 // Size changed in renegotiate pkt +#define SPX_CONNFILE_ACKQ 0x00040000 // Waiting to piggyback ack queue +#define SPX_CONNFILE_PKTQ 0x00080000 // Waiting to packetize queue + +#define SPX_CONNFILE_ASSOC 0x00100000 // associated +#define SPX_CONNFILE_NEG 0x00200000 // CR had neg set (for delayed accept) +#define SPX_CONNFILE_SPX2 0x00400000 +#define SPX_CONNFILE_STREAM 0x00800000 +#define SPX_CONNFILE_R_TIMER 0x01000000 // Retry timer (only after ACTIVE) +#define SPX_CONNFILE_C_TIMER 0x01000000 // Connect timer +#define SPX_CONNFILE_W_TIMER 0x02000000 // Watchdog timer +#define SPX_CONNFILE_T_TIMER 0x04000000 // tdi connect/disc timer specified +#define SPX_CONNFILE_RENEG_PKT 0x08000000 // Renegotiate changed size, repacketize +#define SPX_CONNFILE_IND_IDISC 0x10000000 // Indicated abortive disc to afd +#define SPX_CONNFILE_IND_ODISC 0x20000000 // Indicated orderly release to afd + +#define SPX_CONNFILE_STOPPING 0x40000000 +#define SPX_CONNFILE_CLOSING 0x80000000 // closing + +#define SPX_CONNFILE2_PKT_NOIND 0x00000001 +#define SPX_CONNFILE2_RENEGRECD 0x00000002 // A renegotiate was received. + // scf_RenegAckAckNum set. +#define SPX_CONNFILE2_PKT 0x00000004 +#define SPX_CONNFILE2_FINDROUTE 0x00000010 // A find route in progress on conn. +#define SPX_CONNFILE2_NOACKWAIT 0x00000020 // Dont delay acks on connection, option +#define SPX_CONNFILE2_IMMED_ACK 0x00000040 // Send an immediate ack,no back traffic +#define SPX_CONNFILE2_IPXHDR 0x00000080 // Pass ipxhdr in receives + +// +// [SA] Saves the IDisc flag passed to AbortiveDisc; this is TRUE only if there was +// a remote disconnect on an SPX connection (in which case, we indicate TDI_DISCONNECT_RELEASE +// else we indicate TDI_DISCONNECT_ABORT) +// +#define SPX_CONNFILE2_IDISC 0x00000100 + +// +// Indicates an SPXI connfile waiting for a local disconnect in response +// to a TDI_DISCONNECT_RELEASE to AFD. +// +#define SPX_CONNFILE2_DISC_WAIT 0x00000200 + +// FindRoute request structure +typedef struct _SPX_FIND_ROUTE_REQUEST +{ + // !!!!This must be the first element in the structure + IPX_FIND_ROUTE_REQUEST fr_FindRouteReq; + PVOID fr_Ctx; + +} SPX_FIND_ROUTE_REQUEST, *PSPX_FIND_ROUTE_REQUEST; + +typedef struct _SPX_CONNFILE_LIST +{ + PSPX_CONN_FILE pcl_Head; + PSPX_CONN_FILE pcl_Tail; + +} SPX_CONNFILE_LIST, *PSPX_CONNFILE_LIST; + +// Exported routines + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT pConnCtx, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT NTSTATUS * pStatus); + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile); + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn); + +#if DBG +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile); +#endif + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus); + +BOOLEAN +SpxConnDequeuePktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pAckHdr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +// LOCAL functions +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr); + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile); + +USHORT +spxConnGetId( + VOID); + +VOID +spxConnInsertIntoActiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoInactiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoListenList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile); + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt); + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt); + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize); + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size); + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest); + +NTSTATUS +spxConnProcessRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnProcessIndData( + IN PSPX_CONN_FILE pSpxConnFile, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnOrderlyDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnInformedDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN Flag); // [SA] Bug #15249 + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +// +// MACROS +// +#define SHIFT100000 16 + +#define SPX_CONVERT100NSTOCENTISEC(Li) \ + RtlExtendedMagicDivide((Li), Magic100000, SHIFT100000) + +#define UNSIGNED_BETWEEN_WITH_WRAP(Low, High, Target) \ + ((Low <= High) ? ((Target >= Low) && (Target <= High)) : \ + ((Target >= Low) || (Target <= High))) + +// This is with the assumption that the window size will never be greater +// than the difference of 0x8000 and 0x1000. If High is < 1000 and Low +// is > 8000 then we can assume a wrap happened. Otherwise, we assume no +// wrap and do a straight compare. +#define MAX_WINDOW_SIZE 0x6000 +#define DEFAULT_WINDOW_SIZE 8 + +#define UNSIGNED_GREATER_WITH_WRAP(High, Low) \ + (((High < 0x1000) && (Low > 0x8000)) ? TRUE : (High > Low)) + +#define SPX_SET_ACKNUM(pSpxConnFile, RecdAckNum, RecdAllocNum) \ + { \ + DBGPRINT(SEND, DBG, \ + ("SPX_SET_ACKNUM: %lx.%lx = %lx.%lx (%s.%d)\n", \ + (RecdAckNum), (RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAllocNum), \ + __FILE__, __LINE__)); \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAckNum))) \ + { \ + (pSpxConnFile)->scf_RecdAckNum = (RecdAckNum); \ + } \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAllocNum)))\ + { \ + (pSpxConnFile)->scf_RecdAllocNum = (RecdAllocNum); \ + } \ + } + +#define BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum) \ + { \ + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT); \ + } + +#define END_PROCESS_PACKET(pSpxConnFile, fBuffered, fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, \ + (SPX_CONNFILE2_PKT |SPX_CONNFILE2_RENEGRECD)); \ + if (fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); \ + SPX_SET_RECVNUM(pSpxConnFile, fBuffered); \ + } \ + } + +#define INCREMENT_WINDOW(pSpxConnFile) \ + ((pSpxConnFile)->scf_SentAllocNum++) + +#define ADD_TO_WINDOW(pSpxConnFile, numPkts) \ + ((pSpxConnFile)->scf_SentAllocNum += (numPkts)) + +#if DBG_WDW_CLOSE +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + \ + if (fBuffered && \ + (UNSIGNED_GREATER_WITH_WRAP( \ + (pSpxConnFile)->scf_RecvSeqNum, \ + (pSpxConnFile)->scf_SentAllocNum))) \ + { \ + KeQuerySystemTime( \ + (PLARGE_INTEGER)&pSpxConnFile->scf_WdwCloseTime); \ + } \ + } +#else +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + } +#endif + + +#define SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest) \ + { \ + RemoveEntryList(REQUEST_LINKAGE((pRequest))); \ + pSpxConnFile->scf_CurRecvReq = NULL; \ + pSpxConnFile->scf_CurRecvOffset = 0; \ + pSpxConnFile->scf_CurRecvSize = 0; \ + if (!IsListEmpty(&(pSpxConnFile)->scf_RecvLinkage)) \ + { \ + PTDI_REQUEST_KERNEL_RECEIVE _p; \ + DBGPRINT(RECEIVE, DBG, \ + ("spxConnProcessRecv: CURRECV %lx\n", pRequest)); \ + \ + (pSpxConnFile)->scf_CurRecvReq = \ + LIST_ENTRY_TO_REQUEST( \ + (pSpxConnFile)->scf_RecvLinkage.Flink); \ + \ + _p = (PTDI_REQUEST_KERNEL_RECEIVE) \ + REQUEST_PARAMETERS((pSpxConnFile)->scf_CurRecvReq); \ + \ + (pSpxConnFile)->scf_CurRecvOffset = 0; \ + (pSpxConnFile)->scf_CurRecvSize = (_p)->ReceiveLength; \ + } \ + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || \ + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) \ + { \ + SPX_RECV_SETSTATE( \ + pSpxConnFile, \ + (pSpxConnFile->scf_CurRecvReq == NULL) ? \ + SPX_RECV_IDLE : SPX_RECV_POSTED); \ + } \ + } + +#define SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ActiveConnList; \ + (pSpxAddr)->sa_ActiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_INACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_InactiveConnList; \ + (pSpxAddr)->sa_InactiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ListenConnList; \ + (pSpxAddr)->sa_ListenConnList = pSpxConnFile; \ + } + + +// +// STATE MANIPULATION +// + +#if 0 +// +// Disabled for now +// +#define SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_StateBuffer[(pSpxConnFile)->scf_NextStatePtr++] = \ + (pSpxConnFile)->scf_Flags; \ + (pSpxConnFile)->scf_NextStatePtr %= CFMAX_STATES; +#else + +#define SPX_STORE_LAST_STATE(pSpxConnFile) + +#endif + +#define SPX_MAIN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNFILE_MAINMASK) + +// #define SPX_CONN_IDLE(pSpxConnFile) \ +// ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == 0)) + +#define SPX_CONN_IDLE(pSpxConnFile) \ + ((BOOLEAN)((SPX_MAIN_STATE(pSpxConnFile) == 0) || \ + ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && \ + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)))) + +#define SPX_CONN_ACTIVE(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE)) + +#define SPX_CONN_CONNECTING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_CONNECTING)) + +#define SPX_CONN_LISTENING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_LISTENING)) + +#define SPX_CONN_DISC(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN)) + +#if DBG + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#else + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#endif + +#define SPX_CONN_FLAG(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags & (Flag)) != 0)) + +#define SPX_CONN_FLAG2(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags2 & (Flag)) != 0)) + +#if DBG + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) +#else + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) + +#endif + +#define SPX_CONN_SETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 |= (Flag)) + +#define SPX_CONN_RESETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags &= ~(Flag)) + +#define SPX_CONN_RESETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 &= ~(Flag)) + +#define SPX2_CONN(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_SPX2)) + +#define SPX_CONN_STREAM(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_CONN_MSG(pSpxConnFile) \ + (!SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_LISTEN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_LISTEN_MASK) + +#define SPX_CONNECT_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNECT_MASK) + +#define SPX_SEND_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_SEND_MASK) + +#define SPX_RECV_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_RECV_MASK) + +#define SPX_DISC_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_DISC_MASK) + +#if DBG + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } + +#else + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } +#endif //DBG +#define SpxConnQueueSendPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendListTail->sr_Next = _pSendResd; \ + (pSpxConnFile)->scf_SendListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } + +#define SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendSeqListTail->sr_Next = _pSendResd;\ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendSeqPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendSeqListHead;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } + +#define SpxConnQueueRecvPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + (pSpxConnFile)->scf_RecvListTail->rr_Next = _pRecvResd; \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } \ + } + +#define SpxConnQueueRecvPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + _pRecvResd->rr_Next = (pSpxConnFile)->scf_RecvListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd; \ + } \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } + +#if DBG +#define SpxConnFileReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileRef (_ConnFile); \ + } + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileLockRef (_ConnFile); \ + } + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxConnFileDeref (_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _l; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_l)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, _l); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxConnFileReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock) + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock); + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + SpxConnFileDeref(_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_lockHandle)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, (_lockHandle)); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) + +#endif // DBG + + +// Set the packet size. If we are spx1 or spx2 and !neg, check if we are different +// nets, set to min then, else use the size indicated by IPX. If we are spx2, just +// set it to our local max. +// +// Also always even out packet size and round down. This solves an issue with +// data size needing to be even for some novell 802.2 clients. +// +// Fix after beta2 for tokring using receive size. Only if spx2 and neg. +#if defined(_PNP_POWER) +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + &(pSpxConnFile)->scf_LocalTarget.NicHandle, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#else +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + (pSpxConnFile)->scf_LocalTarget.NicId, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#endif _PNP_POWER + +#if DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#else // DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#endif // DBG + +#define SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile) \ + { \ + if (!SPX_CONN_FLAG( \ + (pSpxConnFile), \ + SPX_CONNFILE_RECVQ)) \ + { \ + SPX_CONN_SETFLAG((pSpxConnFile), SPX_CONNFILE_RECVQ); \ + SpxConnFileLockReference(pSpxConnFile, CFREF_RECV); \ + SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile); \ + } \ + } + +#define SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile) \ + { \ + if (SpxPktConnList.pcl_Tail) \ + { \ + SpxPktConnList.pcl_Tail->scf_PktNext = pSpxConnFile; \ + SpxPktConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxPktConnList.pcl_Tail = \ + SpxPktConnList.pcl_Head = pSpxConnFile; \ + } \ + } + +#define SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile) \ + { \ + if (SpxRecvConnList.pcl_Tail) \ + { \ + SpxRecvConnList.pcl_Tail->scf_ProcessRecvNext = pSpxConnFile; \ + SpxRecvConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxRecvConnList.pcl_Tail = \ + SpxRecvConnList.pcl_Head = pSpxConnFile; \ + } \ + } + + diff --git a/private/ntos/tdi/isnp/spx/h/spxdev.h b/private/ntos/tdi/isnp/spx/h/spxdev.h new file mode 100644 index 000000000..30c0adae5 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxdev.h @@ -0,0 +1,204 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +// Hash buckets for SPX_ADDR done using socket number +#define NUM_SPXADDR_HASH_BUCKETS 8 +#define NUM_SPXADDR_HASH_MASK 7 +#define NUM_SPXCONN_HASH_BUCKETS 8 +#define NUM_SPXCONN_HASH_MASK 7 + +// This structure defines the per-device structure for SPX +// (one of these is allocated globally). +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_ORPHAN 4 + +#define DREF_TOTAL 5 + +typedef struct _DEVICE { + + DEVICE_OBJECT dev_DevObj; // the I/O system's device object. + +#if DBG + ULONG dev_RefTypes[DREF_TOTAL]; +#endif + + CSHORT dev_Type; // type of this structure + USHORT dev_Size; // size of this structure + +#if DBG + UCHAR dev_Signature1[4]; // contains "SPX1" +#endif + + // activity count/this provider. + LONG dev_RefCount; + UCHAR dev_State; + + // number of adapters IPX is bound to. + USHORT dev_Adapters; + + // GLOBAL lock for reference count (used in ExInterlockedXxx calls). + CTELock dev_Interlock; + CTELock dev_Lock; + + // Hash table of lists of addresses opened on this device + struct _SPX_ADDR * dev_AddrHashTable[NUM_SPXADDR_HASH_BUCKETS]; + + // List of all active connections, later this be a tree. + struct _SPX_CONN_FILE * dev_GlobalActiveConnList[NUM_SPXCONN_HASH_BUCKETS]; + USHORT dev_NextConnId; + + // Other configuration parameters. + // Where the current socket allocation is. + USHORT dev_CurrentSocket; + + // Our node and network. + UCHAR dev_Network[4]; + UCHAR dev_Node[6]; + + // Pointer to the config information from registry + PCONFIG dev_ConfigInfo; + + // Control channel identifier + ULONG dev_CcId; + + // These are kept around for error logging, and stored right + // after this structure. + PWCHAR dev_DeviceName; +#if defined(_PNP_POWER) + USHORT dev_DeviceNameLen; +#else + ULONG dev_DeviceNameLen; +#endif _PNP_POWER + +#if DBG + UCHAR dev_Signature2[4]; // contains "SPX2" +#endif + + // Handle to ndis buffer pool for spx stack. + NDIS_HANDLE dev_NdisBufferPoolHandle; + + // registration handle with tdi clients. +#if defined(_PNP_POWER) + HANDLE dev_TdiRegistrationHandle; +#endif _PNP_POWER + + // This interlock is used to guard access to the statistics + // define below. + KSPIN_LOCK dev_StatInterlock; // for ULONG quantities + KSPIN_LOCK dev_StatSpinLock; // for LARGE_INTEGER quantities + + // Counters for most of the statistics that SPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + TDI_PROVIDER_STATISTICS dev_Stat; + + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + ERESOURCE dev_AddrResource; + + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + TDI_PROVIDER_INFO dev_ProviderInfo; // information about this provider. + +} DEVICE, * PDEVICE; + +// device state definitions +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +// SPX device name +#define SPX_DEVICE_NAME L"\\Device\\NwlnkSpx" + +#define SPX_TDI_RESOURCES 9 + + +// MACROS +#if DBG + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxDerefDevice (_Device); \ + } + +#else + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + SpxDerefDevice (_Device) + +#endif + +// EXPORTED ROUTINES + +VOID +SpxDestroyDevice( + IN PDEVICE Device); + +VOID +SpxDerefDevice( + IN PDEVICE Device); + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr); diff --git a/private/ntos/tdi/isnp/spx/h/spxerror.h b/private/ntos/tdi/isnp/spx/h/spxerror.h new file mode 100644 index 000000000..761342512 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxerror.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxerror.h + +Abstract: + + This module contains some error definitions for spx. + +Author: + + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + +Notes: Tab stop: 4 +--*/ + +// Define the modules names for SPX - use the high bits. +#define SPXDRVR 0x00010000 +#define SPXREG 0x00020000 +#define SPXDEV 0x00030000 +#define SPXBIND 0x00040000 +#define SPXRECV 0x00050000 +#define SPXSEND 0x00060000 +#define SPXTIMER 0x00070000 +#define SPXERROR 0x00080000 +#define SPXPKT 0x00090000 +#define SPXUTILS 0x000a0000 +#define SPXCPKT 0x000b0000 +#define SPXCONN 0x000c0000 +#define SPXADDR 0x000d0000 +#define SPXCUTIL 0x000e0000 +#define SPXINIT 0x000f0000 +#define SPXMEM 0x00100000 +#define SPXQUERY 0x00200000 + + +// DEBUGGING SUPPORT: +// Debugging messages are provided per-subsystem defined here, and within +// the subsystems, there are 4 levels of messages. +// +// The four levels of debug messages are: +// +// INFO: Informational messages, eg., entry exit in routines +// DBG: Used when debugging some msgs are turned from info to dbg +// WARN: Something went wrong, but its not an error, eg., packet was not ours +// ERR: Error situations, but we can still run if a retry happens +// FATAL: In this situation, the driver is not operational + +#define DBG_LEVEL_INFO 0x4000 +#define DBG_LEVEL_DBG 0x5000 +#define DBG_LEVEL_DBG1 0x5001 +#define DBG_LEVEL_DBG2 0x5002 +#define DBG_LEVEL_DBG3 0x5003 +#define DBG_LEVEL_WARN 0x6000 +#define DBG_LEVEL_ERR 0x7000 +#define DBG_LEVEL_FATAL 0x8000 + +// SUBSYSTEMS +#define DBG_COMP_DEVICE 0x00000001 +#define DBG_COMP_CREATE 0x00000002 +#define DBG_COMP_ADDRESS 0x00000004 +#define DBG_COMP_SEND 0x00000008 +#define DBG_COMP_NDIS 0x00000010 +#define DBG_COMP_RECEIVE 0x00000020 +#define DBG_COMP_CONFIG 0x00000040 +#define DBG_COMP_PACKET 0x00000080 +#define DBG_COMP_RESOURCES 0x00000100 +#define DBG_COMP_BIND 0x00000200 +#define DBG_COMP_UNLOAD 0x00000400 +#define DBG_COMP_DUMP 0x00000800 +#define DBG_COMP_REFCOUNTS 0x00001000 +#define DBG_COMP_SYSTEM 0x00002000 +#define DBG_COMP_CRITSEC 0x00004000 +#define DBG_COMP_UTILS 0x00008000 +#define DBG_COMP_TDI 0x00010000 +#define DBG_COMP_CONNECT 0x00020000 +#define DBG_COMP_DISC 0x00040000 +#define DBG_COMP_ACTION 0x00080000 +#define DBG_COMP_STATE 0x00100000 + +#define DBG_COMP_MOST (DBG_COMP_DEVICE | \ + DBG_COMP_CREATE | \ + DBG_COMP_ADDRESS | \ + DBG_COMP_SEND | \ + DBG_COMP_NDIS | \ + DBG_COMP_RECEIVE | \ + DBG_COMP_CONFIG | \ + DBG_COMP_PACKET | \ + DBG_COMP_RESOURCES | \ + DBG_COMP_BIND | \ + DBG_COMP_UNLOAD | \ + DBG_COMP_DUMP | \ + DBG_COMP_REFCOUNTS | \ + DBG_COMP_SYSTEM | \ + DBG_COMP_CRITSEC | \ + DBG_COMP_UTILS | \ + DBG_COMP_TDI | \ + DBG_COMP_CONNECT | \ + DBG_COMP_DISC | \ + DBG_COMP_ACTION | \ + DBG_COMP_STATE) + + +// More debugging support. These values define the dumping components. +// There are a max of 32 such components that can be defined. Each of +// these are associated with a dump routine. It one is specified and +// enabled, periodically it is called. It is upto that component to +// decide what it wants to do + +#define DBG_DUMP_DEF_INTERVAL 30 // In Seconds + +// This defines the number of times an error has to happen consecutively before +// it gets logged again. +#define ERROR_CONSEQ_FREQ 200 +#define ERROR_CONSEQ_TIME (60*30) // 30 minutes + +#ifdef DBG +typedef VOID (*DUMP_ROUTINE)(VOID); + +extern +BOOLEAN +SpxDumpComponents( + IN PVOID Context); + +#endif + +// +// PROTOTYPES +// + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue); + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); + + +// +// MACROS +// + +#if DBG +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#else + +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#endif + + +#if DBG + +#define DBGPRINT(Component, Level, Fmt) \ + { \ + if (((DBG_LEVEL_ ## Level) >= SpxDebugLevel) && \ + (SpxDebugSystems & (DBG_COMP_ ## Component))) \ + { \ + DbgPrint("SPX: "); \ + DbgPrint Fmt; \ + } \ + } + +#define DBGBRK(Level) \ + { \ + if ((DBG_LEVEL_ ## Level) >= SpxDebugLevel) \ + DbgBreakPoint(); \ + } + +#define TMPLOGERR() \ + { \ + DBGPRINT(MOST, ERR, \ + ("TempErrLog: %s, Line %ld\n", __FILE__, __LINE__)); \ + } + +#else +#define DBGPRINT(Component, Level, Fmt) +#define DBGBRK(Level) +#define TMPLOGERR() +#endif + +extern +VOID +SpxWriteErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); diff --git a/private/ntos/tdi/isnp/spx/h/spxmem.h b/private/ntos/tdi/isnp/spx/h/spxmem.h new file mode 100644 index 000000000..717c69a6b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxmem.h @@ -0,0 +1,142 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.h + +Abstract: + + This module contains memory management routines. + +Author: + + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +#define QWORDSIZEBLOCK(Size) (((Size)+sizeof(LARGE_INTEGER)-1) & ~(sizeof(LARGE_INTEGER)-1)) +#define SPX_MEMORY_SIGNATURE *(PULONG)"SPXM" +#define ZEROED_MEMORY_TAG 0xF0000000 +#define SPX_TAG *((PULONG)"SPX ") + +// +// Definitions for the block management package +// +typedef UCHAR BLKID; + +// Add a BLKID_xxx and an entry to atalkBlkSize for every block client +#define BLKID_TIMERLIST (BLKID)0 +#define BLKID_NDISSEND (BLKID)1 +#define BLKID_NDISRECV (BLKID)2 +#define NUM_BLKIDS (BLKID)3 + +typedef struct _BLK_CHUNK +{ + struct _BLK_CHUNK * bc_Next; // Pointer to next in the link + SHORT bc_NumFrees; // Number of free blocks in the chunk + UCHAR bc_Age; // Number of invocations since the chunk free + BLKID bc_BlkId; // Id of the block + struct _BLK_HDR * bc_FreeHead; // Head of the list of free blocks + +#ifndef SPX_OWN_PACKETS + PVOID bc_ChunkCtx; // Used to store pool header if not own + // packets +#else + PVOID bc_Padding; // Keep the header 16 bytes +#endif + + // This is followed by an array of N blks of size M such that the block header + // is exactly spxChunkSize[i] + +} BLK_CHUNK, *PBLK_CHUNK; + +typedef struct _BLK_HDR +{ + union + { + struct _BLK_HDR * bh_Next; // Valid when it is free + struct _BLK_CHUNK * bh_pChunk; // The parent chunk to which this blocks belong + // valid when it is allocated + }; + PVOID bh_Padding; // Make the header 8 bytes +} BLK_HDR, *PBLK_HDR; + +#define BC_OVERHEAD (8+8) // LARGE_INTEGER for SpxAllocMemory() header and + // POOL_HEADER for ExAllocatePool() header + +#define BLOCK_POOL_TIMER 1000 // Check interval (1 sec) +#define MAX_BLOCK_POOL_AGE 3 // # of timer invocations before free + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + + +#ifdef TRACK_MEMORY_USAGE + +#define SpxAllocateMemory(Size) SpxAllocMem((Size), FILENUM | __LINE__) + +extern +PVOID +SpxAllocMem( + IN ULONG Size, + IN ULONG FileLine +); + +extern +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine +); + +#else + +#define SpxAllocateMemory(Size) SpxAllocMem(Size) +#define SpxTrackMemoryUsage(pMem, Alloc, FileLine) + +extern +PVOID +SpxAllocMem( + IN ULONG Size +); + +#endif // TRACK_MEMORY_USAGE + +VOID +SpxFreeMemory( + IN PVOID pBuf); + +#define SpxAllocateZeroedMemory(Size) SpxAllocateMemory((Size) | ZEROED_MEMORY_TAG) + + +extern +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice); + +extern +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice); + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId); + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId); + diff --git a/private/ntos/tdi/isnp/spx/h/spxntdef.h b/private/ntos/tdi/isnp/spx/h/spxntdef.h new file mode 100644 index 000000000..60f9c9e54 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxntdef.h @@ -0,0 +1,72 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxntdef.h + +Abstract: + + Missing nt definitions in ntddk.h + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +NTSTATUS +NTAPI +NtCreateFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength + ); + +NTSTATUS +NTAPI +NtClose( + IN HANDLE Handle + ); + +NTSTATUS +NTAPI +NtDeviceIoControlFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG IoControlCode, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength + ); + +NTSTATUS +NTAPI +NtWaitForSingleObject( + IN HANDLE Handle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout OPTIONAL + ); + + diff --git a/private/ntos/tdi/isnp/spx/h/spxpkt.h b/private/ntos/tdi/isnp/spx/h/spxpkt.h new file mode 100644 index 000000000..b643ed95b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxpkt.h @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// Use our own NDIS packets +#define SPX_OWN_PACKETS 1 + +// Offsets into the IPX header +#define IPX_HDRSIZE 30 // Size of the IPX header +#define IPX_CHECKSUM 0 // Checksum +#define IPX_LENGTH 2 // Length +#define IPX_XPORTCTL 4 // Transport Control +#define IPX_PKTTYPE 5 // Packet Type +#define IPX_DESTADDR 6 // Dest. Address (Total) +#define IPX_DESTNET 6 // Dest. Network Address +#define IPX_DESTNODE 10 // Dest. Node Address +#define IPX_DESTSOCK 16 // Dest. Socket Number +#define IPX_SRCADDR 18 // Source Address (Total) +#define IPX_SRCNET 18 // Source Network Address +#define IPX_SRCNODE 22 // Source Node Address +#define IPX_SRCSOCK 28 // Source Socket Number + +#define IPX_NET_LEN 4 +#define IPX_NODE_LEN 6 + + +#include <packon.h> + +// Definition of the IPX/SPX header. +typedef struct _IPXSPX_HEADER +{ + USHORT hdr_CheckSum; + USHORT hdr_PktLen; + UCHAR hdr_XportCtrl; + UCHAR hdr_PktType; + UCHAR hdr_DestNet[4]; + UCHAR hdr_DestNode[6]; + USHORT hdr_DestSkt; + UCHAR hdr_SrcNet[4]; + UCHAR hdr_SrcNode[6]; + USHORT hdr_SrcSkt; + + // SPX Header Elements + UCHAR hdr_ConnCtrl; + UCHAR hdr_DataType; + USHORT hdr_SrcConnId; + USHORT hdr_DestConnId; + USHORT hdr_SeqNum; + USHORT hdr_AckNum; + USHORT hdr_AllocNum; + + // For non-CR SPXII packets only + USHORT hdr_NegSize; + +} IPXSPX_HDR, *PIPXSPX_HDR; + +#include <packoff.h> + +// NDIS Packet size +#define NDIS_PACKET_SIZE 48 + +// Minimum header size (doesnt include neg size) +#define MIN_IPXSPX_HDRSIZE (sizeof(IPXSPX_HDR) - sizeof(USHORT)) +#define MIN_IPXSPX2_HDRSIZE sizeof(IPXSPX_HDR) +#define SPX_CR_PKTLEN 42 + +// SPX packet type +#define SPX_PKT_TYPE 0x5 + +// Connection control fields +#define SPX_CC_XHD 0x01 +#define SPX_CC_RES1 0x02 +#define SPX_CC_NEG 0x04 +#define SPX_CC_SPX2 0x08 +#define SPX_CC_EOM 0x10 +#define SPX_CC_ATN 0x20 +#define SPX_CC_ACK 0x40 +#define SPX_CC_SYS 0x80 + +#define SPX_CC_CR (SPX_CC_ACK | SPX_CC_SYS) + +// Data stream types +#define SPX2_DT_ORDREL 0xFD +#define SPX2_DT_IDISC 0xFE +#define SPX2_DT_IDISC_ACK 0xFF + +// Negotiation size +#define SPX_MAX_PACKET 576 +#define SPX_NEG_MIN SPX_MAX_PACKET +#define SPX_NEG_MAX 65535 + +// No packet references connection. But if the sends are being aborted, and +// the packet happens to be owned by ipx at the time, the pkt is dequeued from +// conn, the ABORT flag is set and conn is referenced for packet. +// +// Send packet states +// ABORT : Used for aborted packet. Calls AbortSendPkt(). +// IPXOWNS : Currently owned by ipx +// FREEDATA: Frees the data associated with second ndis buffer desc +// ACKREQ : Only for sequenced packets. Set by retry timer in packets it wants +// resent (1 for spx1, all pending for spx2) with ack bit set. +// DESTROY : Only for non-sequenced packets, dequeue packet from list and free. +// REQ : For both seq/non-seq. A request is associated with the packet +// SEQ : Packet is a sequenced packet. +// LASTPKT : Packet is last packet comprising the request, if acked req is done. +// EOM : Send EOM with the last packet for this request +// ACKEDPKT: Send completion must only deref req with pkt and complete if zero. +// + +#define SPX_SENDPKT_IDLE 0 +#define SPX_SENDPKT_ABORT 0x0002 +#define SPX_SENDPKT_IPXOWNS 0x0004 +#define SPX_SENDPKT_FREEDATA 0x0008 +#define SPX_SENDPKT_ACKREQ 0x0010 +#define SPX_SENDPKT_DESTROY 0x0020 +#define SPX_SENDPKT_REQ 0x0040 +#define SPX_SENDPKT_SEQ 0x0080 +#define SPX_SENDPKT_LASTPKT 0x0100 +#define SPX_SENDPKT_ACKEDPKT 0x0200 +#define SPX_SENDPKT_EOM 0x0400 +#define SPX_SENDPKT_REXMIT 0x0800 + +// Packet types +#define SPX_TYPE_CR 0x01 +#define SPX_TYPE_CRACK 0x02 +#define SPX_TYPE_SN 0x03 +#define SPX_TYPE_SNACK 0x04 +#define SPX_TYPE_SS 0x05 +#define SPX_TYPE_SSACK 0x06 +#define SPX_TYPE_RR 0x07 +#define SPX_TYPE_RRACK 0x08 +#define SPX_TYPE_IDISC 0x09 +#define SPX_TYPE_IDISCACK 0x0a +#define SPX_TYPE_ORDREL 0x0b +#define SPX_TYPE_ORDRELACK 0x0c +#define SPX_TYPE_DATA 0x0d +#define SPX_TYPE_DATAACK 0x0e +#define SPX_TYPE_DATANACK 0x0f +#define SPX_TYPE_PROBE 0x10 + +// Definition of the protocol reserved field of a send packet. +// BUGBUG: Make Len/HdrLen USHORTS, move to the end before the +// sr_SentTime so we dont use padding space. +typedef struct _SPX_SEND_RESD +{ + UCHAR sr_Id; // Set to SPX + UCHAR sr_Type; // What kind of packet + USHORT sr_State; // State of send packet + PVOID sr_Reserved1; // Needed by IPX + PVOID sr_Reserved2; // Needed by IPX +#if defined(_PNP_POWER) + PVOID sr_Reserved[SEND_RESERVED_COMMON_SIZE-2]; // needed by IPX for local target +#endif _PNP_POWER + ULONG sr_Len; // Length of packet + ULONG sr_HdrLen; // Included header length + + struct _SPX_SEND_RESD * sr_Next; // Points to next packet + // in send queue in conn. + PREQUEST sr_Request; // request associated + ULONG sr_Offset; // Offset in mdl for sends + +#ifndef SPX_OWN_PACKETS + PVOID sr_FreePtr; // Ptr to use in free chunk +#endif + + struct _SPX_CONN_FILE * sr_ConnFile; // that this send is on + USHORT sr_SeqNum; // Seq num for seq pkts + + // Quad word aligned. + LARGE_INTEGER sr_SentTime; // Time packet was sent + // Only valid for data pkt + // with ACKREQ set. + +} SPX_SEND_RESD, *PSPX_SEND_RESD; + + + +// Recv packet states +#define SPX_RECVPKT_IDLE 0 +#define SPX_RECVPKT_BUFFERING 0x0001 +#define SPX_RECVPKT_IDISC 0x0002 +#define SPX_RECVPKT_ORD_DISC 0x0004 +#define SPX_RECVPKT_INDICATED 0x0008 +#define SPX_RECVPKT_SENDACK 0x0010 +#define SPX_RECVPKT_EOM 0x0020 +#define SPX_RECVPKT_IMMEDACK 0x0040 + +#define SPX_RECVPKT_DISCMASK (SPX_RECVPKT_ORD_DISC | SPX_RECVPKT_IDISC) + +// Definition of the protocol reserved field of a receive packet. +typedef struct _SPX_RECV_RESD +{ + UCHAR rr_Id; // Set to SPX + USHORT rr_State; // State of receive packet + struct _SPX_RECV_RESD * rr_Next; // Points to next packet + ULONG rr_DataOffset; // To indicate/copy from + +#ifndef SPX_OWN_PACKETS + PVOID rr_FreePtr; // Ptr to use in free chunk +#endif + +#if DBG + USHORT rr_SeqNum; // Seq num of packet +#endif + + PREQUEST rr_Request; // request waiting on xfer + struct _SPX_CONN_FILE * rr_ConnFile; // that this recv is on + +} SPX_RECV_RESD, *PSPX_RECV_RESD; + + +// Destination built as an assign of 3 ulongs. +#define SpxBuildIpxHdr(pIpxSpxHdr, PktLen, pRemAddr, SrcSkt) \ + { \ + PBYTE pDestIpxAddr = (PBYTE)pIpxSpxHdr->hdr_DestNet; \ + (pIpxSpxHdr)->hdr_CheckSum = 0xFFFF; \ + PUTSHORT2SHORT((PUSHORT)(&(pIpxSpxHdr)->hdr_PktLen), (PktLen)); \ + (pIpxSpxHdr)->hdr_XportCtrl = 0; \ + (pIpxSpxHdr)->hdr_PktType = SPX_PKT_TYPE; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNet))= \ + *((UNALIGNED ULONG *)(SpxDevice->dev_Network)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNode)) = \ + *((UNALIGNED ULONG *)SpxDevice->dev_Node); \ + *((UNALIGNED USHORT *)((pIpxSpxHdr)->hdr_SrcNode+4)) = \ + *((UNALIGNED USHORT *)(SpxDevice->dev_Node+4)); \ + *((UNALIGNED USHORT *)&((pIpxSpxHdr)->hdr_SrcSkt)) = \ + SrcSkt; \ + } + +#define SpxCopyIpxAddr(pIpxSpxHdr, pDestIpxAddr) \ + { \ + PBYTE pRemAddr = (PBYTE)pIpxSpxHdr->hdr_SrcNet; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + } + +#ifndef SPX_OWN_PACKETS +#define SpxAllocSendPacket(_Device,_SendPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_SEND_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket), (_Handle)); \ + SEND_RESD(_SendPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_RECV_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_RecvPacket), (_Handle)); \ + RECV_RESD(_RecvPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxReInitSendPacket(_Packet) + { + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; + NdisReInitializePacket(_Packet); + SEND_RESD(_Packet)->sr_PoolHandle = (_Handle); + } + +#define SpxReInitRecvPacket(_Packet) + { + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; + NdisReInitializePacket(_Packet); + RECV_RESD(_Packet)->rr_PoolHandle = (_Handle); + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#else + +#define SpxAllocSendPacket(_Device, _SendPacket, _Status) \ + { \ + if (*(_SendPacket) = SpxBPAllocBlock(BLKID_NDISSEND)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + if (*(_RecvPacket) = SpxBPAllocBlock(BLKID_NDISRECV)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISSEND); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISRECV); \ + } + +#define SpxReInitSendPacket(_Packet) \ + { \ + } + +#define SpxReInitRecvPacket(_Packet) \ + { \ + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#endif + + +// +// Routine Prototypes +// + +VOID +SpxPktBuildCr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildCrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildSn( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSs( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSsAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSnAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildRr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State); + +VOID +SpxPktBuildRrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize); + +VOID +SpxPktBuildProbe( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildData( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length); + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ); + +VOID +SpxPktBuildAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend); + +VOID +SpxPktBuildDisc( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType); + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt); + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt); diff --git a/private/ntos/tdi/isnp/spx/h/spxquery.h b/private/ntos/tdi/isnp/spx/h/spxquery.h new file mode 100644 index 000000000..68e0a1ca8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxquery.h @@ -0,0 +1,54 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define SPX_TDI_PROVIDERINFO_VERSION 0x0001 +#define SPX_PINFOSENDSIZE 0xFFFFFFFF +#define SPX_PINFOMINMAXLOOKAHEAD 128 + +// +// Bug #14498: Indicate to AFD that we are capable of orderly disc so AFD can follow +// these semantics. +// In order to have SPXI connections work correctly, we OR this bit in at query time. +// (see SpxTdiQueryInformation) +// +#define SPX_PINFOSERVICEFLAGS ( TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_DELAYED_ACCEPTANCE | \ + TDI_SERVICE_MESSAGE_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY) // | \ + // TDI_SERVICE_ORDERLY_RELEASE ) + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo); + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request); + diff --git a/private/ntos/tdi/isnp/spx/h/spxrecv.h b/private/ntos/tdi/isnp/spx/h/spxrecv.h new file mode 100644 index 000000000..0ce382990 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxrecv.h @@ -0,0 +1,89 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +VOID +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred); + +VOID +SpxReceiveComplete( + IN USHORT NicId); + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize); + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn); + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + diff --git a/private/ntos/tdi/isnp/spx/h/spxreg.h b/private/ntos/tdi/isnp/spx/h/spxreg.h new file mode 100644 index 000000000..4e0cb469b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxreg.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.h + +Abstract: + + Private include file for the ISN SPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + +#define HALFSEC_TO_MS_FACTOR 500 +#define IPX_REG_PATH L"NwlnkIpx\\Linkage" + +// These are used to index into the Parameters array in CONFIG. +#define CONFIG_CONNECTION_COUNT 0 +#define CONFIG_CONNECTION_TIMEOUT 1 +#define CONFIG_INIT_PACKETS 2 +#define CONFIG_MAX_PACKETS 3 +#define CONFIG_INITIAL_RETRANSMIT_TIMEOUT 4 +#define CONFIG_KEEPALIVE_COUNT 5 +#define CONFIG_KEEPALIVE_TIMEOUT 6 +#define CONFIG_WINDOW_SIZE 7 +#define CONFIG_SOCKET_RANGE_START 8 +#define CONFIG_SOCKET_RANGE_END 9 +#define CONFIG_SOCKET_UNIQUENESS 10 +#define CONFIG_MAX_PACKET_SIZE 11 +#define CONFIG_REXMIT_COUNT 12 + +// Hidden parameters +#define CONFIG_DISABLE_SPX2 13 +#define CONFIG_ROUTER_MTU 14 +#define CONFIG_BACKCOMP_SPX 15 + +#define CONFIG_PARAMETERS 16 + +// Main configuration structure. +typedef struct _CONFIG { + + ULONG cf_Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING cf_DeviceName; // device name exported + PWSTR cf_RegistryPathBuffer; // path to config info + +} CONFIG, * PCONFIG; + + +#define PARAM(x) (SpxDevice->dev_ConfigInfo->cf_Parameters[(x)]) + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr); + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config); + diff --git a/private/ntos/tdi/isnp/spx/h/spxsend.h b/private/ntos/tdi/isnp/spx/h/spxsend.h new file mode 100644 index 000000000..40ef810ea --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxsend.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +VOID +SpxSendComplete( + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus); + +VOID +SpxSendPktRelease( + IN PNDIS_PACKET pPkt, + IN UINT BufCount); diff --git a/private/ntos/tdi/isnp/spx/h/spxtimer.h b/private/ntos/tdi/isnp/spx/h/spxtimer.h new file mode 100644 index 000000000..225037bd2 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxtimer.h @@ -0,0 +1,101 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.h + +Abstract: + + This module contains routines to schedule timer events. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + 19 Jun 1992 Initial Version + +Notes: Tab stop: 4 +--*/ + +#define TIMER_DONT_REQUEUE 0 +#define TIMER_REQUEUE_CUR_VALUE 1 + +typedef ULONG (*TIMER_ROUTINE)(IN PVOID Context, IN BOOLEAN TimerShuttingDown); + +extern +NTSTATUS +SpxTimerInit( + VOID); + +extern +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG DeltaTime, // Schedule after this much time + IN PVOID pContext); // Context to pass to the routine + +extern +VOID +SpxTimerFlushAndStop( + VOID); + +extern +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue); + +#define TMR_SIGNATURE *(PULONG)"ATMR" +#if DBG +#define VALID_TMR(pTmr) (((pTmr) != NULL) && \ + ((pTmr)->tmr_Signature == TMR_SIGNATURE)) +#else +#define VALID_TMR(pTmr) ((pTmr) != NULL) +#endif +typedef struct _TimerList +{ +#if DBG + ULONG tmr_Signature; +#endif + struct _TimerList * tmr_Next; // Link to next + struct _TimerList ** tmr_Prev; // Link to prev + struct _TimerList * tmr_Overflow; // Link to overflow entry in hash table + ULONG tmr_AbsTime; // Absolute time, for re-enqueue + ULONG tmr_RelDelta; // Relative to the previous entry + ULONG tmr_Id; // Unique Id for this event + BOOLEAN tmr_Cancelled; // Was the timer cancelled? + TIMER_ROUTINE tmr_Worker; // Real Worker + PVOID tmr_Context; // Real context +} TIMERLIST, *PTIMERLIST; + + +#define SpxGetCurrentTime() (SpxTimerCurrentTime/SPX_TIMER_FACTOR) +#define SpxGetCurrentTick() SpxTimerCurrentTime + +// Keep this at a ONE second level. +#define SPX_TIMER_FACTOR 10 // i.e. 10 ticks per second +#define SPX_MS_TO_TICKS 100 // Divide ms by this to get ticks +#define SPX_TIMER_TICK -1000000L // 100ms in 100ns units +#define SPX_TIMER_WAIT 50 // Time to wait in FlushAndStop in ms +#define TIMER_HASH_TABLE 32 + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); + +VOID +spxTimerWorker( + IN PTIMERLIST pList); + +VOID +spxTimerEnqueue( + PTIMERLIST pListNew); + + diff --git a/private/ntos/tdi/isnp/spx/h/spxutils.h b/private/ntos/tdi/isnp/spx/h/spxutils.h new file mode 100644 index 000000000..074a1649b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxutils.h @@ -0,0 +1,178 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// For PROTO_SPX, i'd return a device name from the dll of the form +// \Device\NwlnkSpx\SpxStream (for SOCK_STREAM) or +// \Device\NwlnkSpx\Spx (for SOCK_SEQPKT) +// +// and for PROTO_SPXII (the more common case we hope, even if +// internally we degrade to SPX1 cause of the remote client's +// limitations) +// \Device\NwlnkSpx\Stream (for SOCK_STREAM) or +// \Device\NwlnkSpx (for SOCK_SEQPKT) + +#define SOCKET1STREAM_SUFFIX L"\\SpxStream" +#define SOCKET1_SUFFIX L"\\Spx" +#define SOCKET2STREAM_SUFFIX L"\\Stream" +#define SOCKET1_TYPE_SEQPKT 0 +#define SOCKET2_TYPE_SEQPKT 1 +#define SOCKET1_TYPE_STREAM 2 +#define SOCKET2_TYPE_STREAM 3 + +#define IN_RANGE(_S, _RangeStart, _RangeEnd) \ + ((_S >= _RangeStart) && (_S <= _RangeEnd)) + + +// +// The following macros deal with on-the-wire integer and long values +// +// On the wire format is big-endian i.e. a long value of 0x01020304 is +// represented as 01 02 03 04. Similarly an int value of 0x0102 is +// represented as 01 02. +// +// The host format is not assumed since it will vary from processor to +// processor. +// + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = (USHORT) (*(PBYTE)(SrcPtr)) + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = (ULONG) (*(PBYTE)(SrcPtr)) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a dword from on-the-wire format to a dword in the host format +#define GETULONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 24) + \ + (*((PBYTE)(SrcPtr)+1) << 16) + \ + (*((PBYTE)(SrcPtr)+2) << 8) + \ + (*((PBYTE)(SrcPtr)+3) )) + +// Get a dword from on-the-wire format to a dword in the same format but +// also watch out for alignment +#define GETULONG2ULONG_NOCONV(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0); \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1); \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2); \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3); + +// Put a dword from the host format to a short to on-the-wire format +#define PUTBYTE2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = ((USHORT)(Src) % 256) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((USHORT)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE)(Src) + +// Put a dword from the host format to a byte to on-the-wire format +#define PUTULONG2BYTE(DstPtr, Src) \ + *(PBYTE)(DstPtr) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTULONG2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE) (Src) + +// Put a dword from the host format to a dword to on-the-wire format +#define PUTULONG2ULONG(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 24), \ + *((PBYTE)(DstPtr)+1) = (BYTE) ((ULONG)(Src) >> 16), \ + *((PBYTE)(DstPtr)+2) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+3) = (BYTE) (Src) + +// Put a BYTE[4] array into another BYTE4 array. +#define PUTBYTE42BYTE4(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0), \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1), \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2), \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3) + +// MIN/MAX macros +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + + + +// Exported prototypes + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr); + +LONG +SpxRandomNumber( + VOID); + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType); + +VOID +SpxSleep( + IN ULONG TimeInMs); + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket); + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr); + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress); + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength); + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1); diff --git a/private/ntos/tdi/isnp/spx/mp/makefile b/private/ntos/tdi/isnp/spx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/spx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/spx/mp/sources b/private/ntos/tdi/isnp/spx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/spx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/spx/nwlnkspx.rc b/private/ntos/tdi/isnp/spx/nwlnkspx.rc new file mode 100644 index 000000000..02175f21d --- /dev/null +++ b/private/ntos/tdi/isnp/spx/nwlnkspx.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 SPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkspx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkspx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/spx/precomp.h b/private/ntos/tdi/isnp/spx/precomp.h new file mode 100644 index 000000000..d227d02f9 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/precomp.h @@ -0,0 +1 @@ +#include "isnspx.h" diff --git a/private/ntos/tdi/isnp/spx/sources.inc b/private/ntos/tdi/isnp/spx/sources.inc new file mode 100644 index 000000000..419f580f1 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/sources.inc @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkspx + +TARGETNAME=nwlnkspx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\h;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\spxdrvr.c \ + ..\spxreg.c \ + ..\spxdev.c \ + ..\spxbind.c \ + ..\spxaddr.c \ + ..\spxconn.c \ + ..\spxcutil.c \ + ..\spxcpkt.c \ + ..\spxrecv.c \ + ..\spxsend.c \ + ..\spxquery.c \ + ..\spxutils.c \ + ..\spxmem.c \ + ..\spxtimer.c \ + ..\spxpkt.c \ + ..\globals.c \ + ..\spxerror.c \ + ..\nwlnkspx.rc + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/ntos/tdi/isnp/spx/spxaddr.c b/private/ntos/tdi/isnp/spx/spxaddr.c new file mode 100644 index 000000000..e9e85dc5c --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxaddr.c @@ -0,0 +1,1729 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, SpxAddrFileCreate) +#pragma alloc_text( PAGE, SpxAddrFileClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXADDR + +// Map all generic accesses to the same one. +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + +#define REORDER(_Socket) ((((_Socket) & 0xff00) >> 8) | (((_Socket) & 0x00ff) << 8)) + + + + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the ST transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR pAddr; + PSPX_ADDR_FILE pAddrFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED * AddressName; + USHORT Socket, hostSocket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle, LockHandleAddr; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; + +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // The network name is in the EA, passed in the request. + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no EA\n", Request)); + + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // this may be a valid name; parse the name from the EA and use it if OK. + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + for (i=0;i<name->TAAddressCount;i++) + { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + Socket = + ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + + GETSHORT2SHORT(&hostSocket, &Socket); + + DBGPRINT(CREATE, DBG, + ("SpxAddrOpen: Creating socket %lx.h%lx\n", + Socket, hostSocket )); + + found = TRUE; + } + break; + + } + else + { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + } + } + + if (!found) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no IPX Address\n", Request)); + + return STATUS_INVALID_ADDRESS_COMPONENT; + } + +#ifdef SOCKET_RANGE_OPEN_LIMITATION_REMOVED + // Is the socket in our range if its in the range 0x4000-0x7FFF + if (IN_RANGE(hostSocket, DYNSKT_RANGE_START, DYNSKT_RANGE_END)) + { + if (!IN_RANGE( + hostSocket, + PARAM(CONFIG_SOCKET_RANGE_START), + PARAM(CONFIG_SOCKET_RANGE_END))) + { + return(STATUS_INVALID_ADDRESS); + } + } +#endif + + // get an address file structure to represent this address. + status = SpxAddrFileCreate(Device, Request, &pAddrFile); + if (!NT_SUCCESS(status)) + return status; + + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + CTEGetLock (&Device->dev_Lock, &LockHandle); + + // We checkfor/create sockets within the critical section. + if (Socket == 0) + { + Socket = SpxAddrAssignSocket(Device); + + if (Socket == 0) + { + DBGPRINT(ADDRESS, ERR, + ("OpenAddress, no unique socket found\n")); + + CTEFreeLock (&Device->dev_Lock, LockHandle); + ExReleaseResource (&Device->dev_AddrResource); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + } + + pAddr = SpxAddrLookup(Device, Socket); + + if (pAddr == NULL) + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // This address doesn't exist. Create it. + // registering it. It also puts a ref of type ADDR_FILE on address. + pAddr = SpxAddrCreate( + Device, + Socket); + + if (pAddr != (PSPX_ADDR)NULL) + { +#ifdef ISN_NT + + // Initialize the shared access now. We use read access + // to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess); + + + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &pAddr->sa_SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) + { + // Error, return status. + IoRemoveShareAccess (IrpSp->FileObject, &pAddr->u.sa_ShareAccess); + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + return status; + } + +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + // if the adapter isn't ready, we can't do any of this; get out +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) +#else + if (Device->dev_State == DEVICE_STATE_STOPPING) +#endif _PNP_POWER + { + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + status = STATUS_DEVICE_NOT_READY; + } + else + { + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; + + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + status = STATUS_SUCCESS; + } + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + + SpxAddrFileDestroy(pAddrFile); + } + } + else + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + DBGPRINT(ADDRESS, ERR, + ("Add to address %lx\n", pAddr)); + + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + pAddr->sa_SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { +#ifdef ISN_NT + + // Now check that we can obtain the desired share + // access. We use read access to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + SpxAddrLockReference (pAddr, AREF_ADDR_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&Device->dev_Lock, LockHandle); + + status = STATUS_SUCCESS; + } + } + + // Remove the reference from SpxLookupAddress. + SpxAddrDereference (pAddr, AREF_LOOKUP); + } + + return status; + +} // SpxAddrOpen + + + + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest + ) +{ + CTELockHandle lockHandle; + NTSTATUS status = STATUS_SUCCESS; + + PSPX_ADDR_FILE + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + PTDI_REQUEST_KERNEL_SET_EVENT + pParam = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(pRequest); + + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(status); + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + switch (pParam->EventType) + { + + case TDI_EVENT_ERROR: + + break; + + case TDI_EVENT_CONNECT: + + pSpxAddrFile->saf_ConnHandler = + (PTDI_IND_CONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_ConnHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE: + + pSpxAddrFile->saf_RecvHandler = + (PTDI_IND_RECEIVE)(pParam->EventHandler); + pSpxAddrFile->saf_RecvHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_DISCONNECT: + + pSpxAddrFile->saf_DiscHandler = + (PTDI_IND_DISCONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_DiscHandlerCtx = + pParam->EventContext; + + break; + + + case TDI_EVENT_SEND_POSSIBLE : + + pSpxAddrFile->saf_SendPossibleHandler = + (PTDI_IND_SEND_POSSIBLE)(pParam->EventHandler); + pSpxAddrFile->saf_SendPossibleHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + case TDI_EVENT_RECEIVE_EXPEDITED: + default: + + status = STATUS_INVALID_PARAMETER; + } + + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return(status); +} + + + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PSPX_ADDR pAddr; + int index; + CTELockHandle lockHandle; + + pAddr = (PSPX_ADDR)SpxAllocateZeroedMemory (sizeof(SPX_ADDR)); + if (pAddr == NULL) + { + DBGPRINT(ADDRESS, INFO, + ("Create address %lx failed\n", (ULONG)Socket)); + + return NULL; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address %lx (%lx)\n", pAddr, (ULONG)Socket)); + + pAddr->sa_Type = SPX_ADDRESS_SIGNATURE; + pAddr->sa_Size = sizeof (SPX_ADDR); + pAddr->sa_Flags = 0; + + pAddr->sa_Device = Device; + pAddr->sa_DeviceLock = &Device->dev_Lock; + CTEInitLock (&pAddr->sa_Lock); + + // This reference is for the address file that will associated with this addr. + pAddr->sa_RefCount = 1; + +#if DBG + pAddr->sa_RefTypes[AREF_ADDR_FILE] = 1; +#endif + + pAddr->sa_Socket = Socket; + + // Insert address into the device hash table. + index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + CTEGetLock (&Device->dev_Lock, &lockHandle); + pAddr->sa_Next = Device->dev_AddrHashTable[index]; + Device->dev_AddrHashTable[index] = pAddr; + CTEFreeLock (&Device->dev_Lock, lockHandle); + + SpxReferenceDevice (Device, DREF_ADDRESS); + + return pAddr; + +} // SpxAddrCreate + + + + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a SPX_ADDR_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PSPX_ADDR Address; + + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + + try + { + if ((pAddrFile->saf_Size == sizeof (SPX_ADDR_FILE)) && + (pAddrFile->saf_Type == SPX_ADDRESSFILE_SIGNATURE) ) + { + Address = pAddrFile->saf_Addr; + + if ((Address->sa_Size == sizeof (SPX_ADDR)) && + (Address->sa_Type == SPX_ADDRESS_SIGNATURE) ) + { + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if ((Address->sa_Flags & SPX_ADDR_CLOSING) == 0) + { + SpxAddrFileLockReference(pAddrFile, AFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx closing\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx bad signature\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pAddrFile)); + + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxAddrFileVerify: AF %lx exception\n", Address)); + + return GetExceptionCode(); + } + + return status; + +} // SpxAddrFileVerify + + + + +VOID +SpxAddrDestroy( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by SpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by SpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PSPX_ADDR pAddr, *ppAddr; + CTELockHandle LockHandle; + + PSPX_ADDR Address = (PSPX_ADDR)Parameter; + PDEVICE Device = Address->sa_Device; + int index = (int)(Address->sa_Socket & NUM_SPXADDR_HASH_MASK); + + DBGPRINT(ADDRESS, INFO, + ("Destroy address %lx\n", Address)); + + SeDeassignSecurity (&Address->sa_SecurityDescriptor); + + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + CTEGetLock (&Device->dev_Lock, &LockHandle); + for (ppAddr = &Device->dev_AddrHashTable[index]; (pAddr = *ppAddr) != NULL;) + { + if (pAddr == Address) + { + *ppAddr = pAddr->sa_Next; + break; + } + + ppAddr = &pAddr->sa_Next; + } + CTEFreeLock (&Device->dev_Lock, LockHandle); + + SpxFreeMemory (Address); + SpxDereferenceDevice (Device, DREF_ADDRESS); + +} + + + + +#if DBG + +VOID +SpxAddrRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} + + + + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} +#endif + + + + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &Address->sa_RefCount, + (ULONG)-1, + Address->sa_DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) + { +#if ISN_NT + ExInitializeWorkItem( + &Address->u.sa_DestroyAddrQueueItem, + SpxAddrDestroy, + (PVOID)Address); + ExQueueWorkItem(&Address->u.sa_DestroyAddrQueueItem, DelayedWorkQueue); +#else + SpxAddrDestroy(Address); +#endif + + } + +} + + + + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + NTSTATUS status; + BYTE socketType; + CTELockHandle LockHandle; + PSPX_ADDR_FILE pAddrFile; + + // What is the address file type? + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) + { + return(status); + } + + pAddrFile = (PSPX_ADDR_FILE)SpxAllocateZeroedMemory (sizeof(SPX_ADDR_FILE)); + if (pAddrFile == NULL) + { + DBGPRINT(ADDRESS, ERR, + ("Create address file failed\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address file %lx\n", pAddrFile)); + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + pAddrFile->saf_Type = SPX_ADDRESSFILE_SIGNATURE; + pAddrFile->saf_Size = sizeof (SPX_ADDR_FILE); + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject = NULL; +#endif + + pAddrFile->saf_Device = Device; + pAddrFile->saf_Flags = SPX_ADDRFILE_OPENING; + if ((socketType == SOCKET1_TYPE_SEQPKT) || + (socketType == SOCKET1_TYPE_STREAM)) + { + if (socketType == SOCKET1_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_SPX2; + if (socketType == SOCKET2_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + pAddrFile->saf_RefCount = 1; + +#if DBG + pAddrFile->saf_RefTypes[AFREF_CREATE] = 1; +#endif + + pAddrFile->saf_CloseReq = (PREQUEST)NULL; + + // Initialize the request handlers. + pAddrFile->saf_ConnHandler = + pAddrFile->saf_ConnHandlerCtx = NULL; + pAddrFile->saf_DiscHandler = + pAddrFile->saf_DiscHandlerCtx = NULL; + pAddrFile->saf_RecvHandler = + pAddrFile->saf_RecvHandlerCtx = NULL; + pAddrFile->saf_ErrHandler = + pAddrFile->saf_ErrHandlerCtx = NULL; + + // Release lock + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // Put in the global list for our reference + spxAddrInsertIntoGlobalList(pAddrFile); + + *ppAddrFile = pAddrFile; + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by SpxAddrFileDereference. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + pAddrFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PSPX_ADDR Address; + PDEVICE Device; + PREQUEST CloseRequest; + PSPX_ADDR_FILE pRemAddr, *ppRemAddr; + + DBGPRINT(ADDRESS, INFO, + ("Destroy address file %lx\n", pAddrFile)); + + Address = pAddrFile->saf_Addr; + Device = pAddrFile->saf_Device; + + if (Address) + { + CTEGetLock (&Device->dev_Lock, &LockHandle1); + + // This addressfile was associated with an address. + CTEGetLock (&Address->sa_Lock, &LockHandle); + + // If the last reference on the address is being removed, set the + // closing flag to prevent further references. + + //if (Address->sa_RefCount == 1) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&Address->sa_RefCount, 0, &Address->sa_Lock) == 1) { + Address->sa_Flags |= SPX_ADDR_CLOSING; + } + + // Dequeue the address file from the address list. + for (ppRemAddr = &Address->sa_AddrFileList; (pRemAddr = *ppRemAddr) != NULL;) + { + if (pRemAddr == pAddrFile) + { + *ppRemAddr = pRemAddr->saf_Next; + break; + } + + ppRemAddr = &pRemAddr->saf_Next; + } + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject->FsContext = NULL; + pAddrFile->saf_FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->sa_Lock, LockHandle); + CTEFreeLock (&Device->dev_Lock, LockHandle1); + + // We will already have been removed from the ShareAccess + // of the owning address. + // + // Now dereference the owning address. + SpxAddrDereference(Address, AREF_ADDR_FILE); + } + + // Save this for later completion. + CloseRequest = pAddrFile->saf_CloseReq; + + // Remove from the global list + spxAddrRemoveFromGlobalList(pAddrFile); + + // return the addressFile to the pool of address files + SpxFreeMemory (pAddrFile); + + if (CloseRequest != (PREQUEST)NULL) + { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + SpxCompleteRequest (CloseRequest); + SpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} + + + + +#if DBG + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} // SpxRefAddressFile + + + + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} +#endif + + + + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddressFile to remove it from the system. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + (ULONG)-1, + pAddrFile->saf_AddrLock); + + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) + { + SpxAddrFileDestroy(pAddrFile); + } + +} + + + + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + SpxAddrLockReference(Address, AREF_LOOKUP); + return Address; + + } + } + + // The specified address was not found. + return NULL; + +} + + + + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + TRUE if so, else FALSE + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match + return TRUE; + } + } + + // The specified address was not found. + return FALSE; + +} // SpxAddrExists + + + + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT PSPX_CONN_FILE *ppSpxConnFile + ) +{ + PSPX_CONN_FILE pSpxConnFile; + NTSTATUS status = STATUS_INVALID_CONNECTION; + + for (pSpxConnFile = pSpxAddr->sa_ActiveConnList; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_Next) + { + if ((pSpxConnFile->scf_RemConnId == SrcConnId) && + (*((UNALIGNED ULONG *)SrcIpxAddr) == + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+4) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+8) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+8))) + { + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + *ppSpxConnFile = pSpxConnFile; + status = STATUS_SUCCESS; + break; + } + } + + return(status); +} + + + + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an pAddrFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + pAddrFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile, pSpxConnFileNext; + CTELockHandle LockHandle; + + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileStop: %lx\n", pAddrFile)); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if (pAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING) + { + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + } + + pAddrFile->saf_Flags |= SPX_ADDRFILE_CLOSING; + + pSpxConnFileNext = NULL; + if (pSpxConnFile = pAddrFile->saf_AssocConnList) + { + pSpxConnFileNext = pSpxConnFile; + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + } + + while (pSpxConnFile) + { + if (pSpxConnFileNext = pSpxConnFile->scf_AssocNext) + { + SpxConnFileReference(pSpxConnFileNext, CFREF_ADDR); + } + CTEFreeLock (&Address->sa_Lock, LockHandle); + + + DBGPRINT(CREATE, INFO, + ("SpxAddrFileClose: Assoc conn stop %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + SpxConnStop(pSpxConnFile); + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + pSpxConnFile = pSpxConnFileNext; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileCleanup( + 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 address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, INFO, + ("SpxAddrFileCleanup: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + if (!NT_SUCCESS (status)) + { + return(status); + } + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + SpxAddrFileStop(pSpxAddrFile, Address); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_SUCCESS; +} + + + + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +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 address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileClose: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + + if (!NT_SUCCESS (status)) + { + return(status); + } + + pSpxAddrFile->saf_CloseReq = Request; + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + // Remove us from the access info for this address. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + +#ifdef ISN_NT + IoRemoveShareAccess (pSpxAddrFile->saf_FileObject, &Address->u.sa_ShareAccess); +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + + SpxAddrFileDereference (pSpxAddrFile, AFREF_CREATE); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_PENDING; + +} // SpxCloseAddressFile + + + + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + BOOLEAN wrapped = FALSE; + USHORT temp, Socket; + + // We have to auto-assign a socket. + temp = Device->dev_CurrentSocket; + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + while (TRUE) + { + Device->dev_CurrentSocket += (USHORT)PARAM(CONFIG_SOCKET_UNIQUENESS); + if (Device->dev_CurrentSocket > PARAM(CONFIG_SOCKET_RANGE_END)) + { + Device->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + wrapped = TRUE; + } + + if (!SpxAddrExists (Device, Socket)) + { + break; + } + + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + if (wrapped && (Device->dev_CurrentSocket >= temp)) + { + // If we have checked all possible values given SOCKET_UNIQUENESS... + // This may actually return ERROR even if there are + // available socket numbers although they may be + // implicitly in use due to SOCKET_UNIQUENESS being + // > 1. That is the way it is to work. + + Socket = 0; + break; + } + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + + return(Socket); +} + + + + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxAddrFile->saf_GlobalNext = SpxGlobalAddrList; + SpxGlobalAddrList = pSpxAddrFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_ADDR_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalAddrList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxAddrFile) + { + DBGPRINT(SEND, INFO, + ("SpxAddrRemoveFromGlobal: %lx\n", pSpxAddrFile)); + + // Remove from list + *ppC = pC->saf_GlobalNext; + break; + } + + ppC = &pC->saf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_ADDRESS; + + return(status); +} + + + + diff --git a/private/ntos/tdi/isnp/spx/spxbind.c b/private/ntos/tdi/isnp/spx/spxbind.c new file mode 100644 index 000000000..7610939f7 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxbind.c @@ -0,0 +1,600 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.c + +Abstract: + + This module contains the code to bind to the IPX transport, as well as the + indication routines for the IPX transport not including the send/recv ones. + +Author: + + Stefan Solomon (stefans) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXBIND + +VOID +SpxStatus ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry); + +VOID +SpxLineDown ( + IN USHORT NicId); + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// globals and externs +// +extern CTELock spxTimerLock; +extern LARGE_INTEGER spxTimerTick; +extern KTIMER spxTimer; +extern KDPC spxTimerDpc; +extern BOOLEAN spxTimerStopped; +#endif _PNP_POWER + +NTSTATUS +SpxInitBindToIpx( + VOID + ) + +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objectAttr; + PIPX_INTERNAL_BIND_INPUT pBindInput; + PIPX_INTERNAL_BIND_OUTPUT pBindOutput; + + InitializeObjectAttributes( + &objectAttr, + &IpxDeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = NtCreateFile( + &IpxHandle, + SYNCHRONIZE | GENERIC_READ, + &objectAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(status)) { + return status; + } + + if ((pBindInput = CTEAllocMem(sizeof(IPX_INTERNAL_BIND_INPUT))) == NULL) { + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Fill in our bind data +#if defined(_PNP_POWER) + pBindInput->Version = ISN_VERSION; +#else + pBindInput->Version = 1; +#endif _PNP_POWER + pBindInput->Identifier = IDENTIFIER_SPX; + pBindInput->BroadcastEnable = FALSE; + pBindInput->LookaheadRequired = IPX_HDRSIZE; + pBindInput->ProtocolOptions = 0; + pBindInput->ReceiveHandler = SpxReceive; + pBindInput->ReceiveCompleteHandler = SpxReceiveComplete; + pBindInput->StatusHandler = SpxStatus; + pBindInput->SendCompleteHandler = SpxSendComplete; + pBindInput->TransferDataCompleteHandler = SpxTransferDataComplete; + pBindInput->FindRouteCompleteHandler = SpxFindRouteComplete; + pBindInput->LineUpHandler = SpxLineUp; + pBindInput->LineDownHandler = SpxLineDown; + pBindInput->ScheduleRouteHandler = SpxScheduleRoute; +#if defined(_PNP_POWER) + pBindInput->PnPHandler = SpxPnPNotification; +#endif _PNP_POWER + + + // First get the length for the output buffer. + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + NULL, // Output Buffer + 0); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status != STATUS_BUFFER_TOO_SMALL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INVALID_PARAMETER); + } + + if ((pBindOutput = CTEAllocMem(ioStatusBlock.Information)) == NULL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + pBindOutput, // Output Buffer + ioStatusBlock.Information); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status == STATUS_SUCCESS) { + + // Get all the info from the bind output buffer and save in + // appropriate places. + IpxLineInfo = pBindOutput->LineInfo; + IpxMacHdrNeeded = pBindOutput->MacHeaderNeeded; + IpxInclHdrOffset = pBindOutput->IncludedHeaderOffset; + + IpxSendPacket = pBindOutput->SendHandler; + IpxFindRoute = pBindOutput->FindRouteHandler; + IpxQuery = pBindOutput->QueryHandler; + IpxTransferData = pBindOutput->TransferDataHandler; + +#if !defined(_PNP_POWER) + // Copy over the network node info. + RtlCopyMemory( + SpxDevice->dev_Network, + pBindOutput->Network, + IPX_NET_LEN); + + RtlCopyMemory( + SpxDevice->dev_Node, + pBindOutput->Node, + IPX_NODE_LEN); + + + DBGPRINT(TDI, INFO, + ("SpxInitBindToIpx: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // Find out how many adapters IPX has, if this fails + // just assume one. + // + + if ((*IpxQuery)( + IPX_QUERY_MAXIMUM_NIC_ID, + 0, + &SpxDevice->dev_Adapters, + sizeof(USHORT), + NULL) != STATUS_SUCCESS) { + + SpxDevice->dev_Adapters = 1; + + } +#endif !_PNP_POWER + } else { + + NtClose(IpxHandle); + status = STATUS_INVALID_PARAMETER; + } + CTEFreeMem(pBindInput); + CTEFreeMem(pBindOutput); + + return status; +} + + + + +VOID +SpxUnbindFromIpx( + VOID + ) + +{ + NtClose(IpxHandle); + return; +} + + + + +VOID +SpxStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxStatus: CALLED WITH %lx\n", + GeneralStatus)); + + return; +} + + + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +{ + CTELockHandle lockHandle; + PSPX_FIND_ROUTE_REQUEST pSpxFrReq = (PSPX_FIND_ROUTE_REQUEST)FindRouteRequest; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)pSpxFrReq->fr_Ctx; + + // This will be on a connection. Grab the lock, check the state and go from + // there. + if (pSpxConnFile == NULL) + { + // Should this ever happen? + KeBugCheck(0); + return; + } + + // Check the state. The called routines release the lock, remove the reference. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + // We are doing an active connect! + SpxConnConnectFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + else // For all others call active + { + SpxConnActiveFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + + // Free the find route request. + SpxFreeMemory(pSpxFrReq); + + return; +} + + + + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + +{ + // With PnP, our local address is changed when we get PnP + // notification. +#if !defined(_PNP_POWER) + + // + // If we get a line up for NicId 0, it means our local + // network number has changed, re-query from IPX. + // + + if (NicId == 0) { + + TDI_ADDRESS_IPX IpxAddress; + + if ((*IpxQuery)( + IPX_QUERY_IPX_ADDRESS, + 0, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + RtlCopyMemory( + SpxDevice->dev_Network, + &IpxAddress.NetworkAddress, + IPX_NET_LEN); + + DBGPRINT(TDI, INFO, + ("SpxLineUp: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // The node shouldn't change! + // + + if (!RtlEqualMemory( + SpxDevice->dev_Node, + IpxAddress.NodeAddress, + IPX_NODE_LEN)) { + + DBGPRINT(TDI, ERR, + ("SpxLineUp: Node address has changed\n")); + } + } + + } else { + + DBGPRINT(RECEIVE, ERR, + ("SpxLineUp: CALLED WITH %lx\n", + NicId)); + } + + return; +#endif !_PNP_POWER + +} + + + + +VOID +SpxLineDown ( + IN USHORT NicId + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxLineDown: CALLED WITH %lx\n", + NicId)); + + return; +} + + + + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxScheduleRoute: CALLED WITH %lx\n", + RouteEntry)); + + return; +} + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + NTSTATUS Status; + PDEVICE Device = SpxDevice; + UNICODE_STRING UnicodeDeviceName; + + DBGPRINT(DEVICE, DBG,("Received a pnp notification, opcode %d\n",OpCode)); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + CTELockHandle TimerLockHandle; + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->dev_State != DEVICE_STATE_OPEN ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + // + // Start the timer. It is possible that the timer + // was still running or we are still in the timer dpc + // from the previous ADD_DEVICE - DELETE_DEVICE execution + // cycle. But it is ok simply restart this, because + // KeSetTimer implicitly cancels the previous Dpc. + // + + CTEGetLock(&spxTimerLock, &TimerLockHandle); + spxTimerStopped = FALSE; + CTEFreeLock(&spxTimerLock, TimerLockHandle); + KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + + Device->dev_State = DEVICE_STATE_OPEN; + + + CTEAssert( !Device->dev_Adapters ); + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + IpxLineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumPacketSize; + // set the provider info + SpxDevice->dev_ProviderInfo.MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + }else { + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + } + + Device->dev_Adapters++; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->dev_DeviceName; + UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen; + UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to register Spx Device with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + CTEAssert( Device->dev_Adapters ); + Device->dev_Adapters--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->dev_State = DEVICE_STATE_LOADED; + Device->dev_Adapters = 0; + } + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + if ( PnPInfo->FirstORLastDevice ) { + SpxTimerFlushAndStop(); + // + // inform tdi clients about the device deletion + // + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to Deregister Spx Device with TDI\n")); + } + } + // + // TBD: call ExNotifyCallback + // + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEAssert( PnPInfo->NewReservedAddress ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/spx/spxconn.c b/private/ntos/tdi/isnp/spx/spxconn.c new file mode 100644 index 000000000..4528b64a4 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxconn.c @@ -0,0 +1,3862 @@ +/*++ + +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" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, SpxConnOpen) +#pragma alloc_text(PAGE, SpxConnCleanup) +#pragma alloc_text(PAGE, SpxConnClose) +#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, + 0, + KernelMode, + (PVOID *)&pFileObj, + NULL); + + if (!NT_SUCCESS(status)) + break; + + pSpxAddrFile = pFileObj->FsContext; + ASSERT(pFileObj->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + // Verify address file/connection file + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + break; + + derefAddr = TRUE; + + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + 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: + + BUGBUG: + 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; + + 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); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // 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)); + // + // Currently SPX implements DS_TYPE in such a way that the DS_TYPE field in a packet + // takes on the latest value from the connectionfile. Some customers complained that + // their apps assume that the DS_TYPE field should reflect the value at the time of + // the send (and Novell does this). + // + // So, instead of keeping a global value, we copy over the DS_TYPE value into the + // request if it pends. Look at spxpkt.c:SpxBuildData. + // + REQUEST_PARAMETERS(pRequest)->Others.Argument3 = (PVOID)pSpxConnFile->scf_DataType; + + } + 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; + } + + NdisQueryBuffer( + REQUEST_NDIS_BUFFER(pRequest), (PVOID *)&NwlinkAction, &BufferLength); + + 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; + } + + // 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; + } + + 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)); + + 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; + 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 0 + { + int i; + char *address = pFrReq->fr_FindRouteReq.LocalTarget.MacAddress; + + DbgPrint("FIND ROUTE LOCALTARGET.MAC:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + + address = pSpxConnFile->scf_RemAddr+4; + DbgPrint("SPX DESTINATION ADDRESS:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + } + + DbgPrint("NIC Id %lx\n", pFrReq->fr_FindRouteReq.LocalTarget.NicId); +#endif + + // 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+2)) == (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 = 0; + pSpxConnFile->scf_AckLocalTarget.NicHandle.NicId = 0; +#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; + USHORT reenqueueTime = TIMER_REQUEUE_CUR_VALUE; + BOOLEAN lockHeld, fResendPkt = FALSE, fDisconnect = FALSE, + fFindRoute = FALSE, fBackoffTimer = FALSE; + PREQUEST pRequest = NULL; + + 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); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + 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; + 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; +} 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 diff --git a/private/ntos/tdi/isnp/spx/spxcutil.c b/private/ntos/tdi/isnp/spx/spxcutil.c new file mode 100644 index 000000000..3f85b2281 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxcutil.c @@ -0,0 +1,1738 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcutil.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" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXCUTIL + +// +// Minor utility routines +// + + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + int i; + + // We go thru table and see if this is the minimum size or if it + // can go down further. Return true if it is not the minimum size. + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[0])); + + if ((ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE) <= SpxMaxPktSize[0]) + return(FALSE); + + for (i = SpxMaxPktSizeIndex-1; i > 0; i--) + { + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[i])); + + if (SpxMaxPktSize[i] < (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE)) + break; + } + + *pNegSize = (USHORT)(SpxMaxPktSize[i] + MIN_IPXSPX2_HDRSIZE); + + DBGPRINT(CONNECT, ERR, + ("spxConnCheckNegSize: Trying Size %lx Min size possible %lx\n", + *pNegSize, SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)); + + return(TRUE); +} + + + + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + UINT bufCount; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + + CTEAssert(Size > 0); + NdisQueryPacket(pPkt, NULL, &bufCount, &pNdisBuffer, NULL); + CTEAssert (bufCount == 3); + + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisAdjustBufferLength(pNdisBuffer, Size); + + // Change it in send reserved + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Len = (Size + MIN_IPXSPX2_HDRSIZE); + + // Change in ipx header + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + PUTSHORT2SHORT((PUSHORT)&pIpxSpxHdr->hdr_PktLen, (Size + MIN_IPXSPX2_HDRSIZE)); + + // Change in the neg packet field of the header. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + (Size + MIN_IPXSPX2_HDRSIZE)); + + DBGPRINT(CONNECT, DBG, + ("spxConnSetNegSize: Setting size to %lx Hdr %lx\n", + Size, (Size + MIN_IPXSPX2_HDRSIZE))); + + return; +} + + + + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, pListHead, pListTail; + PSPX_SEND_RESD pSendResd; + BOOLEAN removed = TRUE; + + // If we are sequenced or not decides which list we choose. + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pListHead = pSpxConnFile->scf_SendSeqListHead; + pListTail = pSpxConnFile->scf_SendSeqListTail; + } + else + { + pListHead = pSpxConnFile->scf_SendListHead; + pListTail = pSpxConnFile->scf_SendListTail; + } + + // Most often, we will be at the head of the list. + if (pListHead == pSendResd) + { + if ((pListHead = pSendResd->sr_Next) == NULL) + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pSendResd)); + + pListTail = NULL; + } + } + else + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pSendResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->sr_Next == pSendResd) + { + if ((pSr->sr_Next = pSendResd->sr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->sr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pSpxConnFile->scf_SendSeqListHead = pListHead; + pSpxConnFile->scf_SendSeqListTail = pListTail; + } + else + { + pSpxConnFile->scf_SendListHead = pListHead; + pSpxConnFile->scf_SendListTail = pListTail; + } + } + + return(removed); +} + + + + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_RECV_RESD pSr, pListHead, pListTail; + PSPX_RECV_RESD pRecvResd; + BOOLEAN removed = TRUE; + + pRecvResd = (PSPX_RECV_RESD)(pPkt->ProtocolReserved); + pListHead = pSpxConnFile->scf_RecvListHead; + pListTail = pSpxConnFile->scf_RecvListTail; + + // Most often, we will be at the head of the list. + if (pListHead == pRecvResd) + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pRecvResd)); + + if ((pListHead = pRecvResd->rr_Next) == NULL) + { + pListTail = NULL; + } + } + else + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pRecvResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->rr_Next == pRecvResd) + { + if ((pSr->rr_Next = pRecvResd->rr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->rr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + pSpxConnFile->scf_RecvListHead = pListHead; + pSpxConnFile->scf_RecvListTail = pListTail; + } + + return(removed); +} + + + + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = (fSeqList ? + &pSpxConnFile->scf_SendSeqListHead : + &pSpxConnFile->scf_SendListHead); + + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_Type == PktType) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, INFO, + ("SpxConnFindByType: %lx.%lx.%d\n", pSr,*ppPkt, fSeqList)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = &pSpxConnFile->scf_SendSeqListHead; + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_SeqNum == SeqNum) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, DBG, + ("SpxConnFindBySeq: %lx.%lx.%d\n", pSr,*ppPkt, SeqNum)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +USHORT +spxConnGetId( + VOID + ) +/*++ + +Routine Description: + + This must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + BOOLEAN wrapped = FALSE; + USHORT startConnId, retConnId; + + startConnId = SpxDevice->dev_NextConnId; + + // Search the global active list. + do + { + if ((SpxDevice->dev_NextConnId >= startConnId) && wrapped) + { + retConnId = 0; + break; + } + + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + continue; + } + + // BUGBUG: Later this be a tree. + for (pSpxConnFile = SpxDevice->dev_GlobalActiveConnList[ + SpxDevice->dev_NextConnId & NUM_SPXCONN_HASH_MASK]; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_GlobalActiveNext) + { + if (pSpxConnFile->scf_LocalConnId == SpxDevice->dev_NextConnId) + { + break; + } + } + + // Increment for next time. + retConnId = SpxDevice->dev_NextConnId++; + + // Ensure we are still legal. We could return if connfile is null. + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + } + + if (pSpxConnFile != NULL) + { + continue; + } + + break; + + } while (TRUE); + + return(retConnId); +} + + + + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_Next; + break; + } + + ppRemConn = &pRemConn->scf_Next; + } + + if (pRemConn == NULL) + { + DBGBRK(FATAL); + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_AssocNext; + break; + } + + ppRemConn = &pRemConn->scf_AssocNext; + } + + if (pRemConn == NULL) + { + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + + // For now, its just a linear list. + pSpxConnFile->scf_GlobalActiveNext = + SpxDevice->dev_GlobalActiveConnList[index]; + + SpxDevice->dev_GlobalActiveConnList[index] = + pSpxConnFile; + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + PSPX_CONN_FILE pC, *ppC; + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + NTSTATUS status = STATUS_SUCCESS; + + // For now, its just a linear list. + for (ppC = &SpxDevice->dev_GlobalActiveConnList[index]; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalActiveNext; + break; + } + + ppC = &pC->scf_GlobalActiveNext; + } + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_GlobalNext = SpxGlobalConnList; + SpxGlobalConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_CONN_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalConnList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalNext; + break; + } + + ppC = &pC->scf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + + + +#if 0 + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_PktNext = SpxPktConnList; + SpxPktConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxPktConnList) != NULL) + { + SpxPktConnList = SpxPktConnList->scf_PktNext; + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + + + + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_ProcessRecvNext = SpxRecvConnList; + SpxRecvConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxRecvConnList) != NULL) + { + SpxRecvConnList = SpxRecvConnList->scf_ProcessRecvNext; + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromRecv: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + +#endif + + +// +// Reference/Dereference routines +// + + +#if DBG + +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFile + + + + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE CONNECTION LOCK HELD. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFileLock + +#endif + + + + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) + +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE DEVICE LOCK HELD!!! + + All active connections should be on the device active list. Later, + this data structure will be a tree, caching the last accessed + connection. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = + SpxDevice->dev_GlobalActiveConnList[ConnId & NUM_SPXCONN_HASH_MASK]; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_GlobalActiveNext) + { + if (pSpxChkConn->scf_LocalConnId == ConnId) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYID); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; + +} + + + + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!! + + Returns a referenced connection file with the associated context and + address file desired. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_Next) + { + if ((pSpxChkConn->scf_ConnCtx == Ctx) && + (pSpxChkConn->scf_AddrFile == pSpxAddrFile)) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYCTX); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; +} + + + + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + + try + { + if ((pConnFile->scf_Size == sizeof (SPX_CONN_FILE)) && + (pConnFile->scf_Type == SPX_CONNFILE_SIGNATURE)) + { + CTEGetLock (&pConnFile->scf_Lock, &LockHandle); + if (!SPX_CONN_FLAG(pConnFile, SPX_CONNFILE_CLOSING)) + { + SpxConnFileLockReference(pConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyConnFile: A %lx closing\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock (&pConnFile->scf_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxVerifyConnFile: AF %lx exception\n", pConnFile)); + + return GetExceptionCode(); + } + + return status; + +} // SpxVerifyConnFile + + + + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyConnectionFile to remove it from the system. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + BOOLEAN fDiscNotIndicated = FALSE; + BOOLEAN fIDiscFlag = FALSE; + BOOLEAN fSpx2; + + CTEAssert(pSpxConnFile->scf_RefCount > 0); + oldvalue = SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + (ULONG)-1, + &pSpxConnFile->scf_Lock); + + CTEAssert (oldvalue > 0); + if (oldvalue == 1) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + LIST_ENTRY discReqList, *p; + PREQUEST pDiscReq; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + PREQUEST pCloseReq = NULL, + pCleanupReq = NULL, + pConnectReq = NULL; + BOOLEAN fDisassoc = FALSE; + + InitializeListHead(&discReqList); + + // We may not be associated at this point. Note: When we are active we + // always have a reference. So its not like we execute this code very often. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (pSpxAddrFile) + { + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + //if (pSpxConnFile->scf_RefCount == 0) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&pSpxConnFile->scf_RefCount, 0, &pSpxConnFile->scf_Lock) == 0) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn is 0 %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // All pending requests on this connection are done. See if we + // need to complete the disconnect phase etc. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_DISCONN: + + // Disconnect is done. Move connection out of all the lists + // it is on, reset states etc. + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn being inactivated %lx\n", + pSpxConnFile)); + + // Time to complete disc requests if present. + // There could be multiple of them. + p = pSpxConnFile->scf_DiscLinkage.Flink; + while (p != &pSpxConnFile->scf_DiscLinkage) + { + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Disc on %lx.%lx\n", + pSpxConnFile, pDiscReq)); + + RemoveEntryList(REQUEST_LINKAGE(pDiscReq)); + + // Make sure Stacy is always wearing a particular + // kind of dress if she isn't already. + if (REQUEST_STATUS(pDiscReq) == STATUS_PENDING) + { + REQUEST_STATUS(pDiscReq) = STATUS_SUCCESS; + } + + InsertTailList( + &discReqList, + REQUEST_LINKAGE(pDiscReq)); + } + + // + // Note the state here, and check after the conn has been inactivated. + // + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) { + fDiscNotIndicated = TRUE; + } + + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC)) { + fIDiscFlag = TRUE; + } + + fSpx2 = (SPX2_CONN(pSpxConnFile)) ? TRUE : FALSE; + + // + // [SA] Bug #14655 + // Do not try to inactivate an already inactivated connection + // + + if (!(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + spxConnInactivate(pSpxConnFile); + } else { + // + // This is an SPXI connection which has got the local disconnect. + // Reset the flags now. + // + CTEAssert(!fDiscNotIndicated); + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_DISC_SETSTATE(pSpxConnFile, 0); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // [SA] If we were waiting for sends to be aborted and did not indicate this + // disconnect to AFD; and AFD did not call a disconnect on this connection, + // then call the disonnect handler now. + // + if (fDiscNotIndicated) { + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + ULONG discCode = 0; + + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) { + + // + // If this was an SPXI connection, the disconnect state is still + // DISCONN, so if this routine is re-entered, we need to prevent + // a re-indicate to AFD. + // Also, we need to wait for a local disconnect from AFD since + // we indicated a TDI_DISCONNECT_RELEASE. We bump up the ref count + // in this case. + // + if (!fSpx2) { + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) ); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + + if (fIDiscFlag) { + SpxConnFileLockReference(pSpxConnFile, CFREF_DISCWAITSPX); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + } + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + DBGPRINT(CONNECT, INFO, + ("spxDerefConnectionFile: 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); + + if (fIDiscFlag) { + // + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + discCode = TDI_DISCONNECT_RELEASE; + } else { + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + discCode = TDI_DISCONNECT_ABORT; + } + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + discCode); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + } + } + + --SpxDevice->dev_Stat.OpenConnections; + + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + // Get connect/accept request if present. + pConnectReq = pSpxConnFile->scf_ConnectReq; + pSpxConnFile->scf_ConnectReq = NULL; + + spxConnInactivate(pSpxConnFile); + break; + + case SPX_CONNFILE_ACTIVE: + + KeBugCheck(0); + + default: + + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == 0); + break; + } + + // If stopping, disassociate from the address file. Complete + // cleanup request. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn stopping %lx\n", + pSpxConnFile)); + + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + 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); + + DBGPRINT(CREATE, INFO, + ("SpxConnDerefDisAssociate: %lx from addr file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + fDisassoc = TRUE; + } + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + } + + if (fDisassoc) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + + if (pConnectReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Connect on %lx req %lx\n", + pSpxConnFile, pConnectReq)); + + // Status will already be set in here. We should be here only if + // connect is being aborted. + SpxCompleteRequest(pConnectReq); + } + + while (!IsListEmpty(&discReqList)) + { + p = RemoveHeadList(&discReqList); + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(CONNECT, DBG, + ("SpxConnFileDeref: DISC REQ %lx.%lx Completing\n", + pSpxConnFile, pDiscReq)); + + SpxCompleteRequest(pDiscReq); + } + + if (pCleanupReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Cleanup complete %lx req %lx\n", + pSpxConnFile, pCleanupReq)); + + REQUEST_INFORMATION(pCleanupReq) = 0; + REQUEST_STATUS(pCleanupReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCleanupReq); + } + + if (pCloseReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Freed %lx close req %lx\n", + pSpxConnFile, pCloseReq)); + + CTEAssert(pSpxConnFile->scf_RefCount == 0); + + // Remove from the global list + if (!NT_SUCCESS(spxConnRemoveFromGlobalList(pSpxConnFile))) + { + KeBugCheck(0); + } + + // Free it up. + SpxFreeMemory (pSpxConnFile); + + REQUEST_INFORMATION(pCloseReq) = 0; + REQUEST_STATUS(pCloseReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCloseReq); + } + } + + return; + +} // SpxDerefConnectionFile + + + + +VOID +spxConnReInit( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + // Reinit all variables. + pSpxConnFile->scf_Flags2 = 0; + + pSpxConnFile->scf_GlobalActiveNext = NULL; + pSpxConnFile->scf_PktNext = NULL; + pSpxConnFile->scf_CRetryCount = 0; + pSpxConnFile->scf_WRetryCount = 0; + pSpxConnFile->scf_RRetryCount = 0; + pSpxConnFile->scf_RRetrySeqNum = 0; + + pSpxConnFile->scf_CTimerId = + pSpxConnFile->scf_RTimerId = + pSpxConnFile->scf_WTimerId = + pSpxConnFile->scf_TTimerId = + pSpxConnFile->scf_ATimerId = 0; + + pSpxConnFile->scf_LocalConnId = + pSpxConnFile->scf_SendSeqNum = + pSpxConnFile->scf_SentAllocNum = + pSpxConnFile->scf_RecvSeqNum = + pSpxConnFile->scf_RetrySeqNum = + pSpxConnFile->scf_RecdAckNum = + pSpxConnFile->scf_RemConnId = + pSpxConnFile->scf_RecdAllocNum = 0; + +#if DBG + // Initialize so we dont hit breakpoint on seq 0 + pSpxConnFile->scf_PktSeqNum = 0xFFFF; +#endif + + pSpxConnFile->scf_DataType = 0; + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListTail == NULL); + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + pSpxConnFile->scf_ReqPkt = NULL; + + return; +} + + + + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!! Called with dev/addr/connection lock held !!! + +Arguments: + + This gets us back to associate SAVING the state of the STOPPING and + CLOSING flags so that dereference can go ahead and finish those. + +Return Value: + + +--*/ +{ + BOOLEAN fStopping, fClosing, fAborting; + + fStopping = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + fClosing = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + + // + // [SA] Bug #14655 + // Save the disconnect states so that a proper error can be given in the case of + // a send after a remote disconnection. + // + + // + // Bug #17729 + // Dont retain these flags if a local disconnect has already occured. + // + + fAborting = (!SPX2_CONN(pSpxConnFile) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)); + +#if DBG + pSpxConnFile->scf_GhostFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_GhostFlags2 = pSpxConnFile->scf_Flags2; + pSpxConnFile->scf_GhostRefCount = pSpxConnFile->scf_RefCount; +#endif + + // Clear all flags, go back to the assoc state. Restore stop/close + pSpxConnFile->scf_Flags = SPX_CONNFILE_ASSOC; + SPX_CONN_SETFLAG(pSpxConnFile, + ((fStopping ? SPX_CONNFILE_STOPPING : 0) | + (fClosing ? SPX_CONNFILE_CLOSING : 0))); + + // + // [SA] bug #14655 + // In order to avoid a re-entry, mark connection as SPX_DISC_INACTIVATED + // + if (fAborting) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_INACTIVATED); + } + + // Remove connection from global list on device + if (!NT_SUCCESS(spxConnRemoveFromGlobalActiveList( + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Remove connection from active list on address + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxConnFile->scf_AddrFile->saf_Addr->sa_ActiveConnList, + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Put connection in inactive list on address + SPX_INSERT_ADDR_INACTIVE( + pSpxConnFile->scf_AddrFile->saf_Addr, + pSpxConnFile); + + spxConnReInit(pSpxConnFile); + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxdev.c b/private/ntos/tdi/isnp/spx/spxdev.c new file mode 100644 index 000000000..431498686 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxdev.c @@ -0,0 +1,242 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDEV + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitCreateDevice) +#pragma alloc_text(PAGE, SpxDestroyDevice) +#endif + + + + +VOID +SpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->dev_RefCount); + + CTEAssert (result >= 0); + + if (result == 0) + { + // Close binding to IPX + SpxUnbindFromIpx(); + + // Set unload event. + KeSetEvent(&SpxUnloadEvent, IO_NETWORK_INCREMENT, FALSE); + } + +} // SpxDerefDevice + + + + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE * DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG DeviceNameOffset; + + + DBGPRINT(DEVICE, INFO, + ("SpxInitCreateDevice - Create device %ws\n", DeviceName->Buffer)); + + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors). + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + DBGPRINT(DEVICE, ERR, ("IoCreateDevice failed\n")); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + Device = (PDEVICE)deviceObject; + + DBGPRINT(DEVICE, INFO, ("IoCreateDevice succeeded %lx\n", Device)); + + // Initialize our part of the device context. + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + DeviceNameOffset = sizeof(DEVICE); + + // Copy over the device name. + Device->dev_DeviceNameLen = DeviceName->Length + sizeof(WCHAR); + Device->dev_DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + + RtlCopyMemory( + Device->dev_DeviceName, + DeviceName->Buffer, + DeviceName->Length); + + Device->dev_DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // Initialize the reference count. + Device->dev_RefCount = 1; + +#if DBG + Device->dev_RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->dev_Signature1, "IDC1", 4); + RtlCopyMemory(Device->dev_Signature2, "IDC2", 4); +#endif + + // Set next conn id to be used. + Device->dev_NextConnId = (USHORT)SpxRandomNumber(); + if (Device->dev_NextConnId == 0xFFFF) + { + Device->dev_NextConnId = 1; + } + + DBGPRINT(DEVICE, ERR, + ("SpxInitCreateDevice: Start Conn Id %lx\n", Device->dev_NextConnId)); + + // Initialize the resource that guards address ACLs. + ExInitializeResource (&Device->dev_AddrResource); + + // initialize the various fields in the device context + CTEInitLock (&Device->dev_Interlock); + CTEInitLock (&Device->dev_Lock); + KeInitializeSpinLock (&Device->dev_StatInterlock); + KeInitializeSpinLock (&Device->dev_StatSpinLock); + + Device->dev_State = DEVICE_STATE_CLOSED; + Device->dev_Type = SPX_DEVICE_SIGNATURE; + Device->dev_Size = sizeof (DEVICE); + + Device->dev_Stat.Version = 0x100; + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} // SpxCreateDevice + + + + +VOID +SpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&Device->dev_AddrResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} // SpxDestroyDevice diff --git a/private/ntos/tdi/isnp/spx/spxdrvr.c b/private/ntos/tdi/isnp/spx/spxdrvr.c new file mode 100644 index 000000000..0e9935d1a --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxdrvr.c @@ -0,0 +1,1008 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdrvr.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the SPX/SPXII module of the ISN transport. + +Author: + + Adam Barr (adamba) Original Version + 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 SPXDRVR + +// Forward declaration of various routines used in this module. + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath); + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject); + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SpxUnload) +#pragma alloc_text(PAGE, SpxDispatchOpenClose) +#pragma alloc_text(PAGE, SpxDispatch) +#pragma alloc_text(PAGE, SpxDeviceControl) +#pragma alloc_text(PAGE, SpxUnload) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the SPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of ST's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + UNICODE_STRING deviceName; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN BoundToIpx = FALSE; + + // DBGBRK(FATAL); + + // Initialize the Common Transport Environment. + if (CTEInitialize() == 0) { + return (STATUS_UNSUCCESSFUL); + } + + // We have this #define'd. Ugh, but CONTAINING_RECORD has problem owise. + CTEAssert(NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); + + // Create the device object. (IoCreateDevice zeroes the memory + // occupied by the object.) + RtlInitUnicodeString(&deviceName, SPX_DEVICE_NAME); + status = SpxInitCreateDevice( + DriverObject, + &deviceName, + &SpxDevice); + + if (!NT_SUCCESS(status)) + { + return(status); + } + + do + { + CTEInitLock (&SpxGlobalInterlock); + CTEInitLock (&SpxGlobalQInterlock); + + // Initialize the unload event + KeInitializeEvent( + &SpxUnloadEvent, + NotificationEvent, + FALSE); + + // !!!The device is created at this point!!! + // Get information from the registry. + status = SpxInitGetConfiguration( + RegistryPath, + &SpxDevice->dev_ConfigInfo); + + if (!NT_SUCCESS(status)) + { + break; + } + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif _PNP_POWER + + // Bind to the IPX transport. + if (!NT_SUCCESS(status = SpxInitBindToIpx())) + { + // BUGBUG: Have ipx name here as second string + LOG_ERROR( + EVENT_TRANSPORT_BINDING_FAILED, + status, + NULL, + NULL, + 0); + + break; + } + + BoundToIpx = TRUE; + +#if !defined(_PNP_POWER) + // Initialize the timer system + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif !_PNP_POWER + + // Initialize the block manager + if (!NT_SUCCESS(status = SpxInitMemorySystem(SpxDevice))) + { + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + break; + } + + // Initialize the driver object with this driver's entry points. + DriverObject->MajorFunction [IRP_MJ_CREATE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] + = SpxDispatch; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] + = SpxDispatchInternal; + DriverObject->DriverUnload = SpxUnload; + + // Initialize the provider info + SpxQueryInitProviderInfo(&SpxDevice->dev_ProviderInfo); + SpxDevice->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + +#if !defined(_PNP_POWER) + // We are open now. + SpxDevice->dev_State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == SpxDevice->dev_State ) { + SpxDevice->dev_State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + } while (FALSE); + + if (!NT_SUCCESS(status) ) + { + // Delete the device and any associated resources created. + if( BoundToIpx ) { + SpxDerefDevice(SpxDevice); + } + SpxDestroyDevice(SpxDevice); + } + + return (status); +} + + + + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. The I/O system will not + call us until nobody above has ST open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + UNREFERENCED_PARAMETER (DriverObject); + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + + // Remove creation reference count on the IPX device object. + SpxDerefDevice(SpxDevice); + + // Wait on the unload event. + KeWaitForSingleObject( + &SpxUnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL); + + // Release the block memory stuff + SpxDeInitMemorySystem(SpxDevice); + SpxDestroyDevice(SpxDevice); + return; +} + + + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // Make sure status information is consistent every time. + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + + Status = SpxDeviceControl (DeviceObject, Irp); + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + // + // Complete the Irp here instead of below. + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } // major function switch + + /* Commented out and re-located to the default case above. + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + */ + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatch + +VOID +SpxAssignControlChannelId( + IN PDEVICE Device, + IN PIRP Request + ) +/*++ + +Routine Description: + + This routine is required to ensure that the Device lock (to protect the ControlChannelId in the Device) + is not taken in a pageable routine (SpxDispatchOpenClose). + + NOTE: SPX returns the ControlChannelId in the Request, but never uses it later when it comes down in a + close/cleanup. The CCID is a ULONG; in future, if we start using this field (as in IPX which uses these Ids + to determine lineup Irps to complete), then we may run out of numbers (since we monotonically increase the CCID); + though there is a low chance of that since we will probably run out of memory before that! Anyhow, if that + happens, one solution (used in IPX) is to make the CCID into a Large Integer and pack the values into the + REQUEST_OPEN_TYPE(Irp) too. + + +Arguments: + + Device - Pointer to the device object for this driver. + + Request - Pointer to the request packet representing the I/O request. + +Return Value: + + None. + +--*/ +{ + CTELockHandle LockHandle; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->dev_CcId); + ++Device->dev_CcId; + if (Device->dev_CcId == 0) { + Device->dev_CcId = 1; + } + + CTEFreeLock (&Device->dev_Lock, LockHandle); +} + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + BOOLEAN found; + PREQUEST Request; + UINT i; + PFILE_FULL_EA_INFORMATION openType; + CONNECTION_CONTEXT connCtx; + + +#if !defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = SpxAddrOpen (Device, Request); + break; + } + + // Connection? + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + if (openType->EaValueLength < sizeof(CONNECTION_CONTEXT)) + { + + DBGPRINT(CREATE, ERR, + ("Create: Context size %d\n", openType->EaValueLength)); + + Status = STATUS_EA_LIST_INCONSISTENT; + break; + } + + connCtx = + *((CONNECTION_CONTEXT UNALIGNED *) + &openType->EaName[openType->EaNameLength+1]); + + Status = SpxConnOpen( + Device, + connCtx, + Request); + + break; + } + + } else { + + // + // Takes a lock in a Pageable routine - call another (non-paged) function to do that. + // + SpxAssignControlChannelId(Device, Request); + + REQUEST_OPEN_TYPE(Request) = (PVOID)SPX_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileClose(Device, Request); + break; + + case TDI_CONNECTION_FILE: + Status = SpxConnClose(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileCleanup(Device, Request); + break; + + case TDI_CONNECTION_FILE: + + Status = SpxConnCleanup(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } // major function switch + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchOpenClose + + + + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // Convert the user call to the proper internal device call. + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + if (Status == STATUS_SUCCESS) { + + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + Status = SpxDispatchInternal (DeviceObject, Irp); + + // + // Return the proper error code here. If SpxDispatchInternal returns an error, + // then we used to map it to pending below; this is wrong since the client above + // us could wait for ever since the IO subsystem does not set the event if an + // error is returned and the Irp is not marked pending. + // + + // Status = STATUS_PENDING; + } else { + + DBGPRINT(TDI, DBG, + ("Unknown Tdi code in Irp: %lx\n", Irp)); + + // + // Complete the Irp.... + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + return Status; + +} // SpxDeviceControl + + + + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PREQUEST Request; + KIRQL oldIrql; + NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; + PDEVICE Device = (PDEVICE)DeviceObject; + + + if (Device->dev_State != DEVICE_STATE_OPEN) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Cancel irp + IoAcquireCancelSpinLock(&oldIrql); + if (!Irp->Cancel) + { + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)SpxTdiCancel); + } + IoReleaseCancelSpinLock(oldIrql); + + if (Irp->Cancel) + return STATUS_CANCELLED; + + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + switch (REQUEST_MINOR_FUNCTION(Request)) + { + case TDI_ACCEPT: + + Status = SpxConnAccept( + Device, + Request); + + break; + + case TDI_SET_EVENT_HANDLER: + + Status = SpxAddrSetEventHandler( + Device, + Request); + + break; + + case TDI_RECEIVE: + + Status = SpxConnRecv( + Device, + Request); + break; + + + case TDI_SEND: + + Status = SpxConnSend( + Device, + Request); + break; + + case TDI_ACTION: + + Status = SpxConnAction( + Device, + Request); + break; + + case TDI_ASSOCIATE_ADDRESS: + + Status = SpxConnAssociate( + Device, + Request); + + break; + + case TDI_DISASSOCIATE_ADDRESS: + + Status = SpxConnDisAssociate( + Device, + Request); + + break; + + case TDI_CONNECT: + + Status = SpxConnConnect( + Device, + Request); + + break; + + case TDI_DISCONNECT: + + Status = SpxConnDisconnect( + Device, + Request); + break; + + case TDI_LISTEN: + + Status = SpxConnListen( + Device, + Request); + break; + + case TDI_QUERY_INFORMATION: + + Status = SpxTdiQueryInformation( + Device, + Request); + + break; + + case TDI_SET_INFORMATION: + + Status = SpxTdiSetInformation( + Device, + Request); + + break; + + // Something we don't know about was submitted. + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (Status != STATUS_PENDING) + { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IoAcquireCancelSpinLock(&oldIrql); + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(oldIrql); + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchInternal + + + + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles cancellation of IO requests + +Arguments: + + +Return Value: +--*/ +{ + PREQUEST Request; + PSPX_ADDR_FILE pSpxAddrFile; + PSPX_ADDR pSpxAddr; + PDEVICE Device = (PDEVICE)DeviceObject; + CTELockHandle connectIrql; + CTELockHandle TempIrql; + PSPX_CONN_FILE pSpxConnFile; + + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + return; + } + + DBGPRINT(TDI, ERR, + ("SpxTdiCancel: Cancel irp called %lx.%lx\n", + Irp, REQUEST_OPEN_CONTEXT(Request))); + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) + { + case TDI_CONNECTION_FILE: + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + CTEGetLock(&pSpxConnFile->scf_Lock, &connectIrql); + + // + // Swap the irql + // + TempIrql = connectIrql; + connectIrql = Irp->CancelIrql; + Irp->CancelIrql = TempIrql; + + IoReleaseCancelSpinLock (Irp->CancelIrql); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + // + // This releases the lock + // + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + connectIrql, + FALSE); // [SA] bug #15249 + } + } + +// SpxConnStop((PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request)); + break; + + case TDI_TRANSPORT_ADDRESS_FILE: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + pSpxAddr = pSpxAddrFile->saf_Addr; + SpxAddrFileStop(pSpxAddrFile, pSpxAddr); + break; + + default: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + break; + + } + +} diff --git a/private/ntos/tdi/isnp/spx/spxerror.c b/private/ntos/tdi/isnp/spx/spxerror.c new file mode 100644 index 000000000..7d2cc7444 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxerror.c @@ -0,0 +1,316 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxerror.c + +Abstract: + + This module contains code which provides error logging support. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXERROR + +LONG SpxLastRawDataLen = 0; +NTSTATUS SpxLastUniqueErrorCode = STATUS_SUCCESS; +NTSTATUS SpxLastNtStatusCode = STATUS_SUCCESS; +ULONG SpxLastErrorCount = 0; +LONG SpxLastErrorTime = 0; +BYTE SpxLastRawData[PORT_MAXIMUM_MESSAGE_LENGTH - \ + sizeof(IO_ERROR_LOG_PACKET)] = {0}; + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + + int insertionStringLength = 0; + + // Filter out events such that the same event recurring close together does not + // cause errorlog clogging. The scheme is - if the event is same as the last event + // and the elapsed time is > THRESHOLD and ERROR_CONSEQ_FREQ simulataneous errors + // have happened, then log it else skip + if ((UniqueErrorCode == SpxLastUniqueErrorCode) && + (NtStatusCode == SpxLastNtStatusCode)) + { + SpxLastErrorCount++; + if ((SpxLastRawDataLen == RawDataLen) && + (RtlEqualMemory(SpxLastRawData, RawDataBuf, RawDataLen)) && + ((SpxLastErrorCount % ERROR_CONSEQ_FREQ) != 0) && + ((SpxGetCurrentTime() - SpxLastErrorTime) < ERROR_CONSEQ_TIME)) + { + return(FALSE); + } + } + + SpxLastUniqueErrorCode = UniqueErrorCode; + SpxLastNtStatusCode = NtStatusCode; + SpxLastErrorCount = 0; + SpxLastErrorTime = SpxGetCurrentTime(); + if (RawDataLen != 0) + { + SpxLastRawDataLen = RawDataLen; + RtlCopyMemory( + SpxLastRawData, + RawDataBuf, + RawDataLen); + } + + return(TRUE); +} + + + + +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + UINT i; + + if (!SpxFilterErrorLogEntry( + EVENT_TRANSPORT_RESOURCE_POOL, + STATUS_INSUFFICIENT_RESOURCES, + (PVOID)&BytesNeeded, + sizeof(BytesNeeded))) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->dev_DeviceNameLen + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + // Convert the error value into a buffer. + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) + { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + RtlCopyMemory( + StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + } +} + + + + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + RawDataBuf - The number of ULONGs of dump data. + + RawDataLen - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[4] = L"Spx"; + + if (!SpxFilterErrorLogEntry( + ErrorCode, + FinalStatus, + RawDataBuf, + RawDataLen)) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + EntrySize += (UCHAR)Device->dev_DeviceNameLen; + } + else + { + EntrySize += sizeof(DriverName); + } + + if (SecondString) + { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)RawDataLen; + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (RawDataLen != 0) + { + RtlCopyMemory(errorLogEntry->DumpData, RawDataBuf, RawDataLen); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + } + else + { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + + if (SecondString) + { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxmem.c b/private/ntos/tdi/isnp/spx/spxmem.c new file mode 100644 index 000000000..9cd400e5b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxmem.c @@ -0,0 +1,897 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.c + +Abstract: + + This module contains code which implements the memory allocation wrappers. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + Jameel Hyder (jameelh) Initial Version + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, SpxInitMemorySystem) +#pragma alloc_text( PAGE, SpxDeInitMemorySystem) +#endif + +// Define module number for event logging entries +#define FILENUM SPXMEM + +// Globals for this module +// Some block sizes (like NDISSEND/NDISRECV are filled in after binding with IPX) +USHORT spxBlkSize[NUM_BLKIDS] = // Size of each block + { + sizeof(BLK_HDR)+sizeof(TIMERLIST), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +USHORT spxChunkSize[NUM_BLKIDS] = // Size of each Chunk + { + 512-BC_OVERHEAD, // BLKID_TIMERLIST + 512-BC_OVERHEAD, // BLKID_NDISSEND + 512-BC_OVERHEAD // BLKID_NDISRECV + }; + + +// Filled in after binding with IPX +// Reference for below. +// ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ +// (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST +USHORT spxNumBlks[NUM_BLKIDS] = // Number of blocks per chunk + { + ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ + (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +CTELock spxBPLock[NUM_BLKIDS] = { 0 }; +PBLK_CHUNK spxBPHead[NUM_BLKIDS] = { 0 }; + + + + +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + !!! MUST BE CALLED AFTER BINDING TO IPX!!! + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i; + NDIS_STATUS ndisStatus; + + // Try to allocate the ndis buffer pool. + NdisAllocateBufferPool( + &ndisStatus, + &pSpxDevice->dev_NdisBufferPoolHandle, + 20); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + return(STATUS_INSUFFICIENT_RESOURCES); + + for (i = 0; i < NUM_BLKIDS; i++) + CTEInitLock (&spxBPLock[i]); + + // Set the sizes in the block id info arrays. + for (i = 0; i < NUM_BLKIDS; i++) + { + // BUGBUG: Do it. + switch (i) + { + case BLKID_NDISSEND: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_SEND_RESD) + + NDIS_PACKET_SIZE + + IpxMacHdrNeeded + + MIN_IPXSPX2_HDRSIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + case BLKID_NDISRECV: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_RECV_RESD) + + NDIS_PACKET_SIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + default: + + break; + } + + } + + SpxTimerScheduleEvent((TIMER_ROUTINE)spxBPAgePool, + BLOCK_POOL_TIMER, + NULL); +} + + + + +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i, j, NumBlksPerChunk; + PBLK_CHUNK pChunk, pFree; + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + for (pChunk = spxBPHead[i]; + pChunk != NULL; ) + { + DBGPRINT(RESOURCES, ERR, + ("SpxInitMemorySystem: Freeing %lx\n", pChunk)); + + CTEAssert (pChunk->bc_NumFrees == NumBlksPerChunk); + + if ((pChunk->bc_BlkId == BLKID_NDISSEND) || + (pChunk->bc_BlkId == BLKID_NDISRECV)) + { + PBLK_HDR pBlkHdr; + + // We need to free the Ndis stuff for these guys + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + +#ifdef SPX_OWN_PACKETS + // Only need to free the ndis buffer. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + + // + // Free the second MDL also + // + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } +#else + // Need to free both the packet and the buffer. + ppNdisPkt = (PNDIS_PACKET *)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + + NdisUnchainBufferAtFront(*ppNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } + NdisFreePacket(*ppNdisPkt); +#endif + } + } + pFree = pChunk; + pChunk = pChunk->bc_Next; + +#ifndef SPX_OWN_PACKETS + // Free the ndis packet pool in chunk + NdisFreePacketPool((NDIS_HANDLE)pFree->bc_ChunkCtx); +#endif + SpxFreeMemory(pFree); + } + } + + // Free up the ndis buffer pool + NdisFreeBufferPool( + pSpxDevice->dev_NdisBufferPoolHandle); + + return; +} + + + + +PVOID +SpxAllocMem( +#ifdef TRACK_MEMORY_USAGE + IN ULONG Size, + IN ULONG FileLine +#else + IN ULONG Size +#endif // TRACK_MEMORY_USAGE + ) +/*++ + +Routine Description: + + Allocate a block of non-paged memory. This is just a wrapper over ExAllocPool. + Allocation failures are error-logged. We always allocate a ULONG more than + the specified size to accomodate the size. This is used by SpxFreeMemory + to update the statistics. + +Arguments: + + +Return Value: + + +--*/ +{ + PBYTE pBuf; + BOOLEAN zeroed; + + // round up the size so that we can put a signature at the end + // that is on a LARGE_INTEGER boundary + zeroed = ((Size & ZEROED_MEMORY_TAG) == ZEROED_MEMORY_TAG); + + Size = QWORDSIZEBLOCK(Size & ~ZEROED_MEMORY_TAG); + + // Do the actual memory allocation. Allocate eight extra bytes so + // that we can store the size of the allocation for the free routine + // and still keep the buffer quadword aligned. + + if ((pBuf = ExAllocatePoolWithTag(NonPagedPool, Size + sizeof(LARGE_INTEGER) +#if DBG + + sizeof(ULONG) +#endif + ,SPX_TAG)) == NULL) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxAllocMemory: failed - size %lx\n", Size)); + + TMPLOGERR(); + return NULL; + } + + // Save the size of this block in the four extra bytes we allocated. + *((PULONG)pBuf) = (Size + sizeof(LARGE_INTEGER)); + + // Return a pointer to the memory after the size longword. + pBuf += sizeof(LARGE_INTEGER); + +#if DBG + *((PULONG)(pBuf+Size)) = SPX_MEMORY_SIGNATURE; + DBGPRINT(RESOURCES, INFO, + ("SpxAllocMemory: %lx Allocated %lx bytes @%lx\n", + *(PULONG)((PBYTE)(&Size) - sizeof(Size)), Size, pBuf)); +#endif + + SpxTrackMemoryUsage((PVOID)(pBuf - sizeof(LARGE_INTEGER)), TRUE, FileLine); + + if (zeroed) + RtlZeroMemory(pBuf, Size); + + return (pBuf); +} + + + + +VOID +SpxFreeMemory( + IN PVOID pBuf + ) +/*++ + +Routine Description: + + Free the block of memory allocated via SpxAllocMemory. This is + a wrapper around ExFreePool. + +Arguments: + + +Return Value: + + +--*/ +{ + PULONG pRealBuffer; + + // Get a pointer to the block allocated by ExAllocatePool -- + // we allocate a LARGE_INTEGER at the front. + pRealBuffer = ((PULONG)pBuf - 2); + + SpxTrackMemoryUsage(pRealBuffer, FALSE, 0); + +#if DBG + // Check the signature at the end + if (*(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) + != SPX_MEMORY_SIGNATURE) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxFreeMemory: Memory overrun on block %lx\n", pRealBuffer)); + + DBGBRK(FATAL); + } + + *(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) = 0; +#endif + +#if DBG + *pRealBuffer = 0; +#endif + + // Free the pool and return. + ExFreePool(pRealBuffer); +} + + + + +#ifdef TRACK_MEMORY_USAGE + +#define MAX_PTR_COUNT 4*1024 +#define MAX_MEM_USERS 512 +CTELock spxMemTrackLock = {0}; +CTELockHandle lockHandle = {0}; +struct +{ + PVOID mem_Ptr; + ULONG mem_FileLine; +} spxMemPtrs[MAX_PTR_COUNT] = {0}; + +struct +{ + ULONG mem_FL; + ULONG mem_Count; +} spxMemUsage[MAX_MEM_USERS] = {0}; + +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine + ) +/*++ + +Routine Description: + + Keep track of memory usage by storing and clearing away pointers as and + when they are allocated or freed. This helps in keeping track of memory + leaks. + +Arguments: + + +Return Value: + + +--*/ +{ + static int i = 0; + int j, k; + + CTEGetLock (&spxMemTrackLock, &lockHandle); + + if (Alloc) + { + for (j = 0; j < MAX_PTR_COUNT; i++, j++) + { + i = i & (MAX_PTR_COUNT-1); + if (spxMemPtrs[i].mem_Ptr == NULL) + { + spxMemPtrs[i].mem_Ptr = pMem; + spxMemPtrs[i++].mem_FileLine = FileLine; + break; + } + } + + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == FileLine) + { + spxMemUsage[k].mem_Count ++; + break; + } + } + if (k == MAX_MEM_USERS) + { + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == 0) + { + spxMemUsage[k].mem_FL = FileLine; + spxMemUsage[k].mem_Count = 1; + break; + } + } + } + if (k == MAX_MEM_USERS) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: Out of space on spxMemUsage !!!\n")); + + DBGBRK(FATAL); + } + } + else + { + for (j = 0, k = i; j < MAX_PTR_COUNT; j++, k--) + { + k = k & (MAX_PTR_COUNT-1); + if (spxMemPtrs[k].mem_Ptr == pMem) + { + spxMemPtrs[k].mem_Ptr = 0; + spxMemPtrs[k].mem_FileLine = 0; + break; + } + } + } + + CTEFreeLock (&spxMemTrackLock, lockHandle); + + if (j == MAX_PTR_COUNT) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: %s\n", Alloc ? "Table Full" : "Can't find")); + + DBGBRK(FATAL); + } +} + +#endif // TRACK_MEMORY_USAGE + + + + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Alloc a block of memory from the block pool package. This is written to speed up + operations where a lot of small fixed size allocations/frees happen. Going to + ExAllocPool() in these cases is expensive. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_HDR pBlk = NULL; + PBLK_CHUNK pChunk, *ppChunkHead; + USHORT BlkSize; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PNDIS_BUFFER pNdisIpxSpxBuffer; + + + CTEAssert (BlockId < NUM_BLKIDS); + + if (BlockId < NUM_BLKIDS) + { + BlkSize = spxBlkSize[BlockId]; + ppChunkHead = &spxBPHead[BlockId]; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = *ppChunkHead; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pChunk->bc_NumFrees > 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Found space in Chunk %lx\n", pChunk)); +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPHits); +#endif + break; + } + } + + if (pChunk == NULL) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Allocating a new chunk for Id %d\n", BlockId)); + +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPMisses); +#endif + pChunk = SpxAllocateMemory(spxChunkSize[BlockId]); + if (pChunk != NULL) + { + LONG i, j; + PBLK_HDR pBlkHdr; + USHORT NumBlksPerChunk; + + NumBlksPerChunk = spxNumBlks[BlockId]; + pChunk->bc_NumFrees = NumBlksPerChunk; + pChunk->bc_BlkId = BlockId; + pChunk->bc_FreeHead = (PBLK_HDR)((PBYTE)pChunk + sizeof(BLK_CHUNK)); + + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Initializing chunk %lx\n", pChunk)); + + // Initialize the blocks in the chunk + for (i = 0, pBlkHdr = pChunk->bc_FreeHead; + i < NumBlksPerChunk; + i++, pBlkHdr = pBlkHdr->bh_Next) + { + NDIS_STATUS ndisStatus; + + pBlkHdr->bh_Next = (PBLK_HDR)((PBYTE)pBlkHdr + BlkSize); + if (BlockId == BLKID_NDISSEND) + { + PBYTE pHdrMem; + +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + // Allocate a ndis buffer descriptor describing hdr memory + // and queue it in. + pHdrMem = (PBYTE)pNdisPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD); + + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem, + IpxMacHdrNeeded); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + + + NdisAllocateBuffer( + &ndisStatus, + &pNdisIpxSpxBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem + IpxMacHdrNeeded, + MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisIpxSpxBuffer); + + + + pSendResd = (PSPX_SEND_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + + // Initialize elements of the protocol reserved structure. + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = SPX_SENDPKT_IDLE; + } + else if (BlockId == BLKID_NDISRECV) + { +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + pRecvResd = (PSPX_RECV_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + // Initialize elements of the protocol reserved structure. + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = SPX_RECVPKT_IDLE; + } + } + + if (i != NumBlksPerChunk) + { + // This has to be a failure from Ndis for send blocks!!! + // Undo a bunch of stuff + CTEAssert (BlockId == BLKID_NDISSEND); + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < i; j++, pBlkHdr = pBlkHdr->bh_Next) + { + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisBuffer); + + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisIpxSpxBuffer); + + if (pNdisIpxSpxBuffer) + { + NdisFreeBuffer(pNdisIpxSpxBuffer); + } + } + + SpxFreeMemory(pChunk); + pChunk = NULL; + } + else + { + // Successfully initialized the chunk, link it in + pChunk->bc_Next = *ppChunkHead; + *ppChunkHead = pChunk; + } + } + } + + if (pChunk != NULL) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + DBGPRINT(RESOURCES, INFO, + ("SpxBPAllocBlock: Allocating a block out of chunk %lx(%d) for Id %d\n", + pChunk, pChunk->bc_NumFrees, BlockId)); + + pChunk->bc_NumFrees --; + pChunk->bc_Age = 0; // Reset age + pBlk = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlk->bh_Next; + pBlk->bh_pChunk = pChunk; + + // Skip the block header! + pBlk++; + } + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + } + + return pBlk; +} + + + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Return a block to its owning chunk. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk; + PBLK_HDR pBlkHdr = (PBLK_HDR)((PCHAR)pBlock - sizeof(BLK_HDR)); + CTELockHandle lockHandle; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = spxBPHead[BlockId]; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pBlkHdr->bh_pChunk == pChunk) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPFreeBlock: Returning Block %lx to chunk %lx for Id %d\n", + pBlkHdr, pChunk, BlockId)); + + CTEAssert (pChunk->bc_NumFrees < spxNumBlks[BlockId]); + pChunk->bc_NumFrees ++; + pBlkHdr->bh_Next = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlkHdr; + break; + } + } + CTEAssert ((pChunk != NULL) && (pChunk->bc_FreeHead == pBlkHdr)); + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + return; +} + + + + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + Age out the block pool of unused blocks + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk, *ppChunk, pFree = NULL; + LONG i, j, NumBlksPerChunk; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + + if (TimerShuttingDown) + { + return TIMER_DONT_REQUEUE; + } + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + CTEGetLock(&spxBPLock[i], &lockHandle); + + for (ppChunk = &spxBPHead[i]; + (pChunk = *ppChunk) != NULL; ) + { + if ((pChunk->bc_NumFrees == NumBlksPerChunk) && + (++(pChunk->bc_Age) >= MAX_BLOCK_POOL_AGE)) + { + DBGPRINT(SYSTEM, INFO, + ("spxBPAgePool: freeing Chunk %lx, Id %d\n", + pChunk, pChunk->bc_BlkId)); + + *ppChunk = pChunk->bc_Next; +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPAge); +#endif + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + PBLK_HDR pBlkHdr; + + // We need to free Ndis stuff for these guys + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + } + } + + SpxFreeMemory(pChunk); + } + else + { + ppChunk = &pChunk->bc_Next; + } + } + CTEFreeLock(&spxBPLock[i], lockHandle); + } + + return TIMER_REQUEUE_CUR_VALUE; +} diff --git a/private/ntos/tdi/isnp/spx/spxpkt.c b/private/ntos/tdi/isnp/spx/spxpkt.c new file mode 100644 index 000000000..5f472d8cd --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxpkt.c @@ -0,0 +1,1594 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.c + +Abstract: + + This module contains code that builds various spx packets. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXPKT + +VOID +SpxPktBuildCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + NOTE: If *ppPkt is NULL, we allocate a packet. If not, we just + recreate the data and don't update the packet's state. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + if (*ppPkt == NULL) { + + SpxAllocSendPacket(SpxDevice, &pCrPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + } else { + + pCrPkt = *ppPkt; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + NdisQueryPacket(pCrPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? (SPX_CC_SPX2 | SPX_CC_NEG) : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = 0xFFFF; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + // Initialize + + if (*ppPkt == NULL) { + + pSendResd = (PSPX_SEND_RESD)(pCrPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX_HDRSIZE; + + *ppPkt = pCrPkt; + } + + return; +} + + + + +VOID +SpxPktBuildCrAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrAckPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pCrAckPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrAckPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pCrAckPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | + (fSpx2 ? SPX_CC_SPX2 : 0) | + (fNeg ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnBuildCrAck: Spx2 packet size %d.%lx\n", + pSpxConnFile->scf_MaxPktSize)); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pCrAckPkt; + return; +} + + + +VOID +SpxPktBuildSn( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSn: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SN; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSnAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SNACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSs( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSs: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SS; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + } while (FALSE); + + return; +} + + + +VOID +SpxPktBuildSsAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SSACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRr( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + // For a renegotiate request, we use the sequence number of + // the first waiting data packet. Passed in. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + SeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_SeqNum = SeqNum; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRrAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + // For the RrAck, ack number will be the appropriate number + // for the last data packet received. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RenegAckAckNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + MaxPktSize); + + DBGPRINT(SEND, DBG3, + ("SpxPktBuildRrAck: SEQ %lx ACKNUM %lx ALLOCNUM %lx MAXPKT %lx\n", + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_RenegAckAckNum, + pSpxConnFile->scf_SentAllocNum, + MaxPktSize)); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDiscPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDiscPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDiscPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pDiscPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_ACK | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0) | + ((DataType == SPX2_DT_IDISC) ? 0 : SPX_CC_EOM)); + + pIpxSpxHdr->hdr_DataType = DataType; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDiscPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = + ((DataType == SPX2_DT_IDISC) ? SPX_TYPE_IDISC : SPX_TYPE_ORDREL); + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pRequest; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Offset = 0; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pDiscPkt; + } + + return; +} + + + + +VOID +SpxPktBuildProbe( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pProbe; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pProbe, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pProbe + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pProbe, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? SPX_CC_SPX2 : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_PROBE; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = NULL; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE + : MIN_IPXSPX_HDRSIZE); + + *ppPkt = pProbe; + } + + return; +} + + + + +VOID +SpxPktBuildData( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length + ) +/*++ + +Routine Description: + + Handles zero length sends. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDataPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDataPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Make a ndis buffer descriptor for the data if present. + if (Length > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_ReqPkt), + pSpxConnFile->scf_ReqPktOffset, + Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the send packet + SpxPktSendRelease(pDataPkt); + return; + } + + // Chain this in the packet + NdisChainBufferAtBack(pDataPkt, pNdisBuffer); + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDataPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + Length += hdrLen; + + NdisQueryPacket(pDataPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + Length, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (((State & SPX_SENDPKT_ACKREQ) ? SPX_CC_ACK : 0) | + ((State & SPX_SENDPKT_EOM) ? SPX_CC_EOM : 0) | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = (UCHAR)REQUEST_PARAMETERS(pSpxConnFile->scf_ReqPkt)->Others.Argument3; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDataPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = SPX_TYPE_DATA; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pSpxConnFile->scf_ReqPkt; + pSendResd->sr_Offset = pSpxConnFile->scf_ReqPktOffset; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = Length; + pSendResd->sr_HdrLen = hdrLen; + + if (State & SPX_SENDPKT_ACKREQ) + { + KeQuerySystemTime((PLARGE_INTEGER)&pSendResd->sr_SentTime); + } + + CTEAssert(pSendResd->sr_Len <= pSpxConnFile->scf_MaxPktSize); + *ppPkt = pDataPkt; + + // Ok, allocation succeeded. Increment send seq. + pSpxConnFile->scf_SendSeqNum++; + } + + return; +} + + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ) +/*++ + +Routine Description: + + Creates a TargetBufferChain from the SourceBufferChain. The copy begins at + the 'Offset' location in the source chain. It copies 'Length' bytes. It also + handles Length = 0. If we run out of source chain before copying length amount + of bytes or run out of memory to create any more buffers for the target chain, + we clean up the partial chain created so far. + +Arguments: + + Status - Status of the request. + TargetChain - Pointer to the allocated buffer descriptor. + PoolHandle - Handle that is used to specify the pool. + SourceChain - Pointer to the descriptor of the source memory. + Offset - The Offset in the sources memory from which the copy is to + begin + Length - Number of Bytes to copy. + +Return Value: + + None. + +--*/ +{ + UINT BytesBeforeCurBuffer = 0; + PNDIS_BUFFER CurBuffer = SourceChain; + UINT BytesLeft; + UINT AvailableBytes; + PNDIS_BUFFER NewNdisBuffer, StartTargetChain; + + CTEAssert( SourceChain ); + + // First of all find the source buffer that contains data that starts at + // Offset. + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( BytesBeforeCurBuffer + AvailableBytes <= Offset ) { + BytesBeforeCurBuffer += AvailableBytes; + CurBuffer = CurBuffer->Next; + if ( CurBuffer ) { + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } else { + break; + } + } + + if ( ! CurBuffer ) { + *Status = STATUS_UNSUCCESSFUL; + return; + } + + // + // Copy the first buffer. This takes care of Length = 0. + // + BytesLeft = Length; + + // + // ( Offset - BytesBeforeCurBuffer ) gives us the offset within this buffer. + // + + AvailableBytes -= ( Offset - BytesBeforeCurBuffer ); + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &NewNdisBuffer, + PoolHandle, + CurBuffer, + Offset - BytesBeforeCurBuffer, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + return; + } + + StartTargetChain = NewNdisBuffer; + BytesLeft -= AvailableBytes; + + // + // Did the first buffer have enough data. If so, we r done. + // + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + // + // Now follow the Mdl chain and copy more buffers. + // + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( CurBuffer ) { + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + PoolHandle, + CurBuffer, + 0, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + return; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + BytesLeft -= AvailableBytes; + + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } + + // + // Ran out of source chain. This should not happen. + // + + CTEAssert( FALSE ); + + // For Retail build we clean up anyways. + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + *Status = STATUS_UNSUCCESSFUL; + return; +} + + +VOID +SpxPktBuildAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, ERR, + ("SpxPktBuildAck: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + // Send where data came from + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | (fSpx2 ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + if (fBuildNack) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + NumToResend); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + // Put current send seq number in packet for spx1 + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = (fBuildNack ? SPX_TYPE_DATANACK : SPX_TYPE_DATAACK); + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pPkt; + return; +} + + + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt + ) +{ + ((PSPX_RECV_RESD)(pPkt->ProtocolReserved))->rr_State = SPX_RECVPKT_IDLE; + SpxFreeRecvPacket(SpxDevice, pPkt); + return; +} + + + + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt + ) +{ + PNDIS_BUFFER pBuf, pIpxSpxBuf, pFreeBuf; + UINT bufCount; + + CTEAssert((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_IPXOWNS) == 0); + + NdisQueryPacket(pPkt, NULL, &bufCount, &pBuf, NULL); + + // BufCount == 1 for only the header. That's ok, we just reset the length + // and free the packet to the buffer pools. Else we need to free user buffers + // before that. + + NdisUnchainBufferAtFront( + pPkt, + &pBuf); + + NdisUnchainBufferAtFront( + pPkt, + &pIpxSpxBuf); + + // + // Set the header length to the max. that can be needed. + // + NdisAdjustBufferLength(pIpxSpxBuf, MIN_IPXSPX2_HDRSIZE); + + while (bufCount-- > 2) + { + PBYTE pData; + ULONG dataLen; + + NdisUnchainBufferAtBack( + pPkt, + &pFreeBuf); + + // See if we free data associated with the buffer + if ((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_FREEDATA) != 0) + { + NdisQueryBuffer(pFreeBuf, &pData, &dataLen); + CTEAssert(pData != NULL); + SpxFreeMemory(pData); + } + + CTEAssert(pFreeBuf != NULL); + NdisFreeBuffer(pFreeBuf); + } + + NdisReinitializePacket(pPkt); + + // Initialize elements of the protocol reserved structure. + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Id = IDENTIFIER_SPX; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State = SPX_SENDPKT_IDLE; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved1= NULL; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved2= NULL; + + NdisChainBufferAtFront( + pPkt, + pBuf); + + NdisChainBufferAtBack( + pPkt, + pIpxSpxBuf); + + SpxFreeSendPacket(SpxDevice, pPkt); + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxquery.c b/private/ntos/tdi/isnp/spx/spxquery.c new file mode 100644 index 000000000..047ecabe8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxquery.c @@ -0,0 +1,259 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Author: + + Adam Barr (adamba) Initial Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxQueryInitProviderInfo) +#endif + +// Define module number for event logging entries +#define FILENUM SPXQUERY + +// Useful macro to obtain the total length of an MDL chain. +#define SpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo + ) +{ + // Initialize to defaults first + RtlZeroMemory((PVOID)ProviderInfo, sizeof(TDI_PROVIDER_INFO)); + + ProviderInfo->Version = SPX_TDI_PROVIDERINFO_VERSION; + KeQuerySystemTime (&ProviderInfo->StartTime); + ProviderInfo->MinimumLookaheadData = SPX_PINFOMINMAXLOOKAHEAD; + ProviderInfo->MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + ProviderInfo->MaxSendSize = SPX_PINFOSENDSIZE; + ProviderInfo->ServiceFlags = SPX_PINFOSERVICEFLAGS; + return; +} + + + + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE AddressFile; + PSPX_CONN_FILE ConnectionFile; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS SpxAddress; + } AddressInfo; + + + + // what type of status do we want? + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) + { + case TDI_QUERY_CONNECTION_INFO: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADDRESS_INFO: + + // The caller wants the exact address value. + + ConnectionFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + status = SpxConnFileVerify(ConnectionFile); + + if (status == STATUS_SUCCESS) { + AddressFile = ConnectionFile->scf_AddrFile; + SpxConnFileDereference(ConnectionFile, CFREF_VERIFY); + } else { + AddressFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + } + + status = SpxAddrFileVerify(AddressFile); + + if (status == STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, INFO, + ("SpxTdiQuery: Net.Socket %lx.%lx\n", + *(PULONG)Device->dev_Network, + AddressFile->saf_Addr->sa_Socket)); + + AddressInfo.ActivityCount = 0; + (VOID)SpxBuildTdiAddress( + &AddressInfo.SpxAddress, + sizeof(TA_IPX_ADDRESS), + Device->dev_Network, + Device->dev_Node, + AddressFile->saf_Addr->sa_Socket); + + status = TdiCopyBufferToMdl( + &AddressInfo, + 0, + sizeof(AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + SpxAddrFileDereference(AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: { + BYTE socketType; + TDI_PROVIDER_INFO providerInfo = Device->dev_ProviderInfo; + + // + // The device name extension comes down in the Irp + // + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) { + DBGPRINT(RECEIVE, ERR, ("TDI_QUERY_PROVIDER_INFO: SpxUtilGetSocketType failed: %lx\n", status)); + return(status); + } + + // + // The Catapult folks had a problem where AFD was discarding buffered sends on the NT box when it got a + // local disconnect on SPX1. This was because the Orderly release flag was always set in the provider + // info. AFD queries this once per device type. We detect the device above and OR in the orderly release + // flag if this query came down on an SPX2 endpoint. + // This is to make sure that AFD follows the correct disconnect semantics for SPX1 and SPX2 (SPX1 does + // only abortive; SPX2 does both abortive and orderly). + // + // BUGBUG: this will still not solve the problem completely since a connection that starts off as an SPX2 + // one can still be negotiated to SPX1 if the remote supports only SPX1. + // + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) { + + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX2 socket\n")); + providerInfo.ServiceFlags |= TDI_SERVICE_ORDERLY_RELEASE; + } else { + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX1 socket\n")); + } + + status = TdiCopyBufferToMdl ( + &providerInfo, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + } + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->dev_Stat, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} // SpxTdiQueryInformation + + + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} // SpxTdiSetInformation + diff --git a/private/ntos/tdi/isnp/spx/spxrecv.c b/private/ntos/tdi/isnp/spx/spxrecv.c new file mode 100644 index 000000000..2408f25e8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxrecv.c @@ -0,0 +1,2837 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXRECV + +VOID +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +{ + PIPXSPX_HDR pHdr; + + // We have a separate routine to process SYS packets. DATA packets are + // processed within this routine. + if (LookaheadBufferSize < MIN_IPXSPX_HDRSIZE) + { + DBGPRINT(RECEIVE, ERR, + ("SpxReceive: Invalid length %lx\n", LookaheadBufferSize)); + + return; + } + + ++SpxDevice->dev_Stat.PacketsReceived; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + if ((pHdr->hdr_ConnCtrl & SPX_CC_SYS) == 0) + { + // Check for data packets + if ((pHdr->hdr_DataType != SPX2_DT_ORDREL) && + (pHdr->hdr_DataType != SPX2_DT_IDISC) && + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK)) + { + // HANDLE DATA PACKET + SpxRecvDataPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + else + { + // The whole packet better be in the lookahead, else we ignore. + if (LookaheadBufferSize == PacketSize) + { + SpxRecvDiscPacket( + LookaheadBuffer, + RemoteAddress, + LookaheadBufferSize); + } + } + } + else + { + SpxRecvSysPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + + return; +} + + + + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + CTELockHandle lockHandle; + NTSTATUS status; + BOOLEAN fAck, fEom, fBuffered, fImmedAck, fLockHeld; + PNDIS_BUFFER pNdisBuffer; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: For %lx with status %lx\n", pNdisPkt, NdisStatus)); + + pRecvResd = RECV_RESD(pNdisPkt); + pSpxConnFile = pRecvResd->rr_ConnFile; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + + fEom = ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + fImmedAck = ((pRecvResd->rr_State & SPX_RECVPKT_IMMEDACK) != 0); + fBuffered = ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + fAck = ((pRecvResd->rr_State & SPX_RECVPKT_SENDACK) != 0); + + // Check if receive is done. If we remove the reference for this + // packet and it goes to zero, that means the receive was aborted. + // Move to the completion queue. + // If receive is filled up, then remove the creation reference + // i.e. just complete the receive at this point. + // There can be only one packet per receive, we dont support + // out of order reception. + + if (!fBuffered) + { + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert((pNdisBuffer != NULL) || (BytesTransferred == 0)); + + // BUG #11772 + // On MP-machines scf_CurRecvReq could be set to NULL. Get the req + // from the recv packet. + // pRequest = pSpxConnFile->scf_CurRecvReq; + // CTEAssert(pRequest == pRecvResd->rr_Request); + pRequest = pRecvResd->rr_Request; + + // Remove reference for this packet. + --(REQUEST_INFORMATION(pRequest)); + + if (NdisStatus == NDIS_STATUS_SUCCESS) + { + pSpxConnFile->scf_CurRecvOffset += BytesTransferred; + pSpxConnFile->scf_CurRecvSize -= BytesTransferred; + +#if DBG + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (BytesTransferred != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= BytesTransferred; + } + } +#endif + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom || + ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL))) + { + CTELockHandle lockHandleInter; + + // We are done with this receive. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + + status = STATUS_SUCCESS; + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + status = STATUS_RECEIVE_PARTIAL; + } + + if ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL)) + { + status = REQUEST_STATUS(pRequest); + } + + REQUEST_STATUS(pRequest) = status; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + } + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + else + { + // Buffered receive, queue it in if successful. + // BUG #18363 + // IF WE DISCONNECTED in the meantime, we need to just dump this + // packet. + if (SPX_CONN_ACTIVE(pSpxConnFile) && + (NdisStatus == NDIS_STATUS_SUCCESS)) + { + // Queue packet in connection. Reference connection for this. + SpxConnQueueRecvPktTail(pSpxConnFile, pNdisPkt); + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: Buffering: %lx Pkt %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, BytesTransferred, pRecvResd->rr_State)); + + // There could either be queued receives. (This could happen in + // a partial receive case. Or if a receive got queued in while we + // were processing this packet (Possible on MP)), or a packet was + // buffered while we were completing some receives + + CTEAssert(pSpxConnFile->scf_RecvListHead); + + if ((pSpxConnFile->scf_CurRecvReq != NULL) || + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)) + { + CTELockHandle interLockHandle; + + // Push this connection into a ProcessRecv queue which will be + // dealt with in receive completion. + + DBGPRINT(RECEIVE, DBG, + ("spxRecvTransferData: Queueing for recvp %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // Get the global q lock, push into recv list. + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + } + else + { + PBYTE pData; + ULONG dataLen; + + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + // Free the data, ndis buffer. + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + SpxFreeMemory(pData); + } + + // Dont send ack, set status to be failure so we free packet/buffer. + fAck = FALSE; + NdisStatus = NDIS_STATUS_FAILURE; + } + } + + END_PROCESS_PACKET( + pSpxConnFile, fBuffered, (NdisStatus == NDIS_STATUS_SUCCESS)); + + if (fAck) + { + // Rem ack addr should have been copied in receive. + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (!fBuffered || (NdisStatus != STATUS_SUCCESS)) + { + // Free the ndis packet/buffer + SpxPktRecvRelease(pNdisPkt); + } + + return; +} + + + + +VOID +SpxReceiveComplete( + IN USHORT NicId + ) + +{ + CTELockHandle lockHandleInter, lockHandle; + PREQUEST pRequest; + BOOLEAN fConnLockHeld, fInterlockHeld; + PSPX_CONN_FILE pSpxConnFile; + int numDerefs = 0; + + // See if any connections need recv processing. This will also take + // care of any acks opening up window so our sends go to the max. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + fInterlockHeld = TRUE; + + while ((pSpxConnFile = SpxRecvConnList.pcl_Head) != NULL) + { + // Reset for each connection + numDerefs = 0; + + if ((SpxRecvConnList.pcl_Head = pSpxConnFile->scf_ProcessRecvNext) == NULL) + SpxRecvConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_ProcessRecvNext = NULL; + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromRecv: %lx\n", pSpxConnFile)); + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + do + { + // Complete pending requests. + while (!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) + { + pRequest = + LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqDoneLinkage.Flink); + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert (REQUEST_MINOR_FUNCTION(pRequest) != TDI_RECEIVE); + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + + // Call process pkts if we have any packets or if any receives to + // complete. Note this will call even when there are no receives + // queued and the first packet has already been indicated. + if ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage) || + (pSpxConnFile->scf_RecvListHead != NULL))) + { + // We have the flag reference on the connection. + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + +#if DBG + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: RecvDone left %lx\n", + pSpxConnFile)); + } +#endif + + // Hmm. This check is rather expensive, and essentially we are doing + // it twice. Should look to see if this can be modified safely. + } while ((!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + ((!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + (SPX_RECVPKT_BUFFERING | SPX_RECVPKT_INDICATED)) == + SPX_RECVPKT_BUFFERING))))); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RECVQ); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_RECV, + CFREF_VERIFY); + + numDerefs++; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + + // First see if we need to packetize. + while ((pSpxConnFile = SpxPktConnList.pcl_Head) != NULL) + { + if ((SpxPktConnList.pcl_Head = pSpxConnFile->scf_PktNext) == NULL) + SpxPktConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_PktNext = NULL; + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fConnLockHeld = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("SpxReceiveComplete: Packetizing %lx\n", pSpxConnFile)); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + if (SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle)) + { + // Done. + fConnLockHeld = FALSE; + } + } + + if (fConnLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_PKTIZE); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + if (fInterlockHeld) + { + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + return; +} + + + + +// +// PACKET HANDLING ROUTINES +// + + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming system packet. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld = FALSE; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvSysPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + if ((pktLen == SPX_CR_PKTLEN) && + (destConnId == 0xFFFF) && + (pHdr->hdr_ConnCtrl & SPX_CC_CR)) + { + spxConnHandleConnReq( + pHdr, + pRemoteAddr); + + return; + } + + // + // [SA] Bug #14917 + // Some SPX SYS packets (no extended ack field) may come in with the SPX2 bit set. + // Make sure we don't discard these packets. + // + + // if ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && (pktLen < MIN_IPXSPX2_HDRSIZE)) + // { + // return; + // } + + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnSysPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Id %lx NOT FOUND\n", destConnId)); + return; + } + + do + { + + DBGPRINT(RECEIVE, INFO, + ("SpxConnSysPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // This could be one of many packets. Connection ack/Session negotiate/ + // Session setup, Data Ack, Probe/Ack, Renegotiate/Ack. We shunt + // off all the packets to different routines but process the data + // ack packets here. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAddr); + + lockHeld = TRUE; + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromSrv( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_LISTENING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromClient( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // NOTE: Our ack to a session setup might get dropped. + // But the SS Ack is similar to a normal SPX2 ack. + // We dont have to do anything special. + + // Received ack/nack/reneg/reneg ack/disc associated packet. + // Disc packets except ordrel ack have non-zero datastream type. + if ((pHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) + { + // We received a renegotiate packet. Ignore all ack values + // in a reneg req. + SpxConnProcessRenegReq(pSpxConnFile, pHdr, pRemoteAddr, lockHandle); + lockHeld = FALSE; + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + if (((pHdr->hdr_ConnCtrl & SPX_CC_ACK) == 0) && + (pHdr->hdr_DataType == 0)) + { + SpxConnProcessAck(pSpxConnFile, pHdr, lockHandle); + lockHeld = FALSE; + } + else + { + // Just process the numbers we got. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + lockHeld = FALSE; + } + + // If the remote wants us to send an ack, do it. + if (pHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // First copy the remote address in connection. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (!lockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + } + + SpxConnSendAck(pSpxConnFile, lockHandle); + lockHeld = FALSE; + break; + } + + break; + + default: + + // Ignore this packet. + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Ignoring packet, state is not active\n")); + break; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (LookaheadSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvDiscPacket: Packet Size %lx\n", + pktLen)); + + return; + } + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDiscPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDiscPacket: Id %lx NOT FOUND", destConnId)); + + return; + } + + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: Id %lx Conn %lx DiscType %lx\n", + destConnId, pSpxConnFile, pHdr->hdr_DataType)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + + // Unless we are in the active/disconnecting, but send state = idle + // and recv state = idle/recv posted, we ignore all disconnect packets. + if (((SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_ACTIVE) && + (SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_DISCONN)) || + ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_IDLE) && + (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_POSTED)) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT))) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx, %lx, %lx.%lx, %d.%d\n", + pSpxConnFile, + SPX_MAIN_STATE(pSpxConnFile), + SPX_SEND_STATE(pSpxConnFile), SPX_RECV_STATE(pSpxConnFile), + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)), + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)))); + + break; + } + + // If we have received a disconnect, process received ack to complete any + // pending sends before we allow the disconnect. This ack number will be + // the last word on this session. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + switch (pHdr->hdr_DataType) + { + case SPX2_DT_ORDREL: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: Recd ORDREl!\n")); + + // BUGBUG: Need to deal with all sthe states. + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // On receive, we do check the seq num for the orderly release, just + // like for a data packet. + // If this was not already indicated, indicate it now. That is all + // we do for an orderly release. When our client does a orderly rel + // and we receive the ack for that, call abortive with success. + + // Verify ord rel packet, this checks if seq nums match also. + if ((pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) || + (pHdr->hdr_DataType != SPX2_DT_ORDREL) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: OR Failed/Ignored %lx, %lx.%lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + lockHeld = FALSE; + } + + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessOrdRel(pSpxConnFile, lockHandle); + lockHeld = FALSE; + } + else + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + // Set flag in last recd buffer + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_ORD_DISC; + } + + break; + + case SPX2_DT_IDISC: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC %lx!\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: SEND %d. RECV %d.%lx!\n", + IsListEmpty(&pSpxConnFile->scf_ReqLinkage), + IsListEmpty(&pSpxConnFile->scf_RecvLinkage), + pSpxConnFile->scf_RecvDoneLinkage)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + !(pHdr->hdr_ConnCtrl & SPX_CC_ACK) || + (pHdr->hdr_DataType != SPX2_DT_IDISC) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:IDISC Ignored %lx.%lx.%lx.%lx\n", + pSpxConnFile, seqNum, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO RECV DATA IDISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessIDisc(pSpxConnFile, lockHandle); + + lockHeld = FALSE; + } + else + { + // Set flag in last recd buffer + + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_IDISC; + } + + break; + + case SPX2_DT_IDISC_ACK: + + // Done with informed disconnect. Call abort connection with + // status success. That completes the pending disconnect request + // with status_success. + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC ack!\n", pSpxConnFile)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:Ver idisc ack Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + break; + } + + // We should be in the right state to accept this. + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_IDISC)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + break; + + default: + + KeBugCheck(0); + } + + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvBufferPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT LookaheadOffset, + IN PIPXSPX_HDR pIpxSpxHdr, + IN UINT PacketSize, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PSPX_RECV_RESD pRecvResd; + ULONG bytesCopied; + BOOLEAN fEom; + NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; + PBYTE pData = NULL; + PNDIS_BUFFER pNdisBuffer = NULL; + + if (PacketSize > 0) + { + // Allocate memory for this data. + if (pData = (PBYTE)SpxAllocateMemory(PacketSize)) + { + // Describe memory with a ndis buffer descriptor. + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + PacketSize); + } + else + { + ndisStatus = NDIS_STATUS_RESOURCES; + } + } + + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Queue the buffer into the packet if there is one. + if (pNdisBuffer) + { + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_DataOffset= 0; + +#if DBG + // Store seq number + GETSHORT2SHORT(&pRecvResd->rr_SeqNum , &pIpxSpxHdr->hdr_SeqNum); +#endif + + pRecvResd->rr_State = + (SPX_RECVPKT_BUFFERING | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fEom ? SPX_RECVPKT_EOM : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + } + + pRecvResd->rr_Request = NULL; + pRecvResd->rr_ConnFile = pSpxConnFile; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvBufferPkt: %lx Len %lx DataPts %lx F %lx\n", + pSpxConnFile, PacketSize, pData, pRecvResd->rr_State)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Call ndis transfer data. Copy ENTIRE packet. copySize has + // been modified so use original values. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (PacketSize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadOffset, + PacketSize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + + // BUG: FDDI returns pending which screws us up here. Stupid bug + ndisStatus = NDIS_STATUS_SUCCESS; + } + } + + // ASSERT: Lock will be freed in the success case. + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvBufferPkt: FAILED!\n")); + + END_PROCESS_PACKET(pSpxConnFile, FALSE, FALSE); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + if (pData != NULL) + { + SpxFreeMemory(pData); + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + + return; +} + + + + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadSize, + IN UINT LookaheadOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + ULONG receiveFlags; + PSPX_CONN_FILE pSpxConnFile; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PIRP pRecvIrp; + ULONG bytesTaken, iOffset, copySize, bytesCopied; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + NDIS_STATUS ndisStatus; + PREQUEST pRequest = NULL; + BOOLEAN fEom, + fImmedAck = FALSE, fLockHeld = FALSE, fPktDone = FALSE; + + pIpxSpxHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pIpxSpxHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + // We keep and use the remote id in the net format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: packet received dest %lx src %lx seq %lx\n", + pIpxSpxHdr->hdr_DestSkt, pIpxSpxHdr->hdr_SrcSkt, seqNum)); + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen < MIN_IPXSPX2_HDRSIZE)) + { + return; + } + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: Id %lx NOT FOUND", destConnId)); + return; + } + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + +#if 0 + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); +#endif + + fLockHeld = TRUE; + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + // Verify data packet, this checks if seq nums match also. + if ((pIpxSpxHdr->hdr_SrcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + !((pktLen >= MIN_IPXSPX_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen >= MIN_IPXSPX2_HDRSIZE)))) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + ++SpxDevice->dev_Stat.DataFramesRejected; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesRejected, + pktLen - (SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + + // + // Bug #16975: Set the remote ack addr for use in SpxConnSendAck() + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + + break; + } + + // If we have received an orderly release, we accept no more data + // packets. + if (SPX_CONN_FLAG( + pSpxConnFile, + (SPX_CONNFILE_IND_IDISC | + SPX_CONNFILE_IND_ODISC)) + + || + + ((pSpxConnFile->scf_RecvListTail != NULL) && + ((pSpxConnFile->scf_RecvListTail->rr_State & + SPX_RECVPKT_DISCMASK) != 0))) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDataPacket: After ord rel %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // We are processing a packet OR a receive is about to complete. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)) + { + BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum); + } + else + { + // Already processing a packet. Or a receive is waiting to + // complete. Get out. + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + iOffset = MIN_IPXSPX2_HDRSIZE; + if (!SPX2_CONN(pSpxConnFile)) + { + iOffset = 0; + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)) + { + iOffset = MIN_IPXSPX_HDRSIZE; + } + } + + copySize = pktLen - iOffset; + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + // Do we attempt to piggyback? If not, fImmedAck is true. + // For SPX1 we dont piggyback. + // Bug #18253 + fImmedAck = (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM) == 0)); + + // If we do not have EOM to indicate AND we are a zero-sized packet + // then just consume this packet. + if (!fEom && (copySize == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: ZERO LENGTH PACKET NO EOM %lx.%lx\n", + pSpxConnFile, seqNum)); + + fPktDone = TRUE; + break; + } + + receiveFlags = TDI_RECEIVE_NORMAL; + receiveFlags |= ((fEom ? TDI_RECEIVE_ENTIRE_MESSAGE : 0) | + (((MacOptions & + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0)); + + ++SpxDevice->dev_Stat.DataFramesReceived; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesReceived, + copySize); + + // Ok, we accept this packet. Depending on our state. + switch (SPX_RECV_STATE(pSpxConnFile)) + { + case SPX_RECV_PROCESS_PKTS: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: recv completions on %lx\n", + pSpxConnFile)); + + goto BufferPacket; + + case SPX_RECV_IDLE: + + // If recv q is non-empty we are buffering data. + // Also, if no receive handler goto buffer data. Also, if receives + // are being completed, buffer this packet. + if ((pSpxConnFile->scf_RecvListHead != NULL) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + !(pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: RecvListHead non-null %lx\n", + pSpxConnFile)); + + goto BufferPacket; + } + + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + + // Don't indicate this packet again. + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); + +#if DBG + CTEAssert(pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + + // + // Comment this out for Buf # 10394. we'r hitting this assert + // even when there was no data loss. + // + // CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != seqNum); + + pSpxConnFile->scf_PktSeqNum = seqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; + + pSpxConnFile->scf_IndBytes = copySize; + pSpxConnFile->scf_IndLine = __LINE__; + + +#endif + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + receiveFlags, + LookaheadSize - iOffset, + copySize, + &bytesTaken, + LookaheadBuffer + iOffset, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: IND Flags %lx.%lx ConnID %lx,\ + %lx Ctx %lx SEQ %lx Size %lx . %lx .%lx IND Status %lx\n", + pIpxSpxHdr->hdr_ConnCtrl, + receiveFlags, + destConnId, + pSpxConnFile, + pSpxConnFile->scf_ConnCtx, + seqNum, + LookaheadSize - iOffset, + copySize, + bytesTaken, + status)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: %x %x %x %x %x %x %x %x %x %x %x %x\n", + *(LookaheadBuffer+iOffset), + *(LookaheadBuffer+iOffset+1), + *(LookaheadBuffer+iOffset+2), + *(LookaheadBuffer+iOffset+3), + *(LookaheadBuffer+iOffset+4), + *(LookaheadBuffer+iOffset+5), + *(LookaheadBuffer+iOffset+6), + *(LookaheadBuffer+iOffset+7), + *(LookaheadBuffer+iOffset+8), + *(LookaheadBuffer+iOffset+9), + *(LookaheadBuffer+iOffset+10), + *(LookaheadBuffer+iOffset+11))); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. + CTEAssert((bytesTaken != 0) || fEom); + fPktDone = TRUE; + +#if DBG + // Set this to 0, since we just indicated, there could + // not have been other data. + pSpxConnFile->scf_IndBytes = 0; +#endif + + break; + } + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + break; + } + + // If there was indicated but not received data waiting + // (which in this path there will never be, the request + // could be completed given the data filled it up, and + // the lock released. + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + } + else if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + // Data was not accepted. Need to buffer data and + // reduce window. + goto BufferPacket; + } + + // Fall through to recv_posted. + } + else + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: !!!Ignoring %lx Seq %lx\n", + pSpxConnFile, + seqNum)); + + break; + } + + case SPX_RECV_POSTED: + + if (pSpxConnFile->scf_RecvListHead != NULL) + { + // This can happen also. Buffer packet if it does. + goto BufferPacket; + } + + // If a receive irp is posted, then process the receive irp. If + // we fell thru we MAY already will have an irp. + if (pRequest == NULL) + { + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_CurRecvReq != NULL); + pRequest = pSpxConnFile->scf_CurRecvReq; + } + + // Process receive. Here we do not need to worry about + // indicated yet not received data. We just deal with + // servicing the current packet. + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + if ((LookaheadSize == PacketSize) && + (pSpxConnFile->scf_CurRecvSize >= copySize)) + { + bytesCopied = 0; + status = STATUS_SUCCESS; + if (copySize > 0) + { + status = TdiCopyBufferToMdl( + LookaheadBuffer, + iOffset, + copySize, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + } + + DBGPRINT(RECEIVE, DBG, + ("BytesCopied %lx CopySize %lx, Recv Size %lx.%lx\n", + bytesCopied, copySize, + pSpxConnFile->scf_CurRecvSize, + pSpxConnFile->scf_CurRecvOffset)); + } + + // Update current request values and see if this request + // is to be completed. Either zero or fEom. + pSpxConnFile->scf_CurRecvOffset += bytesCopied; + pSpxConnFile->scf_CurRecvSize -= bytesCopied; + +#if DBG + // Decrement indicated data count + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + if (bytesCopied != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= bytesCopied; + } + } +#endif + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + CTELockHandle lockHandleInter; + + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest)= + pSpxConnFile->scf_CurRecvOffset; + + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnData: Completing recv %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + fPktDone = TRUE; + } + else + { + // Need to allocate a ndis receive packet for transfer + // data. + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: %lx.%lx Tranfer data needed!\n", + copySize, pSpxConnFile->scf_CurRecvSize)); + + if (copySize > pSpxConnFile->scf_CurRecvSize) + { + // Partial receive. Buffer and then deal with it. + goto BufferPacket; + } + + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Describe the receive irp's data with a ndis buffer + // descriptor. + if (copySize > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + copySize); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the recv packet + SpxPktRecvRelease(pNdisPkt); + break; + } + + // Queue the buffer into the packet + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + // Don't care about whether this is indicated or not here + // as it is not a buffering packet. + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = + ((fEom ? SPX_RECVPKT_EOM : 0) | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fImmedAck ? SPX_RECVPKT_IMMEDACK : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? + SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + } + + pRecvResd->rr_Request = pRequest; + pRecvResd->rr_ConnFile = pSpxConnFile; + + // reference receive request + REQUEST_INFORMATION(pRequest)++; + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + fLockHeld = FALSE; + + // Call ndis transfer data. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (copySize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + copySize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + } + + break; + + default: + + KeBugCheck(0); + break; + } + + break; + +BufferPacket: + + SpxRecvBufferPkt( + pSpxConnFile, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + pIpxSpxHdr, + copySize, + RemoteAddress, + lockHandle); + + fLockHeld = FALSE; + } + + } while (FALSE); + + // Here we process a received ack. + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // Send an ack if one was asked for. And we are done with this packet. + if (fPktDone) + { + END_PROCESS_PACKET(pSpxConnFile, FALSE, TRUE); + } + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) && fPktDone) + { + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // First copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + PBYTE pData; + ULONG dataLen, copyLen; + BOOLEAN fLockHeld = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numPkts = 0, numDerefs = 0; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Flush %lx\n", + pSpxConnFile, BytesToFlush)); + + while (((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + ((BytesToFlush > 0) || + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0))) + { + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + if ((BytesToFlush == 0) && (dataLen != 0)) + { + // Don't flush this packet. + break; + } + + // Allow for zero data, eom only packets. + copyLen = MIN((dataLen - pRecvResd->rr_DataOffset), BytesToFlush); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Pkt %lx DataLen %lx Copy %lx Flush %lx\n", + pSpxConnFile, pNdisPkt, dataLen, copyLen, BytesToFlush)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + BytesToFlush -= (ULONG)copyLen; + +#if DBG + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } +#endif + + if (pRecvResd->rr_DataOffset == dataLen) + { + // Packet consumed. Free it up. Check if disc happened. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + numDerefs++; + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: !!!ALL INDICATED on %lx.%lx.%lx.%lx\n", + pSpxConnFile, pNdisPkt, pNdisBuffer, pData)); + + INCREMENT_WINDOW(pSpxConnFile); + fWdwOpen = TRUE; + } + else + { + // Took only part of this packet. Get out. + break; + } + } + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxRecvFlushBytes: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN - Receive was queued => TRUE + +--*/ +{ + ULONG indicateFlags; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PREQUEST pRequest; + PIRP pRecvIrp; + ULONG bytesTaken, totalSize, bufSize; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PSPX_RECV_RESD pRecvResd; + NTSTATUS status; + PBYTE lookaheadData; + ULONG lookaheadSize; + BOOLEAN fLockHeld = TRUE, fRecvQueued = FALSE; + + + while ((pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) && + ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) == 0)) + { + // Once a receive is queued we better get out. + CTEAssert(!fRecvQueued); + + // Initialize lookahead values + lookaheadData = NULL; + lookaheadSize = 0; + + // We have no indicated but pending data, and there is some data to + // indicate. Figure out how much. Indicate upto end of message or as + // much as we have. + + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &lookaheadData, &lookaheadSize); + CTEAssert(lookaheadData != NULL); + CTEAssert(lookaheadSize >= 0); + } + + // Allow for zero data, eom only packets. + lookaheadSize -= pRecvResd->rr_DataOffset; + totalSize = lookaheadSize; + lookaheadData += pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((lookaheadSize > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + +#if DBG + CTEAssert (pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + // Go ahead and walk the list of waiting packets. Get total size. + while ((pRecvResd->rr_Next != NULL) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) == 0)) + { + // Check next packet. + pRecvResd = pRecvResd->rr_Next; + +#if DBG + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, NULL, &bufSize); + CTEAssert(bufSize >= 0); + + // Allow for zero data, eom only packets. + totalSize += bufSize; + } + +#if DBG + pSpxConnFile->scf_IndBytes = totalSize; + pSpxConnFile->scf_IndLine = __LINE__; + + // There better not be any pending receives. If so, we have data + // corruption about to happen. + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + indicateFlags = TDI_RECEIVE_NORMAL | TDI_RECEIVE_COPY_LOOKAHEAD; + if ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0) + { + indicateFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + indicateFlags, + lookaheadSize, + totalSize, + &bytesTaken, + lookaheadData, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnIndicatePendingData: IND Flags %lx Size %lx .%lx IND Status %lx\n", + indicateFlags, + totalSize, + bytesTaken, + status)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. Free bytesTaken worth of data packets. + // Sometimes AFD returns STATUS_SUCCESS to just flush the data, so + // we can't assume it took only one packet (since lookahead only + // had that information). + CTEAssert(bytesTaken == totalSize); + SpxRecvFlushBytes(pSpxConnFile, totalSize, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + continue; + } + else if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + return (FALSE); + } + + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + fRecvQueued = TRUE; + } + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return fRecvQueued; +} + + + + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Handle buffered data, complete irp if necessary. Set state to idle + if list becomes empty. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN: More data left to indicate => TRUE + +--*/ +{ + ULONG remainingDataLen, copyLen, bytesCopied; + PREQUEST pRequest; + NTSTATUS status; + BOOLEAN fEom; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + ULONG dataLen; + PBYTE pData; + LIST_ENTRY *p; + BOOLEAN fLockHeld = TRUE, fMoreData = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numDerefs = 0; + + if (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_PROCESS_PKTS); + +ProcessReceives: + + while ((pSpxConnFile->scf_CurRecvReq != NULL) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL)) + { + // A buffering recv packet will have one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + // Allow for zero data, eom only packets. + remainingDataLen = dataLen - pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((remainingDataLen > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + + status = STATUS_SUCCESS; + copyLen = 0; + if (remainingDataLen > 0) + { + copyLen = MIN(remainingDataLen, pSpxConnFile->scf_CurRecvSize); + status = TdiCopyBufferToMdl( + pData, + pRecvResd->rr_DataOffset, + copyLen, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_CurRecvReq), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + copyLen = pSpxConnFile->scf_CurRecvSize; + } + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, pData, copyLen, pRecvResd->rr_State)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + pSpxConnFile->scf_CurRecvSize -= (USHORT)copyLen; + pSpxConnFile->scf_CurRecvOffset += (USHORT)copyLen; + +#if DBG + // If this packet was part of indicated data count, decrement. + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } + } +#endif + + // Set fEom/discState (init to 0) only if all of packet was consumed. + fEom = FALSE; + if (pRecvResd->rr_DataOffset == dataLen) + { + fEom = (BOOLEAN)((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + + // Remember if disconnect needed to happen. If set, this better be + // last packet received. Again, only if entire pkt was consumed. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + // Packet consumed. Free it up. + numDerefs++; + + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + INCREMENT_WINDOW(pSpxConnFile); + + fWdwOpen = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx DEQUEUED\n", + pSpxConnFile, pNdisPkt, pData)); + + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + } + else + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx PARTIAL USE %lx.%lx\n", + pSpxConnFile, pNdisPkt, pRecvResd->rr_DataOffset, dataLen)); + } + + // Don't complete until we are out of all packets and stream mode or... + if (((pSpxConnFile->scf_RecvListHead == NULL) && + SPX_CONN_STREAM(pSpxConnFile)) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + // Done with receive, move to completion or complete depending on + // call level. + pRequest = pSpxConnFile->scf_CurRecvReq; + + // Set status. Complete with error from TdiCopy if so. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + REQUEST_STATUS(pRequest) = status; + + // Ensure we dont overwrite an error status. + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom && + NT_SUCCESS(status)) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + // Dequeue this request, set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Recv %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + // Request is done. Move to receive completion list. There + // could already be previously queued requests in here. + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + } + + CTEAssert((discState == 0) || + (pSpxConnFile->scf_RecvListHead == NULL)); + } + + // Complete any completed receives + while ((p = pSpxConnFile->scf_RecvDoneLinkage.Flink) != + &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + + while (fMoreData) + { + // Bug #21036 + // If there is a receive waiting to be processed, we better not + // indicate data before we finish it. + if (pSpxConnFile->scf_CurRecvReq != NULL) + goto ProcessReceives; + + // If a receive was queued the goto beginning again. + if (SpxRecvIndicatePendingData(pSpxConnFile, LockHandleConn)) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + goto ProcessReceives; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + } + + // Set state + SPX_RECV_SETSTATE( + pSpxConnFile, + (pSpxConnFile->scf_CurRecvReq == NULL) ? + SPX_RECV_IDLE : SPX_RECV_POSTED); + } +#if DBG + else + { + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Already processing pkts %lx\n", + pSpxConnFile)); + } +#endif + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered IDISC %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxreg.c b/private/ntos/tdi/isnp/spx/spxreg.c new file mode 100644 index 000000000..4389dca5f --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxreg.c @@ -0,0 +1,400 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXREG + +// Local functions used to access the registry. +NTSTATUS +SpxInitReadIpxDeviceName( + VOID); + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitGetConfiguration) +#pragma alloc_text(INIT, SpxInitFreeConfiguration) +#pragma alloc_text(INIT, SpxInitGetConfigValue) +#pragma alloc_text(INIT, SpxInitReadIpxDeviceName) +#pragma alloc_text(INIT, SpxInitSetIpxDeviceName) +#endif + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + RegistryPath - The name of ST's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + NTSTATUS Status; + UINT i; + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + + ULONG Zero = 0; + ULONG Two = 2; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Twelve = 12; + ULONG Fifteen = 15; + ULONG Thirty = 30; + ULONG FiveHundred = 500; + ULONG Hex4000 = 0x4000; + ULONG Hex7FFF = 0x7FFF; + ULONG FourK = 4096; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"ConnectionCount", &Five }, + { L"ConnectionTimeout", &Two }, + { L"InitPackets", &Five }, + { L"MaxPackets", &Thirty}, + { L"InitialRetransmissionTime", &FiveHundred}, + { L"KeepAliveCount", &Eight}, + { L"KeepAliveTimeout", &Twelve}, + { L"WindowSize", &Four}, + { L"SpxSocketRangeStart", &Hex4000}, + { L"SpxSocketRangeEnd", &Hex7FFF}, + { L"SpxSocketUniqueness", &Eight}, + { L"MaxPacketSize", &FourK}, + { L"RetransmissionCount", &Eight}, + { L"DisableSpx2", &Zero}, + { L"RouterMtu", &Zero}, + { L"BackCompSpx", &Zero} + }; + + if (!NT_SUCCESS(SpxInitReadIpxDeviceName())) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Allocate memory for the main config structure. + Config = CTEAllocMem (sizeof(CONFIG)); + if (Config == NULL) { + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->cf_DeviceName.Buffer = NULL; + + // SpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + RegistryPathBuffer = (PWSTR)CTEAllocMem(RegistryPath->Length + sizeof(WCHAR)); + + if (RegistryPathBuffer == NULL) { + + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory ( + RegistryPathBuffer, + RegistryPath->Buffer, + RegistryPath->Length); + + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->cf_RegistryPathBuffer = RegistryPathBuffer; + + // Read the per-transport (as opposed to per-binding) + // parameters. + // + // Set up QueryTable to do the following: + // 1) Switch to the Parameters key below SPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // 2-14) Call SpxSetBindingValue for each of the keys we + // care about. + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = SpxInitGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + } + + // 15) Stop + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->cf_RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return Status; + } + + CTEFreeMem (RegistryPathBuffer); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} // SpxInitGetConfiguration + + + + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get free any storage that was allocated + by SpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + CTEFreeMem (Config); + +} // SpxInitFreeConfig + + + + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + DBGPRINT(CONFIG, INFO, + ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + + Config->cf_Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + return STATUS_SUCCESS; + +} // SpxInitGetConfigValue + + + + +NTSTATUS +SpxInitReadIpxDeviceName( + VOID + ) + +{ + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PWSTR Export = L"Export"; + PWSTR IpxRegistryPath = IPX_REG_PATH; + + // Set up QueryTable to do the following: + // + // 1) Call SetIpxDeviceName for the string in "Export" + QueryTable[0].QueryRoutine = SpxInitSetIpxDeviceName; + QueryTable[0].Flags = 0; + QueryTable[0].Name = Export; + QueryTable[0].EntryContext = NULL; + QueryTable[0].DefaultType = REG_NONE; + + // 2) Stop + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxRegistryPath, + QueryTable, + NULL, + NULL); + + return Status; +} + + + + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - NULL. + + EntryContext - NULL. + +Return Value: + + status + +--*/ + +{ + PWSTR fileName; + NTSTATUS status = STATUS_SUCCESS; + + fileName = (PWSTR)CTEAllocMem(ValueLength); + if (fileName != NULL) { + RtlCopyMemory(fileName, ValueData, ValueLength); + RtlInitUnicodeString (&IpxDeviceName, fileName); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + return(status); +} + diff --git a/private/ntos/tdi/isnp/spx/spxsend.c b/private/ntos/tdi/isnp/spx/spxsend.c new file mode 100644 index 000000000..6b856953d --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxsend.c @@ -0,0 +1,262 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.c + +Abstract: + + This module contains code that implements the send engine for the + SPX transport provider. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXSEND + +VOID +SpxSendComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pNdisBuffer; + CTELockHandle lockHandle; + UINT bufCount; + PREQUEST pRequest = NULL; + BOOLEAN completeReq = FALSE, freePkt = FALSE, + orphaned = FALSE, lockHeld = FALSE; + + pSendResd = (PSPX_SEND_RESD)(pNdisPkt->ProtocolReserved); + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: For %lx with status **%lx**\n", + pNdisPkt, NdisStatus)); + } +#endif + + // IPX changes the length set for the first ndis buffer descriptor. + // Change it back to its original value here. + NdisQueryPacket(pNdisPkt, NULL, &bufCount, &pNdisBuffer, NULL); + NdisAdjustBufferLength(pNdisBuffer, IpxMacHdrNeeded + MIN_IPXSPX2_HDRSIZE); + + do + { + pSpxConnFile = pSendResd->sr_ConnFile; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; +#if defined(__PNP) + // + // if IPX gave us a new LocalTarget, use for our next send. + // + // But if we are sending connect requests by iterating over NicIds, + // dont update the local target bcoz that will screw up our iteration + // logic. + // + if ( DEVICE_NETWORK_PATH_NOT_FOUND == NdisStatus + && + !( + SPX_CONN_CONNECTING(pSpxConnFile) && + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) + ) ) { + + pSpxConnFile->scf_LocalTarget = pSendResd->LocalTarget; + + // + // Renegotiate the max packet size if we have an active SPX2 + // session going on and we negotiated the max size originally. + // + if ( SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG) ) { + + // + // this call will get the local max size on this new local target + // from IPX. + // + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr ); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + + } +#endif __PNP + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0); + + // IPX dont own this packet nomore. + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + + // If a send packet has been aborted, then we need to call + // abort send to go ahead and free up this packet, and deref associated + // request, if there is one, potentially completing it. + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) != 0) + { + spxConnAbortSendPkt( + pSpxConnFile, + pSendResd, + SPX_CALL_TDILEVEL, + lockHandle); + + lockHeld = FALSE; + break; + } + + // If there is an associated request, remove reference on it. BUT for a + // sequenced packet only if it has been acked and is waiting for the request + // to be dereferenced. It is already dequeued from queue, just free it up. + if ((((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) && + ((pSendResd->sr_State & SPX_SENDPKT_SEQ) == 0)) || + ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0)) + { + freePkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0); + + pRequest = pSendResd->sr_Request; + CTEAssert(pRequest != NULL); + + DBGPRINT(SEND, DBG, + ("IpxSendComplete: ReqRef before dec %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest))); + + // Deref the request and see if we complete it now. We always have our + // own reference on the request. + // !!! Status should already have been set in request...!!! + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + + completeReq = TRUE; + + // If this is acked already, request is not on list. + // BUG #11626 + if ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) == 0) + { + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + } + } + } + + // Do we destroy this packet? + if ((pSendResd->sr_State & SPX_SENDPKT_DESTROY) != 0) + { + // Remove this packet from the send list in the connection. + DBGPRINT(SEND, INFO, + ("IpxSendComplete: destroy packet...\n")); + + SpxConnDequeueSendPktLock(pSpxConnFile, pNdisPkt); + freePkt = TRUE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (freePkt) + { + DBGPRINT(SEND, INFO, + ("IpxSendComplete: free packet...\n")); + + SpxPktSendRelease(pNdisPkt); + } + + if (completeReq) + { + // If this is a send request, set info to data sent, else it will be + // zero. + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_SEND) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + DBGPRINT(SEND, DBG, + ("IpxSendComplete: complete req %lx.%lx...\n", + REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(pRequest != NULL); + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + SpxCompleteRequest(pRequest); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx DISC Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx.%lx.%lx\n", + pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags, + pSpxConnFile->scf_Flags2)); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; + +} // SpxSendComplete + + + diff --git a/private/ntos/tdi/isnp/spx/spxtimer.c b/private/ntos/tdi/isnp/spx/spxtimer.c new file mode 100644 index 000000000..bdb4e1d7f --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxtimer.c @@ -0,0 +1,637 @@ +/* + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.c + +Abstract: + + This file implements the timer routines used by the stack. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + + +Revision History: + 23 Feb 1993 Initial Version + +Notes: Tab stop: 4 +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXTIMER + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxTimerInit) +#endif + +// Globals for this module +PTIMERLIST spxTimerList = NULL; +PTIMERLIST spxTimerTable[TIMER_HASH_TABLE] = {0}; +PTIMERLIST spxTimerActive = NULL; +CTELock spxTimerLock = {0}; +LARGE_INTEGER spxTimerTick = {0}; +KTIMER spxTimer = {0}; +KDPC spxTimerDpc = {0}; +ULONG spxTimerId = 1; +LONG spxTimerCount = 0; +USHORT spxTimerDispatchCount = 0; +BOOLEAN spxTimerStopped = FALSE; + + +NTSTATUS +SpxTimerInit( + VOID + ) +/*++ + +Routine Description: + + Initialize the timer component for the appletalk stack. + +Arguments: + + +Return Value: + + +--*/ +{ +#if !defined(_PNP_POWER) + BOOLEAN TimerStarted; +#endif !_PNP_POWER + + // Initialize the timer and its associated Dpc. timer will be kicked + // off when we get the first card arrival notification from ipx + KeInitializeTimer(&spxTimer); + CTEInitLock(&spxTimerLock); + KeInitializeDpc(&spxTimerDpc, spxTimerDpcRoutine, NULL); + spxTimerTick = RtlConvertLongToLargeInteger(SPX_TIMER_TICK); +#if !defined(_PNP_POWER) + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); +#endif !_PNP_POWER + return STATUS_SUCCESS; +} + + + + +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG MsTime, // Schedule after this much time + IN PVOID pContext // Context(s) to pass to the routine + ) +/*++ + +Routine Description: + + Insert an event in the timer event list. If the list is empty, then + fire off a timer. The time is specified in ms. We convert to ticks. + Each tick is currently 100ms. It may not be zero or negative. The internal + timer fires at 100ms granularity. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + ULONG DeltaTime; + ULONG Id = 0; + + // Convert to ticks. + DeltaTime = MsTime/SPX_MS_TO_TICKS; + if (DeltaTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + DeltaTime = 1; + } + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + // Negative or Zero DeltaTime is invalid. + CTEAssert (DeltaTime > 0); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Routine %lx, Time %d, Context %lx\n", + Worker, DeltaTime, pContext)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, FATAL, + ("SpxTimerScheduleEvent: Called after Flush !!\n")); + } + + else do + { + pList = SpxBPAllocBlock(BLKID_TIMERLIST); + + if (pList == NULL) + { + break; + } + +#if DBG + pList->tmr_Signature = TMR_SIGNATURE; +#endif + pList->tmr_Cancelled = FALSE; + pList->tmr_Worker = Worker; + pList->tmr_AbsTime = DeltaTime; + pList->tmr_Context = pContext; + + Id = pList->tmr_Id = spxTimerId++; + + // Take care of wrap around + if (spxTimerId == 0) + spxTimerId = 1; + + // Enqueue this handler + spxTimerEnqueue(pList); + } while (FALSE); + + CTEFreeLock(&spxTimerLock, lockHandle); + + return Id; +} + + + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This is called in at DISPATCH_LEVEL when the timer expires. The entry at + the head of the list is decremented and if ZERO unlinked and dispatched. + If the list is non-empty, the timer is fired again. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + BOOLEAN TimerStarted; + ULONG ReEnqueueTime; + CTELockHandle lockHandle; + + pKDpc; pContext; SystemArgument1; SystemArgument2; + +#if defined(_PNP_POWER) + CTEGetLock(&spxTimerLock, &lockHandle); + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + + CTEFreeLock(&spxTimerLock, lockHandle); + return; + } +#else + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + return; + } + + CTEGetLock(&spxTimerLock, &lockHandle); +#endif _PNP_POWER + + SpxTimerCurrentTime ++; // Update our relative time + +#ifdef PROFILING + // This is the only place where this is changed. And it always increases. + SpxStatistics.stat_ElapsedTime = SpxTimerCurrentTime; +#endif + + // We should never be here if we have no work to do + if ((spxTimerList != NULL)) + { + // Careful here. If two guys wanna go off together - let them !! + if (spxTimerList->tmr_RelDelta != 0) + (spxTimerList->tmr_RelDelta)--; + + // Dispatch the entry if it is ready to go + if (spxTimerList->tmr_RelDelta == 0) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + + // Unlink from the list + spxTimerList = pList->tmr_Next; + if (spxTimerList != NULL) + spxTimerList->tmr_Prev = &spxTimerList; + + // Unlink from the hash table now + for (ppList = &spxTimerTable[pList->tmr_Id % TIMER_HASH_TABLE]; + *ppList != NULL; + ppList = &((*ppList)->tmr_Overflow)) + { + CTEAssert(VALID_TMR(*ppList)); + if (*ppList == pList) + { + *ppList = pList->tmr_Overflow; + break; + } + } + + CTEAssert (*ppList == pList->tmr_Overflow); + + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Dispatching %lx\n", + pList->tmr_Worker)); + + spxTimerDispatchCount ++; + spxTimerCount --; + spxTimerActive = pList; + CTEFreeLock(&spxTimerLock, lockHandle); + + // If reenqueue time is 0, do not requeue. If 1, then requeue with + // current value, else use value specified. + ReEnqueueTime = (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Reenequeu time %lx.%lx\n", + ReEnqueueTime, pList->tmr_AbsTime)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerActive = NULL; + spxTimerDispatchCount --; + + if (ReEnqueueTime != TIMER_DONT_REQUEUE) + { + // If this chappie was cancelled while it was running + // and it wants to be re-queued, do it right away. + if (pList->tmr_Cancelled) + { + (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + else + { + if (ReEnqueueTime != TIMER_REQUEUE_CUR_VALUE) + { + pList->tmr_AbsTime = ReEnqueueTime/SPX_MS_TO_TICKS; + if (pList->tmr_AbsTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld\n", + pList->tmr_AbsTime)); + } + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld.%ld\n", + ReEnqueueTime, pList->tmr_AbsTime)); + } + + spxTimerEnqueue(pList); + } + } + else + { + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + } + } + +#if defined(_PNP_POWER) + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + // it is possible that while we were here in Dpc, PNP_ADD_DEVICE + // restarted the timer, so this assert is commented out for PnP +// CTEAssert(!TimerStarted); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +#else + CTEFreeLock(&spxTimerLock, lockHandle); + + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); + } +#endif _PNP_POWER +} + + +VOID +spxTimerEnqueue( + IN PTIMERLIST pListNew + ) +/*++ + +Routine Description: + + Here is a thesis on the code that follows. + + The timer events are maintained as a list which the timer dpc routine + looks at every timer tick. The list is maintained in such a way that only + the head of the list needs to be updated every tick i.e. the entire list + is never scanned. The way this is achieved is by keeping delta times + relative to the previous entry. + + Every timer tick, the relative time at the head of the list is decremented. + When that goes to ZERO, the head of the list is unlinked and dispatched. + + To give an example, we have the following events queued at time slots + X Schedule A after 10 ticks. + X+3 Schedule B after 5 ticks. + X+5 Schedule C after 4 ticks. + X+8 Schedule D after 6 ticks. + + So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and + D at X+14 (X+8+6). + + The above example covers all the situations. + + - NULL List. + - Inserting at head of list. + - Inserting in the middle of the list. + - Appending to the list tail. + + The list will look as follows. + + BEFORE AFTER + ------ ----- + + X Head -->| Head -> A(10) ->| + A(10) + + X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->| + B(5) + + X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->| + C(4) + + X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->| + D(6) + + The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + ULONG DeltaTime = pListNew->tmr_AbsTime; + + // The DeltaTime is adjusted in every pass of the loop to reflect the + // time after the previous entry that the new entry will schedule. + for (ppList = &spxTimerList; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Next) + { + CTEAssert(VALID_TMR(pList)); + if (DeltaTime <= pList->tmr_RelDelta) + { + pList->tmr_RelDelta -= DeltaTime; + break; + } + DeltaTime -= pList->tmr_RelDelta; + } + + + // Link this in the chain + pListNew->tmr_RelDelta = DeltaTime; + pListNew->tmr_Next = pList; + pListNew->tmr_Prev = ppList; + *ppList = pListNew; + if (pList != NULL) + { + pList->tmr_Prev = &pListNew->tmr_Next; + } + + // Now link it in the hash table + pListNew->tmr_Overflow = spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE]; + spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE] = pListNew; + spxTimerCount ++; +} + + + + +VOID +SpxTimerFlushAndStop( + VOID + ) +/*++ + +Routine Description: + + Force all entries in the timer queue to be dispatched immediately. No + more queue'ing of timer routines is permitted after this. The timer + essentially shuts down. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + + CTEAssert (KeGetCurrentIrql() == LOW_LEVEL); + + DBGPRINT(SYSTEM, ERR, + ("SpxTimerFlushAndStop: Entered\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerStopped = TRUE; + + KeCancelTimer(&spxTimer); + + if (spxTimerList != NULL) + { + // Dispatch all entries right away + while (spxTimerList != NULL) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + spxTimerList = pList->tmr_Next; + + DBGPRINT(SYSTEM, INFO, + ("spxTimerFlushAndStop: Dispatching %lx\n", + pList->tmr_Worker)); + + // The timer routines assume they are being called at DISPATCH + // level. This is OK since we are calling with SpinLock held. + + (*pList->tmr_Worker)(pList->tmr_Context, TRUE); + + spxTimerCount --; + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + RtlZeroMemory(spxTimerTable, sizeof(spxTimerTable)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + // Wait for all timer routines to complete + while (spxTimerDispatchCount != 0) + { + SpxSleep(SPX_TIMER_WAIT); + } +} + + + + +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue + ) +/*++ + +Routine Description: + + Cancel a previously scheduled timer event, if it hasn't fired already. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + CTELockHandle lockHandle; + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: Entered for TimerId %ld\n", TimerId)); + + CTEAssert(TimerId != 0); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (ppList = &spxTimerTable[TimerId % TIMER_HASH_TABLE]; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Overflow) + { + CTEAssert(VALID_TMR(pList)); + // If we find it, cancel it + if (pList->tmr_Id == TimerId) + { + // Unlink this from the hash table + *ppList = pList->tmr_Overflow; + + // ... and from the list + if (pList->tmr_Next != NULL) + { + pList->tmr_Next->tmr_RelDelta += pList->tmr_RelDelta; + pList->tmr_Next->tmr_Prev = pList->tmr_Prev; + } + *(pList->tmr_Prev) = pList->tmr_Next; + + spxTimerCount --; + if (ReEnqueue) + spxTimerEnqueue(pList); + else SpxBPFreeBlock(pList, BLKID_TIMERLIST); + break; + } + } + + // If we could not find it in the list, see if it currently running. + // If so mark him to not reschedule itself, only if reenqueue was false. + if (pList == NULL) + { + if ((spxTimerActive != NULL) && + (spxTimerActive->tmr_Id == TimerId) && + !ReEnqueue) + { + spxTimerActive->tmr_Cancelled = TRUE; + } + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: %s for Id %ld\n", + (pList != NULL) ? "Success" : "Failure", TimerId)); + + return (pList != NULL); +} + + + + +#if DBG + +VOID +SpxTimerDumpList( + VOID + ) +{ + PTIMERLIST pList; + ULONG CumTime = 0; + CTELockHandle lockHandle; + + DBGPRINT(DUMP, FATAL, + ("TIMER LIST: (Times are in %dms units\n", 1000)); + DBGPRINT(DUMP, FATAL, + ("\tTimerId Time(Abs) Time(Rel) Routine Address\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (pList = spxTimerList; + pList != NULL; + pList = pList->tmr_Next) + { + CumTime += pList->tmr_RelDelta; + DBGPRINT(DUMP, FATAL, + ("\t% 6lx %5d %5ld %lx\n", + pList->tmr_Id, pList->tmr_AbsTime, CumTime, pList->tmr_Worker)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +} + +#endif diff --git a/private/ntos/tdi/isnp/spx/spxutils.c b/private/ntos/tdi/isnp/spx/spxutils.c new file mode 100644 index 000000000..024a36988 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxutils.c @@ -0,0 +1,484 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.c + +Abstract: + + This contains all utility routines for the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXUTILS + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + UINT length = 0; + + while (*Wstr++) + { + length += sizeof(WCHAR); + } + + return length; +} + + + + +LONG +SpxRandomNumber( + VOID + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LARGE_INTEGER Li; + static LONG seed = 0; + + // Return a positive pseudo-random number; simple linear congruential + // algorithm. ANSI C "rand()" function. + + if (seed == 0) + { + KeQuerySystemTime(&Li); + seed = Li.LowPart; + } + + seed *= (0x41C64E6D + 0x3039); + + return (seed & 0x7FFFFFFF); +} + + + + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType + ) +/*++ + +Routine Description: + + For PROTO_SPX, i'd return a device name from the dll of the form + \Device\IsnSpx\SpxStream (for SOCK_STREAM) or + \Device\IsnSpx\Spx (for SOCK_SEQPKT) + + and for PROTO_SPXII (the more common case we hope, even if + internally we degrade to SPX1 cause of the remote client's + limitations) + \Device\IsnSpx\Stream (for SOCK_STREAM) or + \Device\IsnSpx (for SOCK_SEQPKT) + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING typeString; + + *SocketType = SOCKET2_TYPE_SEQPKT; + + // Check for the socket type + do + { + if (RemainingFileName->Length == 0) + { + break; + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_STREAM; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_SEQPKT; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET2STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET2STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET2_TYPE_STREAM; + break; + } + } + + status = STATUS_NO_SUCH_DEVICE; + + } while (FALSE); + + return(status); +} + + + + +#define ONE_MS_IN_100ns -10000L // 1ms in 100ns units + +VOID +SpxSleep( + IN ULONG TimeInMs + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + KTIMER SleepTimer; + + ASSERT (KeGetCurrentIrql() == LOW_LEVEL); + + KeInitializeTimer(&SleepTimer); + + KeSetTimer(&SleepTimer, + RtlConvertLongToLargeInteger(TimeInMs * ONE_MS_IN_100ns), + NULL); + + KeWaitForSingleObject(&SleepTimer, UserRequest, KernelMode, FALSE, NULL); + return; +} + + + + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + for (i=0;i<TransportAddress->TAAddressCount;i++) + { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} // SpxParseTdiAddress + + + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: runt address\n")); + + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) + { + if (addressName->Address > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + return TRUE; + +} // SpxValidateTdiAddress + + + + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. It will write + less than the full address if the buffer is too short. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + AddressBufferLength - The length of the buffer. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + The number of bytes written into AddressBuffer. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + TA_IPX_ADDRESS TempAddress; + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + } + else + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)&TempAddress; + } + + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)Network; + SpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(SpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + return sizeof(TA_IPX_ADDRESS); + } + else + { + RtlCopyMemory(AddressBuffer, &TempAddress, AddressBufferLength); + return AddressBufferLength; + } + +} // SpxBuildTdiAddress + + + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr + ) +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)pIpxAddr; + RtlCopyMemory( + SpxAddress->Address[0].Address[0].NodeAddress, + pIpxAddr+4, + 6); + + GETSHORT2SHORT( + &SpxAddress->Address[0].Address[0].Socket, + pIpxAddr + 10); + + return; +} + + + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1 + ) +/*++ + +Routine Description: + + +Arguments: + + NewT1 - New value for the RTT in ms. + +Return Value: + + +--*/ +{ + int baseT1, error; + + // + // VAN JACOBSEN Algorithm. From Internetworking with Tcp/ip + // (Comer) book. + // + + error = NewT1 - (pSpxConnFile->scf_AveT1 >> 3); + pSpxConnFile->scf_AveT1 += error; + if (pSpxConnFile->scf_AveT1 <= 0) // Make sure not too small + { + pSpxConnFile->scf_AveT1 = SPX_T1_MIN; + } + + if (error < 0) + error = -error; + + error -= (pSpxConnFile->scf_DevT1 >> 2); + pSpxConnFile->scf_DevT1 += error; + if (pSpxConnFile->scf_DevT1 <= 0) + pSpxConnFile->scf_DevT1 = 1; + + baseT1 = (((pSpxConnFile->scf_AveT1 >> 2) + pSpxConnFile->scf_DevT1) >> 1); + + // If less then min - set it + if (baseT1 < SPX_T1_MIN) + baseT1 = SPX_T1_MIN; + + // Set the new value + DBGPRINT(TDI, DBG, + ("SpxCalculateNewT1: Old value %lx New %lx\n", + pSpxConnFile->scf_BaseT1, baseT1)); + + pSpxConnFile->scf_BaseT1 = baseT1; + + // At the time of restarting the timer,we convert this to a tick value. + return; +} + diff --git a/private/ntos/tdi/isnp/spx/up/makefile b/private/ntos/tdi/isnp/spx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/spx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/spx/up/sources b/private/ntos/tdi/isnp/spx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc |