windows-nt/Source/XPSP1/NT/net/rras/ip/ipinip/icmpfn.c
2020-09-26 16:20:57 +08:00

351 lines
7.3 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
ipinip\icmpfn.c
Abstract:
Handlers for ICMP messages relating to the tunnel
Author:
Amritansh Raghav
Revision History:
AmritanR Created
Notes:
--*/
#define __FILE_SIG__ ICMP_SIG
#include "inc.h"
NTSTATUS
HandleTimeExceeded(
PTUNNEL pTunnel,
PICMP_HEADER pIcmpHeader,
PIP_HEADER pInHeader
)
/*++
Routine Description
Locks
The tunnels is locked and refcounted
Arguments
pTunnel Tunnel associated with the ICMP message
pIcmpHeader The ICMP header
pInHeader The original header
Return Value
STATUS_SUCCESS
--*/
{
PPENDING_MESSAGE pMessage;
//
// We mark the tunnel as down.
// Periodically we will sweep the tunnels and mark them as up
//
#if DBG
Trace(TUNN, INFO,
("HandleTimeExceeded: Time exceeded message for %s\n",
pTunnel->asDebugBindName.Buffer));
#endif
pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL;
pTunnel->dwAdminState |= TS_TTL_TOO_LOW;
pMessage = AllocateMessage();
if(pMessage isnot NULL)
{
pMessage->inMsg.ieEvent = IE_INTERFACE_DOWN;
pMessage->inMsg.iseSubEvent = ISE_ICMP_TTL_TOO_LOW;
pMessage->inMsg.dwIfIndex = pTunnel->dwIfIndex;
CompleteNotificationIrp(pMessage);
}
KeQueryTickCount((PLARGE_INTEGER)&((pTunnel->ullLastChange)));
return STATUS_SUCCESS;
}
NTSTATUS
HandleDestUnreachable(
PTUNNEL pTunnel,
PICMP_HEADER pIcmpHeader,
PIP_HEADER pInHeader
)
/*++
Routine Description
Locks
The tunnels is locked and refcounted
Arguments
pTunnel Tunnel associated with the ICMP message
pIcmpHeader The ICMP header
pInHeader The original header
Return Value
STATUS_SUCCESS
--*/
{
PPENDING_MESSAGE pMessage;
if(pIcmpHeader->byCode is ICMP_CODE_DGRAM_TOO_BIG)
{
PICMP_DGRAM_TOO_BIG_MSG pMsg;
ULONG ulNewMtu;
pMsg = (PICMP_DGRAM_TOO_BIG_MSG)pIcmpHeader;
//
// Change the MTU
//
ulNewMtu = (ULONG)(RtlUshortByteSwap(pMsg->usMtu) - MAX_IP_HEADER_LENGTH);
if(ulNewMtu < pTunnel->ulMtu)
{
LLIPMTUChange mtuChangeInfo;
#if DBG
Trace(TUNN, INFO,
("HandleDestUnreachable: Dgram too big %s. Old %d New %d\n",
pTunnel->asDebugBindName.Buffer,
pTunnel->ulMtu,
ulNewMtu));
#endif
pTunnel->ulMtu = ulNewMtu;
mtuChangeInfo.lmc_mtu = ulNewMtu;
g_pfnIpStatus(pTunnel->pvIpContext,
LLIP_STATUS_MTU_CHANGE,
&mtuChangeInfo,
sizeof(LLIPMTUChange),
NULL);
}
else
{
RtAssert(FALSE);
}
KeQueryTickCount((PLARGE_INTEGER)&((pTunnel->ullLastChange)));
return STATUS_SUCCESS;
}
RtAssert(pIcmpHeader->byCode <= ICMP_CODE_DGRAM_TOO_BIG);
//
// Other codes are NetUnreachable, HostUnreachable, ProtoUnreachable
// and PortUnreachable.
//
RtAssert(pIcmpHeader->byCode isnot ICMP_CODE_PORT_UNREACHABLE);
#if DBG
Trace(TUNN, INFO,
("HandleDestUnreachable: Code %d\n",
pIcmpHeader->byCode));
#endif
//
// For these codes, we mark the tunnel down.
// Periodically we will sweep the tunnels and mark them as up
//
pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL;
pTunnel->dwAdminState |= TS_DEST_UNREACH;
pMessage = AllocateMessage();
if(pMessage isnot NULL)
{
pMessage->inMsg.ieEvent = IE_INTERFACE_DOWN;
pMessage->inMsg.iseSubEvent = ISE_DEST_UNREACHABLE;
pMessage->inMsg.dwIfIndex = pTunnel->dwIfIndex;
CompleteNotificationIrp(pMessage);
}
KeQueryTickCount((PLARGE_INTEGER)&((pTunnel->ullLastChange)));
return STATUS_SUCCESS;
}
VOID
IpIpTimerRoutine(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
/*++
Routine Description:
The DPC routine associated with the timer.
Locks:
Arguments:
Dpc
DeferredContext
SystemArgument1
SystemArgument2
Return Value:
NONE
--*/
{
PLIST_ENTRY pleNode;
LARGE_INTEGER liDueTime;
RtAcquireSpinLockAtDpcLevel(&g_rlStateLock);
if(g_dwDriverState != DRIVER_STARTED)
{
RtReleaseSpinLockFromDpcLevel(&g_rlStateLock);
return;
}
RtReleaseSpinLockFromDpcLevel(&g_rlStateLock);
EnterReaderAtDpcLevel(&g_rwlTunnelLock);
for(pleNode = g_leTunnelList.Flink;
pleNode isnot &g_leTunnelList;
pleNode = pleNode->Flink)
{
PTUNNEL pTunnel;
ULONGLONG ullCurrentTime;
BOOLEAN bChange;
pTunnel = CONTAINING_RECORD(pleNode,
TUNNEL,
leTunnelLink);
//
// Lock, but dont refcount the tunnel.
// The ref is not needed since, we have the tunnel list lock and
// that means the tunnel cant be remove from the list, which keeps
// a refcount for us
//
RtAcquireSpinLockAtDpcLevel(&(pTunnel->rlLock));
if(GetAdminState(pTunnel) isnot IF_ADMIN_STATUS_UP)
{
//
// TODO: maybe we should move admin state under the tunnel list
// lock? Possibly a perf improvement
//
RtReleaseSpinLockFromDpcLevel(&(pTunnel->rlLock));
continue;
}
KeQueryTickCount((PLARGE_INTEGER)&ullCurrentTime);
//
// If the tunnel has a local address and either (i) the counter has
// rolled over or (ii) more than the change period time has passed
// - update its mtu and reachability info
// The change period is different depending on whether the tunnel is
// UP or DOWN
//
if(pTunnel->dwOperState is IF_OPER_STATUS_OPERATIONAL)
{
bChange = ((ullCurrentTime - pTunnel->ullLastChange) >=
SECS_TO_TICKS(UP_TO_DOWN_CHANGE_PERIOD));
}
else
{
bChange = ((ullCurrentTime - pTunnel->ullLastChange) >=
SECS_TO_TICKS(DOWN_TO_UP_CHANGE_PERIOD));
}
if((pTunnel->dwAdminState & TS_ADDRESS_PRESENT) and
((pTunnel->ullLastChange > ullCurrentTime) or
bChange))
{
#if DBG
Trace(TUNN, INFO,
("IpIpTimerRoutine: Updating %s\n",
pTunnel->asDebugBindName.Buffer));
#endif
//
// If everything is good, it will set the OperState to up
//
UpdateMtuAndReachability(pTunnel);
}
RtReleaseSpinLockFromDpcLevel(&(pTunnel->rlLock));
}
ExitReaderFromDpcLevel(&g_rwlTunnelLock);
liDueTime = RtlEnlargedUnsignedMultiply(TIMER_IN_MILLISECS,
SYS_UNITS_IN_ONE_MILLISEC);
liDueTime = RtlLargeIntegerNegate(liDueTime);
KeSetTimerEx(&g_ktTimer,
liDueTime,
0,
&g_kdTimerDpc);
return;
}