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

1857 lines
47 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
pool.c
Abstract:
This module contains code for managing the NAT's pool of addresses
as well as its ranges of ports.
Author:
Abolade Gbadegesin (t-abolag) 13-July-1997
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define IP_NAT_MAX_ADDRESS_RANGE (1<<16)
#define IP_NAT_MAX_CLIENT_COUNT (5+2)
//
// Macro used to avoid the fact that RtlInitializeBitMap is pageable
// and hence cannot be called at DPC-level
//
#define INITIALIZE_BITMAP(BMH,B,S) \
((BMH)->Buffer = (B), (BMH)->SizeOfBitMap = (S))
//
// FORWARD DECLARATIONS
//
NTSTATUS
NatCreateAddressPoolEntry(
PNAT_INTERFACE Interfacep,
ULONG PrivateAddress,
ULONG PublicAddress,
DWORD InitialFlags,
PNAT_USED_ADDRESS* InsertionPoint,
PNAT_USED_ADDRESS* AddressCreated
);
PNAT_USED_ADDRESS
NatInsertAddressPoolEntry(
PNAT_USED_ADDRESS Parent,
PNAT_USED_ADDRESS Addressp
);
//
// ADDRESS-POOL ROUTINES (alphabetically)
//
NTSTATUS
NatAcquireFromAddressPool(
PNAT_INTERFACE Interfacep,
ULONG PrivateAddress,
ULONG PublicAddress OPTIONAL,
PNAT_USED_ADDRESS* AddressAcquired
)
/*++
Routine Description:
This routine acquires an address from an address pool.
It initializes the address's port-pool.
Arguments:
Interfacep - interface on which to acquire an address.
PrivateAddress - private address for whom to acquire a public address
PublicAddress - optionally specifies the public-address to be acquired
AddressAcquired - receives a pointer to the address acquired.
Return Value:
NTSTATUS - status code.
--*/
{
ULONG ClientCount;
PNAT_USED_ADDRESS InsertionPoint;
PLIST_ENTRY Link;
PNAT_USED_ADDRESS Sharedp;
NTSTATUS status;
PNAT_USED_ADDRESS Usedp;
CALLTRACE(("NatAcquireFromAddressPool\n"));
*AddressAcquired = NULL;
TRACE(
POOL, ("NatAcquireFromAddressPool: acquiring for %d.%d.%d.%d\n",
ADDRESS_BYTES(PrivateAddress)
));
//
// See if the requesting private address already has a public address
//
for (Link = Interfacep->UsedAddressList.Flink, ClientCount = 0;
Link != &Interfacep->UsedAddressList;
Link = Link->Flink, ClientCount++) {
Usedp = CONTAINING_RECORD(Link, NAT_USED_ADDRESS, Link);
if (Usedp->PrivateAddress == PrivateAddress &&
(!PublicAddress || Usedp->PublicAddress == PublicAddress)) {
break;
}
}
if (Link != &Interfacep->UsedAddressList) {
NatReferenceAddressPoolEntry(Usedp);
*AddressAcquired = Usedp;
return STATUS_SUCCESS;
} else if (ClientCount > IP_NAT_MAX_CLIENT_COUNT &&
SharedUserData->NtProductType == NtProductWinNt) {
#if 0
TRACE(
POOL, ("NatAcquireFromAddressPool: quota exceeded (%d clients)\n",
ClientCount
));
return STATUS_LICENSE_QUOTA_EXCEEDED;
#endif
}
//
// Create a new entry, inserting it in the list and tree
//
status =
NatCreateAddressPoolEntry(
Interfacep,
PrivateAddress,
PublicAddress,
0,
NULL,
AddressAcquired
);
if (NT_SUCCESS(status)) { return STATUS_SUCCESS; }
TRACE(POOL, ("NatAcquireFromAddressPool: no free addresses\n"));
//
// No entry was available;
// if the caller specified a specific address
// or if the interface has port-translation disabled,
// this is a total failure.
// Otherwise, we can try to find an address which we can share.
//
if (PublicAddress || !NAT_INTERFACE_NAPT(Interfacep)) {
return STATUS_UNSUCCESSFUL;
}
//
// Look for an address which can be shared
//
for (Link = Interfacep->UsedAddressList.Flink;
Link != &Interfacep->UsedAddressList; Link = Link->Flink) {
Usedp = CONTAINING_RECORD(Link, NAT_USED_ADDRESS, Link);
//
// We cannot reuse statically mapped addresses,
// and we ignore placeholders while searching.
//
if (NAT_POOL_STATIC(Usedp) || NAT_POOL_PLACEHOLDER(Usedp)) { continue; }
//
// We cannot reuse an entry which would result in our tree
// containing duplicate keys.
//
if (NatLookupAddressPoolEntry(
Interfacep->UsedAddressTree,
PrivateAddress,
Usedp->PublicAddress,
&InsertionPoint
)) { continue; }
break;
}
if (Link == &Interfacep->UsedAddressList) { return STATUS_UNSUCCESSFUL; }
//
// Reuse the used-address;
// If it is referenced, we're sharing it.
//
TRACE(
POOL, ("NatAcquireFromAddressPool: reusing %d.%d.%d.%d\n",
ADDRESS_BYTES(Usedp->PublicAddress)
));
//
// Allocate and initialize a placeholder which we can find
// by searching on 'PrivateAddress'
//
Sharedp =
ExAllocatePoolWithTag(
NonPagedPool,
sizeof(NAT_USED_ADDRESS),
NAT_TAG_USED_ADDRESS
);
if (!Sharedp) {
ERROR(("NatAcquireFromAddressPool: allocation failed\n"));
return STATUS_NO_MEMORY;
}
RtlZeroMemory(Sharedp, sizeof(*Sharedp));
Sharedp->Flags = NAT_POOL_FLAG_PLACEHOLDER;
Sharedp->PrivateAddress = PrivateAddress;
Sharedp->PublicAddress = Usedp->PublicAddress;
Sharedp->Key =
MAKE_USED_ADDRESS_KEY(
Sharedp->PrivateAddress, Sharedp->PublicAddress
);
Sharedp->ReferenceCount = 1;
InitializeListHead(&Sharedp->Link);
RtlInitializeSplayLinks(&Sharedp->SLink);
InsertTailList(&Interfacep->UsedAddressList, &Sharedp->Link);
Interfacep->UsedAddressTree =
NatInsertAddressPoolEntry(InsertionPoint, Sharedp);
//
// Set the placeholder's 'SharedAddress' field
// to point to the actual address containing the port-pools
//
Sharedp->SharedAddress = Usedp;
NatReferenceAddressPoolEntry(Usedp);
*AddressAcquired = Sharedp;
return STATUS_SUCCESS;
} // NatAcquireFromAddressPool
NTSTATUS
NatAcquireEndpointFromAddressPool(
PNAT_INTERFACE Interfacep,
ULONG64 PrivateKey,
ULONG64 RemoteKey,
ULONG PublicAddress OPTIONAL,
USHORT PreferredPort,
BOOLEAN AllowAnyPort,
PNAT_USED_ADDRESS* AddressAcquired,
PUSHORT PortAcquired
)
/*++
Routine Description:
This routine is called to acquire an address and port for a session.
Arguments:
PrivateKey - the private endpoint for the session
RemoteKey - the remote endpoint for the session
PublicAddress - optionally specifies the public address to be acquired
PreferredPort - optionally specifies the port preferred by the caller
AllowAnyPort - if TRUE, any available port can be used for the mapping
if 'Portp' is not available.
AddressAcquired - receives the address acquired
PortAcquired - receives the port acquired
Return Value:
NTSTATUS - indicates success/failure.
Environment:
Invoked with 'MappingLock' and 'Interfacep->Lock' held by the caller.
--*/
{
PNAT_USED_ADDRESS Addressp;
USHORT StopPort;
ULONG i;
PLIST_ENTRY Link;
USHORT Port;
UCHAR Protocol;
ULONG64 PublicKey;
PNAT_USED_ADDRESS SharedAddress;
NTSTATUS status;
CALLTRACE(("NatAcquireEndpointFromAddressPool\n"));
//
// Acquire an address for the session
//
status =
NatAcquireFromAddressPool(
Interfacep,
MAPPING_ADDRESS(PrivateKey),
PublicAddress,
&Addressp
);
if (!NT_SUCCESS(status)) { return status; }
SharedAddress = Addressp;
PLACEHOLDER_TO_ADDRESS(SharedAddress);
//
// Now look for a port range which contains the preferred port
//
Protocol = MAPPING_PROTOCOL(PrivateKey);
if (PreferredPort) {
do {
//
// The caller prefers that we acquire a particular port;
// see if we can satisfy the request.
//
Port = NTOHS(PreferredPort);
if (Port < NTOHS(SharedAddress->StartPort) ||
Port > NTOHS(SharedAddress->EndPort)) {
break;
}
//
// The preferred port is in the current range.
// See if it is in use by another mapping
//
MAKE_MAPPING_KEY(
PublicKey,
Protocol,
Addressp->PublicAddress,
PreferredPort
);
if (NatLookupReverseMapping(PublicKey, RemoteKey, NULL)) { break; }
//
// Now see if it is in use by a ticket
//
if (NatIsPortUsedByTicket(Interfacep, Protocol, PreferredPort)) {
break;
}
//
// The preferred port is available; return
//
*AddressAcquired = Addressp;
*PortAcquired = PreferredPort;
TRACE(
POOL,
("NatAcquireEndpointFromAddressPool: using preferred port %d\n",
NTOHS(PreferredPort)
));
return STATUS_SUCCESS;
} while(FALSE);
//
// We couldn't acquire the preferred port;
// fail if no other port is acceptable.
//
if (!AllowAnyPort || !NAT_INTERFACE_NAPT(Interfacep)) {
NatDereferenceAddressPoolEntry(Interfacep, Addressp);
return STATUS_UNSUCCESSFUL;
}
}
//
// If this is for a UDP session, check to see if there is another
// session with the same private endpoint. If such a session
// exists we want to use the same public endpoint for this session.
//
if (NAT_PROTOCOL_UDP == MAPPING_PROTOCOL(PrivateKey)) {
PNAT_DYNAMIC_MAPPING Mapping;
IP_NAT_PATH Path;
//
// Perform a source-only mapping lookup
//
Mapping = NatSourceLookupForwardMapping(PrivateKey);
if (NULL == Mapping) {
Mapping = NatSourceLookupReverseMapping(PrivateKey);
}
if (NULL != Mapping) {
//
// There's another UDP session with this private endpoint;
// if it has the same public address that we've already
// acquired then use the same public port.
//
Path = NAT_MAPPING_INBOUND(Mapping)
? NatForwardPath
: NatReversePath;
if (SharedAddress->PublicAddress
== MAPPING_ADDRESS(Mapping->DestinationKey[Path])) {
//
// Same public address -- use the port from
// this session.
//
*AddressAcquired = Addressp;
*PortAcquired = MAPPING_PORT(Mapping->DestinationKey[Path]);
TRACE(
POOL,
("NatAcquireEndpointFromAddressPool: reusing UDP port %d\n",
NTOHS(*PortAcquired)
));
return STATUS_SUCCESS;
}
}
}
//
// Acquire the first available port for the session
//
if (SharedAddress->NextPortToTry != SharedAddress->StartPort) {
StopPort =
RtlUshortByteSwap(
(USHORT)(NTOHS(SharedAddress->NextPortToTry) - 1)
);
} else {
StopPort = SharedAddress->EndPort;
}
for (Port = SharedAddress->NextPortToTry; Port != StopPort;
Port = (Port != SharedAddress->EndPort
? RtlUshortByteSwap((USHORT)(NTOHS(Port) + 1))
: SharedAddress->StartPort)) {
//
// See if this port is in use by a mapping
//
MAKE_MAPPING_KEY(PublicKey, Protocol, Addressp->PublicAddress, Port);
if (NatLookupReverseMapping(PublicKey, RemoteKey, NULL)) { continue; }
//
// Now see if it is in use by a ticket
//
if (NatIsPortUsedByTicket(Interfacep, Protocol, Port)) { continue; }
//
// The port is available; return
//
*AddressAcquired = Addressp;
*PortAcquired = Port;
//
// Update the address pool entry with the port with which to
// start the search on the next allocation attempt.
//
if (Port == SharedAddress->EndPort) {
SharedAddress->NextPortToTry = SharedAddress->StartPort;
} else {
SharedAddress->NextPortToTry =
RtlUshortByteSwap(
(USHORT)(NTOHS(Port) + 1)
);
}
TRACE(
POOL, ("NatAcquireEndpointFromAddressPool: using port %d\n",
NTOHS(Port)
));
return STATUS_SUCCESS;
}
//
// We were unable to acquire a port for the session; fail.
//
NatDereferenceAddressPoolEntry(Interfacep, Addressp);
TRACE(POOL, ("NatAcquireEndpointFromAddressPool: no ports available\n"));
return STATUS_UNSUCCESSFUL;
} // NatAcquireEndpointFromAddressPool
NTSTATUS
NatCreateAddressPool(
PNAT_INTERFACE Interfacep
)
/*++
Routine Description:
This routine initializes an interface's address-pool.
This involves setting up the bitmap of free addresses
and reserving the statically mapped IP addresses.
Arguments:
Interfacep - the interface on which to create the address pool.
Return Value:
NTSTATUS - status code
--*/
{
ULONG Address;
BOOLEAN Changed;
PNAT_FREE_ADDRESS FreeMapArray;
ULONG FreeMapCount;
PNAT_FREE_ADDRESS Freep;
ULONG i;
PNAT_USED_ADDRESS InsertionPoint;
ULONG j;
PRTL_SPLAY_LINKS Parent;
PIP_NAT_ADDRESS_RANGE* RangeArrayIndirect;
NTSTATUS status;
PNAT_USED_ADDRESS Usedp;
CALLTRACE(("NatCreateAddressPool\n"));
if (Interfacep->AddressRangeCount <= 1) {
RangeArrayIndirect = &Interfacep->AddressRangeArray;
} else {
//
// Allocate a temporary block of pointers
// to be used to do an indirect sort of the range-array.
//
RangeArrayIndirect =
(PIP_NAT_ADDRESS_RANGE*)ExAllocatePoolWithTag(
NonPagedPool,
Interfacep->AddressRangeCount * sizeof(PVOID),
NAT_TAG_RANGE_ARRAY
);
if (!RangeArrayIndirect) {
ERROR(("NatCreateAddressPool: error allocating sort-buffer\n"));
return STATUS_NO_MEMORY;
}
for (i = 0; i < Interfacep->AddressRangeCount; i++) {
RangeArrayIndirect[i] = &Interfacep->AddressRangeArray[i];
}
do {
//
// Now do a bubble-sort of the ranges
//
Changed = FALSE;
for (i = 0; i < Interfacep->AddressRangeCount - 1; i++) {
PIP_NAT_ADDRESS_RANGE CurrentRange = RangeArrayIndirect[i];
ULONG CurrentRangeStartAddress =
RtlUlongByteSwap(CurrentRange->StartAddress);
PIP_NAT_ADDRESS_RANGE NextRange;
for (j = i+1, NextRange = RangeArrayIndirect[j];
j < Interfacep->AddressRangeCount;
j++, NextRange = RangeArrayIndirect[j]) {
//
// Do a swap if necessary
//
if (CurrentRangeStartAddress <=
RtlUlongByteSwap(NextRange->StartAddress)) { continue; }
RangeArrayIndirect[i] = NextRange;
RangeArrayIndirect[j] = CurrentRange;
CurrentRange = NextRange;
CurrentRangeStartAddress =
RtlUlongByteSwap(NextRange->StartAddress);
Changed = TRUE;
}
}
} while (Changed);
}
//
// Copy the ranges into NAT_FREE_ADDRESS blocks.
// There will be at most 'RangeCount' of these,
// and possibly less, since we will merge any ranges
// which overlap or are adjacent.
//
FreeMapCount = 0;
if (!Interfacep->AddressRangeCount) {
FreeMapArray = NULL;
} else {
FreeMapArray =
(PNAT_FREE_ADDRESS)ExAllocatePoolWithTag(
NonPagedPool,
Interfacep->AddressRangeCount * sizeof(NAT_FREE_ADDRESS),
NAT_TAG_FREE_MAP
);
if (!FreeMapArray) {
ExFreePool(RangeArrayIndirect);
return STATUS_NO_MEMORY;
}
}
for (i = 0, j = 0; i < Interfacep->AddressRangeCount; i++) {
ULONG RangeStartAddress =
RtlUlongByteSwap(RangeArrayIndirect[i]->StartAddress);
//
// See if we should merge with the preceding block;
//
if (FreeMapCount) {
//
// Incrementing the end-address of the preceding range
// enables us to catch both overlaps and adjacencies.
//
if (RangeStartAddress <=
RtlUlongByteSwap(FreeMapArray[j].EndAddress) + 1) {
//
// We need to merge.
//
if (RtlUlongByteSwap(FreeMapArray[j].EndAddress) <
RtlUlongByteSwap(RangeArrayIndirect[i]->EndAddress)) {
FreeMapArray[j].EndAddress =
RangeArrayIndirect[i]->EndAddress;
}
if (RtlUlongByteSwap(FreeMapArray[j].SubnetMask) <
RtlUlongByteSwap(RangeArrayIndirect[i]->SubnetMask)) {
FreeMapArray[j].SubnetMask =
RangeArrayIndirect[i]->SubnetMask;
}
continue;
}
//
// No merge; move to next slot.
//
++j;
}
FreeMapArray[j].StartAddress = RangeArrayIndirect[i]->StartAddress;
FreeMapArray[j].EndAddress = RangeArrayIndirect[i]->EndAddress;
FreeMapArray[j].SubnetMask = RangeArrayIndirect[i]->SubnetMask;
FreeMapCount++;
}
if (Interfacep->AddressRangeCount > 1) { ExFreePool(RangeArrayIndirect); }
//
// Now we have an array of disjoint, non-adjacent address-ranges;
// Initialize the bitmap for each address-range.
//
for (i = 0; i < FreeMapCount; i++) {
//
// We can't allocate large enough bitmaps to support huge ranges;
// for instance, if the address pool is 128.0.0.0-128.255.255.255,
// the corresponding bitmap would have 2^24 bits, or 2MB.
// For now, shrink all ranges to allow at most 2^16 bits, or 8K.
//
j = RtlUlongByteSwap(FreeMapArray[i].EndAddress) -
RtlUlongByteSwap(FreeMapArray[i].StartAddress) + 1;
if (j >= IP_NAT_MAX_ADDRESS_RANGE) {
ERROR(("NatCreateAddressPool: shrinking %d-bit bitmap\n", j));
//
// Resize the range
//
FreeMapArray[i].EndAddress =
RtlUlongByteSwap(
RtlUlongByteSwap(FreeMapArray[i].StartAddress) +
IP_NAT_MAX_ADDRESS_RANGE
);
j = IP_NAT_MAX_ADDRESS_RANGE;
}
//
// Allocate a bitmap for the range
//
FreeMapArray[i].Bitmap =
(PRTL_BITMAP)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(RTL_BITMAP) +
(j + sizeof(ULONG) * 8 - 1) / sizeof(ULONG) * 8,
NAT_TAG_BITMAP
);
if (!FreeMapArray[i].Bitmap) {
ERROR(("NatCreateAddressPool: error allocating bitmap\n"));
while ((LONG)--i >= 0) { ExFreePool(FreeMapArray[i].Bitmap); }
ExFreePool(FreeMapArray);
return STATUS_NO_MEMORY;
}
INITIALIZE_BITMAP(
FreeMapArray[i].Bitmap, (PULONG)(FreeMapArray[i].Bitmap + 1), j
);
RtlClearAllBits(FreeMapArray[i].Bitmap);
}
status = STATUS_SUCCESS;
Interfacep->FreeMapArray = FreeMapArray;
Interfacep->FreeMapCount = FreeMapCount;
//
// Create address-pool entries for each local address
//
for (i = 0; i < Interfacep->AddressCount; i++) {
TRACE(
POOL, ("NatCreateAddressPool: address %d.%d.%d.%d\n",
ADDRESS_BYTES(Interfacep->AddressArray[i].Address))
);
status =
NatCreateAddressPoolEntry(
Interfacep,
Interfacep->AddressArray[i].Address,
Interfacep->AddressArray[i].Address,
NAT_POOL_FLAG_BINDING,
NULL,
&Usedp
);
if (!NT_SUCCESS(status)) { break; }
}
//
// Creating address-pool entries for each statically-mapped address
//
for (i = 0; i < Interfacep->AddressMappingCount; i++) {
TRACE(
POOL, ("NatCreateAddressPool: mapping %d.%d.%d.%d\n",
ADDRESS_BYTES(Interfacep->AddressMappingArray[i].PrivateAddress))
);
status =
NatCreateAddressPoolEntry(
Interfacep,
Interfacep->AddressMappingArray[i].PrivateAddress,
Interfacep->AddressMappingArray[i].PublicAddress,
NAT_POOL_FLAG_STATIC,
NULL,
&Usedp
);
if (!NT_SUCCESS(status)) { break; }
Usedp->AddressMapping = &Interfacep->AddressMappingArray[i];
}
//
// Create address-pool entries for statically-mapped port's address
//
for (i = Interfacep->PortMappingCount; i > 0; i--) {
status =
NatCreateStaticPortMapping(
Interfacep,
&Interfacep->PortMappingArray[i - 1]
);
if (!NT_SUCCESS(status)) { break; }
}
if (!NT_SUCCESS(status)) {
//
// An error occurred. Restore the original state.
//
NatDeleteAddressPool(Interfacep);
return status;
}
return STATUS_SUCCESS;
} // NatCreateAddressPool
NTSTATUS
NatCreateAddressPoolEntry(
PNAT_INTERFACE Interfacep,
ULONG PrivateAddress,
ULONG PublicAddress,
DWORD InitialFlags,
PNAT_USED_ADDRESS* InsertionPoint,
PNAT_USED_ADDRESS* AddressCreated
)
/*++
Routine Description:
This routine creates, initializes and inserts an address pool entry.
Arguments:
Interfacep - theinterface on which to create the entry.
PrivateAddress - the address of the private machine using the address
PublicAddress - the address for the entry, or 0 to allocate any address.
InitialFlags - initial flags for the address-entry, as follows:
NAT_POOL_FLAG_BINDING - if set, the address-entry is treated as
a binding-entry
NAT_POOL_FLAG_STATIC - if set, the address-entry corresponds to
a static mapping
InsertionPoint - optionally supplies the point where the entry
should be inserted in the tree.
AddressCreated - receives the entry created, or the existing entry
if there is a collision.
Return Value:
NTSTATUS - success/failure code.
--*/
{
ULONG ClassMask;
ULONG Hint;
ULONG HostOrderPublicAddress;
ULONG i;
ULONG Index;
PNAT_USED_ADDRESS Insert;
NTSTATUS status = STATUS_SUCCESS;
PNAT_USED_ADDRESS Usedp;
CALLTRACE(("NatCreateAddressPoolEntry\n"));
*AddressCreated = NULL;
if (PublicAddress) {
HostOrderPublicAddress = RtlUlongByteSwap(PublicAddress);
}
//
// Find the free-map which contains this binding, if any.
//
Index = (ULONG)-1;
for (i = 0; i < Interfacep->FreeMapCount; i++) {
//
// See if we're supposed to be looking for a free address
//
if (!PublicAddress) {
//
// See if this free-map has any free addresses.
//
for (Hint = 0; ; Hint = Index + 1) {
Index =
RtlFindClearBits(
Interfacep->FreeMapArray[i].Bitmap, 1, Hint
);
if (Index == (ULONG)-1) { break; }
//
// We've got a free address.
// Make sure it isn't a prohibited address
// (i.e. having 0 or all-ones in the subnet host portion).
//
PublicAddress =
RtlUlongByteSwap(
Index +
RtlUlongByteSwap(
Interfacep->FreeMapArray[i].StartAddress
));
ClassMask = GET_CLASS_MASK(PublicAddress);
if ((PublicAddress &
~Interfacep->FreeMapArray[i].SubnetMask) == 0 ||
(PublicAddress & ~Interfacep->FreeMapArray[i].SubnetMask) ==
~Interfacep->FreeMapArray[i].SubnetMask ||
(PublicAddress & ~ClassMask) == 0 ||
(PublicAddress & ~ClassMask) == ~ClassMask) {
//
// The address is prohibited.
// Mark it as unavailable so we don't waste time
// looking at it ever again.
//
RtlSetBits(Interfacep->FreeMapArray[i].Bitmap, Index, 1);
PublicAddress = 0; continue;
}
//
// The address is not prohibited
//
break;
}
//
// Go on to the next free-map if this one was of no use
//
if (Index == (ULONG)-1) { continue; }
//
// We found an address in the current free-map;
// go on to initialize a used-address entry
//
break;
}
//
// We're not looking for just any free-address;
// We're looking for the free-map of a particular address.
// See if the current free-map contains the address in question.
//
if (HostOrderPublicAddress >
RtlUlongByteSwap(Interfacep->FreeMapArray[i].EndAddress)) {
continue;
} else {
Index = RtlUlongByteSwap(Interfacep->FreeMapArray[i].StartAddress);
if (HostOrderPublicAddress < Index) {
Index = (ULONG)-1;
continue;
}
}
//
// This is the free-map we want.
// See if the address is prohibited, and if so fail.
//
Index = HostOrderPublicAddress - Index;
if ((PublicAddress & ~Interfacep->FreeMapArray[i].SubnetMask) == 0 ||
(PublicAddress & ~Interfacep->FreeMapArray[i].SubnetMask) ==
~Interfacep->FreeMapArray[i].SubnetMask) {
//
// The address is prohibited. Mark it so we don't waste time
// looking at it ever again.
//
RtlSetBits(Interfacep->FreeMapArray[i].Bitmap, Index, 1);
TRACE(POOL, ("NatCreateAddressPoolEntry: bad address requested\n"));
return STATUS_UNSUCCESSFUL;
}
break;
}
if (!PublicAddress) {
//
// We couldn't find a free address.
//
TRACE(POOL, ("NatCreateAddressPoolEntry: no free addresses\n"));
return STATUS_UNSUCCESSFUL;
}
//
// Find the insertion point in the used-tree.
//
if (!InsertionPoint) {
InsertionPoint = &Insert;
Usedp =
NatLookupAddressPoolEntry(
Interfacep->UsedAddressTree,
PrivateAddress,
PublicAddress,
InsertionPoint
);
if (Usedp) {
//
// This private-address already has a mapping; fail.
//
TRACE(POOL, ("NatCreateAddressPoolEntry: duplicate mapping\n"));
return STATUS_UNSUCCESSFUL;
}
}
//
// Allocate a new entry for the new address
//
Usedp =
ExAllocatePoolWithTag(
NonPagedPool,
sizeof(NAT_USED_ADDRESS),
NAT_TAG_USED_ADDRESS
);
if (!Usedp) {
ERROR(("NatCreateAddressPoolEntry: allocation failed\n"));
return STATUS_NO_MEMORY;
}
RtlZeroMemory(Usedp, sizeof(*Usedp));
Usedp->PrivateAddress = PrivateAddress;
Usedp->PublicAddress = PublicAddress;
Usedp->Key = MAKE_USED_ADDRESS_KEY(PrivateAddress, PublicAddress);
Usedp->Flags = InitialFlags;
Usedp->ReferenceCount = 1;
InitializeListHead(&Usedp->Link);
RtlInitializeSplayLinks(&Usedp->SLink);
if (NAT_POOL_BINDING(Usedp)) {
Usedp->StartPort = ReservedPortsLowerRange;
Usedp->EndPort = ReservedPortsUpperRange;
} else {
Usedp->StartPort = NTOHS(1);
Usedp->EndPort = NTOHS(65534);
}
Usedp->NextPortToTry = Usedp->StartPort;
//
// Insert the entry in the splay tree and list.
//
InsertTailList(&Interfacep->UsedAddressList, &Usedp->Link);
Interfacep->UsedAddressTree =
NatInsertAddressPoolEntry(*InsertionPoint, Usedp);
//
// Update the free-map
//
if (Index != (ULONG)-1) {
RtlSetBits(Interfacep->FreeMapArray[i].Bitmap, Index, 1);
}
*AddressCreated = Usedp;
return STATUS_SUCCESS;
}
NTSTATUS
NatCreateStaticPortMapping(
PNAT_INTERFACE Interfacep,
PIP_NAT_PORT_MAPPING PortMapping
)
/*++
Routine Description:
This routine creates a static port mapping (i.e., persistent
ticket) on an interace
Arguments:
Interfacep - the interface on which to create the port mapping.
PortMapping - describes the port mapping to be created.
Return Value:
NTSTATUS - status code
Environment:
Invoked with Interfacep->Lock held by the caller.
--*/
{
PNAT_USED_ADDRESS InsertionPoint;
ULONG PublicAddress;
USHORT PublicPort;
NTSTATUS status;
PNAT_TICKET Ticketp;
PNAT_USED_ADDRESS Usedp;
CALLTRACE(("NatCreateStaticPortMapping\n"));
//
// The handling of the static port-mapping depends upon whether
// its public address is for the interface or from the address-pool.
// If the 'PublicAddress' is zero, then the port-mapping refers
// to sessions destined for the interface's actual address.
// Otherwise, the port-mapping refers to sessions destined for
// an address in the interface's address pool.
//
if (!PortMapping->PublicAddress) {
status =
NatAcquireFromAddressPool(
Interfacep,
PortMapping->PrivateAddress,
0,
&Usedp
);
} else {
Usedp =
NatLookupAddressPoolEntry(
Interfacep->UsedAddressTree,
PortMapping->PrivateAddress,
PortMapping->PublicAddress,
&InsertionPoint
);
if (Usedp) {
status = STATUS_SUCCESS;
NatReferenceAddressPoolEntry(Usedp);
} else {
//
// The mapping was not in use, so we need to create an
// entry for its address.
//
status =
NatCreateAddressPoolEntry(
Interfacep,
PortMapping->PrivateAddress,
PortMapping->PublicAddress,
0,
&InsertionPoint,
&Usedp
);
}
}
//
// Now create a ticket which will direct all incoming sessions
// to the private endpoint specified in the static port-mapping.
//
if (NT_SUCCESS(status)) {
status =
NatCreateTicket(
Interfacep,
PortMapping->Protocol,
PortMapping->PrivateAddress,
PortMapping->PrivatePort,
0,
0,
NAT_TICKET_FLAG_PORT_MAPPING|NAT_TICKET_FLAG_PERSISTENT,
Usedp,
PortMapping->PublicPort,
&PublicAddress,
&PublicPort
);
NatDereferenceAddressPoolEntry(Interfacep, Usedp);
}
return status;
}// NatCreateStaticPortMapping
NTSTATUS
NatDeleteAddressPool(
PNAT_INTERFACE Interfacep
)
/*++
Routine Description:
Destroys an address pool, freeing the memory used by the free-maps
and optionally, the used-address entries.
Arguments:
Interfacep - the interface whose address pool is to be deleted.
Return Value:
NTSTATUS - status code.
--*/
{
ULONG i;
PLIST_ENTRY Link;
PNAT_TICKET Ticketp;
PNAT_USED_ADDRESS Usedp;
CALLTRACE(("NatDeleteAddressPool\n"));
//
// Dispose of the free-maps
//
for (i = 0; i < Interfacep->FreeMapCount; i++) {
if (Interfacep->FreeMapArray[i].Bitmap) {
ExFreePool(Interfacep->FreeMapArray[i].Bitmap);
}
}
Interfacep->FreeMapCount = 0;
if (Interfacep->FreeMapArray) { ExFreePool(Interfacep->FreeMapArray); }
Interfacep->FreeMapArray = NULL;
//
// Clear out port-mappings created as tickets
//
for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
Link = Link->Flink) {
Ticketp = CONTAINING_RECORD(Link, NAT_TICKET, Link);
if (!NAT_TICKET_PORT_MAPPING(Ticketp)) { continue; }
Link = Link->Blink;
NatDeleteTicket(Interfacep, Ticketp);
}
//
// Deal with the used-list;
//
while (!IsListEmpty(&Interfacep->UsedAddressList)) {
Link = RemoveHeadList(&Interfacep->UsedAddressList);
Usedp = CONTAINING_RECORD(Link, NAT_USED_ADDRESS, Link);
Usedp->Flags |= NAT_POOL_FLAG_DELETED;
NatDereferenceAddressPoolEntry(Interfacep, Usedp);
}
Interfacep->UsedAddressTree = NULL;
return STATUS_SUCCESS;
} // NatDeleteAddressPool
PNAT_USED_ADDRESS
NatInsertAddressPoolEntry(
PNAT_USED_ADDRESS Parent,
PNAT_USED_ADDRESS Addressp
)
/*++
Routine Description:
This routine is called to insert an address in the interface's
splay-tree of address-pool entries. The key is the 'PrivateAddress'
field of the address-pool entry.
Arguments:
Parent - the parent of the entry to be inserted, which may be obtained
from NatLookupAddressPoolEntry
Addressp - the address pool entry to be inserted
Return Value:
The new root of the splay tree if successful, NULL otherwise.
--*/
{
ULONG64 Key;
PRTL_SPLAY_LINKS Root;
CALLTRACE(("NatInsertAddressPoolEntry\n"));
if (!Parent) {
TRACE(POOL, ("NatInsertAddressPoolEntry: inserting as root\n"));
return Addressp;
}
//
// Insert as left or right child
//
Key =
MAKE_USED_ADDRESS_KEY(Addressp->PrivateAddress,Addressp->PublicAddress);
if (Addressp->Key < Parent->Key) {
RtlInsertAsLeftChild(&Parent->SLink, &Addressp->SLink);
} else if (Addressp->Key > Parent->Key) {
RtlInsertAsRightChild(&Parent->SLink, &Addressp->SLink);
} else {
//
// Keys are equal; fail.
//
ERROR((
"NatInsertAddressPoolEntry: collision on key 0x%016I64X\n",
Addressp->Key
));
return NULL;
}
//
// Splay the new node and return the resulting root.
//
Root = RtlSplay(&Addressp->SLink);
return CONTAINING_RECORD(Root, NAT_USED_ADDRESS, SLink);
} // NatInsertAddressPoolEntry
PNAT_USED_ADDRESS
NatLookupAddressPoolEntry(
PNAT_USED_ADDRESS Root,
ULONG PrivateAddress,
ULONG PublicAddress,
PNAT_USED_ADDRESS* InsertionPoint
)
/*++
Routine Description:
This routine searches the interface's splay tree of address-pool entries
for a particular entry, returning the entry if found, otherwise supplying
the insertion point for the entry.
Arguments:
Root - the root of the splay tree ('Interfacep->UsedAddressTree').
PrivateAddress - the private part of the address-mapping to be looked up
PublicAddress - the public part of the address-mapping to be looked up
InsertionPoint - receives the insertion point for the entry
Return Value:
The entry if found, NULL otherwise.
--*/
{
PNAT_USED_ADDRESS Addressp;
ULONG64 Key;
PNAT_USED_ADDRESS Parent = NULL;
PRTL_SPLAY_LINKS SLink;
TRACE(PER_PACKET, ("NatLookupAddressPoolEntry\n"));
Key = MAKE_USED_ADDRESS_KEY(PrivateAddress, PublicAddress);
for (SLink = !Root ? NULL : &Root->SLink; SLink; ) {
Addressp = CONTAINING_RECORD(SLink, NAT_USED_ADDRESS, SLink);
if (Key < Addressp->Key) {
Parent = Addressp;
SLink = RtlLeftChild(SLink);
continue;
} else if (Key > Addressp->Key) {
Parent = Addressp;
SLink = RtlRightChild(SLink);
continue;
}
//
// Private-addresses match; we got it.
//
return Addressp;
}
//
// We didn't get it; tell the caller where to insert it.
//
if (InsertionPoint) { *InsertionPoint = Parent; }
return NULL;
} // NatLookupAddressPoolEntry
PNAT_USED_ADDRESS
NatLookupStaticAddressPoolEntry(
PNAT_INTERFACE Interfacep,
ULONG PublicAddress,
BOOLEAN RequireInboundSessions
)
/*++
Routine Description:
This routine is invoked to search for an address pool entry
which is marked static and which corresponds to the given public address.
Arguments:
Interfacep - the interface whose address pool is to be searched
PublicAddress - the public address for which to search
RequireInboundSessions - if TRUE, a match is declared only if
the entry found allows inbound sessions.
Return Value:
PNAT_USED_ADDRESS - the address pool entry found, if any.
--*/
{
ULONG i;
PNAT_USED_ADDRESS Addressp;
PIP_NAT_ADDRESS_MAPPING AddressMapping;
if (!Interfacep->AddressMappingCount) {
return NULL;
} else {
//
// Perform exhaustive search since static address-mappings
// are sorted by private address rather than public address.
//
AddressMapping = NULL;
for (i = 0; i < Interfacep->AddressMappingCount; i++) {
if (PublicAddress !=
Interfacep->AddressMappingArray[i].PublicAddress) {
continue;
}
AddressMapping = &Interfacep->AddressMappingArray[i];
break;
}
}
if (!AddressMapping ||
!(Addressp =
NatLookupAddressPoolEntry(
Interfacep->UsedAddressTree,
AddressMapping->PrivateAddress,
AddressMapping->PublicAddress,
NULL
)) ||
!NAT_POOL_STATIC(Addressp) ||
(RequireInboundSessions && !AddressMapping->AllowInboundSessions)) {
return NULL;
}
return Addressp;
} // NatLookupStaticAddressPoolEntry
NTSTATUS
NatDereferenceAddressPoolEntry(
PNAT_INTERFACE Interfacep,
PNAT_USED_ADDRESS AddressToRelease
)
/*++
Routine Description:
Drops the reference count on an address pool entry.
This routine invalidates the supplied NAT_USED_ADDRESS pointer.
Note, though, that it might not be freed, e.g. in the case where
the address is being shared.
If the entry is a placeholder and its reference count drops to zero,
we also drop the reference count of its target ('SharedAddress') entry.
N.B. If the entry is marked 'deleted', we ignore the 'Interfacep' argument
since the entry must have been already removed from its interface's
address pool.
Arguments:
Interfacep - the interface whose address pool is to be deleted.
AddressToRelease - contains a pointer to the address to be released.
Return Value:
NTSTATUS - status code.
--*/
{
ULONG HostOrderPublicAddress;
ULONG i;
ULONG Index;
PRTL_SPLAY_LINKS SLink;
PNAT_USED_ADDRESS Usedp = AddressToRelease;
CALLTRACE(("NatDereferenceAddressPoolEntry\n"));
if (NAT_POOL_PLACEHOLDER(Usedp)) {
//
// Do nothing if there are other references to the placeholder
//
if (InterlockedDecrement(&Usedp->ReferenceCount)) {
return STATUS_SUCCESS;
}
//
// Unlink and free the placeholder
//
if (!NAT_POOL_DELETED(Usedp)) {
RemoveEntryList(&Usedp->Link);
SLink = RtlDelete(&Usedp->SLink);
Interfacep->UsedAddressTree =
!SLink
? NULL
: CONTAINING_RECORD(SLink, NAT_USED_ADDRESS, SLink);
}
//
// Move on to the shared-address
//
Usedp = Usedp->SharedAddress;
ExFreePool(AddressToRelease);
}
//
// Do nothing if there are others sharing this address
//
if (InterlockedDecrement(&Usedp->ReferenceCount)) { return STATUS_SUCCESS; }
if (!NAT_POOL_DELETED(Usedp)) {
//
// Marking the entry's address as free in the interface's bitmap.
//
Index = Usedp->PublicAddress;
HostOrderPublicAddress = RtlUlongByteSwap(Usedp->PublicAddress);
for (i = 0; i < Interfacep->FreeMapCount; i++) {
if (HostOrderPublicAddress <
RtlUlongByteSwap(Interfacep->FreeMapArray[i].StartAddress) ||
HostOrderPublicAddress >
RtlUlongByteSwap(Interfacep->FreeMapArray[i].EndAddress)) {
continue;
}
Index =
HostOrderPublicAddress -
RtlUlongByteSwap(Interfacep->FreeMapArray[i].StartAddress);
RtlClearBits(Interfacep->FreeMapArray[i].Bitmap, Index, 1);
break;
}
//
// Now we're done; just unlink and free the used-address block
//
RemoveEntryList(&Usedp->Link);
SLink = RtlDelete(&Usedp->SLink);
Interfacep->UsedAddressTree =
!SLink ? NULL : CONTAINING_RECORD(SLink, NAT_USED_ADDRESS, SLink);
}
ExFreePool(Usedp);
return STATUS_SUCCESS;
} // NatDereferenceAddressPoolEntry
//
// PORT-POOL ROUTINES (alphabetically)
//
NTSTATUS
NatAcquireFromPortPool(
PNAT_INTERFACE Interfacep,
PNAT_USED_ADDRESS Addressp,
UCHAR Protocol,
USHORT PreferredPort,
PUSHORT PortAcquired
)
/*++
Routine Description:
This routine is invoked to acquire a unique public port from the pool.
The port acquired is guaranteed to not be in use by any mapping.
This is needed, for instance, when we are obtaining a port for a ticket
to be created by an editor for a dynamically negotiated session.
Arguments:
Interfacep - the interface on which to acquire the port
Addressp - the address on which to acquire the port
Protocol - the protocol for which to acquire the port
PreferredPort - the port preferred by the caller
PortAcquired - receives the port acquired
Return Value:
NTSTATUS - success/failure code.
Environment:
Invoked with 'MappingLock' and 'Interfacep->Lock' held by the caller.
--*/
{
#define QUERY_PUBLIC_PORT(m,p) \
NatQueryInformationMapping((m),NULL,NULL,NULL,NULL,NULL,NULL,p,NULL)
USHORT EndPort;
ULONG i;
PLIST_ENTRY Link;
PNAT_DYNAMIC_MAPPING Mapping;
USHORT Port;
USHORT PublicPort;
PNAT_USED_ADDRESS SharedAddress;
CALLTRACE(("NatAcquireFromPortPool\n"));
SharedAddress = Addressp;
PLACEHOLDER_TO_ADDRESS(SharedAddress);
//
// First attempt to satisfy the caller's preference
//
if (PreferredPort) {
//
// The caller prefers that we acquire a particular port;
// see if we can satisfy the request.
//
do {
Port = NTOHS(PreferredPort);
if (Port < NTOHS(SharedAddress->StartPort) ||
Port > NTOHS(SharedAddress->EndPort)) {
break;
}
//
// The preferred port is in the current range.
// See if it is in use by another mapping
//
KeAcquireSpinLockAtDpcLevel(&InterfaceMappingLock);
for (Link = Interfacep->MappingList.Flink;
Link != &Interfacep->MappingList; Link = Link->Flink) {
Mapping =
CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, InterfaceLink);
QUERY_PUBLIC_PORT(Mapping, &PublicPort);
if (PreferredPort == PublicPort) { break; }
}
if (Link != &Interfacep->MappingList) {
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
break;
}
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
//
// Now see if it is in use by a ticket
//
if (NatIsPortUsedByTicket(Interfacep, Protocol, PreferredPort)) {
break;
}
//
// The preferred port is available; return
//
TRACE(
POOL,
("NatAcquireFromPortPool: using preferred port %d\n",
NTOHS(PreferredPort)
));
*PortAcquired = PreferredPort;
return STATUS_SUCCESS;
} while(FALSE);
//
// We couldn't acquire the preferred port;
// fail if no other port is acceptable.
//
if (!NAT_INTERFACE_NAPT(Interfacep)) {
TRACE(
POOL,
("NatAcquireFromPortPool: unable to use preferred port %d\n",
NTOHS(PreferredPort)
));
return STATUS_UNSUCCESSFUL;
}
}
//
// Search the port pool, looking for a port which
// (a) is not already in use by any mapping, and
// (b) is not already in use by any ticket.
//
EndPort = RtlUshortByteSwap((USHORT)(NTOHS(SharedAddress->EndPort) + 1));
for (Port = SharedAddress->StartPort; Port != EndPort;
Port = RtlUshortByteSwap((USHORT)(NTOHS(Port) + 1))) {
//
// Look through the interface's mappings
//
KeAcquireSpinLockAtDpcLevel(&InterfaceMappingLock);
for (Link = Interfacep->MappingList.Flink;
Link != &Interfacep->MappingList; Link = Link->Flink) {
Mapping =
CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, InterfaceLink);
QUERY_PUBLIC_PORT(Mapping, &PublicPort);
if (Port == PublicPort) { break; }
}
if (Link != &Interfacep->MappingList) {
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
continue;
}
KeReleaseSpinLockFromDpcLevel(&InterfaceMappingLock);
//
// No mapping is using the public-port;
// Now see if any ticket is using the public port
//
if (NatIsPortUsedByTicket(Interfacep, Protocol, Port)) { continue; }
//
// The port is not in use by any mapping or ticket; we're done.
//
TRACE(
POOL, ("NatAcquireFromPortPool: acquiring port %d\n",
NTOHS(Port)
));
*PortAcquired = Port;
return STATUS_SUCCESS;
}
TRACE(POOL, ("NatAcquireFromPortPool: no available ports\n"));
return STATUS_UNSUCCESSFUL;
} // NatAcquireFromPortPool