1385 lines
34 KiB
C
1385 lines
34 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
if.c
|
||
|
||
Abstract:
|
||
|
||
This file contains code for interface management.
|
||
|
||
Author:
|
||
|
||
Abolade Gbadegesin (t-abolag) 12-July-1997
|
||
|
||
Revision History:
|
||
|
||
Abolade Gbadegesin (aboladeg) 19-July-1998
|
||
|
||
Substantially rewritten as part of change to a global mapping-tree.
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
//
|
||
// GLOBAL DATA DEFINITIONS
|
||
//
|
||
|
||
ULONG FirewalledInterfaceCount;
|
||
CACHE_ENTRY InterfaceCache[CACHE_SIZE];
|
||
ULONG InterfaceCount;
|
||
LIST_ENTRY InterfaceList;
|
||
KSPIN_LOCK InterfaceLock;
|
||
KSPIN_LOCK InterfaceMappingLock;
|
||
|
||
|
||
VOID
|
||
NatCleanupInterface(
|
||
PNAT_INTERFACE Interfacep
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called to perform cleanup for an interface.
|
||
|
||
On completion, the memory for the interface is freed and its context
|
||
becomes invalid.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface to be cleaned up.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Invoked with no references to 'Interfacep', and with 'Interfacep' already
|
||
removed from the interface list.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatCleanupInterface\n"));
|
||
|
||
InterlockedClearCache(InterfaceCache, Interfacep->Index);
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
NatResetInterface(Interfacep);
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
if (Interfacep->AddressArray) { ExFreePool(Interfacep->AddressArray); }
|
||
if (Interfacep->Info) { ExFreePool(Interfacep->Info); }
|
||
ExFreePool(Interfacep);
|
||
|
||
InterlockedDecrement(&InterfaceCount);
|
||
|
||
} // NatCleanupInterface
|
||
|
||
|
||
LONG
|
||
FASTCALL
|
||
NatCompareAddressMappingCallback(
|
||
VOID* a,
|
||
VOID* b
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the callback invoked by our sorting routine
|
||
when we ask it to sort the array of configured address-mappings.
|
||
The sorting treats the 'PublicAddress' field as an integer.
|
||
|
||
Arguments:
|
||
|
||
a - first mapping
|
||
|
||
b - second mapping
|
||
|
||
Return Value:
|
||
|
||
LONG - the result of the comparison (<0, ==0, >0).
|
||
|
||
--*/
|
||
|
||
{
|
||
return
|
||
((PIP_NAT_ADDRESS_MAPPING)a)->PrivateAddress -
|
||
((PIP_NAT_ADDRESS_MAPPING)b)->PrivateAddress;
|
||
}
|
||
|
||
|
||
LONG
|
||
FASTCALL
|
||
NatComparePortMappingCallback(
|
||
VOID* a,
|
||
VOID* b
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the callback invoked by our sorting routine
|
||
when we ask it to sort the array of configured port-mappings.
|
||
The sorting catenates the 'Protocol' and 'PublicPort' fields
|
||
and treats the result as a 24 bit integer.
|
||
|
||
Arguments:
|
||
|
||
a - first mapping
|
||
|
||
b - second mapping
|
||
|
||
Return Value:
|
||
|
||
LONG - the result of the comparison (<0, ==0, >0).
|
||
|
||
--*/
|
||
|
||
{
|
||
return
|
||
(((PIP_NAT_PORT_MAPPING)a)->Protocol -
|
||
((PIP_NAT_PORT_MAPPING)b)->Protocol) ||
|
||
(((PIP_NAT_PORT_MAPPING)a)->PublicPort -
|
||
((PIP_NAT_PORT_MAPPING)b)->PublicPort);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NatConfigureInterface(
|
||
IN PIP_NAT_INTERFACE_INFO InterfaceInfo,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the (re)configuration of an interface on receipt
|
||
of IOCTL_IP_NAT_SET_INTERFACE_INFO from a user-mode client.
|
||
|
||
Arguments:
|
||
|
||
InterfaceInfo - contains the new configuration
|
||
|
||
FileObject - file-object of the requestor
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRTR_TOC_ENTRY Entry;
|
||
PRTR_INFO_BLOCK_HEADER Header;
|
||
ULONG i;
|
||
PIP_NAT_INTERFACE_INFO Info;
|
||
PNAT_INTERFACE Interfacep;
|
||
ULONG Index;
|
||
KIRQL Irql;
|
||
ULONG j;
|
||
ULONG Size;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
BOOLEAN WaitRequired;
|
||
|
||
CALLTRACE(("NatConfigureInterface\n"));
|
||
|
||
//
|
||
// Create a copy of the new configuration;
|
||
// we must do this before raising IRQL since 'InterfaceInfo' may be
|
||
// a pageable user-mode buffer.
|
||
//
|
||
|
||
Header = &InterfaceInfo->Header;
|
||
Size = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + Header->Size;
|
||
|
||
Info = ExAllocatePoolWithTag(NonPagedPool, Size, NAT_TAG_IF_CONFIG);
|
||
if (!Info) {
|
||
ERROR(("NatConfigureInterface: allocation failed\n"));
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
RtlCopyMemory(Info, InterfaceInfo, Size);
|
||
|
||
//
|
||
// Lookup the interface to be configured
|
||
//
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
Interfacep = NatLookupInterface(InterfaceInfo->Index, NULL);
|
||
if (!Interfacep || Interfacep->FileObject != FileObject) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
ExFreePool(Info);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
NatReferenceInterface(Interfacep);
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
NatResetInterface(Interfacep);
|
||
if (NAT_INTERFACE_FW(Interfacep)) {
|
||
ASSERT(FirewalledInterfaceCount > 0);
|
||
InterlockedDecrement(&FirewalledInterfaceCount);
|
||
}
|
||
Interfacep->Flags &= ~IP_NAT_INTERFACE_FLAGS_ALL;
|
||
Interfacep->Flags |=
|
||
(Info->Flags & IP_NAT_INTERFACE_FLAGS_ALL);
|
||
|
||
if (NAT_INTERFACE_FW(Interfacep)) {
|
||
InterlockedIncrement(&FirewalledInterfaceCount);
|
||
}
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
|
||
//
|
||
// Destroy the original configuration, if any.
|
||
//
|
||
|
||
if (Interfacep->Info) { ExFreePool(Interfacep->Info); }
|
||
Interfacep->Info = Info;
|
||
Interfacep->AddressRangeCount = 0;
|
||
Interfacep->AddressRangeArray = NULL;
|
||
Interfacep->AddressMappingCount = 0;
|
||
Interfacep->AddressMappingArray = NULL;
|
||
Interfacep->PortMappingCount = 0;
|
||
Interfacep->PortMappingArray = NULL;
|
||
|
||
Header = &Interfacep->Info->Header;
|
||
|
||
//
|
||
// Parse the new configuration
|
||
//
|
||
|
||
for (i = 0; i < Header->TocEntriesCount && NT_SUCCESS(status); i++) {
|
||
|
||
Entry = &Header->TocEntry[i];
|
||
|
||
switch (Entry->InfoType) {
|
||
|
||
case IP_NAT_ADDRESS_RANGE_TYPE: {
|
||
Interfacep->AddressRangeCount = Entry->Count;
|
||
Interfacep->AddressRangeArray =
|
||
(PIP_NAT_ADDRESS_RANGE)GetInfoFromTocEntry(Header,Entry);
|
||
break;
|
||
}
|
||
|
||
case IP_NAT_PORT_MAPPING_TYPE: {
|
||
Interfacep->PortMappingCount = Entry->Count;
|
||
Interfacep->PortMappingArray =
|
||
(PIP_NAT_PORT_MAPPING)GetInfoFromTocEntry(Header,Entry);
|
||
|
||
//
|
||
// Sort the mappings so that we can do fast lookups
|
||
// using binary search later on in the translate-path.
|
||
//
|
||
|
||
status =
|
||
ShellSort(
|
||
Interfacep->PortMappingArray,
|
||
Entry->InfoSize,
|
||
Entry->Count,
|
||
NatComparePortMappingCallback,
|
||
NULL
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
ERROR(("NatConfigureInterface: ShellSort failed\n"));
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case IP_NAT_ADDRESS_MAPPING_TYPE: {
|
||
Interfacep->AddressMappingCount = Entry->Count;
|
||
Interfacep->AddressMappingArray =
|
||
(PIP_NAT_ADDRESS_MAPPING)GetInfoFromTocEntry(Header,Entry);
|
||
|
||
//
|
||
// Sort the mappings so that we can do fast lookups
|
||
// using binary search later on in the translate-path.
|
||
//
|
||
|
||
status =
|
||
ShellSort(
|
||
Interfacep->AddressMappingArray,
|
||
Entry->InfoSize,
|
||
Entry->Count,
|
||
NatCompareAddressMappingCallback,
|
||
NULL
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
ERROR(("NatConfigureInterface: ShellSort failed\n"));
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case IP_NAT_ICMP_CONFIG_TYPE: {
|
||
Interfacep->IcmpFlags =
|
||
*(PULONG) GetInfoFromTocEntry(Header,Entry);
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
InterlockedExchange(
|
||
&Interfacep->NoStaticMappingExists,
|
||
!(Interfacep->AddressMappingCount || Interfacep->PortMappingCount)
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
status = NatCreateAddressPool(Interfacep);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
||
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
NatResetInterface(Interfacep);
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
}
|
||
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
|
||
NatDereferenceInterface(Interfacep);
|
||
|
||
return status;
|
||
|
||
} // NatConfigureInterface
|
||
|
||
|
||
USHORT
|
||
NatpGetInterfaceMTU(
|
||
ULONG index
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the MTU of a interface.
|
||
The code here is a copy-paste version of those in http.sys.
|
||
|
||
Arguments:
|
||
|
||
index - the inteface index
|
||
|
||
Return Value:
|
||
|
||
ULONG - the MTU of the specified interface
|
||
|
||
--*/
|
||
{
|
||
IFEntry *IFEntryPtr = NULL;
|
||
TDIEntityID *EntityTable = NULL, *EntityPtr = NULL;
|
||
BYTE IFBuf[sizeof(IFEntry) + MAX_IFDESCR_LEN];
|
||
TCP_REQUEST_QUERY_INFORMATION_EX ReqInBuf;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
KEVENT LocalEvent;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG InBufLen = 0, OutBufLen = 0;
|
||
TDIObjectID *ID = NULL;
|
||
USHORT InterfaceMTU = 0;
|
||
ULONG i, NumEntities = 0;
|
||
HANDLE EventHandle;
|
||
|
||
CALLTRACE(("NatpGetInterfaceMTU (0x%08X)\n", index));
|
||
|
||
if (NULL == TcpDeviceHandle) {
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Find the interface instance corresponding to the interface index
|
||
//
|
||
InBufLen = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX);
|
||
OutBufLen = sizeof(TDIEntityID) * MAX_TDI_ENTITIES;
|
||
|
||
EntityTable =
|
||
(TDIEntityID *) ExAllocatePoolWithTag(
|
||
PagedPool, OutBufLen, NAT_TAG_IF_CONFIG);
|
||
|
||
if (!EntityTable)
|
||
{
|
||
ERROR(("NatpGetInterfaceMTU: TDIEntityID Buffer Allocation Failed\n"));
|
||
return 0;
|
||
}
|
||
|
||
RtlZeroMemory(EntityTable, OutBufLen);
|
||
RtlZeroMemory(&ReqInBuf, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
|
||
|
||
ID = &(ReqInBuf.ID);
|
||
|
||
ID->toi_entity.tei_entity = GENERIC_ENTITY;
|
||
ID->toi_entity.tei_instance = 0;
|
||
ID->toi_class = INFO_CLASS_GENERIC;
|
||
ID->toi_type = INFO_TYPE_PROVIDER;
|
||
ID->toi_id = ENTITY_LIST_ID;
|
||
|
||
status = ZwCreateEvent (&EventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
|
||
if (!NT_SUCCESS(status)) {
|
||
ERROR(("NatpGetInterfaceMTU: ZwCreateEvent = 0x%08X\n", status));
|
||
if (EntityTable)
|
||
ExFreePool(EntityTable);
|
||
return 0;
|
||
}
|
||
|
||
status =
|
||
ZwDeviceIoControlFile(
|
||
TcpDeviceHandle, // FileHandle
|
||
EventHandle, // Event
|
||
NULL, // ApcRoutine
|
||
NULL, // ApcContext
|
||
&IoStatus, // IoStatusBlock
|
||
IOCTL_TCP_QUERY_INFORMATION_EX, // IoControlCode
|
||
(PVOID)&ReqInBuf, // InputBuffer
|
||
InBufLen, // InputBufferLength
|
||
(PVOID)EntityTable, // OutputBuffer
|
||
OutBufLen // OutputBufferLength
|
||
);
|
||
|
||
if ( STATUS_PENDING == status ) {
|
||
ZwWaitForSingleObject(
|
||
EventHandle,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
status = IoStatus.Status;
|
||
}
|
||
|
||
ZwResetEvent(EventHandle, NULL);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ERROR(("NatpGetInterfaceMTU: TcpQueryInformationEx = 0x%08X\n", status));
|
||
|
||
if (EntityTable)
|
||
ExFreePool(EntityTable);
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Now we have all the TDI entities.
|
||
//
|
||
NumEntities = ((ULONG)(IoStatus.Information)) / sizeof(TDIEntityID);
|
||
|
||
TRACE(XLATE, ("NatpGetInterfaceMTU: Find %d TDI entities\n", NumEntities));
|
||
|
||
// Search through the interface entries
|
||
for (i = 0, EntityPtr = EntityTable; i < NumEntities; i++, EntityPtr++)
|
||
{
|
||
if (EntityPtr->tei_entity == IF_ENTITY)
|
||
{
|
||
//
|
||
// Get the full IFEntry. It's a pitty that we only look at the
|
||
// Mtu size after getting such a big structure.
|
||
//
|
||
OutBufLen = sizeof(IFEntry) + MAX_IFDESCR_LEN;
|
||
IFEntryPtr = (IFEntry *)IFBuf;
|
||
|
||
RtlZeroMemory(IFEntryPtr, OutBufLen);
|
||
|
||
InBufLen = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX);
|
||
RtlZeroMemory(&ReqInBuf,sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
|
||
|
||
ID = &(ReqInBuf.ID);
|
||
|
||
ID->toi_entity.tei_entity = IF_ENTITY;
|
||
ID->toi_entity.tei_instance = EntityPtr->tei_instance;
|
||
ID->toi_class = INFO_CLASS_PROTOCOL;
|
||
ID->toi_type = INFO_TYPE_PROVIDER;
|
||
ID->toi_id = IF_MIB_STATS_ID;
|
||
|
||
status =
|
||
ZwDeviceIoControlFile(
|
||
TcpDeviceHandle, // FileHandle
|
||
EventHandle, // Event
|
||
NULL, // ApcRoutine
|
||
NULL, // ApcContext
|
||
&IoStatus, // IoStatusBlock
|
||
IOCTL_TCP_QUERY_INFORMATION_EX, // IoControlCode
|
||
(PVOID)&ReqInBuf, // InputBuffer
|
||
InBufLen, // InputBufferLength
|
||
(PVOID)IFEntryPtr, // OutputBuffer
|
||
OutBufLen // OutputBufferLength
|
||
);
|
||
|
||
if ( STATUS_PENDING == status ) {
|
||
ZwWaitForSingleObject(
|
||
EventHandle,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
status = IoStatus.Status;
|
||
}
|
||
|
||
ZwResetEvent(EventHandle, NULL);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ERROR(("NatpGetInterfaceMTU: TcpQueryInformationEx (2) = 0x%08X\n", status));
|
||
break;
|
||
}
|
||
|
||
if (IFEntryPtr) {
|
||
|
||
if (IFEntryPtr->if_index == index) {
|
||
//
|
||
// find the specific interface so return its MTU.
|
||
//
|
||
if (IFEntryPtr->if_mtu <= MAXUSHORT)
|
||
InterfaceMTU = (USHORT)(IFEntryPtr->if_mtu);
|
||
|
||
TRACE(
|
||
XLATE,
|
||
("NatpGetInterfaceMTU: Interface (0x%08X)'s MTU = %d\n",
|
||
index, InterfaceMTU));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (EventHandle) {
|
||
ZwClose(EventHandle);
|
||
}
|
||
|
||
if (EntityTable) {
|
||
ExFreePool(EntityTable);
|
||
}
|
||
|
||
if (MIN_VALID_MTU > InterfaceMTU) {
|
||
return 0;
|
||
} else {
|
||
return InterfaceMTU;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
NatCreateInterface(
|
||
IN PIP_NAT_CREATE_INTERFACE CreateInterface,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the creation of a NAT interface-object.
|
||
The interface is initialized and placed on the interface-list;
|
||
the configuration information is later supplied to 'NatConfigureInterface'.
|
||
|
||
Arguments:
|
||
|
||
CreateInterface - describes the interface to be created
|
||
|
||
FileObject - identifies the user-mode process associated with the interface
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_ADDRESS AddressArray;
|
||
ULONG AddressCount;
|
||
PIP_ADAPTER_BINDING_INFO BindingInfo;
|
||
ULONG i;
|
||
ULONG Index;
|
||
PLIST_ENTRY InsertionPoint;
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
USHORT mtu = 0;
|
||
|
||
CALLTRACE(("NatCreateInterface\n"));
|
||
|
||
//
|
||
// Allocate space for the interface's address.
|
||
// We do this before raising IRQL since 'CreateInterface' may be
|
||
// a pageable user-mode buffer.
|
||
//
|
||
// N.B. We allocate one more address than needed,
|
||
// to ensure that 'AddressArray[0]' can be always read
|
||
// even if there are no addresses. This allows us to optimize
|
||
// checks for locally-destined packets in 'NatpReceivePacket'
|
||
// and for locally-originated packets in 'NatpSendPacket'.
|
||
//
|
||
|
||
BindingInfo = (PIP_ADAPTER_BINDING_INFO)CreateInterface->BindingInfo;
|
||
AddressArray =
|
||
(PNAT_ADDRESS)
|
||
ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
(BindingInfo->AddressCount + 1) * sizeof(NAT_ADDRESS),
|
||
NAT_TAG_ADDRESS
|
||
);
|
||
if (!AddressArray) {
|
||
ERROR(("NatCreateInterface: address-array allocation failed\n"));
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Copy the binding information to the allocated space.
|
||
//
|
||
|
||
AddressCount = BindingInfo->AddressCount;
|
||
for (i = 0; i < BindingInfo->AddressCount; i++) {
|
||
AddressArray[i].Address = BindingInfo->Address[i].Address;
|
||
AddressArray[i].Mask = BindingInfo->Address[i].Mask;
|
||
AddressArray[i].NegatedClassMask =
|
||
~(GET_CLASS_MASK(BindingInfo->Address[i].Address));
|
||
}
|
||
|
||
//
|
||
// Obtain the MTU of this interface. If failed, set to the mininum value.
|
||
//
|
||
mtu = NatpGetInterfaceMTU(CreateInterface->Index);
|
||
|
||
//
|
||
// See if an interface with the given index exists already
|
||
//
|
||
|
||
Index = CreateInterface->Index;
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
if (NatLookupInterface(Index, &InsertionPoint)) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
ERROR(("NatCreateInterface: interface %d already exists\n", Index));
|
||
ExFreePool(AddressArray);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Allocate space for the new interface
|
||
//
|
||
|
||
Interfacep =
|
||
(PNAT_INTERFACE)
|
||
ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(NAT_INTERFACE),
|
||
NAT_TAG_INTERFACE
|
||
);
|
||
if (!Interfacep) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
ERROR(("NatCreateInterface: interface allocation failed\n"));
|
||
ExFreePool(AddressArray);
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
RtlZeroMemory(Interfacep, sizeof(NAT_INTERFACE));
|
||
|
||
//
|
||
// Initialize the interface
|
||
//
|
||
|
||
KeInitializeSpinLock(&Interfacep->Lock);
|
||
Interfacep->ReferenceCount = 1;
|
||
Interfacep->Index = Index;
|
||
Interfacep->FileObject = FileObject;
|
||
Interfacep->AddressArray = AddressArray;
|
||
Interfacep->AddressCount = AddressCount;
|
||
Interfacep->MTU = mtu;
|
||
InitializeListHead(&Interfacep->Link);
|
||
InitializeListHead(&Interfacep->UsedAddressList);
|
||
InitializeListHead(&Interfacep->MappingList);
|
||
InitializeListHead(&Interfacep->TicketList);
|
||
|
||
InsertTailList(InsertionPoint, &Interfacep->Link);
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
|
||
InterlockedIncrement(&InterfaceCount);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatCreateInterface
|
||
|
||
|
||
NTSTATUS
|
||
NatDeleteInterface(
|
||
IN ULONG Index,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles interface deletion. The interface is removed from the interface
|
||
list, and if there are no references to it, it is immediately cleaned up.
|
||
|
||
Arguments:
|
||
|
||
Index - specifies the interface to be deleted.
|
||
|
||
FileObject - indicates the file-object of the requestor
|
||
|
||
Return Value
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatDeleteInterface\n"));
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
Interfacep = NatLookupInterface(Index, NULL);
|
||
if (!Interfacep || Interfacep->FileObject != FileObject) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
RemoveEntryList(&Interfacep->Link);
|
||
InterlockedClearCache(InterfaceCache, Interfacep->Index);
|
||
Interfacep->Flags |= NAT_INTERFACE_FLAGS_DELETED;
|
||
if (NAT_INTERFACE_FW(Interfacep)) {
|
||
ASSERT(FirewalledInterfaceCount > 0);
|
||
InterlockedDecrement(&FirewalledInterfaceCount);
|
||
}
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
|
||
if (InterlockedDecrement(&Interfacep->ReferenceCount) > 0) {
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
NatCleanupInterface(Interfacep);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatDeleteInterface
|
||
|
||
|
||
VOID
|
||
NatDeleteAnyAssociatedInterface(
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to delete any interface associated with
|
||
the given file-object.
|
||
|
||
Arguments:
|
||
|
||
FileObject - the file-object to be cleaned up
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
ULONG Index;
|
||
KIRQL Irql;
|
||
PLIST_ENTRY Link;
|
||
CALLTRACE(("NatDeleteAnyAssociatedInterface\n"));
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
for (Link = InterfaceList.Flink; Link != &InterfaceList;
|
||
Link = Link->Flink) {
|
||
Interfacep = CONTAINING_RECORD(Link, NAT_INTERFACE, Link);
|
||
if (Interfacep->FileObject != FileObject) { continue; }
|
||
Index = Interfacep->Index;
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
NatDeleteInterface(Index, FileObject);
|
||
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
||
Link = &InterfaceList;
|
||
}
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
} // NatDeleteAnyAssociatedInterface
|
||
|
||
|
||
VOID
|
||
NatInitializeInterfaceManagement(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine prepares the interface-management module for operation.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
CALLTRACE(("NatInitializeInterfaceManagement\n"));
|
||
FirewalledInterfaceCount = 0;
|
||
InterfaceCount = 0;
|
||
TicketCount = 0;
|
||
KeInitializeSpinLock(&InterfaceLock);
|
||
KeInitializeSpinLock(&InterfaceMappingLock);
|
||
InitializeListHead(&InterfaceList);
|
||
InitializeCache(InterfaceCache);
|
||
|
||
} // NatInitializeInterfaceManagement
|
||
|
||
|
||
PIP_NAT_ADDRESS_MAPPING
|
||
NatLookupAddressMappingOnInterface(
|
||
IN PNAT_INTERFACE Interfacep,
|
||
IN ULONG PublicAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to look up an address-mapping on an interface.
|
||
The interface's address-mappings are stored in sorted-order, allowing
|
||
us to use binary-search to quickly locate an address-mapping.
|
||
(See 'NatConfigureInterface' for the code which does the sorting).
|
||
|
||
This routine is only of benefit in cases where there are many mappings
|
||
configured, since there is overhead involved in setting up the search.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which to perform the search
|
||
|
||
PublicAddress - the public address of the mapping to be looked up
|
||
|
||
Return Value:
|
||
|
||
PIP_NAT_ADDRESS_MAPPING - the mapping if found, NULL otherwise.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG Left = 0;
|
||
LONG Right = (LONG)Interfacep->AddressMappingCount;
|
||
LONG i;
|
||
|
||
for ( ; Left <= Right; ) {
|
||
|
||
i = Left + (Right - Left) / 2;
|
||
|
||
if (PublicAddress < Interfacep->AddressMappingArray[i].PublicAddress) {
|
||
Right = i - 1; continue;
|
||
} else if (PublicAddress >
|
||
Interfacep->AddressMappingArray[i].PublicAddress) {
|
||
Left = i + 1; continue;
|
||
}
|
||
|
||
return &Interfacep->AddressMappingArray[i];
|
||
}
|
||
|
||
return NULL;
|
||
|
||
} // NatLookupAddressMappingOnInterface
|
||
|
||
|
||
PNAT_INTERFACE
|
||
NatLookupInterface(
|
||
IN ULONG Index,
|
||
OUT PLIST_ENTRY* InsertionPoint OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to search for an interface with the given index
|
||
in our list of interfaces.
|
||
|
||
Arguments:
|
||
|
||
Index - identifies the interface to be found
|
||
|
||
InsertionPoint - optionally receives the link in the list before which
|
||
the interface would be inserted
|
||
|
||
Return Value:
|
||
|
||
PNAT_INTERFACE - the interface, if found; otherwise, NULL.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'InterfaceLock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
PLIST_ENTRY Link;
|
||
|
||
TRACE(PER_PACKET, ("NatLookupInterface\n"));
|
||
|
||
for (Link = InterfaceList.Flink; Link != &InterfaceList;
|
||
Link = Link->Flink) {
|
||
Interfacep = CONTAINING_RECORD(Link, NAT_INTERFACE, Link);
|
||
if (Interfacep->Index > Index) {
|
||
continue;
|
||
} else if (Interfacep->Index < Index) {
|
||
break;
|
||
}
|
||
return Interfacep;
|
||
}
|
||
|
||
if (InsertionPoint) { *InsertionPoint = Link; }
|
||
return NULL;
|
||
|
||
} // NatLookupInterface
|
||
|
||
|
||
PIP_NAT_PORT_MAPPING
|
||
NatLookupPortMappingOnInterface(
|
||
IN PNAT_INTERFACE Interfacep,
|
||
IN UCHAR Protocol,
|
||
IN USHORT PublicPort
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to look up a port-mapping on an interface.
|
||
The interface's port-mappings are stored in sorted-order, allowing
|
||
us to use binary-search to quickly locate a port-mapping.
|
||
(See 'NatConfigureInterface' for the code which does the sorting).
|
||
|
||
This routine is only of benefit in cases where there are many mappings
|
||
configured, since there is overhead involved in setting up the search.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which to perform the search
|
||
|
||
Protocol - the protocol of the mapping to be looked up
|
||
|
||
PublicPort - the public port of the mapping to be looked up
|
||
|
||
Return Value:
|
||
|
||
PIP_NAT_PORT_MAPPING - the mapping if found, NULL otherwise.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG Left = 0;
|
||
LONG Right = (LONG)Interfacep->PortMappingCount;
|
||
LONG i;
|
||
ULONG SearchKey, ElementKey;
|
||
|
||
SearchKey = (Protocol << 16) | PublicPort;
|
||
|
||
for ( ; Left <= Right; ) {
|
||
|
||
i = Left + (Right - Left) / 2;
|
||
|
||
ElementKey =
|
||
(Interfacep->PortMappingArray[i].Protocol << 16) |
|
||
Interfacep->PortMappingArray[i].PublicPort;
|
||
|
||
if (SearchKey < ElementKey) {
|
||
Right = i - 1; continue;
|
||
} else if (SearchKey > ElementKey) {
|
||
Left = i + 1; continue;
|
||
}
|
||
|
||
return &Interfacep->PortMappingArray[i];
|
||
}
|
||
|
||
return NULL;
|
||
|
||
} // NatLookupPortMappingOnInterface
|
||
|
||
|
||
VOID
|
||
NatMappingAttachInterface(
|
||
PNAT_INTERFACE Interfacep,
|
||
PVOID InterfaceContext,
|
||
PNAT_DYNAMIC_MAPPING Mapping
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to attach a mapping to an interface.
|
||
It serves as a notification that there is one more mapping
|
||
associated with the interface.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface for the mapping
|
||
|
||
InterfaceContext - context associated with the interface;
|
||
in our case, holds the address-pool entry in use by the mapping
|
||
|
||
Mapping - the mapping to be attached.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Always invoked at dispatch level, with 'InterfaceLock' and
|
||
'InterfaceMappingLock' held.
|
||
|
||
--*/
|
||
|
||
{
|
||
Mapping->Interfacep = Interfacep;
|
||
Mapping->InterfaceContext = InterfaceContext;
|
||
InsertTailList(&Interfacep->MappingList, &Mapping->InterfaceLink);
|
||
InterlockedIncrement(&Interfacep->Statistics.TotalMappings);
|
||
if (NAT_MAPPING_INBOUND(Mapping)) {
|
||
InterlockedIncrement(&Interfacep->Statistics.InboundMappings);
|
||
}
|
||
} // NatMappingAttachInterface
|
||
|
||
|
||
VOID
|
||
NatMappingDetachInterface(
|
||
PNAT_INTERFACE Interfacep,
|
||
PVOID InterfaceContext,
|
||
PNAT_DYNAMIC_MAPPING Mapping
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to detach a mapping from an interface.
|
||
It serves as a notification that there is one less mapping
|
||
associated with the interface.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface for the mapping
|
||
|
||
InterfaceContext - context associated with the interface;
|
||
in our case, holds the address-pool entry in use by the mapping
|
||
|
||
Mapping - the mapping to be attached, or NULL if a mapping could not be
|
||
created.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Always invoked at dispatch level, with 'InterfaceLock' and
|
||
'InterfaceMappingLock' held.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// N.B. The mapping may be NULL, e.g. if its creation failed.
|
||
// In that case we just release the address acquired for the mapping.
|
||
//
|
||
if (Mapping) {
|
||
RemoveEntryList(&Mapping->InterfaceLink);
|
||
Mapping->Interfacep = NULL;
|
||
Mapping->InterfaceContext = NULL;
|
||
if (NAT_MAPPING_INBOUND(Mapping)) {
|
||
InterlockedDecrement(&Interfacep->Statistics.InboundMappings);
|
||
}
|
||
InterlockedDecrement(&Interfacep->Statistics.TotalMappings);
|
||
}
|
||
NatDereferenceAddressPoolEntry(
|
||
Interfacep,
|
||
(PNAT_USED_ADDRESS)InterfaceContext
|
||
);
|
||
} // NatMappingDetachInterface
|
||
|
||
|
||
NTSTATUS
|
||
NatQueryInformationInterface(
|
||
IN ULONG Index,
|
||
IN PIP_NAT_INTERFACE_INFO InterfaceInfo,
|
||
IN PULONG Size
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called to construct the optional information in use on the interface.
|
||
|
||
Arguments:
|
||
|
||
Index - identifies the interface
|
||
|
||
InterfaceInfo - receives the retrieved configuration
|
||
|
||
Size - the size of the given buffer
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if retrieved, STATUS_BUFFER_TOO_SMALL if '*Size' is too
|
||
small, error otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRTR_INFO_BLOCK_HEADER Header;
|
||
ULONG InfoSize;
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PVOID Temp;
|
||
|
||
CALLTRACE(("NatQueryInformationInterface\n"));
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
Interfacep = NatLookupInterface(Index, NULL);
|
||
if (!Interfacep) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
NatReferenceInterface(Interfacep);
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
Header = &Interfacep->Info->Header;
|
||
InfoSize = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + Header->Size;
|
||
if (*Size < InfoSize) {
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
} else {
|
||
//
|
||
// In transferring the requested information, we must be careful
|
||
// because the output-buffer may be a pageable user-mode address.
|
||
// We cannot take a page-fault while holding the interface's lock
|
||
// at dispatch level, so we make a non-paged copy of the information,
|
||
// release the interface's lock to return to passive level,
|
||
// and then copy the information to the caller's buffer.
|
||
//
|
||
Temp = ExAllocatePoolWithTag(NonPagedPool, InfoSize, NAT_TAG_IF_CONFIG);
|
||
if (!Temp) {
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
status = STATUS_NO_MEMORY;
|
||
InfoSize = 0;
|
||
} else {
|
||
RtlCopyMemory(Temp, Interfacep->Info, InfoSize);
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
__try {
|
||
RtlCopyMemory(InterfaceInfo, Temp, InfoSize);
|
||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
status = GetExceptionCode();
|
||
}
|
||
ExFreePool(Temp);
|
||
}
|
||
}
|
||
*Size = InfoSize;
|
||
|
||
NatDereferenceInterface(Interfacep);
|
||
|
||
return status;
|
||
|
||
} // NatQueryInformationInterface
|
||
|
||
|
||
NTSTATUS
|
||
NatQueryStatisticsInterface(
|
||
ULONG Index,
|
||
IN PIP_NAT_INTERFACE_STATISTICS InterfaceStatistics
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to copy the statistics for an interface.
|
||
Note that we do not need to lock the interface to access the statistics,
|
||
since they are all updated using interlocked operations.
|
||
|
||
Arguments:
|
||
|
||
Index - identifies the interface
|
||
|
||
InterfaceStatistics - i/o buffer used for transfer of information
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, error code otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
NTSTATUS Status;
|
||
|
||
CALLTRACE(("NatQueryStatisticsInterface\n"));
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
Interfacep = NatLookupInterface(Index, NULL);
|
||
if (!Interfacep) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
NatReferenceInterface(Interfacep);
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
|
||
//
|
||
// Copy the statistics to the caller's buffer
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
__try {
|
||
*InterfaceStatistics = Interfacep->Statistics;
|
||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
NatDereferenceInterface(Interfacep);
|
||
return Status;
|
||
|
||
} // NatQueryStatisticsInterface
|
||
|
||
|
||
VOID
|
||
NatResetInterface(
|
||
IN PNAT_INTERFACE Interfacep
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to destroy all structures hanging off an interface.
|
||
It is used when reconfiguring or cleaning up an interface.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface to be reset.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'InterfaceLock' held by the caller, and
|
||
(a) 'Interfacep->Lock' also held by the caller, or
|
||
(b) the last reference to the interface released.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY List;
|
||
PLIST_ENTRY Link;
|
||
PNAT_IP_MAPPING IpMapping;
|
||
KIRQL Irql;
|
||
PNAT_DYNAMIC_MAPPING Mapping;
|
||
PNAT_TICKET Ticket;
|
||
|
||
CALLTRACE(("NatResetInterface\n"));
|
||
|
||
//
|
||
// Clean out the interface's dynamic mappings
|
||
//
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&InterfaceMappingLock);
|
||
List = &Interfacep->MappingList;
|
||
while (!IsListEmpty(List)) {
|
||
Mapping =
|
||
CONTAINING_RECORD(List->Flink, NAT_DYNAMIC_MAPPING, InterfaceLink);
|
||
NatExpireMapping(Mapping);
|
||
NatMappingDetachInterface(
|
||
Interfacep, Mapping->InterfaceContext, Mapping
|
||
);
|
||
}
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
|
||
|
||
//
|
||
// Clean out the interface's tickets
|
||
//
|
||
|
||
List = &Interfacep->TicketList;
|
||
while (!IsListEmpty(List)) {
|
||
Ticket = CONTAINING_RECORD(List->Flink, NAT_TICKET, Link);
|
||
NatDeleteTicket(Interfacep, Ticket);
|
||
}
|
||
|
||
//
|
||
// Clean out the interface's address-pool and port-pool
|
||
//
|
||
|
||
NatDeleteAddressPool(Interfacep);
|
||
|
||
} // NatResetInterface
|
||
|
||
|
||
VOID
|
||
NatShutdownInterfaceManagement(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine shuts down the interface-management module.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
CALLTRACE(("NatShutdownInterfaceManagement\n"));
|
||
|
||
//
|
||
// Delete all interfaces
|
||
//
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
while (!IsListEmpty(&InterfaceList)) {
|
||
Interfacep =
|
||
CONTAINING_RECORD(InterfaceList.Flink, NAT_INTERFACE, Link);
|
||
RemoveEntryList(&Interfacep->Link);
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
NatCleanupInterface(Interfacep);
|
||
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
||
}
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
InterfaceCount = 0;
|
||
TicketCount = 0;
|
||
|
||
} // NatShutdownInterfaceManagement
|
||
|
||
|