526 lines
13 KiB
C
526 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997 Microsoft Corporation
|
|||
|
|
|||
|
Module:
|
|||
|
|
|||
|
timer.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Contains code for the NAT's periodic-timer routine.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Abolade Gbadegesin (t-abolag) 22-July-1997
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
//
|
|||
|
// Defines the interval at which the timer fires, in 100-nanosecond intervals
|
|||
|
//
|
|||
|
|
|||
|
#define TIMER_INTERVAL (60 * 1000 * 1000 * 10)
|
|||
|
|
|||
|
//
|
|||
|
// DPC object for stress-triggered invocations of NatTimerRoutine
|
|||
|
//
|
|||
|
|
|||
|
KDPC CleanupDpcObject;
|
|||
|
|
|||
|
//
|
|||
|
// Flag indicating whether stress-triggered cleanup has been scheduled.
|
|||
|
//
|
|||
|
|
|||
|
ULONG CleanupDpcPending;
|
|||
|
|
|||
|
//
|
|||
|
// Return-value of KeQueryTimeIncrement, used for normalizing tick-counts
|
|||
|
//
|
|||
|
|
|||
|
ULONG TimeIncrement;
|
|||
|
|
|||
|
//
|
|||
|
// DPC object for NatTimerRoutine
|
|||
|
//
|
|||
|
|
|||
|
KDPC TimerDpcObject;
|
|||
|
|
|||
|
//
|
|||
|
// Timer object for NatTimerRoutine
|
|||
|
//
|
|||
|
|
|||
|
KTIMER TimerObject;
|
|||
|
|
|||
|
//
|
|||
|
// FORWARD DECLARATIONS
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
NatCleanupDpcRoutine(
|
|||
|
PKDPC Dpc,
|
|||
|
PVOID DeferredContext,
|
|||
|
PVOID SystemArgument1,
|
|||
|
PVOID SystemArgument2
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
NatTimerRoutine(
|
|||
|
PKDPC Dpc,
|
|||
|
PVOID DeferredContext,
|
|||
|
PVOID SystemArgument1,
|
|||
|
PVOID SystemArgument2
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatCleanupDpcRoutine(
|
|||
|
PKDPC Dpc,
|
|||
|
PVOID DeferredContext,
|
|||
|
PVOID SystemArgument1,
|
|||
|
PVOID SystemArgument2
|
|||
|
)
|
|||
|
{
|
|||
|
KIRQL Irql;
|
|||
|
PLIST_ENTRY Link;
|
|||
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|||
|
|
|||
|
KeAcquireSpinLock(&MappingLock, &Irql);
|
|||
|
for (Link = MappingList.Flink; Link != &MappingList; Link = Link->Flink) {
|
|||
|
Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, Link);
|
|||
|
if (NAT_MAPPING_EXPIRED(Mapping)) {
|
|||
|
Link = Link->Blink;
|
|||
|
NatDeleteMapping(Mapping);
|
|||
|
}
|
|||
|
}
|
|||
|
KeReleaseSpinLock(&MappingLock, Irql);
|
|||
|
|
|||
|
InterlockedExchange(&CleanupDpcPending, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatInitializeTimerManagement(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to initialize the timer-management module,
|
|||
|
in preparation for active operation.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CALLTRACE(("NatInitializeTimerManagement\n"));
|
|||
|
TimeIncrement = KeQueryTimeIncrement();
|
|||
|
KeInitializeDpc(&TimerDpcObject, NatTimerRoutine, NULL);
|
|||
|
KeInitializeTimer(&TimerObject);
|
|||
|
CleanupDpcPending = FALSE;
|
|||
|
KeInitializeDpc(&CleanupDpcObject, NatCleanupDpcRoutine, NULL);
|
|||
|
} // NatInitializeTimerManagement
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatShutdownTimerManagement(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to shutdown the timer management module.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CALLTRACE(("NatShutdownTimerManagement\n"));
|
|||
|
NatStopTimer();
|
|||
|
} // NatShutdownTimerManagement
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatStartTimer(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked to start the periodic timer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LARGE_INTEGER DueTime;
|
|||
|
|
|||
|
//
|
|||
|
// Launch the periodic timer
|
|||
|
//
|
|||
|
|
|||
|
DueTime.LowPart = TIMER_INTERVAL;
|
|||
|
DueTime.HighPart = 0;
|
|||
|
DueTime = RtlLargeIntegerNegate(DueTime);
|
|||
|
KeSetTimerEx(
|
|||
|
&TimerObject,
|
|||
|
DueTime,
|
|||
|
TIMER_INTERVAL / 10000,
|
|||
|
&TimerDpcObject
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatStopTimer(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked to stop the periodic timer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KeCancelTimer(&TimerObject);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatTimerRoutine(
|
|||
|
PKDPC Dpc,
|
|||
|
PVOID DeferredContext,
|
|||
|
PVOID SystemArgument1,
|
|||
|
PVOID SystemArgument2
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked periodically to garbage-collect expired mappings.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - associated DPC object
|
|||
|
|
|||
|
DeferredContext - unused.
|
|||
|
|
|||
|
SystemArgument1 - unused.
|
|||
|
|
|||
|
SystemArgument2 - unused.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LONG64 CurrentTime;
|
|||
|
PNAT_EDITOR Editor;
|
|||
|
PNAT_ICMP_MAPPING IcmpMapping;
|
|||
|
PNAT_INTERFACE Interfacep;
|
|||
|
PNAT_IP_MAPPING IpMapping;
|
|||
|
KIRQL Irql;
|
|||
|
PLIST_ENTRY Link;
|
|||
|
PNAT_DYNAMIC_MAPPING Mapping;
|
|||
|
PNAT_PPTP_MAPPING PptpMapping;
|
|||
|
UCHAR Protocol;
|
|||
|
PRTL_SPLAY_LINKS SLink;
|
|||
|
PNAT_TICKET Ticketp;
|
|||
|
LONG64 Timeout;
|
|||
|
LONG64 TcpMinAccessTime;
|
|||
|
LONG64 UdpMinAccessTime;
|
|||
|
|
|||
|
CALLTRACE(("NatTimerRoutine\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Compute the minimum values allowed in TCP/UDP 'LastAccessTime' fields;
|
|||
|
// any mappings last accessed before these thresholds will be eliminated.
|
|||
|
//
|
|||
|
|
|||
|
KeQueryTickCount((PLARGE_INTEGER)&CurrentTime);
|
|||
|
TcpMinAccessTime = CurrentTime - SECONDS_TO_TICKS(TcpTimeoutSeconds);
|
|||
|
UdpMinAccessTime = CurrentTime - SECONDS_TO_TICKS(UdpTimeoutSeconds);
|
|||
|
|
|||
|
//
|
|||
|
// Update mapping statistics and clean out expired mappings,
|
|||
|
// using the above precomputed minimum access times
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLock(&MappingLock, &Irql);
|
|||
|
for (Link = MappingList.Flink; Link != &MappingList; Link = Link->Flink) {
|
|||
|
|
|||
|
Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, Link);
|
|||
|
NatUpdateStatisticsMapping(Mapping);
|
|||
|
|
|||
|
//
|
|||
|
// Don't check for expiration if the mapping is marked no-timeout;
|
|||
|
// however, if it is detached from its director, then go ahead
|
|||
|
// with the expiration-check.
|
|||
|
//
|
|||
|
|
|||
|
if (!NAT_MAPPING_EXPIRED(Mapping) && NAT_MAPPING_NO_TIMEOUT(Mapping) &&
|
|||
|
Mapping->Director) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See if the mapping has expired
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
|
|||
|
Protocol = MAPPING_PROTOCOL(Mapping->SourceKey[NatForwardPath]);
|
|||
|
if (!NAT_MAPPING_EXPIRED(Mapping)) {
|
|||
|
//
|
|||
|
// The mapping is not explicitly marked for expiration;
|
|||
|
// see if its last access time is too long ago
|
|||
|
//
|
|||
|
if (Protocol == NAT_PROTOCOL_TCP) {
|
|||
|
if (!NAT_MAPPING_INBOUND(Mapping)) {
|
|||
|
if ((Mapping->Flags & NAT_MAPPING_FLAG_FWD_SYN)
|
|||
|
&& !(Mapping->Flags & NAT_MAPPING_FLAG_REV_SYN)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is an outbound connection for which we've seen
|
|||
|
// the outbound SYN (which means we've been tracking
|
|||
|
// it from the beginning), but not an inbound SYN. We
|
|||
|
// want to use a smaller timeout here so that we may
|
|||
|
// reclaim memory for mappings created for connection
|
|||
|
// attempts to non-existant servers. (A large number
|
|||
|
// of these types of mappings would exist if a machine
|
|||
|
// on the private network is performing some sort of a
|
|||
|
// network scan; e.g., a machine infected w/ nimda.)
|
|||
|
//
|
|||
|
|
|||
|
if (Mapping->LastAccessTime >= UdpMinAccessTime) {
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Mapping->LastAccessTime >= TcpMinAccessTime) {
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|||
|
continue;
|
|||
|
}
|
|||
|
} else if (!NAT_MAPPING_TCP_OPEN(Mapping)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is an inbound connection for which we have not
|
|||
|
// yet completed the 3-way handshake. We want to use
|
|||
|
// a shorter timeout here to reduce memory consumption
|
|||
|
// in those cases where someone is performing a synflood
|
|||
|
// against us.
|
|||
|
//
|
|||
|
|
|||
|
if (Mapping->LastAccessTime >= UdpMinAccessTime) {
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|||
|
continue;
|
|||
|
}
|
|||
|
} else if (Mapping->LastAccessTime >= TcpMinAccessTime) {
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
if (Mapping->LastAccessTime >= UdpMinAccessTime) {
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
|
|||
|
|
|||
|
//
|
|||
|
// The mapping has expired; remove it
|
|||
|
//
|
|||
|
|
|||
|
TRACE(
|
|||
|
MAPPING, (
|
|||
|
"NatTimerRoutine: >Source,Destination=%016I64X:%016I64X\n",
|
|||
|
Mapping->SourceKey[NatForwardPath],
|
|||
|
Mapping->DestinationKey[NatForwardPath]
|
|||
|
));
|
|||
|
TRACE(
|
|||
|
MAPPING, (
|
|||
|
"NatTimerRoutine: <Source,Destination=%016I64X:%016I64X\n",
|
|||
|
Mapping->SourceKey[NatReversePath],
|
|||
|
Mapping->DestinationKey[NatReversePath]
|
|||
|
));
|
|||
|
|
|||
|
Link = Link->Blink;
|
|||
|
NatDeleteMapping(Mapping);
|
|||
|
}
|
|||
|
KeReleaseSpinLockFromDpcLevel(&MappingLock);
|
|||
|
|
|||
|
//
|
|||
|
// Traverse the PPTP-mapping list and remove all expired entries.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&PptpMappingLock);
|
|||
|
for (Link = PptpMappingList[NatInboundDirection].Flink;
|
|||
|
Link != &PptpMappingList[NatInboundDirection]; Link = Link->Flink) {
|
|||
|
PptpMapping =
|
|||
|
CONTAINING_RECORD(
|
|||
|
Link, NAT_PPTP_MAPPING, Link[NatInboundDirection]
|
|||
|
);
|
|||
|
if (!NAT_PPTP_DISCONNECTED(PptpMapping) ||
|
|||
|
PptpMapping->LastAccessTime >= UdpMinAccessTime) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
Link = Link->Blink;
|
|||
|
RemoveEntryList(&PptpMapping->Link[NatInboundDirection]);
|
|||
|
RemoveEntryList(&PptpMapping->Link[NatOutboundDirection]);
|
|||
|
TRACE(
|
|||
|
MAPPING, ("NatTimerRoutine: Pptp=%016I64X:%016I64X:%d:%d:%d\n",
|
|||
|
PptpMapping->PrivateKey,
|
|||
|
PptpMapping->PublicKey,
|
|||
|
PptpMapping->PrivateCallId,
|
|||
|
PptpMapping->PublicCallId,
|
|||
|
PptpMapping->RemoteCallId
|
|||
|
));
|
|||
|
FREE_PPTP_BLOCK(PptpMapping);
|
|||
|
}
|
|||
|
KeReleaseSpinLockFromDpcLevel(&PptpMappingLock);
|
|||
|
|
|||
|
//
|
|||
|
// Traverse the ICMP-mapping list and remove each expired entry.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
|
|||
|
for (Link = IcmpMappingList[NatInboundDirection].Flink;
|
|||
|
Link != &IcmpMappingList[NatInboundDirection]; Link = Link->Flink) {
|
|||
|
IcmpMapping =
|
|||
|
CONTAINING_RECORD(
|
|||
|
Link, NAT_ICMP_MAPPING, Link[NatInboundDirection]
|
|||
|
);
|
|||
|
if (IcmpMapping->LastAccessTime >= UdpMinAccessTime) { continue; }
|
|||
|
Link = Link->Blink;
|
|||
|
RemoveEntryList(&IcmpMapping->Link[NatInboundDirection]);
|
|||
|
RemoveEntryList(&IcmpMapping->Link[NatOutboundDirection]);
|
|||
|
TRACE(
|
|||
|
MAPPING,
|
|||
|
("NatTimerRoutine: Icmp=%016I64X:%04X::%016I64X:%04X\n",
|
|||
|
IcmpMapping->PrivateKey, IcmpMapping->PrivateId,
|
|||
|
IcmpMapping->PublicKey, IcmpMapping->PublicId
|
|||
|
));
|
|||
|
FREE_ICMP_BLOCK(IcmpMapping);
|
|||
|
}
|
|||
|
KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
|
|||
|
|
|||
|
//
|
|||
|
// Traverse the interface's IP-mapping list
|
|||
|
// and remove each expired entry.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&IpMappingLock);
|
|||
|
for (Link = IpMappingList[NatInboundDirection].Flink;
|
|||
|
Link != &IpMappingList[NatInboundDirection]; Link = Link->Flink) {
|
|||
|
IpMapping =
|
|||
|
CONTAINING_RECORD(
|
|||
|
Link, NAT_IP_MAPPING, Link[NatInboundDirection]
|
|||
|
);
|
|||
|
if (IpMapping->LastAccessTime >= UdpMinAccessTime) { continue; }
|
|||
|
Link = Link->Blink;
|
|||
|
RemoveEntryList(&IpMapping->Link[NatInboundDirection]);
|
|||
|
RemoveEntryList(&IpMapping->Link[NatOutboundDirection]);
|
|||
|
TRACE(
|
|||
|
MAPPING, (
|
|||
|
"NatTimerRoutine: Ip=%d:%016I64X:%016I64X\n",
|
|||
|
IpMapping->Protocol, IpMapping->PrivateKey, IpMapping->PublicKey
|
|||
|
));
|
|||
|
FREE_IP_BLOCK(IpMapping);
|
|||
|
}
|
|||
|
KeReleaseSpinLockFromDpcLevel(&IpMappingLock);
|
|||
|
|
|||
|
//
|
|||
|
// Garbage collect all interfaces' structures
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
|
|||
|
|
|||
|
for (Link = InterfaceList.Flink; Link != &InterfaceList;
|
|||
|
Link = Link->Flink) {
|
|||
|
|
|||
|
Interfacep = CONTAINING_RECORD(Link, NAT_INTERFACE, Link);
|
|||
|
|
|||
|
//
|
|||
|
// Traverse the interface's ticket list
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
|||
|
for (Link = Interfacep->TicketList.Flink;
|
|||
|
Link != &Interfacep->TicketList; Link = Link->Flink) {
|
|||
|
Ticketp = CONTAINING_RECORD(Link, NAT_TICKET, Link);
|
|||
|
if (NAT_TICKET_PERSISTENT(Ticketp)) { continue; }
|
|||
|
if (Ticketp->LastAccessTime >= UdpMinAccessTime) { continue; }
|
|||
|
Link = Link->Blink;
|
|||
|
NatDeleteTicket(Interfacep, Ticketp);
|
|||
|
}
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
|||
|
Link = &Interfacep->Link;
|
|||
|
}
|
|||
|
KeReleaseSpinLock(&InterfaceLock, Irql);
|
|||
|
return;
|
|||
|
|
|||
|
} // NatTimerRoutine
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NatTriggerTimer(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
if (!InterlockedCompareExchange(&CleanupDpcPending, TRUE, FALSE)) {
|
|||
|
#if DBG
|
|||
|
DbgPrint("NatTriggerTimer: scheduling DPC\n");
|
|||
|
#endif
|
|||
|
KeInsertQueueDpc(&CleanupDpcObject, NULL, NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|