2051 lines
62 KiB
C
2051 lines
62 KiB
C
/*++
|
||
|
||
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
|
||
|