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.
1384 lines
35 KiB
1384 lines
35 KiB
/*++
|
|
|
|
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);
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} 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
|
|
|
|
|