windows-nt/Source/XPSP1/NT/net/rras/ip/nat/timer.c

526 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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);
}
}