|
|
/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
NsConn.h Abstract:
Declarations for IpSec NAT shim connection entry management
Author:
Jonathan Burstein (jonburs) 10-July-2001 Environment:
Kernel mode
Revision History:
--*/
#pragma once
//
// Structure: _NS_CONNECTION_ENTRY
//
// This structure holds information about a specific active session.
// Each instance is held on the global connection list as well as
// on the global connection trees for inbound and outbound access.
//
// Each connection entry contains 5 pieces of identifying information:
// 1) the address key (local and remote IP addresses)
// 2) the protocol for the connection (TCP or UDP)
// 3) the IpSec context
// 4) the inbound (original) port key
// 5) the outbound (translated) port key
//
// Each time a packet is processed for a connection, the 'l64AccessOrExpiryTime'
// is set to the number of ticks since system-start (KeQueryTickCount).
// This value is used by our timer routine to eliminate expired connections.
//
// For TCP connections, 'l64AccessOrExpiryTime' will no longer be updated once
// FINs are seen in both direction. This is necessary for the timer routine
// to correctly evaluate whether or not the connection has left the time_wait
// state, and thus to prevent premature port reuse.
//
// Synchronization rules:
//
// We use a reference count to ensure the existence of a connection entry,
// and a spin-lock to ensure its consistency.
//
// The fields of a connection entry are only consistent while the spinlock is
// held (with the exception of fields such as 'ul64AddressKey' which are
// read-only and fields such as 'ulReferenceCount' that are interlocked-access
// only.)
//
// The spinlock can only be acquired if
// (a) the reference-count has been incremented, or
// (b) the connection list lock is already held.
//
typedef struct _NS_CONNECTION_ENTRY { LIST_ENTRY Link; RTL_SPLAY_LINKS SLink[NsMaximumDirection]; KSPIN_LOCK Lock; ULONG ulReferenceCount; // interlocked-access only
ULONG ulFlags;
ULONG64 ul64AddressKey; // read-only
ULONG ulPortKey[NsMaximumDirection]; // read-only
PVOID pvIpSecContext; // read-only
UCHAR ucProtocol; // read-only
LONG64 l64AccessOrExpiryTime; ULONG ulAccessCount[NsMaximumDirection]; // interlocked-access only
ULONG ulProtocolChecksumDelta[NsMaximumDirection]; PNS_PACKET_ROUTINE PacketRoutine[NsMaximumDirection]; // read-only
} NS_CONNECTION_ENTRY, *PNS_CONNECTION_ENTRY;
//
// Set after a connection entry has been deleted; when the last
// reference is released the entry will be freed
//
#define NS_CONNECTION_FLAG_DELETED 0x80000000
#define NS_CONNECTION_DELETED(c) \
((c)->ulFlags & NS_CONNECTION_FLAG_DELETED)
//
// Set when a connection entry is expired
//
#define NS_CONNECTION_FLAG_EXPIRED 0x00000001
#define NS_CONNECTION_EXPIRED(c) \
((c)->ulFlags & NS_CONNECTION_FLAG_EXPIRED)
//
// Set when inbound / outbound FIN for a TCP session is seen
//
#define NS_CONNECTION_FLAG_OB_FIN 0x00000002
#define NS_CONNECTION_FLAG_IB_FIN 0x00000004
#define NS_CONNECTION_FIN(c) \
(((c)->ulFlags & NS_CONNECTION_FLAG_OB_FIN) \ && ((c)->ulFlags & NS_CONNECTION_FLAG_IB_FIN))
//
// Connection entry key manipulation macros
//
#define MAKE_ADDRESS_KEY(Key, ulLocalAddress, ulRemoteAddress) \
((Key) = ((ULONG64)(ulLocalAddress) << 32) | (ulRemoteAddress))
#define CONNECTION_LOCAL_ADDRESS(ul64AddressKey) \
((ULONG)(((ul64AddressKey) >> 32) & 0xFFFFFFFF))
#define CONNECTION_REMOTE_ADDRESS(ul64AddressKey) \
((ULONG)((ul64AddressKey)))
#define MAKE_PORT_KEY(Key, usLocalPort, usRemotePort) \
((Key) = ((ULONG)(usLocalPort & 0xFFFF) << 16) | (usRemotePort & 0xFFFF))
#define CONNECTION_LOCAL_PORT(ulPortKey) \
((USHORT)(((ulPortKey) >> 16) & 0xFFFF))
#define CONNECTION_REMOTE_PORT(ulPortKey) \
((USHORT)(ulPortKey))
//
// Resplay threshold; the entry is resplayed every time its access-count
// passes this value.
//
#define NS_CONNECTION_RESPLAY_THRESHOLD 5
//
// Defines the depth of the lookaside list for allocating connection entries
//
#define NS_CONNECTION_LOOKASIDE_DEPTH 20
//
// Connection entry allocation macros
//
#define ALLOCATE_CONNECTION_BLOCK() \
ExAllocateFromNPagedLookasideList(&NsConnectionLookasideList)
#define FREE_CONNECTION_BLOCK(Block) \
ExFreeToNPagedLookasideList(&NsConnectionLookasideList,(Block))
//
// Port range boundaries
//
#define NS_SOURCE_PORT_BASE 6000
#define NS_SOURCE_PORT_END 65534
//
// GLOBAL VARIABLE DECLARATIONS
//
extern CACHE_ENTRY NsConnectionCache[CACHE_SIZE]; extern ULONG NsConnectionCount; extern LIST_ENTRY NsConnectionList; extern KSPIN_LOCK NsConnectionLock; extern NPAGED_LOOKASIDE_LIST NsConnectionLookasideList; extern PNS_CONNECTION_ENTRY NsConnectionTree[NsMaximumDirection]; extern USHORT NsNextSourcePort;
//
// Function Prototypes
//
NTSTATUS NsAllocateSourcePort( ULONG64 ul64AddressKey, ULONG ulPortKey, UCHAR ucProtocol, BOOLEAN fPortConflicts, PNS_CONNECTION_ENTRY *ppOutboundInsertionPoint, PULONG pulTranslatedPortKey );
VOID NsCleanupConnectionEntry( PNS_CONNECTION_ENTRY pEntry );
NTSTATUS NsCreateConnectionEntry( ULONG64 ul64AddressKey, ULONG ulInboundPortKey, ULONG ulOutboundPortKey, UCHAR ucProtocol, PVOID pvIpSecContext, PNS_CONNECTION_ENTRY pInboundInsertionPoint, PNS_CONNECTION_ENTRY pOutboundInsertionPoint, PNS_CONNECTION_ENTRY *ppNewEntry );
NTSTATUS NsDeleteConnectionEntry( PNS_CONNECTION_ENTRY pEntry );
__forceinline VOID NsDereferenceConnectionEntry( PNS_CONNECTION_ENTRY pEntry ) { if (0 == InterlockedDecrement(&pEntry->ulReferenceCount)) { NsCleanupConnectionEntry(pEntry); } }
NTSTATUS NsInitializeConnectionManagement( VOID );
PNS_CONNECTION_ENTRY NsLookupInboundConnectionEntry( ULONG64 ul64AddressKey, ULONG ulPortKey, UCHAR ucProtocol, PVOID pvIpSecContext, BOOLEAN *pfPortConflicts OPTIONAL, PNS_CONNECTION_ENTRY *ppInsertionPoint OPTIONAL );
PNS_CONNECTION_ENTRY NsLookupOutboundConnectionEntry( ULONG64 ul64AddressKey, ULONG ulPortKey, UCHAR ucProtocol, PNS_CONNECTION_ENTRY *ppInsertionPoint OPTIONAL );
__forceinline BOOLEAN NsReferenceConnectionEntry( PNS_CONNECTION_ENTRY pEntry ) { if (NS_CONNECTION_DELETED(pEntry)) { return FALSE; } else { InterlockedIncrement(&pEntry->ulReferenceCount); return TRUE; } }
__forceinline VOID NsResplayConnectionEntry( PNS_CONNECTION_ENTRY pEntry, IPSEC_NATSHIM_DIRECTION Direction ) { PRTL_SPLAY_LINKS SLink;
KeAcquireSpinLockAtDpcLevel(&NsConnectionLock);
if (!NS_CONNECTION_DELETED(pEntry)) { SLink = RtlSplay(&pEntry->SLink[Direction]); NsConnectionTree[Direction] = CONTAINING_RECORD(SLink, NS_CONNECTION_ENTRY, SLink[Direction]); } KeReleaseSpinLockFromDpcLevel(&NsConnectionLock); }
VOID NsShutdownConnectionManagement( VOID );
__forceinline VOID NsTryToResplayConnectionEntry( PNS_CONNECTION_ENTRY pEntry, IPSEC_NATSHIM_DIRECTION Direction ) { if (0 == InterlockedDecrement(&pEntry->ulAccessCount[Direction])) { NsResplayConnectionEntry(pEntry, Direction); InterlockedExchangeAdd( &pEntry->ulAccessCount[Direction], NS_CONNECTION_RESPLAY_THRESHOLD ); } }
|