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.

1086 lines
35 KiB

  1. /*++
  2. Copyright (c) 2000-2000 Microsoft Corporation
  3. Module Name:
  4. Connect.c
  5. Abstract:
  6. This module implements Connect handling routines
  7. for the PGM Transport
  8. Author:
  9. Mohammad Shabbir Alam (MAlam) 3-30-2000
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #include <tcpinfo.h> // for AO_OPTION_xxx, TCPSocketOption
  14. //******************* Pageable Routine Declarations ****************
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text(PAGE, PgmCreateConnection)
  17. #endif
  18. //******************* Pageable Routine Declarations ****************
  19. //----------------------------------------------------------------------------
  20. NTSTATUS
  21. PgmCreateConnection(
  22. IN tPGM_DEVICE *pPgmDevice,
  23. IN PIRP pIrp,
  24. IN PIO_STACK_LOCATION pIrpSp,
  25. IN PFILE_FULL_EA_INFORMATION TargetEA
  26. )
  27. /*++
  28. Routine Description:
  29. This routine is called to create a connection context for the client
  30. At this time, we do not knwo which address which connection will
  31. be associated with, nor do we know whether it will be a sender
  32. or a receiver.
  33. Arguments:
  34. IN pPgmDevice -- Pgm's Device object context
  35. IN pIrp -- Client's request Irp
  36. IN pIrpSp -- current request's stack pointer
  37. IN TargetEA -- contains the client's Connect context
  38. Return Value:
  39. NTSTATUS - Final status of the set event operation
  40. --*/
  41. {
  42. NTSTATUS status;
  43. CONNECTION_CONTEXT ConnectionContext;
  44. tCOMMON_SESSION_CONTEXT *pSession = NULL;
  45. PAGED_CODE();
  46. if (TargetEA->EaValueLength < sizeof(CONNECTION_CONTEXT))
  47. {
  48. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmCreateConnection",
  49. "(BufferLength=%d < Min=%d)\n", TargetEA->EaValueLength, sizeof(CONNECTION_CONTEXT));
  50. return (STATUS_INVALID_ADDRESS_COMPONENT);
  51. }
  52. if (!(pSession = PgmAllocMem (sizeof(tCOMMON_SESSION_CONTEXT), PGM_TAG('0'))))
  53. {
  54. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmCreateConnection",
  55. "STATUS_INSUFFICIENT_RESOURCES, Requested <%d> bytes\n", sizeof(tCOMMON_SESSION_CONTEXT));
  56. return (STATUS_INSUFFICIENT_RESOURCES);
  57. }
  58. PgmZeroMemory (pSession, sizeof (tCOMMON_SESSION_CONTEXT));
  59. InitializeListHead (&pSession->Linkage);
  60. PgmInitLock (pSession, SESSION_LOCK);
  61. pSession->Verify = PGM_VERIFY_SESSION_UNASSOCIATED;
  62. PGM_REFERENCE_SESSION_UNASSOCIATED (pSession, REF_SESSION_CREATE, TRUE);
  63. pSession->Process = (PEPROCESS) PsGetCurrentProcess();
  64. // the connection context value is stored in the string just after the
  65. // name "connectionContext", and it is most likely unaligned, so just
  66. // copy it out.( 4 bytes of copying ).
  67. PgmCopyMemory (&pSession->ClientSessionContext,
  68. (CONNECTION_CONTEXT) &TargetEA->EaName[TargetEA->EaNameLength+1],
  69. sizeof (CONNECTION_CONTEXT));
  70. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCreateConnection",
  71. "pSession=<%x>, ConnectionContext=<%x>\n",
  72. pSession, * ((PVOID UNALIGNED *) &TargetEA->EaName[TargetEA->EaNameLength+1]));
  73. // link on to list of open connections for this device so that we
  74. // know how many open connections there are at any time (if we need to know)
  75. // This linkage is only in place until the client does an associate, then
  76. // the connection is unlinked from here and linked to the client ConnectHead.
  77. //
  78. PgmInterlockedInsertTailList (&PgmDynamicConfig.ConnectionsCreated, &pSession->Linkage,&PgmDynamicConfig);
  79. pIrpSp->FileObject->FsContext = pSession;
  80. pIrpSp->FileObject->FsContext2 = (PVOID) TDI_CONNECTION_FILE;
  81. return (STATUS_SUCCESS);
  82. }
  83. //----------------------------------------------------------------------------
  84. VOID
  85. PgmCleanupSession(
  86. IN tCOMMON_SESSION_CONTEXT *pSession,
  87. IN PVOID Unused1,
  88. IN PVOID Unused2
  89. )
  90. /*++
  91. Routine Description:
  92. This routine performs the cleanup operation for a session
  93. handle once the RefCount goes to 0. It is called after a
  94. cleanup has been requested on this handle
  95. This routine has to be called at Passive Irql since we will
  96. need to do some file/section handle manipulation here.
  97. Arguments:
  98. IN pSession -- the session object to be cleaned up
  99. Return Value:
  100. NONE
  101. --*/
  102. {
  103. PIRP pIrpCleanup;
  104. PGMLockHandle OldIrq;
  105. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCleanupSession",
  106. "Cleanup Session=<%x>\n", pSession);
  107. if ((pSession->SessionFlags & PGM_SESSION_FLAG_SENDER) &&
  108. (pSession->pSender->SendDataBufferMapping))
  109. {
  110. PgmUnmapAndCloseDataFile (pSession);
  111. pSession->pSender->SendDataBufferMapping = NULL;
  112. }
  113. PgmLock (pSession, OldIrq);
  114. pSession->Process = NULL; // To remember that we have cleanedup
  115. if (pIrpCleanup = pSession->pIrpCleanup)
  116. {
  117. pSession->pIrpCleanup = NULL;
  118. PgmUnlock (pSession, OldIrq);
  119. PgmIoComplete (pIrpCleanup, STATUS_SUCCESS, 0);
  120. }
  121. else
  122. {
  123. PgmUnlock (pSession, OldIrq);
  124. }
  125. }
  126. //----------------------------------------------------------------------------
  127. VOID
  128. PgmDereferenceSessionCommon(
  129. IN tCOMMON_SESSION_CONTEXT *pSession,
  130. IN ULONG SessionType,
  131. IN ULONG RefContext
  132. )
  133. /*++
  134. Routine Description:
  135. This routine is called as a result of a dereference on a session object
  136. Arguments:
  137. IN pSession -- the Session object
  138. IN SessionType -- basically, whether it is PGM_VERIFY_SESSION_SEND
  139. or PGM_VERIFY_SESSION_RECEIVE
  140. IN RefContext -- the context for which this session object was
  141. referenced earlier
  142. Return Value:
  143. NONE
  144. --*/
  145. {
  146. NTSTATUS status;
  147. PGMLockHandle OldIrq;
  148. PIRP pIrpCleanup;
  149. LIST_ENTRY *pEntry;
  150. tNAK_FORWARD_DATA *pNak;
  151. PgmLock (pSession, OldIrq);
  152. ASSERT (PGM_VERIFY_HANDLE2 (pSession, SessionType, PGM_VERIFY_SESSION_DOWN));
  153. ASSERT (pSession->RefCount); // Check for too many derefs
  154. ASSERT (pSession->ReferenceContexts[RefContext]--);
  155. if (--pSession->RefCount)
  156. {
  157. PgmUnlock (pSession, OldIrq);
  158. return;
  159. }
  160. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmDereferenceSessionCommon",
  161. "pSession=<%x> Derefenced out, pIrpCleanup=<%x>\n", pSession, pSession->pIrpCleanup);
  162. ASSERT (!pSession->pAssociatedAddress);
  163. pIrpCleanup = pSession->pIrpCleanup;
  164. //
  165. // Sonce we may have a lot of memory buffered up, we need
  166. // to free it at non-Dpc
  167. //
  168. PgmUnlock (pSession, OldIrq);
  169. if (pSession->pReceiver)
  170. {
  171. CleanupPendingNaks (pSession, (PVOID) FALSE, NULL);
  172. }
  173. //
  174. // Remove from the global list
  175. //
  176. PgmLock (&PgmDynamicConfig, OldIrq);
  177. RemoveEntryList (&pSession->Linkage);
  178. PgmUnlock (&PgmDynamicConfig, OldIrq);
  179. //
  180. // If we are currently at Dpc, we will have to close the handles
  181. // on a Delayed Worker thread!
  182. //
  183. if (PgmGetCurrentIrql())
  184. {
  185. status = PgmQueueForDelayedExecution (PgmCleanupSession, pSession, NULL, NULL, FALSE);
  186. if (!NT_SUCCESS (status))
  187. {
  188. //
  189. // Apparently we ran out of Resources!
  190. // Complete the cleanup Irp for now and hope that the Close
  191. // can complete the rest of the cleanup
  192. //
  193. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmDereferenceSessionCommon",
  194. "OUT_OF_RSRC, cannot queue Worker request\n", pSession);
  195. if (pIrpCleanup)
  196. {
  197. pSession->pIrpCleanup = NULL;
  198. PgmIoComplete (pIrpCleanup, STATUS_SUCCESS, 0);
  199. }
  200. }
  201. }
  202. else
  203. {
  204. PgmCleanupSession (pSession, NULL, NULL);
  205. }
  206. }
  207. //----------------------------------------------------------------------------
  208. NTSTATUS
  209. PgmCleanupConnection(
  210. IN tCOMMON_SESSION_CONTEXT *pSession,
  211. IN PIRP pIrp
  212. )
  213. /*++
  214. Routine Description:
  215. This routine is called as a result of a close on the client's
  216. session handle. If we are a sender, our main job here is to
  217. send the FIN immediately, otherwise if we are a receiver, we
  218. need to remove ourselves from the timer list
  219. Arguments:
  220. IN pSession -- the Session object
  221. IN pIrp -- Client's request Irp
  222. Return Value:
  223. NTSTATUS - Final status of the request (STATUS_PENDING)
  224. --*/
  225. {
  226. tCLIENT_SEND_REQUEST *pSendContext;
  227. PGMLockHandle OldIrq, OldIrq1;
  228. PgmLock (&PgmDynamicConfig, OldIrq);
  229. PgmLock (pSession, OldIrq1);
  230. ASSERT (PGM_VERIFY_HANDLE3 (pSession, PGM_VERIFY_SESSION_UNASSOCIATED,
  231. PGM_VERIFY_SESSION_SEND,
  232. PGM_VERIFY_SESSION_RECEIVE));
  233. pSession->Verify = PGM_VERIFY_SESSION_DOWN;
  234. //
  235. // If Connection is currently associated, we must let the Disassociate handle
  236. // Removing the connection from the list
  237. //
  238. if (pSession->pAssociatedAddress)
  239. {
  240. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCleanupConnection",
  241. "WARNING: pSession=<%x : %x> was not disassociated from pAddress=<%x : %x> earlier\n",
  242. pSession, pSession->Verify,
  243. pSession->pAssociatedAddress, pSession->pAssociatedAddress->Verify);
  244. ASSERT (0);
  245. PgmUnlock (pSession, OldIrq1);
  246. PgmUnlock (&PgmDynamicConfig, OldIrq);
  247. PgmDisassociateAddress (pIrp, IoGetCurrentIrpStackLocation(pIrp));
  248. PgmLock (&PgmDynamicConfig, OldIrq);
  249. PgmLock (pSession, OldIrq1);
  250. }
  251. ASSERT (!pSession->pAssociatedAddress);
  252. //
  253. // Remove connection from either ConnectionsCreated list
  254. // or ConnectionsDisassociated list
  255. //
  256. RemoveEntryList (&pSession->Linkage);
  257. InsertTailList (&PgmDynamicConfig.CleanedUpConnections, &pSession->Linkage);
  258. PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_CONNECT, "PgmCleanupConnection",
  259. "pSession=<%x>\n", pSession);
  260. pSession->pIrpCleanup = pIrp;
  261. if (pSession->SessionFlags & PGM_SESSION_ON_TIMER)
  262. {
  263. pSession->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
  264. }
  265. PgmUnlock (pSession, OldIrq1);
  266. PgmUnlock (&PgmDynamicConfig, OldIrq);
  267. PGM_DEREFERENCE_SESSION_UNASSOCIATED (pSession, REF_SESSION_CREATE);
  268. return (STATUS_PENDING);
  269. }
  270. //----------------------------------------------------------------------------
  271. NTSTATUS
  272. PgmCloseConnection(
  273. IN PIRP pIrp,
  274. IN PIO_STACK_LOCATION pIrpSp
  275. )
  276. /*++
  277. Routine Description:
  278. This routine is the final dispatch operation to be performed
  279. after the cleanup, which should result in the session object
  280. being completely destroyed -- our RefCount must have already
  281. been set to 0 when we completed the Cleanup request.
  282. Arguments:
  283. IN pIrp -- Client's request Irp
  284. IN pIrpSp -- current request's stack pointer
  285. Return Value:
  286. NTSTATUS - Final status of the operation (STATUS_SUCCESS)
  287. --*/
  288. {
  289. tCOMMON_SESSION_CONTEXT *pSession = (tCOMMON_SESSION_CONTEXT *) pIrpSp->FileObject->FsContext;
  290. PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_CONNECT, "PgmCloseConnection",
  291. "pSession=<%x>\n", pSession);
  292. ASSERT (!pSession->pIrpCleanup);
  293. if (pSession->Process)
  294. {
  295. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCloseConnection",
  296. "WARNING! pSession=<%x>, Not yet cleaned up -- Calling cleanup again ...\n", pSession);
  297. PgmCleanupSession (pSession, NULL, NULL);
  298. }
  299. pIrpSp->FileObject->FsContext = NULL;
  300. if (pSession->FECOptions)
  301. {
  302. DestroyFECContext (&pSession->FECContext);
  303. }
  304. if (pSession->pSender)
  305. {
  306. ExDeleteResourceLite (&pSession->pSender->Resource); // Delete the resource
  307. if (pSession->pSender->DataFileName.Buffer)
  308. {
  309. PgmFreeMem (pSession->pSender->DataFileName.Buffer);
  310. }
  311. if (pSession->pSender->pProActiveParityContext)
  312. {
  313. PgmFreeMem (pSession->pSender->pProActiveParityContext);
  314. }
  315. if (pSession->pSender->MaxPayloadSize)
  316. {
  317. ExDeleteNPagedLookasideList (&pSession->pSender->SenderBufferLookaside);
  318. ExDeleteNPagedLookasideList (&pSession->pSender->SendContextLookaside);
  319. }
  320. PgmFreeMem (pSession->pSender);
  321. }
  322. else if (pSession->pReceiver)
  323. {
  324. if (pSession->pFECBuffer)
  325. {
  326. PgmFreeMem (pSession->pFECBuffer);
  327. }
  328. if (pSession->FECGroupSize)
  329. {
  330. ExDeleteNPagedLookasideList (&pSession->pReceiver->NonParityContextLookaside);
  331. if (pSession->FECOptions)
  332. {
  333. ExDeleteNPagedLookasideList (&pSession->pReceiver->ParityContextLookaside);
  334. }
  335. }
  336. PgmFreeMem (pSession->pReceiver);
  337. }
  338. PgmFreeMem (pSession);
  339. //
  340. // The final Dereference will complete the Irp!
  341. //
  342. return (STATUS_SUCCESS);
  343. }
  344. //----------------------------------------------------------------------------
  345. NTSTATUS
  346. PgmConnect(
  347. IN tPGM_DEVICE *pPgmDevice,
  348. IN PIRP pIrp,
  349. IN PIO_STACK_LOCATION pIrpSp
  350. )
  351. /*++
  352. Routine Description:
  353. This routine is called to setup a connection, but since we are
  354. doing PGM, there are no packets to be sent out. What we will
  355. do here is to create the file + section map for buffering the
  356. data packets, and also finalize the settings based on the default
  357. + user -specified settings
  358. Arguments:
  359. IN pPgmDevice -- Pgm's Device object context
  360. IN pIrp -- Client's request Irp
  361. IN pIrpSp -- current request's stack pointer
  362. Return Value:
  363. NTSTATUS - Final status of the connect operation
  364. --*/
  365. {
  366. tIPADDRESS IpAddress, OutIfAddress;
  367. LIST_ENTRY *pEntry;
  368. LIST_ENTRY *pEntry2;
  369. tLOCAL_INTERFACE *pLocalInterface = NULL;
  370. tADDRESS_ON_INTERFACE *pLocalAddress = NULL;
  371. USHORT Port;
  372. PGMLockHandle OldIrq;
  373. NTSTATUS status;
  374. ULONGLONG WindowAdvanceInMSecs;
  375. tADDRESS_CONTEXT *pAddress = NULL;
  376. tSEND_SESSION *pSend = (tSEND_SESSION *) pIrpSp->FileObject->FsContext;
  377. PTDI_REQUEST_KERNEL pRequestKernel = (PTDI_REQUEST_KERNEL) &pIrpSp->Parameters;
  378. ULONG Length, MCastTtl;
  379. //
  380. // Verify Minimum Buffer length!
  381. //
  382. if (!GetIpAddress (pRequestKernel->RequestConnectionInformation->RemoteAddress,
  383. pRequestKernel->RequestConnectionInformation->RemoteAddressLength,
  384. &IpAddress,
  385. &Port))
  386. {
  387. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect",
  388. "pSend=<%x>, Invalid Dest address!\n", pSend);
  389. return (STATUS_INVALID_ADDRESS_COMPONENT);
  390. }
  391. PgmLock (&PgmDynamicConfig, OldIrq);
  392. //
  393. // Now, verify that the Connection handle is valid + associated!
  394. //
  395. if ((!PGM_VERIFY_HANDLE (pSend, PGM_VERIFY_SESSION_SEND)) ||
  396. (!(pAddress = pSend->pAssociatedAddress)) ||
  397. (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)) ||
  398. (pAddress->Flags & PGM_ADDRESS_FLAG_INVALID_OUT_IF))
  399. {
  400. PgmUnlock (&PgmDynamicConfig, OldIrq);
  401. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect",
  402. "BAD Handle(s), pSend=<%x>, pAddress=<%x>\n", pSend, pAddress);
  403. return (STATUS_INVALID_HANDLE);
  404. }
  405. PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT, FALSE);
  406. //
  407. // If an outgoing interface has not yet been specified by the
  408. // client, select one ourselves
  409. //
  410. if (!pAddress->SenderMCastOutIf)
  411. {
  412. status = STATUS_ADDRESS_NOT_ASSOCIATED;
  413. OutIfAddress = 0;
  414. pEntry = &PgmDynamicConfig.LocalInterfacesList;
  415. while ((pEntry = pEntry->Flink) != &PgmDynamicConfig.LocalInterfacesList)
  416. {
  417. pLocalInterface = CONTAINING_RECORD (pEntry, tLOCAL_INTERFACE, Linkage);
  418. pEntry2 = &pLocalInterface->Addresses;
  419. while ((pEntry2 = pEntry2->Flink) != &pLocalInterface->Addresses)
  420. {
  421. pLocalAddress = CONTAINING_RECORD (pEntry2, tADDRESS_ON_INTERFACE, Linkage);
  422. OutIfAddress = htonl (pLocalAddress->IpAddress);
  423. PgmUnlock (&PgmDynamicConfig, OldIrq);
  424. status = SetSenderMCastOutIf (pAddress, OutIfAddress);
  425. PgmLock (&PgmDynamicConfig, OldIrq);
  426. break;
  427. }
  428. if (OutIfAddress)
  429. {
  430. break;
  431. }
  432. }
  433. if (!NT_SUCCESS (status))
  434. {
  435. PgmUnlock (&PgmDynamicConfig, OldIrq);
  436. PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
  437. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect",
  438. "Could not bind sender to <%x>!\n", OutIfAddress);
  439. return (STATUS_UNSUCCESSFUL);
  440. }
  441. }
  442. //
  443. // So, we found a valid address to send to
  444. //
  445. pSend->pSender->DestMCastIpAddress = ntohl (IpAddress);
  446. pSend->pSender->DestMCastPort = pAddress->SenderMCastPort = ntohs (Port);
  447. pSend->pSender->SenderMCastOutIf = pAddress->SenderMCastOutIf;
  448. //
  449. // Set the FEC Info (if applicable)
  450. //
  451. pSend->FECBlockSize = pAddress->FECBlockSize;
  452. pSend->FECGroupSize = pAddress->FECGroupSize;
  453. if (pAddress->FECOptions)
  454. {
  455. Length = sizeof(tBUILD_PARITY_CONTEXT) + pSend->FECGroupSize*sizeof(PUCHAR);
  456. if (!(pSend->pSender->pProActiveParityContext = (tBUILD_PARITY_CONTEXT *) PgmAllocMem (Length,PGM_TAG('0'))) ||
  457. (!NT_SUCCESS (status = CreateFECContext (&pSend->FECContext, pSend->FECGroupSize, pSend->FECBlockSize, FALSE))))
  458. {
  459. PgmUnlock (&PgmDynamicConfig, OldIrq);
  460. PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
  461. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect",
  462. "CreateFECContext returned <%x>, pSend=<%x>, Dest IpAddress=<%x>, Port=<%x>\n",
  463. status, pSend, IpAddress, Port);
  464. return (STATUS_INSUFFICIENT_RESOURCES);
  465. }
  466. pSend->FECOptions = pAddress->FECOptions;
  467. pSend->FECProActivePackets = pAddress->FECProActivePackets;
  468. pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_PARITY_PRM;
  469. pSend->pSender->LastVariableTGPacketSequenceNumber = -1;
  470. ASSERT (!(pSend->FECProActivePackets || pSend->FECProActivePackets) ||
  471. ((pSend->FECGroupSize && pSend->FECBlockSize) &&
  472. (pSend->FECGroupSize < pSend->FECBlockSize)));
  473. //
  474. // Now determine the MaxPayloadsize and buffer size
  475. // We will also need to adjust the buffer size to avoid alignment issues
  476. //
  477. Length = sizeof (tPACKET_OPTIONS) + pAddress->OutIfMTU + sizeof (tPOST_PACKET_FEC_CONTEXT);
  478. pSend->pSender->PacketBufferSize = (Length + sizeof (PVOID) - 1) & ~(sizeof (PVOID) - 1);
  479. pSend->pSender->MaxPayloadSize = pAddress->OutIfMTU - (PGM_MAX_FEC_DATA_HEADER_LENGTH + sizeof (USHORT));
  480. }
  481. else
  482. {
  483. Length = sizeof (tPACKET_OPTIONS) + pAddress->OutIfMTU;
  484. pSend->pSender->PacketBufferSize = (Length + sizeof (PVOID) - 1) & ~(sizeof (PVOID) - 1);
  485. pSend->pSender->MaxPayloadSize = pAddress->OutIfMTU - PGM_MAX_DATA_HEADER_LENGTH;
  486. }
  487. ASSERT (pSend->pSender->MaxPayloadSize);
  488. pSend->TSIPort = PgmDynamicConfig.SourcePort++; // Set the Global Src Port + Global Src Id
  489. if (pSend->TSIPort == PgmDynamicConfig.SourcePort)
  490. {
  491. //
  492. // We don't want the local port and remote port to be the same
  493. // (especially for handling TSI settings on loopback case),
  494. // so pick a different port
  495. //
  496. pSend->TSIPort = PgmDynamicConfig.SourcePort++;
  497. }
  498. //
  499. // Now, initialize the Sender info
  500. //
  501. InitializeListHead (&pSend->pSender->PendingSends);
  502. InitializeListHead (&pSend->pSender->PendingPacketizedSends);
  503. InitializeListHead (&pSend->pSender->CompletedSendsInWindow);
  504. InitializeListHead (&pSend->pSender->PendingRDataRequests);
  505. InitializeListHead (&pSend->pSender->HandledRDataRequests);
  506. MCastTtl = pAddress->MCastPacketTtl;
  507. PgmUnlock (&PgmDynamicConfig, OldIrq);
  508. KeInitializeEvent (&pSend->pSender->SendEvent, SynchronizationEvent, FALSE);
  509. //
  510. // Now, set the MCast TTL
  511. //
  512. status = PgmSetTcpInfo (pAddress->FileHandle,
  513. AO_OPTION_MCASTTTL,
  514. &MCastTtl,
  515. sizeof (ULONG));
  516. if (NT_SUCCESS (status))
  517. {
  518. //
  519. // Set the MCast TTL for the RouterAlert handle also
  520. //
  521. status = PgmSetTcpInfo (pAddress->RAlertFileHandle,
  522. AO_OPTION_MCASTTTL,
  523. &MCastTtl,
  524. sizeof (ULONG));
  525. }
  526. if (NT_SUCCESS (status))
  527. {
  528. status = PgmCreateDataFileAndMapSection (pSend);
  529. if (!NT_SUCCESS (status))
  530. {
  531. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect",
  532. "PgmCreateDataFileAndMapSection returned <%x>, pSend=<%x>, Dest IpAddress=<%x>, Port=<%x>\n",
  533. status, pSend, IpAddress, Port);
  534. }
  535. }
  536. else
  537. {
  538. PgmLog (PGM_LOG_ERROR, DBG_ADDRESS, "PgmConnect",
  539. "AO_OPTION_MCASTTTL returned <%x>\n", status);
  540. }
  541. if (!NT_SUCCESS (status))
  542. {
  543. pSend->pSender->MaxPayloadSize = 0;
  544. PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
  545. return (status);
  546. }
  547. PgmLock (&PgmDynamicConfig, OldIrq);
  548. //
  549. // Set the appropriate data parameters for the timeout routine
  550. // If the Send rate is quite high, we may need to send more than
  551. // 1 packet per timeout, but typically it should be low enough to
  552. // require several timeouts
  553. // Rate of Kb/Sec == Rate of Bytes/MilliSecs
  554. //
  555. ASSERT (pAddress->RateKbitsPerSec &&
  556. (BASIC_TIMER_GRANULARITY_IN_MSECS > BITS_PER_BYTE));
  557. if (((pAddress->RateKbitsPerSec * BASIC_TIMER_GRANULARITY_IN_MSECS) / BITS_PER_BYTE) >=
  558. pSend->pSender->PacketBufferSize)
  559. {
  560. //
  561. // We have a high Send rate, so we need to increment our window every timeout
  562. // So, Bytes to be sent in (x) Millisecs = Rate of Bytes/Millisecs * (x)
  563. //
  564. pSend->pSender->SendTimeoutCount = 1;
  565. }
  566. else
  567. {
  568. //
  569. // We will set our Send timeout count based for a slow timer
  570. // -- enough for pAddress->OutIfMTU
  571. // So, Number of Timeouts of x millisecs before we can send pAddress->OutIfMTU:
  572. // = Rate of Bytes/Millisecs * (x)
  573. //
  574. pSend->pSender->SendTimeoutCount = (pAddress->OutIfMTU +(pAddress->RateKbitsPerSec/BITS_PER_BYTE-1)) /
  575. ((pAddress->RateKbitsPerSec * BASIC_TIMER_GRANULARITY_IN_MSECS)/BITS_PER_BYTE);
  576. if (!pSend->pSender->SendTimeoutCount)
  577. {
  578. ASSERT (0);
  579. pSend->pSender->SendTimeoutCount = 1;
  580. }
  581. }
  582. pSend->pSender->IncrementBytesOnSendTimeout = (ULONG) (pAddress->RateKbitsPerSec *
  583. pSend->pSender->SendTimeoutCount *
  584. BASIC_TIMER_GRANULARITY_IN_MSECS) /
  585. BITS_PER_BYTE;
  586. //
  587. // Now, set the values for the next timeout!
  588. //
  589. pSend->pSender->CurrentTimeoutCount = pSend->pSender->SendTimeoutCount;
  590. pSend->pSender->CurrentBytesSendable = pSend->pSender->IncrementBytesOnSendTimeout;
  591. //
  592. // Set the SPM timeouts
  593. //
  594. pSend->pSender->CurrentSPMTimeout = 0;
  595. pSend->pSender->AmbientSPMTimeout = AMBIENT_SPM_TIMEOUT_IN_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS;
  596. pSend->pSender->InitialHeartbeatSPMTimeout = INITIAL_HEARTBEAT_SPM_TIMEOUT_IN_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS;
  597. pSend->pSender->MaxHeartbeatSPMTimeout = MAX_HEARTBEAT_SPM_TIMEOUT_IN_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS;
  598. pSend->pSender->HeartbeatSPMTimeout = pSend->pSender->InitialHeartbeatSPMTimeout;
  599. //
  600. // Set the Increment window settings
  601. //
  602. // TimerTickCount, LastWindowAdvanceTime and LastTrailingEdgeTime should be 0
  603. WindowAdvanceInMSecs = (((ULONGLONG)pAddress->WindowSizeInMSecs) * pAddress->WindowAdvancePercentage)/100;
  604. pSend->pSender->WindowSizeTime = pAddress->WindowSizeInMSecs / BASIC_TIMER_GRANULARITY_IN_MSECS;
  605. pSend->pSender->WindowAdvanceDeltaTime = WindowAdvanceInMSecs / BASIC_TIMER_GRANULARITY_IN_MSECS;
  606. pSend->pSender->NextWindowAdvanceTime = pSend->pSender->WindowSizeTime + pSend->pSender->WindowAdvanceDeltaTime;
  607. // Set the RData linger time!
  608. pSend->pSender->RDataLingerTime = RDATA_LINGER_TIME_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS;
  609. //
  610. // Set the late Joiner settings
  611. //
  612. if (pAddress->LateJoinerPercentage)
  613. {
  614. pSend->pSender->LateJoinSequenceNumbers = (SEQ_TYPE) ((pSend->pSender->MaxPacketsInBuffer *
  615. pAddress->LateJoinerPercentage) /
  616. (2 * 100));
  617. pSend->pSender->DataOptions |= PGM_OPTION_FLAG_JOIN;
  618. pSend->pSender->DataOptionsLength += PGM_PACKET_OPT_JOIN_LENGTH;
  619. pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_JOIN;
  620. }
  621. // The timer will be started when the first send comes down
  622. pSend->SessionFlags |= (PGM_SESSION_FLAG_FIRST_PACKET | PGM_SESSION_FLAG_SENDER);
  623. #if 0
  624. pSend->pSender->LeadingWindowOffset = pSend->pSender->TrailingWindowOffset =
  625. (pSend->pSender->MaxDataFileSize/(pSend->pSender->PacketBufferSize*2))*pSend->pSender->PacketBufferSize;
  626. #endif
  627. pSend->pSender->LeadingWindowOffset = pSend->pSender->TrailingWindowOffset = 0;
  628. PgmUnlock (&PgmDynamicConfig, OldIrq);
  629. PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
  630. ExInitializeNPagedLookasideList (&pSend->pSender->SenderBufferLookaside,
  631. NULL,
  632. NULL,
  633. 0,
  634. pAddress->OutIfMTU + sizeof (tPOST_PACKET_FEC_CONTEXT),
  635. PGM_TAG('2'),
  636. SENDER_BUFFER_LOOKASIDE_DEPTH);
  637. ExInitializeNPagedLookasideList (&pSend->pSender->SendContextLookaside,
  638. NULL,
  639. NULL,
  640. 0,
  641. sizeof (tCLIENT_SEND_REQUEST),
  642. PGM_TAG('2'),
  643. SEND_CONTEXT_LOOKASIDE_DEPTH);
  644. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmConnect",
  645. "DestIP=<%x>, Rate=<%d>, WinBytes=<%d>, WinMS=<%d>, SendTC=<%d>, IncBytes=<%d>, CurrentBS=<%d>\n",
  646. (ULONG) IpAddress, (ULONG) pAddress->RateKbitsPerSec,
  647. (ULONG) pAddress->WindowSizeInBytes, (ULONG) pAddress->WindowSizeInMSecs,
  648. (ULONG) pSend->pSender->SendTimeoutCount, (ULONG) pSend->pSender->IncrementBytesOnSendTimeout,
  649. (ULONG) pSend->pSender->CurrentBytesSendable);
  650. return (STATUS_SUCCESS);
  651. }
  652. //----------------------------------------------------------------------------
  653. VOID
  654. PgmCancelDisconnectIrp(
  655. IN PDEVICE_OBJECT DeviceContext,
  656. IN PIRP pIrp
  657. )
  658. /*++
  659. Routine Description:
  660. This routine handles the cancelling of a Disconnect Irp. It must
  661. release the cancel spin lock before returning re: IoCancelIrp().
  662. Arguments:
  663. Return Value:
  664. None
  665. --*/
  666. {
  667. PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
  668. tCOMMON_SESSION_CONTEXT *pSession = (tCOMMON_SESSION_CONTEXT *) pIrpSp->FileObject->FsContext;
  669. PGMLockHandle OldIrq;
  670. PIRP pIrpToComplete;
  671. if (!PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_SEND, PGM_VERIFY_SESSION_RECEIVE))
  672. {
  673. IoReleaseCancelSpinLock (pIrp->CancelIrql);
  674. PgmLog (PGM_LOG_ERROR, (DBG_SEND | DBG_ADDRESS | DBG_CONNECT), "PgmCancelDisconnectIrp",
  675. "pIrp=<%x> pSession=<%x>, pAddress=<%x>\n", pIrp, pSession, pSession->pAssociatedAddress);
  676. return;
  677. }
  678. PgmLock (pSession, OldIrq);
  679. ASSERT (pIrp == pSession->pIrpDisconnect);
  680. if ((pIrpToComplete = pSession->pIrpDisconnect) &&
  681. (pIrpToComplete == pIrp))
  682. {
  683. pSession->pIrpDisconnect = NULL;
  684. }
  685. else
  686. {
  687. pIrpToComplete = NULL;
  688. }
  689. if (pSession->pSender)
  690. {
  691. pSession->pSender->DisconnectTimeInTicks = pSession->pSender->TimerTickCount;
  692. }
  693. //
  694. // If we have reached here, then the Irp must already
  695. // be in the process of being completed!
  696. //
  697. PgmUnlock (pSession, OldIrq);
  698. IoReleaseCancelSpinLock (pIrp->CancelIrql);
  699. PgmLog (PGM_LOG_INFORM_ALL_FUNCS, (DBG_SEND | DBG_ADDRESS | DBG_CONNECT), "PgmCancelDisconnectIrp",
  700. "pIrp=<%x> was CANCELled, pIrpTpComplete=<%x>\n", pIrp, pIrpToComplete);
  701. if (pIrpToComplete)
  702. {
  703. pIrp->IoStatus.Information = 0;
  704. pIrp->IoStatus.Status = STATUS_CANCELLED;
  705. IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT);
  706. }
  707. }
  708. //----------------------------------------------------------------------------
  709. NTSTATUS
  710. PgmDisconnect(
  711. IN tPGM_DEVICE *pPgmDevice,
  712. IN PIRP pIrp,
  713. IN PIO_STACK_LOCATION pIrpSp
  714. )
  715. /*++
  716. Routine Description:
  717. This routine is called by the client to disconnect a currently-active
  718. session
  719. Arguments:
  720. IN pPgmDevice -- Pgm's Device object context
  721. IN pIrp -- Client's request Irp
  722. IN pIrpSp -- current request's stack pointer
  723. Return Value:
  724. NTSTATUS - Final status of the disconnect operation
  725. --*/
  726. {
  727. LIST_ENTRY PendingIrpsList;
  728. PGMLockHandle OldIrq1, OldIrq2, OldIrq3;
  729. PIRP pIrpReceive;
  730. NTSTATUS Status;
  731. LARGE_INTEGER TimeoutInMSecs;
  732. LARGE_INTEGER *pTimeoutInMSecs;
  733. tADDRESS_CONTEXT *pAddress = NULL;
  734. tCOMMON_SESSION_CONTEXT *pSession = (tCOMMON_SESSION_CONTEXT *) pIrpSp->FileObject->FsContext;
  735. PTDI_REQUEST_KERNEL_DISCONNECT pDisconnectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(pIrpSp->Parameters);
  736. PgmLock (&PgmDynamicConfig, OldIrq1);
  737. //
  738. // Now, verify that the Connection handle is valid + associated!
  739. //
  740. if ((!PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_SEND, PGM_VERIFY_SESSION_RECEIVE)) ||
  741. (!(pAddress = pSession->pAssociatedAddress)) ||
  742. (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)))
  743. {
  744. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmDisconnect",
  745. "BAD Handle(s), pSession=<%x>, pAddress=<%x>\n", pSession, pAddress);
  746. PgmUnlock (&PgmDynamicConfig, OldIrq1);
  747. return (STATUS_INVALID_HANDLE);
  748. }
  749. Status = STATUS_SUCCESS;
  750. InitializeListHead (&PendingIrpsList);
  751. TimeoutInMSecs.QuadPart = 0;
  752. IoAcquireCancelSpinLock (&OldIrq2);
  753. PgmLock (pSession, OldIrq3);
  754. if (pSession->pReceiver)
  755. {
  756. //
  757. // If we have any receive Irps pending, cancel them
  758. //
  759. RemovePendingIrps (pSession, &PendingIrpsList);
  760. }
  761. else if (pSession->pSender)
  762. {
  763. //
  764. // See if there is an abortive or graceful disconnect, and
  765. // also if there is a timeout specified.
  766. //
  767. if ((pDisconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) ||
  768. (pSession->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET)) // No packets sent yet!
  769. {
  770. pSession->pSender->DisconnectTimeInTicks = pSession->pSender->TimerTickCount;
  771. }
  772. else if (NT_SUCCESS (PgmCheckSetCancelRoutine (pIrp, PgmCancelDisconnectIrp, TRUE)))
  773. {
  774. if ((pTimeoutInMSecs = pDisconnectRequest->RequestSpecific) &&
  775. ((pTimeoutInMSecs->LowPart != -1) || (pTimeoutInMSecs->HighPart != -1))) // Check Infinite
  776. {
  777. //
  778. // NT relative timeouts are negative. Negate first to get a
  779. // positive value to pass to the transport.
  780. //
  781. TimeoutInMSecs.QuadPart = -((*pTimeoutInMSecs).QuadPart);
  782. TimeoutInMSecs = PgmConvert100nsToMilliseconds (TimeoutInMSecs);
  783. pSession->pSender->DisconnectTimeInTicks = pSession->pSender->TimerTickCount +
  784. TimeoutInMSecs.QuadPart /
  785. BASIC_TIMER_GRANULARITY_IN_MSECS;
  786. }
  787. pSession->pIrpDisconnect = pIrp;
  788. Status = STATUS_PENDING;
  789. }
  790. else
  791. {
  792. Status = STATUS_CANCELLED;
  793. }
  794. }
  795. if (NT_SUCCESS (Status))
  796. {
  797. pSession->SessionFlags |= PGM_SESSION_CLIENT_DISCONNECTED;
  798. }
  799. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmDisconnect",
  800. "pIrp=<%x>, pSession=<%x>, pAddress=<%x>, Timeout=<%x : %x>, %s\n",
  801. pIrp, pSession, pAddress, TimeoutInMSecs.QuadPart,
  802. (pDisconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT ? "ABORTive" : "GRACEful"));
  803. PgmUnlock (pSession, OldIrq3);
  804. IoReleaseCancelSpinLock (OldIrq2);
  805. PgmUnlock (&PgmDynamicConfig, OldIrq1);
  806. while (!IsListEmpty (&PendingIrpsList))
  807. {
  808. pIrpReceive = CONTAINING_RECORD (PendingIrpsList.Flink, IRP, Tail.Overlay.ListEntry);
  809. RemoveEntryList (&pIrpReceive->Tail.Overlay.ListEntry);
  810. PgmCancelCancelRoutine (pIrpReceive);
  811. pIrpReceive->IoStatus.Status = STATUS_CANCELLED;
  812. IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT);
  813. }
  814. return (Status);
  815. }
  816. //----------------------------------------------------------------------------
  817. NTSTATUS
  818. PgmSetRcvBufferLength(
  819. IN PIRP pIrp,
  820. IN PIO_STACK_LOCATION pIrpSp
  821. )
  822. /*++
  823. Routine Description:
  824. This routine is called by the client by the client to set the receive buffer length
  825. Currently, we do not utilize this option meaningfully.
  826. Arguments:
  827. IN pIrp -- Client's request Irp
  828. IN pIrpSp -- current request's stack pointer
  829. Return Value:
  830. NTSTATUS - Final status of the operation
  831. --*/
  832. {
  833. NTSTATUS status;
  834. tRECEIVE_SESSION *pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext;
  835. tPGM_MCAST_REQUEST *pInputBuffer = (tPGM_MCAST_REQUEST *) pIrp->AssociatedIrp.SystemBuffer;
  836. PAGED_CODE();
  837. if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (tPGM_MCAST_REQUEST))
  838. {
  839. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmSetRcvBufferLength",
  840. "Invalid BufferLength, <%d> < <%d>\n",
  841. pIrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof (tPGM_MCAST_REQUEST));
  842. return (STATUS_INVALID_PARAMETER);
  843. }
  844. if (!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE))
  845. {
  846. PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmSetRcvBufferLength",
  847. "Invalid Handle <%x>\n", pReceive);
  848. return (STATUS_INVALID_HANDLE);
  849. }
  850. pReceive->pReceiver->RcvBufferLength = pInputBuffer->RcvBufferLength;
  851. PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmSetRcvBufferLength",
  852. "RcvBufferLength=<%d>\n", pReceive->pReceiver->RcvBufferLength);
  853. //
  854. // ISSUE: What else should we do here ?
  855. //
  856. return (STATUS_SUCCESS);
  857. }
  858. //----------------------------------------------------------------------------