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.

1374 lines
32 KiB

  1. /*++
  2. Copyright (c) 1997, Microsoft Corporation
  3. Module Name:
  4. ticket.c
  5. Abstract:
  6. This module contains code for the NAT's ticket-management.
  7. Author:
  8. Abolade Gbadegesin (t-abolag) 22-Aug-1997
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. ULONG DynamicTicketCount;
  14. LIST_ENTRY DynamicTicketList;
  15. KSPIN_LOCK DynamicTicketLock;
  16. ULONG TicketCount;
  17. NTSTATUS
  18. NatCreateDynamicTicket(
  19. PIP_NAT_CREATE_DYNAMIC_TICKET CreateTicket,
  20. ULONG InputBufferLength,
  21. PFILE_OBJECT FileObject
  22. )
  23. /*++
  24. Routine Description:
  25. This routine is invoked to create a dynamically-activated ticket
  26. in response to an 'IOCTL_IP_NAT_CREATE_DYNAMIC_TICKET' request.
  27. Arguments:
  28. CreateTicket - describes the dynamic ticket to be created
  29. InputBufferLength - the length of the buffer specified by 'CreateTicket'
  30. FileObject - file-object with which to associate the dynamic ticket
  31. Return Value:
  32. NTSTATUS - status code.
  33. --*/
  34. {
  35. PLIST_ENTRY InsertionPoint;
  36. ULONG i;
  37. KIRQL Irql;
  38. ULONG Key;
  39. ULONG ResponseArrayLength;
  40. PNAT_DYNAMIC_TICKET Ticket;
  41. CALLTRACE(("NatCreateDynamicTicket\n"));
  42. //
  43. // Validate the parameters.
  44. //
  45. if ((CreateTicket->Protocol != NAT_PROTOCOL_TCP &&
  46. CreateTicket->Protocol != NAT_PROTOCOL_UDP) || !CreateTicket->Port) {
  47. return STATUS_INVALID_PARAMETER;
  48. } else if (CreateTicket->ResponseCount >
  49. MAXLONG / sizeof(CreateTicket->ResponseArray[0])) {
  50. return STATUS_INVALID_BUFFER_SIZE;
  51. }
  52. ResponseArrayLength =
  53. CreateTicket->ResponseCount *
  54. sizeof(CreateTicket->ResponseArray[0]) +
  55. FIELD_OFFSET(IP_NAT_CREATE_DYNAMIC_TICKET, ResponseArray);
  56. if (InputBufferLength < ResponseArrayLength) {
  57. return STATUS_INVALID_BUFFER_SIZE;
  58. }
  59. for (i = 0; i < CreateTicket->ResponseCount; i++) {
  60. if ((CreateTicket->ResponseArray[i].Protocol != NAT_PROTOCOL_TCP &&
  61. CreateTicket->ResponseArray[i].Protocol != NAT_PROTOCOL_UDP) ||
  62. !CreateTicket->ResponseArray[i].StartPort ||
  63. !CreateTicket->ResponseArray[i].EndPort ||
  64. NTOHS(CreateTicket->ResponseArray[i].StartPort) >
  65. NTOHS(CreateTicket->ResponseArray[i].EndPort)) {
  66. return STATUS_INVALID_PARAMETER;
  67. }
  68. }
  69. //
  70. // Construct a key and search for a duplicate
  71. //
  72. Key = MAKE_DYNAMIC_TICKET_KEY(CreateTicket->Protocol, CreateTicket->Port);
  73. KeAcquireSpinLock(&DynamicTicketLock, &Irql);
  74. if (NatLookupDynamicTicket(Key, &InsertionPoint)) {
  75. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  76. TRACE(TICKET, ("NatCreateDynamicTicket: collision %08X\n", Key));
  77. return STATUS_UNSUCCESSFUL;
  78. }
  79. //
  80. // Allocate and initialize the new dynamic ticket
  81. //
  82. Ticket =
  83. ExAllocatePoolWithTag(
  84. NonPagedPool,
  85. sizeof(NAT_DYNAMIC_TICKET) + ResponseArrayLength,
  86. NAT_TAG_DYNAMIC_TICKET
  87. );
  88. if (!Ticket) {
  89. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  90. ERROR(("NatCreateTicket: ticket could not be allocated\n"));
  91. return STATUS_NO_MEMORY;
  92. }
  93. Ticket->Key = Key;
  94. Ticket->FileObject = FileObject;
  95. Ticket->ResponseCount = CreateTicket->ResponseCount;
  96. Ticket->ResponseArray = (PVOID)(Ticket + 1);
  97. for (i = 0; i < CreateTicket->ResponseCount; i++) {
  98. Ticket->ResponseArray[i].Protocol =
  99. CreateTicket->ResponseArray[i].Protocol;
  100. Ticket->ResponseArray[i].StartPort =
  101. CreateTicket->ResponseArray[i].StartPort;
  102. Ticket->ResponseArray[i].EndPort =
  103. CreateTicket->ResponseArray[i].EndPort;
  104. }
  105. InsertTailList(InsertionPoint, &Ticket->Link);
  106. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  107. InterlockedIncrement(&DynamicTicketCount);
  108. return STATUS_SUCCESS;
  109. } // NatCreateDynamicTicket
  110. NTSTATUS
  111. NatCreateTicket(
  112. PNAT_INTERFACE Interfacep,
  113. UCHAR Protocol,
  114. ULONG PrivateAddress,
  115. USHORT PrivateOrEndPort,
  116. ULONG RemoteAddress OPTIONAL,
  117. ULONG RemotePort OPTIONAL,
  118. ULONG Flags,
  119. PNAT_USED_ADDRESS AddressToUse OPTIONAL,
  120. USHORT PortToUse OPTIONAL,
  121. PULONG PublicAddress,
  122. PUSHORT PublicPort
  123. )
  124. /*++
  125. Routine Description:
  126. This routine allocates and initializes a NAT ticket to allow a single
  127. inbound session to be established using 'Protocol'. The routine acquires
  128. an address and port to be advertised as the publicly-visible endpoint
  129. of the session, and sets the ticket to expire in 'TimeoutSeconds'.
  130. Arguments:
  131. Interfacep - the interface on which the ticket is to be created
  132. Protocol - the protocol of the inbound session to be allowed
  133. PrivateAddress - the private address to which the inbound session
  134. should be directed when the ticket is used.
  135. PrivateOrEndPort - contains either
  136. (a) the private port to which the inbound session should be
  137. directed when the ticket is used, or
  138. (b) the end of a range of public ports which starts with 'PortToUse',
  139. if 'Flags' has 'NAT_TICKET_FLAG_IS_RANGE' set,
  140. in which case the *private* port to which the inbound session
  141. should be directed is determined when the ticket is used.
  142. Flags - the initial flags for the ticket;
  143. NAT_TICKET_FLAG_PERSISTENT - the ticket is reusable
  144. NAT_TICKET_FLAG_PORT_MAPPING - the ticket is for a port-mapping
  145. NAT_TICKET_FLAG_IS_RANGE - the ticket is for a range of ports
  146. AddressToUse - optionally supplies the public address for the ticket
  147. PortToUse - if 'AddressToUse' is set, must supply the public-port
  148. PublicAddress - receives the public address assigned to the ticket.
  149. PublicPort - receives the public port assigned to the ticket.
  150. Return Value:
  151. NTSTATUS - indicates success/failure.
  152. Environment:
  153. Invoked with 'Interfacep->Lock' held by the caller.
  154. --*/
  155. {
  156. PLIST_ENTRY InsertionPoint;
  157. ULONG64 Key;
  158. ULONG64 RemoteKey;
  159. NTSTATUS status;
  160. PNAT_TICKET Ticket;
  161. TRACE(TICKET, ("NatCreateTicket\n"));
  162. if (AddressToUse) {
  163. if (!NatReferenceAddressPoolEntry(AddressToUse)) {
  164. return STATUS_UNSUCCESSFUL;
  165. }
  166. *PublicAddress = AddressToUse->PublicAddress;
  167. *PublicPort = PortToUse;
  168. } else {
  169. //
  170. // Acquire a public address
  171. //
  172. status =
  173. NatAcquireFromAddressPool(
  174. Interfacep,
  175. PrivateAddress,
  176. 0,
  177. &AddressToUse
  178. );
  179. if (!NT_SUCCESS(status)) { return status; }
  180. //
  181. // Acquire a unique public port
  182. //
  183. status =
  184. NatAcquireFromPortPool(
  185. Interfacep,
  186. AddressToUse,
  187. Protocol,
  188. PrivateOrEndPort,
  189. &PortToUse
  190. );
  191. if (!NT_SUCCESS(status)) {
  192. NatDereferenceAddressPoolEntry(Interfacep, AddressToUse);
  193. return status;
  194. }
  195. *PublicAddress = AddressToUse->PublicAddress;
  196. *PublicPort = PortToUse;
  197. }
  198. //
  199. // Look for a duplicate of the key
  200. //
  201. Key = MAKE_TICKET_KEY(Protocol, *PublicAddress, *PublicPort);
  202. RemoteKey = MAKE_TICKET_KEY(Protocol, RemoteAddress, RemotePort);
  203. if (NatLookupTicket(Interfacep, Key, RemoteKey, &InsertionPoint)) {
  204. //
  205. // Collision; fail
  206. //
  207. TRACE(TICKET, ("NatCreateTicket: collision %016I64X:%016I64X\n",
  208. Key, RemoteKey));
  209. NatDereferenceAddressPoolEntry(Interfacep, AddressToUse);
  210. return STATUS_UNSUCCESSFUL;
  211. }
  212. //
  213. // Allocate and initialize the ticket
  214. //
  215. Ticket = ALLOCATE_TICKET_BLOCK();
  216. if (!Ticket) {
  217. ERROR(("NatCreateTicket: ticket could not be allocated\n"));
  218. NatDereferenceAddressPoolEntry(Interfacep, AddressToUse);
  219. return STATUS_NO_MEMORY;
  220. }
  221. Ticket->Key = Key;
  222. Ticket->RemoteKey = RemoteKey;
  223. Ticket->Flags = Flags;
  224. Ticket->UsedAddress = AddressToUse;
  225. Ticket->PrivateAddress = PrivateAddress;
  226. if (NAT_TICKET_IS_RANGE(Ticket)) {
  227. Ticket->PrivateOrHostOrderEndPort = NTOHS(PrivateOrEndPort);
  228. } else {
  229. Ticket->PrivateOrHostOrderEndPort = PrivateOrEndPort;
  230. }
  231. InsertTailList(InsertionPoint, &Ticket->Link);
  232. KeQueryTickCount((PLARGE_INTEGER)&Ticket->LastAccessTime);
  233. InterlockedIncrement(&TicketCount);
  234. return STATUS_SUCCESS;
  235. } // NatCreateTicket
  236. VOID
  237. NatDeleteAnyAssociatedDynamicTicket(
  238. PFILE_OBJECT FileObject
  239. )
  240. /*++
  241. Routine Description:
  242. This routine is invoked when cleanup is in progress for a file-object
  243. opened for \Device\IpNat. It deletes any dynamic tickets associated with
  244. the file-object.
  245. Arguments:
  246. FileObject - the file-object being cleaned up
  247. Return Value:
  248. none.
  249. --*/
  250. {
  251. KIRQL Irql;
  252. PLIST_ENTRY Link;
  253. PNAT_DYNAMIC_TICKET Ticket;
  254. CALLTRACE(("NatDeleteAnyAssociatedDynamicTicket\n"));
  255. KeAcquireSpinLock(&DynamicTicketLock, &Irql);
  256. for (Link = DynamicTicketList.Flink; Link != &DynamicTicketList;
  257. Link = Link->Flink) {
  258. Ticket = CONTAINING_RECORD(Link, NAT_DYNAMIC_TICKET, Link);
  259. if (Ticket->FileObject != FileObject) { continue; }
  260. Link = Link->Blink;
  261. RemoveEntryList(&Ticket->Link);
  262. ExFreePool(Ticket);
  263. InterlockedDecrement(&DynamicTicketCount);
  264. }
  265. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  266. } // NatDeleteAnyAssociatedDynamicTicket
  267. NTSTATUS
  268. NatDeleteDynamicTicket(
  269. PIP_NAT_DELETE_DYNAMIC_TICKET DeleteTicket,
  270. PFILE_OBJECT FileObject
  271. )
  272. /*++
  273. Routine Description:
  274. This routine is invoked when an 'IOCTL_IP_DELETE_DYNAMIC_TICKET' request
  275. is issued to delete a dynamic ticket.
  276. Arguments:
  277. DeleteTicket - specifies the ticket to be deleted
  278. FileObject - specifies the file-object in which the request was issued
  279. Return Value:
  280. NTSTATUS - indicates success/failure.
  281. --*/
  282. {
  283. PLIST_ENTRY InsertionPoint;
  284. KIRQL Irql;
  285. ULONG Key;
  286. PNAT_DYNAMIC_TICKET Ticket;
  287. CALLTRACE(("NatDeleteDynamicTicket\n"));
  288. Key = MAKE_DYNAMIC_TICKET_KEY(DeleteTicket->Protocol, DeleteTicket->Port);
  289. KeAcquireSpinLock(&DynamicTicketLock, &Irql);
  290. if (!(Ticket = NatLookupDynamicTicket(Key, &InsertionPoint))) {
  291. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  292. return STATUS_UNSUCCESSFUL;
  293. } else if (Ticket->FileObject != FileObject) {
  294. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  295. return STATUS_ACCESS_DENIED;
  296. }
  297. RemoveEntryList(&Ticket->Link);
  298. ExFreePool(Ticket);
  299. KeReleaseSpinLock(&DynamicTicketLock, Irql);
  300. InterlockedDecrement(&DynamicTicketCount);
  301. return STATUS_SUCCESS;
  302. } // NatDeleteDynamicTicket
  303. VOID
  304. NatDeleteTicket(
  305. PNAT_INTERFACE Interfacep,
  306. PNAT_TICKET Ticket
  307. )
  308. /*++
  309. Routine Description:
  310. This routine is called to delete a NAT ticket.
  311. Arguments:
  312. Interfacep - the ticket's interface
  313. Ticket - the ticket to be deleted
  314. Return Value:
  315. none.
  316. Environment:
  317. Invoked with 'Interfacep->Lock' held by the caller.
  318. --*/
  319. {
  320. InterlockedDecrement(&TicketCount);
  321. RemoveEntryList(&Ticket->Link);
  322. NatDereferenceAddressPoolEntry(Interfacep, Ticket->UsedAddress);
  323. FREE_TICKET_BLOCK(Ticket);
  324. } // NatDeleteTicket
  325. VOID
  326. NatInitializeDynamicTicketManagement(
  327. VOID
  328. )
  329. /*++
  330. Routine Description:
  331. This routine is called to initialize state used for managing
  332. dynamic tickets.
  333. Arguments:
  334. none.
  335. Return Value:
  336. none.
  337. --*/
  338. {
  339. CALLTRACE(("NatInitializeDynamicTicketManagement\n"));
  340. InitializeListHead(&DynamicTicketList);
  341. KeInitializeSpinLock(&DynamicTicketLock);
  342. DynamicTicketCount = 0;
  343. } // NatInitializeDynamicTicketManagement
  344. BOOLEAN
  345. NatIsPortUsedByTicket(
  346. PNAT_INTERFACE Interfacep,
  347. UCHAR Protocol,
  348. USHORT PublicPort
  349. )
  350. /*++
  351. Routine Description:
  352. This routine searches the interface's ticket-list to see if the given port
  353. is in use as the public port of any ticket.
  354. Arguments:
  355. Interfacep - the interface whose ticket list is to be searched
  356. Protocol - indicates either TCP or UDP
  357. PublicPort - the port for which to search
  358. Return Value:
  359. BOOLEAN - TRUE if the port is in use, FALSE otherwise
  360. Environment:
  361. Invoked with 'Interfacep->Lock' held by the caller.
  362. --*/
  363. {
  364. PLIST_ENTRY Link;
  365. USHORT HostOrderPort;
  366. ULONG64 Key;
  367. PNAT_TICKET Ticket;
  368. TRACE(TICKET, ("NatIsPortUsedByTicket\n"));
  369. HostOrderPort = NTOHS(PublicPort);
  370. Key = MAKE_TICKET_KEY(Protocol, 0, PublicPort);
  371. for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
  372. Link = Link->Flink) {
  373. Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
  374. if (NAT_TICKET_IS_RANGE(Ticket)) {
  375. if (HostOrderPort > Ticket->PrivateOrHostOrderEndPort) {
  376. continue;
  377. } else if (HostOrderPort < NTOHS(TICKET_PORT(Ticket->Key))) {
  378. continue;
  379. }
  380. } else if (Key != (Ticket->Key & MAKE_TICKET_KEY(~0,0,~0))) {
  381. continue;
  382. }
  383. return TRUE;
  384. }
  385. return FALSE;
  386. } // NatIsPortUsedByTicket
  387. VOID
  388. NatLookupAndApplyDynamicTicket(
  389. UCHAR Protocol,
  390. USHORT DestinationPort,
  391. PNAT_INTERFACE Interfacep,
  392. ULONG PublicAddress,
  393. ULONG PrivateAddress
  394. )
  395. /*++
  396. Routine Description:
  397. This routine is invoked to determine whether there is a dynamic ticket
  398. which should be activated for the given outbound session.
  399. Arguments:
  400. Protocol - the protocol of the outbound session
  401. DestinationPort - the destination port of the outbound session
  402. Interfacep - the interface across which the outbound session
  403. will be translated
  404. PublicAddress - the public address used by the outbound session's mapping
  405. PrivateAddress - the private address of the outbound session's mapping
  406. Return Value:
  407. none.
  408. Environment:
  409. Invoked at dispatch level with neither 'Interfacep->Lock' nor
  410. 'DynamicTicketLock' held by the caller.
  411. --*/
  412. {
  413. PNAT_USED_ADDRESS AddressToUse;
  414. ULONG i;
  415. ULONG Key;
  416. USHORT PublicPort;
  417. NTSTATUS status;
  418. PNAT_DYNAMIC_TICKET Ticket;
  419. Key = MAKE_DYNAMIC_TICKET_KEY(Protocol, DestinationPort);
  420. KeAcquireSpinLockAtDpcLevel(&DynamicTicketLock);
  421. if (!(Ticket = NatLookupDynamicTicket(Key, NULL))) {
  422. KeReleaseSpinLockFromDpcLevel(&DynamicTicketLock);
  423. return;
  424. }
  425. KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
  426. for (i = 0; i < Ticket->ResponseCount; i++) {
  427. status =
  428. NatAcquireFromAddressPool(
  429. Interfacep,
  430. PrivateAddress,
  431. 0,
  432. &AddressToUse
  433. );
  434. if (NT_SUCCESS(status)) {
  435. NatCreateTicket(
  436. Interfacep,
  437. Ticket->ResponseArray[i].Protocol,
  438. PrivateAddress,
  439. Ticket->ResponseArray[i].EndPort,
  440. 0,
  441. 0,
  442. NAT_TICKET_FLAG_IS_RANGE,
  443. AddressToUse,
  444. Ticket->ResponseArray[i].StartPort,
  445. &PublicAddress,
  446. &PublicPort
  447. );
  448. }
  449. }
  450. KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
  451. KeReleaseSpinLockFromDpcLevel(&DynamicTicketLock);
  452. } // NatLookupAndApplyDynamicTicket
  453. NTSTATUS
  454. NatLookupAndDeleteTicket(
  455. PNAT_INTERFACE Interfacep,
  456. ULONG64 Key,
  457. ULONG64 RemoteKey
  458. )
  459. /*++
  460. Routine Description:
  461. This routine looks for a ticket with the specified key and, if found,
  462. removes and deallocates the ticket after releasing its address and port.
  463. Arguments:
  464. Interfacep - the interface on which to look for the ticket
  465. Key - the ticket to look for
  466. Return Value:
  467. NTSTATUS - indicates success/failure
  468. Environment:
  469. Invoked with 'Interfacep->Lock' held by the caller.
  470. --*/
  471. {
  472. PNAT_TICKET Ticket;
  473. TRACE(TICKET, ("NatLookupAndDeleteTicket\n"));
  474. //
  475. // Look for the ticket
  476. //
  477. Ticket = NatLookupTicket(Interfacep, Key, RemoteKey, NULL);
  478. if (Ticket) {
  479. NatDeleteTicket(Interfacep, Ticket);
  480. return STATUS_SUCCESS;
  481. }
  482. return STATUS_UNSUCCESSFUL;
  483. } // NatLookupAndDeleteTicket
  484. NTSTATUS
  485. NatLookupAndRemoveTicket(
  486. PNAT_INTERFACE Interfacep,
  487. ULONG64 Key,
  488. ULONG64 RemoteKey,
  489. PNAT_USED_ADDRESS* UsedAddress,
  490. PULONG PrivateAddress,
  491. PUSHORT PrivatePort
  492. )
  493. /*++
  494. Routine Description:
  495. This routine looks for a ticket with the specified key and, if found,
  496. removes and deallocates the ticket after storing the private address/port
  497. for the ticket in the caller's arguments.
  498. Arguments:
  499. Interfacep - the interface on which to look for the ticket
  500. Key - the public key of the ticket to look for
  501. UsedAddress - receives a pointer to the public-address used by the ticket
  502. PrivateAddress - receives the address to which the ticket grants access
  503. PrivatePort - receives the port to which the ticket grants access
  504. Return Value:
  505. NTSTATUS - indicates success/failure
  506. Environment:
  507. Invoked with 'Interfacep->Lock' held by the caller.
  508. --*/
  509. {
  510. PLIST_ENTRY Link;
  511. USHORT HostOrderPort;
  512. PNAT_TICKET Ticket;
  513. ULONG RemoteAddress;
  514. USHORT RemotePort;
  515. TRACE(PER_PACKET, ("NatLookupAndRemoveTicket\n"));
  516. //
  517. // Look for the ticket.
  518. //
  519. HostOrderPort = NTOHS(TICKET_PORT(Key));
  520. for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
  521. Link = Link->Flink) {
  522. Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
  523. if (NAT_TICKET_IS_RANGE(Ticket)) {
  524. if (HostOrderPort > Ticket->PrivateOrHostOrderEndPort) {
  525. continue;
  526. } else if (HostOrderPort < NTOHS(TICKET_PORT(Ticket->Key))) {
  527. continue;
  528. }
  529. } else if (Key != Ticket->Key) {
  530. continue;
  531. }
  532. //
  533. // Primary key matches, also need to check remote key.
  534. //
  535. if (RemoteKey != Ticket->RemoteKey) {
  536. //
  537. // Handle cases where remote key wasn't specified
  538. //
  539. RemoteAddress = TICKET_ADDRESS(Ticket->RemoteKey);
  540. if (RemoteAddress != 0
  541. && RemoteAddress != TICKET_ADDRESS(RemoteKey)) {
  542. continue;
  543. }
  544. RemotePort = TICKET_PORT(Ticket->RemoteKey);
  545. if (RemotePort != 0
  546. && RemotePort != TICKET_PORT(RemoteKey)) {
  547. continue;
  548. }
  549. }
  550. //
  551. // This is the ticket
  552. //
  553. *UsedAddress = Ticket->UsedAddress;
  554. *PrivateAddress = Ticket->PrivateAddress;
  555. if (NAT_TICKET_IS_RANGE(Ticket)) {
  556. *PrivatePort = TICKET_PORT(Key);
  557. } else {
  558. *PrivatePort = Ticket->PrivateOrHostOrderEndPort;
  559. }
  560. if (!NAT_TICKET_PERSISTENT(Ticket)) {
  561. InterlockedDecrement(&TicketCount);
  562. RemoveEntryList(&Ticket->Link);
  563. FREE_TICKET_BLOCK(Ticket);
  564. } else {
  565. //
  566. // Reference the ticket's address again for the next use
  567. //
  568. NatReferenceAddressPoolEntry(Ticket->UsedAddress);
  569. }
  570. return STATUS_SUCCESS;
  571. }
  572. return STATUS_UNSUCCESSFUL;
  573. } // NatLookupAndRemoveTicket
  574. PNAT_DYNAMIC_TICKET
  575. NatLookupDynamicTicket(
  576. ULONG Key,
  577. PLIST_ENTRY *InsertionPoint
  578. )
  579. /*++
  580. Routine Description:
  581. This routine is invoked to look for a dynamic ticket with the given key.
  582. Arguments:
  583. Key - the key for the dynamic ticket to be found
  584. InsertionPoint - if the ticket is not found, receives the insertion point
  585. Return Value:
  586. PNAT_DYNAMIC_TICKET - the dynamic ticket, if found
  587. Environment:
  588. Invoked with 'DynamicTicketLock' held by the caller.
  589. --*/
  590. {
  591. PLIST_ENTRY Link;
  592. PNAT_DYNAMIC_TICKET Ticket;
  593. TRACE(TICKET, ("NatLookupDynamicTicket\n"));
  594. for (Link = DynamicTicketList.Flink; Link != &DynamicTicketList;
  595. Link = Link->Flink) {
  596. Ticket = CONTAINING_RECORD(Link, NAT_DYNAMIC_TICKET, Link);
  597. if (Key > Ticket->Key) {
  598. continue;
  599. } else if (Key < Ticket->Key) {
  600. break;
  601. }
  602. return Ticket;
  603. }
  604. if (InsertionPoint) { *InsertionPoint = Link; }
  605. return NULL;
  606. } // NatLookupDynamicTicket
  607. PNAT_TICKET
  608. NatLookupFirewallTicket(
  609. PNAT_INTERFACE Interfacep,
  610. UCHAR Protocol,
  611. USHORT Port
  612. )
  613. /*++
  614. Routine Description:
  615. This routine is invoked to look for a firewall ticket with the given
  616. protocol and port. A firewall ticket must:
  617. * have the same public and private address
  618. * have the same public and private port
  619. * be marked persistent
  620. * not be a range
  621. Arguments:
  622. Interfacep - the interface on which to search for a ticket
  623. Protocol - the protocl for the ticket to be found
  624. Port - the port for the ticket to be found
  625. Return Value:
  626. PNAT_TICKET - the ticket, if found
  627. Environment:
  628. Invoked with 'Interfacep->Lock' held by the caller.
  629. --*/
  630. {
  631. PLIST_ENTRY Link;
  632. PNAT_TICKET Ticket;
  633. TRACE(TICKET, ("NatLookupFirewallTicket\n"));
  634. for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
  635. Link = Link->Flink) {
  636. Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
  637. if (Protocol == TICKET_PROTOCOL(Ticket->Key)
  638. && Port == TICKET_PORT(Ticket->Key)
  639. && Port == Ticket->PrivateOrHostOrderEndPort
  640. && Ticket->PrivateAddress == TICKET_ADDRESS(Ticket->Key)
  641. && NAT_TICKET_PERSISTENT(Ticket)
  642. && !NAT_TICKET_IS_RANGE(Ticket)) {
  643. return Ticket;
  644. }
  645. }
  646. return NULL;
  647. } // NatLookupFirewallTicket
  648. PNAT_TICKET
  649. NatLookupTicket(
  650. PNAT_INTERFACE Interfacep,
  651. ULONG64 Key,
  652. ULONG64 RemoteKey,
  653. PLIST_ENTRY *InsertionPoint
  654. )
  655. /*++
  656. Routine Description:
  657. This routine is invoked to look for a ticket with the given key.
  658. Arguments:
  659. Interfacep - the interface on which to search for a ticket
  660. Key - the key for the ticket to be found
  661. InsertionPoint - if the ticket is not found, receives the insertion point
  662. Return Value:
  663. PNAT_TICKET - the ticket, if found
  664. Environment:
  665. Invoked with 'Interfacep->Lock' held by the caller.
  666. --*/
  667. {
  668. PLIST_ENTRY Link;
  669. PNAT_TICKET Ticket;
  670. TRACE(TICKET, ("NatLookupTicket\n"));
  671. for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
  672. Link = Link->Flink) {
  673. Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
  674. if (Key > Ticket->Key) {
  675. continue;
  676. } else if (Key < Ticket->Key) {
  677. break;
  678. }
  679. //
  680. // Primary keys match, check secondary.
  681. //
  682. if (RemoteKey > Ticket->RemoteKey) {
  683. continue;
  684. } else if (RemoteKey < Ticket->RemoteKey) {
  685. break;
  686. }
  687. return Ticket;
  688. }
  689. if (InsertionPoint) { *InsertionPoint = Link; }
  690. return NULL;
  691. } // NatLookupTicket
  692. NTSTATUS
  693. NatProcessCreateTicket(
  694. PIP_NAT_CREATE_TICKET CreateTicket,
  695. PFILE_OBJECT FileObject
  696. )
  697. /*++
  698. Routine Description:
  699. This routine is invoked to create a ticket in response to an
  700. 'IOCTL_IP_NAT_CREATE_TICKET' request.
  701. Arguments:
  702. CreateTicket - describes the ticket to be created
  703. Return Value:
  704. NTSTATUS - status code.
  705. --*/
  706. {
  707. PNAT_INTERFACE Interfacep;
  708. KIRQL Irql;
  709. NTSTATUS Status;
  710. TRACE(TICKET, ("NatProcessCreateTicket\n"));
  711. //
  712. // Validate the parameters
  713. //
  714. if (0 == CreateTicket->InterfaceIndex
  715. || INVALID_IF_INDEX == CreateTicket->InterfaceIndex
  716. || (NAT_PROTOCOL_TCP != CreateTicket->PortMapping.Protocol
  717. && NAT_PROTOCOL_UDP != CreateTicket->PortMapping.Protocol)
  718. || 0 == CreateTicket->PortMapping.PublicPort
  719. || 0 == CreateTicket->PortMapping.PrivatePort
  720. || 0 == CreateTicket->PortMapping.PrivateAddress) {
  721. return STATUS_INVALID_PARAMETER;
  722. }
  723. //
  724. // Lookup and reference the interface
  725. //
  726. KeAcquireSpinLock(&InterfaceLock, &Irql);
  727. Interfacep = NatLookupInterface(CreateTicket->InterfaceIndex, NULL);
  728. if (NULL == Interfacep) {
  729. KeReleaseSpinLock(&InterfaceLock, Irql);
  730. return STATUS_INVALID_PARAMETER;
  731. }
  732. if (Interfacep->FileObject != FileObject) {
  733. KeReleaseSpinLock(&InterfaceLock, Irql);
  734. return STATUS_ACCESS_DENIED;
  735. }
  736. if (!NatReferenceInterface(Interfacep)) {
  737. KeReleaseSpinLock(&InterfaceLock, Irql);
  738. return STATUS_INVALID_PARAMETER;
  739. }
  740. KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
  741. KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
  742. //
  743. // Create the actual ticket
  744. //
  745. Status =
  746. NatCreateStaticPortMapping(
  747. Interfacep,
  748. &CreateTicket->PortMapping
  749. );
  750. KeReleaseSpinLock(&Interfacep->Lock, Irql);
  751. NatDereferenceInterface(Interfacep);
  752. return Status;
  753. } // NatProcessCreateTicket
  754. NTSTATUS
  755. NatProcessDeleteTicket(
  756. PIP_NAT_CREATE_TICKET DeleteTicket,
  757. PFILE_OBJECT FileObject
  758. )
  759. /*++
  760. Routine Description:
  761. This routine is invoked to delete a ticket in response to an
  762. 'IOCTL_IP_NAT_DELETE_TICKET' request.
  763. Arguments:
  764. DeleteTicket - describes the ticket to be created
  765. Return Value:
  766. NTSTATUS - status code.
  767. --*/
  768. {
  769. PNAT_INTERFACE Interfacep;
  770. KIRQL Irql;
  771. ULONG64 Key;
  772. ULONG64 RemoteKey;
  773. NTSTATUS Status;
  774. PNAT_TICKET Ticketp;
  775. PNAT_USED_ADDRESS Usedp;
  776. TRACE(TICKET, ("NatProcessDeleteTicket\n"));
  777. //
  778. // Validate the parameters
  779. //
  780. if (0 == DeleteTicket->InterfaceIndex
  781. || INVALID_IF_INDEX == DeleteTicket->InterfaceIndex
  782. || (NAT_PROTOCOL_TCP != DeleteTicket->PortMapping.Protocol
  783. && NAT_PROTOCOL_UDP != DeleteTicket->PortMapping.Protocol)
  784. || 0 == DeleteTicket->PortMapping.PublicPort) {
  785. return STATUS_INVALID_PARAMETER;
  786. }
  787. RemoteKey =
  788. MAKE_TICKET_KEY(
  789. DeleteTicket->PortMapping.Protocol,
  790. 0,
  791. 0
  792. );
  793. //
  794. // Lookup and reference the interface
  795. //
  796. KeAcquireSpinLock(&InterfaceLock, &Irql);
  797. Interfacep = NatLookupInterface(DeleteTicket->InterfaceIndex, NULL);
  798. if (NULL == Interfacep) {
  799. KeReleaseSpinLock(&InterfaceLock, Irql);
  800. return STATUS_INVALID_PARAMETER;
  801. }
  802. if (Interfacep->FileObject != FileObject) {
  803. KeReleaseSpinLock(&InterfaceLock, Irql);
  804. return STATUS_ACCESS_DENIED;
  805. }
  806. if (!NatReferenceInterface(Interfacep)) {
  807. KeReleaseSpinLock(&InterfaceLock, Irql);
  808. return STATUS_INVALID_PARAMETER;
  809. }
  810. KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
  811. KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
  812. //
  813. // If the caller didn't specify a public address we need
  814. // to use the address of the interface itself
  815. //
  816. if (!DeleteTicket->PortMapping.PublicAddress) {
  817. Status =
  818. NatAcquireFromAddressPool(
  819. Interfacep,
  820. DeleteTicket->PortMapping.PrivateAddress,
  821. 0,
  822. &Usedp
  823. );
  824. if (NT_SUCCESS(Status)) {
  825. DeleteTicket->PortMapping.PublicAddress = Usedp->PublicAddress;
  826. NatDereferenceAddressPoolEntry(Interfacep, Usedp);
  827. }
  828. }
  829. Key =
  830. MAKE_TICKET_KEY(
  831. DeleteTicket->PortMapping.Protocol,
  832. DeleteTicket->PortMapping.PublicAddress,
  833. DeleteTicket->PortMapping.PublicPort
  834. );
  835. //
  836. // Search for the ticket on the interface, and delete
  837. // it if found.
  838. //
  839. Ticketp =
  840. NatLookupTicket(
  841. Interfacep,
  842. Key,
  843. RemoteKey,
  844. NULL
  845. );
  846. if (NULL != Ticketp) {
  847. NatDeleteTicket(Interfacep, Ticketp);
  848. Status = STATUS_SUCCESS;
  849. } else {
  850. Status = STATUS_NOT_FOUND;
  851. }
  852. KeReleaseSpinLock(&Interfacep->Lock, Irql);
  853. NatDereferenceInterface(Interfacep);
  854. return Status;
  855. } // NatProcessDeleteTicket
  856. NTSTATUS
  857. NatProcessLookupTicket(
  858. PIP_NAT_CREATE_TICKET LookupTicket,
  859. PIP_NAT_PORT_MAPPING Ticket,
  860. PFILE_OBJECT FileObject
  861. )
  862. /*++
  863. Routine Description:
  864. This routine is invoked to lookup a ticket in response to an
  865. 'IOCTL_IP_NAT_LOOKUP_TICKET' request.
  866. Arguments:
  867. LookupTicket - describes the ticket to search for
  868. Ticket - Receives the information about the ticket, if found
  869. Return Value:
  870. NTSTATUS - status code.
  871. --*/
  872. {
  873. PNAT_INTERFACE Interfacep;
  874. KIRQL Irql;
  875. ULONG64 Key;
  876. IP_NAT_PORT_MAPPING PortMapping;
  877. ULONG64 RemoteKey;
  878. NTSTATUS Status;
  879. PNAT_TICKET Ticketp;
  880. TRACE(TICKET, ("NatProcessLookupTicket\n"));
  881. //
  882. // Validate the parameters
  883. //
  884. if (0 == LookupTicket->InterfaceIndex
  885. || INVALID_IF_INDEX == LookupTicket->InterfaceIndex
  886. || (NAT_PROTOCOL_TCP != LookupTicket->PortMapping.Protocol
  887. && NAT_PROTOCOL_UDP != LookupTicket->PortMapping.Protocol)
  888. || 0 == LookupTicket->PortMapping.PublicPort) {
  889. return STATUS_INVALID_PARAMETER;
  890. }
  891. RemoteKey =
  892. MAKE_TICKET_KEY(
  893. LookupTicket->PortMapping.Protocol,
  894. 0,
  895. 0
  896. );
  897. //
  898. // Lookup and reference the interface
  899. //
  900. KeAcquireSpinLock(&InterfaceLock, &Irql);
  901. Interfacep = NatLookupInterface(LookupTicket->InterfaceIndex, NULL);
  902. if (NULL == Interfacep) {
  903. KeReleaseSpinLock(&InterfaceLock, Irql);
  904. return STATUS_INVALID_PARAMETER;
  905. }
  906. if (Interfacep->FileObject != FileObject) {
  907. KeReleaseSpinLock(&InterfaceLock, Irql);
  908. return STATUS_ACCESS_DENIED;
  909. }
  910. if (!NatReferenceInterface(Interfacep)) {
  911. KeReleaseSpinLock(&InterfaceLock, Irql);
  912. return STATUS_INVALID_PARAMETER;
  913. }
  914. KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
  915. KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
  916. //
  917. // If the caller didn't specify a public address we need
  918. // to use the address of the interface itself
  919. //
  920. if (!LookupTicket->PortMapping.PublicAddress
  921. && Interfacep->AddressCount > 0) {
  922. LookupTicket->PortMapping.PublicAddress =
  923. Interfacep->AddressArray[0].Address;
  924. }
  925. Key =
  926. MAKE_TICKET_KEY(
  927. LookupTicket->PortMapping.Protocol,
  928. LookupTicket->PortMapping.PublicAddress,
  929. LookupTicket->PortMapping.PublicPort
  930. );
  931. //
  932. // Search for the ticket on the interface.
  933. //
  934. Ticketp =
  935. NatLookupTicket(
  936. Interfacep,
  937. Key,
  938. RemoteKey,
  939. NULL
  940. );
  941. if (NULL != Ticketp) {
  942. //
  943. // We can't write into the output buffer while holding any
  944. // locks, since that buffer may be paged out. Copy the
  945. // information from the ticket into a local port mapping
  946. // structure.
  947. //
  948. PortMapping.Protocol = TICKET_PROTOCOL(Ticketp->Key);
  949. PortMapping.PublicAddress = TICKET_ADDRESS(Ticketp->Key);
  950. PortMapping.PublicPort = TICKET_PORT(Ticketp->Key);
  951. PortMapping.PrivateAddress = Ticketp->PrivateAddress;
  952. PortMapping.PrivatePort = Ticketp->PrivateOrHostOrderEndPort;
  953. Status = STATUS_SUCCESS;
  954. } else {
  955. Status = STATUS_NOT_FOUND;
  956. }
  957. KeReleaseSpinLock(&Interfacep->Lock, Irql);
  958. NatDereferenceInterface(Interfacep);
  959. if (NT_SUCCESS(Status)) {
  960. //
  961. // Copy the port mapping information into the
  962. // output buffer.
  963. //
  964. __try {
  965. RtlCopyMemory(Ticket, &PortMapping, sizeof(*Ticket));
  966. } __except(EXCEPTION_EXECUTE_HANDLER) {
  967. Status = GetExceptionCode();
  968. }
  969. }
  970. return Status;
  971. } // NatProcessLookupTicket
  972. VOID
  973. NatShutdownDynamicTicketManagement(
  974. VOID
  975. )
  976. /*++
  977. Routine Description:
  978. This routine is invoked to cleanup resources used for managing
  979. dynamic tickets.
  980. Arguments:
  981. none.
  982. Return Value:
  983. none.
  984. --*/
  985. {
  986. PNAT_DYNAMIC_TICKET Ticket;
  987. CALLTRACE(("NatShutdownDynamicTicketManagement\n"));
  988. while (!IsListEmpty(&DynamicTicketList)) {
  989. Ticket =
  990. CONTAINING_RECORD(
  991. DynamicTicketList.Flink, NAT_DYNAMIC_TICKET, Link
  992. );
  993. RemoveEntryList(&Ticket->Link);
  994. ExFreePool(Ticket);
  995. }
  996. DynamicTicketCount = 0;
  997. } // NatShutdownDynamicTicketManagement