/*++ Copyright (c) 1997-2001 Microsoft Corporation Module Name: NsIcmp.c Abstract: IpSec NAT shim ICMP management Author: Jonathan Burstein (jonburs) 11-July-2001 Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Global Variables // LIST_ENTRY NsIcmpList; KSPIN_LOCK NsIcmpLock; NPAGED_LOOKASIDE_LIST NsIcmpLookasideList; // // Function Prototypes // NTSTATUS NspCreateIcmpEntry( ULONG64 ul64AddressKey, USHORT usOriginalIdentifier, PVOID pvIpSecContext, BOOLEAN fIdConflicts, PLIST_ENTRY pInsertionPoint, PNS_ICMP_ENTRY *ppNewEntry ); NTSTATUS FASTCALL NspHandleInboundIcmpError( PNS_PACKET_CONTEXT pContext, PVOID pvIpSecContext ); NTSTATUS FASTCALL NspHandleOutboundIcmpError( PNS_PACKET_CONTEXT pContext, PVOID *ppvIpSecContext ); PNS_ICMP_ENTRY NspLookupInboundIcmpEntry( ULONG64 ul64AddressKey, USHORT usOriginalIdentifier, PVOID pvIpSecContext, BOOLEAN *pfIdentifierConflicts, PLIST_ENTRY *ppInsertionPoint ); PNS_ICMP_ENTRY NspLookupOutboundIcmpEntry( ULONG64 ul64AddressKey, USHORT usTranslatedIdentifier ); NTSTATUS NsInitializeIcmpManagement( VOID ) /*++ Routine Description: This routine is invoked to initialize the ICMP management module. Arguments: none. Return Value: NTSTATUS. --*/ { CALLTRACE(("NsInitializeIcmpManagement\n")); InitializeListHead(&NsIcmpList); KeInitializeSpinLock(&NsIcmpLock); ExInitializeNPagedLookasideList( &NsIcmpLookasideList, NULL, NULL, 0, sizeof(NS_ICMP_ENTRY), NS_TAG_ICMP, NS_ICMP_LOOKASIDE_DEPTH ); return STATUS_SUCCESS; } // NsInitializeIcmpManagement NTSTATUS FASTCALL NspHandleInboundIcmpError( PNS_PACKET_CONTEXT pContext, PVOID pvIpSecContext ) /*++ Routine Description: This routine is called to process inbound ICMP error messages. Based on the protocol of the embedded packet it will attempt to find a matching connection entry (for TCP, UDP, or ICMP) and perform any necessary translations. Arguments: pContext - the context information for the packet. pvIpSecContext - the IpSec context for this packet; this is considered an opaque value. Return Value: NTSTATUS. --*/ { KIRQL Irql; PNS_CONNECTION_ENTRY pEntry; PNS_ICMP_ENTRY pIcmpEntry; ICMP_HEADER UNALIGNED *pIcmpHeader; UCHAR ucProtocol; ULONG64 ul64AddressKey; ULONG ulChecksum; ULONG ulChecksumDelta; ULONG ulChecksumDelta2; ULONG ulPortKey; ASSERT(NULL != pContext); // // Make sure that the buffer is large enough to contain the // encapsulated packet. // if (pContext->ulProtocolHeaderLength < sizeof(ICMP_HEADER)) { return STATUS_INVALID_PARAMETER; } // // If the embedded header is not TCP, UDP, or ICMP exit quickly, // as we have nothing to do. // pIcmpHeader = pContext->pIcmpHeader; ucProtocol = pIcmpHeader->EncapsulatedIpHeader.Protocol; if (NS_PROTOCOL_TCP != ucProtocol && NS_PROTOCOL_UDP != ucProtocol && NS_PROTOCOL_ICMP != ucProtocol ) { return STATUS_SUCCESS; } // // See if the embedded packet belongs to a known conection. Notice that // the order of the addresses here -- since the embedded packet is one // that we sent, the source address is local and the destination address // is remote. // MAKE_ADDRESS_KEY( ul64AddressKey, pIcmpHeader->EncapsulatedIpHeader.SourceAddress, pIcmpHeader->EncapsulatedIpHeader.DestinationAddress ); if (NS_PROTOCOL_ICMP == ucProtocol) { KeAcquireSpinLock(&NsIcmpLock, &Irql); pIcmpEntry = NspLookupInboundIcmpEntry( ul64AddressKey, pIcmpHeader->EncapsulatedIcmpHeader.Identifier, pvIpSecContext, NULL, NULL ); if (NULL != pIcmpEntry) { KeQueryTickCount((PLARGE_INTEGER)&pIcmpEntry->l64LastAccessTime); if (pIcmpEntry->usTranslatedId != pIcmpEntry->usOriginalId) { // // We found an ICMP entry for the embedded packet that // has a translated ID. This means that we need to: // // 1) Change the ID in the embedded packet. // 2) Update the ICMP checksum of the embedded packet. // 3) Update the ICMP checksum of the original packet, based // on the previous changes. // pIcmpHeader->EncapsulatedIcmpHeader.Identifier = pIcmpEntry->usTranslatedId; ulChecksumDelta = 0; CHECKSUM_LONG(ulChecksumDelta, ~pIcmpEntry->usOriginalId); CHECKSUM_LONG(ulChecksumDelta, pIcmpEntry->usTranslatedId); ulChecksumDelta2 = ulChecksumDelta; CHECKSUM_LONG( ulChecksumDelta2, ~pIcmpHeader->EncapsulatedIcmpHeader.Checksum ); CHECKSUM_UPDATE(pIcmpHeader->EncapsulatedIcmpHeader.Checksum); CHECKSUM_LONG( ulChecksumDelta2, pIcmpHeader->EncapsulatedIcmpHeader.Checksum ); ulChecksumDelta = ulChecksumDelta2; CHECKSUM_UPDATE(pIcmpHeader->Checksum); } } KeReleaseSpinLock(&NsIcmpLock, Irql); } else { MAKE_PORT_KEY( ulPortKey, pIcmpHeader->EncapsulatedUdpHeader.SourcePort, pIcmpHeader->EncapsulatedUdpHeader.DestinationPort ); KeAcquireSpinLock(&NsConnectionLock, &Irql); pEntry = NsLookupInboundConnectionEntry( ul64AddressKey, ulPortKey, ucProtocol, pvIpSecContext, NULL, NULL ); if (NULL != pEntry && pEntry->ulPortKey[NsInboundDirection] != pEntry->ulPortKey[NsOutboundDirection]) { // // We found a connection entry that contains a translated // port. This means we need to: // // 1) Change the remote (destination) port in the // embedded packet. // 2) Update the checksum of the embedded packet, if it's // UDP. (An embedded TCP packet is not long enough to // contain the checksum.) // 3) Update the ICMP checksum of the original packet, based // on the previous changes. // pIcmpHeader->EncapsulatedUdpHeader.DestinationPort = CONNECTION_REMOTE_PORT(pEntry->ulPortKey[NsOutboundDirection]); ulChecksumDelta = 0; CHECKSUM_LONG( ulChecksumDelta, ~CONNECTION_REMOTE_PORT(pEntry->ulPortKey[NsInboundDirection]) ); CHECKSUM_LONG( ulChecksumDelta, CONNECTION_REMOTE_PORT(pEntry->ulPortKey[NsOutboundDirection]) ); if (NS_PROTOCOL_UDP == ucProtocol) { ulChecksumDelta2 = ulChecksumDelta; CHECKSUM_LONG( ulChecksumDelta2, ~pIcmpHeader->EncapsulatedUdpHeader.Checksum ); CHECKSUM_UPDATE(pIcmpHeader->EncapsulatedUdpHeader.Checksum); CHECKSUM_LONG( ulChecksumDelta2, pIcmpHeader->EncapsulatedUdpHeader.Checksum ); ulChecksumDelta = ulChecksumDelta2; } CHECKSUM_UPDATE(pIcmpHeader->Checksum); } KeReleaseSpinLock(&NsConnectionLock, Irql); } return STATUS_SUCCESS; } // NspHandleInboundIcmpError NTSTATUS FASTCALL NspHandleOutboundIcmpError( PNS_PACKET_CONTEXT pContext, PVOID *ppvIpSecContext ) /*++ Routine Description: This routine is called to process outbound ICMP error messages. Based on the protocol of the embedded packet it will attempt to find a matching connection entry (for TCP, UDP, or ICMP), obtain the IpSec context for the embedded packet, and perform any necessary translations. Arguments: pContext - the context information for the packet. ppvIpSecContext - receives the IpSec context for this packet, if any; receives NULL if no context exists. Return Value: NTSTATUS. --*/ { KIRQL Irql; PNS_CONNECTION_ENTRY pEntry; PNS_ICMP_ENTRY pIcmpEntry; ICMP_HEADER UNALIGNED *pIcmpHeader; UCHAR ucProtocol; ULONG64 ul64AddressKey; ULONG ulChecksum; ULONG ulChecksumDelta; ULONG ulChecksumDelta2; ULONG ulPortKey; ASSERT(NULL != pContext); ASSERT(NULL != ppvIpSecContext); // // Make sure that the buffer is large enough to contain the // encapsulated packet. // if (pContext->ulProtocolHeaderLength < sizeof(ICMP_HEADER)) { return STATUS_INVALID_PARAMETER; } // // If the embedded header is not TCP, UDP, or ICMP exit quickly, // as we have nothing to do. // pIcmpHeader = pContext->pIcmpHeader; ucProtocol = pIcmpHeader->EncapsulatedIpHeader.Protocol; if (NS_PROTOCOL_TCP != ucProtocol && NS_PROTOCOL_UDP != ucProtocol && NS_PROTOCOL_ICMP != ucProtocol ) { return STATUS_SUCCESS; } // // See if the embedded packet belongs to a known conection. Notice that // the order of the addresses here -- since the embedded packet is one // that we received, the source address is remote and the destination // address is local. // MAKE_ADDRESS_KEY( ul64AddressKey, pIcmpHeader->EncapsulatedIpHeader.DestinationAddress, pIcmpHeader->EncapsulatedIpHeader.SourceAddress ); if (NS_PROTOCOL_ICMP == ucProtocol) { KeAcquireSpinLock(&NsIcmpLock, &Irql); pIcmpEntry = NspLookupOutboundIcmpEntry( ul64AddressKey, pIcmpHeader->EncapsulatedIcmpHeader.Identifier ); if (NULL != pIcmpEntry) { *ppvIpSecContext = pIcmpEntry->pvIpSecContext; KeQueryTickCount((PLARGE_INTEGER)&pIcmpEntry->l64LastAccessTime); if (pIcmpEntry->usTranslatedId != pIcmpEntry->usOriginalId) { // // We found an ICMP entry for the embedded packet that // has a translated ID. This means that we need to: // // 1) Change the ID in the embedded packet. // 2) Update the ICMP checksum of the embedded packet. // 3) Update the ICMP checksum of the original packet, based // on the previous changes. // pIcmpHeader->EncapsulatedIcmpHeader.Identifier = pIcmpEntry->usOriginalId; ulChecksumDelta = 0; CHECKSUM_LONG(ulChecksumDelta, ~pIcmpEntry->usTranslatedId); CHECKSUM_LONG(ulChecksumDelta, pIcmpEntry->usOriginalId); ulChecksumDelta2 = ulChecksumDelta; CHECKSUM_LONG( ulChecksumDelta2, ~pIcmpHeader->EncapsulatedIcmpHeader.Checksum ); CHECKSUM_UPDATE(pIcmpHeader->EncapsulatedIcmpHeader.Checksum); CHECKSUM_LONG( ulChecksumDelta2, pIcmpHeader->EncapsulatedIcmpHeader.Checksum ); ulChecksumDelta = ulChecksumDelta2; CHECKSUM_UPDATE(pIcmpHeader->Checksum); } } KeReleaseSpinLock(&NsIcmpLock, Irql); } else { MAKE_PORT_KEY( ulPortKey, pIcmpHeader->EncapsulatedUdpHeader.DestinationPort, pIcmpHeader->EncapsulatedUdpHeader.SourcePort ); KeAcquireSpinLock(&NsConnectionLock, &Irql); pEntry = NsLookupOutboundConnectionEntry( ul64AddressKey, ulPortKey, ucProtocol, NULL ); if (NULL != pEntry) { *ppvIpSecContext = pEntry->pvIpSecContext; if (pEntry->ulPortKey[NsInboundDirection] != pEntry->ulPortKey[NsOutboundDirection]) { // // We found a connection entry that contains a translated // port. This means we need to: // // 1) Change the remote (source) port in the // embedded packet. // 2) Update the checksum of the embedded packet, if it's // UDP. (An embedded TCP packet is not long enough to // contain the checksum.) // 3) Update the ICMP checksum of the original packet, based // on the previous changes. // pIcmpHeader->EncapsulatedUdpHeader.SourcePort = CONNECTION_REMOTE_PORT(pEntry->ulPortKey[NsInboundDirection]); ulChecksumDelta = 0; CHECKSUM_LONG( ulChecksumDelta, ~CONNECTION_REMOTE_PORT(pEntry->ulPortKey[NsOutboundDirection]) ); CHECKSUM_LONG( ulChecksumDelta, CONNECTION_REMOTE_PORT(pEntry->ulPortKey[NsInboundDirection]) ); if (NS_PROTOCOL_UDP == ucProtocol) { ulChecksumDelta2 = ulChecksumDelta; CHECKSUM_LONG( ulChecksumDelta2, ~pIcmpHeader->EncapsulatedUdpHeader.Checksum ); CHECKSUM_UPDATE(pIcmpHeader->EncapsulatedUdpHeader.Checksum); CHECKSUM_LONG( ulChecksumDelta2, pIcmpHeader->EncapsulatedUdpHeader.Checksum ); ulChecksumDelta = ulChecksumDelta2; } CHECKSUM_UPDATE(pIcmpHeader->Checksum); } } KeReleaseSpinLock(&NsConnectionLock, Irql); } return STATUS_SUCCESS; } // NspHandleOutboundIcmpError NTSTATUS NspCreateIcmpEntry( ULONG64 ul64AddressKey, USHORT usOriginalIdentifier, PVOID pvIpSecContext, BOOLEAN fIdConflicts, PLIST_ENTRY pInsertionPoint, PNS_ICMP_ENTRY *ppNewEntry ) /*++ Routine Description: Creates an ICMP entry (for request / response message types). If necessary this routine will allocate a new identifier. Arguments: ul64AddressKey - the addressing information for the entry usOriginalIdentifier - the original ICMP identifier for the entry pvIpSecContext - the IpSec context for the entry fIdConflicts - TRUE if the original identifier is known to conflict with an existing entry on the inbound path pInsertionPoint - the insertion point for the new entry ppNewEntry - receives the newly created entry on success Return Value: NTSTATUS Environment: Invoked with 'NsIcmpLock' held by the caller. --*/ { PNS_ICMP_ENTRY pEntry; NTSTATUS Status = STATUS_UNSUCCESSFUL; USHORT usTranslatedId; TRACE(ICMP, ("NspCreateIcmpEntry\n")); ASSERT(NULL != pInsertionPoint); ASSERT(NULL != ppNewEntry); // // Determine what translated ID should be used for this // entry // if (fIdConflicts) { usTranslatedId = (USHORT) -1; } else { usTranslatedId = usOriginalIdentifier; } do { if (NULL == NspLookupOutboundIcmpEntry(ul64AddressKey, usTranslatedId)) { // // This identifier does not conflict. // Status = STATUS_SUCCESS; break; } if (fIdConflicts) { usTranslatedId -= 1; } else { fIdConflicts = TRUE; usTranslatedId = (USHORT) -1; } } while (usTranslatedId > 0); if (STATUS_SUCCESS == Status) { // // Allocate and initialize the new entry // pEntry = ALLOCATE_ICMP_BLOCK(); if (NULL != pEntry) { RtlZeroMemory(pEntry, sizeof(*pEntry)); pEntry->ul64AddressKey = ul64AddressKey; pEntry->usOriginalId = usOriginalIdentifier; pEntry->usTranslatedId = usTranslatedId; pEntry->pvIpSecContext = pvIpSecContext; InsertTailList(pInsertionPoint, &pEntry->Link); *ppNewEntry = pEntry; } else { ERROR(("NspCreateIcmpEntry: Allocation Failed\n")); Status = STATUS_NO_MEMORY; } } return Status; } // NspCreateIcmpEntry PNS_ICMP_ENTRY NspLookupInboundIcmpEntry( ULONG64 ul64AddressKey, USHORT usOriginalIdentifier, PVOID pvIpSecContext, BOOLEAN *pfIdentifierConflicts, PLIST_ENTRY *ppInsertionPoint ) /*++ Routine Description: Called to lookup an inbound ICMP entry. Arguments: ul64AddressKey - the addressing information for the entry usOriginalIdentifier - the original ICMP identifier for the entry pvIpSecContext - the IpSec context for the entry pfIdentifierConflicts - on failure, receives a boolean the indicates why the lookup failed: TRUE if the lookup failed because there is an identical entry w/ different IpSec context, FALSE otherwise. (optional) ppInsertionPoint - receives the insertion point if not found (optional) Return Value: PNS_ICMP_ENTRY - a pointer to the connection entry, if found, or NULL otherwise. Environment: Invoked with 'NsIcmpLock' held by the caller. --*/ { PNS_ICMP_ENTRY pEntry; PLIST_ENTRY pLink; if (pfIdentifierConflicts) { *pfIdentifierConflicts = FALSE; } for (pLink = NsIcmpList.Flink; pLink != &NsIcmpList; pLink = pLink->Flink) { pEntry = CONTAINING_RECORD(pLink, NS_ICMP_ENTRY, Link); // // For inbound entries the search order is: // 1) address key // 2) original identifier // 3) IpSec context // if (ul64AddressKey > pEntry->ul64AddressKey) { continue; } else if (ul64AddressKey < pEntry->ul64AddressKey) { break; } else if (usOriginalIdentifier > pEntry->usOriginalId) { continue; } else if (usOriginalIdentifier < pEntry->usOriginalId) { break; } else if (pvIpSecContext > pEntry->pvIpSecContext) { // // This entry matches everything requested except // for the IpSec context. Inform the caller of this // fact (if desired). // if (pfIdentifierConflicts) { *pfIdentifierConflicts = TRUE; } continue; } else if (pvIpSecContext < pEntry->pvIpSecContext) { // // This entry matches everything requested except // for the IpSec context. Inform the caller of this // fact (if desired). // if (pfIdentifierConflicts) { *pfIdentifierConflicts = TRUE; } break; } // // This is the requested entry. // return pEntry; } // // Entry not found -- set insertion point if so requested. // if (ppInsertionPoint) { *ppInsertionPoint = pLink; } return NULL; } // NspLookupInboundIcmpEntry PNS_ICMP_ENTRY NspLookupOutboundIcmpEntry( ULONG64 ul64AddressKey, USHORT usTranslatedIdentifier ) /*++ Routine Description: Called to lookup an outbound ICMP entry. Arguments: ul64AddressKey - the addressing information for the entry usTranslatedIdentifier - the translated ICMP identifier for the entry Return Value: PNS_ICMP_ENTRY - a pointer to the entry, if found, or NULL otherwise. Environment: Invoked with 'NsIcmpLock' held by the caller. --*/ { PNS_ICMP_ENTRY pEntry; PLIST_ENTRY pLink; for (pLink = NsIcmpList.Flink; pLink != &NsIcmpList; pLink = pLink->Flink) { pEntry = CONTAINING_RECORD(pLink, NS_ICMP_ENTRY, Link); // // When searching for an outbound entry, we can depend on the // ordering of address keys. However, we cannot depend on the // order of the translated identifiers, so we have to perform // an exhaustive search of all entries with the proper // address key. // if (ul64AddressKey > pEntry->ul64AddressKey) { continue; } else if (ul64AddressKey < pEntry->ul64AddressKey) { break; } else if (usTranslatedIdentifier == pEntry->usTranslatedId) { // // This is the requested entry. // return pEntry; } } // // Entry not found. // return NULL; } // NspLookupOutboundIcmpEntry NTSTATUS FASTCALL NsProcessIncomingIcmpPacket( PNS_PACKET_CONTEXT pContext, PVOID pvIpSecContext ) /*++ Routine Description: This routine is invoked by IpSec for each incoming ICMP packet. Arguments: pContext - the context information for the packet. pvIpSecContext - the IpSec context for this packet; this is considered an opaque value. Return Value: NTSTATUS. --*/ { BOOLEAN fIdConflicts; KIRQL Irql; PNS_ICMP_ENTRY pIcmpEntry; PLIST_ENTRY pInsertionPoint; NTSTATUS Status = STATUS_SUCCESS; ULONG64 ul64AddressKey; ULONG ulChecksum; ULONG ulChecksumDelta; ASSERT(NULL != pContext); TRACE( ICMP, ("NsProcessIncomingIcmpPacket: %d.%d.%d.%d -> %d.%d.%d.%d : %d, %d : %d\n", ADDRESS_BYTES(pContext->ulSourceAddress), ADDRESS_BYTES(pContext->ulDestinationAddress), pContext->pIcmpHeader->Type, pContext->pIcmpHeader->Code, pvIpSecContext )); // // Branch to proper behavior based on ICMP Type // switch (pContext->pIcmpHeader->Type) { case ICMP_ROUTER_REPLY: case ICMP_MASK_REPLY: case ICMP_ECHO_REPLY: case ICMP_TIMESTAMP_REPLY: { // // No action is needed for inbound replies. // break; } case ICMP_ROUTER_REQUEST: case ICMP_MASK_REQUEST: case ICMP_ECHO_REQUEST: case ICMP_TIMESTAMP_REQUEST: { // // See if we have an ICMP entry that matches this packet. // MAKE_ADDRESS_KEY( ul64AddressKey, pContext->ulDestinationAddress, pContext->ulSourceAddress ); KeAcquireSpinLock(&NsIcmpLock, &Irql); pIcmpEntry = NspLookupInboundIcmpEntry( ul64AddressKey, pContext->pIcmpHeader->Identifier, pvIpSecContext, &fIdConflicts, &pInsertionPoint ); if (NULL == pIcmpEntry) { // // No entry was found; attempt to create a new // one. (The creation function will allocate // a new ID, if necessary.) // Status = NspCreateIcmpEntry( ul64AddressKey, pContext->pIcmpHeader->Identifier, pvIpSecContext, fIdConflicts, pInsertionPoint, &pIcmpEntry ); } if (STATUS_SUCCESS == Status) { ASSERT(NULL != pIcmpEntry); KeQueryTickCount((PLARGE_INTEGER)&pIcmpEntry->l64LastAccessTime); if (pIcmpEntry->usTranslatedId != pIcmpEntry->usOriginalId) { // // Need to translate the ICMP ID for this packet, and // adjust the checksum accordingly. // pContext->pIcmpHeader->Identifier = pIcmpEntry->usTranslatedId; ulChecksumDelta = 0; CHECKSUM_LONG(ulChecksumDelta, ~pIcmpEntry->usOriginalId); CHECKSUM_LONG(ulChecksumDelta, pIcmpEntry->usTranslatedId); CHECKSUM_UPDATE(pContext->pIcmpHeader->Checksum); } } KeReleaseSpinLock(&NsIcmpLock, Irql); break; } case ICMP_TIME_EXCEED: case ICMP_PARAM_PROBLEM: case ICMP_DEST_UNREACH: case ICMP_SOURCE_QUENCH: { Status = NspHandleInboundIcmpError(pContext, pvIpSecContext); break; } default: { break; } } return Status; } // NsProcessIncomingIcmpPacket NTSTATUS FASTCALL NsProcessOutgoingIcmpPacket( PNS_PACKET_CONTEXT pContext, PVOID *ppvIpSecContext ) /*++ Routine Description: This routine is invoked by IpSec for each outgoing ICMP packet. Arguments: pContext - the context information for the packet. ppvIpSecContext - receives the IpSec context for this packet, if any; receives NULL if no context exists. Return Value: NTSTATUS. --*/ { KIRQL Irql; PNS_ICMP_ENTRY pIcmpEntry; NTSTATUS Status = STATUS_SUCCESS; ULONG64 ul64AddressKey; ULONG ulChecksum; ULONG ulChecksumDelta; ASSERT(NULL != pContext); ASSERT(NULL != ppvIpSecContext); TRACE( ICMP, ("NsProcessOutgoingIcmpPacket: %d.%d.%d.%d -> %d.%d.%d.%d : %d, %d\n", ADDRESS_BYTES(pContext->ulSourceAddress), ADDRESS_BYTES(pContext->ulDestinationAddress), pContext->pIcmpHeader->Type, pContext->pIcmpHeader->Code )); // // Set context to the default value // *ppvIpSecContext = NULL; // // Branch to proper behavior based on ICMP Type // switch (pContext->pIcmpHeader->Type) { case ICMP_ROUTER_REPLY: case ICMP_MASK_REPLY: case ICMP_ECHO_REPLY: case ICMP_TIMESTAMP_REPLY: { // // See if we have an ICMP entry that matches this packet. // MAKE_ADDRESS_KEY( ul64AddressKey, pContext->ulSourceAddress, pContext->ulDestinationAddress ); KeAcquireSpinLock(&NsIcmpLock, &Irql); pIcmpEntry = NspLookupOutboundIcmpEntry( ul64AddressKey, pContext->pIcmpHeader->Identifier ); if (NULL != pIcmpEntry) { *ppvIpSecContext = pIcmpEntry->pvIpSecContext; KeQueryTickCount((PLARGE_INTEGER)&pIcmpEntry->l64LastAccessTime); if (pIcmpEntry->usTranslatedId != pIcmpEntry->usOriginalId) { // // Need to translate the ICMP ID for this packet, and // adjust the checksum accordingly. // pContext->pIcmpHeader->Identifier = pIcmpEntry->usOriginalId; ulChecksumDelta = 0; CHECKSUM_LONG(ulChecksumDelta, ~pIcmpEntry->usTranslatedId); CHECKSUM_LONG(ulChecksumDelta, pIcmpEntry->usOriginalId); CHECKSUM_UPDATE(pContext->pIcmpHeader->Checksum); } } KeReleaseSpinLock(&NsIcmpLock, Irql); break; } case ICMP_ROUTER_REQUEST: case ICMP_MASK_REQUEST: case ICMP_ECHO_REQUEST: case ICMP_TIMESTAMP_REQUEST: { // // No action is needed for outgoing requests. // break; } case ICMP_TIME_EXCEED: case ICMP_PARAM_PROBLEM: case ICMP_DEST_UNREACH: case ICMP_SOURCE_QUENCH: { Status = NspHandleOutboundIcmpError(pContext, ppvIpSecContext); break; } default: { break; } } return Status; } // NsProcessOutgoingIcmpPacket VOID NsShutdownIcmpManagement( VOID ) /*++ Routine Description: This routine is invoked to cleanup the ICMP management module. Arguments: none. Return Value: none. --*/ { KIRQL Irql; PNS_ICMP_ENTRY pEntry; CALLTRACE(("NsShutdownIcmpManagement\n")); KeAcquireSpinLock(&NsIcmpLock, &Irql); while (!IsListEmpty(&NsIcmpList)) { pEntry = CONTAINING_RECORD( RemoveHeadList(&NsIcmpList), NS_ICMP_ENTRY, Link ); FREE_ICMP_BLOCK(pEntry); } KeReleaseSpinLock(&NsIcmpLock, Irql); ExDeleteNPagedLookasideList(&NsIcmpLookasideList); } // NsShutdownIcmpManagement