Leaked source code of windows server 2003
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.

1531 lines
41 KiB

  1. // Copyright (c) 1997, Microsoft Corporation, all rights reserved
  2. //
  3. // util.c
  4. // RAS L2TP WAN mini-port/call-manager driver
  5. // General utility routines
  6. //
  7. // 01/07/97 Steve Cobb
  8. #include "l2tpp.h"
  9. #include "util.tmh"
  10. // Debug counts of oddities that should not be happening.
  11. //
  12. ULONG g_ulAllocTwFailures = 0;
  13. //-----------------------------------------------------------------------------
  14. // Local prototypes (alphabetically)
  15. //-----------------------------------------------------------------------------
  16. ULONG
  17. atoul(
  18. IN CHAR* pszNumber );
  19. VOID
  20. ReversePsz(
  21. IN OUT CHAR* psz );
  22. VOID
  23. TunnelWork(
  24. IN NDIS_WORK_ITEM* pWork,
  25. IN VOID* pContext );
  26. VOID
  27. ultoa(
  28. IN ULONG ul,
  29. OUT CHAR* pszBuf );
  30. //-----------------------------------------------------------------------------
  31. // General utility routines (alphabetically)
  32. //-----------------------------------------------------------------------------
  33. #if 0
  34. ULONGLONG g_llLastTime2 = 0;
  35. ULONGLONG g_llLastTime1 = 0;
  36. ULONGLONG g_llLastTime = 0;
  37. NDIS_SPIN_LOCK g_lockX;
  38. VOID
  39. XNdisGetCurrentSystemTime(
  40. IN LARGE_INTEGER* plrgTime )
  41. {
  42. static BOOLEAN f = 0;
  43. if (!f)
  44. {
  45. NdisAllocateSpinLock( &g_lockX );
  46. f = 1;
  47. }
  48. NdisGetCurrentSystemTime( plrgTime );
  49. NdisAcquireSpinLock( &g_lockX );
  50. {
  51. LONGLONG ll;
  52. g_llLastTime2 = g_llLastTime1;
  53. g_llLastTime1 = g_llLastTime;
  54. g_llLastTime = plrgTime->QuadPart;
  55. ll = g_llLastTime - g_llLastTime1;
  56. TRACE( TL_I, TM_Spec, ( "Time delta=%d", *((LONG* )&ll) ) );
  57. ASSERT( g_llLastTime >= g_llLastTime1 );
  58. }
  59. NdisReleaseSpinLock( &g_lockX );
  60. }
  61. #endif
  62. VOID
  63. AddHostRoute(
  64. IN TUNNELWORK* pWork,
  65. IN TUNNELCB* pTunnel,
  66. IN VCCB* pVc,
  67. IN ULONG_PTR* punpArgs )
  68. // A PTUNNELWORK routine to change an existing host route.
  69. //
  70. // This routine is called only at PASSIVE IRQL.
  71. //
  72. {
  73. ADAPTERCB* pAdapter;
  74. TRACE( TL_N, TM_Misc, ( "AddHostRoute" ) );
  75. // Unpack context information then free the work item.
  76. //
  77. pAdapter = pTunnel->pAdapter;
  78. FREE_TUNNELWORK( pAdapter, pWork );
  79. // Add the host route, noting success for clean-up later, or closing the
  80. // tunnel on failure.
  81. //
  82. pTunnel->pRoute =
  83. TdixAddHostRoute(
  84. &pAdapter->tdix,
  85. pTunnel->address.ulIpAddress,
  86. pTunnel->localaddress.ifindex);
  87. if (pTunnel->pRoute != NULL)
  88. {
  89. NDIS_STATUS status;
  90. // Setup the connection to do connected udp
  91. // if required
  92. //
  93. pTunnel->pRoute->sPort = pTunnel->address.sUdpPort;
  94. status = TdixSetupConnection(
  95. &pAdapter->tdix,
  96. pTunnel->pRoute,
  97. pTunnel->localaddress.ulIpAddress,
  98. &pTunnel->udpContext);
  99. if(status != STATUS_SUCCESS)
  100. {
  101. TdixDestroyConnection(&pTunnel->udpContext);
  102. TdixDeleteHostRoute(&pAdapter->tdix,
  103. pTunnel->address.ulIpAddress);
  104. pTunnel->pRoute = NULL;
  105. ScheduleTunnelWork(
  106. pTunnel, NULL, FsmCloseTunnel,
  107. (ULONG_PTR )TRESULT_GeneralWithError,
  108. (ULONG_PTR )GERR_NoResources,
  109. 0, 0, FALSE, FALSE );
  110. }
  111. SetFlags( &pTunnel->ulFlags, TCBF_HostRouteAdded );
  112. if (pTunnel->udpContext.hCtrlAddr != NULL) {
  113. SetFlags (&pTunnel->ulFlags, TCBF_SendConnected);
  114. }
  115. }
  116. else
  117. {
  118. ScheduleTunnelWork(
  119. pTunnel, NULL, FsmCloseTunnel,
  120. (ULONG_PTR )TRESULT_GeneralWithError,
  121. (ULONG_PTR )GERR_NoResources,
  122. 0, 0, FALSE, FALSE );
  123. }
  124. }
  125. BOOLEAN
  126. AdjustSendWindowAtAckReceived(
  127. IN ULONG ulMaxSendWindow,
  128. IN OUT ULONG* pulAcksSinceSendTimeout,
  129. IN OUT ULONG* pulSendWindow )
  130. // Adjust send window/factors for the acknowledge just received.
  131. //
  132. // Returns true if the send window was changed, false if not.
  133. //
  134. {
  135. // Update the "ack streak" counter and, if a full windows worth has been
  136. // received since timing out, bump up the send window.
  137. //
  138. ++(*pulAcksSinceSendTimeout);
  139. if (*pulAcksSinceSendTimeout >= *pulSendWindow
  140. && *pulSendWindow < ulMaxSendWindow)
  141. {
  142. TRACE( TL_N, TM_Send,
  143. ( "SW open to %d, %d acks",
  144. (*pulSendWindow), *pulAcksSinceSendTimeout ) );
  145. *pulAcksSinceSendTimeout = 0;
  146. ++(*pulSendWindow);
  147. return TRUE;
  148. }
  149. return FALSE;
  150. }
  151. VOID
  152. AdjustTimeoutsAtAckReceived(
  153. IN LONGLONG llSendTime,
  154. IN ULONG ulMaxSendTimeoutMs,
  155. OUT ULONG* pulSendTimeoutMs,
  156. IN OUT ULONG* pulRoundTripMs,
  157. IN OUT LONG* plDeviationMs )
  158. // Adjust send timeout/factors for the acknowledge just received.
  159. //
  160. {
  161. LARGE_INTEGER lrgTime;
  162. LONGLONG llSampleMs;
  163. ULONG ulSampleMs;
  164. LONG lDiff;
  165. LONG lDif8;
  166. LONG lAbsDif8;
  167. LONG lDev8;
  168. ULONG ulAto;
  169. // First, calculate the "sample", i.e. the time that was actually required
  170. // for the round trip.
  171. //
  172. NdisGetCurrentSystemTime( &lrgTime );
  173. if (llSendTime > lrgTime.QuadPart)
  174. {
  175. // This shouldn't happen but once it appeared that it did, so this
  176. // defensive conditional is included. Maybe NdisGetCurrentSystemTime
  177. // has a bug?
  178. //
  179. TRACE( TL_A, TM_Misc, ( "Future send time?" ) );
  180. llSendTime = lrgTime.QuadPart;
  181. }
  182. llSampleMs = (lrgTime.QuadPart - llSendTime) / 10000;
  183. ASSERT( ((LARGE_INTEGER* )(&llSampleMs))->HighPart == 0 );
  184. ulSampleMs = (ULONG )(((LARGE_INTEGER* )(&llSampleMs))->LowPart);
  185. // The typical 'alpha' of 1/8, 'beta' of 1/4, and 'chi' of 4 are used, per
  186. // the suggestion in the draft/RFC. To eliminate multiplication and
  187. // division, the factors are scaled by 8, calculated, and scaled back.
  188. //
  189. // Find the intermediate DIFF value, representing the difference between
  190. // the estimated and actual round trip times, and the scaled and absolute
  191. // scaled values of same.
  192. //
  193. lDiff = (LONG )ulSampleMs - (LONG )(*pulRoundTripMs);
  194. lDif8 = lDiff << 3;
  195. lAbsDif8 = (lDif8 < 0) ? -lDif8 : lDif8;
  196. // Calculate the scaled new DEV value, representing the approximate
  197. // standard deviation.
  198. //
  199. lDev8 = *plDeviationMs << 3;
  200. lDev8 = lDev8 + ((lAbsDif8 - lDev8) << 1);
  201. *plDeviationMs = lDev8 >> 3;
  202. // Find the scaled new RTT value, representing the estimated round trip
  203. // time. The draft/RFC shows the calculation "old RTT + diff", but that's
  204. // just the "sample" we found earlier, i.e. the actual round trip time of
  205. // this packet.
  206. //
  207. *pulRoundTripMs = ulSampleMs;
  208. // Calculate the ATO value, representing the new send timeout. Because of
  209. // clock granularity the timeout might come out 0, which is converted to
  210. // the more reasonable 1.
  211. //
  212. ulAto = (ULONG )(((LONG )*pulRoundTripMs) + (*plDeviationMs << 2));
  213. if (ulAto == 0)
  214. {
  215. ulAto = 1;
  216. }
  217. *pulSendTimeoutMs = min( ulAto, ulMaxSendTimeoutMs );
  218. }
  219. VOID
  220. AdjustTimeoutsAndSendWindowAtTimeout(
  221. IN ULONG ulMaxSendTimeoutMs,
  222. IN LONG lDeviationMs,
  223. OUT ULONG* pulSendTimeoutMs,
  224. IN OUT ULONG* pulRoundTripMs,
  225. IN OUT ULONG* pulSendWindow,
  226. OUT ULONG* pulAcksSinceSendTimeout )
  227. // Adjust send timeout/factors and send window for the timeout that just
  228. // occurred.
  229. //
  230. // Returns true if the send window was changed, false if not.
  231. //
  232. {
  233. ULONG ulNew;
  234. // Using the suggested 'delta' of 2, the round trip estimate is doubled.
  235. //
  236. *pulRoundTripMs <<= 1;
  237. // Using the typical 'chi' of 4, the send timeout is increased. Because
  238. // of clock granularity the timeout might come out 0, which is converted
  239. // to the more reasonable 1.
  240. //
  241. ulNew = (ULONG )(((LONG )*pulRoundTripMs) + (lDeviationMs << 2));
  242. *pulSendTimeoutMs = min( ulNew, ulMaxSendTimeoutMs );
  243. if (*pulSendTimeoutMs == 0)
  244. {
  245. *pulSendTimeoutMs = 1;
  246. }
  247. // The send window is halved.
  248. //
  249. ulNew = *pulSendWindow >> 1;
  250. *pulSendWindow = max( ulNew, 1 );
  251. // Consecutive acknowledge counter is reset.
  252. //
  253. *pulAcksSinceSendTimeout = 0;
  254. }
  255. VOID
  256. CalculateResponse(
  257. IN UCHAR* puchChallenge,
  258. IN ULONG ulChallengeLength,
  259. IN CHAR* pszPassword,
  260. IN UCHAR uchId,
  261. OUT UCHAR* puchResponse )
  262. // Loads caller's 16-byte challenge response buffer, 'puchResponse', with
  263. // the CHAP-style MD5ed response based on packet ID 'uchId', the
  264. // 'ulChallengeLength' byte challenge 'puchChallenge', and the null
  265. // terminated password 'pszPassword'.
  266. //
  267. {
  268. ULONG ul;
  269. MD5_CTX md5ctx;
  270. MD5Init( &md5ctx );
  271. MD5Update( &md5ctx, &uchId, 1 );
  272. MD5Update( &md5ctx, pszPassword, strlen( pszPassword ) );
  273. MD5Update( &md5ctx, puchChallenge, ulChallengeLength );
  274. MD5Final( &md5ctx );
  275. NdisMoveMemory( puchResponse, md5ctx.digest, 16 );
  276. }
  277. VOID
  278. ChangeHostRoute(
  279. IN TUNNELWORK* pWork,
  280. IN TUNNELCB* pTunnel,
  281. IN VCCB* pVc,
  282. IN ULONG_PTR* punpArgs )
  283. // A PTUNNELWORK routine to change an existing host route. Arg0 is the IP
  284. // address of the existing host route to be deleted. Arg1 is the IP
  285. // address of the host route to add.
  286. //
  287. // This routine is called only at PASSIVE IRQL.
  288. //
  289. {
  290. ADAPTERCB* pAdapter;
  291. ULONG ulOldIpAddress;
  292. ULONG ulNewIpAddress;
  293. TRACE( TL_N, TM_Misc, ( "ChangeHostRoute" ) );
  294. // Unpack context information then free the work item.
  295. //
  296. pAdapter = pTunnel->pAdapter;
  297. ulOldIpAddress = (ULONG )(punpArgs[ 0 ]);
  298. ulNewIpAddress = (ULONG )(punpArgs[ 1 ]);
  299. FREE_TUNNELWORK( pAdapter, pWork );
  300. // Add the new host route, then delete the old one.
  301. //
  302. if (TdixAddHostRoute(
  303. &pAdapter->tdix,
  304. ulNewIpAddress,
  305. pTunnel->localaddress.ifindex))
  306. {
  307. ClearFlags( &pTunnel->ulFlags, TCBF_HostRouteAdded );
  308. TdixDestroyConnection(&pTunnel->udpContext);
  309. TdixDeleteHostRoute( &pAdapter->tdix, ulOldIpAddress);
  310. }
  311. else
  312. {
  313. ScheduleTunnelWork(
  314. pTunnel, NULL, CloseTunnel,
  315. 0, 0, 0, 0, FALSE, FALSE );
  316. }
  317. }
  318. VOID
  319. ClearFlags(
  320. IN OUT ULONG* pulFlags,
  321. IN ULONG ulMask )
  322. // Set 'ulMask' bits in '*pulFlags' flags as an interlocked operation.
  323. //
  324. {
  325. ULONG ulFlags;
  326. ULONG ulNewFlags;
  327. do
  328. {
  329. ulFlags = ReadFlags( pulFlags );
  330. ulNewFlags = ulFlags & ~(ulMask);
  331. }
  332. while (InterlockedCompareExchange(
  333. pulFlags, ulNewFlags, ulFlags ) != (LONG )ulFlags);
  334. }
  335. VOID
  336. CloseTdix(
  337. IN TUNNELWORK* pWork,
  338. IN TUNNELCB* pTunnel,
  339. IN VCCB* pVc,
  340. IN ULONG_PTR* punpArgs )
  341. // A PTUNNELWORK routine to close the TDIX context associated with a
  342. // tunnel.
  343. //
  344. // This routine is called only at PASSIVE IRQL.
  345. //
  346. {
  347. ADAPTERCB* pAdapter;
  348. TRACE( TL_N, TM_Misc, ( "CloseTdix" ) );
  349. // Unpack context information then free the work item.
  350. //
  351. pAdapter = pTunnel->pAdapter;
  352. FREE_TUNNELWORK( pAdapter, pWork );
  353. // Delete the old host route, and note same in tunnel flags.
  354. //
  355. TdixClose( &pAdapter->tdix );
  356. ClearFlags( &pTunnel->ulFlags, TCBF_TdixReferenced );
  357. }
  358. VOID
  359. DeleteHostRoute(
  360. IN TUNNELWORK* pWork,
  361. IN TUNNELCB* pTunnel,
  362. IN VCCB* pVc,
  363. IN ULONG_PTR* pulArgs )
  364. // A PTUNNELWORK routine to change an existing host route.
  365. //
  366. // This routine is called only at PASSIVE IRQL.
  367. //
  368. {
  369. ADAPTERCB* pAdapter;
  370. TRACE( TL_N, TM_Misc, ( "DeleteHostRoute" ) );
  371. // Unpack context information then free the work item.
  372. //
  373. pAdapter = pTunnel->pAdapter;
  374. FREE_TUNNELWORK( pAdapter, pWork );
  375. // Destroy the connected udp context
  376. //
  377. TdixDestroyConnection(&pTunnel->udpContext);
  378. // Delete the old host route, and note same in tunnel flags.
  379. //
  380. TdixDeleteHostRoute( &pAdapter->tdix,
  381. pTunnel->address.ulIpAddress);
  382. ClearFlags( &pTunnel->ulFlags, TCBF_HostRouteAdded );
  383. }
  384. VOID
  385. DottedFromIpAddress(
  386. IN ULONG ulIpAddress,
  387. OUT CHAR* pszIpAddress,
  388. IN BOOLEAN fUnicode )
  389. // Converts network byte-ordered IP addresss 'ulIpAddress' to a string in
  390. // the a.b.c.d form and returns same in caller's 'pszIpAddress' buffer.
  391. // The buffer should be at least 16 characters long. If 'fUnicode' is set
  392. // the returned 'pszIpAddress' is in Unicode and must be at least 16 wide
  393. // characters long.
  394. //
  395. {
  396. CHAR szBuf[ 3 + 1 ];
  397. ULONG ulA = (ulIpAddress & 0x000000FF);
  398. ULONG ulB = (ulIpAddress & 0x0000FF00) >> 8;
  399. ULONG ulC = (ulIpAddress & 0x00FF0000) >> 16;
  400. ULONG ulD = (ulIpAddress & 0xFF000000) >> 24;
  401. ultoa( ulA, szBuf );
  402. strcpy( pszIpAddress, szBuf );
  403. strcat( pszIpAddress, "." );
  404. ultoa( ulB, szBuf );
  405. strcat( pszIpAddress, szBuf );
  406. strcat( pszIpAddress, "." );
  407. ultoa( ulC, szBuf );
  408. strcat( pszIpAddress, szBuf );
  409. strcat( pszIpAddress, "." );
  410. ultoa( ulD, szBuf );
  411. strcat( pszIpAddress, szBuf );
  412. if (fUnicode)
  413. {
  414. WCHAR* psz;
  415. psz = StrDupAsciiToUnicode( pszIpAddress, strlen( pszIpAddress ) );
  416. if (psz)
  417. {
  418. NdisMoveMemory(
  419. pszIpAddress, psz, (StrLenW( psz ) + 1) * sizeof(WCHAR) );
  420. FREE_NONPAGED( psz );
  421. }
  422. else
  423. {
  424. *((WCHAR*)pszIpAddress) = L'\0';
  425. }
  426. }
  427. }
  428. #if 0
  429. NDIS_STATUS
  430. ExecuteWork(
  431. IN ADAPTERCB* pAdapter,
  432. IN NDIS_PROC pProc,
  433. IN PVOID pContext,
  434. IN ULONG ulArg1,
  435. IN ULONG ulArg2,
  436. IN ULONG ulArg3,
  437. IN ULONG ulArg4 )
  438. // This provides a way to call a routine designed to be called by the
  439. // ScheduleWork utility when caller is already at passive IRQL. The
  440. // 'pProc' routine is executed inline instead of scheduled. The context
  441. // 'pContext' is passed to 'pProc' The extra context arguments 'ulArg1'
  442. // and 'ulArg2' are stashed in extra space allocated on the end of the
  443. // NDIS_WORK_ITEM. 'PAdapter' is the adapter control block from which the
  444. // work item is allocated.
  445. //
  446. // Returns NDIS_STATUS_SUCCESS or an error code.
  447. //
  448. {
  449. NDIS_STATUS status;
  450. NDIS_WORK_ITEM* pWork;
  451. // TDI setup must be done at PASSIVE IRQL so schedule a routine to do it.
  452. //
  453. pWork = ALLOC_NDIS_WORK_ITEM( pAdapter );
  454. if (!pWork)
  455. {
  456. return NDIS_STATUS_RESOURCES;
  457. }
  458. ((ULONG*)(pWork + 1))[ 0 ] = ulArg1;
  459. ((ULONG*)(pWork + 1))[ 1 ] = ulArg2;
  460. ((ULONG*)(pWork + 1))[ 2 ] = ulArg3;
  461. ((ULONG*)(pWork + 1))[ 3 ] = ulArg4;
  462. pProc( pWork, pContext );
  463. }
  464. #endif
  465. USHORT
  466. GetNextTerminationCallId(
  467. IN ADAPTERCB* pAdapter )
  468. // Returns the next unused termination Call-ID. Termination Call-IDs are
  469. // IDs out of the VC lookup table range that are used to gracefully
  470. // terminate failed incoming calls.
  471. //
  472. {
  473. do
  474. {
  475. ++pAdapter->usNextTerminationCallId;
  476. }
  477. while (pAdapter->usNextTerminationCallId < pAdapter->usMaxVcs + 1);
  478. return pAdapter->usNextTerminationCallId;
  479. }
  480. USHORT
  481. GetNextTunnelId(
  482. IN ADAPTERCB* pAdapter )
  483. // Returns the next tunnel ID to be assigned.
  484. //
  485. // IMPORTANT: Caller must hold 'pAdapter->lockTunnels'.
  486. {
  487. while (++pAdapter->usNextTunnelId == 0)
  488. ;
  489. return pAdapter->usNextTunnelId;
  490. }
  491. CHAR*
  492. GetFullHostNameFromRegistry(
  493. VOID )
  494. // Returns a heap block containing an ASCII string of the form
  495. // "hostname.domain", or if no domain of the form "hostname". Returns
  496. // NULL if none. Caller must eventually call FREE_NONPAGED on the
  497. // returned string.
  498. //
  499. {
  500. NTSTATUS status;
  501. OBJECT_ATTRIBUTES objattr;
  502. UNICODE_STRING uni;
  503. HANDLE hParams;
  504. CHAR* pszResult;
  505. WCHAR* pszFullHostName;
  506. KEY_VALUE_PARTIAL_INFORMATION* pHostNameValue;
  507. KEY_VALUE_PARTIAL_INFORMATION* pDomainValue;
  508. ULONG ulSize;
  509. TRACE( TL_I, TM_Cm, ( "GetFullHostNameFromRegistry" ) );
  510. hParams = NULL;
  511. pszFullHostName = NULL;
  512. pHostNameValue = NULL;
  513. pDomainValue = NULL;
  514. pszResult = NULL;
  515. #define GFHNFR_BufSize 512
  516. do
  517. {
  518. // Get a handle to the TCPIP Parameters registry key.
  519. //
  520. RtlInitUnicodeString(
  521. &uni,
  522. L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters" );
  523. InitializeObjectAttributes(
  524. &objattr, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL );
  525. status = ZwOpenKey(
  526. &hParams, KEY_QUERY_VALUE, &objattr );
  527. if (status != STATUS_SUCCESS)
  528. {
  529. TRACE( TL_A, TM_Cm, ( "ZwOpenKey(ipp)=$%08x?", status ) );
  530. break;
  531. }
  532. // Query the "Hostname" registry value.
  533. //
  534. pHostNameValue = ALLOC_NONPAGED( GFHNFR_BufSize, MTAG_UTIL );
  535. if (!pHostNameValue)
  536. {
  537. break;
  538. }
  539. RtlInitUnicodeString( &uni, L"Hostname" );
  540. status = ZwQueryValueKey(
  541. hParams, &uni, KeyValuePartialInformation,
  542. pHostNameValue, GFHNFR_BufSize, &ulSize );
  543. if (status != STATUS_SUCCESS || pHostNameValue->Type != REG_SZ ||
  544. pHostNameValue->DataLength < sizeof(WCHAR) * 2)
  545. {
  546. TRACE( TL_A, TM_Cm, ( "ZwQValueKey=$%08x?", status ) );
  547. break;
  548. }
  549. ASSERT(pHostNameValue->DataLength < GFHNFR_BufSize);
  550. // Query the "Domain" registry value.
  551. //
  552. pDomainValue = ALLOC_NONPAGED( GFHNFR_BufSize, MTAG_UTIL );
  553. if (pDomainValue)
  554. {
  555. RtlInitUnicodeString( &uni, L"Domain" );
  556. status = ZwQueryValueKey(
  557. hParams, &uni, KeyValuePartialInformation,
  558. pDomainValue, GFHNFR_BufSize, &ulSize );
  559. }
  560. else
  561. {
  562. status = !STATUS_SUCCESS;
  563. }
  564. // Build a Unicode version of the combined "hostname.domain" or
  565. // "hostname".
  566. //
  567. pszFullHostName = ALLOC_NONPAGED( GFHNFR_BufSize * 2, MTAG_UTIL );
  568. if (!pszFullHostName)
  569. {
  570. break;
  571. }
  572. NdisMoveMemory(pszFullHostName, pHostNameValue->Data, pHostNameValue->DataLength);
  573. pszFullHostName[pHostNameValue->DataLength/2 - 1] = L'\0';
  574. if (status == STATUS_SUCCESS
  575. && pDomainValue->Type == REG_SZ
  576. && pDomainValue->DataLength >= sizeof(WCHAR) * 2
  577. && ((WCHAR* )pDomainValue->Data)[ 0 ] != L'\0')
  578. {
  579. WCHAR* pch;
  580. pch = &pszFullHostName[pHostNameValue->DataLength / 2 - 1];
  581. *pch = L'.';
  582. ++pch;
  583. NdisMoveMemory( pch, (WCHAR* )pDomainValue->Data, pDomainValue->DataLength);
  584. pch[pDomainValue->DataLength/2 - 1] = L'\0';
  585. }
  586. // Convert the Unicode version to ASCII.
  587. //
  588. pszResult = StrDupUnicodeToAscii(
  589. pszFullHostName, StrLenW( pszFullHostName ) * sizeof(WCHAR) );
  590. }
  591. while (FALSE);
  592. if (hParams)
  593. {
  594. ZwClose( hParams );
  595. }
  596. if (pHostNameValue)
  597. {
  598. FREE_NONPAGED( pHostNameValue );
  599. }
  600. if (pDomainValue)
  601. {
  602. FREE_NONPAGED( pDomainValue );
  603. }
  604. if (pszFullHostName)
  605. {
  606. FREE_NONPAGED( pszFullHostName );
  607. }
  608. return pszResult;
  609. }
  610. ULONG
  611. IpAddressFromDotted(
  612. IN CHAR* pchIpAddress )
  613. // Convert caller's a.b.c.d IP address string to the network byte-order
  614. // numeric equivalent.
  615. //
  616. // Returns the numeric IP address or 0 if formatted incorrectly.
  617. //
  618. {
  619. INT i;
  620. ULONG ulResult;
  621. CHAR* pch;
  622. ulResult = 0;
  623. pch = pchIpAddress;
  624. for (i = 1; i <= 4; ++i)
  625. {
  626. ULONG ulField;
  627. ulField = atoul( pch );
  628. if (ulField > 255)
  629. return 0;
  630. ulResult = (ulResult << 8) + ulField;
  631. while (*pch >= '0' && *pch <= '9')
  632. ++pch;
  633. if (i < 4 && *pch != '.')
  634. return 0;
  635. ++pch;
  636. }
  637. return htonl( ulResult );
  638. }
  639. VOID
  640. IndicateLinkStatus(
  641. IN VCCB* pVc,
  642. IN LINKSTATUSINFO* pInfo )
  643. // Indicate new WAN_CO_LINKPARAMS settings for 'pVc' to NDISWAN. Caller
  644. // should not be holding locks.
  645. //
  646. {
  647. ASSERT( pInfo->params.SendWindow > 0 );
  648. TRACE( TL_I, TM_Mp, ( "NdisMCoIndStatus(LINK) bps=%d sw=%d",
  649. pInfo->params.TransmitSpeed, pInfo->params.SendWindow ) );
  650. NdisMCoIndicateStatus(
  651. pInfo->MiniportAdapterHandle,
  652. pInfo->NdisVcHandle,
  653. NDIS_STATUS_WAN_CO_LINKPARAMS,
  654. &pInfo->params,
  655. sizeof(pInfo->params) );
  656. TRACE( TL_N, TM_Mp, ( "NdisMCoIndStatus done" ) );
  657. }
  658. CHAR*
  659. MsgTypePszFromUs(
  660. IN USHORT usMsgType )
  661. // Debug utility to convert message type attribute code 'usMsgType' to a
  662. // corresponding display string.
  663. //
  664. {
  665. static CHAR szBuf[ 5 + 1 ];
  666. static CHAR* aszMsgType[ 16 ] =
  667. {
  668. "SCCRQ",
  669. "SCCRP",
  670. "SCCCN",
  671. "StopCCN",
  672. "StopCCRP???",
  673. "Hello",
  674. "OCRQ",
  675. "OCRP",
  676. "OCCN",
  677. "ICRQ",
  678. "ICRP",
  679. "ICCN",
  680. "CCR???",
  681. "CDN",
  682. "WEN",
  683. "SLI"
  684. };
  685. if (usMsgType >= 1 && usMsgType <= 16)
  686. {
  687. return aszMsgType[ usMsgType - 1 ];
  688. }
  689. else
  690. {
  691. ultoa( (ULONG )usMsgType, szBuf );
  692. return szBuf;
  693. }
  694. }
  695. #ifndef READFLAGSDIRECT
  696. ULONG
  697. ReadFlags(
  698. IN ULONG* pulFlags )
  699. // Read the value of '*pulFlags' as an interlocked operation.
  700. //
  701. {
  702. return InterlockedExchangeAdd( pulFlags, 0 );
  703. }
  704. #endif
  705. VOID
  706. ScheduleTunnelWork(
  707. IN TUNNELCB* pTunnel,
  708. IN VCCB* pVc,
  709. IN PTUNNELWORK pHandler,
  710. IN ULONG_PTR unpArg0,
  711. IN ULONG_PTR unpArg1,
  712. IN ULONG_PTR unpArg2,
  713. IN ULONG_PTR unpArg3,
  714. IN BOOLEAN fTcbPreReferenced,
  715. IN BOOLEAN fHighPriority )
  716. // Schedules caller's 'pHandler' to be executed in an APC serially with
  717. // other work scheduled via this routine. 'PTunnel' is the tunnel to
  718. // which the work is related. 'UnpArgX' are the context arguments passed
  719. // to caller's 'pHandler'. 'FPreRefenced' indicates caller has already
  720. // made the tunnel reference associated with a scheduled work item. This
  721. // is a convenience if he already holds 'ADAPTERCB.lockTunnels'.
  722. // 'FHighPriority' causes the item to be queued at the head rather than
  723. // the tail of the list.
  724. //
  725. {
  726. ADAPTERCB* pAdapter;
  727. TUNNELWORK* pWork;
  728. pAdapter = pTunnel->pAdapter;
  729. if (!fTcbPreReferenced)
  730. {
  731. // Each queued work item holds a tunnel reference.
  732. //
  733. ReferenceTunnel( pTunnel, FALSE );
  734. }
  735. pWork = ALLOC_TUNNELWORK( pAdapter );
  736. if (!pWork)
  737. {
  738. // Can't get memory to schedule an APC so there's no
  739. // way we'll ever get things cleaned up.
  740. //
  741. ++g_ulAllocTwFailures;
  742. if (!fTcbPreReferenced)
  743. {
  744. DereferenceTunnel( pTunnel );
  745. }
  746. return;
  747. }
  748. if (pVc)
  749. {
  750. // Each queued work item that refers to a VC holds a VC reference.
  751. //
  752. ReferenceVc( pVc );
  753. }
  754. pWork->pHandler = pHandler;
  755. pWork->pVc = pVc;
  756. pWork->aunpArgs[ 0 ] = unpArg0;
  757. pWork->aunpArgs[ 1 ] = unpArg1;
  758. pWork->aunpArgs[ 2 ] = unpArg2;
  759. pWork->aunpArgs[ 3 ] = unpArg3;
  760. NdisAcquireSpinLock( &pTunnel->lockWork );
  761. {
  762. if (fHighPriority)
  763. {
  764. InsertHeadList( &pTunnel->listWork, &pWork->linkWork );
  765. TRACE( TL_N, TM_TWrk, ( "Q-TunnelWork($%08x,HIGH)", pHandler ) );
  766. }
  767. else
  768. {
  769. InsertTailList( &pTunnel->listWork, &pWork->linkWork );
  770. TRACE( TL_N, TM_TWrk, ( "Q-TunnelWork($%08x)", pHandler ) );
  771. }
  772. // Kickstart the tunnel worker if it's not running already.
  773. //
  774. if (!(ReadFlags( &pTunnel->ulFlags ) & TCBF_InWork ))
  775. {
  776. SetFlags( &pTunnel->ulFlags, TCBF_InWork );
  777. TRACE( TL_N, TM_TWrk, ( "Schedule TunnelWork" ) );
  778. ScheduleWork( pAdapter, TunnelWork, pTunnel );
  779. }
  780. }
  781. NdisReleaseSpinLock( &pTunnel->lockWork );
  782. }
  783. NDIS_STATUS
  784. ScheduleWork(
  785. IN ADAPTERCB* pAdapter,
  786. IN NDIS_PROC pProc,
  787. IN PVOID pContext )
  788. // Schedules a PASSIVE IRQL callback to routine 'pProc' which will be
  789. // passed 'pContext'. 'PAdapter' is the adapter control block from which
  790. // the work item is allocated. This routine takes an adapter reference
  791. // that should be removed by the called 'pProc'.
  792. //
  793. // Returns NDIS_STATUS_SUCCESS or an error code.
  794. //
  795. {
  796. NDIS_STATUS status;
  797. NDIS_WORK_ITEM* pWork;
  798. pWork = ALLOC_NDIS_WORK_ITEM( pAdapter );
  799. if (!pWork)
  800. {
  801. return NDIS_STATUS_RESOURCES;
  802. }
  803. NdisInitializeWorkItem( pWork, pProc, pContext );
  804. ReferenceAdapter( pAdapter );
  805. status = NdisScheduleWorkItem( pWork );
  806. if (status != NDIS_STATUS_SUCCESS)
  807. {
  808. FREE_NDIS_WORK_ITEM( pAdapter, pWork );
  809. DereferenceAdapter( pAdapter );
  810. }
  811. return status;
  812. }
  813. VOID
  814. SetFlags(
  815. IN OUT ULONG* pulFlags,
  816. IN ULONG ulMask )
  817. // Set 'ulMask' bits in '*pulFlags' flags as an interlocked operation.
  818. //
  819. {
  820. ULONG ulFlags;
  821. ULONG ulNewFlags;
  822. do
  823. {
  824. ulFlags = InterlockedExchangeAdd( pulFlags, 0 );
  825. ulNewFlags = ulFlags | ulMask;
  826. }
  827. while (InterlockedCompareExchange(
  828. pulFlags, ulNewFlags, ulFlags ) != (LONG )ulFlags);
  829. }
  830. WCHAR*
  831. StrDupNdisString(
  832. IN NDIS_STRING* pNdisString )
  833. // Returns null-terminated Unicode copy of the NDIS_STRING 'pNdisString'
  834. // Caller must eventually call FREE_NONPAGED on the returned string.
  835. //
  836. {
  837. WCHAR* pwszDup = NULL;
  838. if(pNdisString->Length >= sizeof(WCHAR) && (pNdisString->Length & 1) == 0 &&
  839. pNdisString->Buffer[0] != L'\0')
  840. {
  841. pwszDup = ALLOC_NONPAGED( pNdisString->Length + sizeof(WCHAR), MTAG_UTIL );
  842. if (pwszDup)
  843. {
  844. NdisMoveMemory( pwszDup, pNdisString->Buffer, pNdisString->Length );
  845. pwszDup[pNdisString->Length / sizeof(WCHAR)] = L'\0';
  846. }
  847. }
  848. return pwszDup;
  849. }
  850. CHAR*
  851. StrDupNdisStringToA(
  852. IN NDIS_STRING* pNdisString )
  853. // Returns null-terminated ASCII copy of the NDIS_STRING 'pNdisString'
  854. // Caller must eventually call FREE_NONPAGED on the returned string.
  855. //
  856. {
  857. return StrDupUnicodeToAscii( pNdisString->Buffer, pNdisString->Length );
  858. }
  859. CHAR*
  860. StrDupNdisVarDataDescStringToA(
  861. IN NDIS_VAR_DATA_DESC UNALIGNED* pDesc )
  862. // Returns null-terminated ASCII copy of the NDIS_VAR_DATA_DESC string
  863. // 'pDesc'. Caller must eventually call FREE_NON-PAGED on the returned
  864. // string.
  865. //
  866. {
  867. return StrDupUnicodeToAscii(
  868. (WCHAR* )(((CHAR* )pDesc) + pDesc->Offset), pDesc->Length );
  869. }
  870. CHAR*
  871. StrDupSized(
  872. IN CHAR* psz,
  873. IN ULONG ulLength,
  874. IN ULONG ulExtra )
  875. // Return a duplicate of the first 'ulLength' bytes of 'psz' followed by a
  876. // null character and 'ulExtra' extra bytes, or NULL on error. Caller
  877. // must eventually call FREE_NONPAGED on the returned string.
  878. //
  879. {
  880. CHAR* pszDup = NULL;
  881. if(ulLength && psz[0] != '\0')
  882. {
  883. pszDup = ALLOC_NONPAGED( ulLength + 1 + ulExtra, MTAG_UTIL );
  884. if (pszDup)
  885. {
  886. NdisMoveMemory( pszDup, psz, ulLength );
  887. pszDup[ ulLength ] = '\0';
  888. }
  889. }
  890. return pszDup;
  891. }
  892. CHAR*
  893. StrDupUnicodeToAscii(
  894. IN WCHAR* pwsz,
  895. IN ULONG ulPwszBytes )
  896. // Returns an ASCII duplicate of Unicode string 'pwsz', where 'pwsz' is
  897. // 'ulPwszBytes' in length and not necessarily null terminated. A null
  898. // terminator is added to the ASCII result. The "conversion" consists of
  899. // picking out every other byte, hopefully all the non-zero ones. This is
  900. // not foolproof, but then Unicode doesn't convert to ASCII in any
  901. // foolproof way. It is caller's responsibility to FREE_NONPAGED the
  902. // returned string, if non-NULL.
  903. //
  904. {
  905. CHAR* pszDup = NULL;
  906. // Validate the input parameters
  907. // Don't allow empty string
  908. if(ulPwszBytes >= sizeof(WCHAR) && (ulPwszBytes & 1) == 0 &&
  909. pwsz[0] != L'\0' && *((PCHAR)pwsz + 1) == '\0')
  910. {
  911. pszDup = ALLOC_NONPAGED( ulPwszBytes/2 + 1, MTAG_UTIL );
  912. if (pszDup)
  913. {
  914. ULONG i;
  915. for (i = 0; i < ulPwszBytes / sizeof(WCHAR); ++i)
  916. {
  917. pszDup[ i ] = (CHAR)pwsz[ i ];
  918. }
  919. pszDup[ulPwszBytes / sizeof(WCHAR)] = '\0';
  920. }
  921. }
  922. return pszDup;
  923. }
  924. WCHAR*
  925. StrDupAsciiToUnicode(
  926. IN CHAR* psz,
  927. IN ULONG ulPszBytes )
  928. // Returns a Unicode duplicate of ASCII string 'psz', where 'psz' is
  929. // 'ulPszBytes' in length and not necessarily null terminated. A null
  930. // terminator is added to the Unicode result. The "conversion" consists
  931. // of adding zero characters every other byte. This is not foolproof, but
  932. // is OK for numericals like IP address strings, avoiding the change to
  933. // PASSIVE IRQL required to use the real RTL conversions. It is caller's
  934. // responsibility to FREE_NONPAGED the returned string, if non-NULL.
  935. //
  936. {
  937. WCHAR* pwszDup = NULL;
  938. if(ulPszBytes >= sizeof(CHAR) && psz[0] != '\0')
  939. {
  940. pwszDup = (WCHAR* )ALLOC_NONPAGED(
  941. (ulPszBytes + 1) * sizeof(WCHAR), MTAG_UTIL );
  942. if (pwszDup)
  943. {
  944. ULONG i;
  945. for (i = 0; i < ulPszBytes; ++i)
  946. {
  947. pwszDup[ i ] = (WCHAR )(psz[ i ]);
  948. }
  949. pwszDup[ i ] = L'\0';
  950. }
  951. }
  952. return pwszDup;
  953. }
  954. ULONG
  955. StrLenW(
  956. IN WCHAR* psz )
  957. // Return the length in characters of null terminated wide string 'psz'.
  958. //
  959. {
  960. ULONG ulLen;
  961. ulLen = 0;
  962. if (psz)
  963. {
  964. while (*psz++ != L'\0')
  965. {
  966. ++ulLen;
  967. }
  968. }
  969. return ulLen;
  970. }
  971. TUNNELCB*
  972. TunnelCbFromIpAddressAndAssignedTunnelId(
  973. IN ADAPTERCB* pAdapter,
  974. IN ULONG ulIpAddress,
  975. IN USHORT usUdpPort,
  976. IN USHORT usAssignedTunnelId )
  977. // Return the tunnel control block associated with 'ulIpAddress' in
  978. // 'pAdapter's list of TUNNELCBs or NULL if not found. If
  979. // 'usAssignedTunnelId' is non-zero, that must match as well, otherwise it
  980. // is ignored. Tunnels in the process of closing are not returned.
  981. //
  982. // IMPORTANT: Caller must hold 'pAdapter->lockTunnels'.
  983. //
  984. {
  985. TUNNELCB* pTunnel;
  986. LIST_ENTRY* pLink;
  987. pTunnel = NULL;
  988. for (pLink = pAdapter->listTunnels.Flink;
  989. pLink != &pAdapter->listTunnels;
  990. pLink = pLink->Flink)
  991. {
  992. TUNNELCB* pThis;
  993. pThis = CONTAINING_RECORD( pLink, TUNNELCB, linkTunnels );
  994. if (pThis->address.ulIpAddress == ulIpAddress
  995. && (!usUdpPort
  996. || usUdpPort == pThis->address.sUdpPort)
  997. && (!usAssignedTunnelId
  998. || usAssignedTunnelId == pThis->usAssignedTunnelId))
  999. {
  1000. BOOLEAN fClosing;
  1001. fClosing = !!(ReadFlags( &pThis->ulFlags ) & TCBF_Closing);
  1002. if (fClosing)
  1003. {
  1004. TRACE( TL_A, TM_Misc, ( "Closing pT=$%p skipped", pThis ) );
  1005. }
  1006. else
  1007. {
  1008. pTunnel = pThis;
  1009. break;
  1010. }
  1011. }
  1012. }
  1013. return pTunnel;
  1014. }
  1015. VOID
  1016. TransferLinkStatusInfo(
  1017. IN VCCB* pVc,
  1018. OUT LINKSTATUSINFO* pInfo )
  1019. // Transfer information from 'pVc' to callers 'pInfo' block in preparation
  1020. // for a call to IndicateLinkStatus after 'lockV' has been released.
  1021. //
  1022. // IMPORTANT: Caller must hold 'pVc->lockV'.
  1023. //
  1024. {
  1025. ADAPTERCB* pAdapter;
  1026. pAdapter = pVc->pAdapter;
  1027. pInfo->MiniportAdapterHandle = pAdapter->MiniportAdapterHandle;
  1028. pInfo->NdisVcHandle = pVc->NdisVcHandle;
  1029. //
  1030. // Convert to bytes per second
  1031. //
  1032. pInfo->params.TransmitSpeed = pVc->ulConnectBps/8;
  1033. pInfo->params.ReceiveSpeed = pInfo->params.TransmitSpeed/8;
  1034. pInfo->params.SendWindow =
  1035. min( pVc->ulSendWindow, pAdapter->info.MaxSendWindow );
  1036. }
  1037. VOID
  1038. TunnelWork(
  1039. IN NDIS_WORK_ITEM* pWork,
  1040. IN VOID* pContext )
  1041. // An NDIS_PROC routine to execute work from a tunnel work queue. The
  1042. // context passed is the TUNNELCB, which has been referenced for this
  1043. // operation.
  1044. //
  1045. // This routine is called only at PASSIVE IRQL.
  1046. //
  1047. {
  1048. ADAPTERCB* pAdapter;
  1049. TUNNELCB* pTunnel;
  1050. LIST_ENTRY* pLink;
  1051. LONG lDerefTunnels;
  1052. // Unpack context information then free the work item.
  1053. //
  1054. pTunnel = (TUNNELCB* )pContext;
  1055. pAdapter = pTunnel->pAdapter;
  1056. FREE_NDIS_WORK_ITEM( pAdapter, pWork );
  1057. // Execute all work queued on the tunnel serially.
  1058. //
  1059. lDerefTunnels = 0;
  1060. NdisAcquireSpinLock( &pTunnel->lockWork );
  1061. {
  1062. ASSERT( ReadFlags( &pTunnel->ulFlags ) & TCBF_InWork );
  1063. while (!IsListEmpty( &pTunnel->listWork ))
  1064. {
  1065. TUNNELWORK* pTunnelWork;
  1066. pLink = RemoveHeadList( &pTunnel->listWork );
  1067. InitializeListHead( pLink );
  1068. pTunnelWork = CONTAINING_RECORD( pLink, TUNNELWORK, linkWork );
  1069. TRACE( TL_N, TM_TWrk,
  1070. ( "\nL2TP: TUNNELWORK=$%08x", pTunnelWork->pHandler ) );
  1071. NdisReleaseSpinLock( &pTunnel->lockWork );
  1072. {
  1073. VCCB* pVc;
  1074. pVc = pTunnelWork->pVc;
  1075. pTunnelWork->pHandler( pTunnelWork, pTunnel, pVc, pTunnelWork->aunpArgs );
  1076. if (pVc)
  1077. {
  1078. DereferenceVc( pVc );
  1079. }
  1080. ++lDerefTunnels;
  1081. }
  1082. NdisAcquireSpinLock( &pTunnel->lockWork );
  1083. }
  1084. ClearFlags( &pTunnel->ulFlags, TCBF_InWork );
  1085. }
  1086. NdisReleaseSpinLock( &pTunnel->lockWork );
  1087. while (lDerefTunnels--)
  1088. {
  1089. DereferenceTunnel( pTunnel );
  1090. }
  1091. // Remove the reference for scheduled work.
  1092. //
  1093. DereferenceAdapter( pAdapter );
  1094. }
  1095. VOID
  1096. UpdateGlobalCallStats(
  1097. IN VCCB* pVc )
  1098. // Add the call statistics in 'pVc' to the global call statistics.
  1099. //
  1100. // IMPORTANT: Caller must hold 'pVc->lockV'.
  1101. //
  1102. {
  1103. extern CALLSTATS g_stats;
  1104. extern NDIS_SPIN_LOCK g_lockStats;
  1105. CALLSTATS* pStats;
  1106. pStats = &pVc->stats;
  1107. if (pStats->ulSeconds == 0)
  1108. {
  1109. return;
  1110. }
  1111. NdisAcquireSpinLock( &g_lockStats );
  1112. {
  1113. ++g_stats.llCallUp;
  1114. g_stats.ulSeconds += pStats->ulSeconds;
  1115. g_stats.ulDataBytesRecd += pStats->ulDataBytesRecd;
  1116. g_stats.ulDataBytesSent += pStats->ulDataBytesSent;
  1117. g_stats.ulRecdDataPackets += pStats->ulRecdDataPackets;
  1118. g_stats.ulDataPacketsDequeued += pStats->ulDataPacketsDequeued;
  1119. g_stats.ulRecdZlbs += pStats->ulRecdZlbs;
  1120. g_stats.ulRecdResets += pStats->ulRecdResets;
  1121. g_stats.ulRecdResetsIgnored += pStats->ulRecdResetsIgnored;
  1122. g_stats.ulSentDataPacketsSeq += pStats->ulSentDataPacketsSeq;
  1123. g_stats.ulSentDataPacketsUnSeq += pStats->ulSentDataPacketsUnSeq;
  1124. g_stats.ulSentPacketsAcked += pStats->ulSentPacketsAcked;
  1125. g_stats.ulSentPacketsTimedOut += pStats->ulSentPacketsTimedOut;
  1126. g_stats.ulSentZAcks += pStats->ulSentZAcks;
  1127. g_stats.ulSentResets += pStats->ulSentResets;
  1128. g_stats.ulSendWindowChanges += pStats->ulSendWindowChanges;
  1129. g_stats.ulSendWindowTotal += pStats->ulSendWindowTotal;
  1130. g_stats.ulMaxSendWindow += pStats->ulMaxSendWindow;
  1131. g_stats.ulMinSendWindow += pStats->ulMinSendWindow;
  1132. g_stats.ulRoundTrips += pStats->ulRoundTrips;
  1133. g_stats.ulRoundTripMsTotal += pStats->ulRoundTripMsTotal;
  1134. g_stats.ulMaxRoundTripMs += pStats->ulMaxRoundTripMs;
  1135. g_stats.ulMinRoundTripMs += pStats->ulMinRoundTripMs;
  1136. }
  1137. NdisReleaseSpinLock( &g_lockStats );
  1138. TRACE( TL_I, TM_Stat,
  1139. ( ".--- CALL STATISTICS -------------------------" ) );
  1140. TRACE( TL_I, TM_Stat,
  1141. ( "| Duration: %d minutes, %d seconds",
  1142. pStats->ulSeconds / 60,
  1143. pStats->ulSeconds % 60 ) );
  1144. TRACE( TL_I, TM_Stat,
  1145. ( "| Data out: %d bytes, %d/sec, %d/pkt",
  1146. pStats->ulDataBytesSent,
  1147. AVGTRACE(
  1148. pStats->ulDataBytesSent,
  1149. pStats->ulSeconds ),
  1150. AVGTRACE(
  1151. pStats->ulDataBytesSent,
  1152. pStats->ulRecdDataPackets ) ) );
  1153. TRACE( TL_I, TM_Stat,
  1154. ( "| Data in: %d bytes, %d/sec, %d/pkt",
  1155. pStats->ulDataBytesRecd,
  1156. AVGTRACE( pStats->ulDataBytesRecd, pStats->ulSeconds ),
  1157. AVGTRACE(
  1158. pStats->ulDataBytesRecd,
  1159. pStats->ulSentDataPacketsSeq
  1160. + pStats->ulSentDataPacketsUnSeq ) ) );
  1161. TRACE( TL_I, TM_Stat,
  1162. ( "| Acks in: %d/%d (%d%%) %d flushed",
  1163. pStats->ulSentPacketsAcked,
  1164. pStats->ulSentDataPacketsSeq,
  1165. PCTTRACE(
  1166. pStats->ulSentPacketsAcked,
  1167. pStats->ulSentPacketsAcked
  1168. + pStats->ulSentPacketsTimedOut ),
  1169. pStats->ulSentDataPacketsSeq
  1170. + pStats->ulSentDataPacketsUnSeq
  1171. - pStats->ulSentPacketsAcked
  1172. - pStats->ulSentPacketsTimedOut ) );
  1173. TRACE( TL_I, TM_Stat,
  1174. ( "| Misordered: %d (%d%%)",
  1175. pStats->ulDataPacketsDequeued,
  1176. PCTTRACE(
  1177. pStats->ulDataPacketsDequeued,
  1178. pStats->ulRecdDataPackets ) ) );
  1179. TRACE( TL_I, TM_Stat,
  1180. ( "| Out: Resets=%d ZAcks=%d UnSeqs=%d",
  1181. pStats->ulSentResets,
  1182. pStats->ulSentZAcks,
  1183. pStats->ulSentDataPacketsUnSeq ) );
  1184. TRACE( TL_I, TM_Stat,
  1185. ( "| In: Resets=%d (%d%% old) Zlbs=%d",
  1186. pStats->ulRecdResets,
  1187. PCTTRACE(
  1188. pStats->ulRecdResetsIgnored,
  1189. pStats->ulRecdResets ),
  1190. pStats->ulRecdZlbs ) );
  1191. TRACE( TL_I, TM_Stat,
  1192. ( "| Send window: Min=%d Avg=%d Max=%d Changes=%d",
  1193. pStats->ulMinSendWindow,
  1194. AVGTRACE(
  1195. pStats->ulSendWindowTotal,
  1196. pStats->ulSentDataPacketsSeq ),
  1197. pStats->ulMaxSendWindow,
  1198. pStats->ulSendWindowChanges ) );
  1199. TRACE( TL_I, TM_Stat,
  1200. ( "| Trip in ms: Min=%d Avg=%d Max=%d",
  1201. pStats->ulMinRoundTripMs,
  1202. AVGTRACE(
  1203. pStats->ulRoundTripMsTotal,
  1204. pStats->ulRoundTrips ),
  1205. pStats->ulMaxRoundTripMs ) );
  1206. TRACE( TL_I, TM_Stat,
  1207. ( "'---------------------------------------------" ) );
  1208. }
  1209. //-----------------------------------------------------------------------------
  1210. // Local utility routines (alphabetically)
  1211. //-----------------------------------------------------------------------------
  1212. ULONG
  1213. atoul(
  1214. IN CHAR* pszNumber )
  1215. // Convert string of digits 'pszNumber' to it's ULONG value.
  1216. //
  1217. {
  1218. ULONG ulResult;
  1219. ulResult = 0;
  1220. while (*pszNumber)
  1221. {
  1222. if(*pszNumber >= '0' && *pszNumber <= '9')
  1223. {
  1224. ulResult *= 10;
  1225. ulResult += *pszNumber - '0';
  1226. }
  1227. else
  1228. {
  1229. break;
  1230. }
  1231. ++pszNumber;
  1232. }
  1233. return ulResult;
  1234. }
  1235. VOID
  1236. ReversePsz(
  1237. IN OUT CHAR* psz )
  1238. // Reverse the order of the characters in 'psz' in place.
  1239. //
  1240. {
  1241. CHAR* pchLeft;
  1242. CHAR* pchRight;
  1243. pchLeft = psz;
  1244. pchRight = psz + strlen( psz ) - 1;
  1245. while (pchLeft < pchRight)
  1246. {
  1247. CHAR ch;
  1248. ch = *pchLeft;
  1249. *pchLeft = *pchRight;
  1250. *pchRight = ch;
  1251. ++pchLeft;
  1252. --pchRight;
  1253. }
  1254. }
  1255. VOID
  1256. ultoa(
  1257. IN ULONG ul,
  1258. OUT CHAR* pszBuf )
  1259. // Convert 'ul' to null-terminated string form in caller's 'pszBuf'. It's
  1260. // caller job to make sure 'pszBuf' is long enough to hold the returned
  1261. // string.
  1262. //
  1263. {
  1264. CHAR* pch;
  1265. pch = pszBuf;
  1266. do
  1267. {
  1268. *pch++ = (CHAR )((ul % 10) + '0');
  1269. ul /= 10;
  1270. }
  1271. while (ul);
  1272. *pch = '\0';
  1273. ReversePsz( pszBuf );
  1274. }