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.
3485 lines
103 KiB
3485 lines
103 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xlate.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code for translation of IP datagrams.
|
|
'NatTranslatePacket' is the routine directly invoked by TCPIP.SYS
|
|
for every locally-received and locally-generated packet.
|
|
Also included here is the cache of routes used to optimize forwarding.
|
|
|
|
Author:
|
|
|
|
Abolade Gbadegesin (t-abolag) 16-July-1997
|
|
|
|
Revision History:
|
|
|
|
Abolade Gbadegesin (aboladeg) 15-Apr-1998
|
|
|
|
Added route-lookup cache; first stable version of multiple-client
|
|
firewall hook.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// GLOBAL DATA DECLARATIONS
|
|
//
|
|
|
|
//
|
|
// Index of TCP/IP loopback interface
|
|
//
|
|
|
|
ULONG LoopbackIndex;
|
|
|
|
//
|
|
// Cache of routing-information
|
|
//
|
|
|
|
CACHE_ENTRY RouteCache[CACHE_SIZE];
|
|
|
|
//
|
|
// I/O request packet for notification of changes to the IP routing table.
|
|
//
|
|
|
|
PIRP RouteCacheIrp;
|
|
|
|
//
|
|
// Spin-lock controlling access to all route-cache information
|
|
//
|
|
|
|
KSPIN_LOCK RouteCacheLock;
|
|
|
|
//
|
|
// Array of entries corresponding to locations in 'RouteCache'
|
|
//
|
|
|
|
NAT_CACHED_ROUTE RouteCacheTable[CACHE_SIZE];
|
|
|
|
//
|
|
// Array of translation routines, indexed by IP protocol
|
|
//
|
|
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutineTable[256];
|
|
|
|
//
|
|
// CONSTANTS
|
|
//
|
|
|
|
//
|
|
// The boundary for UDP loose source matching. A mapping must
|
|
// have private port greater than this to allow another session
|
|
// to be created by a UDP packet that matches only the public
|
|
// endpoint.
|
|
//
|
|
|
|
#define NAT_XLATE_UDP_LSM_LOW_PORT 1024
|
|
|
|
//
|
|
// FORWARD DECLARATIONS
|
|
//
|
|
|
|
NTSTATUS
|
|
NatpDirectPacket(
|
|
ULONG ReceiveIndex,
|
|
ULONG SendIndex,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer,
|
|
FORWARD_ACTION* ForwardAction
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatpForwardPacket(
|
|
ULONG ReceiveIndex,
|
|
ULONG SendIndex,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
);
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
NatpIsUnicastPacket(
|
|
ULONG Address
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatpReceiveNonUnicastPacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatpReceivePacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
);
|
|
|
|
NTSTATUS
|
|
NatpRouteChangeCompletionRoutine(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
);
|
|
|
|
ULONG
|
|
FASTCALL
|
|
NatpRoutePacket(
|
|
ULONG DestinationAddress,
|
|
PNAT_XLATE_CONTEXT Contextp OPTIONAL,
|
|
PNTSTATUS Status
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatpSendNonUnicastPacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatpSendPacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatpTranslateLocalTraffic(
|
|
PNAT_INTERFACE Interfacep OPTIONAL,
|
|
IP_NAT_DIRECTION Direction,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InRecvBuffer,
|
|
IPRcvBuf** OutRecvBuffer
|
|
);
|
|
|
|
|
|
|
|
FORWARD_ACTION
|
|
NatForwardTcpStateCheck(
|
|
PNAT_DYNAMIC_MAPPING pMapping,
|
|
PTCP_HEADER pTcpHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates that packets for a TCP active open are valid:
|
|
-- only SYN at first
|
|
-- after the SYN/ACK, either only SYN (SYN/ACK lost) or only ACK
|
|
-- after the ACK of SYN/ACK (connection open), no SYN
|
|
|
|
Arguments:
|
|
|
|
pMapping -- the mapping this packet belongs to
|
|
|
|
pTcpHeader -- the TCP header of the packet
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
Environment:
|
|
|
|
Invoked with pMapping->Lock held by the caller
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT Flags = TCP_ALL_FLAGS(pTcpHeader);
|
|
|
|
if (NAT_MAPPING_TCP_OPEN(pMapping)) {
|
|
|
|
//
|
|
// Connection open -- SYN not allowed
|
|
//
|
|
|
|
return (Flags & TCP_FLAG_SYN) ? DROP : FORWARD;
|
|
|
|
} else if( pMapping->Flags & NAT_MAPPING_FLAG_REV_SYN ) {
|
|
|
|
ASSERT( pMapping->Flags & NAT_MAPPING_FLAG_FWD_SYN );
|
|
|
|
if ((Flags & TCP_FLAG_ACK) && !(Flags & TCP_FLAG_SYN)) {
|
|
|
|
//
|
|
// This is the ACK of the SYN/ACK -- connection now open
|
|
//
|
|
|
|
pMapping->Flags |= NAT_MAPPING_FLAG_TCP_OPEN;
|
|
} else if (TCP_FLAG_SYN != Flags
|
|
&& TCP_FLAG_RST != Flags
|
|
&& (TCP_FLAG_ACK | TCP_FLAG_RST) != Flags) {
|
|
|
|
//
|
|
// It's not an ACK of the SYN/ACK, it's not a RST (or ACK/RST),
|
|
// and it's not a retransmittal of the SYN (possible
|
|
// in this state, though rare) -- drop.
|
|
//
|
|
|
|
return DROP;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We've yet to receive a SYN/ACK -- this packet can have only a SYN
|
|
//
|
|
|
|
if (TCP_FLAG_SYN != Flags) {
|
|
return DROP;
|
|
}
|
|
|
|
pMapping->Flags |= NAT_MAPPING_FLAG_FWD_SYN;
|
|
}
|
|
|
|
return FORWARD;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NatInitializePacketManagement(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to initialize the packet-management module.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Invoked at passive level.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Length;
|
|
IPRouteLookupData RouteLookupData;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
CALLTRACE(("NatInitializePacketManagement\n"));
|
|
|
|
//
|
|
// Initialize our route-cache and set up the table of translation-routines
|
|
// indexed by IP protocol numbers.
|
|
//
|
|
|
|
InitializeCache(RouteCache);
|
|
RouteCacheIrp = NULL;
|
|
KeInitializeSpinLock(&RouteCacheLock);
|
|
RtlZeroMemory(RouteCacheTable, sizeof(RouteCacheTable));
|
|
|
|
RtlZeroMemory(TranslateRoutineTable, sizeof(TranslateRoutineTable));
|
|
TranslateRoutineTable[NAT_PROTOCOL_ICMP] = NatTranslateIcmp;
|
|
TranslateRoutineTable[NAT_PROTOCOL_PPTP] = NatTranslatePptp;
|
|
TranslateRoutineTable[NAT_PROTOCOL_IP6IN4] = NatpTranslateLocalTraffic;
|
|
TranslateRoutineTable[NAT_PROTOCOL_IPSEC_ESP] = NatpTranslateLocalTraffic;
|
|
TranslateRoutineTable[NAT_PROTOCOL_IPSEC_AH] = NatpTranslateLocalTraffic;
|
|
TranslateRoutineTable[NAT_PROTOCOL_TCP] =
|
|
(PNAT_IP_TRANSLATE_ROUTINE)NatTranslatePacket;
|
|
TranslateRoutineTable[NAT_PROTOCOL_UDP] =
|
|
(PNAT_IP_TRANSLATE_ROUTINE)NatTranslatePacket;
|
|
|
|
//
|
|
// Retrieve the index of the loopback interface, which we will use
|
|
// to detect and ignore loopback packets in 'NatTranslatePacket' below.
|
|
//
|
|
|
|
RouteLookupData.Version = 0;
|
|
RouteLookupData.SrcAdd = 0;
|
|
RouteLookupData.DestAdd = 0x0100007f;
|
|
Length = sizeof(LoopbackIndex);
|
|
status =
|
|
LookupRouteInformation(
|
|
&RouteLookupData,
|
|
NULL,
|
|
IPRouteOutgoingFirewallContext,
|
|
&LoopbackIndex,
|
|
&Length
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
LoopbackIndex = INVALID_IF_INDEX;
|
|
} else {
|
|
TRACE(
|
|
XLATE, (
|
|
"NatInitializePacketManagement: Loopback=%d\n", LoopbackIndex
|
|
));
|
|
}
|
|
|
|
//
|
|
// Obtain a reference to the module on the completion routine's behalf,
|
|
// set up the IRP which will be used to request route-change notification,
|
|
// and issue the first route-change notification request.
|
|
//
|
|
|
|
if (!REFERENCE_NAT()) { return; }
|
|
|
|
RouteCacheIrp =
|
|
IoBuildDeviceIoControlRequest(
|
|
IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
|
|
IpDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!RouteCacheIrp) {
|
|
ERROR((
|
|
"NatInitializePacketManagement: IoBuildDeviceIoControlRequest==0\n"
|
|
));
|
|
DEREFERENCE_NAT();
|
|
} else {
|
|
IoSetCompletionRoutine(
|
|
RouteCacheIrp,
|
|
NatpRouteChangeCompletionRoutine,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
status = IoCallDriver(IpDeviceObject, RouteCacheIrp);
|
|
if (!NT_SUCCESS(status)) {
|
|
ERROR(("NatInitializePacketManagement: IoCallDriver=%x\n", status));
|
|
}
|
|
}
|
|
|
|
} // NatInitializePacketManagement
|
|
|
|
|
|
NTSTATUS
|
|
NatpDirectPacket(
|
|
ULONG ReceiveIndex,
|
|
ULONG SendIndex,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer,
|
|
FORWARD_ACTION* ForwardAction
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to process packets which might be subject
|
|
to control by a director.
|
|
|
|
Arguments:
|
|
|
|
ReceiveIndex - the interface on which the packet was received,
|
|
for locally received packets
|
|
|
|
SendIndex - the interface on which the packet is to be sent,
|
|
for non-locally destined packets
|
|
|
|
Contextp - contains context information about the packet
|
|
|
|
InReceiveBuffer - points to the packet's buffer chain
|
|
|
|
OutReceiveBuffer - receives the packet buffer chain if translation occurs
|
|
|
|
ForwardAction - contains the action to take on the packet,
|
|
if a director applies.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the packet was directed elsewhere, STATUS_UNSUCCESSFUL
|
|
otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 DestinationKey[NatMaximumPath];
|
|
USHORT DestinationPort;
|
|
PNAT_DIRECTOR Director;
|
|
IP_NAT_DIRECTOR_QUERY DirectorQuery;
|
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|
ULONG MappingFlags;
|
|
UCHAR Protocol;
|
|
PNAT_DIRECTOR RedirectDirector;
|
|
ULONG64 SourceKey[NatMaximumPath];
|
|
USHORT SourcePort;
|
|
NTSTATUS status;
|
|
PNAT_INTERFACE Interfacep = NULL;
|
|
USHORT MaxMSS = 0;
|
|
|
|
TRACE(PER_PACKET, ("NatpDirectPacket\n"));
|
|
|
|
Protocol = Contextp->Header->Protocol;
|
|
if (Protocol == NAT_PROTOCOL_TCP || Protocol == NAT_PROTOCOL_UDP) {
|
|
SourcePort = ((PUSHORT)Contextp->ProtocolHeader)[0];
|
|
DestinationPort = ((PUSHORT)Contextp->ProtocolHeader)[1];
|
|
} else {
|
|
SourcePort = 0;
|
|
DestinationPort = 0;
|
|
}
|
|
|
|
//
|
|
// Choose a director to examine the packet.
|
|
// If any redirects exist, we first allow the redirect-director
|
|
// to look at the packet. Otherwise, we look for a specific director.
|
|
// Prepare for any eventuality by retrieving and referencing
|
|
// both the redirect-director (if any) and the specific director (if any).
|
|
//
|
|
|
|
if (!RedirectCount) {
|
|
Director = NatLookupAndReferenceDirector(Protocol, DestinationPort);
|
|
RedirectDirector = NULL;
|
|
} else {
|
|
Director = NatLookupAndReferenceDirector(Protocol, DestinationPort);
|
|
RedirectDirector =
|
|
(PNAT_DIRECTOR)RedirectRegisterDirector.DirectorHandle;
|
|
if (!NatReferenceDirector(RedirectDirector)) {
|
|
RedirectDirector = NULL;
|
|
}
|
|
}
|
|
|
|
if (!Director && !RedirectDirector) { return STATUS_UNSUCCESSFUL; }
|
|
|
|
DirectorQuery.ReceiveIndex = ReceiveIndex;
|
|
DirectorQuery.SendIndex = SendIndex;
|
|
DirectorQuery.Protocol = Protocol;
|
|
DirectorQuery.DestinationAddress = Contextp->DestinationAddress;
|
|
DirectorQuery.DestinationPort = DestinationPort;
|
|
DirectorQuery.SourceAddress = Contextp->SourceAddress;
|
|
DirectorQuery.SourcePort = SourcePort;
|
|
if (Contextp->Flags & NAT_XLATE_FLAG_LOOPBACK) {
|
|
DirectorQuery.Flags = IP_NAT_DIRECTOR_QUERY_FLAG_LOOPBACK;
|
|
} else {
|
|
DirectorQuery.Flags = 0;
|
|
}
|
|
|
|
//
|
|
// Consult a director to get a private address/port
|
|
// to which the incoming session should be directed:
|
|
//
|
|
// If there is a redirect director, try that first.
|
|
// If that succeeds, release the specific director (if any)
|
|
// and retain the redirect director in 'Director'.
|
|
// Otherwise, release the redirect director,
|
|
// and try the specific director next, if any.
|
|
// Otherwise, try the specific director right away.
|
|
//
|
|
|
|
if (RedirectDirector) {
|
|
DirectorQuery.DirectorContext = RedirectDirector->Context;
|
|
status = RedirectDirector->QueryHandler(&DirectorQuery);
|
|
if (NT_SUCCESS(status)) {
|
|
if (Director) { NatDereferenceDirector(Director); }
|
|
Director = RedirectDirector;
|
|
} else {
|
|
NatDereferenceDirector(RedirectDirector);
|
|
if (Director && Director != RedirectDirector) {
|
|
DirectorQuery.DirectorContext = Director->Context;
|
|
if (Contextp->Flags & NAT_XLATE_FLAG_LOOPBACK) {
|
|
DirectorQuery.Flags = IP_NAT_DIRECTOR_QUERY_FLAG_LOOPBACK;
|
|
} else {
|
|
DirectorQuery.Flags = 0;
|
|
}
|
|
status = Director->QueryHandler(&DirectorQuery);
|
|
}
|
|
}
|
|
} else {
|
|
DirectorQuery.DirectorContext = Director->Context;
|
|
status = Director->QueryHandler(&DirectorQuery);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (Director) { NatDereferenceDirector(Director); }
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Either the primary director or the redirect-director
|
|
// has told us what to do with the session; see now if it should be dropped
|
|
// or directed.
|
|
//
|
|
|
|
if (DirectorQuery.Flags & IP_NAT_DIRECTOR_QUERY_FLAG_DROP) {
|
|
NatDereferenceDirector(Director);
|
|
*ForwardAction = DROP;
|
|
return STATUS_SUCCESS;
|
|
} else if (Protocol != NAT_PROTOCOL_TCP && Protocol != NAT_PROTOCOL_UDP) {
|
|
ULONG Checksum;
|
|
ULONG ChecksumDelta = 0;
|
|
|
|
NatDereferenceDirector(Director);
|
|
|
|
//
|
|
// Translate the packet as instructed by the director.
|
|
// N.B. The director must specify both the destination and source
|
|
// addresses.
|
|
//
|
|
|
|
CHECKSUM_LONG(ChecksumDelta, ~Contextp->Header->DestinationAddress);
|
|
CHECKSUM_LONG(ChecksumDelta, ~Contextp->Header->SourceAddress);
|
|
Contextp->Header->DestinationAddress =
|
|
DirectorQuery.NewDestinationAddress;
|
|
Contextp->Header->SourceAddress =
|
|
DirectorQuery.NewSourceAddress;
|
|
CHECKSUM_LONG(ChecksumDelta, Contextp->Header->DestinationAddress);
|
|
CHECKSUM_LONG(ChecksumDelta, Contextp->Header->SourceAddress);
|
|
CHECKSUM_UPDATE(Contextp->Header->Checksum);
|
|
*ForwardAction = FORWARD;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
TRACE(
|
|
XLATE, (
|
|
"NatpDirectPacket: directed %d.%d.%d.%d/%d:%d.%d.%d.%d/%d to %d.%d.%d.%d/%d:%d.%d.%d.%d/%d\n",
|
|
ADDRESS_BYTES(DirectorQuery.DestinationAddress),
|
|
NTOHS(DirectorQuery.DestinationPort),
|
|
ADDRESS_BYTES(DirectorQuery.SourceAddress),
|
|
NTOHS(DirectorQuery.SourcePort),
|
|
ADDRESS_BYTES(DirectorQuery.NewDestinationAddress),
|
|
NTOHS(DirectorQuery.NewDestinationPort),
|
|
ADDRESS_BYTES(DirectorQuery.NewSourceAddress),
|
|
NTOHS(DirectorQuery.NewSourcePort)
|
|
));
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->DestinationAddress,
|
|
DestinationPort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatReversePath],
|
|
Protocol,
|
|
DirectorQuery.NewDestinationAddress,
|
|
DirectorQuery.NewDestinationPort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatReversePath],
|
|
Protocol,
|
|
DirectorQuery.NewSourceAddress,
|
|
DirectorQuery.NewSourcePort
|
|
);
|
|
|
|
//
|
|
// A director requested that a mapping be established for a session.
|
|
// Create a mapping using the director's private endpoint.
|
|
//
|
|
|
|
|
|
MappingFlags =
|
|
NAT_MAPPING_FLAG_INBOUND |
|
|
NAT_MAPPING_FLAG_DO_NOT_LOG |
|
|
((DirectorQuery.Flags &
|
|
IP_NAT_DIRECTOR_QUERY_FLAG_NO_TIMEOUT)
|
|
? NAT_MAPPING_FLAG_NO_TIMEOUT : 0) |
|
|
((DirectorQuery.Flags &
|
|
IP_NAT_DIRECTOR_QUERY_FLAG_UNIDIRECTIONAL)
|
|
? NAT_MAPPING_FLAG_UNIDIRECTIONAL : 0) |
|
|
((DirectorQuery.Flags &
|
|
IP_NAT_DIRECTOR_QUERY_FLAG_DELETE_ON_DISSOCIATE)
|
|
? NAT_MAPPING_FLAG_DELETE_ON_DISSOCIATE_DIRECTOR : 0);
|
|
|
|
#ifdef NAT_WMI
|
|
|
|
//
|
|
// Determine if this mapping should be logged. We only want to log
|
|
// mappings that are crossing a boundary or firewalled interface.
|
|
// Furthermore, we only want to perform those checks if connection
|
|
// logging is actually enabled.
|
|
//
|
|
|
|
if (NatWmiEnabledEvents[NAT_WMI_CONNECTION_CREATION_EVENT]) {
|
|
BOOLEAN LogConnection = FALSE;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
|
|
if (NatLookupCachedInterface(ReceiveIndex, Interfacep)) {
|
|
LogConnection =
|
|
NAT_INTERFACE_BOUNDARY(Interfacep)
|
|
|| NAT_INTERFACE_FW(Interfacep);
|
|
}
|
|
|
|
if (!LogConnection
|
|
&& NatLookupCachedInterface(SendIndex, Interfacep)) {
|
|
|
|
if (NAT_INTERFACE_BOUNDARY(Interfacep)
|
|
|| NAT_INTERFACE_FW(Interfacep)) {
|
|
|
|
//
|
|
// This isn't an inbound connection
|
|
//
|
|
|
|
MappingFlags &= ~NAT_MAPPING_FLAG_INBOUND;
|
|
LogConnection = TRUE;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
if (LogConnection) {
|
|
MappingFlags &= ~NAT_MAPPING_FLAG_DO_NOT_LOG;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Record maximum MSS value in case it needs to be set in the mapping.
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
if (NatLookupCachedInterface(SendIndex, Interfacep) && NAT_INTERFACE_BOUNDARY(Interfacep)) {
|
|
|
|
MaxMSS = MAX_MSSOPTION(Interfacep->MTU);
|
|
} else if (NatLookupCachedInterface(ReceiveIndex, Interfacep) &&
|
|
|
|
NAT_INTERFACE_BOUNDARY(Interfacep)) {
|
|
MaxMSS = MAX_MSSOPTION(Interfacep->MTU);
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
status =
|
|
NatCreateMapping(
|
|
MappingFlags,
|
|
DestinationKey,
|
|
SourceKey,
|
|
NULL,
|
|
NULL,
|
|
MaxMSS,
|
|
Director,
|
|
DirectorQuery.DirectorSessionContext,
|
|
NULL,
|
|
NULL,
|
|
&Mapping
|
|
);
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
if (!NT_SUCCESS(status)) {
|
|
TRACE(XLATE, ("NatpDirectPacket: mapping not created\n"));
|
|
NatDereferenceDirector(Director);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NatDereferenceDirector(Director);
|
|
|
|
//
|
|
// Perform the actual translation.
|
|
// This replaces the destination endpoint
|
|
// with whatever the director provided as a destination.
|
|
//
|
|
|
|
*ForwardAction =
|
|
Mapping->TranslateRoutine[NatForwardPath](
|
|
Mapping,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
NatDereferenceMapping(Mapping);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // NatpDirectPacket
|
|
|
|
|
|
FORWARD_ACTION
|
|
NatpForwardPacket(
|
|
ULONG ReceiveIndex,
|
|
ULONG SendIndex,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to process packets to be forwarded.
|
|
Such packets are not locally-destined, and hence we care about them
|
|
only if the outgoing interface is a NAT boundary interface, in which case
|
|
the packets must be automatically translated using a public IP address.
|
|
In the process, a mapping is created so that translation of the packet's
|
|
successors is handled in the fast path in 'NatTranslatePacket'.
|
|
|
|
Arguments:
|
|
|
|
ReceiveIndex - the interface on which the packet was received
|
|
|
|
SendIndex - the interface on which the packet is to be forwarded
|
|
|
|
Contextp - contains context information about the packet
|
|
|
|
TranslateRoutine - points to the routine which performs translation
|
|
|
|
InReceiveBuffer - points to the packet buffer chain
|
|
|
|
OutReceiveBuffer - receives the packet buffer chain if translation occurs
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
--*/
|
|
|
|
{
|
|
FORWARD_ACTION act;
|
|
PNAT_USED_ADDRESS Addressp;
|
|
ULONG64 DestinationKey[NatMaximumPath];
|
|
USHORT DestinationPort;
|
|
PNAT_DYNAMIC_MAPPING InsertionPoint;
|
|
PNAT_INTERFACE Interfacep;
|
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|
USHORT PortAcquired;
|
|
UCHAR Protocol;
|
|
ULONG PublicAddress;
|
|
PNAT_INTERFACE ReceiveInterface;
|
|
ULONG ReverseSourceAddress;
|
|
ULONG64 SourceKey[NatMaximumPath];
|
|
USHORT SourcePort;
|
|
PNAT_USED_ADDRESS StaticAddressp;
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
USHORT MaxMSS = 0;
|
|
|
|
TRACE(PER_PACKET, ("NatpForwardPacket\n"));
|
|
|
|
//
|
|
// Look up the sending and receiving interfaces, and set the default action
|
|
// in 'act'. If the sending interface is a boundary interface,
|
|
// then if we are unable to translate for any reason, the packet must be
|
|
// dropped since it contains a private address.
|
|
// Otherwise, we allow the stack to see the packet even if we cannot
|
|
// translate it.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
if (!NatLookupCachedInterface(SendIndex, Interfacep)) {
|
|
act = FORWARD;
|
|
|
|
//
|
|
// We need to see if this packet was received on a firewalled
|
|
// interface, and, if so, drop the packet.
|
|
//
|
|
|
|
if (NatLookupCachedInterface(ReceiveIndex, ReceiveInterface)
|
|
&& NAT_INTERFACE_FW(ReceiveInterface)) {
|
|
act = DROP;
|
|
}
|
|
|
|
} else {
|
|
if (!NatLookupCachedInterface(ReceiveIndex, ReceiveInterface)) {
|
|
|
|
//
|
|
// The receiving interface has not been added.
|
|
// This packet will not be translated, and should furthermore
|
|
// be dropped if the outgoing interface is a boundary or
|
|
// firewalled interface.
|
|
// This prevents unauthorized access to the remote network.
|
|
//
|
|
|
|
act =
|
|
(NAT_INTERFACE_BOUNDARY(Interfacep)
|
|
|| NAT_INTERFACE_FW(Interfacep))
|
|
? DROP
|
|
: FORWARD;
|
|
Interfacep = NULL;
|
|
|
|
} else if (NAT_INTERFACE_BOUNDARY(ReceiveInterface)) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
//
|
|
// Treat this packet like a received packet.
|
|
// This case may occur when we have an address pool and someone
|
|
// on the public network sends a packet to an address in the pool.
|
|
// The destination will be non-local, as for a transit packet,
|
|
// (hence the invocation of 'NatpForwardPacket') when actually
|
|
// the packet should be treated as a receipt
|
|
// (via 'NatpReceivePacket').
|
|
//
|
|
|
|
return
|
|
NatpReceivePacket(
|
|
ReceiveIndex,
|
|
Contextp,
|
|
TranslateRoutine,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
} else if (NAT_INTERFACE_FW(ReceiveInterface)) {
|
|
|
|
//
|
|
// We've received a packet on a non-translating firewalled
|
|
// interface that is not directly addressed to us.
|
|
//
|
|
|
|
Interfacep = NULL;
|
|
act = DROP;
|
|
|
|
} else if (NAT_INTERFACE_BOUNDARY(Interfacep)) {
|
|
|
|
//
|
|
// The outgoing interface is a boundary interface,
|
|
// and the receiving interface is permitted access.
|
|
// If translation fails, the packet must be dropped.
|
|
//
|
|
|
|
NatReferenceInterface(Interfacep);
|
|
act = DROP;
|
|
|
|
} else if (NAT_INTERFACE_FW(Interfacep)) {
|
|
|
|
//
|
|
// The outgoing interface is a non-boundary firewalled
|
|
// interface; transit traffic is not permitted through
|
|
// such an interface.
|
|
//
|
|
|
|
Interfacep = NULL;
|
|
act = DROP;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The outgoing interface is not a boundary or firewalled
|
|
// interface.
|
|
//
|
|
|
|
Interfacep = NULL;
|
|
act = FORWARD;
|
|
}
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
if (!Interfacep) { return act; }
|
|
|
|
if ((PVOID)TranslateRoutine == (PVOID)NatTranslatePacket) {
|
|
|
|
//
|
|
// This is either a TCP or a UDP packet.
|
|
//
|
|
|
|
Protocol = Contextp->Header->Protocol;
|
|
SourcePort = ((PUSHORT)Contextp->ProtocolHeader)[0];
|
|
DestinationPort = ((PUSHORT)Contextp->ProtocolHeader)[1];
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->DestinationAddress,
|
|
DestinationPort
|
|
);
|
|
|
|
//
|
|
// We now generate an outbound mapping and translate the packet.
|
|
//
|
|
// First, acquire an endpoint for the mapping;
|
|
// note that this must be done with the interface's lock held,
|
|
// since it involves consulting the interface's address-pool.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
status =
|
|
NatAcquireEndpointFromAddressPool(
|
|
Interfacep,
|
|
SourceKey[NatForwardPath],
|
|
DestinationKey[NatForwardPath],
|
|
0,
|
|
MAPPING_PORT(SourceKey[NatForwardPath]),
|
|
TRUE,
|
|
&Addressp,
|
|
&PortAcquired
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
ExInterlockedAddLargeStatistic(
|
|
(PLARGE_INTEGER)&Interfacep->Statistics.RejectsForward, 1
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
return DROP;
|
|
}
|
|
|
|
PublicAddress = Addressp->PublicAddress;
|
|
|
|
//
|
|
// Next, if there are static mappings for the interface,
|
|
// handle the special case where a client A behind the NAT
|
|
// is attempting to send to another client B behind the NAT,
|
|
// using the *statically-mapped* address for B.
|
|
// We detect this case by looking for a static address mapping
|
|
// from 'Contextp->DestinationAddress' to a private address.
|
|
//
|
|
|
|
if (Interfacep->NoStaticMappingExists) {
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
ReverseSourceAddress = Contextp->DestinationAddress;
|
|
} else {
|
|
StaticAddressp =
|
|
NatLookupStaticAddressPoolEntry(
|
|
Interfacep,
|
|
Contextp->DestinationAddress,
|
|
FALSE
|
|
);
|
|
if (StaticAddressp) {
|
|
ReverseSourceAddress = StaticAddressp->PrivateAddress;
|
|
} else {
|
|
ReverseSourceAddress = Contextp->DestinationAddress;
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
}
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatReversePath],
|
|
Protocol,
|
|
ReverseSourceAddress,
|
|
DestinationPort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatReversePath],
|
|
Protocol,
|
|
PublicAddress,
|
|
PortAcquired
|
|
);
|
|
|
|
|
|
//
|
|
// Set Maximum MSS value on the mapping of the sending interface.
|
|
//
|
|
if (NAT_INTERFACE_BOUNDARY(Interfacep)) {
|
|
MaxMSS = MAX_MSSOPTION(Interfacep->MTU);
|
|
}
|
|
|
|
//
|
|
// Allocate a mapping.
|
|
//
|
|
|
|
status =
|
|
NatCreateMapping(
|
|
0,
|
|
DestinationKey,
|
|
SourceKey,
|
|
Interfacep,
|
|
(PVOID)Addressp,
|
|
MaxMSS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Mapping
|
|
);
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExInterlockedAddLargeStatistic(
|
|
(PLARGE_INTEGER)&Interfacep->Statistics.RejectsForward, 1
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
return DROP;
|
|
}
|
|
|
|
//
|
|
// Activate any applicable dynamic tickets
|
|
//
|
|
|
|
if (DynamicTicketCount) {
|
|
NatLookupAndApplyDynamicTicket(
|
|
Protocol,
|
|
DestinationPort,
|
|
Interfacep,
|
|
PublicAddress,
|
|
Contextp->SourceAddress
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Perform the actual translation
|
|
//
|
|
|
|
act =
|
|
Mapping->TranslateRoutine[NatForwardPath](
|
|
Mapping,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
NatDereferenceMapping(Mapping);
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
|
|
} // TranslateRoutine != NatTranslatePacket
|
|
|
|
//
|
|
// The packet is neither a TCP nor a UDP packet.
|
|
// Only translate if the outgoing interface is a boundary interface.
|
|
//
|
|
// N.B. The translation routine must be invoked with a reference made
|
|
// to the boundary interface, and without holding the mapping lock.
|
|
//
|
|
|
|
if (TranslateRoutine) {
|
|
act =
|
|
TranslateRoutine(
|
|
Interfacep,
|
|
NatOutboundDirection,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
}
|
|
NatDereferenceInterface(Interfacep);
|
|
|
|
return act;
|
|
|
|
} // NatpForwardPacket
|
|
|
|
void
|
|
FASTCALL
|
|
NatAdjustMSSOption(
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
USHORT MaxMSS
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine lowers MSS option in a TCP SYN packet if that MSS value is too large
|
|
for the outgoing link. It also updates the TCP checksum accordingly.
|
|
It assumes that IP and TCP checksums have been computed so it has to be called after
|
|
the translation route completes.
|
|
TCP options follow the general format of one byte type, one byte length, zero or more
|
|
data indicated by the length field. The exception to this general format are one byte
|
|
NOP and ENDOFOPTION option types.
|
|
|
|
Arguments:
|
|
|
|
Contextp - contains context information about the packet
|
|
|
|
MaxMSS - the maximum MSS value on the sending interface which is equal to the
|
|
interface MTU minus IP and TCP fixed header size
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT tempMSS;
|
|
PTCP_HEADER TcpHeader = (PTCP_HEADER)Contextp->ProtocolHeader;
|
|
PUCHAR OptionsPtr = (PUCHAR)(TcpHeader + 1);
|
|
PUCHAR OptionsEnd = NULL, TcpBufEnd = NULL;
|
|
ULONG tcpChecksumDelta;
|
|
UNALIGNED MSSOption *MSSPtr = NULL;
|
|
|
|
CALLTRACE(("NatpAdjustMSSOption\n"));
|
|
//
|
|
// Only TCP SYN has MSS options.
|
|
//
|
|
ASSERT(TCP_FLAG(TcpHeader, SYN) && MaxMSS > 0);
|
|
|
|
//
|
|
// Do some bound checking
|
|
//
|
|
TcpBufEnd = Contextp->ProtocolRecvBuffer->ipr_buffer + Contextp->ProtocolRecvBuffer->ipr_size;
|
|
if ((TcpBufEnd - (PUCHAR)TcpHeader) >= TCP_DATA_OFFSET( TcpHeader )) {
|
|
OptionsEnd = (PUCHAR)TcpHeader + TCP_DATA_OFFSET( TcpHeader );
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// MSS option is not the first option so it is necessary to do a complete parsing.
|
|
//
|
|
while (OptionsPtr < OptionsEnd) {
|
|
|
|
switch (*OptionsPtr) {
|
|
|
|
case TCP_OPTION_ENDOFOPTIONS:
|
|
return;
|
|
|
|
case TCP_OPTION_NOP:
|
|
OptionsPtr++;
|
|
break;
|
|
|
|
case TCP_OPTION_MSS:
|
|
|
|
MSSPtr = (UNALIGNED MSSOption *)OptionsPtr;
|
|
//
|
|
// Found malformed MSS option so quit and do nothing.
|
|
//
|
|
if (MSS_OPTION_SIZE > (OptionsEnd - OptionsPtr) ||
|
|
MSS_OPTION_SIZE != MSSPtr->OptionLen) {
|
|
return;
|
|
}
|
|
|
|
tempMSS = MSSPtr->MSSValue;
|
|
//
|
|
// if the current MSS option is smaller than sndMTU - (IP Header + TCP header),
|
|
// nothing needs to be done.
|
|
//
|
|
if (RtlUshortByteSwap( tempMSS ) <= MaxMSS) {
|
|
OptionsPtr += MSS_OPTION_SIZE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Adjust the MSS option.
|
|
//
|
|
MSSPtr->MSSValue = RtlUshortByteSwap( MaxMSS );
|
|
|
|
//
|
|
// Update the TCP check sum. It assumes that this routine is always called
|
|
// after translation so that even in the case of off-loading both IP and TCP
|
|
// checksums are already calculated.
|
|
//
|
|
CHECKSUM_XFER(tcpChecksumDelta, TcpHeader->Checksum);
|
|
|
|
//
|
|
// Check to see if the MSS option starts at 16 bit boundary. If not then need
|
|
// to byte swap the 16-bit MSS value when updating the check sum.
|
|
//
|
|
if (0 == (OptionsPtr - (PUCHAR)TcpHeader) % 2) {
|
|
tcpChecksumDelta += (USHORT)~tempMSS;
|
|
tcpChecksumDelta += MSSPtr->MSSValue;
|
|
} else {
|
|
//
|
|
// The MSS option does not sit on a 16 bit boundary, so the packets is like this:
|
|
// [MSS Option Size][MSS' high byte][MSS' low byte][one byte pointed by OptionPtr]
|
|
// Use these two 16-bit fields to update the checksum.
|
|
//
|
|
tcpChecksumDelta += (USHORT)~((USHORT)((tempMSS & 0xFF00) >> 8) | (MSS_OPTION_SIZE << 8));
|
|
tcpChecksumDelta += (USHORT)((MSSPtr->MSSValue & 0xFF00) >> 8) | (MSS_OPTION_SIZE << 8);
|
|
|
|
tcpChecksumDelta += (USHORT)~((USHORT)((tempMSS & 0xFF) <<8) | (USHORT)*OptionsPtr);
|
|
tcpChecksumDelta += (USHORT)((MSSPtr->MSSValue & 0xFF) <<8) | (USHORT)*OptionsPtr;
|
|
}
|
|
|
|
CHECKSUM_FOLD(tcpChecksumDelta);
|
|
CHECKSUM_XFER(TcpHeader->Checksum, tcpChecksumDelta);
|
|
|
|
OptionsPtr += MSS_OPTION_SIZE;
|
|
|
|
TRACE(
|
|
XLATE,
|
|
("NatpAdjustMSSOption: Adjusted TCP MSS Option from %d to %d\n",
|
|
RtlUshortByteSwap( tempMSS ),
|
|
MaxMSS));
|
|
|
|
break;
|
|
|
|
case TCP_OPTION_WSCALE:
|
|
//
|
|
// Found malformed WS options so quit and do nothing.
|
|
//
|
|
if (WS_OPTION_SIZE > OptionsPtr - OptionsEnd || WS_OPTION_SIZE != OptionsPtr[1]) {
|
|
return;
|
|
}
|
|
|
|
OptionsPtr += WS_OPTION_SIZE;
|
|
break;
|
|
|
|
case TCP_OPTION_TIMESTAMPS:
|
|
//
|
|
// Found malformed Time Stamp options so quit and do nothing.
|
|
//
|
|
if (TS_OPTION_SIZE > OptionsPtr - OptionsEnd || TS_OPTION_SIZE != OptionsPtr[1]) {
|
|
return;
|
|
}
|
|
|
|
OptionsPtr += TS_OPTION_SIZE;
|
|
break;
|
|
|
|
case TCP_OPTION_SACK_PERMITTED:
|
|
//
|
|
// Found malformed Sack Permitted options so quit and do nothing.
|
|
//
|
|
if (SP_OPTION_SIZE > OptionsPtr - OptionsEnd || SP_OPTION_SIZE != OptionsPtr[1]) {
|
|
return;
|
|
}
|
|
|
|
OptionsPtr += SP_OPTION_SIZE;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// unknown option. Check to see if it has a valid length field.
|
|
//
|
|
if (OptionsEnd > OptionsPtr + 1) {
|
|
// Found malformed unknown options so quit and do nothing.
|
|
if (OptionsPtr[1] < 2 || OptionsPtr[1] > OptionsEnd - OptionsPtr)
|
|
return;
|
|
|
|
OptionsPtr += OptionsPtr[1];
|
|
} else {
|
|
return;
|
|
}
|
|
break;
|
|
} // switch
|
|
} // while
|
|
}
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
NatpIsUnicastPacket(
|
|
ULONG Address
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to determine whether or not a packet is a unicast
|
|
packet, based on its address.
|
|
|
|
Arguments:
|
|
|
|
Address - the destination address of the packet
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the packet appears to be a unicast, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// See if the packet is multicast or all-ones broadcast
|
|
//
|
|
|
|
if (ADDRESS_CLASS_D(Address) || ADDRESS_CLASS_E(Address)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// See if the address is a network-class directed broadcast
|
|
//
|
|
|
|
if ((Address | ~GET_CLASS_MASK(Address)) == Address) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
FORWARD_ACTION
|
|
NatpReceiveNonUnicastPacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to process locally destined non-unicast packets.
|
|
If the packet was received on a firewalled interface, it will be dropped
|
|
unless an exemption exists.
|
|
|
|
Arguments:
|
|
|
|
Index - index of the interface on which the packet was received
|
|
|
|
Contextp - the context for this packet
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
--*/
|
|
|
|
{
|
|
FORWARD_ACTION act;
|
|
USHORT DestinationPort;
|
|
PNAT_INTERFACE Interfacep;
|
|
KIRQL Irql;
|
|
USHORT SourcePort;
|
|
PNAT_TICKET Ticketp;
|
|
UCHAR Type;
|
|
|
|
TRACE(PER_PACKET, ("NatpReceiveNonUnicastPacket\n"));
|
|
|
|
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
|
|
|
if (!NatLookupCachedInterface(Index, Interfacep)
|
|
|| !NAT_INTERFACE_FW(Interfacep)) {
|
|
|
|
//
|
|
// The packet was not received on a firewalled interface
|
|
//
|
|
|
|
KeReleaseSpinLock(&InterfaceLock, Irql);
|
|
act = FORWARD;
|
|
|
|
} else {
|
|
|
|
NatReferenceInterface(Interfacep);
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
//
|
|
// This packet was received on a firealled interface. Drop it,
|
|
// unless:
|
|
// * it appears to be a DHCP response packet
|
|
// * it's a UDP packet, and there exist a firewall port mapping
|
|
// (i.e., one that does not change the destination address
|
|
// or port) for the destination port
|
|
// * it's an IGMP packet
|
|
// * it's a permitted ICMP type
|
|
//
|
|
|
|
act = DROP;
|
|
|
|
switch (Contextp->Header->Protocol) {
|
|
|
|
case NAT_PROTOCOL_ICMP: {
|
|
Type = ((PICMP_HEADER)Contextp->ProtocolHeader)->Type;
|
|
|
|
switch (Type) {
|
|
case ICMP_ECHO_REQUEST:
|
|
case ICMP_TIMESTAMP_REQUEST:
|
|
case ICMP_ROUTER_REQUEST:
|
|
case ICMP_MASK_REQUEST: {
|
|
|
|
//
|
|
// These types are allowed in based on the interface's
|
|
// configuration.
|
|
//
|
|
|
|
if (NAT_INTERFACE_ALLOW_ICMP(Interfacep, Type)) {
|
|
act = FORWARD;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Any other inbound ICMP type is always dropped.
|
|
//
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case NAT_PROTOCOL_IGMP: {
|
|
act = FORWARD;
|
|
break;
|
|
}
|
|
|
|
case NAT_PROTOCOL_UDP: {
|
|
SourcePort = ((PUSHORT)Contextp->ProtocolHeader)[0];
|
|
DestinationPort = ((PUSHORT)Contextp->ProtocolHeader)[1];
|
|
|
|
if (NTOHS(DHCP_SERVER_PORT) == SourcePort
|
|
&& NTOHS(DHCP_CLIENT_PORT) == DestinationPort) {
|
|
|
|
act = FORWARD;
|
|
} else {
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
Ticketp =
|
|
NatLookupFirewallTicket(
|
|
Interfacep,
|
|
NAT_PROTOCOL_UDP,
|
|
DestinationPort
|
|
);
|
|
|
|
if (NULL != Ticketp) {
|
|
act = FORWARD;
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
NatDereferenceInterface(Interfacep);
|
|
KeLowerIrql(Irql);
|
|
}
|
|
|
|
return act;
|
|
} // NatpReceiveNonUnicastPacket
|
|
|
|
FORWARD_ACTION
|
|
NatpReceivePacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to process locally destined packets.
|
|
All initial automatic translation of such packets occurs here,
|
|
based on destination of the packet, which may be a local IP address
|
|
or an IP address from the pool assigned to a boundary interface.
|
|
In the process, a mapping is created so that translation of the packet's
|
|
successors is handled in the fast path in 'NatTranslatePacket'.
|
|
|
|
Arguments:
|
|
|
|
Index - index of the interface on which the packet was received
|
|
|
|
DestinationType - receives 'DEST_INVALID' if destination changed
|
|
|
|
TranslateRoutine - points to the routine which performs translation
|
|
|
|
InReceiveBuffer - points to the packet buffer chain
|
|
|
|
OutReceiveBuffer - receives the packet buffer chain if translation occurs
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
--*/
|
|
|
|
{
|
|
FORWARD_ACTION act;
|
|
PNAT_USED_ADDRESS Addressp;
|
|
ULONG64 DestinationKey[NatMaximumPath];
|
|
USHORT DestinationPort;
|
|
ULONG i;
|
|
PNAT_INTERFACE Interfacep;
|
|
PLIST_ENTRY Link;
|
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|
ULONG NewDestinationAddress;
|
|
USHORT NewDestinationPort;
|
|
UCHAR Protocol;
|
|
ULONG64 SourceKey[NatMaximumPath];
|
|
USHORT SourcePort;
|
|
NTSTATUS status;
|
|
BOOLEAN TicketProcessingOnly;
|
|
USHORT MaxMSS = 0;
|
|
|
|
TRACE(PER_PACKET, ("NatpReceivePacket\n"));
|
|
|
|
//
|
|
// Look up the receiving interface.
|
|
// If the receiving interface is a boundary interface,
|
|
// then if we are unable to translate for any reason, the packet must be
|
|
// dropped as a matter of policy, unless it is locally-destined.
|
|
// Otherwise, we allow the stack to see the packet even if we cannot
|
|
// translate it.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
if (!NatLookupCachedInterface(Index, Interfacep)) {
|
|
act = FORWARD;
|
|
} else {
|
|
if (!NAT_INTERFACE_BOUNDARY(Interfacep)
|
|
&& !NAT_INTERFACE_FW(Interfacep)) {
|
|
Interfacep = NULL;
|
|
act = FORWARD;
|
|
} else {
|
|
NatReferenceInterface(Interfacep);
|
|
|
|
if(NAT_INTERFACE_FW(Interfacep)) {
|
|
act = DROP;
|
|
} else {
|
|
//
|
|
// See if the packet is locally-destined
|
|
//
|
|
|
|
if (Interfacep->AddressArray[0].Address ==
|
|
Contextp->DestinationAddress) {
|
|
act = FORWARD;
|
|
} else {
|
|
act = DROP;
|
|
for (i = 1; i < Interfacep->AddressCount; i++) {
|
|
if (Interfacep->AddressArray[i].Address ==
|
|
Contextp->DestinationAddress) {
|
|
//
|
|
// The packet's destination-address is local.
|
|
//
|
|
act = FORWARD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Set MaxMSS for the receiving interface so that SYN/ACK's MSS option might
|
|
// be adjusted if necessary.
|
|
//
|
|
if (NAT_INTERFACE_BOUNDARY(Interfacep)) {
|
|
MaxMSS = MAX_MSSOPTION(Interfacep->MTU);
|
|
}
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
if ((PVOID)TranslateRoutine == (PVOID)NatTranslatePacket) {
|
|
|
|
//
|
|
// If we don't recognize the receiving interface, return right away,
|
|
// unless someone has created a ticket somewhere. In the latter case,
|
|
// the packet to which the ticket must be applied may be received
|
|
// on a different interface than the interface to which the ticket
|
|
// is attached. (This may happen with one-way cable-modems or other
|
|
// asymmetric routes.) We catch that case here by using the ticket's
|
|
// interface for translation.
|
|
//
|
|
|
|
if (!Interfacep && !TicketCount) { return act; }
|
|
|
|
//
|
|
// This is either a TCP or a UDP packet.
|
|
//
|
|
|
|
Protocol = Contextp->Header->Protocol;
|
|
SourcePort = ((PUSHORT)Contextp->ProtocolHeader)[0];
|
|
DestinationPort = ((PUSHORT)Contextp->ProtocolHeader)[1];
|
|
|
|
//
|
|
// We allow the packet through if one of the following is true:
|
|
// (a) a ticket exists for the packet (e.g. a static port mapping)
|
|
// (b) a static address mapping exists for the packet's destination
|
|
// (c) this appears to be a DHCP unicast response:
|
|
// -- UDP
|
|
// -- source port 67
|
|
// -- destination port 68
|
|
// (d) this is a UDP packet that is destined for the local endpoint
|
|
// of some other mapping ("loose source matching")
|
|
//
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->DestinationAddress,
|
|
DestinationPort
|
|
);
|
|
|
|
if (Interfacep) {
|
|
TicketProcessingOnly = FALSE;
|
|
} else {
|
|
|
|
//
|
|
// We only reach this point if a ticket exists and we want to check
|
|
// if it applies to this packet even though this packet was not
|
|
// received on this interface. We now scan the interface list
|
|
// (again) to see if we can find one which has this ticket.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
for (Link = InterfaceList.Flink; Link != &InterfaceList;
|
|
Link = Link->Flink) {
|
|
Interfacep = CONTAINING_RECORD(Link, NAT_INTERFACE, Link);
|
|
if (NAT_INTERFACE_DELETED(Interfacep) ||
|
|
IsListEmpty(&Interfacep->TicketList)) {
|
|
Interfacep = NULL;
|
|
continue;
|
|
}
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
if (NatLookupTicket(
|
|
Interfacep,
|
|
DestinationKey[NatForwardPath],
|
|
SourceKey[NatForwardPath],
|
|
NULL
|
|
)) {
|
|
|
|
//
|
|
// This interface has a ticket for the packet;
|
|
// make a reference to it and end the search.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
NatReferenceInterface(Interfacep);
|
|
break;
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
Interfacep = NULL;
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
if (!Interfacep) { return act; }
|
|
TicketProcessingOnly = TRUE;
|
|
}
|
|
|
|
Mapping = NULL;
|
|
|
|
do {
|
|
|
|
//
|
|
// First see if we can quickly determine that this packet won't
|
|
// meet any of the allow in criteria.
|
|
//
|
|
|
|
if (!TicketCount
|
|
&& Interfacep->NoStaticMappingExists
|
|
&& NAT_PROTOCOL_UDP != Protocol
|
|
) {
|
|
|
|
//
|
|
// There's no way for this packet to meet any of the criteria
|
|
// that would allow it in:
|
|
// a) no tickets exist
|
|
// b) no static mappings exist for this interface
|
|
// c) it's not a UDP packet, and thus cannot be a unicast DHCP
|
|
// response. nor will it match a local UDP session endpoint
|
|
//
|
|
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// See if a ticket exists which applies to this session.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
|
|
if (!IsListEmpty(&Interfacep->TicketList)) {
|
|
|
|
status =
|
|
NatLookupAndRemoveTicket(
|
|
Interfacep,
|
|
DestinationKey[NatForwardPath],
|
|
SourceKey[NatForwardPath],
|
|
&Addressp,
|
|
&NewDestinationAddress,
|
|
&NewDestinationPort
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
|
|
//
|
|
// A ticket was found. Create a mapping for it.
|
|
//
|
|
|
|
TRACE(
|
|
XLATE, (
|
|
"NatpReceivePacket: using ticket to %d.%d.%d.%d/%d\n",
|
|
ADDRESS_BYTES(NewDestinationAddress),
|
|
NTOHS(NewDestinationPort)
|
|
));
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatReversePath],
|
|
Protocol,
|
|
NewDestinationAddress,
|
|
NewDestinationPort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatReversePath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
status =
|
|
NatCreateMapping(
|
|
NAT_MAPPING_FLAG_INBOUND,
|
|
DestinationKey,
|
|
SourceKey,
|
|
Interfacep,
|
|
Addressp,
|
|
MaxMSS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Mapping
|
|
);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// We have a mapping now in 'Mapping';
|
|
// Drop to the translation code below.
|
|
//
|
|
|
|
TicketProcessingOnly = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// No ticket, or failure creating mapping.
|
|
// Try other possibilities.
|
|
//
|
|
|
|
} // !IsListEmpty(TicketList)
|
|
|
|
//
|
|
// If we only reached this point because of a ticket,
|
|
// stop here, since the packet was not really received on
|
|
// 'Interfacep'.
|
|
//
|
|
|
|
if (TicketProcessingOnly) {
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// Since this is an inbound packet, we now look for
|
|
// a static-address mapping which allows inbound sessions.
|
|
//
|
|
|
|
if ((Addressp =
|
|
NatLookupStaticAddressPoolEntry(
|
|
Interfacep,
|
|
Contextp->DestinationAddress,
|
|
TRUE
|
|
))
|
|
&& NatReferenceAddressPoolEntry(Addressp)) {
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
|
|
TRACE(
|
|
XLATE, (
|
|
"NatpReceivePacket: using static address to %d.%d.%d.%d/%d\n",
|
|
ADDRESS_BYTES(Addressp->PrivateAddress),
|
|
NTOHS(DestinationPort)
|
|
));
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatReversePath],
|
|
Protocol,
|
|
Addressp->PrivateAddress,
|
|
DestinationPort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatReversePath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
|
|
//
|
|
// We will allow the packet through if we can reserve
|
|
// its destination port, i.e. if no existing session
|
|
// from the same remote endpoint is using that destination.
|
|
// Initialize a new dynamic mapping for the packet,
|
|
// and note that this will fail if such a duplicate exists.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
status =
|
|
NatCreateMapping(
|
|
NAT_MAPPING_FLAG_INBOUND,
|
|
DestinationKey,
|
|
SourceKey,
|
|
Interfacep,
|
|
Addressp,
|
|
MaxMSS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Mapping
|
|
);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
if (!NT_SUCCESS(status)) {
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// On reaching here, we will have created a mapping
|
|
// from a static address mapping.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If this is a UDP packet, see if its destination matches
|
|
// the public endpoint of an already existing mapping (i.e.,
|
|
// perform a mapping lookup ignoring the packet's source
|
|
// address and port).
|
|
//
|
|
|
|
if (NAT_PROTOCOL_UDP == Protocol) {
|
|
|
|
ULONG PrivateAddress;
|
|
USHORT PrivatePort;
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
|
|
//
|
|
// First search for a forward path (sessions that
|
|
// were originally outbound) match.
|
|
//
|
|
|
|
Mapping =
|
|
NatDestinationLookupForwardMapping(
|
|
DestinationKey[NatForwardPath]
|
|
);
|
|
|
|
if (NULL == Mapping) {
|
|
|
|
//
|
|
// No forward path match was found; attempt to
|
|
// locate a reverse path (sessions that were
|
|
// originally inbound) match.
|
|
//
|
|
|
|
Mapping =
|
|
NatDestinationLookupReverseMapping(
|
|
DestinationKey[NatForwardPath]
|
|
);
|
|
}
|
|
|
|
if (NULL != Mapping) {
|
|
|
|
IP_NAT_PATH Path;
|
|
|
|
//
|
|
// Determine the private address and port
|
|
//
|
|
|
|
Path =
|
|
NAT_MAPPING_INBOUND(Mapping)
|
|
? NatReversePath
|
|
: NatForwardPath;
|
|
|
|
PrivateAddress = MAPPING_ADDRESS(Mapping->SourceKey[Path]);
|
|
PrivatePort = MAPPING_PORT(Mapping->SourceKey[Path]);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
|
|
if (NULL != Mapping
|
|
&& NTOHS(PrivatePort) > NAT_XLATE_UDP_LSM_LOW_PORT) {
|
|
|
|
//
|
|
// A partial-match mapping exists, and the private port
|
|
// is within the allowed rage. Get the address
|
|
// object for the private endpoint
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
|
|
status =
|
|
NatAcquireFromAddressPool(
|
|
Interfacep,
|
|
PrivateAddress,
|
|
MAPPING_ADDRESS(DestinationKey[NatForwardPath]),
|
|
&Addressp
|
|
);
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Create the new mapping.
|
|
//
|
|
|
|
TRACE(
|
|
XLATE, (
|
|
"NatpReceivePacket: UDP LSM to %d.%d.%d.%d/%d\n",
|
|
ADDRESS_BYTES(PrivateAddress),
|
|
NTOHS(PrivatePort)
|
|
));
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatReversePath],
|
|
Protocol,
|
|
PrivateAddress,
|
|
PrivatePort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatReversePath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
status =
|
|
NatCreateMapping(
|
|
NAT_MAPPING_FLAG_INBOUND,
|
|
DestinationKey,
|
|
SourceKey,
|
|
Interfacep,
|
|
Addressp,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Mapping
|
|
);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
if (!NT_SUCCESS(status)) {
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// On reaching here, we will have created a mapping
|
|
// due to a loose UDP source match.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The code below assumes that this lock is held.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
}
|
|
|
|
//
|
|
// Check if this is may be a DHCP response packet. If the
|
|
// request that elicited this response was broadcast we
|
|
// won't have a corresponding mapping to allow the packet
|
|
// in; dropping the packet, though, will cause connectivity
|
|
// problems.
|
|
//
|
|
|
|
if (NAT_PROTOCOL_UDP == Protocol
|
|
&& NTOHS(DHCP_SERVER_PORT) == SourcePort
|
|
&& NTOHS(DHCP_CLIENT_PORT) == DestinationPort
|
|
&& NAT_INTERFACE_FW(Interfacep)) {
|
|
|
|
//
|
|
// What appears to be a unicast DHCP response was received
|
|
// on a firewalled interface. We need to always let such
|
|
// packets through to prevent an interruption in network
|
|
// connectivity.
|
|
//
|
|
|
|
act = FORWARD;
|
|
}
|
|
|
|
//
|
|
// This packet doesn't meet any criteria that allow for
|
|
// the creation of a mapping. Return the default action.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
|
|
} while (FALSE); // !Mapping
|
|
|
|
if (Interfacep) { NatDereferenceInterface(Interfacep); }
|
|
|
|
//
|
|
// Somewhere above a mapping was found or created.
|
|
// Translate the packet using that mapping
|
|
//
|
|
|
|
act =
|
|
Mapping->TranslateRoutine[NatForwardPath](
|
|
Mapping,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
//
|
|
// Release our reference on the mapping and the interface
|
|
//
|
|
|
|
NatDereferenceMapping(Mapping);
|
|
return act;
|
|
|
|
} // TranslateRoutine != NatTranslatePacket
|
|
|
|
//
|
|
// This is neither a TCP nor a UDP packet.
|
|
// If it is coming in on a boundary interface, translate it;
|
|
// otherwise let it pass through unscathed.
|
|
//
|
|
// N.B. The translation routine must be invoked with a reference made
|
|
// to the boundary interface, and without holding the mapping lock.
|
|
//
|
|
|
|
if (TranslateRoutine) {
|
|
act =
|
|
TranslateRoutine(
|
|
Interfacep,
|
|
NatInboundDirection,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
}
|
|
|
|
if (Interfacep) { NatDereferenceInterface(Interfacep); }
|
|
return act;
|
|
|
|
} // NatpReceivePacket
|
|
|
|
|
|
NTSTATUS
|
|
NatpRouteChangeCompletionRoutine(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked by the I/O manager upon completion of
|
|
a route-change-notification request. It invalidates our route-cache and,
|
|
unless shutdown is in progress, re-issues the route-change-notification
|
|
request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object of the IP driver
|
|
|
|
Irp - the completed I/O request packet
|
|
|
|
Context - unused
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED - indication that the IRP should be freed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
KIRQL Irql;
|
|
NTSTATUS status;
|
|
|
|
CALLTRACE(("NatpRouteChangeCompletionRoutine\n"));
|
|
|
|
//
|
|
// Invalidate the entire route-cache.
|
|
//
|
|
|
|
KeAcquireSpinLock(&RouteCacheLock, &Irql);
|
|
InitializeCache(RouteCache);
|
|
|
|
//
|
|
// If we cannot re-reference the module, relinquish the IRP.
|
|
//
|
|
|
|
if (!RouteCacheIrp || !REFERENCE_NAT()) {
|
|
KeReleaseSpinLock(&RouteCacheLock, Irql);
|
|
DEREFERENCE_NAT_AND_RETURN(STATUS_SUCCESS);
|
|
}
|
|
|
|
Irp->Cancel = FALSE;
|
|
KeReleaseSpinLock(&RouteCacheLock, Irql);
|
|
|
|
//
|
|
// Reinitialize the IRP structure and submit it again
|
|
// for further notification.
|
|
//
|
|
|
|
Irp->IoStatus.Status = 0;
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
IoSetCompletionRoutine(
|
|
Irp,
|
|
NatpRouteChangeCompletionRoutine,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_IP_RTCHANGE_NOTIFY_REQUEST;
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
|
|
status = IoCallDriver(IpDeviceObject, Irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ERROR(("NatpRouteChangeWorkerRoutine: IoCallDriver=0x%08X\n", status));
|
|
}
|
|
|
|
DEREFERENCE_NAT_AND_RETURN(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // NatpRouteChangeCompletionRoutine
|
|
|
|
|
|
ULONG
|
|
FASTCALL
|
|
NatpRoutePacket(
|
|
ULONG DestinationAddress,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNTSTATUS Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to determine the index of the outgoing adapter
|
|
for a given source/destination pair.
|
|
It attempts to retrieve the required information from our route-table
|
|
cache, and if unsuccessful consults the IP routing tables.
|
|
|
|
Arguments:
|
|
|
|
DestinationAddress - the destination address for the packet
|
|
|
|
Contextp - optionally supplies the context of the packet on whose
|
|
behalf a route-lookup is being requested. If demand-dial were required
|
|
in order to forward the packet, this data would be needed in order
|
|
for demand-dial filters to function correctly.
|
|
|
|
Status - receives the status of the lookup in the event of a failure
|
|
|
|
Return Value:
|
|
|
|
ULONG - the index of the outgoing interface, or INVALID_IF_INDEX if none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR Buffer;
|
|
ULONG BufferLength;
|
|
PNAT_CACHED_ROUTE CachedRoute;
|
|
ULONG Index;
|
|
KIRQL Irql;
|
|
ULONG Length;
|
|
IPRouteLookupData RouteLookupData;
|
|
|
|
TRACE(PER_PACKET, ("NatpRoutePacket\n"));
|
|
|
|
//
|
|
// Probe the cache for the destination IP address specified.
|
|
//
|
|
|
|
KeAcquireSpinLock(&RouteCacheLock, &Irql);
|
|
if ((CachedRoute = ProbeCache(RouteCache, DestinationAddress)) &&
|
|
CachedRoute->DestinationAddress == DestinationAddress) {
|
|
Index = CachedRoute->Index;
|
|
KeReleaseSpinLock(&RouteCacheLock, Irql);
|
|
TRACE(PER_PACKET, ("NatpRoutePacket: cache hit\n"));
|
|
return Index;
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&RouteCacheLock);
|
|
|
|
//
|
|
// The cache did not have the value requested,
|
|
// so consult the TCP/IP driver directly.
|
|
//
|
|
|
|
RouteLookupData.Version = 0;
|
|
RouteLookupData.DestAdd = DestinationAddress;
|
|
if (Contextp) {
|
|
RouteLookupData.SrcAdd = Contextp->SourceAddress;
|
|
RouteLookupData.Info[0] = (Contextp->Header)->Protocol;
|
|
|
|
if (NAT_PROTOCOL_TCP == RouteLookupData.Info[0]) {
|
|
Buffer = Contextp->ProtocolHeader;
|
|
BufferLength = sizeof(TCP_HEADER);
|
|
} else if (NAT_PROTOCOL_UDP == RouteLookupData.Info[0]) {
|
|
Buffer = Contextp->ProtocolHeader;
|
|
BufferLength = sizeof(UDP_HEADER);
|
|
} else if (NAT_PROTOCOL_ICMP == RouteLookupData.Info[0]) {
|
|
Buffer = Contextp->ProtocolHeader;
|
|
BufferLength = sizeof(ICMP_HEADER);
|
|
} else {
|
|
Buffer = Contextp->RecvBuffer->ipr_buffer;
|
|
BufferLength = Contextp->RecvBuffer->ipr_size;
|
|
}
|
|
} else {
|
|
RouteLookupData.SrcAdd = 0;
|
|
Buffer = NULL;
|
|
BufferLength = 0;
|
|
}
|
|
Length = sizeof(Index);
|
|
|
|
*Status =
|
|
LookupRouteInformationWithBuffer(
|
|
&RouteLookupData,
|
|
Buffer,
|
|
BufferLength,
|
|
NULL,
|
|
IPRouteOutgoingFirewallContext,
|
|
&Index,
|
|
&Length
|
|
);
|
|
|
|
if (!NT_SUCCESS(*Status) || *Status == STATUS_PENDING) {
|
|
KeLowerIrql(Irql);
|
|
return INVALID_IF_INDEX;
|
|
}
|
|
|
|
//
|
|
// Update the cache with the entry retrieved,
|
|
// assuming we've had enough misses to warrant
|
|
// replacement of the cache-index's current contents.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&RouteCacheLock);
|
|
CachedRoute = &RouteCacheTable[CACHE_INDEX(DestinationAddress)];
|
|
if (UpdateCache(RouteCache, DestinationAddress, CachedRoute)) {
|
|
CachedRoute->DestinationAddress = DestinationAddress;
|
|
CachedRoute->Index = Index;
|
|
}
|
|
KeReleaseSpinLock(&RouteCacheLock, Irql);
|
|
|
|
return Index;
|
|
|
|
} // NatpRoutePacket
|
|
|
|
|
|
FORWARD_ACTION
|
|
NatpSendNonUnicastPacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to process locally sent non-unicast packets.
|
|
If the packet is to be sent on a firewalled interface it must have a valid
|
|
source address for that interface.
|
|
|
|
Arguments:
|
|
|
|
Index - index of the interface on which the packet is to be sent
|
|
|
|
Contextp - the context for this packet
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
--*/
|
|
|
|
{
|
|
FORWARD_ACTION act;
|
|
PNAT_INTERFACE Interfacep;
|
|
ULONG i;
|
|
KIRQL Irql;
|
|
|
|
TRACE(PER_PACKET, ("NatpSendNonUnicastPacket\n"));
|
|
|
|
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
|
|
|
if (!NatLookupCachedInterface(Index, Interfacep)
|
|
|| !NAT_INTERFACE_FW(Interfacep)) {
|
|
|
|
//
|
|
// The packet is not to be sent on a firewalled interface
|
|
//
|
|
|
|
KeReleaseSpinLock(&InterfaceLock, Irql);
|
|
act = FORWARD;
|
|
|
|
} else {
|
|
|
|
NatReferenceInterface(Interfacep);
|
|
KeReleaseSpinLock(&InterfaceLock, Irql);
|
|
|
|
//
|
|
// Make sure the packet has a valid source address
|
|
//
|
|
|
|
act = DROP;
|
|
|
|
if (Interfacep->AddressArray[0].Address == Contextp->SourceAddress) {
|
|
act = FORWARD;
|
|
} else {
|
|
for (i = 1; i < Interfacep->AddressCount; i++) {
|
|
if (Contextp->SourceAddress ==
|
|
Interfacep->AddressArray[i].Address) {
|
|
|
|
act = FORWARD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DROP == act
|
|
&& 0 == Contextp->SourceAddress
|
|
&& NAT_PROTOCOL_UDP == Contextp->Header->Protocol
|
|
&& NTOHS(DHCP_CLIENT_PORT) ==
|
|
((PUDP_HEADER)Contextp->ProtocolHeader)->SourcePort
|
|
&& NTOHS(DHCP_SERVER_PORT) ==
|
|
((PUDP_HEADER)Contextp->ProtocolHeader)->DestinationPort) {
|
|
|
|
//
|
|
// This appears to be a DHCP request sent from an adapter that
|
|
// has a non-DHCP allocated address (e.g., an autonet address). In
|
|
// this situation the DHCP client will use a source address of
|
|
// 0.0.0.0. These packets should always be forwarded.
|
|
//
|
|
|
|
act = FORWARD;
|
|
}
|
|
|
|
NatDereferenceInterface(Interfacep);
|
|
}
|
|
|
|
return act;
|
|
|
|
} // NatpSendNonUnicastPacket
|
|
|
|
FORWARD_ACTION
|
|
NatpSendPacket(
|
|
ULONG Index,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to process locally generated packets.
|
|
Most locally-generated packets do not require translation at all.
|
|
The exceptions arise with applications that bind to a private IP address
|
|
but then send packets to the public network, as well as with certain
|
|
applications (PPTP, ICMP) which must be forced through the translation
|
|
path to ensure that certain fields are unique for all sessions sharing
|
|
the public IP address(es). (E.g. the PPTP GRE call-identifier.)
|
|
|
|
Arguments:
|
|
|
|
Index - the interface on which the packet is to be sent
|
|
|
|
DestinationType - receives 'DEST_INVALID' if destination changed
|
|
|
|
TranslateRoutine - points to the routine which performs translation
|
|
|
|
InReceiveBuffer - points to the packet buffer chain
|
|
|
|
OutReceiveBuffer - receives the packet buffer chain if translation occurs
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
--*/
|
|
|
|
{
|
|
FORWARD_ACTION act;
|
|
PNAT_USED_ADDRESS Addressp;
|
|
ULONG64 DestinationKey[NatMaximumPath];
|
|
USHORT DestinationPort;
|
|
ULONG i;
|
|
PNAT_DYNAMIC_MAPPING InsertionPoint;
|
|
PNAT_INTERFACE Interfacep;
|
|
KIRQL Irql;
|
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|
USHORT PortAcquired;
|
|
UCHAR Protocol;
|
|
ULONG64 SourceKey[NatMaximumPath];
|
|
USHORT SourcePort;
|
|
NTSTATUS status;
|
|
|
|
TRACE(PER_PACKET, ("NatpSendPacket\n"));
|
|
|
|
//
|
|
// Look up the sending interface, and set the default action
|
|
// in 'act'. If the packet's source address is not on the sending interface,
|
|
// then we must translate it like any other outbound packet.
|
|
// Otherwise, we may let the packet through unchanged, so long as
|
|
// there is no mapping for it created by a director.
|
|
// N.B. We record the original IRQL since local-sends are not guaranteed
|
|
// to be passed to us at dispatch level.
|
|
//
|
|
|
|
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
|
if (!NatLookupCachedInterface(Index, Interfacep) ||
|
|
(!NAT_INTERFACE_BOUNDARY(Interfacep)
|
|
&& !NAT_INTERFACE_FW(Interfacep))) {
|
|
|
|
//
|
|
// This packet is not being sent on a boundary or firewalled interface,
|
|
// so we won't normally translate it.
|
|
//
|
|
// However, a special case arises when a local MTU mismatch results in
|
|
// the forwarder generating an ICMP path MTU error message.
|
|
// The forwarder generates the error based on the *translated* packet
|
|
// which has the public IP address as its source, and so the error
|
|
// ends up being sent on the loopback interface to the local machine.
|
|
// When we see an ICMP message on the loopback interface, give the
|
|
// ICMP translator a chance to modify it *if there are any mappings*.
|
|
//
|
|
|
|
if (LoopbackIndex == Index &&
|
|
TranslateRoutine == NatTranslateIcmp &&
|
|
MappingCount) {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
act =
|
|
NatTranslateIcmp(
|
|
NULL,
|
|
NatInboundDirection,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
KeLowerIrql(Irql);
|
|
return act;
|
|
} else {
|
|
KeReleaseSpinLock(&InterfaceLock, Irql);
|
|
return FORWARD;
|
|
}
|
|
}
|
|
NatReferenceInterface(Interfacep);
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
|
|
Protocol = Contextp->Header->Protocol;
|
|
|
|
if (Interfacep->AddressArray[0].Address == Contextp->SourceAddress) {
|
|
act = FORWARD;
|
|
} else {
|
|
|
|
//
|
|
// The outgoing interface is a boundary interface.
|
|
// This means that if the packet's source-address is the address
|
|
// of an interface other than 'Interfacep', it is a private address,
|
|
// and we'll need to discard the packet if it cannot be translated.
|
|
// Therefore, see whether the packet's source-address is public.
|
|
//
|
|
|
|
act = DROP;
|
|
for (i = 1; i < Interfacep->AddressCount; i++) {
|
|
if (Contextp->SourceAddress ==
|
|
Interfacep->AddressArray[i].Address) {
|
|
|
|
//
|
|
// The packet's source-address is public,
|
|
// so the packet will be allowed to go out untranslated.
|
|
//
|
|
|
|
act = FORWARD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the packet's source-address is not private, we can avoid
|
|
// translating it unless
|
|
// (a) it's an ICMP packet
|
|
// (b) it's a PPTP data packet
|
|
// (c) it's a PPTP control-session packet.
|
|
// (d) the interface is in FW mode -- need to generate a mapping so
|
|
// that inbound packets for this connection won't be dropped
|
|
//
|
|
|
|
if (act == FORWARD &&
|
|
!NAT_INTERFACE_FW(Interfacep) &&
|
|
Protocol != NAT_PROTOCOL_ICMP &&
|
|
Protocol != NAT_PROTOCOL_PPTP &&
|
|
(Protocol != NAT_PROTOCOL_TCP ||
|
|
((PUSHORT)Contextp->ProtocolHeader)[1] != NTOHS(PPTP_CONTROL_PORT))) {
|
|
KeLowerIrql(Irql);
|
|
NatDereferenceInterface(Interfacep);
|
|
return FORWARD;
|
|
}
|
|
|
|
//
|
|
// The packet may require some form of translation.
|
|
//
|
|
|
|
if ((PVOID)TranslateRoutine == (PVOID)NatTranslatePacket) {
|
|
|
|
SourcePort = ((PUSHORT)Contextp->ProtocolHeader)[0];
|
|
DestinationPort = ((PUSHORT)Contextp->ProtocolHeader)[1];
|
|
|
|
//
|
|
// The packet is either TCP or UDP.
|
|
// Generate a mapping for the packet's session.
|
|
//
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->SourceAddress,
|
|
SourcePort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatForwardPath],
|
|
Protocol,
|
|
Contextp->DestinationAddress,
|
|
DestinationPort
|
|
);
|
|
|
|
//
|
|
// Acquire an endpoint for the mapping. If the interface
|
|
// is in FW mode and act == FORWARD, however, we only
|
|
// want to get the address structure corresponding to the
|
|
// source address of the packet
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&MappingLock);
|
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|
|
|
if(act != FORWARD || !NAT_INTERFACE_FW(Interfacep)) {
|
|
status =
|
|
NatAcquireEndpointFromAddressPool(
|
|
Interfacep,
|
|
SourceKey[NatForwardPath],
|
|
DestinationKey[NatForwardPath],
|
|
0,
|
|
MAPPING_PORT(SourceKey[NatForwardPath]),
|
|
TRUE,
|
|
&Addressp,
|
|
&PortAcquired
|
|
);
|
|
|
|
} else {
|
|
|
|
PortAcquired = SourcePort;
|
|
status =
|
|
NatAcquireFromAddressPool(
|
|
Interfacep,
|
|
Contextp->SourceAddress,
|
|
Contextp->SourceAddress,
|
|
&Addressp
|
|
);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|
if (!NT_SUCCESS(status)) {
|
|
KeReleaseSpinLock(&MappingLock, Irql);
|
|
ExInterlockedAddLargeStatistic(
|
|
(PLARGE_INTEGER)&Interfacep->Statistics.RejectsForward, 1
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
return DROP;
|
|
}
|
|
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey[NatReversePath],
|
|
Protocol,
|
|
Contextp->DestinationAddress,
|
|
DestinationPort
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey[NatReversePath],
|
|
Protocol,
|
|
Addressp->PublicAddress,
|
|
PortAcquired
|
|
);
|
|
|
|
//
|
|
// Allocate a mapping.
|
|
//
|
|
|
|
status =
|
|
NatCreateMapping(
|
|
0,
|
|
DestinationKey,
|
|
SourceKey,
|
|
Interfacep,
|
|
(PVOID)Addressp,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Mapping
|
|
);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
if (!NT_SUCCESS(status)) {
|
|
KeLowerIrql(Irql);
|
|
ExInterlockedAddLargeStatistic(
|
|
(PLARGE_INTEGER)&Interfacep->Statistics.RejectsForward, 1
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
return DROP;
|
|
}
|
|
|
|
//
|
|
// Activate any applicable dynamic tickets
|
|
//
|
|
|
|
if (DynamicTicketCount) {
|
|
NatLookupAndApplyDynamicTicket(
|
|
Protocol,
|
|
DestinationPort,
|
|
Interfacep,
|
|
Addressp->PublicAddress,
|
|
Contextp->SourceAddress
|
|
);
|
|
}
|
|
|
|
//
|
|
// Perform the actual translation
|
|
// This replaces the source endpoint with a globally-valid
|
|
// endpoint.
|
|
//
|
|
|
|
act =
|
|
Mapping->TranslateRoutine[NatForwardPath](
|
|
Mapping,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
//
|
|
// Release our reference on the mapping and the interface
|
|
//
|
|
|
|
KeLowerIrql(Irql);
|
|
NatDereferenceMapping(Mapping);
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
|
|
} // TranslateRoutine != NatTranslatePacket
|
|
|
|
//
|
|
// Perform ICMP/PPTP translation.
|
|
//
|
|
// N.B. The translation routine must be invoked with a reference made
|
|
// to the boundary interface, and without holding the mapping lock.
|
|
//
|
|
|
|
if (TranslateRoutine) {
|
|
act =
|
|
TranslateRoutine(
|
|
Interfacep,
|
|
NatOutboundDirection,
|
|
Contextp,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
}
|
|
KeLowerIrql(Irql);
|
|
NatDereferenceInterface(Interfacep);
|
|
return act;
|
|
|
|
} // NatpSendPacket
|
|
|
|
|
|
FORWARD_ACTION
|
|
NatpTranslateLocalTraffic(
|
|
PNAT_INTERFACE Interfacep OPTIONAL,
|
|
IP_NAT_DIRECTION Direction,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InRecvBuffer,
|
|
IPRcvBuf** OutRecvBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will forward unmodified traffic that is either:
|
|
* received by the local machine
|
|
* sent by the local machine
|
|
|
|
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;
|
|
ULONG i;
|
|
|
|
TRACE(PER_PACKET, ("NatpTranslateLocalTraffic\n"));
|
|
|
|
if (NatInboundDirection == Direction) {
|
|
|
|
//
|
|
// Inbound traffic must be directed to the local machine, and thus
|
|
// is always forwarded.
|
|
//
|
|
|
|
act = FORWARD;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is an outgoing packet. We only allow packets that have the
|
|
// same source address as the interface that they are being sent
|
|
// on. This prevents a packet from the private network from being
|
|
// sent to the public network.
|
|
//
|
|
|
|
if (Interfacep->AddressArray[0].Address == Contextp->SourceAddress) {
|
|
act = FORWARD;
|
|
} else {
|
|
act = DROP;
|
|
for (i = 1; i < Interfacep->AddressCount; i++) {
|
|
if (Contextp->SourceAddress ==
|
|
Interfacep->AddressArray[i].Address) {
|
|
|
|
//
|
|
// The packet's source-address is valid,
|
|
// so the packet will be allowed to go out.
|
|
//
|
|
|
|
act = FORWARD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return act;
|
|
} // NatpTranslateLocalTraffic
|
|
|
|
|
|
|
|
FORWARD_ACTION
|
|
NatReverseTcpStateCheck(
|
|
PNAT_DYNAMIC_MAPPING pMapping,
|
|
PTCP_HEADER pTcpHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates that packets for a TCP passive open are valid:
|
|
-- only SYN/ACK (or RST) at first
|
|
-- no SYN after connection is opened (ACK of SYN/ACK)
|
|
|
|
Arguments:
|
|
|
|
pMapping -- the mapping this packet belongs to
|
|
|
|
pTcpHeader -- the TCP header of the packet
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
Environment:
|
|
|
|
Invoked with pMapping->Lock held by the caller
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT Flags = TCP_ALL_FLAGS(pTcpHeader);
|
|
|
|
if (NAT_MAPPING_TCP_OPEN(pMapping)) {
|
|
|
|
//
|
|
// Connection open -- SYN not allowed
|
|
//
|
|
|
|
return (Flags & TCP_FLAG_SYN) ? DROP : FORWARD;
|
|
|
|
} else {
|
|
|
|
ASSERT(pMapping->Flags & NAT_MAPPING_FLAG_FWD_SYN);
|
|
|
|
//
|
|
// SYN received, can only send SYN/ACK, RST, or ACK/RST
|
|
//
|
|
|
|
if (Flags == (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
|
|
pMapping->Flags |= NAT_MAPPING_FLAG_REV_SYN;
|
|
} else if (Flags != TCP_FLAG_RST
|
|
&& Flags != (TCP_FLAG_ACK | TCP_FLAG_RST)) {
|
|
return DROP;
|
|
}
|
|
}
|
|
|
|
return FORWARD;
|
|
}
|
|
|
|
|
|
VOID
|
|
NatShutdownPacketManagement(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to shutdown the packet-management module.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Invoked at passive level.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL Irql;
|
|
CALLTRACE(("NatShutdownPacketManagement\n"));
|
|
|
|
KeAcquireSpinLock(&RouteCacheLock, &Irql);
|
|
if (RouteCacheIrp) {
|
|
PIRP Irp = RouteCacheIrp;
|
|
RouteCacheIrp = NULL;
|
|
KeReleaseSpinLock(&RouteCacheLock, Irql);
|
|
IoCancelIrp(Irp);
|
|
KeAcquireSpinLock(&RouteCacheLock, &Irql);
|
|
}
|
|
KeReleaseSpinLock(&RouteCacheLock, Irql);
|
|
|
|
} // NatShutdownPacketManagement
|
|
|
|
|
|
FORWARD_ACTION
|
|
NatTranslatePacket(
|
|
IPRcvBuf** InReceiveBuffer,
|
|
ULONG ReceiveIndex,
|
|
PULONG SendIndex,
|
|
PUCHAR DestinationType,
|
|
PVOID Unused,
|
|
ULONG UnusedLength,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to translate a packet just received or a packet
|
|
about to be transmitted. This is the entrypoint into the NAT from TCP/IP,
|
|
invoked for every locally-received and locally-generated IP packet,
|
|
including loopback and transit packets. It is therefore critical
|
|
that a decision be made on each packet as early as possible.
|
|
|
|
Arguments:
|
|
|
|
InReceiveBuffer - points to the packet buffer chain
|
|
|
|
ReceiveIndex - index of the adapter on which the packet arrived
|
|
|
|
SendIndex - index of the adapter on which the packet is to be sent
|
|
|
|
DestinationType - indicates type of packet (broadcast/multicast/unicast)
|
|
|
|
Unused - unused
|
|
|
|
UnusedLength - unused
|
|
|
|
OutReceiveBuffer - receives packet buffer chain if translation occurs.
|
|
|
|
Return Value:
|
|
|
|
FORWARD_ACTION - indicates whether to 'FORWARD' or 'DROP' the packet.
|
|
|
|
--*/
|
|
|
|
{
|
|
FORWARD_ACTION act;
|
|
NAT_XLATE_CONTEXT Context;
|
|
ULONG64 DestinationKey;
|
|
PIP_HEADER IpHeader;
|
|
KIRQL Irql;
|
|
ULONG Length;
|
|
USHORT TcpFlags;
|
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|
IPRouteLookupData RouteLookupData;
|
|
ULONG64 SourceKey;
|
|
NTSTATUS status;
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutine;
|
|
|
|
TRACE(
|
|
PER_PACKET, (
|
|
"NatTranslatePacket(r=%d,s=%d,t=%d)\n",
|
|
ReceiveIndex,
|
|
*SendIndex,
|
|
*DestinationType
|
|
));
|
|
|
|
//
|
|
// See if the packet is a unicast, and if not, return immediately.
|
|
//
|
|
|
|
if (IS_BCAST_DEST(*DestinationType)) {
|
|
|
|
//
|
|
// Double-check the dest-type flag,
|
|
// which will appear to be set if the dest-type has been invalidated.
|
|
// If the dest-type has been invalidated, we'll need to guess
|
|
// whether the packet is a unicast.
|
|
//
|
|
|
|
if (*DestinationType != DEST_INVALID ||
|
|
!NatpIsUnicastPacket(
|
|
((PIP_HEADER)(*InReceiveBuffer)->ipr_buffer)->DestinationAddress
|
|
)) {
|
|
|
|
//
|
|
// We process non-unicast packets if
|
|
// * It is locally-destined or locally-sent
|
|
// * There is at least one firewalled interface
|
|
// * AllowInboundNonUnicastTraffic is FALSE
|
|
//
|
|
|
|
if (!AllowInboundNonUnicastTraffic
|
|
&& FirewalledInterfaceCount > 0
|
|
&& (LOCAL_IF_INDEX == *SendIndex
|
|
|| LOCAL_IF_INDEX == ReceiveIndex)) {
|
|
|
|
//
|
|
// Build the context for this packet and, if successful, call
|
|
// the non-unicast processing routine.
|
|
//
|
|
|
|
IpHeader = (PIP_HEADER)(*InReceiveBuffer)->ipr_buffer;
|
|
NAT_BUILD_XLATE_CONTEXT(
|
|
&Context,
|
|
IpHeader,
|
|
DestinationType,
|
|
*InReceiveBuffer,
|
|
IpHeader->SourceAddress,
|
|
IpHeader->DestinationAddress
|
|
);
|
|
if (!Context.ProtocolRecvBuffer) { return DROP; }
|
|
|
|
if (LOCAL_IF_INDEX == *SendIndex) {
|
|
act = NatpReceiveNonUnicastPacket(ReceiveIndex, &Context);
|
|
} else {
|
|
act = NatpSendNonUnicastPacket(*SendIndex, &Context);
|
|
}
|
|
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
return act;
|
|
|
|
} else {
|
|
TRACE(PER_PACKET, ("NatTranslatePacket: non-unicast ignored\n"));
|
|
return FORWARD;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We guessed the packet is a unicast; process it below.
|
|
//
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// This is a unicast packet;
|
|
// Determine which translation routine should handle it,
|
|
// based on the packet's IP-layer protocol number.
|
|
//
|
|
// N.B. This determination is made with *one* access to the table
|
|
// of translation routines. Interlocked changes may be made to this table
|
|
// as the global configuration changes, so we must read from it
|
|
// using a single access.
|
|
//
|
|
|
|
IpHeader = (PIP_HEADER)(*InReceiveBuffer)->ipr_buffer;
|
|
TranslateRoutine = TranslateRoutineTable[IpHeader->Protocol];
|
|
|
|
//
|
|
// Return quickly if we have nothing to do, that is,
|
|
// if this is a TCP/UDP packet but there are no interfaces,
|
|
// no registered directors, and no mappings.
|
|
//
|
|
|
|
if ((PVOID)TranslateRoutine == (PVOID)NatTranslatePacket &&
|
|
!InterfaceCount &&
|
|
!DirectorCount &&
|
|
!MappingCount) {
|
|
return FORWARD;
|
|
}
|
|
|
|
//
|
|
// Prepare to translate the packet by building a translation context
|
|
// that encapsulates all the information we'll be using in the remainder
|
|
// of the translation path. If this fails, the packet must be malformed
|
|
// in some way, and we return control right away.
|
|
//
|
|
|
|
NAT_BUILD_XLATE_CONTEXT(
|
|
&Context,
|
|
IpHeader,
|
|
DestinationType,
|
|
*InReceiveBuffer,
|
|
IpHeader->SourceAddress,
|
|
IpHeader->DestinationAddress
|
|
);
|
|
if (!Context.ProtocolRecvBuffer) { return DROP; }
|
|
|
|
//
|
|
// The packet is a loopback, so return control right away unless there is
|
|
// at least one director. Loopback packets are never translated unless a
|
|
// director specifically asks us to do so. One director that might request
|
|
// a loopback translation is the redirect-director.
|
|
// (See 'REDIRECT.C' and the flag 'IP_NAT_REDIRECT_FLAG_LOOPBACK'.)
|
|
//
|
|
|
|
if (LoopbackIndex != INVALID_IF_INDEX &&
|
|
((ReceiveIndex == LOCAL_IF_INDEX && *SendIndex == LoopbackIndex) ||
|
|
(*SendIndex == LOCAL_IF_INDEX && ReceiveIndex == LoopbackIndex))) {
|
|
|
|
if (!DirectorCount && TranslateRoutine != NatTranslateIcmp) {
|
|
TRACE(
|
|
PER_PACKET, (
|
|
"NatTranslatePacket: ignoring loopback (r=%d,s=%d,t=%d)\n",
|
|
ReceiveIndex,
|
|
*SendIndex,
|
|
*DestinationType
|
|
));
|
|
return FORWARD;
|
|
}
|
|
Context.Flags = NAT_XLATE_FLAG_LOOPBACK;
|
|
} else {
|
|
Context.Flags = 0;
|
|
}
|
|
|
|
//
|
|
// Now we are at the fast-path for translation of TCP/UDP session packets.
|
|
// From here on we need to execute at dispatch IRQL, so raise the IRQL
|
|
// in case we were entered at passive IRQL (e.g. during a local send).
|
|
// We then perform a mapping lookup and attempt to use that to translate
|
|
// this packet.
|
|
//
|
|
|
|
if ((PVOID)TranslateRoutine != (PVOID)NatTranslatePacket || !MappingCount) {
|
|
KeRaiseIrql(DISPATCH_LEVEL, &Irql);
|
|
} else {
|
|
|
|
//
|
|
// If this is a TCP packet, check for invalid TCP flags combinations:
|
|
// * no flag bit sets
|
|
// * none of SYN, ACK, or RST are set
|
|
// * RST w/ anything except ACK
|
|
// * SYN w/ anything except ACK
|
|
//
|
|
// These checks need to happen before searching the mapping trees to
|
|
// prevents certain classes of denial of service attacks (e.g.,
|
|
// 'stream.c').
|
|
//
|
|
|
|
if (NAT_PROTOCOL_TCP == IpHeader->Protocol) {
|
|
TcpFlags = TCP_ALL_FLAGS((PTCP_HEADER)Context.ProtocolHeader);
|
|
if (!TcpFlags
|
|
|| !(TcpFlags & (TCP_FLAG_SYN | TCP_FLAG_ACK | TCP_FLAG_RST))
|
|
|| ((TcpFlags & TCP_FLAG_RST)
|
|
&& (TcpFlags & ~(TCP_FLAG_RST | TCP_FLAG_ACK)))
|
|
|| ((TcpFlags & TCP_FLAG_SYN)
|
|
&& (TcpFlags & ~(TCP_FLAG_SYN | TCP_FLAG_ACK)))) {
|
|
|
|
#if NAT_WMI
|
|
NatLogDroppedPacket(&Context);
|
|
#endif
|
|
return DROP;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build a mapping lookup key
|
|
//
|
|
|
|
MAKE_MAPPING_KEY(
|
|
DestinationKey,
|
|
IpHeader->Protocol,
|
|
Context.DestinationAddress,
|
|
((PUSHORT)Context.ProtocolHeader)[1]
|
|
);
|
|
MAKE_MAPPING_KEY(
|
|
SourceKey,
|
|
IpHeader->Protocol,
|
|
Context.SourceAddress,
|
|
((PUSHORT)Context.ProtocolHeader)[0]
|
|
);
|
|
|
|
//
|
|
// Look for a mapping, and translate if found.
|
|
//
|
|
// N.B. We expect to receive more data than we send,
|
|
// and so we first look for a reverse mapping (i.e., incoming).
|
|
//
|
|
|
|
KeAcquireSpinLock(&MappingLock, &Irql);
|
|
if (Mapping =
|
|
NatLookupReverseMapping(
|
|
DestinationKey,
|
|
SourceKey,
|
|
NULL
|
|
)) {
|
|
NatReferenceMapping(Mapping);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
*SendIndex = INVALID_IF_INDEX;
|
|
|
|
act =
|
|
Mapping->TranslateRoutine[NatReversePath](
|
|
Mapping,
|
|
&Context,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
NatDereferenceMapping(Mapping);
|
|
KeLowerIrql(Irql);
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
return act;
|
|
} else if (Mapping =
|
|
NatLookupForwardMapping(
|
|
DestinationKey,
|
|
SourceKey,
|
|
NULL
|
|
)) {
|
|
NatReferenceMapping(Mapping);
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
*SendIndex = INVALID_IF_INDEX;
|
|
|
|
act =
|
|
Mapping->TranslateRoutine[NatForwardPath](
|
|
Mapping,
|
|
&Context,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
NatDereferenceMapping(Mapping);
|
|
KeLowerIrql(Irql);
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
return act;
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|
|
|
//
|
|
// No mapping was found; go through the process of establishing one.
|
|
//
|
|
}
|
|
|
|
//
|
|
// The packet could not be dispensed with in the fast path,
|
|
// so now we enter the second stage of processing.
|
|
// We will first look for a director who knows what to do with the packet,
|
|
// then we will attempt to automatically translate the packet,
|
|
// if it will cross a boundary interface.
|
|
//
|
|
// N.B. If we have neither local interfaces nor installed directors,
|
|
// we know we will make no changes, so return quickly.
|
|
//
|
|
|
|
if (!InterfaceCount && !DirectorCount) {
|
|
KeLowerIrql(Irql);
|
|
return FORWARD;
|
|
}
|
|
|
|
//
|
|
// Look first of all for a director.
|
|
// If no director is found, or if the director found supplies no mapping,
|
|
// proceed to the automatic-translation code, which is performed
|
|
// for packets which cross boundary interfaces. Note, therefore,
|
|
// that we never do automatic translation for loopback packets.
|
|
//
|
|
|
|
if (DirectorCount) {
|
|
status =
|
|
NatpDirectPacket(
|
|
ReceiveIndex,
|
|
*SendIndex,
|
|
&Context,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer,
|
|
&act
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
KeLowerIrql(Irql);
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
return act;
|
|
}
|
|
}
|
|
|
|
KeLowerIrql(Irql);
|
|
|
|
if (!InterfaceCount ||
|
|
((Context.Flags & NAT_XLATE_FLAG_LOOPBACK) &&
|
|
!(*SendIndex == LoopbackIndex &&
|
|
TranslateRoutine == NatTranslateIcmp &&
|
|
MappingCount))) {
|
|
return FORWARD;
|
|
}
|
|
|
|
//
|
|
// Now decide whether the packet should be automatically translated.
|
|
// We classify the packet as local-send, local-receive, or transit.
|
|
// The action taken depends on which of the three cases is applicable:
|
|
//
|
|
// Locally-sent packets are not translated, with a few exceptions
|
|
// (see 'NatpSendPacket').
|
|
//
|
|
// Locally-received packets are translated if they match the inbound-path
|
|
// of a previously established mapping, or if they match a configured
|
|
// static port-mapping, or a static address-mapping with inbound sessions
|
|
// enabled.
|
|
//
|
|
// Transit packets are translated if their outgoing interface
|
|
// is a boundary interface; transit packets whose incoming interface
|
|
// is a boundary interface are dropped.
|
|
//
|
|
|
|
if (ReceiveIndex != LOCAL_IF_INDEX) {
|
|
|
|
//
|
|
// The packet is either a locally-destined packet or a transit packet.
|
|
//
|
|
|
|
if (*SendIndex == LOCAL_IF_INDEX) {
|
|
|
|
//
|
|
// The packet is locally destined.
|
|
// We assume this will happen mostly when packets are received
|
|
// from the public network, and since we expect more incoming
|
|
// packets than outgoing ones, this is our fast path.
|
|
//
|
|
|
|
act =
|
|
NatpReceivePacket(
|
|
ReceiveIndex,
|
|
&Context,
|
|
TranslateRoutine,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// The packet is a transit packet.
|
|
// This will happen when packets are sent to the public network
|
|
// from a private client. Since we expect fewer outgoing packets
|
|
// than incoming ones, this will be hit less often.
|
|
//
|
|
// Unfortunately, it requires a route-lookup.
|
|
// To mitigate this unpleasantness, we use a cache of routes.
|
|
// In cases where there are a few high-volume connections,
|
|
// this will be a big win since we won't have to go through
|
|
// IP's routing table lock to retrieve the outgoing interface.
|
|
//
|
|
|
|
*SendIndex =
|
|
NatpRoutePacket(
|
|
Context.DestinationAddress,
|
|
&Context,
|
|
&status
|
|
);
|
|
|
|
//
|
|
// If we can't route the packet, neither can IP; drop it early.
|
|
//
|
|
|
|
if (*SendIndex == INVALID_IF_INDEX) {
|
|
TRACE(XLATE, ("NatTranslatePacket: dropping unroutable packet\n"));
|
|
if (status != STATUS_PENDING) {
|
|
NatSendRoutingFailureNotification(
|
|
Context.DestinationAddress,
|
|
Context.SourceAddress
|
|
);
|
|
}
|
|
#if NAT_WMI
|
|
NatLogDroppedPacket(&Context);
|
|
#endif
|
|
return DROP;
|
|
}
|
|
|
|
act =
|
|
NatpForwardPacket(
|
|
ReceiveIndex,
|
|
*SendIndex,
|
|
&Context,
|
|
TranslateRoutine,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// The packet is an outgoing locally-generated packet.
|
|
//
|
|
|
|
if (*SendIndex != INVALID_IF_INDEX) {
|
|
act =
|
|
NatpSendPacket(
|
|
*SendIndex,
|
|
&Context,
|
|
TranslateRoutine,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
|
|
return act;
|
|
}
|
|
|
|
//
|
|
// 'SendIndex' has been invalidated.
|
|
// Use a route-lookup to get the sending adapter index.
|
|
//
|
|
|
|
*SendIndex = NatpRoutePacket(Context.DestinationAddress, NULL, &status);
|
|
|
|
//
|
|
// Drop unroutable packets early.
|
|
//
|
|
|
|
if (*SendIndex == INVALID_IF_INDEX) {
|
|
TRACE(XLATE, ("NatTranslatePacket: dropping unroutable packet\n"));
|
|
if (status != STATUS_PENDING) {
|
|
NatSendRoutingFailureNotification(
|
|
Context.DestinationAddress,
|
|
Context.SourceAddress
|
|
);
|
|
}
|
|
|
|
#if NAT_WMI
|
|
NatLogDroppedPacket(&Context);
|
|
#endif
|
|
|
|
return DROP;
|
|
}
|
|
|
|
act =
|
|
NatpSendPacket(
|
|
*SendIndex,
|
|
&Context,
|
|
TranslateRoutine,
|
|
InReceiveBuffer,
|
|
OutReceiveBuffer
|
|
);
|
|
|
|
#if NAT_WMI
|
|
if (DROP == act) {
|
|
NatLogDroppedPacket(&Context);
|
|
}
|
|
#endif
|
|
|
|
return act;
|
|
|
|
} // NatTranslatePacket
|
|
|
|
|
|
//
|
|
// Now include the code for the translation routines;
|
|
// See XLATE.H for more details.
|
|
//
|
|
|
|
#define XLATE_CODE
|
|
|
|
#define XLATE_FORWARD
|
|
#include "xlate.h"
|
|
#undef XLATE_FORWARD
|
|
|
|
#define XLATE_REVERSE
|
|
#include "xlate.h"
|
|
#undef XLATE_REVERSE
|
|
|