Source code of Windows XP (NT5)
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.

525 lines
13 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module:
  4. timer.c
  5. Abstract:
  6. Contains code for the NAT's periodic-timer routine.
  7. Author:
  8. Abolade Gbadegesin (t-abolag) 22-July-1997
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. //
  14. // Defines the interval at which the timer fires, in 100-nanosecond intervals
  15. //
  16. #define TIMER_INTERVAL (60 * 1000 * 1000 * 10)
  17. //
  18. // DPC object for stress-triggered invocations of NatTimerRoutine
  19. //
  20. KDPC CleanupDpcObject;
  21. //
  22. // Flag indicating whether stress-triggered cleanup has been scheduled.
  23. //
  24. ULONG CleanupDpcPending;
  25. //
  26. // Return-value of KeQueryTimeIncrement, used for normalizing tick-counts
  27. //
  28. ULONG TimeIncrement;
  29. //
  30. // DPC object for NatTimerRoutine
  31. //
  32. KDPC TimerDpcObject;
  33. //
  34. // Timer object for NatTimerRoutine
  35. //
  36. KTIMER TimerObject;
  37. //
  38. // FORWARD DECLARATIONS
  39. //
  40. VOID
  41. NatCleanupDpcRoutine(
  42. PKDPC Dpc,
  43. PVOID DeferredContext,
  44. PVOID SystemArgument1,
  45. PVOID SystemArgument2
  46. );
  47. VOID
  48. NatTimerRoutine(
  49. PKDPC Dpc,
  50. PVOID DeferredContext,
  51. PVOID SystemArgument1,
  52. PVOID SystemArgument2
  53. );
  54. VOID
  55. NatCleanupDpcRoutine(
  56. PKDPC Dpc,
  57. PVOID DeferredContext,
  58. PVOID SystemArgument1,
  59. PVOID SystemArgument2
  60. )
  61. {
  62. KIRQL Irql;
  63. PLIST_ENTRY Link;
  64. PNAT_DYNAMIC_MAPPING Mapping;
  65. KeAcquireSpinLock(&MappingLock, &Irql);
  66. for (Link = MappingList.Flink; Link != &MappingList; Link = Link->Flink) {
  67. Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, Link);
  68. if (NAT_MAPPING_EXPIRED(Mapping)) {
  69. Link = Link->Blink;
  70. NatDeleteMapping(Mapping);
  71. }
  72. }
  73. KeReleaseSpinLock(&MappingLock, Irql);
  74. InterlockedExchange(&CleanupDpcPending, FALSE);
  75. }
  76. VOID
  77. NatInitializeTimerManagement(
  78. VOID
  79. )
  80. /*++
  81. Routine Description:
  82. This routine is called to initialize the timer-management module,
  83. in preparation for active operation.
  84. Arguments:
  85. none.
  86. Return Value:
  87. none.
  88. --*/
  89. {
  90. CALLTRACE(("NatInitializeTimerManagement\n"));
  91. TimeIncrement = KeQueryTimeIncrement();
  92. KeInitializeDpc(&TimerDpcObject, NatTimerRoutine, NULL);
  93. KeInitializeTimer(&TimerObject);
  94. CleanupDpcPending = FALSE;
  95. KeInitializeDpc(&CleanupDpcObject, NatCleanupDpcRoutine, NULL);
  96. } // NatInitializeTimerManagement
  97. VOID
  98. NatShutdownTimerManagement(
  99. VOID
  100. )
  101. /*++
  102. Routine Description:
  103. This routine is called to shutdown the timer management module.
  104. Arguments:
  105. none.
  106. Return Value:
  107. none.
  108. --*/
  109. {
  110. CALLTRACE(("NatShutdownTimerManagement\n"));
  111. NatStopTimer();
  112. } // NatShutdownTimerManagement
  113. VOID
  114. NatStartTimer(
  115. VOID
  116. )
  117. /*++
  118. Routine Description:
  119. This routine is invoked to start the periodic timer.
  120. Arguments:
  121. none.
  122. Return Value:
  123. none.
  124. --*/
  125. {
  126. LARGE_INTEGER DueTime;
  127. //
  128. // Launch the periodic timer
  129. //
  130. DueTime.LowPart = TIMER_INTERVAL;
  131. DueTime.HighPart = 0;
  132. DueTime = RtlLargeIntegerNegate(DueTime);
  133. KeSetTimerEx(
  134. &TimerObject,
  135. DueTime,
  136. TIMER_INTERVAL / 10000,
  137. &TimerDpcObject
  138. );
  139. }
  140. VOID
  141. NatStopTimer(
  142. VOID
  143. )
  144. /*++
  145. Routine Description:
  146. This routine is invoked to stop the periodic timer.
  147. Arguments:
  148. none.
  149. Return Value:
  150. none.
  151. --*/
  152. {
  153. KeCancelTimer(&TimerObject);
  154. }
  155. VOID
  156. NatTimerRoutine(
  157. PKDPC Dpc,
  158. PVOID DeferredContext,
  159. PVOID SystemArgument1,
  160. PVOID SystemArgument2
  161. )
  162. /*++
  163. Routine Description:
  164. This routine is invoked periodically to garbage-collect expired mappings.
  165. Arguments:
  166. Dpc - associated DPC object
  167. DeferredContext - unused.
  168. SystemArgument1 - unused.
  169. SystemArgument2 - unused.
  170. Return Value:
  171. none.
  172. --*/
  173. {
  174. LONG64 CurrentTime;
  175. PNAT_EDITOR Editor;
  176. PNAT_ICMP_MAPPING IcmpMapping;
  177. PNAT_INTERFACE Interfacep;
  178. PNAT_IP_MAPPING IpMapping;
  179. KIRQL Irql;
  180. PLIST_ENTRY Link;
  181. PNAT_DYNAMIC_MAPPING Mapping;
  182. PNAT_PPTP_MAPPING PptpMapping;
  183. UCHAR Protocol;
  184. PRTL_SPLAY_LINKS SLink;
  185. PNAT_TICKET Ticketp;
  186. LONG64 Timeout;
  187. LONG64 TcpMinAccessTime;
  188. LONG64 UdpMinAccessTime;
  189. CALLTRACE(("NatTimerRoutine\n"));
  190. //
  191. // Compute the minimum values allowed in TCP/UDP 'LastAccessTime' fields;
  192. // any mappings last accessed before these thresholds will be eliminated.
  193. //
  194. KeQueryTickCount((PLARGE_INTEGER)&CurrentTime);
  195. TcpMinAccessTime = CurrentTime - SECONDS_TO_TICKS(TcpTimeoutSeconds);
  196. UdpMinAccessTime = CurrentTime - SECONDS_TO_TICKS(UdpTimeoutSeconds);
  197. //
  198. // Update mapping statistics and clean out expired mappings,
  199. // using the above precomputed minimum access times
  200. //
  201. KeAcquireSpinLock(&MappingLock, &Irql);
  202. for (Link = MappingList.Flink; Link != &MappingList; Link = Link->Flink) {
  203. Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, Link);
  204. NatUpdateStatisticsMapping(Mapping);
  205. //
  206. // Don't check for expiration if the mapping is marked no-timeout;
  207. // however, if it is detached from its director, then go ahead
  208. // with the expiration-check.
  209. //
  210. if (!NAT_MAPPING_EXPIRED(Mapping) && NAT_MAPPING_NO_TIMEOUT(Mapping) &&
  211. Mapping->Director) {
  212. continue;
  213. }
  214. //
  215. // See if the mapping has expired
  216. //
  217. KeAcquireSpinLockAtDpcLevel(&Mapping->Lock);
  218. Protocol = MAPPING_PROTOCOL(Mapping->SourceKey[NatForwardPath]);
  219. if (!NAT_MAPPING_EXPIRED(Mapping)) {
  220. //
  221. // The mapping is not explicitly marked for expiration;
  222. // see if its last access time is too long ago
  223. //
  224. if (Protocol == NAT_PROTOCOL_TCP) {
  225. if (!NAT_MAPPING_INBOUND(Mapping)) {
  226. if ((Mapping->Flags & NAT_MAPPING_FLAG_FWD_SYN)
  227. && !(Mapping->Flags & NAT_MAPPING_FLAG_REV_SYN)) {
  228. //
  229. // This is an outbound connection for which we've seen
  230. // the outbound SYN (which means we've been tracking
  231. // it from the beginning), but not an inbound SYN. We
  232. // want to use a smaller timeout here so that we may
  233. // reclaim memory for mappings created for connection
  234. // attempts to non-existant servers. (A large number
  235. // of these types of mappings would exist if a machine
  236. // on the private network is performing some sort of a
  237. // network scan; e.g., a machine infected w/ nimda.)
  238. //
  239. if (Mapping->LastAccessTime >= UdpMinAccessTime) {
  240. KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
  241. continue;
  242. }
  243. }
  244. else if (Mapping->LastAccessTime >= TcpMinAccessTime) {
  245. KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
  246. continue;
  247. }
  248. } else if (!NAT_MAPPING_TCP_OPEN(Mapping)) {
  249. //
  250. // This is an inbound connection for which we have not
  251. // yet completed the 3-way handshake. We want to use
  252. // a shorter timeout here to reduce memory consumption
  253. // in those cases where someone is performing a synflood
  254. // against us.
  255. //
  256. if (Mapping->LastAccessTime >= UdpMinAccessTime) {
  257. KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
  258. continue;
  259. }
  260. } else if (Mapping->LastAccessTime >= TcpMinAccessTime) {
  261. KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
  262. continue;
  263. }
  264. }
  265. else
  266. if (Mapping->LastAccessTime >= UdpMinAccessTime) {
  267. KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
  268. continue;
  269. }
  270. }
  271. KeReleaseSpinLockFromDpcLevel(&Mapping->Lock);
  272. //
  273. // The mapping has expired; remove it
  274. //
  275. TRACE(
  276. MAPPING, (
  277. "NatTimerRoutine: >Source,Destination=%016I64X:%016I64X\n",
  278. Mapping->SourceKey[NatForwardPath],
  279. Mapping->DestinationKey[NatForwardPath]
  280. ));
  281. TRACE(
  282. MAPPING, (
  283. "NatTimerRoutine: <Source,Destination=%016I64X:%016I64X\n",
  284. Mapping->SourceKey[NatReversePath],
  285. Mapping->DestinationKey[NatReversePath]
  286. ));
  287. Link = Link->Blink;
  288. NatDeleteMapping(Mapping);
  289. }
  290. KeReleaseSpinLockFromDpcLevel(&MappingLock);
  291. //
  292. // Traverse the PPTP-mapping list and remove all expired entries.
  293. //
  294. KeAcquireSpinLockAtDpcLevel(&PptpMappingLock);
  295. for (Link = PptpMappingList[NatInboundDirection].Flink;
  296. Link != &PptpMappingList[NatInboundDirection]; Link = Link->Flink) {
  297. PptpMapping =
  298. CONTAINING_RECORD(
  299. Link, NAT_PPTP_MAPPING, Link[NatInboundDirection]
  300. );
  301. if (!NAT_PPTP_DISCONNECTED(PptpMapping) ||
  302. PptpMapping->LastAccessTime >= UdpMinAccessTime) {
  303. continue;
  304. }
  305. Link = Link->Blink;
  306. RemoveEntryList(&PptpMapping->Link[NatInboundDirection]);
  307. RemoveEntryList(&PptpMapping->Link[NatOutboundDirection]);
  308. TRACE(
  309. MAPPING, ("NatTimerRoutine: Pptp=%016I64X:%016I64X:%d:%d:%d\n",
  310. PptpMapping->PrivateKey,
  311. PptpMapping->PublicKey,
  312. PptpMapping->PrivateCallId,
  313. PptpMapping->PublicCallId,
  314. PptpMapping->RemoteCallId
  315. ));
  316. FREE_PPTP_BLOCK(PptpMapping);
  317. }
  318. KeReleaseSpinLockFromDpcLevel(&PptpMappingLock);
  319. //
  320. // Traverse the ICMP-mapping list and remove each expired entry.
  321. //
  322. KeAcquireSpinLockAtDpcLevel(&IcmpMappingLock);
  323. for (Link = IcmpMappingList[NatInboundDirection].Flink;
  324. Link != &IcmpMappingList[NatInboundDirection]; Link = Link->Flink) {
  325. IcmpMapping =
  326. CONTAINING_RECORD(
  327. Link, NAT_ICMP_MAPPING, Link[NatInboundDirection]
  328. );
  329. if (IcmpMapping->LastAccessTime >= UdpMinAccessTime) { continue; }
  330. Link = Link->Blink;
  331. RemoveEntryList(&IcmpMapping->Link[NatInboundDirection]);
  332. RemoveEntryList(&IcmpMapping->Link[NatOutboundDirection]);
  333. TRACE(
  334. MAPPING,
  335. ("NatTimerRoutine: Icmp=%016I64X:%04X::%016I64X:%04X\n",
  336. IcmpMapping->PrivateKey, IcmpMapping->PrivateId,
  337. IcmpMapping->PublicKey, IcmpMapping->PublicId
  338. ));
  339. FREE_ICMP_BLOCK(IcmpMapping);
  340. }
  341. KeReleaseSpinLockFromDpcLevel(&IcmpMappingLock);
  342. //
  343. // Traverse the interface's IP-mapping list
  344. // and remove each expired entry.
  345. //
  346. KeAcquireSpinLockAtDpcLevel(&IpMappingLock);
  347. for (Link = IpMappingList[NatInboundDirection].Flink;
  348. Link != &IpMappingList[NatInboundDirection]; Link = Link->Flink) {
  349. IpMapping =
  350. CONTAINING_RECORD(
  351. Link, NAT_IP_MAPPING, Link[NatInboundDirection]
  352. );
  353. if (IpMapping->LastAccessTime >= UdpMinAccessTime) { continue; }
  354. Link = Link->Blink;
  355. RemoveEntryList(&IpMapping->Link[NatInboundDirection]);
  356. RemoveEntryList(&IpMapping->Link[NatOutboundDirection]);
  357. TRACE(
  358. MAPPING, (
  359. "NatTimerRoutine: Ip=%d:%016I64X:%016I64X\n",
  360. IpMapping->Protocol, IpMapping->PrivateKey, IpMapping->PublicKey
  361. ));
  362. FREE_IP_BLOCK(IpMapping);
  363. }
  364. KeReleaseSpinLockFromDpcLevel(&IpMappingLock);
  365. //
  366. // Garbage collect all interfaces' structures
  367. //
  368. KeAcquireSpinLockAtDpcLevel(&InterfaceLock);
  369. for (Link = InterfaceList.Flink; Link != &InterfaceList;
  370. Link = Link->Flink) {
  371. Interfacep = CONTAINING_RECORD(Link, NAT_INTERFACE, Link);
  372. //
  373. // Traverse the interface's ticket list
  374. //
  375. KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
  376. for (Link = Interfacep->TicketList.Flink;
  377. Link != &Interfacep->TicketList; Link = Link->Flink) {
  378. Ticketp = CONTAINING_RECORD(Link, NAT_TICKET, Link);
  379. if (NAT_TICKET_PERSISTENT(Ticketp)) { continue; }
  380. if (Ticketp->LastAccessTime >= UdpMinAccessTime) { continue; }
  381. Link = Link->Blink;
  382. NatDeleteTicket(Interfacep, Ticketp);
  383. }
  384. KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
  385. Link = &Interfacep->Link;
  386. }
  387. KeReleaseSpinLock(&InterfaceLock, Irql);
  388. return;
  389. } // NatTimerRoutine
  390. VOID
  391. NatTriggerTimer(
  392. VOID
  393. )
  394. {
  395. if (!InterlockedCompareExchange(&CleanupDpcPending, TRUE, FALSE)) {
  396. #if DBG
  397. DbgPrint("NatTriggerTimer: scheduling DPC\n");
  398. #endif
  399. KeInsertQueueDpc(&CleanupDpcObject, NULL, NULL);
  400. }
  401. }