Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1196 lines
31 KiB

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