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.
2074 lines
61 KiB
2074 lines
61 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xlate.h
|
|
|
|
Abstract:
|
|
|
|
This file contains the code for the translation-routines used for
|
|
mappings.
|
|
|
|
The forward routines have the exact same logic as the reverse routines.
|
|
However, for reasons of efficiency the two are separate routines,
|
|
to avoid the cost of indexing on 'NAT_PATH' for every packet
|
|
processed.
|
|
|
|
To avoid duplicating the code, then, this header file consolidates the code
|
|
in one location. This file is included twice in XLATE.C, and before each
|
|
inclusion, either 'XLATE_FORWARD' or 'XLATE_REVERSE' is defined.
|
|
|
|
This causes the compiler to generate the code for separate functions,
|
|
as desired, while avoiding the unpleasantness of code-duplication.
|
|
|
|
The translation routines are as follows:
|
|
|
|
NatTranslate?Tcp - no editor for either direction
|
|
NatTranslate?Udp - no editor for either direction
|
|
NatTranslate?TcpEdit - an editor for at least one direction
|
|
NatTranslate?UdpEdit - an editor for the given direction
|
|
NatTranslate?TcpResize - a resizing editor for at least one direction
|
|
|
|
Each routine is invoked from 'NatTranslatePacket' at dispatch level
|
|
with no locks held and with a reference acquired for the mapping.
|
|
|
|
Author:
|
|
|
|
Abolade Gbadegesin (t-abolag) July-30-1997
|
|
|
|
Revision History:
|
|
|
|
Abolade Gbadegesin (aboladeg) July-15-1997
|
|
|
|
Reworked to deal with a global splay-tree of mappings, rather than
|
|
a per-interface splay-tree.
|
|
|
|
--*/
|
|
|
|
|
|
#ifndef XLATE_CODE // Just provide declarations.
|
|
|
|
//
|
|
// Structure: NAT_CACHED_ROUTE
|
|
//
|
|
// This structure holds information for a cached route.
|
|
//
|
|
|
|
typedef struct _NAT_CACHED_ROUTE {
|
|
ULONG DestinationAddress;
|
|
ULONG Index;
|
|
} NAT_CACHED_ROUTE, *PNAT_CACHED_ROUTE;
|
|
|
|
|
|
//
|
|
// Structure: NAT_XLATE_CONTEXT
|
|
//
|
|
// This structure holds context-information for a packet as it is passed thru
|
|
// the translation code. This context is passed to the translation routines,
|
|
// and handed to 'NatEditorEditSession' when it is invoked by an editor to
|
|
// make changes to the packet.
|
|
//
|
|
// Included are the original IP header, the checksum-delta to be updated if
|
|
// an editor makes any changes to the packet, and the TCP sequence number delta
|
|
// to be set if an editor resizes a TCP segment.
|
|
//
|
|
|
|
typedef struct _NAT_XLATE_CONTEXT {
|
|
IPRcvBuf* RecvBuffer;
|
|
PIP_HEADER Header;
|
|
PUCHAR DestinationType;
|
|
ULONG SourceAddress;
|
|
ULONG DestinationAddress;
|
|
IPRcvBuf* ProtocolRecvBuffer;
|
|
PUCHAR ProtocolHeader;
|
|
ULONG ProtocolDataOffset;
|
|
ULONG Flags;
|
|
PULONG ChecksumDelta;
|
|
LONG TcpSeqNumDelta;
|
|
BOOLEAN ChecksumOffloaded;
|
|
BOOLEAN IpSecTransformed;
|
|
} NAT_XLATE_CONTEXT, *PNAT_XLATE_CONTEXT;
|
|
|
|
|
|
//
|
|
// Definitions of flags for the field NAT_XLATE_CONTEXT.Flags
|
|
//
|
|
|
|
#define NAT_XLATE_FLAG_EDITED 0x00000001
|
|
#define NAT_XLATE_EDITED(h) ((h)->Flags & NAT_XLATE_FLAG_EDITED)
|
|
#define NAT_XLATE_FLAG_LOOPBACK 0x00000002
|
|
#define NAT_XLATE_LOOPBACK(h) ((h)->Flags & NAT_XLATE_FLAG_LOOPBACK)
|
|
#if NAT_WMI
|
|
#define NAT_XLATE_FLAG_LOGGED 0x00000004
|
|
#define NAT_XLATE_LOGGED(h) ((h)->Flags & NAT_XLATE_FLAG_LOGGED)
|
|
#endif
|
|
|
|
//
|
|
// Inline routine to initialize a translation context
|
|
// given appropriate arguments.
|
|
//
|
|
|
|
#define \
|
|
NAT_BUILD_XLATE_CONTEXT( \
|
|
_Context, \
|
|
_Header, \
|
|
_DestinationType, \
|
|
_RecvBuffer, \
|
|
_SourceAddress, \
|
|
_DestinationAddress \
|
|
) \
|
|
(_Context)->Header = (PIP_HEADER)_Header; \
|
|
(_Context)->DestinationType = _DestinationType; \
|
|
(_Context)->RecvBuffer = (_RecvBuffer); \
|
|
(_Context)->SourceAddress = _SourceAddress; \
|
|
(_Context)->DestinationAddress = _DestinationAddress; \
|
|
(_Context)->ChecksumOffloaded = \
|
|
((_RecvBuffer)->ipr_flags & IPR_FLAG_CHECKSUM_OFFLOAD) \
|
|
== IPR_FLAG_CHECKSUM_OFFLOAD; \
|
|
(_Context)->IpSecTransformed = \
|
|
((_RecvBuffer)->ipr_flags & IPR_FLAG_IPSEC_TRANSFORMED) \
|
|
== IPR_FLAG_IPSEC_TRANSFORMED; \
|
|
if ((_RecvBuffer)->ipr_size == (ULONG)IP_DATA_OFFSET(_Header)) { \
|
|
if ((_Context)->ProtocolRecvBuffer = (_RecvBuffer)->ipr_next) { \
|
|
(_Context)->ProtocolHeader = (_RecvBuffer)->ipr_next->ipr_buffer; \
|
|
} \
|
|
} \
|
|
else if (IP_DATA_OFFSET(_Header) < (_RecvBuffer)->ipr_size) { \
|
|
(_Context)->ProtocolRecvBuffer = (_RecvBuffer); \
|
|
(_Context)->ProtocolHeader = \
|
|
(_RecvBuffer)->ipr_buffer + IP_DATA_OFFSET(_Header); \
|
|
} else { \
|
|
(_Context)->ProtocolRecvBuffer = NULL; \
|
|
(_Context)->ProtocolHeader = NULL; \
|
|
} \
|
|
if ((_Context)->ProtocolHeader) { \
|
|
UINT ProtocolHeaderSize = 0; \
|
|
UINT HeaderSize = \
|
|
(_Context)->ProtocolRecvBuffer->ipr_size \
|
|
- (UINT) ((_Context)->ProtocolHeader \
|
|
- (_Context)->ProtocolRecvBuffer->ipr_buffer); \
|
|
switch ((_Context)->Header->Protocol) { \
|
|
case NAT_PROTOCOL_TCP: { \
|
|
ProtocolHeaderSize = sizeof(TCP_HEADER); \
|
|
break; \
|
|
} \
|
|
case NAT_PROTOCOL_UDP: { \
|
|
ProtocolHeaderSize = sizeof(UDP_HEADER); \
|
|
break; \
|
|
} \
|
|
case NAT_PROTOCOL_ICMP: { \
|
|
ProtocolHeaderSize = \
|
|
FIELD_OFFSET(ICMP_HEADER, EncapsulatedIpHeader); \
|
|
break; \
|
|
} \
|
|
case NAT_PROTOCOL_PPTP: { \
|
|
ProtocolHeaderSize = sizeof(GRE_HEADER); \
|
|
break; \
|
|
} \
|
|
} \
|
|
if (HeaderSize < ProtocolHeaderSize) { \
|
|
(_Context)->ProtocolRecvBuffer = NULL; \
|
|
(_Context)->ProtocolHeader = NULL; \
|
|
} \
|
|
}
|
|
|
|
|
|
//
|
|
// Checksum manipulation macros
|
|
//
|
|
|
|
//
|
|
// Fold carry-bits of a checksum into the low-order word
|
|
//
|
|
#define CHECKSUM_FOLD(xsum) \
|
|
(xsum) = (USHORT)(xsum) + ((xsum) >> 16); \
|
|
(xsum) += ((xsum) >> 16)
|
|
|
|
//
|
|
// Sum the words of a 32-bit value into a checksum
|
|
//
|
|
#define CHECKSUM_LONG(xsum,l) \
|
|
(xsum) += (USHORT)(l) + (USHORT)((l) >> 16)
|
|
|
|
//
|
|
// Transfer a checksum to or from the negated format sent on the network
|
|
//
|
|
#define CHECKSUM_XFER(dst,src) \
|
|
(dst) = (USHORT)~(src)
|
|
|
|
//
|
|
// Update the checksum field 'x' using standard variables 'Checksum' and
|
|
// 'ChecksumDelta'
|
|
//
|
|
#define CHECKSUM_UPDATE(x) \
|
|
CHECKSUM_XFER(Checksum, (x)); \
|
|
Checksum += ChecksumDelta; \
|
|
CHECKSUM_FOLD(Checksum); \
|
|
CHECKSUM_XFER((x), Checksum)
|
|
|
|
|
|
//
|
|
// Checksum computation routines (inlined)
|
|
//
|
|
|
|
__forceinline
|
|
VOID
|
|
NatComputeIpChecksum(
|
|
PIP_HEADER IpHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the IP Checksum for a packet, and places that checksum
|
|
into the packet header.
|
|
|
|
Arguments:
|
|
|
|
IpHeader - pointer to the IP header for which the checksum is to
|
|
be computed. The checksum field of this header will be modified.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG IpChecksum;
|
|
|
|
IpHeader->Checksum = 0;
|
|
IpChecksum =
|
|
tcpxsum(
|
|
0,
|
|
(PUCHAR)IpHeader,
|
|
IP_DATA_OFFSET(IpHeader)
|
|
);
|
|
|
|
CHECKSUM_FOLD(IpChecksum);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksum);
|
|
|
|
} // NatComputeIpChecksum
|
|
|
|
__forceinline
|
|
VOID
|
|
NatComputeTcpChecksum(
|
|
PIP_HEADER IpHeader,
|
|
PTCP_HEADER TcpHeader,
|
|
IPRcvBuf *TcpRcvBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the TCP checksum for a packet, and places that checksum
|
|
into the TCP header.
|
|
|
|
Arguments:
|
|
|
|
IpHeader - pointer to the IP header for the packet.
|
|
|
|
TcpHeader - pointer to the TCP for the packet. The checksum field
|
|
in this header will be modified.
|
|
|
|
TcpRcvBuffer - the IPRcvBuf containing the TCP header.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PreviousSize;
|
|
ULONG NeedSwap = 0;
|
|
ULONG TcpChecksum;
|
|
IPRcvBuf* Temp;
|
|
|
|
TcpChecksum = NTOHS(IpHeader->TotalLength);
|
|
TcpChecksum -= IP_DATA_OFFSET(IpHeader);
|
|
TcpChecksum = NTOHS(TcpChecksum);
|
|
CHECKSUM_LONG(TcpChecksum, IpHeader->SourceAddress);
|
|
CHECKSUM_LONG(TcpChecksum, IpHeader->DestinationAddress);
|
|
TcpChecksum += (NAT_PROTOCOL_TCP << 8);
|
|
|
|
TcpHeader->Checksum = 0;
|
|
TcpChecksum +=
|
|
tcpxsum(
|
|
0,
|
|
(PUCHAR)TcpHeader,
|
|
PreviousSize = TcpRcvBuffer->ipr_size -
|
|
(ULONG)((PUCHAR)TcpHeader - TcpRcvBuffer->ipr_buffer)
|
|
);
|
|
|
|
for (Temp = TcpRcvBuffer->ipr_next;
|
|
Temp;
|
|
Temp = Temp->ipr_next
|
|
) {
|
|
|
|
//
|
|
// Whenever an odd number of bytes is checksummed in the interior
|
|
// of the buffer-chain, swap the sum and go on so that the next byte
|
|
// will overlay the existing sum correctly.
|
|
//
|
|
// (The correctness of this swap is a property of ones-complement
|
|
// checksums.)
|
|
//
|
|
|
|
if (PreviousSize & 1) {
|
|
CHECKSUM_FOLD(TcpChecksum);
|
|
TcpChecksum = RtlUshortByteSwap(TcpChecksum);
|
|
NeedSwap ^= 1;
|
|
}
|
|
|
|
TcpChecksum +=
|
|
tcpxsum(
|
|
0,
|
|
Temp->ipr_buffer,
|
|
PreviousSize = Temp->ipr_size
|
|
);
|
|
}
|
|
|
|
CHECKSUM_FOLD(TcpChecksum);
|
|
|
|
if (NeedSwap) {
|
|
|
|
//
|
|
// If an odd number of swaps were performed, swap once more
|
|
// to undo the unmatched swap and obtain the final result.
|
|
//
|
|
|
|
TcpChecksum = RtlUshortByteSwap(TcpChecksum);
|
|
}
|
|
|
|
CHECKSUM_XFER(TcpHeader->Checksum, TcpChecksum);
|
|
|
|
} // NatComputeTcpChecksum
|
|
|
|
__forceinline
|
|
VOID
|
|
NatComputeUdpChecksum(
|
|
PIP_HEADER IpHeader,
|
|
PUDP_HEADER UdpHeader,
|
|
IPRcvBuf *UdpRcvBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the UDP checksum for a packet, and places that checksum
|
|
into the UDP header.
|
|
|
|
Arguments:
|
|
|
|
IpHeader - pointer to the IP header for the packet.
|
|
|
|
UdpHeader - pointer to the UDP for the packet. The checksum field
|
|
in this header will be modified.
|
|
|
|
UdpRcvBuffer - the IPRcvBuf containing the UDP header.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PreviousSize;
|
|
ULONG NeedSwap = 0;
|
|
ULONG UdpChecksum;
|
|
IPRcvBuf* Temp;
|
|
|
|
UdpChecksum = UdpHeader->Length;
|
|
CHECKSUM_LONG(UdpChecksum, IpHeader->SourceAddress);
|
|
CHECKSUM_LONG(UdpChecksum, IpHeader->DestinationAddress);
|
|
UdpChecksum += (NAT_PROTOCOL_UDP << 8);
|
|
|
|
UdpHeader->Checksum = 0;
|
|
UdpChecksum +=
|
|
tcpxsum(
|
|
0,
|
|
(PUCHAR)UdpHeader,
|
|
PreviousSize = UdpRcvBuffer->ipr_size -
|
|
(ULONG)((PUCHAR)UdpHeader - UdpRcvBuffer->ipr_buffer)
|
|
);
|
|
|
|
for (Temp = UdpRcvBuffer->ipr_next;
|
|
Temp;
|
|
Temp = Temp->ipr_next
|
|
) {
|
|
|
|
//
|
|
// Whenever an odd number of bytes is checksummed in the interior
|
|
// of the buffer-chain, swap the sum and go on so that the next byte
|
|
// will overlay the existing sum correctly.
|
|
//
|
|
// (The correctness of this swap is a property of ones-complement
|
|
// checksums.)
|
|
//
|
|
|
|
if (PreviousSize & 1) {
|
|
CHECKSUM_FOLD(UdpChecksum);
|
|
UdpChecksum = RtlUshortByteSwap(UdpChecksum);
|
|
NeedSwap ^= 1;
|
|
}
|
|
|
|
UdpChecksum +=
|
|
tcpxsum(
|
|
0,
|
|
Temp->ipr_buffer,
|
|
PreviousSize = Temp->ipr_size
|
|
);
|
|
}
|
|
|
|
CHECKSUM_FOLD(UdpChecksum);
|
|
|
|
if (NeedSwap) {
|
|
|
|
//
|
|
// If an odd number of swaps were performed, swap once more
|
|
// to undo the unmatched swap and obtain the final result.
|
|
//
|
|
|
|
UdpChecksum = RtlUshortByteSwap(UdpChecksum);
|
|
}
|
|
|
|
if (0xffff == UdpChecksum) {
|
|
|
|
//
|
|
// The negated form of this checksum would be 0. Since in
|
|
// a UDP packet 0 specifies no checksum by specification we
|
|
// use the non-negated form.
|
|
//
|
|
|
|
UdpHeader->Checksum = 0xffff;
|
|
} else {
|
|
CHECKSUM_XFER(UdpHeader->Checksum, UdpChecksum);
|
|
}
|
|
|
|
} // NatComputeUdpChecksum
|
|
|
|
|
|
//
|
|
// Forward declarations of structures defined elsewhere.
|
|
//
|
|
|
|
struct _NAT_INTERFACE;
|
|
#define PNAT_INTERFACE struct _NAT_INTERFACE*
|
|
|
|
struct _NAT_DYNAMIC_MAPPING;
|
|
#define PNAT_DYNAMIC_MAPPING struct _NAT_DYNAMIC_MAPPING*
|
|
|
|
//
|
|
// Functional signature macro
|
|
//
|
|
|
|
#define XLATE_ROUTINE(Name) \
|
|
FORWARD_ACTION \
|
|
Name( \
|
|
PNAT_DYNAMIC_MAPPING Mapping, \
|
|
PNAT_XLATE_CONTEXT Contextp, \
|
|
IPRcvBuf** InReceiveBuffer, \
|
|
IPRcvBuf** OutReceiveBuffer \
|
|
);
|
|
|
|
//
|
|
// Prototype: PNAT_TRANSLATE_ROUTINE
|
|
//
|
|
// This is the prototype for the routines which handle translation for
|
|
// different classes of sessions. Each mapping's 'TranslateRoutine' field
|
|
// is initialized with a pointer to such a routine.
|
|
//
|
|
// This allows us to take advantage of our foreknowledge about sessions.
|
|
// For instance, a TCP connection which has no editor registered will never
|
|
// have to take the editor-lock, and can skip the check for an editor.
|
|
// Similarly, a TCP connection whose editor never resizes a packet does not
|
|
// need to ever adjust sequence numbers.
|
|
//
|
|
|
|
typedef XLATE_ROUTINE((FASTCALL*PNAT_TRANSLATE_ROUTINE))
|
|
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardTcp)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseTcp)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardUdp)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseUdp)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardTcpEdit)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseTcpEdit)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardUdpEdit)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseUdpEdit)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardTcpResize)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseTcpResize)
|
|
|
|
//
|
|
// NatTranslate?Null is used for firewall-only mappings (i.e., mappings that
|
|
// don't perform any actual translation). These routines never have to modify
|
|
// any of the packet data; they only update the bookkeeping...
|
|
//
|
|
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardTcpNull)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseTcpNull)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateForwardUdpNull)
|
|
XLATE_ROUTINE(FASTCALL NatTranslateReverseUdpNull)
|
|
|
|
|
|
//
|
|
// Functional signature macro
|
|
//
|
|
|
|
#define XLATE_IP_ROUTINE(Name) \
|
|
FORWARD_ACTION \
|
|
Name( \
|
|
PNAT_INTERFACE Interfacep OPTIONAL, \
|
|
IP_NAT_DIRECTION Direction, \
|
|
PNAT_XLATE_CONTEXT Contextp, \
|
|
IPRcvBuf** InRecvBuffer, \
|
|
IPRcvBuf** OutRecvBuffer \
|
|
);
|
|
|
|
//
|
|
// Prototype: PNAT_IP_TRANSLATE_ROUTINE
|
|
//
|
|
// This is the prototype for the routines which handle translation for
|
|
// protocols other than TCP and UDP, i.e. for IP-layer protocols.
|
|
//
|
|
// All such routines are responsible for updating the IP header checksum,
|
|
// and updating 'InRecvBuffer' and 'OutRecvBuffer' in the event of any change
|
|
// to the packet being processed.
|
|
//
|
|
|
|
typedef XLATE_IP_ROUTINE((*PNAT_IP_TRANSLATE_ROUTINE))
|
|
|
|
//
|
|
// Prototype: Nat?TCPStateCheck
|
|
//
|
|
// These routines are used in FW mode to protect against various forms of
|
|
// constructed packets (e.g., SYN/FIN).
|
|
//
|
|
void
|
|
FASTCALL
|
|
NatAdjustMSSOption(
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
USHORT maxMSS
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatForwardTcpStateCheck(
|
|
PNAT_DYNAMIC_MAPPING pMapping,
|
|
PTCP_HEADER pTcpHeader
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatReverseTcpStateCheck(
|
|
PNAT_DYNAMIC_MAPPING pMapping,
|
|
PTCP_HEADER pTcpHeader
|
|
);
|
|
|
|
#undef PNAT_INTERFACE
|
|
#undef PNAT_DYNAMIC_MAPPING
|
|
|
|
PNAT_IP_TRANSLATE_ROUTINE TranslateRoutineTable[256];
|
|
|
|
|
|
//
|
|
// FUNCTION PROTOTYPES
|
|
//
|
|
|
|
VOID
|
|
NatInitializePacketManagement(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
NatShutdownPacketManagement(
|
|
VOID
|
|
);
|
|
|
|
FORWARD_ACTION
|
|
NatTranslatePacket(
|
|
IPRcvBuf** InReceiveBuffer,
|
|
ULONG ReceiveAdapterIndex,
|
|
PULONG SendAdapterIndex,
|
|
PUCHAR DestinationType,
|
|
PVOID Unused,
|
|
ULONG UnusedLength,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
);
|
|
|
|
|
|
#else // XLATE_CODE
|
|
|
|
//
|
|
// Produce code for the protocol-layer translation routines.
|
|
// Produce forward-routines if 'XLATE_FORWARD' is defined,
|
|
// and reverse routines otherwise.
|
|
//
|
|
|
|
#ifdef XLATE_FORWARD
|
|
#define XLATE_POSITIVE NatForwardPath
|
|
#define XLATE_NEGATIVE NatReversePath
|
|
#define NAT_TRANSLATE_TCP NatTranslateForwardTcp
|
|
#define NAT_TRANSLATE_UDP NatTranslateForwardUdp
|
|
#define NAT_TRANSLATE_TCP_EDIT NatTranslateForwardTcpEdit
|
|
#define NAT_TRANSLATE_UDP_EDIT NatTranslateForwardUdpEdit
|
|
#define NAT_TRANSLATE_TCP_RESIZE NatTranslateForwardTcpResize
|
|
#define NAT_TRANSLATE_TCP_NULL NatTranslateForwardTcpNull
|
|
#define NAT_TRANSLATE_UDP_NULL NatTranslateForwardUdpNull
|
|
#define NAT_TRANSLATE_SYN NAT_MAPPING_FLAG_FWD_SYN
|
|
#define NAT_TRANSLATE_FIN NAT_MAPPING_FLAG_FWD_FIN
|
|
#define NAT_TRANSLATE_TCP_STATE_CHECK NatForwardTcpStateCheck
|
|
#define DATA_HANDLER ForwardDataHandler
|
|
#define BYTE_COUNT BytesForward
|
|
#define PACKET_COUNT PacketsForward
|
|
#define REJECT_COUNT RejectsForward
|
|
#define NAT_TRANSLATE_HEADER() \
|
|
Contextp->Header->DestinationAddress = \
|
|
MAPPING_ADDRESS(Mapping->SourceKey[NatReversePath]); \
|
|
((PUSHORT)Contextp->ProtocolHeader)[1] = \
|
|
MAPPING_PORT(Mapping->SourceKey[NatReversePath]); \
|
|
Contextp->Header->SourceAddress = \
|
|
MAPPING_ADDRESS(Mapping->DestinationKey[NatReversePath]); \
|
|
((PUSHORT)Contextp->ProtocolHeader)[0] = \
|
|
MAPPING_PORT(Mapping->DestinationKey[NatReversePath])
|
|
#define NAT_DROP_IF_UNIDIRECTIONAL()
|
|
#else
|
|
#define XLATE_POSITIVE NatReversePath
|
|
#define XLATE_NEGATIVE NatForwardPath
|
|
#define NAT_TRANSLATE_TCP NatTranslateReverseTcp
|
|
#define NAT_TRANSLATE_UDP NatTranslateReverseUdp
|
|
#define NAT_TRANSLATE_TCP_EDIT NatTranslateReverseTcpEdit
|
|
#define NAT_TRANSLATE_UDP_EDIT NatTranslateReverseUdpEdit
|
|
#define NAT_TRANSLATE_TCP_RESIZE NatTranslateReverseTcpResize
|
|
#define NAT_TRANSLATE_TCP_NULL NatTranslateReverseTcpNull
|
|
#define NAT_TRANSLATE_UDP_NULL NatTranslateReverseUdpNull
|
|
#define NAT_TRANSLATE_SYN NAT_MAPPING_FLAG_REV_SYN
|
|
#define NAT_TRANSLATE_FIN NAT_MAPPING_FLAG_REV_FIN
|
|
#define NAT_TRANSLATE_TCP_STATE_CHECK NatReverseTcpStateCheck
|
|
#define DATA_HANDLER ReverseDataHandler
|
|
#define BYTE_COUNT BytesReverse
|
|
#define PACKET_COUNT PacketsReverse
|
|
#define REJECT_COUNT RejectsReverse
|
|
#define NAT_TRANSLATE_HEADER() \
|
|
Contextp->Header->DestinationAddress = \
|
|
MAPPING_ADDRESS(Mapping->SourceKey[NatForwardPath]); \
|
|
((PUSHORT)Contextp->ProtocolHeader)[1] = \
|
|
MAPPING_PORT(Mapping->SourceKey[NatForwardPath]); \
|
|
Contextp->Header->SourceAddress = \
|
|
MAPPING_ADDRESS(Mapping->DestinationKey[NatForwardPath]); \
|
|
((PUSHORT)Contextp->ProtocolHeader)[0] = \
|
|
MAPPING_PORT(Mapping->DestinationKey[NatForwardPath])
|
|
#define NAT_DROP_IF_UNIDIRECTIONAL() \
|
|
if (NAT_MAPPING_UNIDIRECTIONAL(Mapping)) { return DROP; }
|
|
#endif
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_TCP(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
ULONG IpChecksumDelta;
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
ULONG ProtocolChecksumDelta;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
PTCP_HEADER TcpHeader = (PTCP_HEADER)Contextp->ProtocolHeader;
|
|
|
|
//
|
|
// We know we will make changes to the buffer-chain,
|
|
// so move the head of the list to 'OutReceiveBuffer'.
|
|
//
|
|
// We also know that we will not be changing anything beyond
|
|
// the headers of the packet, so set IPR_FLAG_BUFFER_UNCHANGED
|
|
// in the receive buffer. This allows the stack to use a
|
|
// faster forwarding path.
|
|
//
|
|
|
|
*OutReceiveBuffer = *InReceiveBuffer; *InReceiveBuffer = NULL;
|
|
(*OutReceiveBuffer)->ipr_flags |= IPR_FLAG_BUFFER_UNCHANGED;
|
|
|
|
//
|
|
// Update the IP and protocol headers with the translated address/port
|
|
//
|
|
|
|
NAT_TRANSLATE_HEADER();
|
|
|
|
if (!Contextp->ChecksumOffloaded) {
|
|
|
|
//
|
|
// Now add the checksum-delta incurred by changes to the IP header
|
|
//
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
CHECKSUM_XFER(
|
|
ProtocolChecksumDelta,
|
|
((PTCP_HEADER)Contextp->ProtocolHeader)->Checksum
|
|
);
|
|
|
|
IpChecksumDelta += Mapping->IpChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
|
|
ProtocolChecksumDelta += Mapping->ProtocolChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(ProtocolChecksumDelta);
|
|
CHECKSUM_XFER(
|
|
((PTCP_HEADER)Contextp->ProtocolHeader)->Checksum,
|
|
ProtocolChecksumDelta
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Compute the IP and TCP checksums
|
|
//
|
|
|
|
NatComputeIpChecksum(IpHeader);
|
|
NatComputeTcpChecksum(
|
|
IpHeader,
|
|
(PTCP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
|
|
if (NAT_MAPPING_CLEAR_DF_BIT(Mapping) &&
|
|
(IpHeader->OffsetAndFlags & IP_DF_FLAG)) {
|
|
|
|
//
|
|
// Clear the DF bit from this packet and adjust the IP
|
|
// checksum accordingly.
|
|
//
|
|
|
|
IpHeader->OffsetAndFlags &= ~IP_DF_FLAG;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
IpChecksumDelta += ~IP_DF_FLAG;
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
}
|
|
|
|
//
|
|
// Check if need to adjust MSS options in TCP SYNs
|
|
//
|
|
if (TCP_FLAG(TcpHeader, SYN) && (Mapping->MaxMSS > 0)) {
|
|
NatAdjustMSSOption(Contextp, Mapping->MaxMSS);
|
|
}
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
|
|
//
|
|
// Perform state validation for inbound mappings.
|
|
//
|
|
|
|
if (NAT_MAPPING_INBOUND(Mapping)
|
|
&& DROP == NAT_TRANSLATE_TCP_STATE_CHECK(
|
|
Mapping,
|
|
TcpHeader
|
|
)) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
return DROP;
|
|
} else if (TCP_FLAG(TcpHeader, SYN)) {
|
|
|
|
//
|
|
// Record that we've seen a SYN in this direction
|
|
//
|
|
|
|
Mapping->Flags |= NAT_TRANSLATE_SYN;
|
|
}
|
|
|
|
//
|
|
// Now we need to update the connection state for the sender
|
|
// based on the flags in the packet:
|
|
//
|
|
// When a RST is seen, we close both ends of the connection.
|
|
// As each FIN is seen, we mark the mapping appropriately.
|
|
// When both FINs have been seen, we mark the mapping for deletion.
|
|
//
|
|
|
|
if (TCP_FLAG(((PTCP_HEADER)Contextp->ProtocolHeader), RST)) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
else
|
|
if (TCP_FLAG(((PTCP_HEADER)Contextp->ProtocolHeader), FIN)) {
|
|
Mapping->Flags |= NAT_TRANSLATE_FIN;
|
|
if (NAT_MAPPING_FIN(Mapping)) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the mapping's timestamp and statistics
|
|
//
|
|
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
TCP_DATA_OFFSET(((PTCP_HEADER)Contextp->ProtocolHeader))
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically), and indicate the change
|
|
// by invalidating 'DestinationType'
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
*Contextp->DestinationType = DEST_INVALID;
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_TCP
|
|
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_UDP(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
ULONG IpChecksumDelta;
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
ULONG ProtocolChecksumDelta;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
PUDP_HEADER UdpHeader = (PUDP_HEADER)Contextp->ProtocolHeader;
|
|
BOOLEAN UpdateXsum;
|
|
|
|
//
|
|
// We know we will make changes to the buffer-chain,
|
|
// so move the head of the list to 'OutReceiveBuffer'.
|
|
//
|
|
// We also know that we will not be changing anything beyond
|
|
// the headers of the packet, so set IPR_FLAG_BUFFER_UNCHANGED
|
|
// in the receive buffer. This allows the stack to use a
|
|
// faster forwarding path.
|
|
//
|
|
|
|
*OutReceiveBuffer = *InReceiveBuffer; *InReceiveBuffer = NULL;
|
|
(*OutReceiveBuffer)->ipr_flags |= IPR_FLAG_BUFFER_UNCHANGED;
|
|
|
|
|
|
//
|
|
// Unidirectional flows require that reverse packets be dropped;
|
|
// This is primarily to support H.323 proxy.
|
|
//
|
|
|
|
NAT_DROP_IF_UNIDIRECTIONAL();
|
|
|
|
//
|
|
// We have to handle the fact that the UDP checksum is optional;
|
|
// if the checksum in the header is zero, then no checksum was sent
|
|
// and we will make no changes to the field.
|
|
//
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
if (!UdpHeader->Checksum) {
|
|
UpdateXsum = FALSE;
|
|
}
|
|
else {
|
|
UpdateXsum = TRUE;
|
|
CHECKSUM_XFER(ProtocolChecksumDelta, UdpHeader->Checksum);
|
|
}
|
|
|
|
//
|
|
// Update the IP and protocol headers with the translated address/port
|
|
//
|
|
|
|
NAT_TRANSLATE_HEADER();
|
|
|
|
if (!Contextp->ChecksumOffloaded) {
|
|
|
|
//
|
|
// Update the checksums
|
|
//
|
|
|
|
IpChecksumDelta += Mapping->IpChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
|
|
if (UpdateXsum) {
|
|
ProtocolChecksumDelta += Mapping->ProtocolChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(ProtocolChecksumDelta);
|
|
CHECKSUM_XFER(UdpHeader->Checksum, ProtocolChecksumDelta);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Compute the IP and (optionally) UDP checksums
|
|
//
|
|
|
|
NatComputeIpChecksum(IpHeader);
|
|
|
|
if (UpdateXsum) {
|
|
NatComputeUdpChecksum(
|
|
IpHeader,
|
|
(PUDP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
}
|
|
|
|
if (NAT_MAPPING_CLEAR_DF_BIT(Mapping) &&
|
|
(IpHeader->OffsetAndFlags & IP_DF_FLAG)) {
|
|
|
|
//
|
|
// Clear the DF bit from this packet and adjust the IP
|
|
// checksum accordingly.
|
|
//
|
|
|
|
IpHeader->OffsetAndFlags &= ~IP_DF_FLAG;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
IpChecksumDelta += ~IP_DF_FLAG;
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
}
|
|
|
|
//
|
|
// Update the mapping's statistics and timestamp
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
sizeof(UDP_HEADER)
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically), and indicate the change
|
|
// by invalidating 'DestinationType'
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
*Contextp->DestinationType = DEST_INVALID;
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_UDP
|
|
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_TCP_EDIT(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
PNAT_EDITOR Editor;
|
|
PVOID EditorContext;
|
|
PNAT_INTERFACE Interfacep;
|
|
ULONG IpChecksumDelta;
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
BOOLEAN IsReset;
|
|
ULONG ProtocolChecksumDelta;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
NTSTATUS status;
|
|
PTCP_HEADER TcpHeader = (PTCP_HEADER)Contextp->ProtocolHeader;
|
|
|
|
//
|
|
// We know we will make changes to the buffer-chain,
|
|
// so move the head of the list to 'OutReceiveBuffer'.
|
|
//
|
|
|
|
*OutReceiveBuffer = *InReceiveBuffer; *InReceiveBuffer = NULL;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
CHECKSUM_XFER(ProtocolChecksumDelta, TcpHeader->Checksum);
|
|
|
|
IsReset = !!TCP_FLAG(TcpHeader, RST);
|
|
|
|
//
|
|
// Call the editor for this session, if there is one.
|
|
// Note that the mapping's cached pointers to the editor and interface
|
|
// are referenced within the appropriate lock before being used.
|
|
// See the synchronization rules governing 'Mapping->Editor*'
|
|
// and 'Mapping->Interface*' in 'MAPPING.H' for the logic behind
|
|
// the operations below.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&EditorLock);
|
|
if (!(Editor = Mapping->Editor) ||
|
|
!Editor->DATA_HANDLER ||
|
|
!NatReferenceEditor(Editor)) {
|
|
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
|
}
|
|
else {
|
|
EditorContext = Mapping->EditorContext;
|
|
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
|
|
|
//
|
|
// Set up context fields for the editor
|
|
//
|
|
|
|
Contextp->ProtocolDataOffset =
|
|
(ULONG)((PUCHAR)TcpHeader -
|
|
(PUCHAR)Contextp->ProtocolRecvBuffer->ipr_buffer) +
|
|
TCP_DATA_OFFSET(TcpHeader);
|
|
Contextp->ChecksumDelta = &ProtocolChecksumDelta;
|
|
|
|
//
|
|
// Invoke the editor's receive handler
|
|
// if this is not a TCP RST segment.
|
|
//
|
|
|
|
if (!IsReset) {
|
|
|
|
//
|
|
// The editor-helper functions require that
|
|
// 'Interfacep', 'Editor' and 'Mapping' be referenced
|
|
// but not locked.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
if (!(Interfacep = Mapping->Interfacep) ||
|
|
!NatReferenceInterface(Interfacep)
|
|
) {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
}
|
|
else {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
status =
|
|
Editor->DATA_HANDLER(
|
|
Interfacep,
|
|
(PVOID)Mapping,
|
|
(PVOID)Contextp,
|
|
Editor->Context,
|
|
EditorContext,
|
|
(PVOID)Contextp->ProtocolRecvBuffer,
|
|
Contextp->ProtocolDataOffset
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
NatDereferenceEditor(Editor);
|
|
if (!NT_SUCCESS(status)) {
|
|
InterlockedIncrement(&Mapping->REJECT_COUNT);
|
|
return DROP;
|
|
}
|
|
|
|
//
|
|
// Reset the fields formerly retrieved from the context,
|
|
// which may now point to memory that has been freed.
|
|
// (see 'NatHelperEditSession').
|
|
//
|
|
|
|
IpHeader = Contextp->Header;
|
|
TcpHeader = (PTCP_HEADER)Contextp->ProtocolHeader;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the IP and protocol headers with the translated address/port
|
|
//
|
|
|
|
NAT_TRANSLATE_HEADER();
|
|
|
|
//
|
|
// Now add the checksum-delta incurred by changes to the IP header
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
|
|
//
|
|
// Perform state validation for inbound mappings.
|
|
//
|
|
|
|
if (NAT_MAPPING_INBOUND(Mapping)
|
|
&& DROP == NAT_TRANSLATE_TCP_STATE_CHECK(
|
|
Mapping,
|
|
TcpHeader
|
|
)) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
return DROP;
|
|
} else if (TCP_FLAG(TcpHeader, SYN)) {
|
|
|
|
//
|
|
// Record that we've seen a SYN in this direction
|
|
//
|
|
|
|
Mapping->Flags |= NAT_TRANSLATE_SYN;
|
|
}
|
|
|
|
if (!Contextp->ChecksumOffloaded) {
|
|
|
|
IpChecksumDelta += Mapping->IpChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
|
|
if (!NAT_XLATE_EDITED(Contextp)) {
|
|
ProtocolChecksumDelta += Mapping->ProtocolChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(ProtocolChecksumDelta);
|
|
CHECKSUM_XFER(TcpHeader->Checksum, ProtocolChecksumDelta);
|
|
|
|
} else {
|
|
|
|
//
|
|
// NatEditorEditSession was called on the packet;
|
|
// Completely recompute the TCP checksum.
|
|
//
|
|
|
|
NatComputeTcpChecksum(
|
|
IpHeader,
|
|
(PTCP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Compute the IP and TCP checksums
|
|
//
|
|
|
|
NatComputeIpChecksum(IpHeader);
|
|
NatComputeTcpChecksum(
|
|
IpHeader,
|
|
(PTCP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
|
|
if (NAT_MAPPING_CLEAR_DF_BIT(Mapping) &&
|
|
(IpHeader->OffsetAndFlags & IP_DF_FLAG)) {
|
|
|
|
//
|
|
// Clear the DF bit from this packet and adjust the IP
|
|
// checksum accordingly.
|
|
//
|
|
|
|
IpHeader->OffsetAndFlags &= ~IP_DF_FLAG;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
IpChecksumDelta += ~IP_DF_FLAG;
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
}
|
|
|
|
//
|
|
// Check if need to adjust MSS options in TCP SYNs
|
|
//
|
|
if (TCP_FLAG(TcpHeader, SYN) && (Mapping->MaxMSS > 0)) {
|
|
NatAdjustMSSOption(Contextp, Mapping->MaxMSS);
|
|
}
|
|
|
|
//
|
|
// Now we need to update the connection state for the sender
|
|
// based on the flags in the packet:
|
|
//
|
|
// When a RST is seen, we close both ends of the connection.
|
|
// As each FIN is seen, we mark the mapping appropriately.
|
|
// When both FINs have been seen, we mark the mapping for deletion.
|
|
//
|
|
|
|
if (IsReset) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
else
|
|
if (TCP_FLAG(TcpHeader, FIN)) {
|
|
Mapping->Flags |= NAT_TRANSLATE_FIN;
|
|
if (NAT_MAPPING_FIN(Mapping)) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the mapping's statistics and timestamp
|
|
//
|
|
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
TCP_DATA_OFFSET(TcpHeader)
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically), and indicate the change
|
|
// by invalidating 'DestinationType'
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
*Contextp->DestinationType = DEST_INVALID;
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_TCP_EDIT
|
|
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_UDP_EDIT(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
PNAT_EDITOR Editor;
|
|
PVOID EditorContext;
|
|
PNAT_INTERFACE Interfacep;
|
|
ULONG IpChecksumDelta;
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
BOOLEAN IsReset;
|
|
ULONG ProtocolChecksumDelta;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
NTSTATUS status;
|
|
BOOLEAN UpdateXsum;
|
|
PUDP_HEADER UdpHeader = (PUDP_HEADER)Contextp->ProtocolHeader;
|
|
|
|
//
|
|
// We know we will make changes to the buffer-chain,
|
|
// so move the head of the list to 'OutReceiveBuffer'.
|
|
//
|
|
|
|
*OutReceiveBuffer = *InReceiveBuffer; *InReceiveBuffer = NULL;
|
|
|
|
//
|
|
// We have to handle the fact that the UDP checksum is optional;
|
|
// if the checksum in the header is zero, then no checksum was sent
|
|
// and we will make no changes to the field.
|
|
//
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
|
|
if (!UdpHeader->Checksum) {
|
|
UpdateXsum = FALSE;
|
|
}
|
|
else {
|
|
UpdateXsum = TRUE;
|
|
CHECKSUM_XFER(ProtocolChecksumDelta, UdpHeader->Checksum);
|
|
}
|
|
|
|
//
|
|
// Call the editor for this session, if there is one.
|
|
// Note that the mapping's cached pointers to the editor and interface
|
|
// are referenced within the appropriate lock before being used.
|
|
// See the synchronization rules governing 'Mapping->Editor*'
|
|
// and 'Mapping->Interface*' in 'MAPPING.H' for the logic behind
|
|
// the operations below.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&EditorLock);
|
|
if (!(Editor = Mapping->Editor) ||
|
|
!Editor->DATA_HANDLER ||
|
|
!NatReferenceEditor(Editor)) {
|
|
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
|
}
|
|
else {
|
|
EditorContext = Mapping->EditorContext;
|
|
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
|
|
|
//
|
|
// Set up the context fields to be used for editing
|
|
//
|
|
|
|
Contextp->ProtocolDataOffset =
|
|
(ULONG)((PUCHAR)UdpHeader -
|
|
Contextp->ProtocolRecvBuffer->ipr_buffer) +
|
|
sizeof(UDP_HEADER);
|
|
Contextp->ChecksumDelta = UpdateXsum ? &ProtocolChecksumDelta : NULL;
|
|
|
|
//
|
|
// Invoke the editor's receive handler
|
|
//
|
|
// The editor-helper functions require that
|
|
// 'Interfacep', 'Editor' and 'Mapping' be referenced
|
|
// but not locked.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
if (!(Interfacep = Mapping->Interfacep) ||
|
|
!NatReferenceInterface(Interfacep)
|
|
) {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
}
|
|
else {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
status =
|
|
Editor->DATA_HANDLER(
|
|
Interfacep,
|
|
(PVOID)Mapping,
|
|
(PVOID)Contextp,
|
|
Editor->Context,
|
|
EditorContext,
|
|
(PVOID)Contextp->ProtocolRecvBuffer,
|
|
Contextp->ProtocolDataOffset
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
if (!NT_SUCCESS(status)) {
|
|
NatDereferenceEditor(Editor);
|
|
InterlockedIncrement(&Mapping->REJECT_COUNT);
|
|
return DROP;
|
|
}
|
|
|
|
//
|
|
// Reset the fields formerly retrieved from the context,
|
|
// which may now point to memory that has been freed.
|
|
// (see 'NatHelperEditSession').
|
|
//
|
|
|
|
IpHeader = Contextp->Header;
|
|
UdpHeader = (PUDP_HEADER)Contextp->ProtocolHeader;
|
|
}
|
|
|
|
NatDereferenceEditor(Editor);
|
|
}
|
|
|
|
//
|
|
// Update the IP and protocol headers with the translated address/port
|
|
//
|
|
|
|
NAT_TRANSLATE_HEADER();
|
|
|
|
if (!Contextp->ChecksumOffloaded) {
|
|
|
|
//
|
|
// Update the checksums
|
|
//
|
|
|
|
IpChecksumDelta += Mapping->IpChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
|
|
if (UpdateXsum) {
|
|
|
|
if (!NAT_XLATE_EDITED(Contextp)) {
|
|
ProtocolChecksumDelta +=
|
|
Mapping->ProtocolChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(ProtocolChecksumDelta);
|
|
CHECKSUM_XFER(UdpHeader->Checksum, ProtocolChecksumDelta);
|
|
}
|
|
else {
|
|
//
|
|
// NatEditorEditSession was called on the packet;
|
|
// Completely recompute the UDP checksum.
|
|
//
|
|
|
|
NatComputeUdpChecksum(
|
|
IpHeader,
|
|
(PUDP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Compute the IP and (optionally) UDP checksums
|
|
//
|
|
|
|
NatComputeIpChecksum(IpHeader);
|
|
|
|
if (UpdateXsum) {
|
|
NatComputeUdpChecksum(
|
|
IpHeader,
|
|
(PUDP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
if (NAT_MAPPING_CLEAR_DF_BIT(Mapping) &&
|
|
(IpHeader->OffsetAndFlags & IP_DF_FLAG)) {
|
|
|
|
//
|
|
// Clear the DF bit from this packet and adjust the IP
|
|
// checksum accordingly.
|
|
//
|
|
|
|
IpHeader->OffsetAndFlags &= ~IP_DF_FLAG;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
IpChecksumDelta += ~IP_DF_FLAG;
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
}
|
|
|
|
//
|
|
// Update the mapping's statistics and timestamp
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
sizeof(UDP_HEADER)
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically), and indicate the change
|
|
// by invalidating 'DestinationType'
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
*Contextp->DestinationType = DEST_INVALID;
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_UDP_EDIT
|
|
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_TCP_RESIZE(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
PNAT_EDITOR Editor;
|
|
PVOID EditorContext;
|
|
PNAT_INTERFACE Interfacep;
|
|
ULONG IpChecksumDelta;
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
BOOLEAN IsResend;
|
|
BOOLEAN IsReset;
|
|
BOOLEAN IsSyn;
|
|
ULONG ProtocolChecksumDelta;
|
|
PRTL_SPLAY_LINKS SLink;
|
|
NTSTATUS status;
|
|
PTCP_HEADER TcpHeader = (PTCP_HEADER)Contextp->ProtocolHeader;
|
|
|
|
//
|
|
// We know we will make changes to the buffer-chain,
|
|
// so move the head of the list to 'OutReceiveBuffer'.
|
|
//
|
|
|
|
*OutReceiveBuffer = *InReceiveBuffer; *InReceiveBuffer = NULL;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
CHECKSUM_XFER(ProtocolChecksumDelta, TcpHeader->Checksum);
|
|
|
|
IsResend = FALSE;
|
|
IsReset = !!TCP_FLAG(TcpHeader, RST);
|
|
IsSyn = !!TCP_FLAG(TcpHeader, SYN);
|
|
|
|
//
|
|
// Set up context fields for the editor
|
|
//
|
|
|
|
Contextp->ProtocolDataOffset =
|
|
(ULONG)((PUCHAR)TcpHeader -
|
|
Contextp->ProtocolRecvBuffer->ipr_buffer) +
|
|
TCP_DATA_OFFSET(TcpHeader);
|
|
Contextp->ChecksumDelta = &ProtocolChecksumDelta;
|
|
Contextp->TcpSeqNumDelta = 0;
|
|
|
|
//
|
|
// Call the editor for this session, if there is one.
|
|
// Note that the mapping's cached pointers to the editor and interface
|
|
// are referenced within the appropriate lock before being used.
|
|
// See the synchronization rules governing 'Mapping->Editor*'
|
|
// and 'Mapping->Interface*' in 'MAPPING.H' for the logic behind
|
|
// the operations below.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&EditorLock);
|
|
if (!(Editor = Mapping->Editor) ||
|
|
!NatReferenceEditor(Editor)) {
|
|
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
|
}
|
|
else {
|
|
EditorContext = Mapping->EditorContext;
|
|
KeReleaseSpinLockFromDpcLevel(&EditorLock);
|
|
|
|
//
|
|
// On a SYN packet, just record the sequence number
|
|
// On a RST packet, the sequence number is ignored
|
|
// On other packets, make sure the packet is in sequence.
|
|
// If the packet is a retransmission, we try to apply
|
|
// the sequence number delta. If the packet is too old
|
|
// (i.e. we don't have the delta which would apply to it)
|
|
// then we drop the packet.
|
|
//
|
|
// N.B. With resized TCP sessions the checksum-delta may change,
|
|
// and so we only touch it under cover of the mapping's lock.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
|
|
//
|
|
// Perform state validation for inbound mappings.
|
|
//
|
|
|
|
if (NAT_MAPPING_INBOUND(Mapping)
|
|
&& DROP == NAT_TRANSLATE_TCP_STATE_CHECK(
|
|
Mapping,
|
|
TcpHeader
|
|
)) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
return DROP;
|
|
} else if (IsSyn) {
|
|
|
|
//
|
|
// Record that we've seen a SYN in this direction
|
|
//
|
|
|
|
Mapping->Flags |= NAT_TRANSLATE_SYN;
|
|
}
|
|
|
|
|
|
if (IsSyn || !(Mapping->Flags & NAT_TRANSLATE_SYN)) {
|
|
|
|
//
|
|
// First packet for this direction;
|
|
// just record the sequence number as if it were expected.
|
|
//
|
|
|
|
Mapping->u.TcpSeqNum.Base[XLATE_POSITIVE] =
|
|
TcpHeader->SequenceNumber;
|
|
Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE] =
|
|
RtlUlongByteSwap(
|
|
RtlUlongByteSwap(TcpHeader->SequenceNumber) + 1
|
|
);
|
|
}
|
|
else
|
|
if (TcpHeader->SequenceNumber ==
|
|
Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE] || IsReset
|
|
) {
|
|
|
|
//
|
|
// The packet is in sequence, which is the most common case,
|
|
// or the segment has the reset bit set.
|
|
// No action is required.
|
|
//
|
|
|
|
}
|
|
else {
|
|
|
|
ULONG Sn =
|
|
RtlUlongByteSwap(TcpHeader->SequenceNumber);
|
|
ULONG SnE =
|
|
RtlUlongByteSwap(Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE]);
|
|
ULONG Base =
|
|
RtlUlongByteSwap(Mapping->u.TcpSeqNum.Base[XLATE_POSITIVE]);
|
|
|
|
//
|
|
// The packet is out of sequence.
|
|
// See if the current delta applies to it,
|
|
// i.e. if it is a retransmission of a packet
|
|
// which appears *after* the sequence number at which
|
|
// the current delta was computed.
|
|
// N.B. When comparing sequence numbers, account for wraparound
|
|
// by adding half the sequence-number space.
|
|
//
|
|
|
|
if ((Sn < SnE || (Sn + MAXLONG) < (SnE + MAXLONG)) &&
|
|
(Sn >= Base || (Sn + MAXLONG) > (Base + MAXLONG))
|
|
) {
|
|
|
|
//
|
|
// The packet is a retransmission, and our delta applies.
|
|
//
|
|
|
|
IsResend = TRUE;
|
|
|
|
TRACE(
|
|
XLATE, ("NatTranslate: retransmission %u, expected %u\n",
|
|
Sn, SnE
|
|
));
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The packet is an old retransmission or it is out-of-order.
|
|
// We have no choice but to drop it.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
NatDereferenceEditor(Editor);
|
|
|
|
TRACE(
|
|
XLATE, ("NatTranslate: out-of-order %u, expected %u\n",
|
|
Sn, SnE
|
|
));
|
|
|
|
InterlockedIncrement(&Mapping->REJECT_COUNT);
|
|
return DROP;
|
|
}
|
|
}
|
|
|
|
if (!IsResend) {
|
|
|
|
//
|
|
// Compute the next expected sequence number
|
|
//
|
|
|
|
Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE] =
|
|
RtlUlongByteSwap(Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE]) +
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
TCP_DATA_OFFSET(TcpHeader);
|
|
|
|
Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE] =
|
|
RtlUlongByteSwap(Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE]);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
//
|
|
// Invoke the editor's receive handler
|
|
// if this is not a TCP SYN or RST segment.
|
|
//
|
|
|
|
if (!IsSyn && !IsReset) {
|
|
if (Editor->DATA_HANDLER) {
|
|
|
|
//
|
|
// The editor-helper functions require that
|
|
// 'Interfacep', 'Editor' and 'Mapping' be referenced
|
|
// but not locked.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|
if (!(Interfacep = Mapping->Interfacep) ||
|
|
!NatReferenceInterface(Interfacep)
|
|
) {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
}
|
|
else {
|
|
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
|
status =
|
|
Editor->DATA_HANDLER(
|
|
Interfacep,
|
|
(PVOID)Mapping,
|
|
(PVOID)Contextp,
|
|
Editor->Context,
|
|
EditorContext,
|
|
(PVOID)Contextp->ProtocolRecvBuffer,
|
|
Contextp->ProtocolDataOffset
|
|
);
|
|
NatDereferenceInterface(Interfacep);
|
|
if (!NT_SUCCESS(status)) {
|
|
NatDereferenceEditor(Editor);
|
|
InterlockedIncrement(&Mapping->REJECT_COUNT);
|
|
return DROP;
|
|
}
|
|
|
|
//
|
|
// Reset the fields formerly retrieved from the context,
|
|
// which may now point to memory that has been freed.
|
|
// (see 'NatHelperEditSession').
|
|
//
|
|
|
|
IpHeader = Contextp->Header;
|
|
TcpHeader = (PTCP_HEADER)Contextp->ProtocolHeader;
|
|
|
|
//
|
|
// We can't allow editors to edit retransmitted packets,
|
|
// since they wouldn't be able to guarantee that they make
|
|
// exactly the same changes to the retransmission as they
|
|
// made to the original.
|
|
//
|
|
|
|
if (IsResend && NAT_XLATE_EDITED(Contextp)) {
|
|
NatDereferenceEditor(Editor);
|
|
InterlockedIncrement(&Mapping->REJECT_COUNT);
|
|
return DROP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NatDereferenceEditor(Editor);
|
|
}
|
|
|
|
if (!IsReset) {
|
|
|
|
//
|
|
// The editor is done doing the things that it does,
|
|
// including changing the packet size, possibly,
|
|
// so now adjust the TCP header's sequence number if necessary.
|
|
// Again the exclusion is RST segments.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
|
|
//
|
|
// If necessary, apply a delta value to the sequence number
|
|
// in the header of the TCP segment.
|
|
//
|
|
|
|
if (Mapping->u.TcpSeqNum.Delta[XLATE_POSITIVE]) {
|
|
|
|
//
|
|
// Update the checksum (see RFC1624):
|
|
//
|
|
// Take out ones-complement sum of old sequence number
|
|
//
|
|
|
|
CHECKSUM_LONG(ProtocolChecksumDelta, ~TcpHeader->SequenceNumber);
|
|
|
|
//
|
|
// Store new sequence number
|
|
//
|
|
|
|
TcpHeader->SequenceNumber =
|
|
RtlUlongByteSwap(TcpHeader->SequenceNumber) +
|
|
Mapping->u.TcpSeqNum.Delta[XLATE_POSITIVE];
|
|
TcpHeader->SequenceNumber =
|
|
RtlUlongByteSwap(TcpHeader->SequenceNumber);
|
|
|
|
//
|
|
// Add in new sequence number
|
|
//
|
|
|
|
CHECKSUM_LONG(ProtocolChecksumDelta, TcpHeader->SequenceNumber);
|
|
}
|
|
|
|
//
|
|
// The editor may have just modified the packet-size.
|
|
// Pick up any sequence-number delta it set for us,
|
|
// and update the basis of the new sequence-number delta
|
|
// in the sequence-number space
|
|
//
|
|
|
|
if (Contextp->TcpSeqNumDelta) {
|
|
Mapping->u.TcpSeqNum.Base[XLATE_POSITIVE] =
|
|
Mapping->u.TcpSeqNum.Expected[XLATE_POSITIVE];
|
|
Mapping->u.TcpSeqNum.Delta[XLATE_POSITIVE] +=
|
|
Contextp->TcpSeqNumDelta;
|
|
}
|
|
|
|
//
|
|
// Adjust the ACK numbers
|
|
//
|
|
|
|
if (!TCP_FLAG(TcpHeader, ACK)) {
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
}
|
|
else {
|
|
|
|
if (!Mapping->u.TcpSeqNum.Delta[XLATE_NEGATIVE]) {
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Update the checksum (see RFC 1624)
|
|
//
|
|
// Take old ACK number out of checksum
|
|
//
|
|
|
|
CHECKSUM_LONG(ProtocolChecksumDelta, ~TcpHeader->AckNumber);
|
|
|
|
//
|
|
// Store new ACK number (note we *subtract* the delta)
|
|
//
|
|
|
|
TcpHeader->AckNumber =
|
|
RtlUlongByteSwap(TcpHeader->AckNumber) -
|
|
Mapping->u.TcpSeqNum.Delta[XLATE_NEGATIVE];
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
TcpHeader->AckNumber = RtlUlongByteSwap(TcpHeader->AckNumber);
|
|
|
|
//
|
|
// Add new ACK number to checksum
|
|
//
|
|
|
|
CHECKSUM_LONG(ProtocolChecksumDelta, TcpHeader->AckNumber);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the IP and protocol headers with the translated address/port
|
|
//
|
|
|
|
NAT_TRANSLATE_HEADER();
|
|
|
|
//
|
|
// Now add the checksum-delta incurred by changes to the IP header
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
|
|
if (!Contextp->ChecksumOffloaded) {
|
|
|
|
IpChecksumDelta += Mapping->IpChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
|
|
if (!NAT_XLATE_EDITED(Contextp)) {
|
|
ProtocolChecksumDelta += Mapping->ProtocolChecksumDelta[XLATE_POSITIVE];
|
|
CHECKSUM_FOLD(ProtocolChecksumDelta);
|
|
CHECKSUM_XFER(TcpHeader->Checksum, ProtocolChecksumDelta);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// NatEditorEditSession was called on the packet;
|
|
// Completely recompute the TCP checksum.
|
|
//
|
|
|
|
NatComputeTcpChecksum(
|
|
IpHeader,
|
|
(PTCP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Compute the IP and TCP checksums
|
|
//
|
|
|
|
NatComputeIpChecksum(IpHeader);
|
|
NatComputeTcpChecksum(
|
|
IpHeader,
|
|
(PTCP_HEADER)Contextp->ProtocolHeader,
|
|
Contextp->ProtocolRecvBuffer
|
|
);
|
|
}
|
|
|
|
if (NAT_MAPPING_CLEAR_DF_BIT(Mapping) &&
|
|
(IpHeader->OffsetAndFlags & IP_DF_FLAG)) {
|
|
|
|
//
|
|
// Clear the DF bit from this packet and adjust the IP
|
|
// checksum accordingly.
|
|
//
|
|
|
|
IpHeader->OffsetAndFlags &= ~IP_DF_FLAG;
|
|
|
|
CHECKSUM_XFER(IpChecksumDelta, IpHeader->Checksum);
|
|
IpChecksumDelta += ~IP_DF_FLAG;
|
|
CHECKSUM_FOLD(IpChecksumDelta);
|
|
CHECKSUM_XFER(IpHeader->Checksum, IpChecksumDelta);
|
|
}
|
|
|
|
//
|
|
// Check if need to adjust MSS options in TCP SYNs
|
|
//
|
|
if (TCP_FLAG(TcpHeader, SYN) && (Mapping->MaxMSS > 0)) {
|
|
NatAdjustMSSOption(Contextp, Mapping->MaxMSS);
|
|
}
|
|
|
|
//
|
|
// Now we need to update the connection state for the sender
|
|
// based on the flags in the packet:
|
|
//
|
|
// When a RST is seen, we close both ends of the connection.
|
|
// As each FIN is seen, we mark the mapping appropriately.
|
|
// When both FINs have been seen, we mark the mapping for deletion.
|
|
//
|
|
|
|
if (IsReset) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
else
|
|
if (TCP_FLAG(TcpHeader, FIN)) {
|
|
Mapping->Flags |= NAT_TRANSLATE_FIN;
|
|
if (NAT_MAPPING_FIN(Mapping)) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the mapping's statistics and timestamp
|
|
//
|
|
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
TCP_DATA_OFFSET(TcpHeader)
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically), and indicate the change
|
|
// by invalidating 'DestinationType'
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
*Contextp->DestinationType = DEST_INVALID;
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_TCP_RESIZE
|
|
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_TCP_NULL(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
|
|
//
|
|
// Perform state validation for inbound mappings.
|
|
//
|
|
|
|
if (NAT_MAPPING_INBOUND(Mapping)
|
|
&& DROP == NAT_TRANSLATE_TCP_STATE_CHECK(
|
|
Mapping,
|
|
((PTCP_HEADER)Contextp->ProtocolHeader)
|
|
)) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
return DROP;
|
|
} else if (TCP_FLAG(((PTCP_HEADER)Contextp->ProtocolHeader), SYN)) {
|
|
|
|
//
|
|
// Record that we've seen a SYN in this direction
|
|
//
|
|
|
|
Mapping->Flags |= NAT_TRANSLATE_SYN;
|
|
}
|
|
|
|
//
|
|
// Now we need to update the connection state for the sender
|
|
// based on the flags in the packet:
|
|
//
|
|
// When a RST is seen, we close both ends of the connection.
|
|
// As each FIN is seen, we mark the mapping appropriately.
|
|
// When both FINs have been seen, we mark the mapping for deletion.
|
|
//
|
|
|
|
|
|
if (TCP_FLAG(((PTCP_HEADER)Contextp->ProtocolHeader), RST)) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
else
|
|
if (TCP_FLAG(((PTCP_HEADER)Contextp->ProtocolHeader), FIN)) {
|
|
Mapping->Flags |= NAT_TRANSLATE_FIN;
|
|
if (NAT_MAPPING_FIN(Mapping)) {
|
|
NatExpireMapping(Mapping);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the mapping's timestamp and statistics
|
|
//
|
|
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
TCP_DATA_OFFSET(((PTCP_HEADER)Contextp->ProtocolHeader))
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically).
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_TCP_NULL
|
|
|
|
|
|
FORWARD_ACTION
|
|
FASTCALL
|
|
NAT_TRANSLATE_UDP_NULL(
|
|
PNAT_DYNAMIC_MAPPING Mapping,
|
|
PNAT_XLATE_CONTEXT Contextp,
|
|
IPRcvBuf** InReceiveBuffer,
|
|
IPRcvBuf** OutReceiveBuffer
|
|
)
|
|
{
|
|
PIP_HEADER IpHeader = Contextp->Header;
|
|
PUDP_HEADER UdpHeader = (PUDP_HEADER)Contextp->ProtocolHeader;
|
|
|
|
//
|
|
// Unidirectional flows require that reverse packets be dropped;
|
|
// This is primarily to support H.323 proxy.
|
|
//
|
|
|
|
NAT_DROP_IF_UNIDIRECTIONAL();
|
|
|
|
//
|
|
// Update the mapping's statistics and timestamp
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|
KeQueryTickCount((PLARGE_INTEGER)&Mapping->LastAccessTime);
|
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|
|
|
InterlockedExchangeAdd(
|
|
&Mapping->BYTE_COUNT,
|
|
NTOHS(IpHeader->TotalLength) -
|
|
IP_DATA_OFFSET(IpHeader) -
|
|
sizeof(UDP_HEADER)
|
|
);
|
|
InterlockedIncrement(&Mapping->PACKET_COUNT);
|
|
|
|
//
|
|
// Resplay the mapping (periodically).
|
|
//
|
|
|
|
NatTryToResplayMapping(Mapping, XLATE_POSITIVE);
|
|
return FORWARD;
|
|
|
|
} // NAT_TRANSLATE_UDP_NULL
|
|
|
|
|
|
#undef XLATE_FORWARD
|
|
#undef XLATE_REVERSE
|
|
#undef XLATE_POSITIVE
|
|
#undef XLATE_NEGATIVE
|
|
#undef NAT_TRANSLATE_TCP
|
|
#undef NAT_TRANSLATE_UDP
|
|
#undef NAT_TRANSLATE_TCP_EDIT
|
|
#undef NAT_TRANSLATE_UDP_EDIT
|
|
#undef NAT_TRANSLATE_TCP_RESIZE
|
|
#undef NAT_TRANSLATE_TCP_NULL
|
|
#undef NAT_TRANSLATE_UDP_NULL
|
|
#undef NAT_TRANSLATE_SYN
|
|
#undef NAT_TRANSLATE_FIN
|
|
#undef NAT_TRANSLATE_TCP_STATE_CHECK
|
|
#undef DATA_HANDLER
|
|
#undef BYTE_COUNT
|
|
#undef PACKET_COUNT
|
|
#undef REJECT_COUNT
|
|
#undef NAT_TRANSLATE_HEADER
|
|
#undef NAT_DROP_IF_UNIDIRECTIONAL
|
|
|
|
#endif // XLATE_CODE
|