Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1212 lines
25 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
entry.c
Abstract:
This module contains the entry-code for the IP Network Address Translator.
Author:
Abolade Gbadegesin (t-abolag) 11-July-1997
Revision History:
William Ingle (billi) 12-May-2001 NULL security descriptor check
--*/
#include "precomp.h"
#pragma hdrstop
//
// GLOBAL DATA DEFINITIONS
//
COMPONENT_REFERENCE ComponentReference;
//
// Win32 device-name
//
WCHAR ExternalName[] = L"\\DosDevices\\IPNAT";
//
// Device- and file-object for the IP driver
//
extern PDEVICE_OBJECT IpDeviceObject = NULL;
extern PFILE_OBJECT IpFileObject = NULL;
extern HANDLE TcpDeviceHandle = NULL;
//
// Device-object for the NAT driver
//
extern PDEVICE_OBJECT NatDeviceObject = NULL;
//
// Registry parameters key name
//
WCHAR ParametersName[] = L"Parameters";
//
// Name of value holding reserved ports
//
WCHAR ReservedPortsName[] = L"ReservedPorts";
//
// Start and end of reserved-port range
//
USHORT ReservedPortsLowerRange = DEFAULT_START_PORT;
USHORT ReservedPortsUpperRange = DEFAULT_END_PORT;
//
// Device- and file-object for the TCP driver
//
extern PDEVICE_OBJECT TcpDeviceObject = NULL;
extern PFILE_OBJECT TcpFileObject = NULL;
//
// Registry path for the driver's parameters
//
const WCHAR IpNatParametersPath[] =
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services"
L"\\IpNat\\Parameters";
//
// Timeout interval for TCP session mappings
//
ULONG TcpTimeoutSeconds = DEFAULT_TCP_TIMEOUT;
//
// Bitmap of enabled tracing message classes
//
ULONG TraceClassesEnabled = 0;
//
// Registry trace-class value name
//
WCHAR TraceClassesEnabledName[] = L"TraceClassesEnabled";
//
// Timeout interval for UDP and other message-oriented session mappings
//
ULONG UdpTimeoutSeconds = DEFAULT_UDP_TIMEOUT;
#if NAT_WMI
//
// Copy of our registry path for WMI use.
//
UNICODE_STRING NatRegistryPath;
#endif
//
// Name of value for allowing inbound non-unicast
//
WCHAR AllowInboundNonUnicastTrafficName[] = L"AllowInboundNonUnicastTraffic";
//
// If true, non-unicast traffic will not be dropped
// when recevied on a firewalled interface.
//
BOOLEAN AllowInboundNonUnicastTraffic = FALSE;
//
// FUNCTION PROTOTYPES (alphabetically)
//
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
NatAdjustSecurityDescriptor(
VOID
);
VOID
NatCleanupDriver(
VOID
);
VOID
NatCreateExternalNaming(
IN PUNICODE_STRING DeviceString
);
VOID
NatDeleteExternalNaming(
VOID
);
NTSTATUS
NatInitializeDriver(
VOID
);
NTSTATUS
NatSetFirewallHook(
BOOLEAN Install
);
VOID
NatUnloadDriver(
IN PDRIVER_OBJECT DriverObject
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, NatAdjustSecurityDescriptor)
#pragma alloc_text(PAGE, NatCreateExternalNaming)
#pragma alloc_text(PAGE, NatDeleteExternalNaming)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Performs driver-initialization for NAT.
Arguments:
Return Value:
STATUS_SUCCESS if initialization succeeded, error code otherwise.
--*/
{
WCHAR DeviceName[] = DD_IP_NAT_DEVICE_NAME;
UNICODE_STRING DeviceString;
LONG i;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE ParametersKey;
HANDLE ServiceKey;
NTSTATUS status;
UNICODE_STRING String;
PAGED_CODE();
CALLTRACE(("DriverEntry\n"));
#if DBG
//
// Open the registry key
//
InitializeObjectAttributes(
&ObjectAttributes,
RegistryPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status = ZwOpenKey(&ServiceKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&String, ParametersName);
InitializeObjectAttributes(
&ObjectAttributes,
&String,
OBJ_CASE_INSENSITIVE,
ServiceKey,
NULL
);
status = ZwOpenKey(&ParametersKey, KEY_READ, &ObjectAttributes);
ZwClose(ServiceKey);
if (NT_SUCCESS(status)) {
UCHAR Buffer[32];
ULONG BytesRead;
PKEY_VALUE_PARTIAL_INFORMATION Value;
RtlInitUnicodeString(&String, TraceClassesEnabledName);
status =
ZwQueryValueKey(
ParametersKey,
&String,
KeyValuePartialInformation,
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
sizeof(Buffer),
&BytesRead
);
ZwClose(ParametersKey);
if (NT_SUCCESS(status) &&
((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Type == REG_DWORD) {
TraceClassesEnabled =
*(PULONG)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data;
}
}
}
#endif
#if NAT_WMI
//
// Record our registry path for WMI use
//
NatRegistryPath.Length = 0;
NatRegistryPath.MaximumLength
= RegistryPath->MaximumLength + sizeof( UNICODE_NULL );
NatRegistryPath.Buffer = ExAllocatePoolWithTag(
PagedPool,
NatRegistryPath.MaximumLength,
NAT_TAG_WMI
);
if( NatRegistryPath.Buffer )
{
RtlCopyUnicodeString( &NatRegistryPath, RegistryPath );
}
else
{
ERROR(("NAT: Unable to allocate string for RegistryPath\n"));
return STATUS_NO_MEMORY;
}
#endif
//
// Create the device's object.
//
RtlInitUnicodeString(&DeviceString, DeviceName);
status =
IoCreateDevice(
DriverObject,
0,
&DeviceString,
FILE_DEVICE_NETWORK,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&NatDeviceObject
);
if (!NT_SUCCESS(status)) {
ERROR(("IoCreateDevice failed (0x%08X)\n", status));
return status;
}
//
// Adjust the security descriptor on the device object.
//
status = NatAdjustSecurityDescriptor();
if (!NT_SUCCESS(status)) {
ERROR(("NatAdjustSecurityDescriptor failed (0x%08x)\n", status));
return status;
}
//
// Initialize file-object tracking items
//
KeInitializeSpinLock(&NatFileObjectLock);
NatOwnerProcessId = NULL;
NatFileObjectCount = 0;
//
// Setup the driver object
//
DriverObject->DriverUnload = NatUnloadDriver;
DriverObject->FastIoDispatch = &NatFastIoDispatch;
DriverObject->DriverStartIo = NULL;
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction[i] = NatDispatch;
}
//
// Create a Win32-accessible device object
//
NatCreateExternalNaming(&DeviceString);
//
// Initialize the driver's structures
//
status = NatInitializeDriver();
return status;
} // DriverEntry
NTSTATUS
NatAdjustSecurityDescriptor(
VOID
)
/*++
Routine Description:
Modifies the security descriptor on the NAT's device object so
that only SYSTEM has any permissions.
Arguments:
none.
Return Value:
NTSTATUS - success/error code.
--*/
{
PACE_HEADER AceHeader;
PSID AceSid;
PACL Dacl;
BOOLEAN DaclDefaulted;
BOOLEAN DaclPresent;
DWORD i;
BOOLEAN MemoryAllocated;
PSECURITY_DESCRIPTOR NatSD = NULL;
PACL NewDacl = NULL;
SECURITY_DESCRIPTOR NewSD;
SECURITY_INFORMATION SecurityInformation;
ULONG Size;
NTSTATUS status;
do
{
//
// Get our original security descriptor
//
status =
ObGetObjectSecurity(
NatDeviceObject,
&NatSD,
&MemoryAllocated
);
// ObGetObjectSecurity can return a NULL security descriptor
// even with NT_SUCCESS status code
if (!NT_SUCCESS(status) || (NULL==NatSD)) {
break;
}
//
// Obtain the Dacl from the security descriptor
//
status =
RtlGetDaclSecurityDescriptor(
NatSD,
&DaclPresent,
&Dacl,
&DaclDefaulted
);
if (!NT_SUCCESS(status)) {
break;
}
ASSERT(FALSE != DaclPresent);
//
// Make a copy of the Dacl so that we can modify it.
//
NewDacl =
ExAllocatePoolWithTag(
PagedPool,
Dacl->AclSize,
NAT_TAG_SD
);
if (NULL == NewDacl) {
status = STATUS_NO_MEMORY;
break;
}
RtlCopyMemory(NewDacl, Dacl, Dacl->AclSize);
//
// Loop through the DACL, removing any access allowed
// entries that aren't for SYSTEM
//
for (i = 0; i < NewDacl->AceCount; i++) {
status = RtlGetAce(NewDacl, i, &AceHeader);
if (NT_SUCCESS(status)) {
if (ACCESS_ALLOWED_ACE_TYPE == AceHeader->AceType) {
AceSid = (PSID) &((ACCESS_ALLOWED_ACE*)AceHeader)->SidStart;
if (!RtlEqualSid(AceSid, SeExports->SeLocalSystemSid)) {
status = RtlDeleteAce(NewDacl, i);
if (NT_SUCCESS(status)) {
i -= 1;
}
}
}
}
}
ASSERT(NewDacl->AceCount > 0);
//
// Create a new security descriptor to hold the new Dacl.
//
status =
RtlCreateSecurityDescriptor(
&NewSD,
SECURITY_DESCRIPTOR_REVISION
);
if (!NT_SUCCESS(status)) {
break;
}
//
// Place the new Dacl into the new SD
//
status =
RtlSetDaclSecurityDescriptor(
&NewSD,
TRUE,
NewDacl,
FALSE
);
if (!NT_SUCCESS(status)) {
break;
}
//
// Set the new SD into our device object. Only the Dacl from the
// SD will be set.
//
SecurityInformation = DACL_SECURITY_INFORMATION;
status =
ObSetSecurityObjectByPointer(
NatDeviceObject,
SecurityInformation,
&NewSD
);
} while (FALSE);
if (NULL != NatSD) {
ObReleaseObjectSecurity(NatSD, MemoryAllocated);
}
if (NULL != NewDacl) {
ExFreePool(NewDacl);
}
return status;
} // NatAdjustSecurityDescriptor
VOID
NatCleanupDriver(
VOID
)
/*++
Routine Description:
This routine is invoked when the last reference to the NAT driver
is released.
Arguments:
none.
Return Value:
none.
--*/
{
CALLTRACE(("NatCleanupDriver\n"));
} // NatCleanupDriver
VOID
NatCreateExternalNaming(
IN PUNICODE_STRING DeviceString
)
/*++
Routine Description:
Creates a symbolic-link to the NAT's device-object so
the NAT can be opened by a user-mode process.
Arguments:
DeviceString - Unicode name of the NAT's device-object.
Return Value:
none.
--*/
{
UNICODE_STRING symLinkString;
PAGED_CODE();
RtlInitUnicodeString(&symLinkString, ExternalName);
IoCreateSymbolicLink(&symLinkString, DeviceString);
} // NatCreateExternalNaming
VOID
NatDeleteExternalNaming(
VOID
)
/*++
Routine Description:
Deletes the Win32 symbolic-link to the NAT's device-object
Arguments:
Return Value:
none.
--*/
{
UNICODE_STRING symLinkString;
PAGED_CODE();
RtlInitUnicodeString(&symLinkString, ExternalName);
IoDeleteSymbolicLink(&symLinkString);
} // NatDeleteExternalNaming
NTSTATUS
NatInitializeDriver(
VOID
)
/*++
Routine Description:
Performs initialization of the driver's structures.
Arguments:
none.
Return Value:
NTSTATUS - success/error code.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE ParametersKey;
NTSTATUS status;
NTSTATUS status2;
UNICODE_STRING UnicodeString;
IO_STATUS_BLOCK IoStatus;
CALLTRACE(("NatInitializeDriver\n"));
//
// Set up global synchronization objects
//
InitializeComponentReference(&ComponentReference, NatCleanupDriver);
//
// Obtain the IP and TCP driver device-objects
//
RtlInitUnicodeString(&UnicodeString, DD_IP_DEVICE_NAME);
status =
IoGetDeviceObjectPointer(
&UnicodeString,
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
&IpFileObject,
&IpDeviceObject
);
if (!NT_SUCCESS(status)) {
ERROR(("NatInitializeDriver: error %X getting IP object\n", status));
return status;
}
RtlInitUnicodeString(&UnicodeString, DD_TCP_DEVICE_NAME);
status =
IoGetDeviceObjectPointer(
&UnicodeString,
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
&TcpFileObject,
&TcpDeviceObject
);
if (!NT_SUCCESS(status)) {
ERROR(("NatInitializeDriver: error %X getting TCP object\n", status));
return status;
}
//
// Open Tcp Kernel Device
//
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status =
ZwCreateFile(
&TcpDeviceHandle,
GENERIC_READ,
&ObjectAttributes,
&IoStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0);
if ( !NT_SUCCESS(status) )
{
ERROR(("ZwCreateFile failed (0x%08X)\n", status));
}
ObReferenceObject(IpDeviceObject);
ObReferenceObject(TcpDeviceObject);
//
// Initialize all object-modules
//
NatInitializeTimerManagement();
NatInitializeMappingManagement();
NatInitializeDirectorManagement();
NatInitializeEditorManagement();
NatInitializeRedirectManagement();
NatInitializeDynamicTicketManagement();
NatInitializeIcmpManagement();
NatInitializeRawIpManagement();
NatInitializeInterfaceManagement();
#if 0
status = NatInitializeAddressManagement();
if (!NT_SUCCESS(status)) { return status; }
#endif
NatInitializePacketManagement();
NatInitializeNotificationManagement();
#if NAT_WMI
NatInitializeWMI();
#endif
//
// Initialize NAT-provided editors.
//
status = NatInitializePptpManagement();
if (!NT_SUCCESS(status)) { return status; }
//
// Commence translation of packets, and start the periodic timer.
//
status = NatInitiateTranslation();
//
// Read optional registry settings.
// The user may customize the range of ports used by modifying
// the reserved-ports setting in the registry.
// We now check to see if there is such a value,
// and if so, we use it as our reserved-port range.
//
// The user may also specify that inbound non-unicast traffic
// is allowed on a firewalled interface.
//
//
// N.B. Failures here are not returned to the caller.
//
RtlInitUnicodeString(&UnicodeString, IpNatParametersPath);
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status2 = ZwOpenKey(&ParametersKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(status2)) {
UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
ULONG EndPort;
PWCHAR p;
ULONG StartPort;
PKEY_VALUE_PARTIAL_INFORMATION Value = NULL;
ULONG ValueLength;
//
// First check for allowed non-unicast traffic.
//
RtlInitUnicodeString(
&UnicodeString,
AllowInboundNonUnicastTrafficName
);
status2 =
ZwQueryValueKey(
ParametersKey,
&UnicodeString,
KeyValuePartialInformation,
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
sizeof(Buffer),
&ValueLength
);
if (NT_SUCCESS(status2)
&& REG_DWORD == ((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Type) {
AllowInboundNonUnicastTraffic =
1 == *((PULONG)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data);
}
//
// Check for reserved ports
//
do {
RtlInitUnicodeString(&UnicodeString, ReservedPortsName);
status2 =
ZwQueryValueKey(
ParametersKey,
&UnicodeString,
KeyValuePartialInformation,
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
sizeof(Buffer),
&ValueLength
);
if (status2 != STATUS_BUFFER_OVERFLOW) { break; }
Value =
(PKEY_VALUE_PARTIAL_INFORMATION)
ExAllocatePoolWithTag(
PagedPool, ValueLength, NAT_TAG_RANGE_ARRAY
);
if (!Value) { break; }
status2 =
ZwQueryValueKey(
ParametersKey,
&UnicodeString,
KeyValuePartialInformation,
(PKEY_VALUE_PARTIAL_INFORMATION)Value,
ValueLength,
&ValueLength
);
if (!NT_SUCCESS(status2)) { break; }
//
// The value should be in the format "xxx-yyy\0\0";
// read the first number
//
p = (PWCHAR)Value->Data;
RtlInitUnicodeString(&UnicodeString, p);
status2 = RtlUnicodeStringToInteger(&UnicodeString, 10, &StartPort);
if (!NT_SUCCESS(status2)) { break; }
//
// Advance past '-'
//
while (*p && *p != L'-') { ++p; }
if (*p != L'-') { break; } else { ++p; }
//
// Read second number
//
RtlInitUnicodeString(&UnicodeString, p);
status2 = RtlUnicodeStringToInteger(&UnicodeString, 10, &EndPort);
if (!NT_SUCCESS(status2)) { break; }
//
// Validate the resulting range
//
if (StartPort > 0 &&
StartPort < 65535 &&
EndPort > 0 &&
EndPort < 65535 &&
StartPort <= EndPort
) {
ReservedPortsLowerRange = NTOHS((USHORT)StartPort);
ReservedPortsUpperRange = NTOHS((USHORT)EndPort);
}
} while(FALSE);
if (Value) { ExFreePool(Value); }
ZwClose(ParametersKey);
}
return status;
} // NatInitializeDriver
NTSTATUS
NatInitiateTranslation(
VOID
)
/*++
Routine Description:
This routine is invoked on creation of the first interface,
to launch the periodic timer and install the firewall hook.
Arguments:
none.
Return Value:
STATUS_SUCCESS if successful, error code otherwise.
--*/
{
CALLTRACE(("NatInitiateTranslation\n"));
//
// Launch the timer
//
NatStartTimer();
//
// Install 'NatTranslate' as the firewall hook
//
return NatSetFirewallHook(TRUE);
} // NatInitiateTranslation
NTSTATUS
NatSetFirewallHook(
BOOLEAN Install
)
/*++
Routine Description:
This routine is called to set (Install==TRUE) or clear (Install==FALSE) the
value of the firewall-callout function pointer in the IP driver.
Arguments:
Install - indicates whether to install or remove the hook.
Return Value:
NTSTATUS - indicates success/failure
Environment:
The routine assumes the caller is executing at PASSIVE_LEVEL.
--*/
{
IP_SET_FIREWALL_HOOK_INFO HookInfo;
IO_STATUS_BLOCK IoStatus;
PIRP Irp;
TCP_RESERVE_PORT_RANGE PortRange;
KEVENT LocalEvent;
NTSTATUS status;
CALLTRACE(("NatSetFirewallHook\n"));
//
// Register (or deregister) as a firewall
//
HookInfo.FirewallPtr = (IPPacketFirewallPtr)NatTranslatePacket;
HookInfo.Priority = 1;
HookInfo.Add = Install;
KeInitializeEvent(&LocalEvent, SynchronizationEvent, FALSE);
Irp =
IoBuildDeviceIoControlRequest(
IOCTL_IP_SET_FIREWALL_HOOK,
IpDeviceObject,
(PVOID)&HookInfo,
sizeof(HookInfo),
NULL,
0,
FALSE,
&LocalEvent,
&IoStatus
);
if (!Irp) {
ERROR(("NatSetFirewallHook: IoBuildDeviceIoControlRequest=0\n"));
return STATUS_UNSUCCESSFUL;
}
status = IoCallDriver(IpDeviceObject, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&LocalEvent, Executive, KernelMode, FALSE, NULL);
status = IoStatus.Status;
}
if (!NT_SUCCESS(status)) {
ERROR(("NatSetFirewallHook: IpSetFirewallHook=0x%08X\n", status));
return status;
}
if (ReservedPortsLowerRange != DEFAULT_START_PORT ||
ReservedPortsUpperRange != DEFAULT_END_PORT
) {
return STATUS_SUCCESS;
}
//
// Reserve (or unreserve) our port-range
//
// N.B. The IOCTL expects host-order numbers and we store the range
// in network order, so do a swap before reserving the ports.
//
PortRange.LowerRange = NTOHS(DEFAULT_START_PORT);
PortRange.UpperRange = NTOHS(DEFAULT_END_PORT);
Irp =
IoBuildDeviceIoControlRequest(
Install
? IOCTL_TCP_RESERVE_PORT_RANGE
: IOCTL_TCP_UNRESERVE_PORT_RANGE,
TcpDeviceObject,
(PVOID)&PortRange,
sizeof(PortRange),
NULL,
0,
FALSE,
&LocalEvent,
&IoStatus
);
if (!Irp) {
ERROR(("NatSetFirewallHook: IoBuildDeviceIoControlRequest(2)=0\n"));
return STATUS_UNSUCCESSFUL;
}
status = IoCallDriver(TcpDeviceObject, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&LocalEvent, Executive, KernelMode, FALSE, NULL);
status = IoStatus.Status;
}
if (!NT_SUCCESS(status)) {
ERROR(("NatSetFirewallHook: Tcp(Un)ReservePortRange=0x%08X\n", status));
}
return status;
} // NatSetFirewallHook
VOID
NatTerminateTranslation(
VOID
)
/*++
Routine Description:
On cleanup of the last interface, this routine is invoked
to stop the periodic timer and de-install the firewall hook.
Arguments:
none.
Return Value:
none.
--*/
{
CALLTRACE(("NatTerminateTranslation\n"));
NatSetFirewallHook(FALSE);
} // NatTerminateTranslation
VOID
NatUnloadDriver(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
Performs cleanup for the NAT.
Arguments:
DriverObject - reference to the NAT's driver-object
Return Value:
--*/
{
PNAT_EDITOR Editor;
PLIST_ENTRY List;
CALLTRACE(("NatUnloadDriver\n"));
//
// Stop translation and clear the periodic timer
//
NatTerminateTranslation();
//
// Stop the route-change-notification in our packet-management and
// address-management modules.
// This forces completion of the route-change and address-change IRPs,
// which in turn releases component-references which would otherwise not
// drop until a route-change and address-change occurred.
//
NatShutdownNotificationManagement();
NatShutdownPacketManagement();
#if 0
NatShutdownAddressManagement();
#endif
//
// Drop our self-reference and wait for all activity to cease.
//
ReleaseInitialComponentReference(&ComponentReference, TRUE);
//
// Tear down our Win32-namespace symbolic link
//
NatDeleteExternalNaming();
//
// Delete the NAT's device object
//
IoDeleteDevice(DriverObject->DeviceObject);
//
// Shutdown object modules
//
#if NAT_WMI
NatShutdownWMI();
if( NatRegistryPath.Buffer )
{
ExFreePool( NatRegistryPath.Buffer );
RtlInitUnicodeString( &NatRegistryPath, NULL );
}
#endif
NatShutdownPptpManagement();
NatShutdownTimerManagement();
NatShutdownMappingManagement();
NatShutdownEditorManagement();
NatShutdownDirectorManagement();
NatShutdownDynamicTicketManagement();
NatShutdownRawIpManagement();
NatShutdownIcmpManagement();
NatShutdownInterfaceManagement();
//
// Release references to the IP and TCP driver objects
//
ObDereferenceObject((PVOID)IpFileObject);
ObDereferenceObject(IpDeviceObject);
ObDereferenceObject((PVOID)TcpFileObject);
ObDereferenceObject(TcpDeviceObject);
if (TcpDeviceHandle) {
ZwClose(TcpDeviceHandle);
TcpDeviceHandle = NULL;
}
DeleteComponentReference(&ComponentReference);
} // NatUnloadDriver