/*++ Copyright (c) 1997-2001 Microsoft Corporation Module Name: NsConn.c Abstract: IpSec NAT shim connection entry management Author: Jonathan Burstein (jonburs) 11-July-2001 Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Global Variables // CACHE_ENTRY NsConnectionCache[CACHE_SIZE]; ULONG NsConnectionCount; LIST_ENTRY NsConnectionList; KSPIN_LOCK NsConnectionLock; NPAGED_LOOKASIDE_LIST NsConnectionLookasideList; PNS_CONNECTION_ENTRY NsConnectionTree[NsMaximumDirection]; USHORT NsNextSourcePort; // // Function Prototypes // PNS_CONNECTION_ENTRY NspInsertInboundConnectionEntry( PNS_CONNECTION_ENTRY pParent, PNS_CONNECTION_ENTRY pEntry ); PNS_CONNECTION_ENTRY NspInsertOutboundConnectionEntry( PNS_CONNECTION_ENTRY pParent, PNS_CONNECTION_ENTRY pEntry ); NTSTATUS NsAllocateSourcePort( ULONG64 ul64AddressKey, ULONG ulPortKey, UCHAR ucProtocol, BOOLEAN fPortConflicts, PNS_CONNECTION_ENTRY *ppOutboundInsertionPoint, PULONG pulTranslatedPortKey ) /*++ Routine Description: Called to allocate a source port for a connection entry. If the original port does not conflict with any existing connection entry it will be used. Arguments: ul64AddressKey - the addressing information for the connection ulPortKey - the original port information for the connection ucProtocol - the protocol for the connection fPortConflicts - if TRUE, indicates that the caller knows that the original port information conflicts w/ an existing connection. If FALSE the caller does not know whether or not a conflict definately exists. ppOutboundInsertionPoint - receives the insertion point for the outbound path pulTranslatedPortKey - on success, receives the allocated port information. Return Value: NTSTATUS. Environment: Invoked with NsConnectionLock held by the caller. --*/ { NTSTATUS Status = STATUS_UNSUCCESSFUL; ULONG ulOutboundPortKey; USHORT usLocalPort; USHORT usStopPort; ASSERT(NULL != ppOutboundInsertionPoint); ASSERT(NULL != pulTranslatedPortKey); TRACE( PORT_ALLOC, ("NsAllocateSourcePort: %d: %d.%d.%d.%d/%d -> %d.%d.%d.%d/%d\n", ucProtocol, ADDRESS_BYTES(CONNECTION_REMOTE_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_REMOTE_PORT(ulPortKey)), ADDRESS_BYTES(CONNECTION_LOCAL_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_LOCAL_PORT(ulPortKey)) )); usLocalPort = CONNECTION_LOCAL_PORT(ulPortKey); if (FALSE == fPortConflicts) { // // The caller indicates that the remote port does not // conflict on the inbound path, so we'll first attempt // to use the original port. // ulOutboundPortKey = ulPortKey; usStopPort = (NS_SOURCE_PORT_END == NsNextSourcePort ? NS_SOURCE_PORT_BASE : NsNextSourcePort + 1); } else { // // The caller indicates that the remote port conflicts // on the inbound path, so we'll assume that it also // conflicts on the outbound path and start by trying // out a new port. // usStopPort = NsNextSourcePort--; MAKE_PORT_KEY( ulOutboundPortKey, usLocalPort, usStopPort ); if (NsNextSourcePort < NS_SOURCE_PORT_BASE) { NsNextSourcePort = NS_SOURCE_PORT_END; } } do { // // Check to see if our current candidate conflicts // with any connection entries on the outbound path. // if (NULL == NsLookupOutboundConnectionEntry( ul64AddressKey, ulOutboundPortKey, ucProtocol, ppOutboundInsertionPoint )) { // // No conflict was found -- break out of the loop and // return this info to the caller. // TRACE(PORT_ALLOC, ("NsAllocateSourcePort: Assigning %d\n", NTOHS(CONNECTION_REMOTE_PORT(ulOutboundPortKey)))); *pulTranslatedPortKey = ulOutboundPortKey; Status = STATUS_SUCCESS; break; } // // This candidate conflicted; move on to the next. // MAKE_PORT_KEY( ulOutboundPortKey, usLocalPort, NsNextSourcePort-- ); if (NsNextSourcePort < NS_SOURCE_PORT_BASE) { NsNextSourcePort = NS_SOURCE_PORT_END; } } while (usStopPort != CONNECTION_REMOTE_PORT(ulOutboundPortKey)); TRACE(PORT_ALLOC, ("NsAllocateSourcePort: No port available\n")); return Status; } // NsAllocateSourcePort VOID NsCleanupConnectionEntry( PNS_CONNECTION_ENTRY pEntry ) /*++ Routine Description: Called to perform final cleanup for a connection entry. Arguments: pEntry - the connection entry to be deleted. Return Value: none. Environment: Invoked with the last reference to the connection entry released. --*/ { TRACE(CONN_LIFETIME, ("NsCleanupConnectionEntry\n")); ASSERT(NULL != pEntry); FREE_CONNECTION_BLOCK(pEntry); } // NsCleanupConnectionEntry NTSTATUS NsCreateConnectionEntry( ULONG64 ul64AddressKey, ULONG ulInboundPortKey, ULONG ulOutboundPortKey, UCHAR ucProtocol, PVOID pvIpSecContext, PNS_CONNECTION_ENTRY pInboundInsertionPoint, PNS_CONNECTION_ENTRY pOutboundInsertionPoint, PNS_CONNECTION_ENTRY *ppNewEntry ) /*++ Routine Description: Called to create a connection entry. On success, the connection entry will have been referenced twice -- the initial reference for the entry (which is released in NsDeleteConnectionEntry) and a reference for the caller. Thus, the caller must call NsDereferenceConnectionEntry on the new entry. Arguments: ul64AddressKey - the addressing information for this entry ulInboundPortKey - the inbound (original) ports for this entry ulOutboundPortKey - the outbound (translated) ports for this entry ucProtocol - the protocol for this entry pvIpSecContext - the IpSec context for this entry p*InsertionPoint - the inbound and outbound insertion points (normally obtained through NsAllocateSourcePort). ppEntry - receives a pointer to the newley-created connection entry. The caller must call NsDereferenceConnectionEntry on this pointer. Return Value: NTSTATUS - indicates success/failure. Environment: Invoked with 'NsConnectionLock' held by the caller. --*/ { PNS_CONNECTION_ENTRY pEntry; TRACE( CONN_LIFETIME, ("NsCreateConnectionEntry: %d: %d.%d.%d.%d/%d/%d -> %d.%d.%d.%d/%d : %d\n", ucProtocol, ADDRESS_BYTES(CONNECTION_REMOTE_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_REMOTE_PORT(ulInboundPortKey)), NTOHS(CONNECTION_REMOTE_PORT(ulOutboundPortKey)), ADDRESS_BYTES(CONNECTION_LOCAL_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_LOCAL_PORT(ulInboundPortKey)), pvIpSecContext )); ASSERT(NULL != ppNewEntry); ASSERT(NS_PROTOCOL_TCP == ucProtocol || NS_PROTOCOL_UDP == ucProtocol); pEntry = ALLOCATE_CONNECTION_BLOCK(); if (NULL == pEntry) { ERROR(("NsCreateConnectionEntry: Unable to allocate entry\n")); return STATUS_NO_MEMORY; } RtlZeroMemory(pEntry, sizeof(*pEntry)); KeInitializeSpinLock(&pEntry->Lock); pEntry->ulReferenceCount = 1; pEntry->ul64AddressKey = ul64AddressKey; pEntry->ulPortKey[NsInboundDirection] = ulInboundPortKey; pEntry->ulPortKey[NsOutboundDirection] = ulOutboundPortKey; pEntry->ucProtocol = ucProtocol; pEntry->pvIpSecContext = pvIpSecContext; pEntry->ulAccessCount[NsInboundDirection] = NS_CONNECTION_RESPLAY_THRESHOLD; pEntry->ulAccessCount[NsOutboundDirection] = NS_CONNECTION_RESPLAY_THRESHOLD; InitializeListHead(&pEntry->Link); RtlInitializeSplayLinks(&pEntry->SLink[NsInboundDirection]); RtlInitializeSplayLinks(&pEntry->SLink[NsOutboundDirection]); // // Incremeent the reference count on the connection; the caller // is required to do a dereference. // pEntry->ulReferenceCount += 1; // // Setup checksum deltas (if necessary) and per-packet routines // if (ulInboundPortKey != ulOutboundPortKey) { // // This connection entry is translating the remote port, so // precompute the checksum deltas (see RFC 1624). // pEntry->ulProtocolChecksumDelta[NsInboundDirection] = (USHORT)~CONNECTION_REMOTE_PORT(ulInboundPortKey) + (USHORT)CONNECTION_REMOTE_PORT(ulOutboundPortKey); pEntry->ulProtocolChecksumDelta[NsOutboundDirection] = (USHORT)~CONNECTION_REMOTE_PORT(ulOutboundPortKey) + (USHORT)CONNECTION_REMOTE_PORT(ulInboundPortKey); if (NS_PROTOCOL_TCP == ucProtocol) { pEntry->PacketRoutine[NsInboundDirection] = NsInboundTcpTranslatePortPacketRoutine; pEntry->PacketRoutine[NsOutboundDirection] = NsOutboundTcpTranslatePortPacketRoutine; } else { pEntry->PacketRoutine[NsInboundDirection] = NsInboundUdpTranslatePortPacketRoutine; pEntry->PacketRoutine[NsOutboundDirection] = NsOutboundUdpTranslatePortPacketRoutine; } } else if (NS_PROTOCOL_TCP == ucProtocol) { pEntry->PacketRoutine[NsInboundDirection] = NsInboundTcpPacketRoutine; pEntry->PacketRoutine[NsOutboundDirection] = NsOutboundTcpPacketRoutine; } else { pEntry->PacketRoutine[NsInboundDirection] = NsInboundUdpPacketRoutine; pEntry->PacketRoutine[NsOutboundDirection] = NsOutboundUdpPacketRoutine; } NsConnectionTree[NsInboundDirection] = NspInsertInboundConnectionEntry(pInboundInsertionPoint, pEntry); NsConnectionTree[NsOutboundDirection] = NspInsertOutboundConnectionEntry(pOutboundInsertionPoint, pEntry); InsertTailList(&NsConnectionList, &pEntry->Link); InterlockedIncrement(&NsConnectionCount); *ppNewEntry = pEntry; return STATUS_SUCCESS; } // NsCreateConnectionEntry NTSTATUS NsDeleteConnectionEntry( PNS_CONNECTION_ENTRY pEntry ) /*++ Routine Description: Called to delete a connection entry. The initial reference to the entry is released, so that cleanup occurs whenever the last reference is released. Arguments: pEntry - the connection entry to be deleted. Return Value: NTSTATUS - indicates success/failure. Environment: Invoked with 'NsConnectionLock' held by the caller. --*/ { PRTL_SPLAY_LINKS SLink; TRACE(CONN_LIFETIME, ("NsDeleteConnectionEntry\n")); ASSERT(NULL != pEntry); if (NS_CONNECTION_DELETED(pEntry)) { return STATUS_PENDING; } // // Mark the entry as deleted so attempts to reference it // will fail from now on. // pEntry->ulFlags |= NS_CONNECTION_FLAG_DELETED; // // Take the entry off the list and splay-trees // InterlockedDecrement(&NsConnectionCount); RemoveEntryList(&pEntry->Link); SLink = RtlDelete(&pEntry->SLink[NsInboundDirection]); NsConnectionTree[NsInboundDirection] = (SLink ? CONTAINING_RECORD(SLink,NS_CONNECTION_ENTRY,SLink[NsInboundDirection]) : NULL); SLink = RtlDelete(&pEntry->SLink[NsOutboundDirection]); NsConnectionTree[NsOutboundDirection] = (SLink ? CONTAINING_RECORD(SLink,NS_CONNECTION_ENTRY,SLink[NsOutboundDirection]) : NULL); // // Clear the entry from the connection cache // ClearCache( NsConnectionCache, (ULONG)pEntry->ul64AddressKey ); if (0 != InterlockedDecrement(&pEntry->ulReferenceCount)) { // // The entry is in use, defer final cleanup // return STATUS_PENDING; } // // Go ahead with final cleanup // NsCleanupConnectionEntry(pEntry); return STATUS_SUCCESS; } // NsDeleteConnectionEntry NTSTATUS NsInitializeConnectionManagement( VOID ) /*++ Routine Description: This routine is invoked to initialize the connection management module. Arguments: none. Return Value: NTSTATUS. --*/ { CALLTRACE(("NsInitializeConnectionManagement\n")); InitializeCache(NsConnectionCache); NsConnectionCount = 0; InitializeListHead(&NsConnectionList); KeInitializeSpinLock(&NsConnectionLock); ExInitializeNPagedLookasideList( &NsConnectionLookasideList, NULL, NULL, 0, sizeof(NS_CONNECTION_ENTRY), NS_TAG_CONNECTION, NS_CONNECTION_LOOKASIDE_DEPTH ); NsConnectionTree[NsInboundDirection] = NULL; NsConnectionTree[NsOutboundDirection] = NULL; NsNextSourcePort = NS_SOURCE_PORT_END; return STATUS_SUCCESS; } // NsInitializeConnectionManagement PNS_CONNECTION_ENTRY NsLookupInboundConnectionEntry( ULONG64 ul64AddressKey, ULONG ulPortKey, UCHAR ucProtocol, PVOID pvIpSecContext, BOOLEAN *pfPortConflicts OPTIONAL, PNS_CONNECTION_ENTRY *ppInsertionPoint OPTIONAL ) /*++ Routine Description: Called to lookup an inbound connection entry. Arguments: ul64AddressKey - the addressing information for the connection ulPortKey - the port information for the connection ucProtocol - the protocol for the connection pvIpSecContext - the IpSec context for the connection pfPortConflicts - on failure, receives a boolean the indicates why the lookup failed: TRUE if the lookup failed because there is an identical connection entry w/ different IpSec context, FALSE otherwise. ppInsertionPoint - receives the insertion point if not found Return Value: PNS_CONNECTION_ENTRY - a pointer to the connection entry, if found, or NULL otherwise. Environment: Invoked with NsConnectionLock held by the caller. --*/ { PNS_CONNECTION_ENTRY pRoot; PNS_CONNECTION_ENTRY pEntry; PNS_CONNECTION_ENTRY pParent = NULL; PRTL_SPLAY_LINKS SLink; TRACE( CONN_LOOKUP, ("NsLookupInboundConnectionEntry: %d: %d.%d.%d.%d/%d -> %d.%d.%d.%d/%d : %d\n", ucProtocol, ADDRESS_BYTES(CONNECTION_REMOTE_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_REMOTE_PORT(ulPortKey)), ADDRESS_BYTES(CONNECTION_LOCAL_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_LOCAL_PORT(ulPortKey)), pvIpSecContext )); // // First look in the cache // pEntry = (PNS_CONNECTION_ENTRY) ProbeCache( NsConnectionCache, (ULONG)ul64AddressKey ); if (NULL != pEntry && pEntry->ul64AddressKey == ul64AddressKey && pEntry->ucProtocol == ucProtocol && pEntry->ulPortKey[NsInboundDirection] == ulPortKey && pEntry->pvIpSecContext == pvIpSecContext) { TRACE(CONN_LOOKUP, ("NsLookupInboundConnectionEntry: Cache Hit\n")); return pEntry; } if (pfPortConflicts) { *pfPortConflicts = FALSE; } // // Search the full tree. Keys are checked in the // following order: // // 1. Address Key // 2. Protocol // 3. Inbound port key // 4. IpSec context // pRoot = NsConnectionTree[NsInboundDirection]; for (SLink = (pRoot ? &pRoot->SLink[NsInboundDirection] : NULL ); SLink; ) { pEntry = CONTAINING_RECORD(SLink, NS_CONNECTION_ENTRY, SLink[NsInboundDirection]); if (ul64AddressKey < pEntry->ul64AddressKey) { pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (ul64AddressKey > pEntry->ul64AddressKey) { pParent = pEntry; SLink = RtlRightChild(SLink); continue; } else if (ucProtocol < pEntry->ucProtocol) { pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (ucProtocol > pEntry->ucProtocol) { pParent = pEntry; SLink = RtlRightChild(SLink); continue; } else if (ulPortKey < pEntry->ulPortKey[NsInboundDirection]) { pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (ulPortKey > pEntry->ulPortKey[NsInboundDirection]) { pParent = pEntry; SLink = RtlRightChild(SLink); continue; } else if (pvIpSecContext < pEntry->pvIpSecContext) { // // Everything matched w/ the exception of the IpSec // context -- we have a port conflict. // if (pfPortConflicts) { *pfPortConflicts = TRUE; } pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (pvIpSecContext > pEntry->pvIpSecContext) { // // Everything matched w/ the exception of the IpSec // context -- we have a port conflict. // if (pfPortConflicts) { *pfPortConflicts = TRUE; } pParent = pEntry; SLink = RtlRightChild(SLink); continue; } // // We found the entry -- update cache and return // UpdateCache( NsConnectionCache, (ULONG)ul64AddressKey, (PVOID)pEntry ); return pEntry; } // // Not found -- provide insertion point if requested // if (ppInsertionPoint) { *ppInsertionPoint = pParent; } return NULL; } // NsLookupInboundConnectionEntry PNS_CONNECTION_ENTRY NsLookupOutboundConnectionEntry( ULONG64 ul64AddressKey, ULONG ulPortKey, UCHAR ucProtocol, PNS_CONNECTION_ENTRY *ppInsertionPoint OPTIONAL ) /*++ Routine Description: Called to lookup an outbound connection entry. Arguments: ul64AddressKey - the addressing information for the connection ulPortKey - the port information for the connection ucProtocol - the protocol for the connection ppInsertionPoint - receives the insertion point if not found Return Value: PNS_CONNECTION_ENTRY - a pointer to the connection entry, if found, or NULL otherwise. Environment: Invoked with NsConnectionLock held by the caller. --*/ { PNS_CONNECTION_ENTRY pRoot; PNS_CONNECTION_ENTRY pEntry; PNS_CONNECTION_ENTRY pParent = NULL; PRTL_SPLAY_LINKS SLink; TRACE( CONN_LOOKUP, ("NsLookupOutboundConnectionEntry: %d: %d.%d.%d.%d/%d -> %d.%d.%d.%d/%d\n", ucProtocol, ADDRESS_BYTES(CONNECTION_LOCAL_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_LOCAL_PORT(ulPortKey)), ADDRESS_BYTES(CONNECTION_REMOTE_ADDRESS(ul64AddressKey)), NTOHS(CONNECTION_REMOTE_PORT(ulPortKey)) )); // // First look in the cache // pEntry = (PNS_CONNECTION_ENTRY) ProbeCache( NsConnectionCache, (ULONG)ul64AddressKey ); if (NULL != pEntry && pEntry->ul64AddressKey == ul64AddressKey && pEntry->ucProtocol == ucProtocol && pEntry->ulPortKey[NsOutboundDirection] == ulPortKey) { TRACE(CONN_LOOKUP, ("NsLookupOutboundConnectionEntry: Cache Hit\n")); return pEntry; } // // Search the full tree. Keys are checked in the // following order: // // 1. Address Key // 2. Protocol // 3. Outbound port key // pRoot = NsConnectionTree[NsOutboundDirection]; for (SLink = (pRoot ? &pRoot->SLink[NsOutboundDirection] : NULL ); SLink; ) { pEntry = CONTAINING_RECORD(SLink, NS_CONNECTION_ENTRY, SLink[NsOutboundDirection]); if (ul64AddressKey < pEntry->ul64AddressKey) { pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (ul64AddressKey > pEntry->ul64AddressKey) { pParent = pEntry; SLink = RtlRightChild(SLink); continue; } else if (ucProtocol < pEntry->ucProtocol) { pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (ucProtocol > pEntry->ucProtocol) { pParent = pEntry; SLink = RtlRightChild(SLink); continue; } else if (ulPortKey < pEntry->ulPortKey[NsOutboundDirection]) { pParent = pEntry; SLink = RtlLeftChild(SLink); continue; } else if (ulPortKey > pEntry->ulPortKey[NsOutboundDirection]) { pParent = pEntry; SLink = RtlRightChild(SLink); continue; } // // We found the entry -- update cache and return // UpdateCache( NsConnectionCache, (ULONG)ul64AddressKey, (PVOID)pEntry ); return pEntry; } // // Not found -- provide insertion point if requested // if (ppInsertionPoint) { *ppInsertionPoint = pParent; } return NULL; } // NsLookupOutboundConnectionEntry PNS_CONNECTION_ENTRY NspInsertInboundConnectionEntry( PNS_CONNECTION_ENTRY pParent, PNS_CONNECTION_ENTRY pEntry ) /*++ Routine Description: This routine inserts a connection entry into the tree. Arguments: pParent - the node to be the parent for the new connection entry. If NULL, the new entry becomes the root. pEntry - the new connection entry to be inserted. Return Value: PNS_CONNECTION_ENTRY - The new root of the tree. If insertion fails, returns NULL. Environment: Invoked with 'NsConnectionLock' held by the caller. --*/ { PRTL_SPLAY_LINKS pRoot; ASSERT(NULL != pEntry); if (NULL == pParent) { // // The new entry is to be the root. // return pEntry; } // // Insert as left or right child. Keys are checked in the // following order: // // 1. Address Key // 2. Protocol // 3. Inbound port key // 4. IpSec context // if (pEntry->ul64AddressKey < pParent->ul64AddressKey) { RtlInsertAsLeftChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->ul64AddressKey > pParent->ul64AddressKey) { RtlInsertAsRightChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->ucProtocol < pParent->ucProtocol) { RtlInsertAsLeftChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->ucProtocol > pParent->ucProtocol) { RtlInsertAsRightChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->ulPortKey[NsInboundDirection] < pParent->ulPortKey[NsInboundDirection]) { RtlInsertAsLeftChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->ulPortKey[NsInboundDirection] > pParent->ulPortKey[NsInboundDirection]) { RtlInsertAsRightChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->pvIpSecContext < pParent->pvIpSecContext) { RtlInsertAsLeftChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else if (pEntry->pvIpSecContext > pParent->pvIpSecContext) { RtlInsertAsRightChild( &pParent->SLink[NsInboundDirection], &pEntry->SLink[NsInboundDirection] ); } else { // // Duplicate entry -- this should not happen. // ASSERT(FALSE); return NULL; } // // Splay the new node and return the resulting root. // pRoot = RtlSplay(&pEntry->SLink[NsInboundDirection]); return CONTAINING_RECORD(pRoot, NS_CONNECTION_ENTRY, SLink[NsInboundDirection]); } // NspInsertInboundConnectionEntry PNS_CONNECTION_ENTRY NspInsertOutboundConnectionEntry( PNS_CONNECTION_ENTRY pParent, PNS_CONNECTION_ENTRY pEntry ) /*++ Routine Description: This routine inserts a connection entry into the tree. Arguments: pParent - the node to be the parent for the new connection entry. If NULL, the new entry becomes the root. pEntry - the new connection entry to be inserted. Return Value: PNS_CONNECTION_ENTRY - The new root of the tree. If insertion fails, returns NULL. Environment: Invoked with 'NsConnectionLock' held by the caller. --*/ { PRTL_SPLAY_LINKS pRoot; ASSERT(NULL != pEntry); if (NULL == pParent) { // // The new entry is to be the root. // return pEntry; } // // Insert as left or right child. Keys are checked in the // following order: // // 1. Address Key // 2. Protocol // 3. Outbound port key // if (pEntry->ul64AddressKey < pParent->ul64AddressKey) { RtlInsertAsLeftChild( &pParent->SLink[NsOutboundDirection], &pEntry->SLink[NsOutboundDirection] ); } else if (pEntry->ul64AddressKey > pParent->ul64AddressKey) { RtlInsertAsRightChild( &pParent->SLink[NsOutboundDirection], &pEntry->SLink[NsOutboundDirection] ); } else if (pEntry->ucProtocol < pParent->ucProtocol) { RtlInsertAsLeftChild( &pParent->SLink[NsOutboundDirection], &pEntry->SLink[NsOutboundDirection] ); } else if (pEntry->ucProtocol > pParent->ucProtocol) { RtlInsertAsRightChild( &pParent->SLink[NsOutboundDirection], &pEntry->SLink[NsOutboundDirection] ); } else if (pEntry->ulPortKey[NsOutboundDirection] < pParent->ulPortKey[NsOutboundDirection]) { RtlInsertAsLeftChild( &pParent->SLink[NsOutboundDirection], &pEntry->SLink[NsOutboundDirection] ); } else if (pEntry->ulPortKey[NsOutboundDirection] > pParent->ulPortKey[NsOutboundDirection]) { RtlInsertAsRightChild( &pParent->SLink[NsOutboundDirection], &pEntry->SLink[NsOutboundDirection] ); } else { // // Duplicate entry -- this should not happen. // ASSERT(FALSE); return NULL; } // // Splay the new node and return the resulting root. // pRoot = RtlSplay(&pEntry->SLink[NsOutboundDirection]); return CONTAINING_RECORD(pRoot, NS_CONNECTION_ENTRY, SLink[NsOutboundDirection]); } // NspInsertOutboundConnectionEntry VOID NsShutdownConnectionManagement( VOID ) /*++ Routine Description: This routine is invoked to shutdown the connection management module. Arguments: none. Return Value: none. Environment: Invoked with no references made to any connection entry. --*/ { KIRQL Irql; PNS_CONNECTION_ENTRY pEntry; CALLTRACE(("NsShutdownConnectionManagement\n")); KeAcquireSpinLock(&NsConnectionLock, &Irql); while (!IsListEmpty(&NsConnectionList)) { pEntry = CONTAINING_RECORD( RemoveHeadList(&NsConnectionList), NS_CONNECTION_ENTRY, Link ); NsCleanupConnectionEntry(pEntry); } NsConnectionTree[NsInboundDirection] = NULL; NsConnectionTree[NsOutboundDirection] = NULL; KeReleaseSpinLock(&NsConnectionLock, Irql); ExDeleteNPagedLookasideList(&NsConnectionLookasideList); } // NsShutdownConnectionManagement