windows-nt/Source/XPSP1/NT/net/rras/ip/nat/if.c
2020-09-26 16:20:57 +08:00

1385 lines
34 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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