/*++ 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