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.

725 lines
20 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. netspeed.cpp
  5. Abstract :
  6. Main source file for throttle control.
  7. Author :
  8. Revision History :
  9. ---> for small files, we need to feed the file size in to the block calculator,
  10. because the server-speed estimator will be incorrect if m_BlockSize is 65000
  11. but the download time is based on a file size of 2002 bytes.
  12. ***********************************************************************/
  13. #include "stdafx.h"
  14. #include <malloc.h>
  15. #include <limits.h>
  16. #if !defined(BITS_V12_ON_NT4)
  17. #include "netspeed.tmh"
  18. #endif
  19. //
  20. // the maximum % of the perceived bandwidth that BITS will use for itself
  21. //
  22. const float MAX_BANDWIDTH_FRACTION = 0.95f;
  23. //
  24. // timer periods in seconds
  25. //
  26. const float DEFAULT_BLOCK_INTERVAL = 2.0f;
  27. const float MIN_BLOCK_INTERVAL = 0.001f;
  28. const float MAX_BLOCK_INTERVAL = 5.0f;
  29. //
  30. // observed header sizes. request = 250, reply = 300
  31. //
  32. #define REQUEST_OVERHEAD 550
  33. //
  34. // smallest block we will pull down
  35. //
  36. #define MIN_BLOCK_SIZE 2000
  37. //
  38. // size when we occasionally pull down a block on a full network
  39. //
  40. #define BUSY_BLOCK_SIZE 1500
  41. //
  42. // Very small blocks give unreliable speed measurements.
  43. //
  44. #define MIN_BLOCK_SIZE_TO_MEASURE 500
  45. const NETWORK_RATE CNetworkInterface::DEFAULT_SPEED = 1600.0f;
  46. // Work around limitations of the protocol stack
  47. const DWORD MAX_BLOCK_SIZE = 2147483648;
  48. //------------------------------------------------------------------------
  49. //
  50. // The observed server speed is reported as the average of this many usable samples.
  51. //
  52. const float SERVER_SPEED_SAMPLE_COUNT = 3.0F;
  53. /*
  54. The algorithm used to determine the speed and loading of the network is as follows:
  55. 1. After contacting the web site with Wininet calls, see whether an HTTP 1.1 "Via" header is present.
  56. If so, a proxy was used, and we locate the proper net card to talk with the proxy. Otherwise,
  57. a proxy was not used, and we locate the proper net card to talk with the HTTP server itself.
  58. 2. Chop time into 1/2-second intervals, and measure the interface's bytes-in and bytes-out count
  59. three times per interval: first at the beginning, just before a block is downloaded, second at
  60. the completion of the block, and third at the end of the interval.
  61. */
  62. HRESULT
  63. CNetworkInterface::TakeSnapshot(
  64. int StatIndex
  65. )
  66. {
  67. DWORD s;
  68. ULONG size = 0;
  69. //
  70. // The network speed can be calculated only if all three snapshots succeeded.
  71. // We keep track of the error status of the current series of snapshots.
  72. //
  73. if (StatIndex == BLOCK_START)
  74. {
  75. m_SnapshotError = S_OK;
  76. m_SnapshotsValid = false;
  77. }
  78. m_TempRow.dwIndex = m_InterfaceIndex;
  79. DWORD dwGetIfEntryError = GetIfEntry( &m_TempRow );
  80. if ( dwGetIfEntryError )
  81. {
  82. LogWarning( "[%d] : GetIfRow(%d) failed %!winerr!", StatIndex, m_InterfaceIndex, dwGetIfEntryError );
  83. m_SnapshotError = HRESULT_FROM_WIN32( dwGetIfEntryError );
  84. return m_SnapshotError;
  85. }
  86. QueryPerformanceCounter( &m_Snapshots[ StatIndex ].TimeStamp );
  87. m_Snapshots[ StatIndex ].BytesIn = m_TempRow.dwInOctets;
  88. m_Snapshots[ StatIndex ].BytesOut = m_TempRow.dwOutOctets;
  89. LogDl( "[%d] : in=%d, out=%d, timestamp=%d",
  90. StatIndex,
  91. m_Snapshots[ StatIndex ].BytesIn,
  92. m_Snapshots[ StatIndex ].BytesOut,
  93. m_Snapshots[ StatIndex ].TimeStamp.u.LowPart );
  94. if (StatIndex == BLOCK_INTERVAL_END &&
  95. m_SnapshotError == S_OK)
  96. {
  97. m_SnapshotsValid = true;
  98. }
  99. return S_OK;
  100. }
  101. float
  102. CNetworkInterface::GetTimeDifference(
  103. int start,
  104. int finish
  105. )
  106. {
  107. float TotalTime;
  108. TotalTime = m_Snapshots[ finish ].TimeStamp.QuadPart - m_Snapshots[ start ].TimeStamp.QuadPart;
  109. TotalTime /= g_GlobalInfo->m_PerformanceCounterFrequency.QuadPart; // convert to seconds
  110. if (TotalTime <= 0)
  111. {
  112. // pretend it was half a tick.
  113. TotalTime = 1 / float(2 * g_GlobalInfo->m_PerformanceCounterFrequency.QuadPart);
  114. }
  115. return TotalTime;
  116. }
  117. CNetworkInterface::CNetworkInterface()
  118. {
  119. Reset();
  120. }
  121. HRESULT
  122. CNetworkInterface::SetInterfaceIndex(
  123. const TCHAR host[]
  124. )
  125. {
  126. DWORD index;
  127. HRESULT Hr = FindInterfaceIndex( host, &index );
  128. if (FAILED(Hr))
  129. return Hr;
  130. if (m_InterfaceIndex != index)
  131. {
  132. m_InterfaceIndex = index;
  133. Reset();
  134. }
  135. return S_OK;
  136. }
  137. void
  138. CNetworkInterface::Reset()
  139. {
  140. m_ServerSpeed = DEFAULT_SPEED;
  141. m_NetcardSpeed = DEFAULT_SPEED;
  142. m_PercentFree = 0.5f;
  143. m_SnapshotsValid = false;
  144. m_SnapshotError = E_FAIL;
  145. m_state = DOWNLOADED_BLOCK;
  146. }
  147. void
  148. CNetworkInterface::SetInterfaceSpeed()
  149. {
  150. float TotalTime, ratio;
  151. NETWORK_RATE rate = 0;
  152. //
  153. // Adjust server speed based on block download stats.
  154. //
  155. if (m_SnapshotsValid && m_BlockSize)
  156. {
  157. float ExpectedTime = m_BlockInterval * m_PercentFree;
  158. //
  159. // Calculate interface speed from the time the last block took.
  160. //
  161. TotalTime = GetTimeDifference( BLOCK_START, BLOCK_END );
  162. if (ExpectedTime > 0)
  163. {
  164. ratio = ExpectedTime / TotalTime;
  165. rate = m_ServerSpeed * ratio;
  166. }
  167. else
  168. {
  169. // either m_PercentFree was zero, or the interval was zero. The ordinary calculation
  170. // would always produce a ratio of zero and drag down our average speed incorrectly.
  171. // use strict bytes per second measure
  172. rate = m_BlockSize / TotalTime;
  173. if (rate < m_ServerSpeed)
  174. {
  175. rate = m_ServerSpeed;
  176. }
  177. }
  178. m_ServerSpeed *= (SERVER_SPEED_SAMPLE_COUNT-1) / SERVER_SPEED_SAMPLE_COUNT;
  179. m_ServerSpeed += (rate / SERVER_SPEED_SAMPLE_COUNT);
  180. LogDl("expected interval %f, actual= %f, rate= %!netrate!, avg %!netrate!",
  181. ExpectedTime, TotalTime, rate, m_ServerSpeed );
  182. }
  183. //
  184. // Adjust usage and netcard speed based on interval stats.
  185. //
  186. if (m_SnapshotsValid)
  187. {
  188. float Bytes;
  189. Bytes = m_Snapshots[ BLOCK_END ].BytesIn - m_Snapshots[ BLOCK_START ].BytesIn;
  190. Bytes += m_Snapshots[ BLOCK_END ].BytesOut - m_Snapshots[ BLOCK_START ].BytesOut;
  191. ASSERT( Bytes >= 0 );
  192. TotalTime = GetTimeDifference( BLOCK_START, BLOCK_END );
  193. rate = Bytes/TotalTime;
  194. // use whichever estimate is larger
  195. if (rate < m_ServerSpeed)
  196. {
  197. rate = m_ServerSpeed;
  198. }
  199. if (m_NetcardSpeed == 0)
  200. {
  201. m_NetcardSpeed = rate;
  202. }
  203. else
  204. {
  205. if (rate < m_NetcardSpeed * 0.9f)
  206. {
  207. //
  208. // If the rate drops precipitously, it's probably just a quiet moment on the Net;
  209. // a strict average would unduly lower our estimated throughput.
  210. // But reduce the average a little in case it's a long-term slowdown. If so,
  211. // eventually the average will be lowered enough that the incoming rates are greater
  212. // than m_NetcardSpeed / 2.
  213. //
  214. rate = m_NetcardSpeed * 0.9f;
  215. }
  216. //
  217. // Keep a running average of the perceived rate.
  218. //
  219. m_NetcardSpeed *= (SERVER_SPEED_SAMPLE_COUNT-1) / SERVER_SPEED_SAMPLE_COUNT;
  220. m_NetcardSpeed += (rate / SERVER_SPEED_SAMPLE_COUNT);
  221. }
  222. LogDl("bandwidth: bytes %f, time %f, rate %f, avg. %f", Bytes, TotalTime, rate, m_NetcardSpeed);
  223. //
  224. // Subtract our usage from the calculated usage. Compare usage to top speed to get free bandwidth.
  225. //
  226. Bytes = m_Snapshots[ BLOCK_INTERVAL_END ].BytesIn - m_Snapshots[ BLOCK_START ].BytesIn;
  227. Bytes += m_Snapshots[ BLOCK_INTERVAL_END ].BytesOut - m_Snapshots[ BLOCK_START ].BytesOut;
  228. Bytes -= m_BlockSize;
  229. if (Bytes < 0)
  230. {
  231. Bytes = 0;
  232. }
  233. TotalTime = GetTimeDifference( BLOCK_START, BLOCK_INTERVAL_END );
  234. rate = Bytes/TotalTime;
  235. m_PercentFree = 1 - (rate / m_NetcardSpeed);
  236. }
  237. LogDl("usage: %f / %f, percent free %f", rate, m_NetcardSpeed, m_PercentFree);
  238. if (m_PercentFree < 0)
  239. {
  240. m_PercentFree = 0;
  241. }
  242. else if (m_PercentFree > MAX_BANDWIDTH_FRACTION) // never monopolize the net
  243. {
  244. m_PercentFree = MAX_BANDWIDTH_FRACTION;
  245. }
  246. }
  247. //------------------------------------------------------------------------
  248. DWORD
  249. CNetworkInterface::BlockSizeFromInterval(
  250. SECONDS interval
  251. )
  252. {
  253. NETWORK_RATE FreeBandwidth = GetInterfaceSpeed() * GetPercentFree() * interval;
  254. if (FreeBandwidth <= REQUEST_OVERHEAD)
  255. {
  256. return 0;
  257. }
  258. return FreeBandwidth - REQUEST_OVERHEAD;
  259. }
  260. CNetworkInterface::SECONDS
  261. CNetworkInterface::IntervalFromBlockSize(
  262. DWORD BlockSize
  263. )
  264. {
  265. NETWORK_RATE FreeBandwidth = GetInterfaceSpeed() * GetPercentFree();
  266. BlockSize += REQUEST_OVERHEAD;
  267. if (BlockSize / MAX_BLOCK_INTERVAL > FreeBandwidth )
  268. {
  269. return -1;
  270. }
  271. return BlockSize / FreeBandwidth;
  272. }
  273. void
  274. CNetworkInterface::CalculateIntervalAndBlockSize(
  275. UINT64 MaxBlockSize
  276. )
  277. {
  278. MaxBlockSize = min( MaxBlockSize, MAX_BLOCK_SIZE );
  279. if (MaxBlockSize == 0)
  280. {
  281. m_BlockInterval = 0;
  282. m_BlockSize = 0;
  283. SetTimerInterval( m_BlockInterval );
  284. LogDl( "block %d bytes, interval %f seconds", m_BlockSize, m_BlockInterval );
  285. return;
  286. }
  287. //
  288. // Calculate new block size from the average interface speed.
  289. //
  290. DWORD OldState = m_state;
  291. m_BlockInterval = DEFAULT_BLOCK_INTERVAL;
  292. m_BlockSize = BlockSizeFromInterval( m_BlockInterval );
  293. if (m_BlockSize > MaxBlockSize)
  294. {
  295. m_BlockSize = MaxBlockSize;
  296. m_BlockInterval = IntervalFromBlockSize( m_BlockSize );
  297. ASSERT( m_BlockInterval > 0 );
  298. }
  299. else if (m_BlockSize < MIN_BLOCK_SIZE)
  300. {
  301. m_BlockSize = min( MIN_BLOCK_SIZE, MaxBlockSize );
  302. m_BlockInterval = IntervalFromBlockSize( m_BlockSize );
  303. }
  304. if (m_BlockInterval < 0)
  305. {
  306. m_BlockSize = 0;
  307. }
  308. //
  309. // choose the new block download state.
  310. //
  311. if (m_BlockSize > 0)
  312. {
  313. m_state = DOWNLOADED_BLOCK;
  314. }
  315. else
  316. {
  317. //
  318. // The first time m_BlockSize is set to zero, retain the default interval.
  319. // If blocksize is zero twice in a row, expand to MAX_BLOCK_INTERVAL.
  320. // Then force a small download.
  321. //
  322. switch (OldState)
  323. {
  324. case DOWNLOADED_BLOCK:
  325. {
  326. m_BlockInterval = DEFAULT_BLOCK_INTERVAL;
  327. m_state = SKIPPED_ONE_BLOCK;
  328. break;
  329. }
  330. case SKIPPED_ONE_BLOCK:
  331. {
  332. m_BlockInterval = MAX_BLOCK_INTERVAL;
  333. m_state = SKIPPED_TWO_BLOCKS;
  334. break;
  335. }
  336. case SKIPPED_TWO_BLOCKS:
  337. {
  338. m_BlockSize = min( BUSY_BLOCK_SIZE, MaxBlockSize);
  339. m_BlockInterval = MAX_BLOCK_INTERVAL;
  340. m_state = DOWNLOADED_BLOCK;
  341. break;
  342. }
  343. default:
  344. ASSERT( 0 );
  345. }
  346. }
  347. SetTimerInterval( m_BlockInterval );
  348. LogDl( "block %d bytes, interval %f seconds", m_BlockSize, m_BlockInterval );
  349. ASSERT( m_BlockSize <= MaxBlockSize );
  350. }
  351. BOOL
  352. CNetworkInterface::SetTimerInterval(
  353. SECONDS interval
  354. )
  355. {
  356. DWORD msec = interval*1000;
  357. if (msec <= 0)
  358. {
  359. msec = MIN_BLOCK_INTERVAL;
  360. }
  361. LogDl( "%d milliseconds", msec );
  362. if (FALSE == m_Timer.Start( msec ))
  363. {
  364. return FALSE;
  365. }
  366. return TRUE;
  367. }
  368. HRESULT
  369. CNetworkInterface::FindInterfaceIndex(
  370. const TCHAR host[],
  371. DWORD * pIndex
  372. )
  373. {
  374. //related to finding statistics
  375. /* Use GetBestInterface with some IP address to get the index. Double check that this index
  376. * occurs in the output of the IP Address table and look it up in the results of GetIfTable.
  377. */
  378. #define AOL_ADAPTER _T("AOL Adapter")
  379. #define AOL_DIALUP_ADAPTER _T("AOL Dial-Up Adapter")
  380. BOOL bFound = FALSE;
  381. BOOL bAOL = FALSE;
  382. unsigned i;
  383. DWORD dwAddr;
  384. ULONG HostAddress;
  385. struct sockaddr_in dest;
  386. DWORD dwIndex = -1;
  387. static TCHAR szIntfName[512];
  388. *pIndex = -1;
  389. try
  390. {
  391. //
  392. // Translate the host name into a SOCKADDR.
  393. //
  394. unsigned length = 3 * lstrlen(host);
  395. CAutoStringA AsciiHost ( new char[ length ]);
  396. if (! WideCharToMultiByte( CP_ACP,
  397. 0, // no flags
  398. host,
  399. -1, // use strlen
  400. AsciiHost.get(),
  401. length, // use strlen
  402. NULL, // no default char
  403. NULL // no default char
  404. ))
  405. {
  406. DWORD dwError = GetLastError();
  407. LogError( "Unicode conversion failed %!winerr!", dwError );
  408. return HRESULT_FROM_WIN32( dwError );
  409. }
  410. HostAddress = inet_addr( AsciiHost.get() );
  411. if (HostAddress == -1)
  412. {
  413. struct hostent *pHostEntry = gethostbyname( AsciiHost.get() );
  414. if (pHostEntry == 0)
  415. {
  416. DWORD dwError = WSAGetLastError();
  417. LogError( "failed to find host '%s': %!winerr!", AsciiHost.get(), dwError );
  418. return HRESULT_FROM_WIN32( dwError );
  419. }
  420. if (pHostEntry->h_addr_list[0] == NULL)
  421. {
  422. DWORD dwError = WSANO_DATA;
  423. LogError( "host address list empty '%s': %!winerr!", AsciiHost.get(), dwError );
  424. return HRESULT_FROM_WIN32( dwError );
  425. }
  426. HostAddress = *PULONG(pHostEntry->h_addr_list[0]);
  427. }
  428. }
  429. catch ( ComError err )
  430. {
  431. LogError( "exception 0x%x finding server IP address", err.Error() );
  432. return err.Error();
  433. }
  434. //for remote addr
  435. dest.sin_addr.s_addr = HostAddress;
  436. dest.sin_family = AF_INET;
  437. dest.sin_port = 80;
  438. DWORD dwGetBestInterfaceError = GetBestInterface(dest.sin_addr.s_addr, &dwIndex);
  439. if (dwGetBestInterfaceError != NO_ERROR)
  440. {
  441. LogError( "GetBestInterface failed with error %!winerr!, might be Win95", dwGetBestInterfaceError);
  442. //manually parse the routing table
  443. ULONG size = 0;
  444. DWORD dwIpForwardError = GetIpForwardTable(NULL, &size, FALSE);
  445. if (dwIpForwardError != ERROR_INSUFFICIENT_BUFFER)
  446. {
  447. LogError( "sizing GetIpForwardTable failed %!winerr!", dwIpForwardError );
  448. return HRESULT_FROM_WIN32( dwIpForwardError );
  449. }
  450. auto_ptr<MIB_IPFORWARDTABLE> pIpFwdTable((PMIB_IPFORWARDTABLE)new char[size]);
  451. if ( !pIpFwdTable.get() )
  452. {
  453. LogError( "out of memory getting %d bytes", size);
  454. return E_OUTOFMEMORY;
  455. }
  456. dwIpForwardError = GetIpForwardTable(pIpFwdTable.get(), &size, TRUE);
  457. if (dwIpForwardError == NO_ERROR) //sort by dest addr
  458. {
  459. //perform bitwise AND of dest address with netmask and see if it matches network dest
  460. //todo check for multiple matches and then take longest mask
  461. for (i=0; i < pIpFwdTable->dwNumEntries; i++)
  462. {
  463. if ((dest.sin_addr.s_addr & pIpFwdTable->table[i].dwForwardMask) == pIpFwdTable->table[i].dwForwardDest)
  464. {
  465. dwIndex = pIpFwdTable->table[i].dwForwardIfIndex;
  466. break;
  467. }
  468. }
  469. if (dwIndex == -1)
  470. {
  471. // no match
  472. return HRESULT_FROM_WIN32( ERROR_NETWORK_UNREACHABLE );
  473. }
  474. }
  475. else
  476. {
  477. LogError( "GetIpForwardTable failed with error %!winerr!, exiting", dwIpForwardError );
  478. return HRESULT_FROM_WIN32( dwIpForwardError );
  479. }
  480. }
  481. //
  482. // At this point dwIndex should be correct.
  483. //
  484. ASSERT( dwIndex != -1 );
  485. #if DBG
  486. try
  487. {
  488. //
  489. // Discover the local IP address for the correct interface.
  490. //
  491. ULONG size = 0;
  492. DWORD dwGetIpAddr = GetIpAddrTable(NULL, &size, FALSE);
  493. if (dwGetIpAddr != ERROR_INSUFFICIENT_BUFFER)
  494. {
  495. LogError( "GetIpAddrTable #1 returned %!winerr!", dwGetIpAddr );
  496. return HRESULT_FROM_WIN32( dwGetIpAddr );
  497. }
  498. auto_ptr<MIB_IPADDRTABLE> pAddrTable( (PMIB_IPADDRTABLE) new char[size] );
  499. dwGetIpAddr = GetIpAddrTable(pAddrTable.get(), &size, TRUE);
  500. if (dwGetIpAddr != NO_ERROR)
  501. {
  502. LogError( "GetIpAddrTable #2 returned %!winerr!", dwGetIpAddr );
  503. return HRESULT_FROM_WIN32( dwGetIpAddr );
  504. }
  505. for (i=0; i < pAddrTable->dwNumEntries; i++)
  506. {
  507. if (dwIndex == pAddrTable->table[i].dwIndex)
  508. {
  509. in_addr address;
  510. address.s_addr = pAddrTable->table[i].dwAddr;
  511. LogDl( "Throttling on interface with IP address - %s", inet_ntoa( address ));
  512. break;
  513. }
  514. }
  515. if (i >= pAddrTable->dwNumEntries)
  516. {
  517. LogWarning( "can't find interface with index %d in the IP address table", dwIndex );
  518. }
  519. }
  520. catch ( ComError err )
  521. {
  522. LogWarning("unable to print the local IP address due to exception %x", err.Error() );
  523. }
  524. #endif // DBG
  525. //
  526. // See if the adapter in question is the AOL adapter. If so, use the AOL dial-up adapter instead.
  527. //
  528. static MIB_IFROW s_TempRow;
  529. s_TempRow.dwIndex = dwIndex;
  530. DWORD dwEntryError = GetIfEntry( &s_TempRow );
  531. if ( NO_ERROR != dwEntryError )
  532. {
  533. LogError( "GetIfEntry(%d) returned %!winerr!", dwIndex, dwEntryError );
  534. return HRESULT_FROM_WIN32( dwEntryError );
  535. }
  536. if (lstrcmp( LPCWSTR(s_TempRow.bDescr), AOL_ADAPTER) == 0)
  537. {
  538. LogWarning( "found AOL adapter, searching for dial-up adapter...");
  539. dwIndex = -1;
  540. ULONG size = 0;
  541. DWORD dwGetIfTableError = GetIfTable( NULL, &size, FALSE );
  542. if (dwGetIfTableError != ERROR_INSUFFICIENT_BUFFER)
  543. {
  544. LogError( "GetIfTable #2 returned %!winerr!", dwGetIfTableError );
  545. return HRESULT_FROM_WIN32( dwGetIfTableError );
  546. }
  547. auto_ptr<MIB_IFTABLE> pIfTable( (PMIB_IFTABLE) new char[size] );
  548. if ( !pIfTable.get() )
  549. {
  550. LogError( "out of memory getting %d bytes", size);
  551. return E_OUTOFMEMORY;
  552. }
  553. dwGetIfTableError = GetIfTable( pIfTable.get(), &size, FALSE );
  554. if ( NO_ERROR != dwGetIfTableError )
  555. {
  556. LogError( "GetIfTable #2 returned %!winerr!", dwGetIfTableError );
  557. return HRESULT_FROM_WIN32( dwGetIfTableError );
  558. }
  559. for (i=0; i < pIfTable->dwNumEntries; ++i)
  560. {
  561. if (lstrcmp( LPCWSTR(pIfTable->table[i].bDescr), AOL_DIALUP_ADAPTER) == 0)
  562. {
  563. dwIndex = pIfTable->table[i].dwIndex;
  564. break;
  565. }
  566. }
  567. }
  568. ASSERT( dwIndex != -1 );
  569. *pIndex = dwIndex;
  570. LogDl( "using interface index %d", dwIndex );
  571. return S_OK;
  572. }