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
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
|
|
|