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