2210 lines
60 KiB
C
2210 lines
60 KiB
C
/*++
|
||
|
||
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
|