/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Ipx.c Abstract: This module implements the low level Ipx support routines for the NetWare redirector. Author: Colin Watson [ColinW] 28-Dec-1992 Revision History: --*/ #include "Procs.h" #include "wsnwlink.h" // // Define IPX interfaces that should be in a public header file but aren't // (at least for NT 1.0). For Daytona, include isnkrnl.h. // #define IPX_ID 'M'<<24 | 'I'<<16 | 'P'<<8 | 'X' #define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8)) #define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/ #define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */ #define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */ #define MIPX_LINECHANGE I_MIPX | 310 /* queued until WAN line goes up/down */ #define Dbg (DEBUG_TRACE_IPX) extern BOOLEAN WorkerRunning; // From timer.c extern POBJECT_TYPE *IoFileObjectType; typedef TA_IPX_ADDRESS UNALIGNED *PUTA_IPX_ADDRESS; typedef struct _ADDRESS_INFORMATION { ULONG ActivityCount; TA_IPX_ADDRESS NetworkName; ULONG Unused; // Junk needed to work around streams NWLINK bug. } ADDRESS_INFORMATION, *PADDRESS_INFORMATION; // // Handle difference between NT1.0 and use of ntifs.h // #ifdef IFS #define ATTACHPROCESS(_X) KeAttachProcess(_X); #else #define ATTACHPROCESS(_X) KeAttachProcess(&(_X)->Pcb); #endif NTSTATUS SubmitTdiRequest ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ); NTSTATUS CompletionEvent( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); NTSTATUS QueryAddressInformation( IN PIRP_CONTEXT pIrpContext, IN PNW_TDI_STRUCT pTdiStruct, OUT PADDRESS_INFORMATION AddressInformation ); NTSTATUS QueryProviderInformation( IN PIRP_CONTEXT pIrpContext, IN PNW_TDI_STRUCT pTdiStruct, OUT PTDI_PROVIDER_INFO ProviderInfo ); USHORT GetSocketNumber( IN PIRP_CONTEXT pIrpC, IN PNW_TDI_STRUCT pTdiStruc ); NTSTATUS SetTransportOption( IN PIRP_CONTEXT pIrpC, IN PNW_TDI_STRUCT pTdiStruc, IN ULONG Option ); #ifndef QFE_BUILD NTSTATUS CompletionLineChange( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); #endif #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, IPX_Get_Local_Target ) #pragma alloc_text( PAGE, IPX_Get_Internetwork_Address ) #pragma alloc_text( PAGE, IPX_Get_Interval_Marker ) #pragma alloc_text( PAGE, IPX_Open_Socket ) #pragma alloc_text( PAGE, IPX_Close_Socket ) #pragma alloc_text( PAGE, IpxOpen ) #pragma alloc_text( PAGE, IpxOpenHandle ) #pragma alloc_text( PAGE, BuildIpxAddressEa ) #pragma alloc_text( PAGE, IpxClose ) #pragma alloc_text( PAGE, SetEventHandler ) #pragma alloc_text( PAGE, SubmitTdiRequest ) #pragma alloc_text( PAGE, GetSocketNumber ) #pragma alloc_text( PAGE, GetMaximumPacketSize ) #pragma alloc_text( PAGE, QueryAddressInformation ) #pragma alloc_text( PAGE, QueryProviderInformation ) #pragma alloc_text( PAGE, SetTransportOption ) #pragma alloc_text( PAGE, GetNewRoute ) #ifndef QFE_BUILD #pragma alloc_text( PAGE, SubmitLineChangeRequest ) #pragma alloc_text( PAGE, FspProcessLineChange ) #endif #ifndef QFE_BUILD #pragma alloc_text( PAGE1, CompletionEvent ) #endif #endif #if 0 // Not pageable BuildIpxAddress CompletionLineChange // see ifndef QFE_BUILD above #endif NTSTATUS IPX_Get_Local_Target( IN IPXaddress* RemoteAddress, OUT NodeAddress* LocalTarget, OUT word* Ticks ) /*++ Routine Description: Determine the address in the caller's own network to which to transmit in order to reach the specified machine. This is not required for NT since the IPX transport handles the issue of determining routing between this machine and the remote address. Arguments: RemoteAddress - Supplies the remote computers address NodeAddress - Where to store the intermediate machine address Ticks - Returns the expected number of ticks to reach the remote address Return Value: status of the operation --*/ { PAGED_CODE(); DebugTrace(0, Dbg, "IPX_Get_Local_Target\n", 0); return STATUS_NOT_IMPLEMENTED; } VOID IPX_Get_Internetwork_Address( OUT IPXaddress* LocalAddress ) /*++ Routine Description: Determine the callers full address in a set of interconnected networks. in order to reach the specified machine. This is not required for NT since the IPX transport handles the issue of determining routing between this machine and the remote address. Arguments: LocalAddress - Where to store the local address Return Value: none --*/ { PAGED_CODE(); DebugTrace(0, Dbg, "IPX_Get_Internetwork_Address\n", 0); RtlFillMemory(LocalAddress, sizeof(IPXaddress), 0xff); } word IPX_Get_Interval_Marker( VOID ) /*++ Routine Description: Determine the interval marker in clock ticks. Arguments: Return Value: interval marker --*/ { PAGED_CODE(); DebugTrace(0, Dbg, "IPX_Get_Interval_Marker\n", 0); return 0xff; } NTSTATUS IPX_Open_Socket( IN PIRP_CONTEXT pIrpC, IN PNW_TDI_STRUCT pTdiStruc ) /*++ Routine Description: Open a local socket to be used for a conection to a remote server. Arguments: pIrpC - supplies the irp context for the request creating the socket. pTdiStruc - supplies where to record the handle and both device and file object pointers Return Value: 0 success --*/ { NTSTATUS Status; UCHAR NetworkName[ sizeof( FILE_FULL_EA_INFORMATION )-1 + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof(TA_IPX_ADDRESS)]; static UCHAR LocalNodeAddress[6] = {0,0,0,0,0,0}; PAGED_CODE(); DebugTrace(+1, Dbg, "IPX_Open_Socket %X\n", pTdiStruc->Socket); // // Let the transport decide the network number and node address // if the caller specified socket 0. This will allow the transport // to use whatever local adapters are available to get to the // remote server. // BuildIpxAddressEa( (ULONG)0, LocalNodeAddress, (USHORT)pTdiStruc->Socket, &NetworkName ); Status = IpxOpenHandle( &pTdiStruc->Handle, &pTdiStruc->pDeviceObject, &pTdiStruc->pFileObject, &NetworkName, FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof(TA_IPX_ADDRESS)); if ( !NT_SUCCESS(Status) ) { return( Status ); } if ( pTdiStruc->Socket == 0 ) { // // Find out the socket number assigned by the transport // pTdiStruc->Socket = GetSocketNumber( pIrpC, pTdiStruc ); DebugTrace(0, Dbg, "Assigned socket number %X\n", pTdiStruc->Socket ); } // // Tell transport to accept packet type being set in the connection // information provided with the send datagram. Transport reports // the packet type similarly on receive datagram. // Status = SetTransportOption( pIrpC, pTdiStruc, MIPX_SENDPTYPE ); DebugTrace(-1, Dbg, " %X\n", Status ); return Status; } VOID IPX_Close_Socket( IN PNW_TDI_STRUCT pTdiStruc ) /*++ Routine Description: Terminate a connection over the network. Arguments: pTdiStruc - supplies where to record the handle and both device and file object pointers Return Value: none --*/ { BOOLEAN ProcessAttached = FALSE; PAGED_CODE(); DebugTrace(+1, Dbg, "IPX_Close_Socket %x\n", pTdiStruc->Socket); if ( pTdiStruc->Handle == NULL ) { return; } ObDereferenceObject( pTdiStruc->pFileObject ); // // Attach to the redirector's FSP to allow the handle for the // connection to hang around. // if (PsGetCurrentProcess() != FspProcess) { ATTACHPROCESS(FspProcess); ProcessAttached = TRUE; } ZwClose( pTdiStruc->Handle ); if (ProcessAttached) { // // Now re-attach back to our original process // KeDetachProcess(); } pTdiStruc->Handle = NULL; pTdiStruc->pFileObject = NULL; DebugTrace(-1, Dbg, "IPX_Close_Socket\n", 0); return; } NTSTATUS IpxOpen( VOID ) /*++ Routine Description: Open handle to the Ipx transport. Arguments: none. Return Value: none --*/ { NTSTATUS Status; Status = IpxOpenHandle( &IpxHandle, &pIpxDeviceObject, &pIpxFileObject, NULL, 0 ); DebugTrace(-1, Dbg, "IpxOpen of local node address %X\n", Status); return Status; } NTSTATUS IpxOpenHandle( OUT PHANDLE pHandle, OUT PDEVICE_OBJECT* ppDeviceObject, OUT PFILE_OBJECT* ppFileObject, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength ) /*++ Routine Description: Open handle to the Ipx transport. Arguments: OUT Handle - The handle to the transport if return value is NT_SUCCESS Return Value: none --*/ { OBJECT_ATTRIBUTES AddressAttributes; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; BOOLEAN ProcessAttached = FALSE; PAGED_CODE(); DebugTrace(+1, Dbg, "IpxOpenHandle\n", 0); *pHandle = NULL; if (IpxTransportName.Buffer == NULL) { // // we are being called with an open ipx when transport is not bound // Status = STATUS_CONNECTION_INVALID ; DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status); return Status ; } InitializeObjectAttributes (&AddressAttributes, &IpxTransportName, OBJ_CASE_INSENSITIVE,// Attributes NULL, // RootDirectory NULL); // SecurityDescriptor // // Attach to the redirector's FSP to allow the handle for the // connection to hang around. Normally we create 3 handles at once // so the outer code already has done this to avoid the expensive // attach procedure. // if (PsGetCurrentProcess() != FspProcess) { ATTACHPROCESS(FspProcess); ProcessAttached = TRUE; } Status = ZwCreateFile(pHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &AddressAttributes, // Object Attributes &IoStatusBlock, // Final I/O status block NULL, // Allocation Size FILE_ATTRIBUTE_NORMAL, // Normal attributes FILE_SHARE_READ,// Sharing attributes FILE_OPEN_IF, // Create disposition 0, // CreateOptions EaBuffer,EaLength); if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = IoStatusBlock.Status)) { goto error_cleanup2; } // // Obtain a referenced pointer to the file object. // Status = ObReferenceObjectByHandle ( *pHandle, 0, NULL, KernelMode, ppFileObject, NULL ); if (!NT_SUCCESS(Status)) { goto error_cleanup; } if (ProcessAttached) { // // Now re-attach back to our original process // KeDetachProcess(); } *ppDeviceObject = IoGetRelatedDeviceObject( *ppFileObject ); DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status); return Status; error_cleanup2: if ( *pHandle != NULL ) { ZwClose( *pHandle ); *pHandle = NULL; } error_cleanup: if (ProcessAttached) { // // Now re-attach back to our original process // KeDetachProcess(); } DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status); return Status; } VOID BuildIpxAddress( IN ULONG NetworkAddress, IN PUCHAR NodeAddress, IN USHORT Socket, OUT PTA_IPX_ADDRESS NetworkName ) /*++ Routine Description: This routine builds a TA_NETBIOS_ADDRESS structure in the locations pointed to by NetworkName. All fields are filled out. Arguments: NetworkAddress - Supplies the network number NodeAddress - Supplies the node number Socket - The socket number (in Hi-Lo order) NetworkName - Supplies the structure to place the address Return Value: none. --*/ { // Warn compiler that TAAddressCount may be mis-aligned. PUTA_IPX_ADDRESS UNetworkName = (PUTA_IPX_ADDRESS)NetworkName; DebugTrace(+0, Dbg, "BuildIpxAddress\n", 0); UNetworkName->TAAddressCount = 1; UNetworkName->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; UNetworkName->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IPX; RtlMoveMemory ( UNetworkName->Address[0].Address[0].NodeAddress, NodeAddress, 6); UNetworkName->Address[0].Address[0].NetworkAddress = NetworkAddress; UNetworkName->Address[0].Address[0].Socket = Socket; } /* TdiBuildIpxAddress */ VOID BuildIpxAddressEa ( IN ULONG NetworkAddress, IN PUCHAR NodeAddress, IN USHORT Socket, OUT PVOID NetworkName ) /*++ Routine Description: Builds an EA describing a Netbios address in the buffer supplied by the user. Arguments: NetworkAddress - Supplies the network number NodeAddress - Supplies the node number Socket - NetworkName - The Ea structure that describes the input parameters. Return Value: An informative error code if something goes wrong. STATUS_SUCCESS if the ea is built properly. --*/ { PFILE_FULL_EA_INFORMATION EaBuffer; PTA_IPX_ADDRESS TAAddress; ULONG Length; DebugTrace(+0, Dbg, "BuildIpxAddressEa\n", 0); Length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + sizeof (TA_IPX_ADDRESS); EaBuffer = (PFILE_FULL_EA_INFORMATION)NetworkName; EaBuffer->NextEntryOffset = 0; EaBuffer->Flags = 0; EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; EaBuffer->EaValueLength = sizeof (TA_IPX_ADDRESS); RtlCopyMemory ( EaBuffer->EaName, TdiTransportAddress, EaBuffer->EaNameLength + 1); TAAddress = (PTA_IPX_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; BuildIpxAddress( NetworkAddress, NodeAddress, Socket, TAAddress); return; } VOID IpxClose( VOID ) /*++ Routine Description: Close handle to the Ipx transport. Arguments: none Return Value: none --*/ { PAGED_CODE(); DebugTrace(+1, Dbg, "IpxClose...\n", 0); if ( pIpxFileObject ) { ObDereferenceObject( pIpxFileObject ); pIpxFileObject = NULL; } // if ( pIpxDeviceObject ) { // ObDereferenceObject( pIpxDeviceObject ); // pIpxDeviceObject = NULL; // } pIpxDeviceObject = NULL; if ( IpxTransportName.Buffer != NULL ) { FREE_POOL( IpxTransportName.Buffer ); IpxTransportName.Buffer = NULL; } if (IpxHandle) { // // Attach to the redirector's FSP to allow the handle for the // connection to hang around. // if (PsGetCurrentProcess() != FspProcess) { ATTACHPROCESS(FspProcess); ZwClose( IpxHandle ); KeDetachProcess(); } else { ZwClose( IpxHandle ); } IpxHandle = NULL; } DebugTrace(-1, Dbg, "IpxClose\n", 0); } NTSTATUS SetEventHandler ( IN PIRP_CONTEXT pIrpC, IN PNW_TDI_STRUCT pTdiStruc, IN ULONG EventType, IN PVOID pEventHandler, IN PVOID pContext ) /*++ Routine Description: This routine registers an event handler with a TDI transport provider. Arguments: pIrpC - supplies an Irp among other things. pTdiStruc - supplies the handle and both device and file object pointers to the transport. IN ULONG EventType, - Supplies the type of event. IN PVOID pEventHandler - Supplies the event handler. IN PVOID pContext - Supplies the context to be supplied to the event handler. Return Value: NTSTATUS - Final status of the set event operation --*/ { NTSTATUS Status; PAGED_CODE(); TdiBuildSetEventHandler(pIrpC->pOriginalIrp, pTdiStruc->pDeviceObject, pTdiStruc->pFileObject, NULL, NULL, EventType, pEventHandler, pContext); Status = SubmitTdiRequest(pTdiStruc->pDeviceObject, pIrpC->pOriginalIrp); return Status; } NTSTATUS SubmitTdiRequest ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This routine submits a request to TDI and waits for it to complete. Arguments: IN PDevice_OBJECT DeviceObject - Connection or Address handle for TDI request IN PIRP Irp - TDI request to submit. Return Value: NTSTATUS - Final status of request. --*/ { NTSTATUS Status; KEVENT Event; PAGED_CODE(); DebugTrace(+1, Dbg, "SubmitTdiRequest\n", 0); KeInitializeEvent (&Event, NotificationEvent, FALSE); IoSetCompletionRoutine(pIrp, CompletionEvent, &Event, TRUE, TRUE, TRUE); // // Submit the request // Status = IoCallDriver(pDeviceObject, pIrp); // // If it failed immediately, return now, otherwise wait. // if (!NT_SUCCESS(Status)) { DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status); return Status; } if (Status == STATUS_PENDING) { DebugTrace(+0, Dbg, "Waiting....\n", 0); Status = KeWaitForSingleObject(&Event, // Object to wait on. Executive, // Reason for waiting KernelMode, // Processor mode FALSE, // Alertable NULL); // Timeout if (!NT_SUCCESS(Status)) { DebugTrace(-1, Dbg, "SubmitTdiRequest could not wait %X\n", Status); return Status; } Status = pIrp->IoStatus.Status; } DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status); return(Status); } NTSTATUS CompletionEvent( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: This routine does not complete the Irp. It is used to signal to a synchronous part of the driver that it can proceed. Arguments: DeviceObject - unused. Irp - Supplies Irp that the transport has finished processing. Context - Supplies the event associated with the Irp. Return Value: The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops processing Irp stack locations at this point. --*/ { DebugTrace( 0, Dbg, "CompletionEvent\n", 0 ); KeSetEvent((PKEVENT )Context, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; UNREFERENCED_PARAMETER( DeviceObject ); UNREFERENCED_PARAMETER( Irp ); } USHORT GetSocketNumber( IN PIRP_CONTEXT pIrpC, IN PNW_TDI_STRUCT pTdiStruc ) /*++ Routine Description: Use a TDI_ACTION to set the Option. Arguments: pIrpC - supplies an Irp among other things. pTdiStruc - supplies the handle and both device and file object pointers to the transport. Option - supplies the option to set. Return Value: 0 failed otherwise the socket number. --*/ { ADDRESS_INFORMATION AddressInfo; NTSTATUS Status; USHORT SocketNumber; PAGED_CODE(); Status = QueryAddressInformation( pIrpC, pTdiStruc, &AddressInfo ); if ( !NT_SUCCESS( Status ) ) { SocketNumber = 0; } else { SocketNumber = AddressInfo.NetworkName.Address[0].Address[0].Socket; RtlCopyMemory( &OurAddress, &AddressInfo.NetworkName.Address[0].Address[0], sizeof(TDI_ADDRESS_IPX)); } return( SocketNumber ); } NTSTATUS GetMaximumPacketSize( IN PIRP_CONTEXT pIrpContext, IN PNW_TDI_STRUCT pTdiStruct, OUT PULONG pMaximumPacketSize ) /*++ Routine Description: Query the maximum packet size for this network. Arguments: pIrpContext - supplies an Irp among other things. pTdiStruct - supplies the handle and both device and file object pointers to the transport. pMaximumPacketSize - Returns the maximum packet size for the network. Return Value: The status of the query. --*/ { TDI_PROVIDER_INFO ProviderInfo; NTSTATUS Status; PAGED_CODE(); Status = QueryProviderInformation( pIrpContext, pTdiStruct, &ProviderInfo ); if ( NT_SUCCESS( Status ) ) { *pMaximumPacketSize = ProviderInfo.MaxDatagramSize; } return( Status ); } NTSTATUS QueryAddressInformation( PIRP_CONTEXT pIrpContext, IN PNW_TDI_STRUCT pTdiStruct, PADDRESS_INFORMATION AddressInformation ) { NTSTATUS Status; PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress; PMDL Mdl; PAGED_CODE(); Mdl = ALLOCATE_MDL( AddressInformation, sizeof( *AddressInformation ), FALSE, // Secondary Buffer FALSE, // Charge Quota NULL); if ( Mdl == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } try { MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess ); } except( EXCEPTION_EXECUTE_HANDLER ) { FREE_MDL( Mdl ); return GetExceptionCode(); } TdiBuildQueryInformation( pIrpContext->pOriginalIrp, pTdiStruct->pDeviceObject, pTdiStruct->pFileObject, CompletionEvent, NULL, TDI_QUERY_ADDRESS_INFO, Mdl); Status = SubmitTdiRequest( pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp); pIrpContext->pOriginalIrp->MdlAddress = MdlSave; MmUnlockPages( Mdl ); FREE_MDL( Mdl ); return( Status ); } NTSTATUS QueryProviderInformation( IN PIRP_CONTEXT pIrpContext, IN PNW_TDI_STRUCT pTdiStruct, PTDI_PROVIDER_INFO ProviderInfo ) { NTSTATUS Status; PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress; PMDL Mdl; PAGED_CODE(); Mdl = ALLOCATE_MDL( ProviderInfo, sizeof( *ProviderInfo ), FALSE, // Secondary Buffer FALSE, // Charge Quota NULL); if ( Mdl == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } try { MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess ); } except( EXCEPTION_EXECUTE_HANDLER ) { FREE_MDL( Mdl ); return GetExceptionCode(); } TdiBuildQueryInformation( pIrpContext->pOriginalIrp, pTdiStruct->pDeviceObject, pTdiStruct->pFileObject, CompletionEvent, NULL, TDI_QUERY_PROVIDER_INFO, Mdl); Status = SubmitTdiRequest(pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp); pIrpContext->pOriginalIrp->MdlAddress = MdlSave; MmUnlockPages( Mdl ); FREE_MDL( Mdl ); return( Status ); } NTSTATUS SetTransportOption( IN PIRP_CONTEXT pIrpC, IN PNW_TDI_STRUCT pTdiStruc, IN ULONG Option ) /*++ Routine Description: Use a TDI_ACTION to set the Option. Arguments: pIrpC - supplies an Irp among other things. pTdiStruc - supplies the handle and both device and file object pointers to the transport. Option - supplies the option to set. Return Value: 0 success --*/ { static struct { TDI_ACTION_HEADER Header; BOOLEAN DatagramOption; ULONG BufferLength; ULONG Option; } SetPacketType = { IPX_ID, 0, // ActionCode 0, // Reserved TRUE, // DatagramOption sizeof(ULONG) // BufferLength }; KEVENT Event; NTSTATUS Status; PIRP pIrp = pIrpC->pOriginalIrp; // // Save the original MDL and System buffer address, to restore // after the IRP completes. // // We use both the MDL and SystemBuffer because NWLINK assumes that // we are using SystemBuffer even though we are supposed to use the // MDL to pass a pointer to the action buffer. // PMDL MdlSave = pIrp->MdlAddress; PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer; PMDL Mdl; PAGED_CODE(); Mdl = ALLOCATE_MDL( &SetPacketType, sizeof( SetPacketType ), FALSE, // Secondary Buffer FALSE, // Charge Quota NULL ); if ( Mdl == NULL ) { IPX_Close_Socket( pTdiStruc ); return STATUS_INSUFFICIENT_RESOURCES; } SetPacketType.Option = Option; try { MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess ); } except( EXCEPTION_EXECUTE_HANDLER ) { FREE_MDL( Mdl ); return GetExceptionCode(); } KeInitializeEvent ( &Event, SynchronizationEvent, FALSE); TdiBuildAction( pIrp, pTdiStruc->pDeviceObject, pTdiStruc->pFileObject, CompletionEvent, &Event, Mdl ); // // Set up the system buffer for NWLINK. // pIrp->AssociatedIrp.SystemBuffer = &SetPacketType; Status = IoCallDriver (pTdiStruc->pDeviceObject, pIrp); if ( Status == STATUS_PENDING ) { Status = KeWaitForSingleObject ( &Event, Executive, KernelMode, FALSE, NULL ); if ( NT_SUCCESS( Status ) ) { Status = pIrp->IoStatus.Status; } } // // Now restore the system buffer and MDL address in the IRP // pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave; pIrp->MdlAddress = MdlSave; MmUnlockPages( Mdl ); FREE_MDL( Mdl ); return Status; } NTSTATUS GetNewRoute( IN PIRP_CONTEXT pIrpContext ) /*++ Routine Description: Use a TDI_ACTION to get a new route. Arguments: pIrpContext - Supplies IRP context information. Return Value: The status of the operation. --*/ { struct { TDI_ACTION_HEADER Header; BOOLEAN DatagramOption; ULONG BufferLength; ULONG Option; ULONG info_netnum; USHORT info_hopcount; USHORT info_netdelay; int info_cardnum; UCHAR info_router[6]; } ReRipRequest = { IPX_ID, 0, // ActionCode 0, // Reserved TRUE, // DatagramOption 24 // Buffer length (not including header) }; KEVENT Event; NTSTATUS Status; PIRP pIrp = pIrpContext->pOriginalIrp; // // Save the original MDL and System buffer address, to restore // after the IRP completes. // // We use both the MDL and SystemBuffer because NWLINK assumes that // we are using SystemBuffer even though we are supposed to use the // MDL to pass a pointer to the action buffer. // PMDL MdlSave = pIrp->MdlAddress; PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer; PMDL Mdl; PAGED_CODE(); Mdl = ALLOCATE_MDL( &ReRipRequest, sizeof( ReRipRequest ), FALSE, // Secondary Buffer FALSE, // Charge Quota NULL ); if ( Mdl == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } ReRipRequest.Option = MIPX_RERIPNETNUM; ReRipRequest.info_netnum = pIrpContext->pNpScb->ServerAddress.Net; try { MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess ); } except( EXCEPTION_EXECUTE_HANDLER ) { FREE_MDL( Mdl ); return GetExceptionCode(); } KeInitializeEvent ( &Event, SynchronizationEvent, FALSE); TdiBuildAction( pIrp, pIrpContext->pNpScb->Server.pDeviceObject, pIrpContext->pNpScb->Server.pFileObject, CompletionEvent, &Event, Mdl ); // // Set up the system buffer for NWLINK. // pIrp->AssociatedIrp.SystemBuffer = &ReRipRequest; Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp); if ( Status == STATUS_PENDING ) { Status = KeWaitForSingleObject ( &Event, Executive, KernelMode, FALSE, NULL ); if ( NT_SUCCESS( Status ) ) { Status = pIrp->IoStatus.Status; } } // // Now restore the system buffer and MDL address in the IRP // pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave; pIrp->MdlAddress = MdlSave; MmUnlockPages( Mdl ); FREE_MDL( Mdl ); return Status; } NTSTATUS GetTickCount( IN PIRP_CONTEXT pIrpContext, OUT PUSHORT TickCount ) /*++ Routine Description: Use a TDI_ACTION to get a new route. Arguments: pIrpContext - Supplies IRP context information. Return Value: The status of the operation. --*/ { struct { TDI_ACTION_HEADER Header; BOOLEAN DatagramOption; ULONG BufferLength; ULONG Option; IPX_NETNUM_DATA NetNumData; } GetTickCountInput = { IPX_ID, 0, // ActionCode 0, // Reserved TRUE, // DatagramOption sizeof( IPX_NETNUM_DATA) + 2 * sizeof( ULONG ) }; struct _GET_TICK_COUNT_OUTPUT { ULONG Option; IPX_NETNUM_DATA NetNumData; }; struct _GET_TICK_COUNT_OUTPUT *GetTickCountOutput; KEVENT Event; NTSTATUS Status; PIRP pIrp = pIrpContext->pOriginalIrp; // // Save the original MDL and System buffer address, to restore // after the IRP completes. // // We use both the MDL and SystemBuffer because NWLINK assumes that // we are using SystemBuffer even though we are supposed to use the // MDL to pass a pointer to the action buffer. // PMDL MdlSave = pIrp->MdlAddress; PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer; PMDL Mdl; PAGED_CODE(); Mdl = ALLOCATE_MDL( &GetTickCountInput, sizeof( GetTickCountInput ), FALSE, // Secondary Buffer FALSE, // Charge Quota NULL ); if ( Mdl == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } GetTickCountInput.Option = MIPX_GETNETINFO; *(PULONG)GetTickCountInput.NetNumData.netnum = pIrpContext->pNpScb->ServerAddress.Net; try { MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess ); } except( EXCEPTION_EXECUTE_HANDLER ) { FREE_MDL( Mdl ); return GetExceptionCode(); } KeInitializeEvent ( &Event, SynchronizationEvent, FALSE); TdiBuildAction( pIrp, pIrpContext->pNpScb->Server.pDeviceObject, pIrpContext->pNpScb->Server.pFileObject, CompletionEvent, &Event, Mdl ); // // Set up the system buffer for NWLINK. // pIrp->AssociatedIrp.SystemBuffer = &GetTickCountInput; Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp); if ( Status == STATUS_PENDING ) { Status = KeWaitForSingleObject ( &Event, Executive, KernelMode, FALSE, NULL ); if ( NT_SUCCESS( Status ) ) { Status = pIrp->IoStatus.Status; } } DebugTrace( +0, Dbg, "Get Tick Count, net= %x\n", pIrpContext->pNpScb->ServerAddress.Net ); if ( NT_SUCCESS( Status ) ) { // // HACK-o-rama. Streams and non-streams IPX have different output // buffer formats. For now accept both. // if ( IpxTransportName.Length == 32 ) { // ISNIPX format *TickCount = GetTickCountInput.NetNumData.netdelay; } else { // NWLINK format GetTickCountOutput = (struct _GET_TICK_COUNT_OUTPUT *)&GetTickCountInput; *TickCount = GetTickCountOutput->NetNumData.netdelay; } DebugTrace( +0, Dbg, "Tick Count = %d\n", *TickCount ); // // Don't let the transport have us wait forever. // if ( *TickCount > 600 ) { ASSERT( FALSE ); } } else { DebugTrace( +0, Dbg, "GetTickCount failed, status = %X\n", Status ); } // // Now restore the system buffer and MDL address in the IRP // pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave; pIrp->MdlAddress = MdlSave; MmUnlockPages( Mdl ); FREE_MDL( Mdl ); return Status; } #ifndef QFE_BUILD static PIRP LineChangeIrp = NULL; NTSTATUS SubmitLineChangeRequest( VOID ) /*++ Routine Description: Use a TDI_ACTION to get a new route. Arguments: pIrpContext - Supplies IRP context information. Return Value: The status of the operation. --*/ { NTSTATUS Status; struct _LINE_CHANGE { TDI_ACTION_HEADER Header; BOOLEAN DatagramOption; ULONG BufferLength; ULONG Option; } *LineChangeInput; PIRP pIrp; PMDL Mdl; PAGED_CODE(); LineChangeInput = ALLOCATE_POOL( NonPagedPool, sizeof( struct _LINE_CHANGE ) ); if (!LineChangeInput) { return STATUS_INSUFFICIENT_RESOURCES; } // // Complete initialization of the request, and allocate and build an // MDL for the request input buffer. // LineChangeInput->Header.TransportId = IPX_ID; LineChangeInput->Header.ActionCode = 0; LineChangeInput->Header.Reserved = 0; LineChangeInput->DatagramOption = 2; LineChangeInput->BufferLength = 2 * sizeof( ULONG ); LineChangeInput->Option = MIPX_LINECHANGE; Mdl = ALLOCATE_MDL( LineChangeInput, sizeof( *LineChangeInput ), FALSE, // Secondary Buffer FALSE, // Charge Quota NULL ); if ( Mdl == NULL ) { FREE_POOL( LineChangeInput ); return STATUS_INSUFFICIENT_RESOURCES; } pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE ); if ( pIrp == NULL ) { FREE_POOL( LineChangeInput ); FREE_MDL( Mdl ); return STATUS_INSUFFICIENT_RESOURCES; } // // Remember this IRP so that we can cancel it. // LineChangeIrp = pIrp; MmBuildMdlForNonPagedPool( Mdl ); // // Build and submit a TDI request packet. // TdiBuildAction( pIrp, pIpxDeviceObject, pIpxFileObject, CompletionLineChange, NULL, Mdl ); Status = IoCallDriver ( pIpxDeviceObject, pIrp ); return( Status ); } NTSTATUS CompletionLineChange( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: This routine is called when the transport completes a line change IRP. This means that we have switched nets, and that we should mark all of our servers disconnected. Arguments: DeviceObject - unused. Irp - Supplies Irp that the transport has finished processing. Context - unused. Return Value: The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops processing Irp stack locations at this point. --*/ { PMDL Mdl; PWORK_QUEUE_ITEM WorkQueueItem; DebugTrace( 0, Dbg, "CompletionLineChange\n", 0 ); Mdl = Irp->MdlAddress; if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) { FREE_POOL( Mdl->MappedSystemVa ); FREE_MDL( Mdl ); FREE_IRP( Irp ); return( STATUS_MORE_PROCESSING_REQUIRED ); } // // If the scavenger is running, simply make a note that // we need to do this when it is finished. // KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock ); if ( WorkerRunning ) { if ( ( DelayedProcessLineChange != FALSE ) && ( DelayedLineChangeIrp != NULL ) ) { // // We've already got a line change. Dump this one. // KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock ); DebugTrace( 0, Dbg, "Dumping an additional line change request.\n", 0 ); FREE_POOL( Mdl->MappedSystemVa ); FREE_MDL( Mdl ); FREE_IRP( Irp ); return( STATUS_MORE_PROCESSING_REQUIRED ); } else { DebugTrace( 0, Dbg, "Delaying a line change request.\n", 0 ); DelayedProcessLineChange = TRUE; DelayedLineChangeIrp = Irp; KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock ); return STATUS_MORE_PROCESSING_REQUIRED; } } else { // // Don't let the scavenger start up while we're running. // WorkerRunning = TRUE; KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock ); } WorkQueueItem = ALLOCATE_POOL( NonPagedPool, sizeof( *WorkQueueItem ) ); if ( WorkQueueItem == NULL ) { FREE_POOL( Mdl->MappedSystemVa ); FREE_MDL( Mdl ); FREE_IRP( Irp ); return( STATUS_MORE_PROCESSING_REQUIRED ); } // // Use the user buffer field as a convenient place to remember where // the address of the WorkQueueItem. We can get away with this since // we don't let this IRP complete. // Irp->UserBuffer = WorkQueueItem; // // Process the line change in the FSP. // ExInitializeWorkItem( WorkQueueItem, FspProcessLineChange, Irp ); ExQueueWorkItem( WorkQueueItem, DelayedWorkQueue ); return( STATUS_MORE_PROCESSING_REQUIRED ); } VOID FspProcessLineChange( IN PVOID Context ) { PIRP Irp; ULONG ActiveHandles; NwReferenceUnlockableCodeSection(); Irp = (PIRP)Context; // // Free the work queue item // FREE_POOL( Irp->UserBuffer ); Irp->UserBuffer = NULL; // // Invalid all remote handles // ActiveHandles = NwInvalidateAllHandles(NULL, NULL); // // Now that we're done walking all the servers, it's safe // to let the scavenger run again. // WorkerRunning = FALSE; // // Resubmit the IRP // TdiBuildAction( Irp, pIpxDeviceObject, pIpxFileObject, CompletionLineChange, NULL, Irp->MdlAddress ); IoCallDriver ( pIpxDeviceObject, Irp ); NwDereferenceUnlockableCodeSection (); return; } #endif