1364 lines
29 KiB
C
1364 lines
29 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1995 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
net\ip\ipinip\driver.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
IP in IP driver shell
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Gurdeep Singh Pall 8/2/95 Created
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#define __FILE_SIG__ DRIVER_SIG
|
||
|
|
||
|
#include "inc.h"
|
||
|
|
||
|
#pragma alloc_text(INIT, DriverEntry)
|
||
|
|
||
|
NTSTATUS
|
||
|
DriverEntry(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PUNICODE_STRING RegistryPath
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
Installable driver initialization entry point.
|
||
|
This entry point is called directly by the I/O system and must be named
|
||
|
"Driver Entry"
|
||
|
The function is discardable since it is only called once
|
||
|
On checked builds we read some values from registry and initialize the
|
||
|
debugging
|
||
|
We create a DEVICE_OBJECT for ourselves to field the IOCTLs, create
|
||
|
a DOS name for the device and initialize some events and spinlocks
|
||
|
|
||
|
Locks
|
||
|
|
||
|
None
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
DriverObject Pointer to I/O subsystem created driver object
|
||
|
RegistryPath Points to driver key in HKLM\CCS\Services...
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
STATUS_SUCCESS if everything went as planned or some status code from
|
||
|
ntstatus.h
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
NTSTATUS nStatus;
|
||
|
PDEVICE_OBJECT pDeviceObject;
|
||
|
UNICODE_STRING usDeviceName;
|
||
|
DWORD dwVal, i;
|
||
|
HANDLE hRegKey;
|
||
|
|
||
|
WCHAR ParametersRegistryPath[] =
|
||
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\IpInIp\\Parameters";
|
||
|
|
||
|
RtInitializeDebug();
|
||
|
|
||
|
//
|
||
|
// Read the registry for parameters
|
||
|
//
|
||
|
|
||
|
nStatus = OpenRegKey(&hRegKey,
|
||
|
ParametersRegistryPath);
|
||
|
|
||
|
if(nStatus is STATUS_SUCCESS)
|
||
|
{
|
||
|
|
||
|
#if RT_TRACE_DEBUG
|
||
|
|
||
|
nStatus = GetRegDWORDValue(hRegKey,
|
||
|
L"DebugLevel",
|
||
|
&dwVal);
|
||
|
|
||
|
if(nStatus is STATUS_SUCCESS)
|
||
|
{
|
||
|
g_byDebugLevel = (BYTE)dwVal;
|
||
|
}
|
||
|
|
||
|
nStatus = GetRegDWORDValue(hRegKey,
|
||
|
L"DebugComp",
|
||
|
&dwVal);
|
||
|
|
||
|
if(nStatus is STATUS_SUCCESS)
|
||
|
{
|
||
|
g_fDebugComp = dwVal;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if DBG
|
||
|
nStatus = GetRegDWORDValue(hRegKey,
|
||
|
L"DebugBreak",
|
||
|
&dwVal);
|
||
|
|
||
|
if((nStatus is STATUS_SUCCESS) and
|
||
|
(dwVal is 1))
|
||
|
{
|
||
|
DbgBreakPoint();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
ZwClose(hRegKey);
|
||
|
}
|
||
|
|
||
|
TraceEnter(GLOBAL, "DriverEntry");
|
||
|
|
||
|
//
|
||
|
// Initialize some globals
|
||
|
//
|
||
|
|
||
|
g_dwDriverState = DRIVER_STOPPED;
|
||
|
g_ulNumThreads = 0;
|
||
|
g_ulNumOpens = 0;
|
||
|
|
||
|
g_hIpRegistration = NULL;
|
||
|
|
||
|
//
|
||
|
// Create the device
|
||
|
//
|
||
|
|
||
|
RtlInitUnicodeString(&usDeviceName,
|
||
|
DD_IPINIP_DEVICE_NAME);
|
||
|
|
||
|
nStatus = IoCreateDevice(DriverObject,
|
||
|
0,
|
||
|
&usDeviceName,
|
||
|
FILE_DEVICE_NETWORK,
|
||
|
FILE_DEVICE_SECURE_OPEN,
|
||
|
FALSE,
|
||
|
&pDeviceObject);
|
||
|
|
||
|
if (!NT_SUCCESS(nStatus))
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("DriverEntry: Cant create device object %ws, status %lx.\n",
|
||
|
DD_IPINIP_DEVICE_NAME,
|
||
|
nStatus));
|
||
|
|
||
|
TraceLeave(GLOBAL, "DriverEntry");
|
||
|
|
||
|
return nStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the driver object
|
||
|
//
|
||
|
|
||
|
DriverObject->DriverUnload = IpIpUnload;
|
||
|
DriverObject->FastIoDispatch = NULL;
|
||
|
|
||
|
for(i=0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
|
||
|
{
|
||
|
DriverObject->MajorFunction[i] = IpIpDispatch;
|
||
|
}
|
||
|
|
||
|
|
||
|
if(!SetupExternalName(&usDeviceName,
|
||
|
WIN32_IPINIP_SYMBOLIC_LINK,
|
||
|
TRUE))
|
||
|
{
|
||
|
Trace(GLOBAL,ERROR,
|
||
|
("DriverEntry: Win32 device name could not be created\n"));
|
||
|
|
||
|
IoDeleteDevice(pDeviceObject);
|
||
|
|
||
|
TraceLeave(GLOBAL, "DriverEntry");
|
||
|
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Lock and event needed to keep track of threads in the driver
|
||
|
//
|
||
|
|
||
|
RtInitializeSpinLock(&g_rlStateLock);
|
||
|
|
||
|
KeInitializeEvent(&(g_keStateEvent),
|
||
|
SynchronizationEvent,
|
||
|
FALSE);
|
||
|
|
||
|
//
|
||
|
// Many threads may be waiting for us to start so this needs to be a
|
||
|
// NotificationEvent
|
||
|
//
|
||
|
|
||
|
KeInitializeEvent(&(g_keStartEvent),
|
||
|
NotificationEvent,
|
||
|
FALSE);
|
||
|
|
||
|
InitRwLock(&g_rwlTunnelLock);
|
||
|
|
||
|
InitializeListHead(&g_leTunnelList);
|
||
|
|
||
|
InitializeListHead(&g_lePendingMessageList);
|
||
|
InitializeListHead(&g_lePendingIrpList);
|
||
|
|
||
|
//
|
||
|
// Register with IP
|
||
|
//
|
||
|
|
||
|
nStatus = RegisterWithIp();
|
||
|
|
||
|
if(nStatus isnot STATUS_SUCCESS)
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("DriverEntry: RegisterWithIP failed\n"));
|
||
|
|
||
|
IoDeleteDevice(pDeviceObject);
|
||
|
|
||
|
TraceLeave(GLOBAL, "InitializeDriver");
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
g_pIpIpDevice = pDeviceObject;
|
||
|
|
||
|
TraceLeave(GLOBAL, "DriverEntry");
|
||
|
|
||
|
return nStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#pragma alloc_text(PAGE, IpIpDispatch)
|
||
|
|
||
|
NTSTATUS
|
||
|
IpIpDispatch(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
The functions which handles the IRPs sent to the driver
|
||
|
|
||
|
Locks
|
||
|
|
||
|
This code is PAGEABLE so can not acquire locks
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
STATUS_SUCCESS
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PIO_STACK_LOCATION irpStack;
|
||
|
ULONG ulInputBuffLen;
|
||
|
ULONG ulOutputBuffLen;
|
||
|
ULONG ioControlCode;
|
||
|
NTSTATUS nStatus;
|
||
|
KIRQL kiIrql;
|
||
|
LARGE_INTEGER liTimeOut;
|
||
|
BOOLEAN bEnter;
|
||
|
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
TraceEnter(GLOBAL, "IpIpDispatch");
|
||
|
|
||
|
Irp->IoStatus.Information = 0;
|
||
|
|
||
|
//
|
||
|
// Get a pointer to the current location in the Irp. This is where
|
||
|
// the function codes and parameters are located.
|
||
|
//
|
||
|
|
||
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
|
||
|
//
|
||
|
// Get the pointer to the input/output buffer and it's length
|
||
|
//
|
||
|
|
||
|
ulInputBuffLen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
||
|
ulOutputBuffLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
|
||
|
switch (irpStack->MajorFunction)
|
||
|
{
|
||
|
case IRP_MJ_CREATE:
|
||
|
{
|
||
|
|
||
|
Trace(GLOBAL, TRACE,
|
||
|
("IpIpDispatch: IRP_MJ_CREATE\n"));
|
||
|
|
||
|
//
|
||
|
// We start the driver when the first CreateFile is done
|
||
|
// But we need to serialize the Creates
|
||
|
//
|
||
|
|
||
|
nStatus = StartDriver();
|
||
|
|
||
|
if(nStatus is STATUS_PENDING)
|
||
|
{
|
||
|
//
|
||
|
// Means someone is trying to start the driver
|
||
|
// We wait for some time (since we are at PASSIVE)
|
||
|
//
|
||
|
|
||
|
liTimeOut.QuadPart = START_TIMEOUT;
|
||
|
|
||
|
nStatus = KeWaitForSingleObject(&g_keStartEvent,
|
||
|
UserRequest,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
&liTimeOut);
|
||
|
|
||
|
if(nStatus isnot STATUS_SUCCESS)
|
||
|
{
|
||
|
//
|
||
|
// We timed out - bad things are happening here
|
||
|
//
|
||
|
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("IpIpDispatch: Timeout trying to start driver\n"));
|
||
|
|
||
|
nStatus = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Make sure the driver actually started
|
||
|
//
|
||
|
|
||
|
bEnter = EnterDriverCode();
|
||
|
|
||
|
ExitDriverCode();
|
||
|
|
||
|
if(!bEnter)
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("IpIpDispatch: Wait successful, but unable to start driver\n"));
|
||
|
|
||
|
nStatus = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IRP_MJ_CLOSE:
|
||
|
{
|
||
|
Trace(GLOBAL, TRACE,
|
||
|
("IpIpDispatch: IRP_MJ_CLOSE\n"));
|
||
|
|
||
|
//
|
||
|
// We handle cleanup and not close
|
||
|
//
|
||
|
|
||
|
nStatus = STATUS_SUCCESS;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IRP_MJ_CLEANUP:
|
||
|
{
|
||
|
Trace(GLOBAL, TRACE,
|
||
|
("IpIpDispatch: IRP_MJ_CLEANUP\n"));
|
||
|
|
||
|
StopDriver();
|
||
|
|
||
|
nStatus = STATUS_SUCCESS;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IRP_MJ_DEVICE_CONTROL:
|
||
|
{
|
||
|
DWORD dwState;
|
||
|
ULONG ulControl;
|
||
|
|
||
|
//
|
||
|
// Get the control code and our code
|
||
|
//
|
||
|
|
||
|
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
||
|
|
||
|
//
|
||
|
// If the driver is stopping, dont process anything else
|
||
|
//
|
||
|
|
||
|
bEnter = EnterDriverCode();
|
||
|
|
||
|
if(!bEnter)
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("IpIpDispatch: Driver is not started\n"));
|
||
|
|
||
|
nStatus = STATUS_NO_SUCH_DEVICE;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch(ioControlCode)
|
||
|
{
|
||
|
case IOCTL_IPINIP_CREATE_TUNNEL:
|
||
|
{
|
||
|
nStatus = AddTunnelInterface(Irp,
|
||
|
ulInputBuffLen,
|
||
|
ulOutputBuffLen);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_IPINIP_DELETE_TUNNEL:
|
||
|
{
|
||
|
nStatus = DeleteTunnelInterface(Irp,
|
||
|
ulInputBuffLen,
|
||
|
ulOutputBuffLen);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_IPINIP_SET_TUNNEL_INFO:
|
||
|
{
|
||
|
nStatus = SetTunnelInfo(Irp,
|
||
|
ulInputBuffLen,
|
||
|
ulOutputBuffLen);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_IPINIP_GET_TUNNEL_TABLE:
|
||
|
{
|
||
|
nStatus = GetTunnelTable(Irp,
|
||
|
ulInputBuffLen,
|
||
|
ulOutputBuffLen);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_IPINIP_NOTIFICATION:
|
||
|
{
|
||
|
nStatus = ProcessNotification(Irp,
|
||
|
ulInputBuffLen,
|
||
|
ulOutputBuffLen);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("IpIpDispatch: Unknown IRP_MJ_DEVICE_CONTROL %x\n",
|
||
|
ioControlCode));
|
||
|
|
||
|
nStatus = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExitDriverCode();
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("IpIpDispatch: Unknown IRP_MJ_XX - %x\n",
|
||
|
irpStack->MajorFunction));
|
||
|
|
||
|
nStatus = STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(nStatus isnot STATUS_PENDING)
|
||
|
{
|
||
|
Irp->IoStatus.Status = nStatus;
|
||
|
|
||
|
IoCompleteRequest(Irp,
|
||
|
IO_NETWORK_INCREMENT);
|
||
|
}
|
||
|
|
||
|
TraceLeave(GLOBAL, "IpIpDispatch");
|
||
|
|
||
|
return nStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
IpIpUnload(
|
||
|
PDRIVER_OBJECT DriverObject
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
Called by the I/O subsystem, when our driver is being unloaded
|
||
|
|
||
|
Locks
|
||
|
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
UNICODE_STRING usDeviceName;
|
||
|
BOOLEAN bWait;
|
||
|
KIRQL kiIrql;
|
||
|
NDIS_STATUS nsStatus;
|
||
|
NTSTATUS nStatus;
|
||
|
|
||
|
TraceEnter(GLOBAL,"IpIpUnload");
|
||
|
|
||
|
//
|
||
|
// The driver must have stopped before it came here
|
||
|
//
|
||
|
|
||
|
RtAssert(g_dwDriverState is DRIVER_STOPPED);
|
||
|
|
||
|
RemoveAllTunnels();
|
||
|
|
||
|
//
|
||
|
// Clear out IP's state
|
||
|
//
|
||
|
|
||
|
DeregisterWithIp();
|
||
|
|
||
|
//
|
||
|
// Remove ourself from NT and DOS namespace
|
||
|
//
|
||
|
|
||
|
RtlInitUnicodeString(&usDeviceName,
|
||
|
DD_IPINIP_DEVICE_NAME);
|
||
|
|
||
|
SetupExternalName(&usDeviceName,
|
||
|
WIN32_IPINIP_SYMBOLIC_LINK,
|
||
|
FALSE);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Clean out address blocks, if any
|
||
|
//
|
||
|
|
||
|
while(!IsListEmpty(&g_leAddressList))
|
||
|
{
|
||
|
PADDRESS_BLOCK pAddrBlock;
|
||
|
PLIST_ENTRY pleNode;
|
||
|
|
||
|
pleNode = RemoveHeadList(&g_leAddressList);
|
||
|
|
||
|
pAddrBlock = CONTAINING_RECORD(pleNode,
|
||
|
ADDRESS_BLOCK,
|
||
|
leAddressLink);
|
||
|
|
||
|
RtFree(pAddrBlock);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if we have any free memory
|
||
|
//
|
||
|
|
||
|
RtAuditMemory();
|
||
|
|
||
|
//
|
||
|
// Delete the device object
|
||
|
//
|
||
|
|
||
|
IoDeleteDevice(DriverObject->DeviceObject);
|
||
|
|
||
|
TraceLeave(GLOBAL,"IpIpUnload");
|
||
|
}
|
||
|
|
||
|
#pragma alloc_text(PAGE, SetupExternalName)
|
||
|
|
||
|
BOOLEAN
|
||
|
SetupExternalName(
|
||
|
PUNICODE_STRING pusNtName,
|
||
|
PWCHAR pwcDosName,
|
||
|
BOOLEAN bCreate
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
Setup or delete a symbolic link to DOS namespace
|
||
|
|
||
|
Locks
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
pusNtName Name in NT space
|
||
|
pwcDosName Name in DOS space
|
||
|
bCreate Set to TRUE to create, FALSE to delete
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
TRUE if successful
|
||
|
FALSE otherwise
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
UNICODE_STRING usSymbolicLinkName;
|
||
|
WCHAR rgwcBuffer[100];
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
//
|
||
|
// Form the full symbolic link name we wish to create.
|
||
|
//
|
||
|
|
||
|
usSymbolicLinkName.Buffer = rgwcBuffer;
|
||
|
|
||
|
RtlInitUnicodeString(&usSymbolicLinkName,
|
||
|
pwcDosName);
|
||
|
|
||
|
if(bCreate)
|
||
|
{
|
||
|
if(!NT_SUCCESS(IoCreateSymbolicLink(&usSymbolicLinkName,
|
||
|
pusNtName)))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IoDeleteSymbolicLink(&usSymbolicLinkName);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
StartDriver(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
Main routine to start the driver. We call this when we get CREATE irp
|
||
|
If the driver has started, we return success. If someone is starting the
|
||
|
driver, we return pending. The caller then needs to wait on g_keStartEvent
|
||
|
We try and start the driver. If all goes well, we set the event and
|
||
|
everyone parties on from there
|
||
|
|
||
|
Locks
|
||
|
|
||
|
The function takes the g_rlStateLock to check the state and increment
|
||
|
the number of CREATEs it has received (open handles)
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
STATUS_SUCCESS if the driver started
|
||
|
STATUS_PENDING if the driver is being started by some other thread's
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
KIRQL kiOldIrql;
|
||
|
NTSTATUS nStatus;
|
||
|
DWORD dwState;
|
||
|
OPEN_CONTEXT TdixContext;
|
||
|
WORK_QUEUE_ITEM WorkItem;
|
||
|
KEVENT keTempEvent;
|
||
|
|
||
|
TraceEnter(GLOBAL, "StartDriver");
|
||
|
|
||
|
RtAcquireSpinLock(&g_rlStateLock,
|
||
|
&kiOldIrql);
|
||
|
|
||
|
g_ulNumOpens++;
|
||
|
|
||
|
if(g_ulNumOpens isnot 1)
|
||
|
{
|
||
|
if(g_dwDriverState is DRIVER_STARTING)
|
||
|
{
|
||
|
//
|
||
|
// Someone is trying to start the driver
|
||
|
//
|
||
|
|
||
|
Trace(GLOBAL, INFO,
|
||
|
("StartDriver: Driver is being started by someone else\n"));
|
||
|
|
||
|
nStatus = STATUS_PENDING;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// If we are not the first CreateFile, and the driver is not
|
||
|
// starting then the driver must already be running
|
||
|
//
|
||
|
|
||
|
RtAssert(g_dwDriverState is DRIVER_STARTED);
|
||
|
|
||
|
nStatus = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
kiOldIrql);
|
||
|
|
||
|
return nStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The first CreateFile
|
||
|
//
|
||
|
|
||
|
RtAssert(g_dwDriverState is DRIVER_STOPPED);
|
||
|
|
||
|
//
|
||
|
// Set the state to starting, release the lock and actually start
|
||
|
// the driver
|
||
|
//
|
||
|
|
||
|
g_dwDriverState = DRIVER_STARTING;
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
kiOldIrql);
|
||
|
|
||
|
dwState = DRIVER_STARTED;
|
||
|
|
||
|
//
|
||
|
// Initialize the event and work item to start TDI
|
||
|
//
|
||
|
|
||
|
KeInitializeEvent(&keTempEvent,
|
||
|
SynchronizationEvent,
|
||
|
FALSE);
|
||
|
|
||
|
TdixContext.pkeEvent = &keTempEvent;
|
||
|
|
||
|
ExInitializeWorkItem(&WorkItem,
|
||
|
TdixInitialize,
|
||
|
&TdixContext);
|
||
|
|
||
|
//
|
||
|
// Start TDI in the system context so that we the handles are not
|
||
|
// associated with the current process
|
||
|
//
|
||
|
|
||
|
ExQueueWorkItem(&WorkItem,
|
||
|
DelayedWorkQueue);
|
||
|
|
||
|
//
|
||
|
// Wait for TDI to get done
|
||
|
//
|
||
|
|
||
|
nStatus = KeWaitForSingleObject(TdixContext.pkeEvent,
|
||
|
UserRequest,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL);
|
||
|
|
||
|
if((nStatus isnot STATUS_SUCCESS) or
|
||
|
(TdixContext.nStatus isnot STATUS_SUCCESS))
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("StartDriver: TdixInitialize failed with %x %x\n",
|
||
|
nStatus, TdixContext.nStatus));
|
||
|
|
||
|
dwState = DRIVER_STOPPED;
|
||
|
}
|
||
|
|
||
|
if(dwState is DRIVER_STARTED)
|
||
|
{
|
||
|
LARGE_INTEGER liDueTime;
|
||
|
|
||
|
KeInitializeDpc(&g_kdTimerDpc,
|
||
|
IpIpTimerRoutine,
|
||
|
NULL);
|
||
|
|
||
|
KeInitializeTimer(&g_ktTimer);
|
||
|
|
||
|
liDueTime = RtlEnlargedUnsignedMultiply(TIMER_IN_MILLISECS,
|
||
|
SYS_UNITS_IN_ONE_MILLISEC);
|
||
|
|
||
|
liDueTime = RtlLargeIntegerNegate(liDueTime);
|
||
|
|
||
|
KeSetTimerEx(&g_ktTimer,
|
||
|
liDueTime,
|
||
|
0,
|
||
|
&g_kdTimerDpc);
|
||
|
}
|
||
|
|
||
|
RtAcquireSpinLock(&g_rlStateLock,
|
||
|
&kiOldIrql);
|
||
|
|
||
|
g_dwDriverState = dwState;
|
||
|
|
||
|
//
|
||
|
// Someone may have been waiting for us to start
|
||
|
//
|
||
|
|
||
|
KeSetEvent(&g_keStartEvent,
|
||
|
0,
|
||
|
FALSE);
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
kiOldIrql);
|
||
|
|
||
|
TraceLeave(GLOBAL, "StartDriver");
|
||
|
|
||
|
return nStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
StopDriver(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
Called when we get an IRP_MJ_CLEANUP. It is the inverse of StartDriver
|
||
|
If this is the last thread, we set the state to STOPPED and wait till
|
||
|
all threads of execution have exited the driver.
|
||
|
We then clean out resources
|
||
|
|
||
|
Locks
|
||
|
|
||
|
The function takes the g_rlStateLock
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL kiOldIrql;
|
||
|
NTSTATUS nStatus;
|
||
|
BOOLEAN bWait;
|
||
|
ULONG i;
|
||
|
OPEN_CONTEXT TdixContext;
|
||
|
WORK_QUEUE_ITEM WorkItem;
|
||
|
KEVENT keTempEvent;
|
||
|
PIO_WORKITEM pIoWorkItem;
|
||
|
|
||
|
TraceEnter(GLOBAL, "StopDriver");
|
||
|
|
||
|
//
|
||
|
// Acquire the state and ref count spin lock
|
||
|
//
|
||
|
|
||
|
RtAcquireSpinLock(&g_rlStateLock,
|
||
|
&kiOldIrql);
|
||
|
|
||
|
g_ulNumOpens--;
|
||
|
|
||
|
if(g_ulNumOpens isnot 0)
|
||
|
{
|
||
|
//
|
||
|
// Other people still around
|
||
|
//
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
kiOldIrql);
|
||
|
|
||
|
TraceLeave(GLOBAL, "StopDriver");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Set the state to stopping. Any reader will
|
||
|
// return on seeing this. So essentially we are not
|
||
|
// allowing any new readers in
|
||
|
//
|
||
|
|
||
|
g_dwDriverState = DRIVER_STOPPED;
|
||
|
|
||
|
//
|
||
|
// However there may already be readers. We wait
|
||
|
// if there are any
|
||
|
//
|
||
|
|
||
|
bWait = (g_ulNumThreads > 0);
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
kiOldIrql);
|
||
|
|
||
|
//
|
||
|
// Now do a wait. We can do this since we are at PASSIVE
|
||
|
//
|
||
|
|
||
|
if(bWait)
|
||
|
{
|
||
|
Trace(GLOBAL, INFO,
|
||
|
("StopDriver: Need to wait for threads to exit\n"));
|
||
|
|
||
|
do
|
||
|
{
|
||
|
nStatus = KeWaitForSingleObject(&g_keStateEvent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL);
|
||
|
|
||
|
}while((nStatus is STATUS_USER_APC) or
|
||
|
(nStatus is STATUS_ALERTED) or
|
||
|
(nStatus is STATUS_TIMEOUT));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Undo the timer
|
||
|
//
|
||
|
|
||
|
i = 0;
|
||
|
|
||
|
while(KeCancelTimer(&g_ktTimer) is FALSE)
|
||
|
{
|
||
|
LARGE_INTEGER liTimeOut;
|
||
|
|
||
|
//
|
||
|
// Hmm, timer was not in the system queue.
|
||
|
// Set the wait to 2, 4, 6... secs
|
||
|
//
|
||
|
|
||
|
liTimeOut.QuadPart = (LONGLONG)((i + 1) * 2 * 1000 * 1000 * 10 * -1);
|
||
|
|
||
|
KeDelayExecutionThread(UserMode,
|
||
|
FALSE,
|
||
|
&liTimeOut);
|
||
|
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the event and work item to stop TDI
|
||
|
//
|
||
|
|
||
|
KeInitializeEvent(&keTempEvent,
|
||
|
SynchronizationEvent,
|
||
|
FALSE);
|
||
|
|
||
|
TdixContext.pkeEvent = &keTempEvent;
|
||
|
|
||
|
pIoWorkItem = IoAllocateWorkItem(g_pIpIpDevice);
|
||
|
|
||
|
//
|
||
|
// Must have a work item - no failure code path
|
||
|
//
|
||
|
|
||
|
RtAssert(pIoWorkItem);
|
||
|
|
||
|
IoQueueWorkItem(pIoWorkItem,
|
||
|
TdixDeinitialize,
|
||
|
DelayedWorkQueue,
|
||
|
&TdixContext);
|
||
|
|
||
|
//
|
||
|
// Wait for TDI to get done
|
||
|
//
|
||
|
|
||
|
nStatus = KeWaitForSingleObject(TdixContext.pkeEvent,
|
||
|
UserRequest,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL);
|
||
|
|
||
|
RtAssert(nStatus is STATUS_SUCCESS);
|
||
|
|
||
|
IoFreeWorkItem(pIoWorkItem);
|
||
|
|
||
|
//
|
||
|
// Cleanup all resources
|
||
|
//
|
||
|
|
||
|
while(!IsListEmpty(&g_lePendingMessageList))
|
||
|
{
|
||
|
PLIST_ENTRY pleNode;
|
||
|
PPENDING_MESSAGE pMessage;
|
||
|
|
||
|
//
|
||
|
// We have some old info
|
||
|
// Remove it off the pending list
|
||
|
//
|
||
|
|
||
|
pleNode = RemoveHeadList(&g_lePendingMessageList);
|
||
|
|
||
|
//
|
||
|
// Get a pointer to the structure
|
||
|
//
|
||
|
|
||
|
pMessage = CONTAINING_RECORD(pleNode,
|
||
|
PENDING_MESSAGE,
|
||
|
leMessageLink);
|
||
|
|
||
|
//
|
||
|
// Free the allocated message
|
||
|
//
|
||
|
|
||
|
FreeMessage(pMessage);
|
||
|
}
|
||
|
|
||
|
ClearPendingIrps();
|
||
|
|
||
|
TraceLeave(GLOBAL, "StopDriver");
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
RegisterWithIp(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
Registers the ARP module with IP
|
||
|
|
||
|
Locks
|
||
|
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
NDIS_STRING nsIpIpName;
|
||
|
WCHAR pwszName[] = IPINIP_ARP_NAME;
|
||
|
IP_STATUS Status;
|
||
|
IPInfo Info;
|
||
|
|
||
|
TraceEnter(GLOBAL, "RegisterWithIP");
|
||
|
|
||
|
Status = IPGetInfo(&Info,
|
||
|
sizeof(Info));
|
||
|
|
||
|
if(Status isnot IP_SUCCESS)
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("RegisterWithIp: Couldnt get IPInfo %x\n", Status));
|
||
|
|
||
|
TraceLeave(GLOBAL,
|
||
|
"RegisterWithIP");
|
||
|
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
g_pfnOpenRce = Info.ipi_openrce;
|
||
|
g_pfnCloseRce = Info.ipi_closerce;
|
||
|
|
||
|
nsIpIpName.MaximumLength = sizeof(pwszName);
|
||
|
nsIpIpName.Length = sizeof(IPINIP_ARP_NAME) - sizeof(WCHAR);
|
||
|
nsIpIpName.Buffer = pwszName;
|
||
|
|
||
|
Status = IPRegisterARP(&nsIpIpName,
|
||
|
IP_ARP_BIND_VERSION,
|
||
|
IpIpBindAdapter,
|
||
|
&g_pfnIpAddInterface,
|
||
|
&g_pfnIpDeleteInterface,
|
||
|
&g_pfnIpBindComplete,
|
||
|
&g_pfnIpAddLink,
|
||
|
&g_pfnIpDeleteLink,
|
||
|
&g_pfnIpChangeIndex,
|
||
|
&g_pfnIpReserveIndex,
|
||
|
&g_pfnIpDereserveIndex,
|
||
|
&g_hIpRegistration);
|
||
|
|
||
|
if(Status isnot IP_SUCCESS)
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("RegisterWithIp: Couldnt register with IP\n"));
|
||
|
|
||
|
TraceLeave(GLOBAL,
|
||
|
"RegisterWithIP");
|
||
|
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
TraceLeave(GLOBAL,
|
||
|
"RegisterWithIP");
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
DeregisterWithIp(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description
|
||
|
|
||
|
DeRegisters the ARP module with IP
|
||
|
|
||
|
Locks
|
||
|
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
|
||
|
Return Value
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
NTSTATUS nStatus;
|
||
|
|
||
|
TraceEnter(GLOBAL, "DeregisterWithIp");
|
||
|
|
||
|
nStatus = IPDeregisterARP(g_hIpRegistration);
|
||
|
|
||
|
if(nStatus isnot STATUS_SUCCESS)
|
||
|
{
|
||
|
Trace(GLOBAL, ERROR,
|
||
|
("DeregisterWithIp: Couldnt deregister with IP. Error %x\n",
|
||
|
nStatus));
|
||
|
|
||
|
}
|
||
|
|
||
|
g_pfnIpAddInterface = NULL;
|
||
|
g_pfnIpDeleteInterface = NULL;
|
||
|
g_pfnIpBindComplete = NULL;
|
||
|
g_pfnIpRcv = NULL;
|
||
|
g_pfnIpRcvComplete = NULL;
|
||
|
g_pfnIpSendComplete = NULL;
|
||
|
g_pfnIpTDComplete = NULL;
|
||
|
g_pfnIpStatus = NULL;
|
||
|
g_pfnIpRcvPkt = NULL;
|
||
|
g_pfnIpPnp = NULL;
|
||
|
g_hIpRegistration = NULL;
|
||
|
|
||
|
TraceLeave(GLOBAL, "DeregisterWithIp");
|
||
|
}
|
||
|
|
||
|
#pragma alloc_text(PAGE, OpenRegKey)
|
||
|
|
||
|
NTSTATUS
|
||
|
OpenRegKey(
|
||
|
PHANDLE HandlePtr,
|
||
|
PWCHAR KeyName
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
UNICODE_STRING UKeyName;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
RtlInitUnicodeString(&UKeyName, KeyName);
|
||
|
|
||
|
RtlZeroMemory(&ObjectAttributes,
|
||
|
sizeof(OBJECT_ATTRIBUTES));
|
||
|
|
||
|
InitializeObjectAttributes(&ObjectAttributes,
|
||
|
&UKeyName,
|
||
|
OBJ_CASE_INSENSITIVE,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
|
||
|
Status = ZwOpenKey(HandlePtr,
|
||
|
KEY_READ,
|
||
|
&ObjectAttributes);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma alloc_text(PAGE, GetRegDWORDValue)
|
||
|
|
||
|
NTSTATUS
|
||
|
GetRegDWORDValue(
|
||
|
HANDLE KeyHandle,
|
||
|
PWCHAR ValueName,
|
||
|
PULONG ValueData
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
ULONG resultLength;
|
||
|
PKEY_VALUE_FULL_INFORMATION keyValueFullInformation;
|
||
|
UCHAR keybuf[128];
|
||
|
UNICODE_STRING UValueName;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
RtlInitUnicodeString(&UValueName, ValueName);
|
||
|
|
||
|
keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf;
|
||
|
RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation));
|
||
|
|
||
|
|
||
|
status = ZwQueryValueKey(KeyHandle,
|
||
|
&UValueName,
|
||
|
KeyValueFullInformation,
|
||
|
keyValueFullInformation,
|
||
|
128,
|
||
|
&resultLength);
|
||
|
|
||
|
if (NT_SUCCESS(status)) {
|
||
|
if (keyValueFullInformation->Type != REG_DWORD) {
|
||
|
status = STATUS_INVALID_PARAMETER_MIX;
|
||
|
} else {
|
||
|
*ValueData = *((ULONG UNALIGNED *)((PCHAR)keyValueFullInformation +
|
||
|
keyValueFullInformation->DataOffset));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
EnterDriverCode(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
BOOLEAN bEnter;
|
||
|
|
||
|
RtAcquireSpinLock(&g_rlStateLock,
|
||
|
&irql);
|
||
|
|
||
|
if(g_dwDriverState is DRIVER_STARTED)
|
||
|
{
|
||
|
g_ulNumThreads++;
|
||
|
|
||
|
bEnter = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bEnter = FALSE;
|
||
|
}
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
irql);
|
||
|
|
||
|
return bEnter;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ExitDriverCode(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
|
||
|
RtAcquireSpinLock(&g_rlStateLock,
|
||
|
&irql);
|
||
|
|
||
|
g_ulNumThreads--;
|
||
|
|
||
|
if((g_dwDriverState is DRIVER_STOPPED) and
|
||
|
(g_ulNumThreads is 0))
|
||
|
{
|
||
|
KeSetEvent(&g_keStateEvent,
|
||
|
0,
|
||
|
FALSE);
|
||
|
}
|
||
|
|
||
|
RtReleaseSpinLock(&g_rlStateLock,
|
||
|
irql);
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ClearPendingIrps(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Called at cleanup time to return any pending IRPs
|
||
|
|
||
|
Locks:
|
||
|
|
||
|
Acquires the IoCancelSpinLock since it controls the pending irp list
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
|
||
|
TraceEnter(GLOBAL, "ClearPendingIrps");
|
||
|
|
||
|
IoAcquireCancelSpinLock (&irql);
|
||
|
|
||
|
while(!IsListEmpty(&g_lePendingIrpList))
|
||
|
{
|
||
|
PLIST_ENTRY pleNode;
|
||
|
PIRP pIrp;
|
||
|
|
||
|
pleNode = RemoveHeadList(&g_lePendingIrpList);
|
||
|
|
||
|
pIrp = CONTAINING_RECORD(pleNode,
|
||
|
IRP,
|
||
|
Tail.Overlay.ListEntry);
|
||
|
|
||
|
IoSetCancelRoutine(pIrp,
|
||
|
NULL);
|
||
|
|
||
|
pIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
||
|
pIrp->IoStatus.Information = 0;
|
||
|
|
||
|
//
|
||
|
// release lock to complete the IRP
|
||
|
//
|
||
|
|
||
|
IoReleaseCancelSpinLock(irql);
|
||
|
|
||
|
IoCompleteRequest(pIrp,
|
||
|
IO_NETWORK_INCREMENT);
|
||
|
|
||
|
//
|
||
|
// Reaquire the lock
|
||
|
//
|
||
|
|
||
|
IoAcquireCancelSpinLock(&irql);
|
||
|
}
|
||
|
|
||
|
IoReleaseCancelSpinLock(irql);
|
||
|
}
|
||
|
|