Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2050 lines
62 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
icmp.c
Abstract:
This module contains the code for manipulating ICMP request/reply mappings.
When the NAT decides to translate an ICMP request, it creates a mapping
and places it on the interface's ICMP-mapping list, so that when the reply
to the request arrives, it can be directed to the appropriate client.
Author:
Abolade Gbadegesin (t-abolag) 31-July-1997
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// GLOBAL DATA DECLARATIONS
//
NPAGED_LOOKASIDE_LIST IcmpLookasideList;
LIST_ENTRY IcmpMappingList[NatMaximumDirection];
KSPIN_LOCK IcmpMappingLock;
//
// FORWARD DECLARATIONS
//
FORWARD_ACTION
NatpFirewallIcmp(
PNAT_INTERFACE Interfacep,
IP_NAT_DIRECTION Direction,
PNAT_XLATE_CONTEXT Contextp
);
BOOLEAN
NatTranslateIcmpEncapsulatedRequest(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PIP_HEADER IpHeader,
PICMP_HEADER IcmpHeader,
PIP_HEADER EncapsulatedIpHeader,
struct _ENCAPSULATED_ICMP_HEADER* EncapsulatedIcmpHeader,
BOOLEAN ChecksumOffloaded
);
BOOLEAN
NatTranslateIcmpRequest(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PNAT_XLATE_CONTEXT Contextp,
BOOLEAN ReplyCode,
BOOLEAN ChecksumOffloaded
);
BOOLEAN
NatTranslateEncapsulatedRequest(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PIP_HEADER IpHeader,
PICMP_HEADER IcmpHeader,
PIP_HEADER EncapsulatedIpHeader,
struct _ENCAPSULATED_UDP_HEADER* EncapsulatedHeader,
BOOLEAN ChecksumOffloaded
);
NTSTATUS
NatCreateIcmpMapping(
PNAT_INTERFACE Interfacep,
ULONG RemoteAddress,
ULONG PrivateAddress,
ULONG PublicAddress,
PUSHORT PrivateId,
PUSHORT PublicId,
PLIST_ENTRY InboundInsertionPoint,
PLIST_ENTRY OutboundInsertionPoint,
PNAT_ICMP_MAPPING* MappingCreated
)
/*++
Routine Description:
Called to create, initialize, and insert an ICMP mapping in an interface's
list of ICMP mappings.
For outbound ICMP requests, we allocate a unique 'PublicId' for the mapping,
and for inbound requests, we allocate a unique 'PrivateId' for the mapping.
Arguments:
Interfacep - the interface for the new mapping
RemoteAddress - the address of the remote endpoint
PrivateAddress - the address of the machine on the private network
PublicAddress - the publicly-visible address to replace 'PrivateAddress';
in case this is 0, an address is chosen in this routine.
PrivateId - the private-endpoint's identifier for the ICMP message,
or NULL if an identifier should be chosen by this routine.
PublicId - the public-endpoint's identifier for the ICMP message,
or NULL if an identifier should be chosen by this routine.
InboundInsertionPoint - the entry preceding the new mapping in the list
sorted for inbound searching
OutboundInsertionPoint - the entry preceding the new mapping in the list
sorted for outbound searching
MappingCreated - receives the mapping created
Return Value:
NTSTATUS - indicates success/failure
Environment:
Invoked with 'IcmpMappingLock' held by the caller.
--*/
{
USHORT Id;
PLIST_ENTRY Link;
PNAT_ICMP_MAPPING Mapping;
PNAT_ICMP_MAPPING DuplicateMapping;
NTSTATUS status;
PNAT_ICMP_MAPPING Temp;
PNAT_USED_ADDRESS UsedAddress;
CALLTRACE(("NatCreateIcmpMapping\n"));
//
// Allocate a new mapping
//
Mapping = ALLOCATE_ICMP_BLOCK();
if (!Mapping) {
ERROR(("NatCreateIcmpMapping: allocation failed\n"));
return STATUS_NO_MEMORY;
}
//
// Initialize the mapping
//
Mapping->PrivateKey = MAKE_ICMP_KEY(RemoteAddress, PrivateAddress);
//
// See if the public address is specified, and if not, acquire an address
//
if (PublicAddress) {
Mapping->PublicKey = MAKE_ICMP_KEY(RemoteAddress, PublicAddress);
} else {
//
// Acquire an address mapping for the ICMP mapping
//
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
status =
NatAcquireFromAddressPool(
Interfacep,
PrivateAddress,
0,
&UsedAddress
);
if (!NT_SUCCESS(status)) {
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
TRACE(ICMP, ("NatCreateIcmpMapping: no address available\n"));
FREE_ICMP_BLOCK(Mapping);
return STATUS_UNSUCCESSFUL;
}
PublicAddress = UsedAddress->PublicAddress;
NatDereferenceAddressPoolEntry(Interfacep, UsedAddress);
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
Mapping->PublicKey = MAKE_ICMP_KEY(RemoteAddress, PublicAddress);
}
//
// If no 'PrivateId' is specified, select one.
//
if (PrivateId) {
Mapping->PrivateId = *PrivateId;
} else {
//
// Find the next available identifier
// by searching the list of inbound mappings
//
Id = 1;
for (Link = IcmpMappingList[NatOutboundDirection].Flink;
Link != &IcmpMappingList[NatOutboundDirection];
Link = Link->Flink) {
Temp =
CONTAINING_RECORD(
Link, NAT_ICMP_MAPPING, Link[NatOutboundDirection]
);
if (Mapping->PrivateKey > Temp->PrivateKey) {
continue;
} else if (Mapping->PrivateKey < Temp->PrivateKey) {
break;
}
//
// Primary key equal; see if the identifier we've chosen
// collides with this one
//
if (Id > Temp->PrivateId) {
continue;
} else if (Id < Temp->PrivateId) {
break;
}
//
// The identifier's collide; choose another and go on
//
++Id;
}
if (Link == &IcmpMappingList[NatOutboundDirection] && !Id) {
//
// We are at the end of the list, and all 64K-1 IDs are taken
//
FREE_ICMP_BLOCK(Mapping);
return STATUS_UNSUCCESSFUL;
}
Mapping->PrivateId = Id;
//
// And by the way, we now have the outbound insertion point
//
if (!OutboundInsertionPoint) { OutboundInsertionPoint = Link; }
}
//
// If no 'PublicId' is specified, select one.
//
if (PublicId) {
Mapping->PublicId = *PublicId;
} else {
//
// Find the next available identifier
// by searching the list of inbound mappings
//
Id = 1;
for (Link = IcmpMappingList[NatInboundDirection].Flink;
Link != &IcmpMappingList[NatInboundDirection];
Link = Link->Flink) {
Temp =
CONTAINING_RECORD(
Link, NAT_ICMP_MAPPING, Link[NatInboundDirection]
);
if (Mapping->PublicKey > Temp->PublicKey) {
continue;
} else if (Mapping->PublicKey < Temp->PublicKey) {
break;
}
//
// Primary key equal; see if the identifier we've chosen
// collides with this one
//
if (Id > Temp->PublicId) {
continue;
} else if (Id < Temp->PublicId) {
break;
}
//
// The identifier's collide; choose another and go on
//
++Id;
}
if (Link == &IcmpMappingList[NatInboundDirection] && !Id) {
//
// We are at the end of the list, and all 64K-1 IDs are taken
//
FREE_ICMP_BLOCK(Mapping);
return STATUS_UNSUCCESSFUL;
}
Mapping->PublicId = Id;
//
// And by the way, we now have the inbound insertion point
//
if (!InboundInsertionPoint) { InboundInsertionPoint = Link; }
}
TRACE(
MAPPING,
("NatCreateIcmpMapping: Icmp=%016I64X:%04X::%016I64X:%04X\n",
Mapping->PrivateKey, Mapping->PrivateId,
Mapping->PublicKey, Mapping->PublicId
));
//
// Insert the mapping in the inbound list
//
if (!InboundInsertionPoint) {
DuplicateMapping =
NatLookupInboundIcmpMapping(
Mapping->PrivateKey,
Mapping->PrivateId,
&InboundInsertionPoint
);
if (NULL != DuplicateMapping) {
//
// This mapping already exists on the inbound path
//
FREE_ICMP_BLOCK(Mapping);
return STATUS_UNSUCCESSFUL;
}
}
InsertTailList(InboundInsertionPoint, &Mapping->Link[NatInboundDirection]);
//
// Insert the mapping in the outbound list
//
if (!OutboundInsertionPoint) {
DuplicateMapping =
NatLookupOutboundIcmpMapping(
Mapping->PublicKey,
Mapping->PublicId,
&OutboundInsertionPoint
);
if (NULL != DuplicateMapping) {
//
// This mapping already exists on the outbound path
//
RemoveEntryList(&Mapping->Link[NatInboundDirection]);
FREE_ICMP_BLOCK(Mapping);
return STATUS_UNSUCCESSFUL;
}
}
InsertTailList(
OutboundInsertionPoint, &Mapping->Link[NatOutboundDirection]
);
*MappingCreated = Mapping;
return STATUS_SUCCESS;
} // NatCreateIcmpMapping
VOID
NatInitializeIcmpManagement(
VOID
)
/*++
Routine Description:
This routine is called to initialize the ICMP translation module.
Arguments:
none.
Return Value:
none.
--*/
{
CALLTRACE(("NatInitializeIcmpManagement\n"));
KeInitializeSpinLock(&IcmpMappingLock);
InitializeListHead(&IcmpMappingList[NatInboundDirection]);
InitializeListHead(&IcmpMappingList[NatOutboundDirection]);
ExInitializeNPagedLookasideList(
&IcmpLookasideList,
NatAllocateFunction,
NULL,
0,
sizeof(NAT_ICMP_MAPPING),
NAT_TAG_ICMP,
ICMP_LOOKASIDE_DEPTH
);
} // NatInitializeIcmpManagement
PNAT_ICMP_MAPPING
NatLookupInboundIcmpMapping(
ULONG64 PublicKey,
USHORT PublicId,
PLIST_ENTRY* InsertionPoint
)
/*++
Routine Description:
This routine is called to find an ICMP mapping using the remote-address
and the publicly-visible address, which correspond to the 'PublicKey',
and the 'PublicId' field.
Arguments:
PublicKey - the remote-address/public-address combination
PublicId - the mapping's public identifier
InsertionPoint - receives the insertion-point if the mapping is not found.
Return Value:
PNAT_ICMP_MAPPING - the mapping found, or NULL if not found.
--*/
{
PLIST_ENTRY Link;
PNAT_ICMP_MAPPING Mapping;
CALLTRACE(("NatLookupInboundIcmpMapping\n"));
if (InsertionPoint) { *InsertionPoint = NULL; }
for (Link = IcmpMappingList[NatInboundDirection].Flink;
Link != &IcmpMappingList[NatInboundDirection]; Link = Link->Flink) {
Mapping =
CONTAINING_RECORD(
Link, NAT_ICMP_MAPPING, Link[NatInboundDirection]
);
if (PublicKey > Mapping->PublicKey) {
continue;
} else if (PublicKey < Mapping->PublicKey) {
break;
}
//
// Primary keys equal; check secondary keys.
//
if (PublicId > Mapping->PublicId) {
continue;
} else if (PublicId < Mapping->PublicId) {
break;
}
//
// Secondary keys equal, too. This is the requested item.
//
return Mapping;
}
if (InsertionPoint) { *InsertionPoint = Link; }
return NULL;
} // NatLookupInboundIcmpMapping
PNAT_ICMP_MAPPING
NatLookupOutboundIcmpMapping(
ULONG64 PrivateKey,
USHORT PrivateId,
PLIST_ENTRY* InsertionPoint
)
/*++
Routine Description:
This routine is called to find an ICMP mapping using the remote-address
and the private address, which correspond to the 'PrivateKey'.
Arguments:
PrivateKey - the remote-address/private-address combination
PrivateId - the mapping's private identifier
InsertionPoint - receives insertion-point if mapping not found.
Return Value:
PNAT_ICMP_MAPPING - the mapping found, or NULL if not found.
--*/
{
PLIST_ENTRY Link;
PNAT_ICMP_MAPPING Mapping;
CALLTRACE(("NatLookupOutboundIcmpMapping\n"));
if (InsertionPoint) { *InsertionPoint = NULL; }
for (Link = IcmpMappingList[NatOutboundDirection].Flink;
Link != &IcmpMappingList[NatOutboundDirection]; Link = Link->Flink) {
Mapping =
CONTAINING_RECORD(
Link, NAT_ICMP_MAPPING, Link[NatOutboundDirection]
);
if (PrivateKey > Mapping->PrivateKey) {
continue;
} else if (PrivateKey < Mapping->PrivateKey) {
break;
}
//
// Primary keys equal; check secondary keys.
//
if (PrivateId > Mapping->PrivateId) {
continue;
} else if (PrivateId < Mapping->PrivateId) {
break;
}
//
// Keys are equal, so we've found it.
//
return Mapping;
}
if (InsertionPoint) { *InsertionPoint = Link; }
return NULL;
} // NatLookupOutboundIcmpMapping
FORWARD_ACTION
NatpFirewallIcmp(
PNAT_INTERFACE Interfacep,
IP_NAT_DIRECTION Direction,
PNAT_XLATE_CONTEXT Contextp
)
/*++
Routine Description:
This routine encapsulates the ICMP firewall logic. It is
only used (as of now) for non-boundary FW interfaces
Arguments:
Interfacep - the boundary interface over which to translate.
Direction - the direction in which the packet is traveling
Contextp - initialized with context-information for the packet
Return Value:
FORWARD_ACTION - indicates action to take on packet.
Environment:
Invoked with a reference made to 'Interfacep'.
--*/
{
FORWARD_ACTION act;
ULONG i;
PICMP_HEADER IcmpHeader;
PIP_HEADER IpHeader;
PNAT_ICMP_MAPPING IcmpMapping;
ULONG64 PublicKey;
ULONG64 RemoteKey;
PLIST_ENTRY InsertionPoint;
NTSTATUS ntStatus;
TRACE(XLATE, ("NatpFirewallIcmp\n"));
if (NatOutboundDirection == Direction) {
//
// Make sure this packet has a valid source address
// for this interface.
//
act = DROP;
for (i = 0; i < Interfacep->AddressCount; i++) {
if (Contextp->SourceAddress ==
Interfacep->AddressArray[i].Address
) {
act = FORWARD;
break;
}
}
if (DROP == act) {
//
// Invalid source addess -- packet should be
// dropped w/o any further processing.
//
return act;
}
}
IpHeader = Contextp->Header;
IcmpHeader = (PICMP_HEADER)Contextp->ProtocolHeader;
switch (IcmpHeader->Type) {
//
// Message forewarded only if a corresponding mapping
// exists. A mapping for an outbound packet can exist only
// if the use chooses to allow the corresponding request
// type.
//
case ICMP_ECHO_REPLY:
case ICMP_TIMESTAMP_REPLY:
case ICMP_ROUTER_REPLY:
case ICMP_MASK_REPLY: {
if (NatInboundDirection == Direction) {
PublicKey =
MAKE_ICMP_KEY(
Contextp->SourceAddress,
Contextp->DestinationAddress
);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
IcmpMapping =
NatLookupInboundIcmpMapping(
PublicKey,
IcmpHeader->Identifier,
&InsertionPoint
);
if (NULL != IcmpMapping) {
KeQueryTickCount(
(PLARGE_INTEGER)&IcmpMapping->LastAccessTime
);
}
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
act = IcmpMapping != NULL ? FORWARD : DROP;
} else {
PublicKey =
MAKE_ICMP_KEY(
Contextp->DestinationAddress,
Contextp->SourceAddress
);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
IcmpMapping =
NatLookupOutboundIcmpMapping(
PublicKey,
IcmpHeader->Identifier,
&InsertionPoint
);
if (NULL != IcmpMapping) {
KeQueryTickCount(
(PLARGE_INTEGER)&IcmpMapping->LastAccessTime
);
}
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
act = IcmpMapping != NULL ? FORWARD : DROP;
}
break;
}
//
// Outbound messages create a mapping and are forwarded.
// Inbound messages are dropped, unless configured to
// allow inbound; if this is the case, create a mapping
// and forward. The mapping will allow the response
// to go through the firewall
//
case ICMP_ECHO_REQUEST:
case ICMP_TIMESTAMP_REQUEST:
case ICMP_ROUTER_REQUEST:
case ICMP_MASK_REQUEST: {
if (NatOutboundDirection == Direction) {
act = FORWARD;
//
// Check to see if a mapping already exists
//
PublicKey =
MAKE_ICMP_KEY(
Contextp->DestinationAddress,
Contextp->SourceAddress
);
KeAcquireSpinLockAtDpcLevel (&IcmpMappingLock);
IcmpMapping =
NatLookupOutboundIcmpMapping(
PublicKey,
IcmpHeader->Identifier,
&InsertionPoint
);
if (NULL == IcmpMapping) {
//
// One didn't -- create a new mappping.
//
ntStatus =
NatCreateIcmpMapping(
Interfacep,
Contextp->DestinationAddress,
Contextp->SourceAddress,
Contextp->SourceAddress,
&IcmpHeader->Identifier,
&IcmpHeader->Identifier,
NULL,
NULL,
&IcmpMapping
);
if (!NT_SUCCESS(ntStatus)) {
TRACE(
XLATE, (
"NatIcmpFirewall: error 0x%x creating mapping\n",
ntStatus
));
act = DROP;
}
} else {
KeQueryTickCount(
(PLARGE_INTEGER)&IcmpMapping->LastAccessTime
);
}
KeReleaseSpinLockFromDpcLevel( &IcmpMappingLock );
} else {
//
// Check to see if inbound for this type is permitted. If
// so, create a mapping and forward.
//
if (NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)) {
act = FORWARD;
//
// Check to see if a mapping already exists
//
PublicKey =
MAKE_ICMP_KEY(
Contextp->SourceAddress,
Contextp->DestinationAddress
);
KeAcquireSpinLockAtDpcLevel (&IcmpMappingLock);
IcmpMapping =
NatLookupInboundIcmpMapping(
PublicKey,
IcmpHeader->Identifier,
&InsertionPoint
);
if (NULL == IcmpMapping) {
//
// One didn't -- create a new mappping.
//
ntStatus =
NatCreateIcmpMapping(
Interfacep,
Contextp->SourceAddress,
Contextp->DestinationAddress,
Contextp->DestinationAddress,
&IcmpHeader->Identifier,
&IcmpHeader->Identifier,
NULL,
NULL,
&IcmpMapping
);
if (!NT_SUCCESS(ntStatus)) {
TRACE(
XLATE, (
"NatIcmpFirewall: error 0x%x creating mapping\n",
ntStatus
));
act = DROP;
}
} else {
KeQueryTickCount(
(PLARGE_INTEGER)&IcmpMapping->LastAccessTime
);
}
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
} else {
//
// Not permitted.
//
act = DROP;
}
}
break;
}
//
// These messages are allowed inbound, but are dropped outbound
// (unless the user chooses to allow them). Allowing outbound creates
// more avenues of attack for port-scanning tools.
//
case ICMP_TIME_EXCEED:
case ICMP_PARAM_PROBLEM:
case ICMP_DEST_UNREACH:
case ICMP_SOURCE_QUENCH: {
if (NatInboundDirection == Direction) {
act = FORWARD;
} else {
act =
(NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)
? FORWARD
: DROP);
}
break;
}
//
// These messages are always dropped, no matter the direction
// (unless the user chooses to allow them).
//
case ICMP_REDIRECT: {
act =
(NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)
? FORWARD
: DROP);
break;
}
//
// Anything else is dropped by default.
//
default: {
act = DROP;
break;
}
}
return act;
} // NatpFirewallIcmp
VOID
NatShutdownIcmpManagement(
VOID
)
/*++
Routine Description:
This routine is invoked to clean up the ICMP management module
when the NAT driver is unloaded.
Arguments:
none.
Return Value:
none.
--*/
{
ExDeleteNPagedLookasideList(&IcmpLookasideList);
} // NatShutdownIcmpManagement
FORWARD_ACTION
NatTranslateIcmp(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PNAT_XLATE_CONTEXT Contextp,
IPRcvBuf** InRecvBuffer,
IPRcvBuf** OutRecvBuffer
)
/*++
Routine Description:
This routine is invoked to perform translation on an ICMP message.
Arguments:
Interfacep - the boundary interface over which to translate, or NULL
if the packet is inbound and the receiving interface has not been
added to the NAT.
Direction - the direction in which the packet is traveling
Contextp - initialized with context-information for the packet
InRecvBuffer - input buffer-chain
OutRecvBuffer - receives modified buffer-chain.
Return Value:
FORWARD_ACTION - indicates action to take on packet.
Environment:
Invoked with a reference made to 'Interfacep' by the caller.
--*/
{
FORWARD_ACTION act;
BOOLEAN ChecksumOffloaded;
ULONG i;
PICMP_HEADER IcmpHeader;
PIP_HEADER IpHeader;
TRACE(XLATE, ("NatTranslateIcmp\n"));
//
// If the interface is in FW mode and is not a
// boundary interface, go directly to the firewall
// logic
//
if (Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& !NAT_INTERFACE_BOUNDARY(Interfacep)) {
return NatpFirewallIcmp(
Interfacep,
Direction,
Contextp
);
}
IpHeader = Contextp->Header;
IcmpHeader = (PICMP_HEADER)Contextp->ProtocolHeader;
ChecksumOffloaded = Contextp->ChecksumOffloaded;
//
// The default action is chosen as follows:
// i. if the packet is incoming on a boundary interface
// a. drop if not locally destined
// b. drop if the interface is firewalled
// c. forward otherwise
// ii. the packet is outgoing on a boundary interface, drop
// if source-address is private.
//
if (Direction == NatInboundDirection) {
if ((*Contextp->DestinationType >= DEST_REMOTE)
|| (Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& !NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type))) {
act = DROP;
} else {
act = FORWARD;
}
} else {
//
// See if the packet's source-address is private
//
// N.B. 'Interfacep' is always valid for outbound packets.
//
act = DROP;
for (i = 0; i < Interfacep->AddressCount; i++) {
if (Contextp->SourceAddress ==
Interfacep->AddressArray[i].Address
) {
//
// The packet's source-address is public,
// so we'll allow it onto the public network.
//
act = FORWARD;
break;
}
}
}
//
// See what kind of ICMP message this is,
// and translate it if possible.
//
switch (IcmpHeader->Type) {
case ICMP_ROUTER_REPLY:
case ICMP_MASK_REPLY:
case ICMP_ECHO_REPLY:
case ICMP_TIMESTAMP_REPLY: {
if (IpHeader->TimeToLive <= 1) {
TRACE(XLATE, ("NatTranslateIcmp: ttl<=1, no translation\n"));
return FORWARD;
}
if (Contextp->ProtocolRecvBuffer->ipr_size <
FIELD_OFFSET(ICMP_HEADER, EncapsulatedIpHeader) ||
!NatTranslateIcmpRequest(
Interfacep,
Direction,
Contextp,
TRUE,
ChecksumOffloaded
)) {
return act;
}
*OutRecvBuffer = *InRecvBuffer; *InRecvBuffer = NULL;
*Contextp->DestinationType = DEST_INVALID;
return FORWARD;
}
case ICMP_ROUTER_REQUEST:
case ICMP_MASK_REQUEST:
case ICMP_ECHO_REQUEST:
case ICMP_TIMESTAMP_REQUEST: {
if (IpHeader->TimeToLive <= 1) {
TRACE(XLATE, ("NatTranslateIcmp: ttl<=1, no translation\n"));
return FORWARD;
}
if (Contextp->ProtocolRecvBuffer->ipr_size <
FIELD_OFFSET(ICMP_HEADER, EncapsulatedIpHeader) ||
!NatTranslateIcmpRequest(
Interfacep,
Direction,
Contextp,
FALSE,
ChecksumOffloaded
)) {
//
// If the interface is in FW mode, we don't want to let
// a non-translated packet through, unless the user has
// configured the interface otherwise.
//
if (Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& !NAT_INTERFACE_ALLOW_ICMP(
Interfacep,
IcmpHeader->Type
)) {
act = DROP;
}
return act;
}
*OutRecvBuffer = *InRecvBuffer; *InRecvBuffer = NULL;
*Contextp->DestinationType = DEST_INVALID;
return FORWARD;
}
case ICMP_TIME_EXCEED: {
//
// Outgoing on a firewalled interface are dropped, unless
// the user has specified otherwise
//
if (Direction == NatOutboundDirection
&& Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& !NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)) {
return DROP;
}
//
// Time-exceeded messages will be triggered at each hop
// to the final destination of a traceroute sequence.
// Such messages must be translated like ICMP replies.
// Time-exceeded messages may also be generated
// in response to TCP/UDP packets, so we translate them
// in the latter case as well.
//
if (Contextp->ProtocolRecvBuffer->ipr_size <
sizeof(ICMP_HEADER) ||
(IcmpHeader->EncapsulatedIpHeader.VersionAndHeaderLength
& 0x0f) != 5) {
return act;
} else if (IcmpHeader->EncapsulatedIpHeader.Protocol ==
NAT_PROTOCOL_ICMP) {
if ((IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_ECHO_REQUEST
&& IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_MASK_REQUEST
&& IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_ROUTER_REQUEST
&& IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_TIMESTAMP_REQUEST) ||
!NatTranslateIcmpEncapsulatedRequest(
Interfacep,
Direction,
IpHeader,
IcmpHeader,
&IcmpHeader->EncapsulatedIpHeader,
&IcmpHeader->EncapsulatedIcmpHeader,
ChecksumOffloaded
)) {
return act;
}
} else if (IcmpHeader->EncapsulatedIpHeader.Protocol
== NAT_PROTOCOL_TCP ||
IcmpHeader->EncapsulatedIpHeader.Protocol
== NAT_PROTOCOL_UDP) {
if (!NatTranslateEncapsulatedRequest(
Interfacep,
Direction,
IpHeader,
IcmpHeader,
&IcmpHeader->EncapsulatedIpHeader,
&IcmpHeader->EncapsulatedUdpHeader,
ChecksumOffloaded
)) {
return act;
}
} else {
return act;
}
*OutRecvBuffer = *InRecvBuffer; *InRecvBuffer = NULL;
*Contextp->DestinationType = DEST_INVALID;
return FORWARD;
}
case ICMP_PARAM_PROBLEM:
case ICMP_DEST_UNREACH: {
//
// Outgoing on a firewalled interface are dropped, unless
// the user has specified otherwise
//
if (Direction == NatOutboundDirection
&& Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& !NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)) {
return DROP;
}
//
// Destination unreachable messages will be generated for a variety
// of reasons. We are interested in the following cases:
// * Packet-too-big: When a packet received on a boundary
// interface has the 'DF' bit set, the local forwarder may
// generate an ICMP error message to the remote endpoint
// indicating that the remote system should reduce its MSS.
// This error, however, will contain the IP address of the
// private network in the encapsulated packet, since the ICMP
// error was generated after translation.
// * Port-unreachable: Indicates that no application is listening
// at the UDP port to which a packet was sent.
//
if (Contextp->ProtocolRecvBuffer->ipr_size <
sizeof(ICMP_HEADER) ||
(IcmpHeader->EncapsulatedIpHeader.VersionAndHeaderLength
& 0x0f) != 5) {
return act;
} else if (IcmpHeader->EncapsulatedIpHeader.Protocol ==
NAT_PROTOCOL_ICMP) {
if ((IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_ECHO_REQUEST
&& IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_MASK_REQUEST
&& IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_ROUTER_REQUEST
&& IcmpHeader->EncapsulatedIcmpHeader.Type !=
ICMP_TIMESTAMP_REQUEST) ||
!NatTranslateIcmpEncapsulatedRequest(
Interfacep,
Direction,
IpHeader,
IcmpHeader,
&IcmpHeader->EncapsulatedIpHeader,
&IcmpHeader->EncapsulatedIcmpHeader,
ChecksumOffloaded
)) {
return act;
}
} else if (IcmpHeader->EncapsulatedIpHeader.Protocol
== NAT_PROTOCOL_TCP ||
IcmpHeader->EncapsulatedIpHeader.Protocol
== NAT_PROTOCOL_UDP) {
if (!NatTranslateEncapsulatedRequest(
Interfacep,
Direction,
IpHeader,
IcmpHeader,
&IcmpHeader->EncapsulatedIpHeader,
&IcmpHeader->EncapsulatedUdpHeader,
ChecksumOffloaded
)) {
return act;
}
} else {
return act;
}
*OutRecvBuffer = *InRecvBuffer; *InRecvBuffer = NULL;
*Contextp->DestinationType = DEST_INVALID;
return FORWARD;
}
case ICMP_SOURCE_QUENCH: {
//
// Outgoing on a firewalled interface are dropped, unless
// the user has specified otherwise
//
if (Direction == NatOutboundDirection
&& Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& !NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)) {
return DROP;
}
return act;
}
case ICMP_REDIRECT: {
//
// We do not translate ICMP redirect errors, since we want
// the NAT's IP forwarder to see the redirects and adjust
// its routing table accordingly.
//
// However, we do not allow inbound or outbound redirects
// across a firwall interface, unless the user has
// specified otherwise
//
if (Interfacep
&& NAT_INTERFACE_FW(Interfacep)
&& NAT_INTERFACE_ALLOW_ICMP(Interfacep, IcmpHeader->Type)) {
act = FORWARD;
}
return act;
}
default: {
break;
}
}
return act;
} // NatTranslateIcmp
BOOLEAN
NatTranslateIcmpEncapsulatedRequest(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PIP_HEADER IpHeader,
PICMP_HEADER IcmpHeader,
PIP_HEADER EncapsulatedIpHeader,
struct _ENCAPSULATED_ICMP_HEADER* EncapsulatedIcmpHeader,
BOOLEAN ChecksumOffloaded
)
/*++
Routine Description:
This routine is invoked to translate an ICMP error message in which
we have another ICMP message encapsulated. This is necessary, for instance,
in the case of ICMP time-exceeded errors, upon which 'traceroute' relies.
Arguments:
Interfacep - the interface across which the ICMP message will be forwarded,
or NULL if the packet was received on a non-boundary interface
unknown to the NAT.
Direction - the direction in which the ICMP message is traveling
IpHeader - points to the IP header of the ICMP message
IcmpHeader - points to the ICMP header within the IP packet
EncapsulatedIpHeader - points to the IP header of the ICMP message
encapsulated in the data portion of the message
EncapsulatedIcmpHeader - points to the ICMP header of the ICMP message
encapsulated in the data portion of the message
Return Value:
BOOLEAN - TRUE if the packet was translated, FALSE otherwise
Environment:
Invoked at dispatch IRQL with a reference made to 'Interfacep'.
--*/
{
ULONG Checksum;
ULONG ChecksumDelta;
ULONG ChecksumDelta2;
PNAT_ICMP_MAPPING IcmpMapping;
ULONG64 Key;
CALLTRACE(("NatTranslateIcmpEncapsulatedRequest\n"));
//
// The checksum processing for encapsulated messages
// is extremely complicated since we must update
// (1) the ICMP checksum of the encapsulated ICMP message,
// using the change to the encapsulated ICMP identifier
// (2) the IP header-checksum of the encapsulated ICMP message
// using the change to the encapsulated IP addresses
// (3) the ICMP checksum of the containing ICMP error message
// using both the above changes as well as the changes
// to both the above checksums
// (4) the IP header-checksum of the containing ICMP error message
// using the change to the containing IP addresses
// Any changes to the logic below must be made with extreme care.
//
if (Direction == NatInboundDirection) {
Key =
MAKE_ICMP_KEY(
EncapsulatedIpHeader->DestinationAddress,
EncapsulatedIpHeader->SourceAddress
);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
IcmpMapping =
NatLookupInboundIcmpMapping(
Key,
EncapsulatedIcmpHeader->Identifier,
NULL
);
if (!IcmpMapping) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
TRACE(
XLATE, (
"NatTranslateIcmpEncapsulatedRequest: "
"no mapping for error message\n"
));
return FALSE;
}
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedIcmpHeader->Identifier);
EncapsulatedIcmpHeader->Identifier = IcmpMapping->PrivateId;
CHECKSUM_LONG(ChecksumDelta, EncapsulatedIcmpHeader->Identifier);
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedIcmpHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedIcmpHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedIcmpHeader->Checksum);
ChecksumDelta = ChecksumDelta2; CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedIpHeader->SourceAddress);
EncapsulatedIpHeader->SourceAddress =
ICMP_KEY_PRIVATE(IcmpMapping->PrivateKey);
CHECKSUM_LONG(ChecksumDelta, EncapsulatedIpHeader->SourceAddress);
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedIpHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedIpHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedIpHeader->Checksum);
ChecksumDelta = ChecksumDelta2; CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IpHeader->DestinationAddress);
IpHeader->DestinationAddress =
ICMP_KEY_PRIVATE(IcmpMapping->PrivateKey);
CHECKSUM_LONG(ChecksumDelta, IpHeader->DestinationAddress);
CHECKSUM_UPDATE(IpHeader->Checksum);
KeQueryTickCount((PLARGE_INTEGER)&IcmpMapping->LastAccessTime);
} else {
Key =
MAKE_ICMP_KEY(
EncapsulatedIpHeader->SourceAddress,
EncapsulatedIpHeader->DestinationAddress
);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
IcmpMapping =
NatLookupOutboundIcmpMapping(
Key,
EncapsulatedIcmpHeader->Identifier,
NULL
);
if (!IcmpMapping) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
TRACE(
XLATE, (
"NatTranslateIcmpEncapsulatedRequest: "
"no mapping for error message\n"
));
return FALSE;
}
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedIcmpHeader->Identifier);
EncapsulatedIcmpHeader->Identifier = IcmpMapping->PublicId;
CHECKSUM_LONG(ChecksumDelta, EncapsulatedIcmpHeader->Identifier);
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedIcmpHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedIcmpHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedIcmpHeader->Checksum);
ChecksumDelta = ChecksumDelta2; CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedIpHeader->DestinationAddress);
EncapsulatedIpHeader->DestinationAddress =
ICMP_KEY_PUBLIC(IcmpMapping->PublicKey);
CHECKSUM_LONG(ChecksumDelta, EncapsulatedIpHeader->DestinationAddress);
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedIpHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedIpHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedIpHeader->Checksum);
ChecksumDelta = ChecksumDelta2; CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IpHeader->SourceAddress);
IpHeader->SourceAddress =
ICMP_KEY_PUBLIC(IcmpMapping->PublicKey);
CHECKSUM_LONG(ChecksumDelta, IpHeader->SourceAddress);
CHECKSUM_UPDATE(IpHeader->Checksum);
KeQueryTickCount((PLARGE_INTEGER)&IcmpMapping->LastAccessTime);
}
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
//
// If checksum offloading is enabled on this packet, recompute the
// IP checksum. (There is no ICMP checksum-offload, so we will never
// need to fully recompute that.)
//
if (ChecksumOffloaded) {
NatComputeIpChecksum(IpHeader);
}
return TRUE;
} // NatTranslateIcmpEncapsulatedRequest
BOOLEAN
NatTranslateIcmpRequest(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PNAT_XLATE_CONTEXT Contextp,
BOOLEAN ReplyCode,
BOOLEAN ChecksumOffloaded
)
/*++
Routine Description:
This routine is invoked to translate an ICMP request or reply message.
Arguments:
Interfacep - the interface across which the ICMP message is to be forwarded,
or NULL if the packet was received on a non-boundary interface unknown
to the NAT.
Direction - the direction in which the ICMP message is traveling
Contextp - contains information about the packet
ReplyCode - if TRUE, the message is a reply; otherwise, it is a request.
Return Value:
BOOLEAN - TRUE if the message was translated, FALSE otherwise.
Environment:
Invoked at dispatch IRQL with a reference made to 'Interfacep'.
--*/
{
ULONG Checksum;
ULONG ChecksumDelta;
ULONG i;
PICMP_HEADER IcmpHeader;
PNAT_ICMP_MAPPING IcmpMapping;
PIP_HEADER IpHeader;
PLIST_ENTRY InsertionPoint;
PNAT_DYNAMIC_MAPPING Mapping;
ULONG64 PrivateKey;
UCHAR Protocol;
ULONG64 PublicKey;
ULONG64 RemoteKey;
NTSTATUS status;
CALLTRACE(("NatTranslateIcmpRequest\n"));
IpHeader = Contextp->Header;
IcmpHeader = (PICMP_HEADER)Contextp->ProtocolHeader;
//
// For ICMP requests/replies we must maintain mappings, so begin by seeing
// if there is already a mapping for this particular message
//
InsertionPoint = NULL;
if (Direction == NatOutboundDirection) {
PrivateKey =
MAKE_ICMP_KEY(
Contextp->DestinationAddress,
Contextp->SourceAddress
);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
IcmpMapping =
NatLookupOutboundIcmpMapping(
PrivateKey,
IcmpHeader->Identifier,
&InsertionPoint
);
if (!IcmpMapping) {
//
// No mapping was found, so try to create one.
//
// If the packet is an outbound reply-message,
// there really ought to be a corresponding inbound-mapping.
// Hence don't try to create one here, as it will just
// confuse the remote endpoint, which may find itself
// looking at a reply whose origin seems to be different
// from the machine to which it sent a request.
//
if (ReplyCode) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
TRACE(
XLATE, (
"NatTranslateIcmpRequest: ignoring outbound reply\n"
));
return FALSE;
}
//
// First look for a static mapping from this private address
// to a public address. If one is found, it will be used
// in the call to 'NatCreateIcmpMapping' below. Otherwise,
// a public address will be chosen from the address-pool.
//
// N.B. When a packet is outbound, 'Interfacep' is always valid.
//
PublicKey = 0;
if (!Interfacep->NoStaticMappingExists) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
for (i = 0; i < Interfacep->AddressMappingCount; i++) {
if (Contextp->SourceAddress >
Interfacep->AddressMappingArray[i].PrivateAddress) {
continue;
} else if (Contextp->SourceAddress <
Interfacep->AddressMappingArray[i].PrivateAddress) {
break;
}
PublicKey =
Interfacep->AddressMappingArray[i].PublicAddress;
break;
}
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
}
status =
NatCreateIcmpMapping(
Interfacep,
Contextp->DestinationAddress,
Contextp->SourceAddress,
(ULONG)PublicKey,
&IcmpHeader->Identifier,
NULL,
NULL,
InsertionPoint,
&IcmpMapping
);
if (!NT_SUCCESS(status)) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
TRACE(
XLATE, (
"NatTranslateIcmpRequest: error creating mapping\n"
));
return FALSE;
}
}
//
// Replace the Identifier in the packet,
// and replace the destination in the packet,
// updating the checksum as we go.
//
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IcmpHeader->Identifier);
IcmpHeader->Identifier = IcmpMapping->PublicId;
CHECKSUM_LONG(ChecksumDelta, IcmpHeader->Identifier);
CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IpHeader->SourceAddress);
IpHeader->SourceAddress = ICMP_KEY_PUBLIC(IcmpMapping->PublicKey);
CHECKSUM_LONG(ChecksumDelta, IpHeader->SourceAddress);
CHECKSUM_UPDATE(IpHeader->Checksum);
KeQueryTickCount((PLARGE_INTEGER)&IcmpMapping->LastAccessTime);
} else {
//
// The packet is inbound.
//
PublicKey =
MAKE_ICMP_KEY(
Contextp->SourceAddress,
Contextp->DestinationAddress
);
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
IcmpMapping =
NatLookupInboundIcmpMapping(
PublicKey,
IcmpHeader->Identifier,
&InsertionPoint
);
if (!IcmpMapping) {
//
// No mapping was found, so try to create one,
// if there is a static mapping which allows inbound sessions.
// We make an exception for inbound reply-messages;
// if the packet is a reply-message, it might be locally destined,
// in which case we pass it untouched to the stack.
//
// Don't create a mapping for an inbound reply;
// it's probably a reply to a locally-initiated request.
//
if (ReplyCode) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
return FALSE;
}
//
// First look for a static mapping from this public address
// to a private address. If one is found, it will be used
// in the call to 'NatCreateIcmpMapping' below. Otherwise,
// a public address will be chosen from the address-pool.
//
// This involves an exhaustive search since the address-mappings
// are sorted on private address rather than public address.
//
if (!Interfacep) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
return FALSE;
} else if (Interfacep->NoStaticMappingExists) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
TRACE(
XLATE, (
"NatTranslateIcmpRequest: ignoring inbound message\n"
));
return FALSE;
} else {
PrivateKey = 0;
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
for (i = 0; i < Interfacep->AddressMappingCount; i++) {
if (Interfacep->AddressMappingArray[i].PublicAddress !=
Contextp->DestinationAddress ||
!Interfacep->AddressMappingArray[i].AllowInboundSessions) {
continue;
}
PrivateKey =
Interfacep->AddressMappingArray[i].PrivateAddress;
break;
}
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
if (!PrivateKey) {
TRACE(
XLATE, (
"NatTranslateIcmpRequest: ignoring inbound message\n"
));
return FALSE;
}
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
}
status =
NatCreateIcmpMapping(
Interfacep,
Contextp->SourceAddress,
(ULONG)PrivateKey,
Contextp->DestinationAddress,
NULL,
&IcmpHeader->Identifier,
InsertionPoint,
NULL,
&IcmpMapping
);
if (!NT_SUCCESS(status)) {
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
TRACE(
XLATE, (
"NatTranslateIcmpRequest: error creating mapping\n"
));
return FALSE;
}
}
//
// Replace the Identifier in the packet
// and replace the destination in the packet,
// updating the checksum as we go.
//
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IcmpHeader->Identifier);
IcmpHeader->Identifier = IcmpMapping->PrivateId;
CHECKSUM_LONG(ChecksumDelta, IcmpHeader->Identifier);
CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IpHeader->DestinationAddress);
IpHeader->DestinationAddress =
ICMP_KEY_PRIVATE(IcmpMapping->PrivateKey);
CHECKSUM_LONG(ChecksumDelta, IpHeader->DestinationAddress);
CHECKSUM_UPDATE(IpHeader->Checksum);
KeQueryTickCount((PLARGE_INTEGER)&IcmpMapping->LastAccessTime);
}
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
//
// If checksum offloading is enabled on this packet, recompute the
// IP checksum. (There is no ICMP checksum-offload, so we will never
// need to fully recompute that.)
//
if (ChecksumOffloaded) {
NatComputeIpChecksum(IpHeader);
}
return TRUE;
} // NatTranslateIcmpRequest
BOOLEAN
NatTranslateEncapsulatedRequest(
PNAT_INTERFACE Interfacep OPTIONAL,
IP_NAT_DIRECTION Direction,
PIP_HEADER IpHeader,
PICMP_HEADER IcmpHeader,
PIP_HEADER EncapsulatedIpHeader,
struct _ENCAPSULATED_UDP_HEADER* EncapsulatedHeader,
BOOLEAN ChecksumOffloaded
)
/*++
Routine Description:
This routine is invoked to translate an ICMP error message in which
we have TCP segment or UDP datagram encapsulated. This is necessary,
for instance, in the case of ICMP destination-unreachable errors,
particularly where the target will take some action (such as reducing MTU)
upon receipt of the message.
Arguments:
Interfacep - the interface across which the ICMP message will be forwarded,
or NULL if the packet was received on a non-boundary interface.
Direction - the direction in which the ICMP message is traveling
IpHeader - points to the IP header of the ICMP message
IcmpHeader - points to the ICMP header within the IP packet
EncapsulatedIpHeader - points to the IP header of the TCP segment
encapsulated in the data portion of the ICMP message
EncapsulatedHeader - points to the TCP/UDP header of the TCP segment
encapsulated in the data portion of the ICMP message
Return Value:
BOOLEAN - TRUE if the packet was translated, FALSE otherwise
Environment:
Invoked at dispatch IRQL with a reference made to 'Interfacep'.
N.B.!!! This routine will acquire the mapping lock in order to search
the mapping-tree for the entry corresponding to the session for which
this ICMP error message was generated. All callers must take note,
and must ensure that the mapping lock is not already held on entry.
--*/
{
ULONG Checksum;
ULONG ChecksumDelta;
ULONG ChecksumDelta2;
ULONG64 DestinationKey;
PNAT_DYNAMIC_MAPPING Mapping;
ULONG64 ReplacementKey;
ULONG64 SourceKey;
CALLTRACE(("NatTranslateEncapsulatedRequest\n"));
//
// We begin by retrieving the mapping for the TCP session
// to which the contained segment belongs.
//
// We need to save the key with which we will replace the
// encapsulted message's contents. When an error is inbound,
// it must have been generated in response to an outbound message,
// and the outbound message contained in the error will have in it
// the public IP address and port to which we originally translated
// the outbound message. We therefore need to put back
// the private IP address and port so that the private machine
// will be able to identify the error.
// Similarly, when the error is outbound, we need to put in
// the public IP address and port so that the remote machine
// will be able to identify the error.
//
// Onward, then. Construct the key to be used for the lookup,
// take the mapping lock, and look up forward or reverse mappings.
//
MAKE_MAPPING_KEY(
DestinationKey,
EncapsulatedIpHeader->Protocol,
EncapsulatedIpHeader->SourceAddress,
EncapsulatedHeader->SourcePort
);
MAKE_MAPPING_KEY(
SourceKey,
EncapsulatedIpHeader->Protocol,
EncapsulatedIpHeader->DestinationAddress,
EncapsulatedHeader->DestinationPort
);
KeAcquireSpinLockAtDpcLevel(&MappingLock);
if (Direction == NatInboundDirection) {
Mapping = NatLookupReverseMapping(DestinationKey, SourceKey, NULL);
if (Mapping) {
ReplacementKey = Mapping->SourceKey[NatForwardPath];
} else {
Mapping = NatLookupForwardMapping(DestinationKey, SourceKey, NULL);
if (Mapping) {
ReplacementKey = Mapping->SourceKey[NatReversePath];
}
}
} else {
Mapping = NatLookupForwardMapping(DestinationKey, SourceKey, NULL);
if (Mapping) {
ReplacementKey = Mapping->DestinationKey[NatReversePath];
} else {
Mapping = NatLookupReverseMapping(DestinationKey, SourceKey, NULL);
if (Mapping) {
ReplacementKey = Mapping->DestinationKey[NatForwardPath];
}
}
}
KeReleaseSpinLockFromDpcLevel(&MappingLock);
if (!Mapping) {
TRACE(
XLATE, (
"NatTranslateEncapsulatedRequest: no mapping for message\n"
));
return FALSE;
}
//
// The checksum processing for encapsulated messages
// remains extremely complicated since we must update
// [0] for UDP messages, the UDP message checksum, using the change
// to the encapsulated UDP source port. No corresponding change
// is required for TCP segments, whose checksum appears beyond
// the eight bytes included in ICMP error messages.
// (1) the IP header-checksum of the encapsulated TCP segment
// using the change to the encapsulated IP addresses
// (2) the ICMP checksum of the containing ICMP error message
// using both the above change as well as the change
// to the above checksum
// (3) the IP header-checksum of the containing ICMP error message
// using the change to the containing IP addresses
// Any changes to the logic below must be made with extreme care.
//
if (Direction == NatInboundDirection) {
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedHeader->SourcePort);
EncapsulatedHeader->SourcePort = MAPPING_PORT(ReplacementKey);
CHECKSUM_LONG(ChecksumDelta, EncapsulatedHeader->SourcePort);
if (EncapsulatedIpHeader->Protocol == NAT_PROTOCOL_UDP) {
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedHeader->Checksum);
ChecksumDelta = ChecksumDelta2;
}
CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedIpHeader->SourceAddress);
EncapsulatedIpHeader->SourceAddress = MAPPING_ADDRESS(ReplacementKey);
CHECKSUM_LONG(ChecksumDelta, EncapsulatedIpHeader->SourceAddress);
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedIpHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedIpHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedIpHeader->Checksum);
ChecksumDelta = ChecksumDelta2; CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IpHeader->DestinationAddress);
IpHeader->DestinationAddress = MAPPING_ADDRESS(ReplacementKey);
CHECKSUM_LONG(ChecksumDelta, IpHeader->DestinationAddress);
CHECKSUM_UPDATE(IpHeader->Checksum);
} else {
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedHeader->DestinationPort);
EncapsulatedHeader->DestinationPort = MAPPING_PORT(ReplacementKey);
CHECKSUM_LONG(ChecksumDelta, EncapsulatedHeader->DestinationPort);
if (EncapsulatedIpHeader->Protocol == NAT_PROTOCOL_UDP) {
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedHeader->Checksum);
ChecksumDelta = ChecksumDelta2;
}
CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~EncapsulatedIpHeader->DestinationAddress);
EncapsulatedIpHeader->DestinationAddress =
MAPPING_ADDRESS(ReplacementKey);
CHECKSUM_LONG(ChecksumDelta, EncapsulatedIpHeader->DestinationAddress);
ChecksumDelta2 = ChecksumDelta;
CHECKSUM_LONG(ChecksumDelta2, ~EncapsulatedIpHeader->Checksum);
CHECKSUM_UPDATE(EncapsulatedIpHeader->Checksum);
CHECKSUM_LONG(ChecksumDelta2, EncapsulatedIpHeader->Checksum);
ChecksumDelta = ChecksumDelta2; CHECKSUM_UPDATE(IcmpHeader->Checksum);
ChecksumDelta = 0;
CHECKSUM_LONG(ChecksumDelta, ~IpHeader->SourceAddress);
IpHeader->SourceAddress = MAPPING_ADDRESS(ReplacementKey);
CHECKSUM_LONG(ChecksumDelta, IpHeader->SourceAddress);
CHECKSUM_UPDATE(IpHeader->Checksum);
}
//
// If checksum offloading is enabled on this packet, recompute the
// IP checksum. (There is no ICMP checksum-offload, so we will never
// need to fully recompute that.)
//
if (ChecksumOffloaded) {
NatComputeIpChecksum(IpHeader);
}
return TRUE;
} // NatTranslateEncapsulatedRequest