You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2485 lines
72 KiB
2485 lines
72 KiB
/*++
|
|
|
|
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
|
|
|
|
NTKERNELAPI
|
|
BOOLEAN
|
|
ExVerifySuite(
|
|
SUITE_TYPE SuiteType
|
|
);
|
|
|
|
//
|
|
// 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];
|
|
BOOLEAN RunningOnSBS = FALSE;
|
|
PNAT_DYNAMIC_MAPPING SourceMappingTree[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);
|
|
|
|
//
|
|
// If it is an UDP mapping, also add it to the source mapping tree.
|
|
//
|
|
if (Protocol == NAT_PROTOCOL_UDP) {
|
|
|
|
RtlInitializeSplayLinks(&Mapping->u.SourceSLink[NatForwardPath]);
|
|
RtlInitializeSplayLinks(&Mapping->u.SourceSLink[NatReversePath]);
|
|
NatSourceLookupForwardMapping(SourceKey[NatForwardPath], &InsertionPoint1);
|
|
SourceMappingTree[NatForwardPath] = NatSourceInsertForwardMapping(InsertionPoint1, Mapping);
|
|
NatSourceLookupReverseMapping(SourceKey[NatReversePath], &InsertionPoint2);
|
|
SourceMappingTree[NatReversePath] = NatSourceInsertReverseMapping(InsertionPoint2, Mapping);
|
|
}
|
|
|
|
InsertTailList(&MappingList, &Mapping->Link);
|
|
InterlockedIncrement(&MappingCount);
|
|
|
|
*MappingCreated = Mapping;
|
|
|
|
#if NAT_WMI
|
|
|
|
//
|
|
// Log the creation. Logging always uses public addresses,
|
|
// not private, except for outbound connections on SBS
|
|
// SKUs.
|
|
//
|
|
|
|
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 {
|
|
if (!RunningOnSBS) {
|
|
NatLogConnectionCreation(
|
|
MAPPING_ADDRESS(DestinationKey[NatReversePath]),
|
|
MAPPING_ADDRESS(SourceKey[NatReversePath]),
|
|
MAPPING_PORT(DestinationKey[NatReversePath]),
|
|
MAPPING_PORT(SourceKey[NatReversePath]),
|
|
Protocol,
|
|
FALSE
|
|
);
|
|
} else {
|
|
NatLogConnectionCreation(
|
|
MAPPING_ADDRESS(SourceKey[NatForwardPath]),
|
|
MAPPING_ADDRESS(DestinationKey[NatForwardPath]),
|
|
MAPPING_PORT(SourceKey[NatForwardPath]),
|
|
MAPPING_PORT(DestinationKey[NatForwardPath]),
|
|
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_PROTOCOL_UDP == MAPPING_PROTOCOL(Mapping->SourceKey[NatForwardPath])) {
|
|
|
|
SLink = RtlDelete(&Mapping->u.SourceSLink[NatForwardPath]);
|
|
SourceMappingTree[NatForwardPath] =
|
|
(SLink
|
|
? CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,u.SourceSLink[NatForwardPath])
|
|
: NULL);
|
|
|
|
SLink = RtlDelete(&Mapping->u.SourceSLink[NatReversePath]);
|
|
SourceMappingTree[NatReversePath] =
|
|
(SLink
|
|
? CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,u.SourceSLink[NatReversePath])
|
|
: NULL);
|
|
}
|
|
|
|
#if NAT_WMI
|
|
|
|
//
|
|
// Log the deletion. Logging always uses public addresses,
|
|
// not private, except for outbound connections on SBS
|
|
// SKUs.
|
|
//
|
|
|
|
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 {
|
|
if (!RunningOnSBS) {
|
|
NatLogConnectionDeletion(
|
|
MAPPING_ADDRESS(Mapping->DestinationKey[NatReversePath]),
|
|
MAPPING_ADDRESS(Mapping->SourceKey[NatReversePath]),
|
|
MAPPING_PORT(Mapping->DestinationKey[NatReversePath]),
|
|
MAPPING_PORT(Mapping->SourceKey[NatReversePath]),
|
|
MAPPING_PROTOCOL(Mapping->DestinationKey[NatReversePath]),
|
|
FALSE
|
|
);
|
|
} else {
|
|
NatLogConnectionDeletion(
|
|
MAPPING_ADDRESS(Mapping->SourceKey[NatForwardPath]),
|
|
MAPPING_ADDRESS(Mapping->DestinationKey[NatForwardPath]),
|
|
MAPPING_PORT(Mapping->SourceKey[NatForwardPath]),
|
|
MAPPING_PORT(Mapping->DestinationKey[NatForwardPath]),
|
|
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;
|
|
SourceMappingTree[NatForwardPath] = NULL;
|
|
SourceMappingTree[NatReversePath] = NULL;
|
|
InitializeCache(MappingCache[NatForwardPath]);
|
|
InitializeCache(MappingCache[NatReversePath]);
|
|
ExInitializeNPagedLookasideList(
|
|
&MappingLookasideList,
|
|
NatAllocateFunction,
|
|
NULL,
|
|
0,
|
|
sizeof(NAT_DYNAMIC_MAPPING),
|
|
NAT_TAG_MAPPING,
|
|
MAPPING_LOOKASIDE_DEPTH
|
|
);
|
|
RunningOnSBS = ExVerifySuite(SmallBusiness | SmallBusinessRestricted);
|
|
|
|
} // 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
|
|
|
|
PNAT_DYNAMIC_MAPPING
|
|
NatSourceInsertForwardMapping(
|
|
PNAT_DYNAMIC_MAPPING Parent,
|
|
PNAT_DYNAMIC_MAPPING Mapping
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts a mapping into the source mapping tree. Different from NatInserverForwardMapping, it
|
|
does not check for duplicates and will always add mapping to 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.
|
|
|
|
Environment:
|
|
|
|
Invoked with 'MappingLock' held by the caller.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRTL_SPLAY_LINKS Root, SLink;
|
|
PNAT_DYNAMIC_MAPPING RChild;
|
|
|
|
CALLTRACE(("NatInsertForwardSourceMapping\n"));
|
|
|
|
if (!Parent) {
|
|
TRACE(MAPPING, ("NatInsertForwardSourceMapping: inserting as root\n"));
|
|
return Mapping;
|
|
}
|
|
|
|
//
|
|
// Insert as left or right child.
|
|
//
|
|
SLink = &Parent->u.SourceSLink[NatForwardPath];
|
|
if (Mapping->SourceKey[NatForwardPath] <
|
|
Parent->SourceKey[NatForwardPath]) {
|
|
ASSERT(NULL == RtlLeftChild(SLink));
|
|
RtlInsertAsLeftChild(
|
|
&Parent->u.SourceSLink[NatForwardPath], &Mapping->u.SourceSLink[NatForwardPath]
|
|
);
|
|
} else if (Mapping->SourceKey[NatForwardPath] >
|
|
Parent->SourceKey[NatForwardPath]) {
|
|
ASSERT(NULL == RtlRightChild(SLink));
|
|
RtlInsertAsRightChild(
|
|
&Parent->u.SourceSLink[NatForwardPath], &Mapping->u.SourceSLink[NatForwardPath]
|
|
);
|
|
} else {
|
|
//
|
|
// Note that a mapping with the same source key will be added as a right child. Mappings with
|
|
// the same source key are not sorted by the destination keys. Exact duplicates of some mappings
|
|
// may also be in the tree.
|
|
//
|
|
|
|
if (NULL == RtlRightChild(SLink)) {
|
|
RtlInsertAsRightChild(
|
|
&Parent->u.SourceSLink[NatForwardPath], &Mapping->u.SourceSLink[NatForwardPath]
|
|
);
|
|
} else {
|
|
//
|
|
// Parent node already has a right child. Insert the new node as the parent node's right child and
|
|
// make the parent's right child as the new node's right child.
|
|
//
|
|
RChild = CONTAINING_RECORD(RtlRightChild(SLink), NAT_DYNAMIC_MAPPING, u.SourceSLink[NatForwardPath]);
|
|
RtlInsertAsRightChild(
|
|
&Mapping->u.SourceSLink[NatForwardPath], &RChild->u.SourceSLink[NatForwardPath]
|
|
);
|
|
RtlInsertAsRightChild(
|
|
&Parent->u.SourceSLink[NatForwardPath], &Mapping->u.SourceSLink[NatForwardPath]
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Splay the new node and return the resulting root.
|
|
//
|
|
|
|
Root = RtlSplay(&Mapping->u.SourceSLink[NatForwardPath]);
|
|
return CONTAINING_RECORD(Root, NAT_DYNAMIC_MAPPING, u.SourceSLink[NatForwardPath]);
|
|
|
|
} // NatInsertForwardSourceMapping
|
|
|
|
|
|
|
|
PNAT_DYNAMIC_MAPPING
|
|
NatSourceInsertReverseMapping(
|
|
PNAT_DYNAMIC_MAPPING Parent,
|
|
PNAT_DYNAMIC_MAPPING Mapping
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts a mapping into the tree. Different from NatInserverReverseMapping, it
|
|
does not check for duplicates and will always add mapping to 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, SLink;
|
|
PNAT_DYNAMIC_MAPPING RChild;
|
|
|
|
CALLTRACE(("NatInsertReverseSourceMapping\n"));
|
|
|
|
if (!Parent) {
|
|
TRACE(MAPPING, ("NatInsertReverseSourceMapping: inserting as root\n"));
|
|
return Mapping;
|
|
}
|
|
|
|
//
|
|
// Insert as left or right child
|
|
//
|
|
SLink = &Parent->u.SourceSLink[NatReversePath];
|
|
if (Mapping->SourceKey[NatReversePath] <
|
|
Parent->SourceKey[NatReversePath]) {
|
|
ASSERT(NULL == RtlLeftChild(SLink));
|
|
RtlInsertAsLeftChild(
|
|
&Parent->u.SourceSLink[NatReversePath], &Mapping->u.SourceSLink[NatReversePath]
|
|
);
|
|
|
|
} else if (Mapping->SourceKey[NatReversePath] >
|
|
Parent->SourceKey[NatReversePath]) {
|
|
ASSERT(NULL == RtlRightChild(SLink));
|
|
RtlInsertAsRightChild(
|
|
&Parent->u.SourceSLink[NatReversePath], &Mapping->u.SourceSLink[NatReversePath]
|
|
);
|
|
} else {
|
|
//
|
|
// Note that a mapping with the same source key will be added as a right child. Mappings with
|
|
// the same source key are not sorted by the destination keys. Exact duplicates of some mappings
|
|
// may also be in the tree.
|
|
//
|
|
if (NULL == RtlRightChild(SLink)) {
|
|
RtlInsertAsRightChild(
|
|
&Parent->u.SourceSLink[NatReversePath], &Mapping->u.SourceSLink[NatReversePath]
|
|
);
|
|
} else {
|
|
//
|
|
// Parent node already has a right child. Insert the new node as the parent node's right child and
|
|
// make the parent's right child as the new node's right child.
|
|
//
|
|
RChild = CONTAINING_RECORD(RtlRightChild(SLink), NAT_DYNAMIC_MAPPING, u.SourceSLink[NatReversePath]);
|
|
RtlInsertAsRightChild(
|
|
&Mapping->u.SourceSLink[NatReversePath], &RChild->u.SourceSLink[NatReversePath]
|
|
);
|
|
RtlInsertAsRightChild(
|
|
&Parent->u.SourceSLink[NatReversePath], &Mapping->u.SourceSLink[NatReversePath]
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Splay the new node and return the resulting root.
|
|
//
|
|
|
|
Root = RtlSplay(&Mapping->u.SourceSLink[NatReversePath]);
|
|
return CONTAINING_RECORD(Root, NAT_DYNAMIC_MAPPING, u.SourceSLink[NatReversePath]);
|
|
|
|
} // NatInsertReverseSourceMapping
|
|
|
|
|
|
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
|
|
//
|
|
|
|
KeAcquireSpinLock(&DirectorLock, &Irql);
|
|
KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock);
|
|
|
|
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;
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock);
|
|
KeReleaseSpinLock(&DirectorLock, Irql);
|
|
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,
|
|
PNAT_DYNAMIC_MAPPING* InsertionPoint
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
PNAT_DYNAMIC_MAPPING Parent = NULL;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
|
|
TRACE(PER_PACKET, ("NatSourceLookupForwardMapping\n"));
|
|
|
|
//
|
|
// Search the full tree -- the mapping cache can only be used
|
|
// for destination lookups.
|
|
//
|
|
|
|
Root = SourceMappingTree[NatForwardPath];
|
|
|
|
for (SLink = !Root ? NULL : &Root->u.SourceSLink[NatForwardPath]; SLink; ) {
|
|
|
|
Mapping =
|
|
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,u.SourceSLink[NatForwardPath]);
|
|
|
|
if (SourceKey < Mapping->SourceKey[NatForwardPath]) {
|
|
Parent = Mapping;
|
|
SLink = RtlLeftChild(SLink);
|
|
continue;
|
|
} else if (SourceKey > Mapping->SourceKey[NatForwardPath]) {
|
|
Parent = Mapping;
|
|
SLink = RtlRightChild(SLink);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We found the mapping. Since for source tree, a new mapping needs to be inserted even when
|
|
// finding an exisiting mapping with the same source key, InsertionPoint needs to be set here as
|
|
// well. The new mapping will be inserted as a right child of the found mapping.
|
|
//
|
|
if (InsertionPoint) { *InsertionPoint = Mapping; }
|
|
|
|
return Mapping;
|
|
}
|
|
|
|
//
|
|
// No partial match was found
|
|
//
|
|
if (InsertionPoint) { *InsertionPoint = Parent; }
|
|
|
|
return NULL;
|
|
|
|
} // NatSourceLookupForwardMapping
|
|
|
|
|
|
PNAT_DYNAMIC_MAPPING
|
|
NatSourceLookupReverseMapping(
|
|
ULONG64 SourceKey,
|
|
PNAT_DYNAMIC_MAPPING* InsertionPoint
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
PNAT_DYNAMIC_MAPPING Parent = NULL;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
|
|
TRACE(PER_PACKET, ("NatSourceLookupReverseMapping\n"));
|
|
|
|
//
|
|
// Search the full tree -- the mapping cache can only be used
|
|
// for destination lookups.
|
|
//
|
|
|
|
Root = SourceMappingTree[NatReversePath];
|
|
|
|
for (SLink = !Root ? NULL : &Root->u.SourceSLink[NatReversePath]; SLink; ) {
|
|
|
|
Mapping =
|
|
CONTAINING_RECORD(SLink,NAT_DYNAMIC_MAPPING,u.SourceSLink[NatReversePath]);
|
|
|
|
if (SourceKey < Mapping->SourceKey[NatReversePath]) {
|
|
Parent = Mapping;
|
|
SLink = RtlLeftChild(SLink);
|
|
continue;
|
|
} else if (SourceKey > Mapping->SourceKey[NatReversePath]) {
|
|
Parent = Mapping;
|
|
SLink = RtlRightChild(SLink);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We found the mapping. Since for source tree, a new mapping needs to be inserted even when
|
|
// finding an exisiting mapping with the same source key, InsertionPoint needs to be set here as
|
|
// well. The new mapping will be inserted as a right child of the found mapping.
|
|
//
|
|
if (InsertionPoint) { *InsertionPoint = Mapping; }
|
|
|
|
return Mapping;
|
|
}
|
|
|
|
//
|
|
// No partial match was found
|
|
//
|
|
if (InsertionPoint) { *InsertionPoint = Parent; }
|
|
|
|
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;
|
|
SourceMappingTree[NatForwardPath] = NULL;
|
|
SourceMappingTree[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
|