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.

848 lines
18 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. director.c
  5. Abstract:
  6. This module contains the code for director management.
  7. Author:
  8. Abolade Gbadegesin (t-abolag) 16-Feb-1998
  9. Revision History:
  10. Abolade Gbadegesin (aboladeg) 19-Apr-1998
  11. Added support for wildcards in protocol/port of a director registration.
  12. --*/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. //
  16. // GLOBAL DATA DEFINITIONS
  17. //
  18. //
  19. // Count of NAT directors
  20. //
  21. ULONG DirectorCount;
  22. //
  23. // List of NAT directors
  24. //
  25. LIST_ENTRY DirectorList;
  26. //
  27. // Spin-lock controlling access to 'DirectorList'
  28. //
  29. KSPIN_LOCK DirectorLock;
  30. //
  31. // Spin-lock controlling access to the 'MappingList' field of all directors
  32. //
  33. KSPIN_LOCK DirectorMappingLock;
  34. VOID
  35. NatCleanupDirector(
  36. PNAT_DIRECTOR Director
  37. )
  38. /*++
  39. Routine Description:
  40. Called to perform final cleanup for an director.
  41. Arguments:
  42. Director - the director to be cleaned up.
  43. Return Value:
  44. none.
  45. --*/
  46. {
  47. KIRQL Irql;
  48. PLIST_ENTRY Link;
  49. PNAT_DYNAMIC_MAPPING Mapping;
  50. CALLTRACE(("NatCleanupDirector\n"));
  51. //
  52. // Detach the director from all of its mappings
  53. //
  54. KeAcquireSpinLock(&DirectorLock, &Irql);
  55. KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock);
  56. for (Link = Director->MappingList.Flink; Link != &Director->MappingList;
  57. Link = Link->Flink) {
  58. Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, DirectorLink);
  59. Link = Link->Blink;
  60. NatMappingDetachDirector(
  61. Director,
  62. Mapping->DirectorContext,
  63. Mapping,
  64. NatCleanupDirectorDeleteReason
  65. );
  66. }
  67. KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock);
  68. KeReleaseSpinLock(&DirectorLock, Irql);
  69. if (Director->UnloadHandler) {
  70. Director->UnloadHandler(Director->Context);
  71. }
  72. ExFreePool(Director);
  73. } // NatCleanupDirector
  74. NTSTATUS
  75. NatCreateDirector(
  76. PIP_NAT_REGISTER_DIRECTOR RegisterContext
  77. )
  78. /*++
  79. Routine Description:
  80. This routine is invoked when an director attempts to register.
  81. It handles creation of a context-block for the director.
  82. Arguments:
  83. RegisterContext - information about the registering director
  84. Return Value:
  85. NTSTATUS - status code.
  86. --*/
  87. {
  88. PNAT_DIRECTOR Director;
  89. PLIST_ENTRY InsertionPoint;
  90. KIRQL Irql;
  91. ULONG Key;
  92. CALLTRACE(("NatCreateDirector\n"));
  93. RegisterContext->DirectorHandle = NULL;
  94. //
  95. // Validate the registration information
  96. //
  97. if (!RegisterContext->QueryHandler &&
  98. !RegisterContext->CreateHandler &&
  99. !RegisterContext->DeleteHandler &&
  100. !RegisterContext->UnloadHandler) {
  101. ERROR(("NatCreateDirector: bad argument\n"));
  102. return STATUS_INVALID_PARAMETER;
  103. }
  104. //
  105. // Allocate a new director-struct
  106. //
  107. Director =
  108. ExAllocatePoolWithTag(
  109. NonPagedPool, sizeof(NAT_DIRECTOR), NAT_TAG_DIRECTOR
  110. );
  111. if (!Director) {
  112. ERROR(("NatCreateDirector: allocation failed\n"));
  113. return STATUS_NO_MEMORY;
  114. }
  115. KeInitializeSpinLock(&Director->Lock);
  116. Director->ReferenceCount = 1;
  117. Director->Flags = RegisterContext->Flags;
  118. Director->Key =
  119. MAKE_DIRECTOR_KEY(RegisterContext->Protocol, RegisterContext->Port);
  120. InitializeListHead(&Director->MappingList);
  121. Director->Context = RegisterContext->DirectorContext;
  122. Director->CreateHandler = RegisterContext->CreateHandler;
  123. Director->DeleteHandler = RegisterContext->DeleteHandler;
  124. Director->QueryHandler = RegisterContext->QueryHandler;
  125. Director->UnloadHandler = RegisterContext->UnloadHandler;
  126. KeAcquireSpinLock(&DirectorLock, &Irql);
  127. if (NatLookupDirector(Director->Key, &InsertionPoint)) {
  128. KeReleaseSpinLock(&DirectorLock, Irql);
  129. ERROR(
  130. ("NatCreateDirector: duplicate director %d/%d\n",
  131. RegisterContext->Protocol, RegisterContext->Port)
  132. );
  133. ExFreePool(Director);
  134. return STATUS_UNSUCCESSFUL;
  135. }
  136. InsertTailList(InsertionPoint, &Director->Link);
  137. KeReleaseSpinLock(&DirectorLock, Irql);
  138. InterlockedIncrement(&DirectorCount);
  139. //
  140. // Supply the caller with 'out' information
  141. //
  142. RegisterContext->DirectorHandle = (PVOID)Director;
  143. RegisterContext->QueryInfoSession = NatDirectorQueryInfoSession;
  144. RegisterContext->Deregister = NatDirectorDeregister;
  145. RegisterContext->DissociateSession = NatDirectorDissociateSession;
  146. return STATUS_SUCCESS;
  147. } // NatCreateDirector
  148. NTSTATUS
  149. NatDeleteDirector(
  150. PNAT_DIRECTOR Director
  151. )
  152. /*++
  153. Routine Description:
  154. Handles director deletion.
  155. Arguments:
  156. Director - specifies the director to be deleted.
  157. Return Value
  158. NTSTATUS - status code.
  159. --*/
  160. {
  161. KIRQL Irql;
  162. CALLTRACE(("NatDeleteDirector\n"));
  163. if (!Director) { return STATUS_INVALID_PARAMETER; }
  164. InterlockedDecrement(&DirectorCount);
  165. //
  166. // Remove the director from the list
  167. //
  168. KeAcquireSpinLock(&DirectorLock, &Irql);
  169. RemoveEntryList(&Director->Link);
  170. Director->Flags |= NAT_DIRECTOR_FLAG_DELETED;
  171. KeReleaseSpinLock(&DirectorLock, Irql);
  172. //
  173. // Drop its reference count and cleanup if necessary
  174. //
  175. if (InterlockedDecrement(&Director->ReferenceCount) > 0) {
  176. return STATUS_PENDING;
  177. }
  178. NatCleanupDirector(Director);
  179. return STATUS_SUCCESS;
  180. } // NatDeleteDirector
  181. VOID
  182. NatInitializeDirectorManagement(
  183. VOID
  184. )
  185. /*++
  186. Routine Description:
  187. This routine prepares the director-management module for operation.
  188. Arguments:
  189. none.
  190. Return Value:
  191. none.
  192. --*/
  193. {
  194. CALLTRACE(("NatInitializeDirectorManagement\n"));
  195. DirectorCount = 0;
  196. KeInitializeSpinLock(&DirectorLock);
  197. InitializeListHead(&DirectorList);
  198. KeInitializeSpinLock(&DirectorMappingLock);
  199. } // NatInitializeDirectorManagement
  200. PNAT_DIRECTOR
  201. NatLookupAndReferenceDirector(
  202. UCHAR Protocol,
  203. USHORT Port
  204. )
  205. /*++
  206. Routine Description:
  207. This routine is called to search for a director for the given
  208. incoming protocol and port, and to obtain a referenced pointer
  209. to such a director.
  210. This routine must be invoked at DISPATCH_LEVEL.
  211. Arguments:
  212. Protocol - the protocol of the director to be looked up
  213. Port - the port-number of the director to be looked up
  214. Return Value:
  215. PNAT_DIRECTOR - the references director if found; NULL otherwise.
  216. --*/
  217. {
  218. PNAT_DIRECTOR Director;
  219. ULONG Key;
  220. PLIST_ENTRY Link;
  221. KeAcquireSpinLockAtDpcLevel(&DirectorLock);
  222. if (IsListEmpty(&DirectorList)) {
  223. KeReleaseSpinLockFromDpcLevel(&DirectorLock); return NULL;
  224. }
  225. Key = MAKE_DIRECTOR_KEY(Protocol, Port);
  226. //
  227. // Our support for wildcards takes advantage of the fact that
  228. // all wildcards are designated by zero; hence, since our list
  229. // is in descending order we only need to look for wildcards
  230. // at the point where we would break off a normal search.
  231. //
  232. for (Link = DirectorList.Flink; Link != &DirectorList; Link = Link->Flink) {
  233. Director = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link);
  234. if (Key < Director->Key) {
  235. continue;
  236. } else if (Key > Director->Key) {
  237. //
  238. // End of normal search. Now look for wildcards
  239. //
  240. do {
  241. if ((!DIRECTOR_KEY_PROTOCOL(Director->Key) ||
  242. Protocol == DIRECTOR_KEY_PROTOCOL(Director->Key)) &&
  243. (!DIRECTOR_KEY_PORT(Director->Key) ||
  244. Port == DIRECTOR_KEY_PORT(Director->Key))) {
  245. //
  246. // We have a matching wildcard.
  247. //
  248. break;
  249. }
  250. Link = Link->Flink;
  251. } while (Link != &DirectorList);
  252. if (Link == &DirectorList) { break; }
  253. }
  254. //
  255. // We've found it. Reference it and return.
  256. //
  257. if (!NatReferenceDirector(Director)) { Director = NULL; }
  258. KeReleaseSpinLockFromDpcLevel(&DirectorLock);
  259. return Director;
  260. }
  261. KeReleaseSpinLockFromDpcLevel(&DirectorLock);
  262. return NULL;
  263. } // NatLookupAndReferenceDirector
  264. PNAT_DIRECTOR
  265. NatLookupDirector(
  266. ULONG Key,
  267. PLIST_ENTRY* InsertionPoint
  268. )
  269. /*++
  270. Routine Description:
  271. This routine is called to retrieve the director corresponding to the given
  272. key.
  273. Arguments:
  274. Key - the key for which an director is to be found
  275. InsertionPoint - receives the point at which the director should be
  276. inserted if not found
  277. Return Value:
  278. PNAT_DIRECTOR - the required director, if found
  279. --*/
  280. {
  281. PNAT_DIRECTOR Director;
  282. PLIST_ENTRY Link;
  283. for (Link = DirectorList.Flink; Link != &DirectorList; Link = Link->Flink) {
  284. Director = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link);
  285. if (Key < Director->Key) {
  286. continue;
  287. } else if (Key > Director->Key) {
  288. break;
  289. }
  290. return Director;
  291. }
  292. if (InsertionPoint) { *InsertionPoint = Link; }
  293. return NULL;
  294. } // NatLookupDirector
  295. VOID
  296. NatMappingAttachDirector(
  297. PNAT_DIRECTOR Director,
  298. PVOID DirectorSessionContext,
  299. PNAT_DYNAMIC_MAPPING Mapping
  300. )
  301. /*++
  302. Routine Description:
  303. This routine is invoked to attach a mapping to a director.
  304. It serves as a notification that there is one more mapping
  305. associated with the director.
  306. Arguments:
  307. Director - the director for the mapping
  308. DirectorSessionContext - context associated with the mapping by the director
  309. Mapping - the mapping to be attached.
  310. Return Value:
  311. none.
  312. Environment:
  313. Always invoked at dispatch level with 'DirectorLock' and
  314. 'DirectorMappingLock' held by the caller.
  315. --*/
  316. {
  317. Mapping->Director = Director;
  318. Mapping->DirectorContext = DirectorSessionContext;
  319. InsertTailList(&Director->MappingList, &Mapping->DirectorLink);
  320. if (Director->CreateHandler) {
  321. Director->CreateHandler(
  322. Mapping,
  323. Director->Context,
  324. DirectorSessionContext
  325. );
  326. }
  327. } // NatMappingAttachDirector
  328. VOID
  329. NatMappingDetachDirector(
  330. PNAT_DIRECTOR Director,
  331. PVOID DirectorSessionContext,
  332. PNAT_DYNAMIC_MAPPING Mapping,
  333. IP_NAT_DELETE_REASON DeleteReason
  334. )
  335. /*++
  336. Routine Description:
  337. This routine is invoked to detach a mapping from a director.
  338. It serves as a notification that there is one less mapping
  339. associated with the director.
  340. Arguments:
  341. Director - director to be detached
  342. DirectorSessionContext - context associated with the director
  343. Mapping - the mapping to be detached, or NULL if a mapping could not be
  344. created.
  345. Return Value:
  346. none.
  347. Environment:
  348. Always invoked at dispatch level with 'DirectorLock' and
  349. 'DirectorMappingLock' held, in that order.
  350. --*/
  351. {
  352. KIRQL Irql;
  353. if (!Mapping) {
  354. if (Director->DeleteHandler) {
  355. Director->DeleteHandler(
  356. NULL,
  357. Director->Context,
  358. DirectorSessionContext,
  359. DeleteReason
  360. );
  361. }
  362. } else {
  363. if (Director->DeleteHandler) {
  364. Director->DeleteHandler(
  365. Mapping,
  366. Director->Context,
  367. Mapping->DirectorContext,
  368. DeleteReason
  369. );
  370. }
  371. RemoveEntryList(&Mapping->DirectorLink);
  372. Mapping->Director = NULL;
  373. Mapping->DirectorContext = NULL;
  374. }
  375. } // NatMappingDetachDirector
  376. NTSTATUS
  377. NatQueryDirectorTable(
  378. IN PIP_NAT_ENUMERATE_DIRECTORS InputBuffer,
  379. IN PIP_NAT_ENUMERATE_DIRECTORS OutputBuffer,
  380. IN PULONG OutputBufferLength
  381. )
  382. /*++
  383. Routine Description:
  384. This routine is used for enumerating the registered directors.
  385. Arguments:
  386. InputBuffer - supplies context information for the information
  387. OutputBuffer - receives the result of the enumeration
  388. OutputBufferLength - size of the i/o buffer
  389. Return Value:
  390. STATUS_SUCCESS if successful, error code otherwise.
  391. --*/
  392. {
  393. ULONG Count;
  394. ULONG i;
  395. KIRQL Irql;
  396. ULONG Key;
  397. PLIST_ENTRY Link;
  398. PNAT_DIRECTOR Director;
  399. NTSTATUS status;
  400. PIP_NAT_DIRECTOR Table;
  401. CALLTRACE(("NatQueryDirectorTable\n"));
  402. Key = InputBuffer->EnumerateContext;
  403. KeAcquireSpinLock(&DirectorLock, &Irql);
  404. //
  405. // See if this is a new enumeration or a continuation of an old one.
  406. //
  407. if (!Key) {
  408. //
  409. // This is a new enumeration. We start with the first item
  410. // in the list of entries
  411. //
  412. Director =
  413. IsListEmpty(&DirectorList)
  414. ? NULL
  415. : CONTAINING_RECORD(DirectorList.Flink, NAT_DIRECTOR, Link);
  416. } else {
  417. //
  418. // This is a continuation. The context therefore contains
  419. // the key for the next entry.
  420. //
  421. Director = NatLookupDirector(Key, NULL);
  422. }
  423. if (!Director) {
  424. OutputBuffer->EnumerateCount = 0;
  425. OutputBuffer->EnumerateContext = 0;
  426. OutputBuffer->EnumerateTotalHint = DirectorCount;
  427. *OutputBufferLength =
  428. FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable);
  429. KeReleaseSpinLock(&DirectorLock, Irql);
  430. return STATUS_SUCCESS;
  431. }
  432. //
  433. // Compute the maximum number of entries we can store
  434. //
  435. Count =
  436. *OutputBufferLength -
  437. FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable);
  438. Count /= sizeof(IP_NAT_DIRECTOR);
  439. //
  440. // Walk the list storing entries in the caller's buffer
  441. //
  442. Table = OutputBuffer->EnumerateTable;
  443. for (i = 0, Link = &Director->Link; i < Count && Link != &DirectorList;
  444. i++, Link = Link->Flink) {
  445. Director = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link);
  446. Table[i].Protocol = DIRECTOR_KEY_PROTOCOL(Director->Key);
  447. Table[i].Port = DIRECTOR_KEY_PORT(Director->Key);
  448. }
  449. //
  450. // The enumeration is over; update the output structure
  451. //
  452. *OutputBufferLength =
  453. i * sizeof(IP_NAT_DIRECTOR) +
  454. FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable);
  455. OutputBuffer->EnumerateCount = i;
  456. OutputBuffer->EnumerateTotalHint = DirectorCount;
  457. if (Link == &DirectorList) {
  458. //
  459. // We reached the end of the list
  460. //
  461. OutputBuffer->EnumerateContext = 0;
  462. } else {
  463. //
  464. // Save the continuation context
  465. //
  466. OutputBuffer->EnumerateContext =
  467. CONTAINING_RECORD(Link, NAT_DIRECTOR, Link)->Key;
  468. }
  469. KeReleaseSpinLock(&DirectorLock, Irql);
  470. return STATUS_SUCCESS;
  471. } // NatQueryDirectorTable
  472. VOID
  473. NatShutdownDirectorManagement(
  474. VOID
  475. )
  476. /*++
  477. Routine Description:
  478. This routine shuts down the director-management module.
  479. Arguments:
  480. none.
  481. Return Value:
  482. none.
  483. --*/
  484. {
  485. PNAT_DIRECTOR Director;
  486. KIRQL Irql;
  487. CALLTRACE(("NatShutdownDirectorManagement\n"));
  488. //
  489. // Delete all directors
  490. //
  491. KeAcquireSpinLock(&DirectorLock, &Irql);
  492. while (!IsListEmpty(&DirectorList)) {
  493. Director = CONTAINING_RECORD(DirectorList.Flink, NAT_DIRECTOR, Link);
  494. RemoveEntryList(&Director->Link);
  495. KeReleaseSpinLockFromDpcLevel(&DirectorLock);
  496. NatCleanupDirector(Director);
  497. KeAcquireSpinLockAtDpcLevel(&DirectorLock);
  498. }
  499. KeReleaseSpinLock(&DirectorLock, Irql);
  500. } // NatShutdownDirectorManagement
  501. //
  502. // DIRECTOR HELPER ROUTINES
  503. //
  504. // The caller is assumed to be running at DISPATCH_LEVEL.
  505. //
  506. NTSTATUS
  507. NatDirectorDeregister(
  508. IN PVOID DirectorHandle
  509. )
  510. /*++
  511. Routine Description:
  512. This routine is called by a director to remove itself
  513. from the director list.
  514. Arguments:
  515. DirectorHandle - handle of the director to be removed.
  516. Return Value:
  517. NTSTATUS - status code.
  518. --*/
  519. {
  520. CALLTRACE(("NatDirectorDeregister\n"));
  521. return NatDeleteDirector((PNAT_DIRECTOR)DirectorHandle);
  522. } // NatDirectorDeregister
  523. NTSTATUS
  524. NatDirectorDissociateSession(
  525. IN PVOID DirectorHandle,
  526. IN PVOID SessionHandle
  527. )
  528. /*++
  529. Routine Description:
  530. This routine is called by a director to dissociate itself from a specific
  531. session.
  532. Arguments:
  533. DirectorHandle - the director which wishes to dissociate itself.
  534. SessionHandle - the session from which the director is disssociating itself.
  535. Return Value:
  536. NTSTATUS - indicates success/failure
  537. Environment:
  538. Invoked at dispatch level with neither 'DirectorLock' nor
  539. 'DirectorMappingLock' held by the caller.
  540. --*/
  541. {
  542. PNAT_DIRECTOR Director = (PNAT_DIRECTOR)DirectorHandle;
  543. KIRQL Irql;
  544. PNAT_DYNAMIC_MAPPING Mapping = (PNAT_DYNAMIC_MAPPING)SessionHandle;
  545. CALLTRACE(("NatDirectorDissociateSession\n"));
  546. KeAcquireSpinLock(&DirectorLock, &Irql);
  547. if (Mapping->Director != Director) {
  548. KeReleaseSpinLock(&DirectorLock, Irql);
  549. return STATUS_INVALID_PARAMETER;
  550. }
  551. KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock);
  552. NatMappingDetachDirector(
  553. Director,
  554. Mapping->DirectorContext,
  555. Mapping,
  556. NatDissociateDirectorDeleteReason
  557. );
  558. KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock);
  559. if (!NAT_MAPPING_DELETE_ON_DISSOCIATE_DIRECTOR(Mapping)) {
  560. KeReleaseSpinLock(&DirectorLock, Irql);
  561. } else {
  562. KeReleaseSpinLockFromDpcLevel(&DirectorLock);
  563. KeAcquireSpinLockAtDpcLevel(&MappingLock);
  564. NatDeleteMapping(Mapping);
  565. KeReleaseSpinLock(&MappingLock, Irql);
  566. }
  567. return STATUS_SUCCESS;
  568. } // NatDirectorDissociateSession
  569. VOID
  570. NatDirectorQueryInfoSession(
  571. IN PVOID SessionHandle,
  572. OUT PIP_NAT_SESSION_MAPPING_STATISTICS Statistics OPTIONAL
  573. )
  574. /*++
  575. Routine Description:
  576. This routine is invoked by a director to obtain information
  577. about a session.
  578. Arguments:
  579. SessionHandle - the session for which information is required
  580. Statistics - receives statistics for the session
  581. Return Value:
  582. none.
  583. Environment:
  584. Invoked
  585. --*/
  586. {
  587. KIRQL Irql;
  588. KeAcquireSpinLock(&MappingLock, &Irql);
  589. NatQueryInformationMapping(
  590. (PNAT_DYNAMIC_MAPPING)SessionHandle,
  591. NULL,
  592. NULL,
  593. NULL,
  594. NULL,
  595. NULL,
  596. NULL,
  597. NULL,
  598. Statistics
  599. );
  600. KeReleaseSpinLock(&MappingLock, Irql);
  601. } // NatDirectorQueryInfoSession