1792 lines
38 KiB
C
1792 lines
38 KiB
C
/*++
|
||
|
||
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
|
||
|