windows-nt/Source/XPSP1/NT/net/rras/ip/nat/mapping.c

2210 lines
60 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
mapping.c
Abstract:
This file contains the code for mapping management.
Author:
Abolade Gbadegesin (t-abolag) 11-July-1997
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// GLOBAL VARIABLE DEFINITIONS
//
ULONG ExpiredMappingCount;
CACHE_ENTRY MappingCache[NatMaximumPath][CACHE_SIZE];
ULONG MappingCount;
LIST_ENTRY MappingList;
KSPIN_LOCK MappingLock;
NPAGED_LOOKASIDE_LIST MappingLookasideList;
PNAT_DYNAMIC_MAPPING MappingTree[NatMaximumPath];
PVOID
NatAllocateFunction(
POOL_TYPE PoolType,
SIZE_T NumberOfBytes,
ULONG Tag
)
/*++
Routine Description:
Called by lookaside lists to allocate memory from the low-priority
pool.
Arguments:
PoolType - the pool to allocate from (e.g., non-paged)
NumberOfBytes - the number of bytes to allocate
Tag - the tag for the allocation
Return Value:
PVOID - pointer to the allocated memory, or NULL for failure.
--*/
{
return
ExAllocatePoolWithTagPriority(
PoolType,
NumberOfBytes,
Tag,
LowPoolPriority
);
} // NatAllocateFunction
VOID
NatCleanupMapping(
PNAT_DYNAMIC_MAPPING Mapping
)
/*++
Routine Description:
Called to perform final cleanup for a mapping.
Arguments:
Mapping - the mapping to be deleted.
Return Value:
none.
Environment:
Invoked with the last reference to the mapping released.
--*/
{
KIRQL Irql;
CALLTRACE(("NatCleanupMapping\n"));
//
// The last reference to the mapping has been released;
//
// Let the mapping's director know that it's expired
//
KeAcquireSpinLock(&DirectorLock, &Irql);
if (Mapping->Director) {
KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock);
NatMappingDetachDirector(
Mapping->Director,
Mapping->DirectorContext,
Mapping,
NatCleanupSessionDeleteReason
);
KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock);
}
KeReleaseSpinLockFromDpcLevel(&DirectorLock);
//
// Let the mapping's editor know that it's expired
//
KeAcquireSpinLockAtDpcLevel(&EditorLock);
if (Mapping->Editor) {
KeAcquireSpinLockAtDpcLevel(&EditorMappingLock);
NatMappingDetachEditor(Mapping->Editor, Mapping);
KeReleaseSpinLockFromDpcLevel(&EditorMappingLock);
}
KeReleaseSpinLockFromDpcLevel(&EditorLock);
//
// If the mapping is associated with an address-pool, release the address,
// and update the statistics for the mapping's interface
//
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
if (Mapping->Interfacep) {
KeAcquireSpinLockAtDpcLevel(&InterfaceMappingLock);
NatMappingDetachInterface(
Mapping->Interfacep, Mapping->InterfaceContext, Mapping
);
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
}
KeReleaseSpinLock(&InterfaceLock, Irql);
//
// Clear the location the mapping would occupy in the cache,
// in case it is cached.
//
ClearCache(
MappingCache[NatForwardPath],
(ULONG)Mapping->DestinationKey[NatForwardPath]
);
ClearCache(
MappingCache[NatReversePath],
(ULONG)Mapping->DestinationKey[NatReversePath]
);
FREE_MAPPING_BLOCK(Mapping);
} // NatCleanupMapping
NTSTATUS
NatCreateMapping(
ULONG Flags,
ULONG64 DestinationKey[],
ULONG64 SourceKey[],
PNAT_INTERFACE Interfacep,
PVOID InterfaceContext,
USHORT MaxMSS,
PNAT_DIRECTOR Director,
PVOID DirectorSessionContext,
PNAT_DYNAMIC_MAPPING* ForwardInsertionPoint,
PNAT_DYNAMIC_MAPPING* ReverseInsertionPoint,
PNAT_DYNAMIC_MAPPING* MappingCreated
)
/*++
Routine Description:
This function initializes the fields of a mapping.
On returning, the routine will have made an initial reference
to the mapping. The caller must call 'NatDereferenceMapping'
to release this reference.
Arguments:
Flags - controls creation of the mapping
DestinationKey[] - the forward and reverse destination-endpoint keys
SourceKey[] - the forward and reverse source-endpoint keys
Interface* - the interface, if any, with which the mapping is associated,
and the associated context
MaxMSS - the maximum MSS value allowed on the outgoing interface.
Director* - the director, if any, with which the mapping is associated,
and the associated context
ForwardInsertionPoint - optionally supplies the point of insertion
in the forward-lookup mapping-tree
ReverseInsertionPoint - optionally supplies the point of insertion
in the reverse-lookup mapping-tree
MappingCreated - receives the mapping created.
Return Value:
none.
Environment:
Invoked with 'MappingLock' held by the caller, and with both 'Interfacep'
and 'Director' referenced, if specified.
--*/
{
PNAT_EDITOR Editor;
ULONG InboundKey;
PNAT_DYNAMIC_MAPPING InsertionPoint1;
PNAT_DYNAMIC_MAPPING InsertionPoint2;
ULONG InterfaceFlags;
PLIST_ENTRY Link;
PNAT_DYNAMIC_MAPPING Mapping;
ULONG OutboundKey;
UCHAR Protocol;
PRTL_SPLAY_LINKS SLink;
NTSTATUS status;
BOOLEAN FirewallMode = FALSE;
CALLTRACE(("NatCreateMapping\n"));
//
// Allocate the memory for the new block
//
Mapping = ALLOCATE_MAPPING_BLOCK();
if (!Mapping) {
ERROR(("NatCreateMapping: allocation failed\n"));
if (Interfacep) {
NatMappingDetachInterface(Interfacep, InterfaceContext, NULL);
}
if (Director) {
NatMappingDetachDirector(
Director,
DirectorSessionContext,
NULL,
NatCreateFailureDeleteReason
);
}
return STATUS_NO_MEMORY;
}
RtlZeroMemory(Mapping, sizeof(*Mapping));
KeInitializeSpinLock(&Mapping->Lock);
Mapping->ReferenceCount = 1;
Mapping->Flags = Flags;
Mapping->MaxMSS = MaxMSS;
Mapping->DestinationKey[NatForwardPath] = DestinationKey[NatForwardPath];
Mapping->DestinationKey[NatReversePath] = DestinationKey[NatReversePath];
Mapping->SourceKey[NatForwardPath] = SourceKey[NatForwardPath];
Mapping->SourceKey[NatReversePath] = SourceKey[NatReversePath];
Mapping->AccessCount[NatForwardPath] = NAT_MAPPING_RESPLAY_THRESHOLD;
Mapping->AccessCount[NatReversePath] = NAT_MAPPING_RESPLAY_THRESHOLD;
InitializeListHead(&Mapping->Link);
InitializeListHead(&Mapping->DirectorLink);
InitializeListHead(&Mapping->EditorLink);
InitializeListHead(&Mapping->InterfaceLink);
RtlInitializeSplayLinks(&Mapping->SLink[NatForwardPath]);
RtlInitializeSplayLinks(&Mapping->SLink[NatReversePath]);
Protocol = MAPPING_PROTOCOL(DestinationKey[0]);
if (SourceKey[NatForwardPath] == DestinationKey[NatReversePath]
&& DestinationKey[NatForwardPath] == SourceKey[NatReversePath]) {
//
// This mapping is being created for firewall puposes -- no actual
// translation needs to be performed. Knowing this, we can use
// different translation routines and save us some time...
//
TRACE(MAPPING,("NAT: Creating FW null mapping\n"));
FirewallMode = TRUE;
if (Protocol == NAT_PROTOCOL_TCP) {
Mapping->TranslateRoutine[NatForwardPath] = NatTranslateForwardTcpNull;
Mapping->TranslateRoutine[NatReversePath] = NatTranslateReverseTcpNull;
} else {
Mapping->TranslateRoutine[NatForwardPath] = NatTranslateForwardUdpNull;
Mapping->TranslateRoutine[NatReversePath] = NatTranslateReverseUdpNull;
}
} else if (Protocol == NAT_PROTOCOL_TCP) {
Mapping->TranslateRoutine[NatForwardPath] = NatTranslateForwardTcp;
Mapping->TranslateRoutine[NatReversePath] = NatTranslateReverseTcp;
} else {
Mapping->TranslateRoutine[NatForwardPath] = NatTranslateForwardUdp;
Mapping->TranslateRoutine[NatReversePath] = NatTranslateReverseUdp;
}
//
// Increment the reference count on the mapping;
// the caller should then do a 'Dereference'.
//
++Mapping->ReferenceCount;
//
// Attach the mapping to its interface, if any
//
if (Interfacep) {
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
KeAcquireSpinLockAtDpcLevel(&InterfaceMappingLock);
NatMappingAttachInterface(Interfacep, InterfaceContext, Mapping);
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
InterfaceFlags = Interfacep->Flags;
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
}
//
// Attach the mapping to its director, if any
//
if (Director) {
KeAcquireSpinLockAtDpcLevel(&DirectorLock);
KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock);
NatMappingAttachDirector(Director, DirectorSessionContext, Mapping);
KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock);
KeReleaseSpinLockFromDpcLevel(&DirectorLock);
}
//
// We now set up any editors interested in this session,
// if the mapping is associated with a boundary interface.
//
if (Interfacep) {
InboundKey =
MAKE_EDITOR_KEY(
Protocol,
MAPPING_PORT(DestinationKey[NatForwardPath]),
NatInboundDirection
);
OutboundKey =
MAKE_EDITOR_KEY(
Protocol,
MAPPING_PORT(DestinationKey[NatForwardPath]),
NatOutboundDirection
);
KeAcquireSpinLockAtDpcLevel(&EditorLock);
for (Link = EditorList.Flink; Link != &EditorList; Link = Link->Flink) {
Editor = CONTAINING_RECORD(Link, NAT_EDITOR, Link);
//
// Skip any built-in editors that are administratively disabled.
//
if (((InterfaceFlags & IP_NAT_INTERFACE_FLAGS_DISABLE_PPTP) &&
(PVOID)Editor == PptpRegisterEditorClient.EditorHandle) ||
((InterfaceFlags & IP_NAT_INTERFACE_FLAGS_DISABLE_PPTP) &&
(PVOID)Editor == PptpRegisterEditorServer.EditorHandle)) {
continue;
}
if (Editor->Key == InboundKey && NAT_MAPPING_INBOUND(Mapping)) {
KeAcquireSpinLockAtDpcLevel(&EditorMappingLock);
NatMappingAttachEditor(Editor, Mapping);
KeReleaseSpinLockFromDpcLevel(&EditorMappingLock);
//
// Update the mapping's translation-routine table
//
if (Protocol == NAT_PROTOCOL_UDP) {
Mapping->TranslateRoutine[NatForwardPath] =
NatTranslateForwardUdpEdit;
Mapping->TranslateRoutine[NatReversePath] =
NatTranslateReverseUdpEdit;
} else if (!NAT_EDITOR_RESIZE(Editor)) {
Mapping->TranslateRoutine[NatForwardPath] =
NatTranslateForwardTcpEdit;
Mapping->TranslateRoutine[NatReversePath] =
NatTranslateReverseTcpEdit;
} else {
Mapping->TranslateRoutine[NatForwardPath] =
NatTranslateForwardTcpResize;
Mapping->TranslateRoutine[NatReversePath] =
NatTranslateReverseTcpResize;
}
break;
} else if (Editor->Key == OutboundKey &&
!NAT_MAPPING_INBOUND(Mapping)) {
KeAcquireSpinLockAtDpcLevel(&EditorMappingLock);
NatMappingAttachEditor(Editor, Mapping);
KeReleaseSpinLockFromDpcLevel(&EditorMappingLock);
//
// Update the mapping's translation-routine table
//
if (Protocol == NAT_PROTOCOL_UDP) {
Mapping->TranslateRoutine[NatForwardPath] =
NatTranslateForwardUdpEdit;
Mapping->TranslateRoutine[NatReversePath] =
NatTranslateReverseUdpEdit;
} else if (!NAT_EDITOR_RESIZE(Editor)) {
Mapping->TranslateRoutine[NatForwardPath] =
NatTranslateForwardTcpEdit;
Mapping->TranslateRoutine[NatReversePath] =
NatTranslateReverseTcpEdit;
} else {
Mapping->TranslateRoutine[NatForwardPath] =
NatTranslateForwardTcpResize;
Mapping->TranslateRoutine[NatReversePath] =
NatTranslateReverseTcpResize;
}
break;
}
}
KeReleaseSpinLockFromDpcLevel(&EditorLock);
} // Interfacep
if (!FirewallMode) {
//
// Initialize the checksum deltas;
// See RFC1624 for details on the incremental update of the checksum;
//
Mapping->IpChecksumDelta[NatForwardPath] =
(USHORT)~MAPPING_ADDRESS(SourceKey[NatForwardPath]) +
(USHORT)~(MAPPING_ADDRESS(SourceKey[NatForwardPath]) >> 16) +
(USHORT)~MAPPING_ADDRESS(DestinationKey[NatForwardPath]) +
(USHORT)~(MAPPING_ADDRESS(DestinationKey[NatForwardPath]) >> 16) +
(USHORT)MAPPING_ADDRESS(SourceKey[NatReversePath]) +
(USHORT)(MAPPING_ADDRESS(SourceKey[NatReversePath]) >> 16) +
(USHORT)MAPPING_ADDRESS(DestinationKey[NatReversePath]) +
(USHORT)(MAPPING_ADDRESS(DestinationKey[NatReversePath]) >> 16);
Mapping->IpChecksumDelta[NatReversePath] =
(USHORT)MAPPING_ADDRESS(SourceKey[NatForwardPath]) +
(USHORT)(MAPPING_ADDRESS(SourceKey[NatForwardPath]) >> 16) +
(USHORT)MAPPING_ADDRESS(DestinationKey[NatForwardPath]) +
(USHORT)(MAPPING_ADDRESS(DestinationKey[NatForwardPath]) >> 16) +
(USHORT)~MAPPING_ADDRESS(SourceKey[NatReversePath]) +
(USHORT)~(MAPPING_ADDRESS(SourceKey[NatReversePath]) >> 16) +
(USHORT)~MAPPING_ADDRESS(DestinationKey[NatReversePath]) +
(USHORT)~(MAPPING_ADDRESS(DestinationKey[NatReversePath]) >> 16);
Mapping->ProtocolChecksumDelta[NatForwardPath] =
Mapping->IpChecksumDelta[NatForwardPath] +
(USHORT)~MAPPING_PORT(SourceKey[NatForwardPath]) +
(USHORT)~MAPPING_PORT(DestinationKey[NatForwardPath]) +
(USHORT)MAPPING_PORT(SourceKey[NatReversePath]) +
(USHORT)MAPPING_PORT(DestinationKey[NatReversePath]);
Mapping->ProtocolChecksumDelta[NatReversePath] =
Mapping->IpChecksumDelta[NatReversePath] +
(USHORT)MAPPING_PORT(SourceKey[NatForwardPath]) +
(USHORT)MAPPING_PORT(DestinationKey[NatForwardPath]) +
(USHORT)~MAPPING_PORT(SourceKey[NatReversePath]) +
(USHORT)~MAPPING_PORT(DestinationKey[NatReversePath]);
//
// If the mapping has the loopback address as the source on either
// path set NAT_MAPPING_FLAG_CLEAR_DF_BIT. When we change the source
// address of a packet from the loopback address to some other address
// there is the possibility that we'll create an MTU mismatch that
// would result in the packet getting dropped by the stack if the
// DF bit was sent. (Note that since this would occur on the local-send
// path no ICMP error message would be generated.)
//
// Clearing the DF bit for these packets ensures that the stack will
// succeed in sending the packet, though some performance may be
// lost due to the fragmentation.
//
if (MAPPING_ADDRESS(SourceKey[NatForwardPath]) == 0x0100007f ||
MAPPING_ADDRESS(SourceKey[NatReversePath]) == 0x0100007f) {
Mapping->Flags |= NAT_MAPPING_FLAG_CLEAR_DF_BIT;
}
}
//
// Find the insertion points, in the process checking for collisions
//
if (!ForwardInsertionPoint) {
ForwardInsertionPoint = &InsertionPoint1;
if (NatLookupForwardMapping(
DestinationKey[NatForwardPath],
SourceKey[NatForwardPath],
ForwardInsertionPoint
)) {
//
// A collision has been detected.
//
NatCleanupMapping(Mapping);
return STATUS_UNSUCCESSFUL;
}
}
if (!ReverseInsertionPoint) {
ReverseInsertionPoint = &InsertionPoint2;
if (NatLookupReverseMapping(
DestinationKey[NatReversePath],
SourceKey[NatReversePath],
ReverseInsertionPoint
)) {
//
// A collision has been detected.
//
NatCleanupMapping(Mapping);
return STATUS_UNSUCCESSFUL;
}
}
MappingTree[NatForwardPath] =
NatInsertForwardMapping(*ForwardInsertionPoint, Mapping);
MappingTree[NatReversePath] =
NatInsertReverseMapping(*ReverseInsertionPoint, Mapping);
InsertTailList(&MappingList, &Mapping->Link);
InterlockedIncrement(&MappingCount);
*MappingCreated = Mapping;
#if NAT_WMI
//
// Log the creation. Logging always uses public addresses,
// not private.
//
if (!NAT_MAPPING_DO_NOT_LOG(Mapping)) {
if (NAT_MAPPING_INBOUND(Mapping)) {
NatLogConnectionCreation(
MAPPING_ADDRESS(DestinationKey[NatForwardPath]),
MAPPING_ADDRESS(SourceKey[NatForwardPath]),
MAPPING_PORT(DestinationKey[NatForwardPath]),
MAPPING_PORT(SourceKey[NatForwardPath]),
Protocol,
TRUE
);
} else {
NatLogConnectionCreation(
MAPPING_ADDRESS(DestinationKey[NatReversePath]),
MAPPING_ADDRESS(SourceKey[NatReversePath]),
MAPPING_PORT(DestinationKey[NatReversePath]),
MAPPING_PORT(SourceKey[NatReversePath]),
Protocol,
FALSE
);
}
}
#endif
return STATUS_SUCCESS;
} // NatCreateMapping
NTSTATUS
NatDeleteMapping(
PNAT_DYNAMIC_MAPPING Mapping
)
/*++
Routine Description:
Called to delete a mapping from an interface. The initial reference
to the mapping is released, so that cleanup occurs whenever the last
reference is released.
Arguments:
Mapping - the mapping to be deleted.
Return Value:
NTSTATUS - indicates success/failure.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
KIRQL Irql;
PRTL_SPLAY_LINKS SLink;
CALLTRACE(("NatDeleteMapping\n"));
if (NAT_MAPPING_DELETED(Mapping)) { return STATUS_PENDING; }
//
// Mark the mapping as deleted so attempts to reference it
// will fail from now on.
//
Mapping->Flags |= NAT_MAPPING_FLAG_DELETED;
//
// Take the mapping off the list and splay-trees
//
InterlockedDecrement(&MappingCount);
RemoveEntryList(&Mapping->Link);
if (NAT_MAPPING_EXPIRED(Mapping)) {
InterlockedDecrement(&ExpiredMappingCount);
}
SLink = RtlDelete(&Mapping->SLink[NatForwardPath]);
MappingTree[NatForwardPath] =
(SLink
? CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatForwardPath])
: NULL);
SLink = RtlDelete(&Mapping->SLink[NatReversePath]);
MappingTree[NatReversePath] =
(SLink
? CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatReversePath])
: NULL);
#if NAT_WMI
//
// Log the deletion. Logging always uses public addresses,
// not private.
//
if (!NAT_MAPPING_DO_NOT_LOG(Mapping)) {
if (NAT_MAPPING_INBOUND(Mapping)) {
NatLogConnectionDeletion(
MAPPING_ADDRESS(Mapping->DestinationKey[NatForwardPath]),
MAPPING_ADDRESS(Mapping->SourceKey[NatForwardPath]),
MAPPING_PORT(Mapping->DestinationKey[NatForwardPath]),
MAPPING_PORT(Mapping->SourceKey[NatForwardPath]),
MAPPING_PROTOCOL(Mapping->SourceKey[NatForwardPath]),
TRUE
);
} else {
NatLogConnectionDeletion(
MAPPING_ADDRESS(Mapping->DestinationKey[NatReversePath]),
MAPPING_ADDRESS(Mapping->SourceKey[NatReversePath]),
MAPPING_PORT(Mapping->DestinationKey[NatReversePath]),
MAPPING_PORT(Mapping->SourceKey[NatReversePath]),
MAPPING_PROTOCOL(Mapping->SourceKey[NatForwardPath]),
FALSE
);
}
}
#endif
if (InterlockedDecrement(&Mapping->ReferenceCount) > 0) {
//
// The mapping is in use, defer final cleanup
//
return STATUS_PENDING;
}
//
// Go ahead with final cleanup
//
NatCleanupMapping(Mapping);
return STATUS_SUCCESS;
} // NatDeleteMapping
PNAT_DYNAMIC_MAPPING
NatDestinationLookupForwardMapping(
ULONG64 DestinationKey
)
/*++
Routine Description:
This routine retrieves the mapping which matches the given destination
key. The source key of the mapping is not examined.
Arguments:
DestinationKey - the primary key used to search for a mapping.
Return Value:
PNAT_DYNAMIC_MAPPING - the item found, or NULL if no match is found
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PNAT_DYNAMIC_MAPPING Root;
PNAT_DYNAMIC_MAPPING Mapping;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatDestinationLookupForwardMapping\n"));
//
// First look in the mapping-cache
//
if ((Mapping =
(PNAT_DYNAMIC_MAPPING)ProbeCache(
MappingCache[NatForwardPath],
(ULONG)DestinationKey
)) &&
Mapping->DestinationKey[NatForwardPath] == DestinationKey
) {
TRACE(PER_PACKET, ("NatDestinationLookupForwardMapping: cache hit\n"));
return Mapping;
}
//
// Search the full tree
//
Root = MappingTree[NatForwardPath];
for (SLink = !Root ? NULL : &Root->SLink[NatForwardPath]; SLink; ) {
Mapping =
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatForwardPath]);
if (DestinationKey < Mapping->DestinationKey[NatForwardPath]) {
SLink = RtlLeftChild(SLink);
continue;
} else if (DestinationKey > Mapping->DestinationKey[NatForwardPath]) {
SLink = RtlRightChild(SLink);
continue;
}
//
// We found the mapping. We don't update the cache for partial
// lookups.
//
return Mapping;
}
//
// No partial match was found
//
return NULL;
} // NatDestinationLookupForwardMapping
PNAT_DYNAMIC_MAPPING
NatDestinationLookupReverseMapping(
ULONG64 DestinationKey
)
/*++
Routine Description:
This routine retrieves the mapping which matches the given destination
key. The source key of the mapping is not examined.
Arguments:
DestinationKey - the primary key used to search for a mapping.
Return Value:
PNAT_DYNAMIC_MAPPING - the item found, or NULL if no match is found
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PNAT_DYNAMIC_MAPPING Root;
PNAT_DYNAMIC_MAPPING Mapping;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatDestinationLookupReverseMapping\n"));
//
// First look in the mapping-cache
//
if ((Mapping =
(PNAT_DYNAMIC_MAPPING)ProbeCache(
MappingCache[NatReversePath],
(ULONG)DestinationKey
)) &&
Mapping->DestinationKey[NatReversePath] == DestinationKey
) {
TRACE(PER_PACKET, ("NatDestinationLookupReverseMapping: cache hit\n"));
return Mapping;
}
//
// Search the full tree
//
Root = MappingTree[NatReversePath];
for (SLink = !Root ? NULL : &Root->SLink[NatReversePath]; SLink; ) {
Mapping =
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatReversePath]);
if (DestinationKey < Mapping->DestinationKey[NatReversePath]) {
SLink = RtlLeftChild(SLink);
continue;
} else if (DestinationKey > Mapping->DestinationKey[NatReversePath]) {
SLink = RtlRightChild(SLink);
continue;
}
//
// We found the mapping. We don't update the cache for partial
// lookups.
//
return Mapping;
}
//
// No partial match was found
//
return NULL;
} // NatDestinationLookupReverseMapping
VOID
NatInitializeMappingManagement(
VOID
)
/*++
Routine Description:
This routine is invoked to initialize the NAT's mapping-management module.
Arguments:
none.
Return Value:
none.
--*/
{
CALLTRACE(("NatInitializeMappingManagement\n"));
MappingCount = 0;
ExpiredMappingCount = 0;
InitializeListHead(&MappingList);
KeInitializeSpinLock(&MappingLock);
MappingTree[NatForwardPath] = NULL;
MappingTree[NatReversePath] = NULL;
InitializeCache(MappingCache[NatForwardPath]);
InitializeCache(MappingCache[NatReversePath]);
ExInitializeNPagedLookasideList(
&MappingLookasideList,
NatAllocateFunction,
NULL,
0,
sizeof(NAT_DYNAMIC_MAPPING),
NAT_TAG_MAPPING,
MAPPING_LOOKASIDE_DEPTH
);
} // NatInitializeMappingManagement
PNAT_DYNAMIC_MAPPING
NatInsertForwardMapping(
PNAT_DYNAMIC_MAPPING Parent,
PNAT_DYNAMIC_MAPPING Mapping
)
/*++
Routine Description:
This routine inserts a mapping into the tree.
Arguments:
Parent - the node to be the parent for the new mapping.
If NULL, the new mapping becomes the root.
Mapping - the new mapping to be inserted.
Return Value:
PNAT_DYNAMIC_MAPPING - The new root of the tree.
If insertion fails, returns NULL.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PRTL_SPLAY_LINKS Root;
CALLTRACE(("NatInsertForwardMapping\n"));
if (!Parent) {
TRACE(MAPPING, ("NatInsertForwardMapping: inserting as root\n"));
return Mapping;
}
//
// Insert as left or right child
//
if (Mapping->DestinationKey[NatForwardPath] <
Parent->DestinationKey[NatForwardPath]) {
RtlInsertAsLeftChild(
&Parent->SLink[NatForwardPath], &Mapping->SLink[NatForwardPath]
);
} else if (Mapping->DestinationKey[NatForwardPath] >
Parent->DestinationKey[NatForwardPath]) {
RtlInsertAsRightChild(
&Parent->SLink[NatForwardPath], &Mapping->SLink[NatForwardPath]
);
} else {
//
// Primary keys are equal; check secondary keys
//
if (Mapping->SourceKey[NatForwardPath] <
Parent->SourceKey[NatForwardPath]) {
RtlInsertAsLeftChild(
&Parent->SLink[NatForwardPath], &Mapping->SLink[NatForwardPath]
);
} else if (Mapping->SourceKey[NatForwardPath] >
Parent->SourceKey[NatForwardPath]) {
RtlInsertAsRightChild(
&Parent->SLink[NatForwardPath], &Mapping->SLink[NatForwardPath]
);
} else {
//
// Secondary keys equal too; fail.
//
ERROR((
"NatInsertForwardMapping: collision 0x%016I64X,0x%016I64X\n",
Mapping->DestinationKey[NatForwardPath],
Mapping->SourceKey[NatForwardPath]
));
return NULL;
}
}
//
// Splay the new node and return the resulting root.
//
Root = RtlSplay(&Mapping->SLink[NatForwardPath]);
return CONTAINING_RECORD(Root, NAT_DYNAMIC_MAPPING, SLink[NatForwardPath]);
} // NatInsertForwardMapping
PNAT_DYNAMIC_MAPPING
NatInsertReverseMapping(
PNAT_DYNAMIC_MAPPING Parent,
PNAT_DYNAMIC_MAPPING Mapping
)
/*++
Routine Description:
This routine inserts a mapping into the tree.
Arguments:
Parent - the node to be the parent for the new mapping.
If NULL, the new mapping becomes the root.
Mapping - the new mapping to be inserted.
Return Value:
PNAT_DYNAMIC_MAPPING - The new root of the tree.
If insertion fails, returns NULL.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PRTL_SPLAY_LINKS Root;
CALLTRACE(("NatInsertReverseMapping\n"));
if (!Parent) {
TRACE(MAPPING, ("NatInsertReverseMapping: inserting as root\n"));
return Mapping;
}
//
// Insert as left or right child
//
if (Mapping->DestinationKey[NatReversePath] <
Parent->DestinationKey[NatReversePath]) {
RtlInsertAsLeftChild(
&Parent->SLink[NatReversePath], &Mapping->SLink[NatReversePath]
);
} else if (Mapping->DestinationKey[NatReversePath] >
Parent->DestinationKey[NatReversePath]) {
RtlInsertAsRightChild(
&Parent->SLink[NatReversePath], &Mapping->SLink[NatReversePath]
);
} else {
//
// Primary keys are equal; check secondary keys
//
if (Mapping->SourceKey[NatReversePath] <
Parent->SourceKey[NatReversePath]) {
RtlInsertAsLeftChild(
&Parent->SLink[NatReversePath], &Mapping->SLink[NatReversePath]
);
} else if (Mapping->SourceKey[NatReversePath] >
Parent->SourceKey[NatReversePath]) {
RtlInsertAsRightChild(
&Parent->SLink[NatReversePath], &Mapping->SLink[NatReversePath]
);
} else {
//
// Secondary keys equal too; fail.
//
ERROR((
"NatInsertReverseMapping: collision 0x%016I64X,0x%016I64X\n",
Mapping->DestinationKey[NatReversePath],
Mapping->SourceKey[NatReversePath]
));
return NULL;
}
}
//
// Splay the new node and return the resulting root.
//
Root = RtlSplay(&Mapping->SLink[NatReversePath]);
return CONTAINING_RECORD(Root, NAT_DYNAMIC_MAPPING, SLink[NatReversePath]);
} // NatInsertReverseMapping
NTSTATUS
NatLookupAndQueryInformationMapping(
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
OUT PVOID Information,
ULONG InformationLength,
NAT_SESSION_MAPPING_INFORMATION_CLASS InformationClass
)
/*++
Routine Description:
This routine attempts to locate a particular session mapping using either
its forward key or reverse key, and to query information for the mapping,
if found.
Arguments:
Protocol - the IP protocol for the mapping to be located
Destination* - the destination endpoint for the mapping
Source* - the source endpoint for the mapping
Information - on output, receives the requested information
InformationLength - contains the length of the buffer at 'Information'
InformationClass - specifies
Return Value:
NTSTATUS - indicates success/failure.
--*/
{
ULONG64 DestinationKey;
KIRQL Irql;
PNAT_DYNAMIC_MAPPING Mapping;
ULONG64 SourceKey;
NTSTATUS status;
CALLTRACE(("NatLookupAndQueryInformationMapping\n"));
//
// Construct the destination and source key for the mapping,
// and attempt to retrieve it. We try all four possible combinations
// of these keys since the caller can't be guaranteed to know which
// direction the session was headed when it was initiated.
//
MAKE_MAPPING_KEY(
DestinationKey,
Protocol,
DestinationAddress,
DestinationPort
);
MAKE_MAPPING_KEY(
SourceKey,
Protocol,
SourceAddress,
SourcePort
);
KeAcquireSpinLock(&MappingLock, &Irql);
if (!(Mapping = NatLookupForwardMapping(DestinationKey, SourceKey, NULL)) &&
!(Mapping = NatLookupReverseMapping(DestinationKey, SourceKey, NULL)) &&
!(Mapping = NatLookupForwardMapping(SourceKey, DestinationKey, NULL)) &&
!(Mapping = NatLookupReverseMapping(SourceKey, DestinationKey, NULL))) {
KeReleaseSpinLock(&MappingLock, Irql);
return STATUS_UNSUCCESSFUL;
}
NatReferenceMapping(Mapping);
KeReleaseSpinLock(&MappingLock, Irql);
//
// Attempt to supply the information requested about the mapping.
//
switch(InformationClass) {
case NatKeySessionMappingInformation: {
((PIP_NAT_SESSION_MAPPING_KEY)Information)->DestinationAddress =
MAPPING_ADDRESS(Mapping->DestinationKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->DestinationPort =
MAPPING_PORT(Mapping->DestinationKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->SourceAddress =
MAPPING_ADDRESS(Mapping->SourceKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->SourcePort =
MAPPING_PORT(Mapping->SourceKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->NewDestinationAddress =
MAPPING_ADDRESS(Mapping->SourceKey[NatReversePath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->NewDestinationPort =
MAPPING_PORT(Mapping->SourceKey[NatReversePath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->NewSourceAddress =
MAPPING_ADDRESS(Mapping->DestinationKey[NatReversePath]);
((PIP_NAT_SESSION_MAPPING_KEY)Information)->NewSourcePort =
MAPPING_PORT(Mapping->DestinationKey[NatReversePath]);
status = STATUS_SUCCESS;
break;
}
#if _WIN32_WINNT > 0x0500
case NatKeySessionMappingExInformation: {
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->DestinationAddress =
MAPPING_ADDRESS(Mapping->DestinationKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->DestinationPort =
MAPPING_PORT(Mapping->DestinationKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->SourceAddress =
MAPPING_ADDRESS(Mapping->SourceKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->SourcePort =
MAPPING_PORT(Mapping->SourceKey[NatForwardPath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->NewDestinationAddress =
MAPPING_ADDRESS(Mapping->SourceKey[NatReversePath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->NewDestinationPort =
MAPPING_PORT(Mapping->SourceKey[NatReversePath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->NewSourceAddress =
MAPPING_ADDRESS(Mapping->DestinationKey[NatReversePath]);
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->NewSourcePort =
MAPPING_PORT(Mapping->DestinationKey[NatReversePath]);
//
// If this mapping was created by the Redirect director, attempt
// to supply to interface that the redirect was triggered on
//
if (Mapping->Director ==
(PNAT_DIRECTOR)RedirectRegisterDirector.DirectorHandle
&& Mapping->DirectorContext != NULL) {
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->AdapterIndex =
((PNAT_REDIRECT)Mapping->DirectorContext)->RestrictAdapterIndex;
} else {
((PIP_NAT_SESSION_MAPPING_KEY_EX)Information)->AdapterIndex =
INVALID_IF_INDEX;
}
status = STATUS_SUCCESS;
break;
}
#endif
case NatStatisticsSessionMappingInformation: {
NatQueryInformationMapping(
Mapping,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
(PIP_NAT_SESSION_MAPPING_STATISTICS)Information
);
status = STATUS_SUCCESS;
break;
}
default: {
status = STATUS_INVALID_PARAMETER;
break;
}
}
NatDereferenceMapping(Mapping);
return status;
} // NatLookupAndQueryInformationMapping
PNAT_DYNAMIC_MAPPING
NatLookupForwardMapping(
ULONG64 DestinationKey,
ULONG64 SourceKey,
PNAT_DYNAMIC_MAPPING* InsertionPoint
)
/*++
Routine Description:
This routine retrieves the mapping which matches the given key.
If the item is not found, the caller is supplied with the point
at which a new item should be inserted for the given key.
Arguments:
DestinationKey - the primary key used to search for a mapping.
SourceKey - the secondary search key.
InsertionPoint - receives point of insertion in case no match is found.
Return Value:
PNAT_DYNAMIC_MAPPING - The item found, or NULL if no exact match is found.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PNAT_DYNAMIC_MAPPING Root;
PNAT_DYNAMIC_MAPPING Mapping;
PNAT_DYNAMIC_MAPPING Parent = NULL;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatLookupForwardMapping\n"));
//
// First look in the mapping-cache
//
if ((Mapping =
(PNAT_DYNAMIC_MAPPING)ProbeCache(
MappingCache[NatForwardPath],
(ULONG)DestinationKey
)) &&
Mapping->DestinationKey[NatForwardPath] == DestinationKey &&
Mapping->SourceKey[NatForwardPath] == SourceKey
) {
TRACE(PER_PACKET, ("NatLookupForwardMapping: cache hit\n"));
return Mapping;
}
//
// Search the full tree
//
Root = MappingTree[NatForwardPath];
for (SLink = !Root ? NULL : &Root->SLink[NatForwardPath]; SLink; ) {
Mapping =
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatForwardPath]);
if (DestinationKey < Mapping->DestinationKey[NatForwardPath]) {
Parent = Mapping;
SLink = RtlLeftChild(SLink);
continue;
} else if (DestinationKey > Mapping->DestinationKey[NatForwardPath]) {
Parent = Mapping;
SLink = RtlRightChild(SLink);
continue;
}
//
// Primary keys match; check the secondary keys.
//
if (SourceKey < Mapping->SourceKey[NatForwardPath]) {
Parent = Mapping;
SLink = RtlLeftChild(SLink);
continue;
} else if (SourceKey > Mapping->SourceKey[NatForwardPath]) {
Parent = Mapping;
SLink = RtlRightChild(SLink);
continue;
}
//
// Secondary keys match; we got it.
//
UpdateCache(
MappingCache[NatForwardPath],
(ULONG)DestinationKey,
(PVOID)Mapping
);
return Mapping;
}
//
// We didn't get it; tell the caller where to insert it.
//
if (InsertionPoint) { *InsertionPoint = Parent; }
return NULL;
} // NatLookupForwardMapping
PNAT_DYNAMIC_MAPPING
NatLookupReverseMapping(
ULONG64 DestinationKey,
ULONG64 SourceKey,
PNAT_DYNAMIC_MAPPING* InsertionPoint
)
/*++
Routine Description:
This routine retrieves the mapping which matches the given key.
If the item is not found, the caller is supplied with the point
at which a new item should be inserted for the given key.
Arguments:
DestinationKey - the primary key used to search for a mapping.
SourceKey - the secondary search key.
InsertionPoint - receives point of insertion in case no match is found.
Return Value:
PNAT_DYNAMIC_MAPPING - The item found, or NULL if no exact match is found.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PNAT_DYNAMIC_MAPPING Root;
PNAT_DYNAMIC_MAPPING Mapping;
PNAT_DYNAMIC_MAPPING Parent = NULL;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatLookupReverseMapping\n"));
//
// First look in the mapping-cache
//
if ((Mapping =
(PNAT_DYNAMIC_MAPPING)ProbeCache(
MappingCache[NatReversePath],
(ULONG)DestinationKey
)) &&
Mapping->DestinationKey[NatReversePath] == DestinationKey &&
Mapping->SourceKey[NatReversePath] == SourceKey
) {
TRACE(PER_PACKET, ("NatLookupReverseMapping: cache hit\n"));
return Mapping;
}
//
// Search the full tree
//
Root = MappingTree[NatReversePath];
for (SLink = !Root ? NULL : &Root->SLink[NatReversePath]; SLink; ) {
Mapping =
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatReversePath]);
if (DestinationKey < Mapping->DestinationKey[NatReversePath]) {
Parent = Mapping;
SLink = RtlLeftChild(SLink);
continue;
} else if (DestinationKey > Mapping->DestinationKey[NatReversePath]) {
Parent = Mapping;
SLink = RtlRightChild(SLink);
continue;
}
//
// Primary keys match; check the secondary keys.
//
if (SourceKey < Mapping->SourceKey[NatReversePath]) {
Parent = Mapping;
SLink = RtlLeftChild(SLink);
continue;
} else if (SourceKey > Mapping->SourceKey[NatReversePath]) {
Parent = Mapping;
SLink = RtlRightChild(SLink);
continue;
}
//
// Secondary keys match; we got it.
//
UpdateCache(
MappingCache[NatReversePath],
(ULONG)DestinationKey,
(PVOID)Mapping
);
return Mapping;
}
//
// We didn't get it; tell the caller where to insert it.
//
if (InsertionPoint) { *InsertionPoint = Parent; }
return NULL;
} // NatLookupReverseMapping
VOID
NatQueryInformationMapping(
IN PNAT_DYNAMIC_MAPPING Mapping,
OUT PUCHAR Protocol OPTIONAL,
OUT PULONG PrivateAddress OPTIONAL,
OUT PUSHORT PrivatePort OPTIONAL,
OUT PULONG RemoteAddress OPTIONAL,
OUT PUSHORT RemotePort OPTIONAL,
OUT PULONG PublicAddress OPTIONAL,
OUT PUSHORT PublicPort OPTIONAL,
OUT PIP_NAT_SESSION_MAPPING_STATISTICS Statistics OPTIONAL
)
/*++
Routine Description:
This routine is invoked to retrieve information about a mapping.
This is used, for instance, to extract public/private/remote information
from the mapping-keys of mappings associated with boundary interfaces.
Arguments:
Mapping - the mapping for which information is required
Protocol - receives the mapping's protocol
Private* - receive information about the private endpoint
Remote* - receive information about the remote endpoint
Public* - receive information about the public endpoint
Statistics - receives the mapping's statistics
Return Value:
none.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
IP_NAT_PATH ForwardPath =
NAT_MAPPING_INBOUND(Mapping) ? NatReversePath : NatForwardPath;
IP_NAT_PATH ReversePath =
NAT_MAPPING_INBOUND(Mapping) ? NatForwardPath : NatReversePath;
CALLTRACE(("NatQueryInformationMapping\n"));
if (Protocol) {
*Protocol = MAPPING_PROTOCOL(Mapping->SourceKey[ForwardPath]);
}
if (PrivateAddress) {
*PrivateAddress = MAPPING_ADDRESS(Mapping->SourceKey[ForwardPath]);
}
if (PrivatePort) {
*PrivatePort = MAPPING_PORT(Mapping->SourceKey[ForwardPath]);
}
if (PublicAddress) {
*PublicAddress = MAPPING_ADDRESS(Mapping->DestinationKey[ReversePath]);
}
if (PublicPort) {
*PublicPort = MAPPING_PORT(Mapping->DestinationKey[ReversePath]);
}
if (RemoteAddress) {
*RemoteAddress = MAPPING_ADDRESS(Mapping->DestinationKey[ForwardPath]);
}
if (RemotePort) {
*RemotePort = MAPPING_PORT(Mapping->DestinationKey[ForwardPath]);
}
if (Statistics) {
NatUpdateStatisticsMapping(Mapping); *Statistics = Mapping->Statistics;
}
} // NatQueryInformationMapping
NTSTATUS
NatQueryInterfaceMappingTable(
IN PIP_NAT_ENUMERATE_SESSION_MAPPINGS InputBuffer,
IN PIP_NAT_ENUMERATE_SESSION_MAPPINGS OutputBuffer,
IN PULONG OutputBufferLength
)
/*++
Routine Description:
This routine is used for enumerating the session-mappings.
Enumeration makes use of a context structure which is passed
in with each enumeration attempt. The context structure
is updated each time with the key of the next mapping to be enumerated.
Arguments:
InputBuffer - supplies context information for the information
OutputBuffer - receives the result of the enumeration
OutputBufferLength - size of the i/o buffer
Return Value:
STATUS_SUCCESS if successful, error code otherwise.
--*/
{
PULONG Context;
ULONG Count;
LONG64 CurrentTime;
ULONG64 DestinationKey;
ULONG i;
LONG64 IdleTime;
PNAT_INTERFACE Interfacep;
KIRQL Irql;
PLIST_ENTRY Link;
PNAT_DYNAMIC_MAPPING Mapping;
ULONG64 SourceKey;
NTSTATUS status;
PIP_NAT_SESSION_MAPPING Table;
CALLTRACE(("NatQueryInterfaceMappingTable\n"));
KeAcquireSpinLock(&MappingLock, &Irql);
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
Interfacep = NatLookupInterface(InputBuffer->Index, NULL);
if (!Interfacep) {
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
KeReleaseSpinLock(&MappingLock, Irql);
return STATUS_INVALID_PARAMETER;
}
//
// See if this is a new enumeration or a continuation of an old one.
//
Context = InputBuffer->EnumerateContext;
if (!Context[0]) {
//
// This is a new enumeration. We start with the first item
// in the interface's list of mappings
//
Mapping =
IsListEmpty(&Interfacep->MappingList)
? NULL
: CONTAINING_RECORD(
Interfacep->MappingList.Flink,
NAT_DYNAMIC_MAPPING,
InterfaceLink
);
} else {
//
// This is a continuation. The context therefore contains
// the keys for the next mapping, in the fields
// Context[0-1] and Context[2-3] respectively
//
DestinationKey = MAKE_LONG64(Context[0], Context[1]);
SourceKey = MAKE_LONG64(Context[2], Context[3]);
Mapping =
NatLookupForwardMapping(
DestinationKey,
SourceKey,
NULL
);
if (Mapping && !Mapping->Interfacep) { Mapping = NULL; }
}
if (!Mapping) {
OutputBuffer->EnumerateCount = 0;
OutputBuffer->EnumerateContext[0] = 0;
OutputBuffer->EnumerateTotalHint = MappingCount;
*OutputBufferLength =
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable);
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
KeReleaseSpinLock(&MappingLock, Irql);
return STATUS_SUCCESS;
}
KeReleaseSpinLockFromDpcLevel(&MappingLock);
//
// Compute the maximum number of mappings we can store
//
Count =
*OutputBufferLength -
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable);
Count /= sizeof(IP_NAT_SESSION_MAPPING);
//
// Walk the list storing mappings in the caller's buffer
//
Table = OutputBuffer->EnumerateTable;
KeQueryTickCount((PLARGE_INTEGER)&CurrentTime);
for (i = 0, Link = &Mapping->InterfaceLink;
i < Count && Link != &Interfacep->MappingList;
i++, Link = Link->Flink
) {
Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, InterfaceLink);
NatQueryInformationMapping(
Mapping,
&Table[i].Protocol,
&Table[i].PrivateAddress,
&Table[i].PrivatePort,
&Table[i].RemoteAddress,
&Table[i].RemotePort,
&Table[i].PublicAddress,
&Table[i].PublicPort,
NULL
);
Table[i].Direction =
NAT_MAPPING_INBOUND(Mapping)
? NatInboundDirection : NatOutboundDirection;
IdleTime = CurrentTime - Mapping->LastAccessTime;
Table[i].IdleTime = (ULONG)TICKS_TO_SECONDS(IdleTime);
}
//
// The enumeration is over; update the output structure
//
*OutputBufferLength =
i * sizeof(IP_NAT_SESSION_MAPPING) +
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable);
OutputBuffer->EnumerateCount = i;
OutputBuffer->EnumerateTotalHint = MappingCount;
if (Link == &Interfacep->MappingList) {
//
// We reached the end of the mapping list
//
OutputBuffer->EnumerateContext[0] = 0;
} else {
//
// Save the continuation context
//
Mapping =
CONTAINING_RECORD(
Link, NAT_DYNAMIC_MAPPING, InterfaceLink
);
OutputBuffer->EnumerateContext[0] =
(ULONG)Mapping->DestinationKey[NatForwardPath];
OutputBuffer->EnumerateContext[1] =
(ULONG)(Mapping->DestinationKey[NatForwardPath] >> 32);
OutputBuffer->EnumerateContext[2] =
(ULONG)Mapping->SourceKey[NatForwardPath];
OutputBuffer->EnumerateContext[3] =
(ULONG)(Mapping->SourceKey[NatForwardPath] >> 32);
}
KeReleaseSpinLock(&InterfaceLock, Irql);
return STATUS_SUCCESS;
} // NatQueryInterfaceMappingTable
NTSTATUS
NatQueryMappingTable(
IN PIP_NAT_ENUMERATE_SESSION_MAPPINGS InputBuffer,
IN PIP_NAT_ENUMERATE_SESSION_MAPPINGS OutputBuffer,
IN PULONG OutputBufferLength
)
/*++
Routine Description:
This routine is used for enumerating the session-mappings.
Arguments:
InputBuffer - supplies context information for the information
OutputBuffer - receives the result of the enumeration
OutputBufferLength - size of the i/o buffer
Return Value:
STATUS_SUCCESS if successful, error code otherwise.
--*/
{
PULONG Context;
ULONG Count;
LONG64 CurrentTime;
ULONG64 DestinationKey;
ULONG i;
LONG64 IdleTime;
KIRQL Irql;
PLIST_ENTRY Link;
PNAT_DYNAMIC_MAPPING Mapping;
ULONG64 SourceKey;
NTSTATUS status;
PIP_NAT_SESSION_MAPPING Table;
CALLTRACE(("NatQueryMappingTable\n"));
Context = InputBuffer->EnumerateContext;
KeAcquireSpinLock(&MappingLock, &Irql);
//
// See if this is a new enumeration or a continuation of an old one.
//
if (!Context[0]) {
//
// This is a new enumeration. We start with the first item
// in the interface's list of mappings
//
Mapping =
IsListEmpty(&MappingList)
? NULL
: CONTAINING_RECORD(
MappingList.Flink, NAT_DYNAMIC_MAPPING, Link
);
} else {
//
// This is a continuation. The context therefore contains
// the keys for the next mapping, in the fields
// Context[0-1] and Context[2-3] respectively
//
DestinationKey = MAKE_LONG64(Context[0], Context[1]);
SourceKey = MAKE_LONG64(Context[2], Context[3]);
Mapping =
NatLookupForwardMapping(
DestinationKey,
SourceKey,
NULL
);
}
if (!Mapping) {
OutputBuffer->EnumerateCount = 0;
OutputBuffer->EnumerateContext[0] = 0;
OutputBuffer->EnumerateTotalHint = MappingCount;
*OutputBufferLength =
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable);
KeReleaseSpinLock(&MappingLock, Irql);
return STATUS_SUCCESS;
}
//
// Compute the maximum number of mappings we can store
//
Count =
*OutputBufferLength -
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable);
Count /= sizeof(IP_NAT_SESSION_MAPPING);
//
// Walk the list storing mappings in the caller's buffer
//
Table = OutputBuffer->EnumerateTable;
KeQueryTickCount((PLARGE_INTEGER)&CurrentTime);
for (i = 0, Link = &Mapping->Link;
i < Count && Link != &MappingList;
i++, Link = Link->Flink
) {
Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, Link);
NatQueryInformationMapping(
Mapping,
&Table[i].Protocol,
&Table[i].PrivateAddress,
&Table[i].PrivatePort,
&Table[i].RemoteAddress,
&Table[i].RemotePort,
&Table[i].PublicAddress,
&Table[i].PublicPort,
NULL
);
Table[i].Direction =
NAT_MAPPING_INBOUND(Mapping)
? NatInboundDirection : NatOutboundDirection;
IdleTime = CurrentTime - Mapping->LastAccessTime;
Table[i].IdleTime = (ULONG)TICKS_TO_SECONDS(IdleTime);
}
//
// The enumeration is over; update the output structure
//
*OutputBufferLength =
i * sizeof(IP_NAT_SESSION_MAPPING) +
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable);
OutputBuffer->EnumerateCount = i;
OutputBuffer->EnumerateTotalHint = MappingCount;
if (Link == &MappingList) {
//
// We reached the end of the mapping list
//
OutputBuffer->EnumerateContext[0] = 0;
} else {
//
// Save the continuation context
//
Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, Link);
OutputBuffer->EnumerateContext[0] =
(ULONG)Mapping->DestinationKey[NatForwardPath];
OutputBuffer->EnumerateContext[1] =
(ULONG)(Mapping->DestinationKey[NatForwardPath] >> 32);
OutputBuffer->EnumerateContext[2] =
(ULONG)Mapping->SourceKey[NatForwardPath];
OutputBuffer->EnumerateContext[3] =
(ULONG)(Mapping->SourceKey[NatForwardPath] >> 32);
}
KeReleaseSpinLock(&MappingLock, Irql);
return STATUS_SUCCESS;
} // NatQueryMappingTable
PNAT_DYNAMIC_MAPPING
NatSourceLookupForwardMapping(
ULONG64 SourceKey
)
/*++
Routine Description:
This routine retrieves the mapping which matches the given source key.
The destination key of the mapping is not examined.
Arguments:
SourceKey - the primary key used to search for a mapping.
PublicAddressp - receives the private address of the mapping
PublicPortp - receives the private port of the mapping
Return Value:
PNAT_DYNAMIC_MAPPING - the item found, or NULL if no match is found
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PNAT_DYNAMIC_MAPPING Root;
PNAT_DYNAMIC_MAPPING Mapping;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatSourceLookupForwardMapping\n"));
//
// Search the full tree -- the mapping cache can only be used
// for destination lookups.
//
Root = MappingTree[NatForwardPath];
for (SLink = !Root ? NULL : &Root->SLink[NatForwardPath]; SLink; ) {
Mapping =
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatForwardPath]);
if (SourceKey < Mapping->SourceKey[NatForwardPath]) {
SLink = RtlLeftChild(SLink);
continue;
} else if (SourceKey > Mapping->SourceKey[NatForwardPath]) {
SLink = RtlRightChild(SLink);
continue;
}
//
// We found the mapping.
//
return Mapping;
}
//
// No partial match was found
//
return NULL;
} // NatSourceLookupForwardMapping
PNAT_DYNAMIC_MAPPING
NatSourceLookupReverseMapping(
ULONG64 SourceKey
)
/*++
Routine Description:
This routine retrieves the mapping which matches the given source key.
The destination key of the mapping is not examined.
Arguments:
SourceKey - the primary key used to search for a mapping.
Return Value:
PNAT_DYNAMIC_MAPPING - the item found, or NULL if no match is found
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
PNAT_DYNAMIC_MAPPING Root;
PNAT_DYNAMIC_MAPPING Mapping;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatSourceLookupReverseMapping\n"));
//
// Search the full tree -- the mapping cache can only be used
// for destination lookups.
//
Root = MappingTree[NatReversePath];
for (SLink = !Root ? NULL : &Root->SLink[NatReversePath]; SLink; ) {
Mapping =
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,SLink[NatReversePath]);
if (SourceKey < Mapping->SourceKey[NatReversePath]) {
SLink = RtlLeftChild(SLink);
continue;
} else if (SourceKey > Mapping->SourceKey[NatReversePath]) {
SLink = RtlRightChild(SLink);
continue;
}
//
// We found the mapping.
//
return Mapping;
}
//
// No partial match was found
//
return NULL;
} // NatSourceLookupReverseMapping
VOID
NatShutdownMappingManagement(
VOID
)
/*++
Routine Description:
This routine is invoked to shutdown the mapping-management module.
Arguments:
none.
Return Value:
none.
Environment:
Invoked with no references made to any mappings.
--*/
{
KIRQL Irql;
PNAT_DYNAMIC_MAPPING Mapping;
CALLTRACE(("NatShutdownMappingManagement\n"));
KeAcquireSpinLock(&MappingLock, &Irql);
while (!IsListEmpty(&MappingList)) {
Mapping =
CONTAINING_RECORD(MappingList.Flink, NAT_DYNAMIC_MAPPING, Link);
RemoveEntryList(&Mapping->Link);
NatCleanupMapping(Mapping);
}
MappingTree[NatForwardPath] = NULL;
MappingTree[NatReversePath] = NULL;
KeReleaseSpinLock(&MappingLock, Irql);
ExDeleteNPagedLookasideList(&MappingLookasideList);
} // NatShutdownMappingManagement
VOID
NatUpdateStatisticsMapping(
PNAT_DYNAMIC_MAPPING Mapping
)
/*++
Routine Description:
This routine is invoked to immediately update the statistics for a mapping,
adding the 32-bit incremental counters to the 64-bit cumulative counters.
Arguments:
Mapping - the mapping whose statistics are to be updated
Return Value:
none.
Environment:
Invoked with 'MappingLock' held by the caller.
--*/
{
ULONG BytesForward;
ULONG BytesReverse;
ULONG PacketsForward;
ULONG PacketsReverse;
ULONG RejectsForward;
ULONG RejectsReverse;
CALLTRACE(("NatUpdateStatisticsMapping\n"));
//
// Read the statistics accrued since the last incremental update
//
BytesForward = InterlockedExchange(&Mapping->BytesForward, 0);
BytesReverse = InterlockedExchange(&Mapping->BytesReverse, 0);
PacketsForward = InterlockedExchange(&Mapping->PacketsForward, 0);
PacketsReverse = InterlockedExchange(&Mapping->PacketsReverse, 0);
RejectsForward = InterlockedExchange(&Mapping->RejectsForward, 0);
RejectsReverse = InterlockedExchange(&Mapping->RejectsReverse, 0);
# define UPDATE_STATISTIC(x,y) \
if (y) { \
ExInterlockedAddLargeStatistic( \
(PLARGE_INTEGER)&x->Statistics.y, y \
); \
}
//
// Update the cumulative statistics for the mapping
//
UPDATE_STATISTIC(Mapping, BytesForward);
UPDATE_STATISTIC(Mapping, BytesReverse);
UPDATE_STATISTIC(Mapping, PacketsForward);
UPDATE_STATISTIC(Mapping, PacketsReverse);
UPDATE_STATISTIC(Mapping, RejectsForward);
UPDATE_STATISTIC(Mapping, RejectsReverse);
//
// Update cumulative statistics for the mapping's interface, if any
//
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
if (Mapping->Interfacep) {
UPDATE_STATISTIC(Mapping->Interfacep, BytesForward);
UPDATE_STATISTIC(Mapping->Interfacep, BytesReverse);
UPDATE_STATISTIC(Mapping->Interfacep, PacketsForward);
UPDATE_STATISTIC(Mapping->Interfacep, PacketsReverse);
UPDATE_STATISTIC(Mapping->Interfacep, RejectsForward);
UPDATE_STATISTIC(Mapping->Interfacep, RejectsReverse);
}
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
# undef UPDATE_STATISTIC
} // NatUpdateStatisticsMapping