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.

1537 lines
43 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1997 - 1999
  3. Module Name:
  4. lan.cxx
  5. Abstract:
  6. This is the source file relating to the LAN-specific routines of the
  7. Connectivity APIs implementation.
  8. Author:
  9. Gopal Parupudi <GopalP>
  10. [Notes:]
  11. optional-notes
  12. Revision History:
  13. GopalP 10/11/1997 Start.
  14. --*/
  15. #include <precomp.hxx>
  16. //
  17. // Constants
  18. //
  19. #define GETIFTABLE GetIfTable
  20. #define GETIPADDRTABLE GetIpAddrTable
  21. #define GETIPFORWARDTABLE GetIpForwardTable
  22. #define GETRTTANDHOPCOUNT GetRTTAndHopCount
  23. #define GETIPSTATISTICS GetIpStatistics
  24. #define MAX_IFTABLE_ROWS 4
  25. #define MAX_IPADDRTABLE_ROWS 6
  26. #define MAX_IPNETTABLE_ROWS 8
  27. #define MAX_IPFORWARDTABLE_ROWS 8
  28. #define MAX_HOPS_COUNT 0xFFFF
  29. #define BROADCAST_ACTIVITY_THRESHOLD 2 // +2 thru -2
  30. #define MEDIASENSE_INITIALIZATION_DELAY 3*25*1000 // 1:15 minutes
  31. #define MEDIASENSE_EVALUATE_LAN_DELAY 4*1000 // 4 seconds
  32. #define SENS_WINSOCK_VERSION MAKEWORD( 2, 0 )
  33. //
  34. // Globals
  35. //
  36. BOOL gbIpInitSuccessful;
  37. long gdwLastLANTime;
  38. long gdwLANState;
  39. IF_STATE gIfState[MAX_IF_ENTRIES];
  40. MIB_IPSTATS gIpStats;
  41. extern CRITICAL_SECTION gSensLock;
  42. HANDLE ghMediaTimer;
  43. DWORD gdwMediaSenseState;
  44. //
  45. // Macros
  46. //
  47. /*++
  48. Macro Description:
  49. A macro to help in allocating tables when calling IP Helper APIs.
  50. Arguments:
  51. TABLE_TYPE - The type of the IP Table being queried.
  52. ROW_TYPE - The type of the Row corresponding to the TABLE_TYPE.
  53. FUNC_NAME - The IP API to be called to get the IP table.
  54. MAX_NUM_ROWS - The default number of rows for the table which is
  55. being retrieved. These rows are allocated on the stack.
  56. Notes:
  57. o lpdwLastError should be defined as an LPDWORD in the code
  58. fragment that uses this macro.
  59. --*/
  60. #define \
  61. BEGIN_GETTABLE( \
  62. TABLE_TYPE, \
  63. ROW_TYPE, \
  64. FUNC_NAME, \
  65. MAX_NUM_ROWS \
  66. ) \
  67. { \
  68. DWORD dwOldSize; \
  69. DWORD dwSize; \
  70. DWORD dwStatus; \
  71. \
  72. BOOL bOrder; \
  73. \
  74. TABLE_TYPE *pTable; \
  75. \
  76. bOrder = FALSE; \
  77. \
  78. dwSize = sizeof(DWORD) + MAX_NUM_ROWS * sizeof(ROW_TYPE); \
  79. pTable = (TABLE_TYPE *) new char[dwSize]; \
  80. if (pTable == NULL) \
  81. { \
  82. SensPrintA(SENS_MEM, (#FUNC_NAME "(): failed to new %d bytes\n", \
  83. dwSize)); \
  84. *lpdwLastError = ERROR_OUTOFMEMORY; \
  85. return FALSE; \
  86. } \
  87. \
  88. dwOldSize = dwSize; \
  89. \
  90. dwStatus = FUNC_NAME( \
  91. pTable, \
  92. &dwSize, \
  93. bOrder \
  94. ); \
  95. \
  96. if ( (dwStatus == ERROR_INSUFFICIENT_BUFFER) \
  97. || (dwStatus == ERROR_MORE_DATA)) \
  98. { \
  99. ASSERT(dwSize > dwOldSize); \
  100. SensPrintA(SENS_WARN, (#FUNC_NAME "(%d): reallocing buffer to be %d bytes\n", \
  101. dwOldSize, dwSize)); \
  102. delete (char *)pTable; \
  103. pTable = (TABLE_TYPE *) new char[dwSize]; \
  104. if (pTable != NULL) \
  105. { \
  106. dwStatus = FUNC_NAME( \
  107. pTable, \
  108. &dwSize, \
  109. bOrder \
  110. ); \
  111. } \
  112. else \
  113. { \
  114. SensPrintA(SENS_MEM, (#FUNC_NAME "(): failed to new (%d) bytes\n", \
  115. dwSize)); \
  116. *lpdwLastError = ERROR_OUTOFMEMORY; \
  117. return FALSE; \
  118. } \
  119. } \
  120. \
  121. if (dwStatus != 0) \
  122. { \
  123. ASSERT( (dwStatus != ERROR_INSUFFICIENT_BUFFER) \
  124. && (dwStatus != ERROR_MORE_DATA)); \
  125. \
  126. SensPrintA(SENS_ERR, (#FUNC_NAME "() returned %d\n", dwStatus));\
  127. *lpdwLastError = dwStatus; \
  128. /* P3 BUG: Might need to fire ConnectionLost() here */ \
  129. gdwLANState = FALSE; \
  130. UpdateSensCache(LAN); \
  131. delete pTable; \
  132. return FALSE; \
  133. }
  134. /*++
  135. Macro Description:
  136. This macro ends the BEGIN_GETTABLE() macro.
  137. Arguments:
  138. None.
  139. Notes:
  140. a. If we have a return between BEGIN_XXX and END_XXX, we need to make
  141. sure that we free pTable.
  142. --*/
  143. #define \
  144. END_GETTABLE() \
  145. \
  146. delete pTable; \
  147. \
  148. }
  149. BOOL
  150. DoLanSetup(
  151. void
  152. )
  153. /*++
  154. Routine Description:
  155. Arguments:
  156. None.
  157. Return Value:
  158. --*/
  159. {
  160. BOOL bRetValue;
  161. WORD wVersionRequested;
  162. WSADATA wsaData;
  163. int err;
  164. bRetValue = FALSE;
  165. //
  166. // NT5-specific stuff
  167. //
  168. #if defined(SENS_NT5)
  169. ghMediaTimer = NULL;
  170. // Register for Media-sense notifications
  171. if (FALSE == MediaSenseRegister())
  172. {
  173. SensPrintA(SENS_ERR, ("%s MediaSenseRegister() failed.\n", SERVICE_NAME));
  174. }
  175. #endif // SENS_NT5
  176. //
  177. // AOL-specific code
  178. //
  179. #if defined(AOL_PLATFORM)
  180. gdwAOLState = FALSE;
  181. #endif // AOL_PLATFORM
  182. //
  183. // Initialize Winsock.
  184. //
  185. wVersionRequested = SENS_WINSOCK_VERSION;
  186. err = WSAStartup(wVersionRequested, &wsaData);
  187. if (err != 0)
  188. {
  189. SensPrintA(SENS_ERR, ("WSAStartup() returned %d!\n", err));
  190. bRetValue = FALSE;
  191. goto Cleanup;
  192. }
  193. bRetValue = TRUE;
  194. Cleanup:
  195. //
  196. // Cleanup
  197. //
  198. return bRetValue;
  199. }
  200. #ifdef DBG
  201. inline void
  202. PrintIfState(
  203. void
  204. )
  205. /*++
  206. Routine Description:
  207. Arguments:
  208. Return Value:
  209. --*/
  210. {
  211. int i;
  212. SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
  213. SensPrintA(SENS_INFO, ("| Valid Index UcastIN UcastOUT NUcastIN NUcastOUT ErrIN ErrOUT DiscIN DiscOUT |\n"));
  214. SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
  215. for (i = 0; i < MAX_IF_ENTRIES; i++)
  216. {
  217. SensPrintA(SENS_INFO, ("| %c %9d %7d %7d %9d %9d %5d %6d %6d %6d |\n",
  218. gIfState[i].fValid ? 'Y' : 'N',
  219. gIfState[i].dwIndex,
  220. gIfState[i].dwInUcastPkts,
  221. gIfState[i].dwOutUcastPkts,
  222. gIfState[i].dwInNUcastPkts,
  223. gIfState[i].dwOutNUcastPkts,
  224. gIfState[i].dwInErrors,
  225. gIfState[i].dwOutErrors,
  226. gIfState[i].dwInDiscards,
  227. gIfState[i].dwOutDiscards)
  228. );
  229. }
  230. SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
  231. }
  232. #else // DBG
  233. #define PrintIfState() // Nothing
  234. #endif // DBG
  235. #ifdef DETAIL_DEBUG
  236. void
  237. PrintIfTable(
  238. MIB_IFTABLE *pTable
  239. )
  240. {
  241. int i;
  242. SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
  243. SensPrintA(SENS_INFO, ("| Type Index Spd/1K UcastIN UcastOUT ErrorIN OUT DiscIN OUT Opr Adm |\n"));
  244. SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
  245. for (i = 0; i < pTable->dwNumEntries; i++)
  246. {
  247. SensPrintA(SENS_INFO, ("| %4d %7d %6d %7d %8d %7d %3d %6d %3d %3d %3d |\n",
  248. pTable->table[i].dwType,
  249. pTable->table[i].dwIndex,
  250. pTable->table[i].dwSpeed/1000,
  251. pTable->table[i].dwInUcastPkts,
  252. pTable->table[i].dwOutUcastPkts,
  253. pTable->table[i].dwInErrors,
  254. pTable->table[i].dwOutErrors,
  255. pTable->table[i].dwInDiscards,
  256. pTable->table[i].dwOutDiscards,
  257. pTable->table[i].dwOperStatus,
  258. pTable->table[i].dwAdminStatus
  259. )
  260. );
  261. }
  262. SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
  263. }
  264. void
  265. PrintIpStats(
  266. void
  267. )
  268. {
  269. SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
  270. SensPrintA(SENS_INFO, ("| IP_STATS InReceives OutRequests |\n"));
  271. SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
  272. SensPrintA(SENS_INFO, ("| %10d %10d |\n",
  273. gIpStats.dwInReceives,
  274. gIpStats.dwOutRequests)
  275. );
  276. SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
  277. }
  278. #else
  279. #define PrintIfTable(_X_) // Nothing
  280. #define PrintIpStats() // Nothing
  281. #endif // DETAIL_DEBUG
  282. BOOL WINAPI
  283. EvaluateLanConnectivityDelayed(
  284. LPDWORD
  285. )
  286. {
  287. for (int i = 0; i < 4; i++)
  288. {
  289. Sleep(MEDIASENSE_EVALUATE_LAN_DELAY*i); // First time waits 0 ms, no delay
  290. if (EvaluateLanConnectivity(NULL))
  291. {
  292. SensPrintA(SENS_INFO, ("EvaluateLanConnectivity: Delayed eval successful (%d)\n", i));
  293. return TRUE;
  294. }
  295. }
  296. return FALSE;
  297. }
  298. BOOL WINAPI
  299. EvaluateLanConnectivity(
  300. OUT LPDWORD lpdwLastError
  301. )
  302. /*++
  303. Routine Description:
  304. Evaluates LAN Connectivity.
  305. Arguments:
  306. lpdwLastError - if return value is FALSE, GetLastError is returned
  307. in this OUT parameter.
  308. Notes:
  309. a. This routine can be entered by multiple threads at the same time.
  310. Currently only very essential code is under a critical section.
  311. b. This routine can be executed as a threadpool work item.
  312. Return Value:
  313. TRUE, if LAN connectivity is present.
  314. FALSE, otherwise
  315. --*/
  316. {
  317. DWORD dwNow;
  318. DWORD dwLocalLastError;
  319. DWORD dwActiveInterfaceSpeed;
  320. WCHAR wszActiveInterfaceName[MAX_INTERFACE_NAME_LEN];
  321. BOOL bLanAlive;
  322. BOOL bSomeInterfaceActive;
  323. BOOL bCheckCache;
  324. dwNow = GetTickCount();
  325. dwLocalLastError = ERROR_NO_NETWORK;
  326. dwActiveInterfaceSpeed = 0x0;
  327. bLanAlive = FALSE;
  328. bSomeInterfaceActive = FALSE;
  329. bCheckCache = FALSE;
  330. if (lpdwLastError)
  331. {
  332. *lpdwLastError = dwLocalLastError;
  333. }
  334. else
  335. {
  336. lpdwLastError = &dwLocalLastError;
  337. }
  338. //
  339. // Get infomation about IP statistics.
  340. //
  341. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  342. //
  343. // PurgeStaleInterfaces
  344. //
  345. PurgeStaleInterfaces(pTable, lpdwLastError);
  346. //
  347. // Algorithm:
  348. //
  349. // o Create a record.
  350. // o See if this record exists.
  351. // o Save the record, if not and return success.
  352. // o If it does exist, compare. If greater, then save record.
  353. // o If not greater, try other entries.
  354. // o All entries? return failure.
  355. //
  356. IF_STATE ifEntry;
  357. DWORD i;
  358. SensPrintA(SENS_INFO, ("GetIfTable(): Number of entries - %d.\n", pTable->dwNumEntries));
  359. PrintIfTable(pTable);
  360. i = 0;
  361. while (i < pTable->dwNumEntries)
  362. {
  363. //
  364. // Calculate only if it is a non-WAN and non-Loopback interface.
  365. //
  366. if ( (pTable->table[i].dwType != MIB_IF_TYPE_PPP)
  367. && (pTable->table[i].dwType != MIB_IF_TYPE_SLIP)
  368. && (pTable->table[i].dwType != MIB_IF_TYPE_LOOPBACK))
  369. {
  370. BOOL bForceInvalid = FALSE;
  371. //
  372. // BOOT UP WITH NO NETWORK:
  373. //
  374. // Check to see if both UnicastIN and UnicastOUT are zero. If so,
  375. // this interface is considered as not active and we skip it.
  376. //
  377. if ( (pTable->table[i].dwInUcastPkts == 0)
  378. && (pTable->table[i].dwOutUcastPkts == 0))
  379. {
  380. bForceInvalid = TRUE;
  381. }
  382. //
  383. // Check if networking says it is connected, if not, skip it.
  384. //
  385. if (pTable->table[i].dwOperStatus < MIB_IF_OPER_STATUS_CONNECTING)
  386. {
  387. SensPrintA(SENS_INFO, ("GetIfTable: Found interface %d in < connecting state (%d), ignored\n",
  388. pTable->table[i].dwIndex, pTable->table[i].dwOperStatus) );
  389. bForceInvalid = TRUE;
  390. }
  391. //
  392. // At this stage, there is some Unicast activity on this interface.
  393. // So, we can skip the check for Unicast activity below.
  394. //
  395. // Fill the IF_STATE structure
  396. ifEntry.dwIndex = pTable->table[i].dwIndex;
  397. ifEntry.dwInUcastPkts = pTable->table[i].dwInUcastPkts;
  398. ifEntry.dwOutUcastPkts = pTable->table[i].dwOutUcastPkts;
  399. ifEntry.dwInNUcastPkts = pTable->table[i].dwInNUcastPkts;
  400. ifEntry.dwOutNUcastPkts = pTable->table[i].dwOutNUcastPkts;
  401. ifEntry.dwInErrors = pTable->table[i].dwInErrors;
  402. ifEntry.dwOutErrors = pTable->table[i].dwOutErrors;
  403. ifEntry.dwInDiscards = pTable->table[i].dwInDiscards;
  404. ifEntry.dwOutDiscards = pTable->table[i].dwOutDiscards;
  405. bSomeInterfaceActive = HasIfStateChanged(ifEntry, bForceInvalid);
  406. if (TRUE == bSomeInterfaceActive)
  407. {
  408. bLanAlive = TRUE;
  409. // Save info about interface for later use.
  410. dwActiveInterfaceSpeed = max(pTable->table[i].dwSpeed,dwActiveInterfaceSpeed);
  411. StringCchCopy(wszActiveInterfaceName, MAX_INTERFACE_NAME_LEN, pTable->table[i].wszName);
  412. }
  413. else
  414. {
  415. if (!bForceInvalid)
  416. {
  417. bCheckCache = TRUE; // Idle IF found but still valid (enable MAX_LAN_INTERNAL check below)
  418. }
  419. }
  420. }
  421. i++;
  422. } // while ()
  423. PrintIfState();
  424. END_GETTABLE()
  425. //
  426. // RACE Condition Fix:
  427. //
  428. // If there are 2 threads that are in EvaluateLanConnectivity() and one
  429. // of them updates the interface's packet cache, then there is a distinct
  430. // possibility that the second thread will compare with the updated cache
  431. // and wrongly conclude that there is no activity. We ignore any loss of
  432. // connectivity that was evaluated before MAX_LAN_INTERVAL (ie., we should
  433. // keep giving cached information for MAX_LAN_INTERVAL seconds).
  434. //
  435. if ( (TRUE == bCheckCache)
  436. && (FALSE == bLanAlive) )
  437. {
  438. dwNow = GetTickCount();
  439. if ( ((dwNow - gdwLastLANTime) <= MAX_LAN_INTERVAL)
  440. && (gdwLastLANTime != 0) )
  441. {
  442. SensPrintA(SENS_DBG, ("EvaluateLanConnectivity(): Returning TRUE "
  443. "(Now - %d sec, LastLANTime - %d sec)\n", dwNow/1000, gdwLastLANTime/1000));
  444. return TRUE;
  445. }
  446. }
  447. //
  448. // NOTE: If we are doing DWORD InterlockedExchange, then assignment
  449. // should suffice. Using InterlockedExchange is not a bug, though.
  450. //
  451. if (bLanAlive)
  452. {
  453. SensPrintA(SENS_DBG, ("**** EvaluateLanConnectivity(): Setting"
  454. " gdwLastLANTime to %d secs\n", dwNow/1000));
  455. InterlockedExchange(&gdwLastLANTime, dwNow);
  456. }
  457. else
  458. {
  459. SensPrintA(SENS_DBG, ("**** EvaluateLanConnectivity(): Setting"
  460. " gdwLastLANTime to 0 secs\n"));
  461. InterlockedExchange(&gdwLastLANTime, 0x0);
  462. }
  463. //
  464. // Adjust LAN state and fire an event, if necessary.
  465. //
  466. if (InterlockedExchange(&gdwLANState, bLanAlive) != bLanAlive)
  467. {
  468. //
  469. // LAN Connectivity state changed.
  470. //
  471. SENSEVENT_NETALIVE Data;
  472. Data.eType = SENS_EVENT_NETALIVE;
  473. Data.bAlive = bLanAlive;
  474. memset(&Data.QocInfo, 0x0, sizeof(QOCINFO));
  475. Data.QocInfo.dwSize = sizeof(QOCINFO);
  476. Data.QocInfo.dwFlags = NETWORK_ALIVE_LAN;
  477. Data.QocInfo.dwInSpeed = dwActiveInterfaceSpeed;
  478. Data.QocInfo.dwOutSpeed = dwActiveInterfaceSpeed;
  479. //
  480. // NOTE: When dwActiveInterfaceName gets the right value from
  481. // IPHLPAPIs we should use that name. Until then, we use a default.
  482. //
  483. Data.strConnection = DEFAULT_LAN_CONNECTION_NAME;
  484. UpdateSensCache(LAN);
  485. SensFireEvent((PVOID)&Data);
  486. }
  487. return bLanAlive;
  488. }
  489. BOOL
  490. HasIfStateChanged(
  491. IF_STATE ifEntry,
  492. BOOL bForceInvalid
  493. )
  494. /*++
  495. Routine Description:
  496. Compares the current state of a remote network IF with the cached history
  497. to determine if it is active or not.
  498. Arguments:
  499. ifEntry - An interface that appears to have changed state and is "valid" as a remote
  500. LAN if. (ie, loopback, pptp, etc should be filtered out)
  501. bForceInvalid - If TRUE, don't bother to look at the stats; this interface is NOT valid.
  502. Return Value:
  503. TRUE - ifEntry appears up and active
  504. FALSE - ifEntry inactive or down
  505. --*/
  506. {
  507. int i, j;
  508. static int iLastActiveIndex = -1;
  509. BOOL bActive;
  510. BOOL bSeenButInactive;
  511. DWORD dwInDiff;
  512. DWORD dwOutDiff;
  513. int iNUcastDiff;
  514. i = 0;
  515. bActive = FALSE;
  516. bSeenButInactive = FALSE;
  517. dwInDiff = 0;
  518. dwOutDiff = 0;
  519. iNUcastDiff = 0;
  520. RequestSensLock();
  521. //
  522. // Compare the current snapshot with the saved snapshot
  523. // for this interface.
  524. //
  525. while (i < MAX_IF_ENTRIES)
  526. {
  527. if ( (gIfState[i].fValid == TRUE)
  528. && (gIfState[i].dwIndex == ifEntry.dwIndex))
  529. {
  530. if (bForceInvalid)
  531. {
  532. gIfState[i].fValid = FALSE;
  533. break;
  534. }
  535. if ( (ifEntry.dwInUcastPkts > gIfState[i].dwInUcastPkts)
  536. || (ifEntry.dwOutUcastPkts > gIfState[i].dwOutUcastPkts)
  537. || (ifEntry.dwInNUcastPkts > gIfState[i].dwInNUcastPkts)
  538. || (ifEntry.dwOutNUcastPkts > gIfState[i].dwOutNUcastPkts)
  539. || (ifEntry.dwInErrors > gIfState[i].dwInErrors)
  540. || (ifEntry.dwOutErrors > gIfState[i].dwOutErrors)
  541. || (ifEntry.dwInDiscards > gIfState[i].dwInDiscards)
  542. || (ifEntry.dwOutDiscards > gIfState[i].dwOutDiscards))
  543. {
  544. //
  545. // HEURISTIC:
  546. //
  547. // a. When the net tap is pulled out, it has been observed that
  548. // the difference in the incoming non-Unicast packet count
  549. // is within +1 thru -1 of the difference in the outgoing
  550. // non-Unicast packet count. Most of the times the diff of
  551. // these differences is 0. We don't count this as LAN alive
  552. //
  553. // b. Also, there should be no change in the unicast IN packet
  554. // count. Unicast OUT packet count may change. This could be
  555. // problematic.
  556. //
  557. dwInDiff = ifEntry.dwInNUcastPkts - gIfState[i].dwInNUcastPkts;
  558. dwOutDiff = ifEntry.dwOutNUcastPkts - gIfState[i].dwOutNUcastPkts;
  559. iNUcastDiff = dwOutDiff - dwInDiff;
  560. SensPrintA(SENS_INFO, ("HasIfStateChanged(): dwInDiff = %d, "
  561. "dwOutDiff = %d, dwNUcastDiff = %d, UcastINDiff = "
  562. "%d, UcastOUTDiff = %d\n",
  563. dwInDiff, dwOutDiff, iNUcastDiff,
  564. ifEntry.dwInUcastPkts - gIfState[i].dwInUcastPkts,
  565. ifEntry.dwOutUcastPkts - gIfState[i].dwOutUcastPkts));
  566. if ( (ifEntry.dwInUcastPkts == gIfState[i].dwInUcastPkts)
  567. && (iNUcastDiff <= BROADCAST_ACTIVITY_THRESHOLD)
  568. && (iNUcastDiff >= -BROADCAST_ACTIVITY_THRESHOLD))
  569. {
  570. SensPrintA(SENS_INFO, ("HasIfStateChanged(): Interface %d"
  571. " has only Broadcast activity (Diff is %d)!\n",
  572. gIfState[i].dwIndex, iNUcastDiff));
  573. bSeenButInactive = TRUE;
  574. }
  575. else
  576. {
  577. //
  578. // Unicast IN packet counts have changed or Broadcast
  579. // activity is greater than the threshold.
  580. //
  581. iLastActiveIndex = i;
  582. bActive = TRUE;
  583. SensPrintA(SENS_INFO, ("HasStateChanged(): Interface %d "
  584. "has been active.\n", gIfState[i].dwIndex));
  585. gdwLastLANTime = GetTickCount();
  586. SensPrintA(SENS_DBG, ("**** HasIfStateChanged(): Setting "
  587. "gdwLastLANTime to %d secs\n", gdwLastLANTime/1000));
  588. }
  589. //
  590. // Save the new values.
  591. //
  592. memcpy(&gIfState[i], &ifEntry, sizeof(IF_STATE));
  593. gIfState[i].fValid = TRUE;
  594. }
  595. else
  596. {
  597. SensPrintA(SENS_INFO, ("HasStateChanged(): Interface %d has NO activity.\n",
  598. gIfState[i].dwIndex));
  599. bSeenButInactive = TRUE;
  600. }
  601. // Found the interface, so stop searching
  602. break;
  603. }
  604. i++;
  605. } // while ()
  606. ReleaseSensLock();
  607. if ( (bSeenButInactive == TRUE)
  608. || (bForceInvalid) )
  609. {
  610. return FALSE;
  611. }
  612. if (bActive == TRUE)
  613. {
  614. return TRUE;
  615. }
  616. //
  617. // We are seeing this interface for the first time. Go ahead and save it
  618. // in the global interface state array.
  619. //
  620. i = MAX_IF_ENTRIES;
  621. j = iLastActiveIndex;
  622. RequestSensLock();
  623. while (i > 0)
  624. {
  625. // Try to find a free slot starting from the last active slot.
  626. j = (j+1) % MAX_IF_ENTRIES;
  627. if (gIfState[j].fValid == FALSE)
  628. {
  629. // Found one!
  630. break;
  631. }
  632. i--;
  633. }
  634. //
  635. // NOTE: If there are more than MAX_IF_ENTRIES, we will start
  636. // start reusing valid interface elements in gIfState array. This,
  637. // I guess, is OK since we will have enough interfaces to figure
  638. // out connectivity.
  639. //
  640. memcpy(&gIfState[j], &ifEntry, sizeof(IF_STATE));
  641. gIfState[j].fValid = TRUE;
  642. ReleaseSensLock();
  643. SensPrintA(SENS_ERR, ("******** HasIfStateChanged(): Adding a new "
  644. "interface with index %d\n", gIfState[j].dwIndex));
  645. return TRUE;
  646. }
  647. BOOL
  648. MediaSenseRegister(
  649. void
  650. )
  651. /*++
  652. Routine Description:
  653. Schedule a workitem to register for Media-sense notifications from WMI.
  654. Arguments:
  655. None.
  656. Return Value:
  657. TRUE, if success.
  658. FALSE, otherwise.
  659. --*/
  660. {
  661. BOOL bRetVal;
  662. bRetVal = TRUE;
  663. ASSERT(gdwMediaSenseState == SENSSVC_START);
  664. //
  665. // Create a timer object to schedule (one-time only) Media-sense
  666. // registration.
  667. //
  668. SensSetTimerQueueTimer(
  669. bRetVal, // Bool return on NT5
  670. ghMediaTimer, // Handle return on IE5
  671. NULL, // Use default process timer queue
  672. MediaSenseRegisterHelper, // Callback
  673. NULL, // Parameter
  674. MEDIASENSE_INITIALIZATION_DELAY, // Time from now when timer should fire
  675. 0x0, // Time inbetween firings of this timer
  676. 0x0 // No Flags.
  677. );
  678. if (SENS_TIMER_CREATE_FAILED(bRetVal, ghMediaTimer))
  679. {
  680. SensPrintA(SENS_ERR, ("MediaSenseRegister(): SensSetTimerQueueTimer() failed with %d.\n",
  681. GetLastError()));
  682. bRetVal = FALSE;
  683. }
  684. return bRetVal;
  685. }
  686. SENS_TIMER_CALLBACK_RETURN
  687. MediaSenseRegisterHelper(
  688. PVOID pvIgnore,
  689. BOOLEAN bIgnore
  690. )
  691. /*++
  692. Routine Description:
  693. Helper routine that is scheduled to the WMI registration.
  694. Arguments:
  695. pvIgnore - Ignored.
  696. bIgnore - Ignored.
  697. Return Value:
  698. None (void).
  699. --*/
  700. {
  701. ULONG Status;
  702. GUID guid;
  703. RequestSensLock();
  704. if ( (SENSSVC_STOP == gdwMediaSenseState)
  705. || (UNREGISTERED == gdwMediaSenseState))
  706. {
  707. goto Cleanup;
  708. }
  709. //
  710. // Enable the media disconnect event.
  711. //
  712. guid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
  713. Status = WmiNotificationRegistrationW(
  714. &guid, // Event of interest
  715. TRUE, // Enable Notification?
  716. EventCallbackRoutine, // Callback function
  717. 0, // Callback context
  718. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  719. );
  720. if (ERROR_SUCCESS != Status)
  721. {
  722. SensPrintA(SENS_ERR, ("Unable to enable media disconnect event: 0x%x!\n", Status));
  723. goto Cleanup;
  724. }
  725. //
  726. // Enable the media connect event
  727. //
  728. guid = GUID_NDIS_STATUS_MEDIA_CONNECT;
  729. Status = WmiNotificationRegistrationW(
  730. &guid, // Event of interest
  731. TRUE, // Enable Notification?
  732. EventCallbackRoutine, // Callback function
  733. 0, // Callback context
  734. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  735. );
  736. if (ERROR_SUCCESS != Status)
  737. {
  738. SensPrintA(SENS_ERR, ("Unable to enable media connect event: 0x%x!\n", Status));
  739. ASSERT(0); // If we hit this then we need to unregister the first registration above.
  740. goto Cleanup;
  741. }
  742. SensPrintA(SENS_ERR, ("MediaSenseRegister(): Media-sense registration successful.\n"));
  743. gdwMediaSenseState = REGISTERED;
  744. Cleanup:
  745. //
  746. // Cleanup
  747. //
  748. ReleaseSensLock();
  749. return;
  750. }
  751. BOOL
  752. MediaSenseUnregister(
  753. void
  754. )
  755. /*++
  756. Routine Description:
  757. Unregister from Media-sense notifications from WMI.
  758. Arguments:
  759. None.
  760. Return Value:
  761. TRUE, if success.
  762. FALSE, otherwise.
  763. --*/
  764. {
  765. ULONG Status;
  766. GUID guid;
  767. BOOL bRetVal;
  768. BOOL bRegistered;
  769. bRetVal = TRUE;
  770. bRegistered = FALSE;
  771. RequestSensLock();
  772. ASSERT(gdwMediaSenseState == REGISTERED ||
  773. gdwMediaSenseState == SENSSVC_START);
  774. if (gdwMediaSenseState == REGISTERED)
  775. {
  776. bRegistered = TRUE;
  777. }
  778. gdwMediaSenseState = SENSSVC_STOP;
  779. if (NULL != ghMediaTimer)
  780. {
  781. bRetVal = SensCancelTimerQueueTimer(NULL, ghMediaTimer, NULL);
  782. ghMediaTimer = NULL;
  783. SensPrintA(SENS_INFO, ("[%d] MediaSensUnregister(): SensCancelTimer"
  784. "QueueTimer(Media) %s\n", GetTickCount(),
  785. bRetVal ? "succeeded" : "failed!"));
  786. }
  787. if (!bRegistered)
  788. {
  789. // Should not do unregistration.
  790. goto Cleanup;
  791. }
  792. //
  793. // Disable the media disconnect event.
  794. //
  795. guid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
  796. Status = WmiNotificationRegistrationW(
  797. &guid, // Event of interest
  798. FALSE, // Enable Notification?
  799. EventCallbackRoutine, // Callback function
  800. 0, // Callback context
  801. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  802. );
  803. if (ERROR_SUCCESS != Status)
  804. {
  805. SensPrintA(SENS_ERR, ("[%d] MediaSensUnregister(): Unable to disable "
  806. "media disconnect event: 0x%x!\n", GetTickCount(), Status));
  807. ASSERT(0); // If this fails analyze if we should still to the second unregister
  808. bRetVal = FALSE;
  809. }
  810. //
  811. // Disable the connect event
  812. //
  813. guid = GUID_NDIS_STATUS_MEDIA_CONNECT;
  814. Status = WmiNotificationRegistrationW(
  815. &guid, // Event of interest
  816. FALSE, // Enable Notification?
  817. EventCallbackRoutine, // Callback function
  818. 0, // Callback context
  819. NOTIFICATION_CALLBACK_DIRECT // Notification flags
  820. );
  821. if (ERROR_SUCCESS != Status)
  822. {
  823. SensPrintA(SENS_ERR, ("[%d] MediaSensUnregister(): Unable to disable "
  824. "media disconnect event: 0x%x!\n", GetTickCount(), Status));
  825. bRetVal = FALSE;
  826. }
  827. Cleanup:
  828. //
  829. //
  830. //
  831. gdwMediaSenseState = UNREGISTERED;
  832. ReleaseSensLock();
  833. return bRetVal;
  834. }
  835. void
  836. EventCallbackRoutine(
  837. IN PWNODE_HEADER WnodeHeader,
  838. IN ULONG Context
  839. )
  840. /*++
  841. Routine Description:
  842. Arguments:
  843. Return Value:
  844. --*/
  845. {
  846. PULONG Data;
  847. PWNODE_SINGLE_INSTANCE Wnode = (PWNODE_SINGLE_INSTANCE)WnodeHeader;
  848. PWCHAR Name;
  849. DWORD dwIgnore;
  850. ULONG NameLen;
  851. int result;
  852. //
  853. // Get the information for the media disconnect.
  854. //
  855. result = memcmp(&WnodeHeader->Guid, &GUID_NDIS_STATUS_MEDIA_DISCONNECT, sizeof(GUID));
  856. if (0 == result)
  857. {
  858. SensPrintA(SENS_INFO, ("NDIS: received a media disconnect!\n"));
  859. EvaluateConnectivity(TYPE_LAN);
  860. }
  861. else
  862. {
  863. //
  864. // Get the information for the media connect.
  865. //
  866. result = memcmp(&WnodeHeader->Guid, &GUID_NDIS_STATUS_MEDIA_CONNECT, sizeof(GUID));
  867. if (0 == result)
  868. {
  869. SensPrintA(SENS_INFO, ("NDIS: received a media connect!\n"));
  870. EvaluateConnectivity(TYPE_DELAY_LAN);
  871. }
  872. else
  873. {
  874. SensPrintA(SENS_WARN, ("NDIS: Unknown event received!\n"));
  875. }
  876. }
  877. Name = (PWCHAR)RtlOffsetToPointer(Wnode, Wnode->OffsetInstanceName);
  878. SensPrintW(SENS_INFO, (L"NDIS: Instance: %ws\n", Name));
  879. }
  880. BOOL
  881. GetIfEntryStats(
  882. IN DWORD dwIfIndex,
  883. IN LPQOCINFO lpQOCInfo,
  884. OUT LPDWORD lpdwLastError,
  885. OUT LPBOOL lpbIsWanIf
  886. )
  887. /*++
  888. Routine Description:
  889. Get the Statistics field of the Interface entry which has the given
  890. index.
  891. Arguments:
  892. dwIfIndex - The interface of interest.
  893. lpQOCInfo - QOC Info structure whose fields are set when the interface
  894. entry is found in the interface table.
  895. lpdwLastError - The GLE, if any.
  896. lpbIsWanIf - Is the interface at this index a WAN interface or not.
  897. Return Value:
  898. TRUE, if we find the index.
  899. FALSE, otherwise.
  900. --*/
  901. {
  902. DWORD i;
  903. BOOL bFound;
  904. *lpdwLastError = ERROR_SUCCESS;
  905. *lpbIsWanIf = FALSE;
  906. bFound = FALSE;
  907. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  908. // Search the Interface table for the entry with the given index.
  909. for (i = 0; i < pTable->dwNumEntries; i++)
  910. {
  911. if (pTable->table[i].dwIndex == dwIfIndex)
  912. {
  913. bFound = TRUE;
  914. SensPrintA(SENS_INFO, ("GetIfEntryStats(): Interface %d is of "
  915. "type %d\n", dwIfIndex, pTable->table[i].dwType));
  916. if ( (pTable->table[i].dwType == MIB_IF_TYPE_PPP)
  917. || (pTable->table[i].dwType == MIB_IF_TYPE_SLIP))
  918. {
  919. *lpbIsWanIf = TRUE;
  920. }
  921. else
  922. {
  923. *lpbIsWanIf = FALSE;
  924. }
  925. if (lpQOCInfo != NULL)
  926. {
  927. lpQOCInfo->dwSize = sizeof(QOCINFO);
  928. lpQOCInfo->dwInSpeed = pTable->table[i].dwSpeed;
  929. lpQOCInfo->dwOutSpeed = pTable->table[i].dwSpeed;
  930. lpQOCInfo->dwFlags = (*lpbIsWanIf) ? CONNECTION_WAN : CONNECTION_LAN;
  931. }
  932. break;
  933. }
  934. }
  935. END_GETTABLE()
  936. return bFound;
  937. }
  938. BOOL
  939. CheckForReachability(
  940. IN IPAddr DestIpAddr,
  941. IN OUT LPQOCINFO lpQOCInfo,
  942. OUT LPDWORD lpdwLastError
  943. )
  944. /*++
  945. Routine Description:
  946. This helper function does all the dirty work in checking for Reachability
  947. of a particular destination.
  948. Arguments:
  949. DestIpAddr - The Destination of interest.
  950. lpQOCInfo - The QOC Info structure.
  951. lpdwLastError - Returns the GetLastError value when the destination is
  952. not reachable.
  953. Return Value:
  954. TRUE, if the destination IP Address is reachable
  955. FALSE, otherwise. GLE returned in lpdwLastError.
  956. --*/
  957. {
  958. DWORD i;
  959. BOOL bSuccess;
  960. BOOL bSameNetId;
  961. BOOL bReachable;
  962. BOOL bIsWanIf;
  963. DWORD dwNetId;
  964. DWORD dwSubnetMask;
  965. DWORD ifNum;
  966. DWORD dwHopCount;
  967. DWORD dwRtt;
  968. ifNum = -1;
  969. dwRtt = 0;
  970. bSuccess = FALSE;
  971. bIsWanIf = FALSE;
  972. bReachable = FALSE;
  973. bSameNetId = FALSE;
  974. //
  975. // Search the IP Address table for an entry with the same NetId as the
  976. // Destination. If such an entry exists, the Destination is in the same
  977. // sub-net and hence reachable.
  978. //
  979. BEGIN_GETTABLE(MIB_IPADDRTABLE, MIB_IPADDRROW, GETIPADDRTABLE, MAX_IPADDRTABLE_ROWS)
  980. // Search for an entry with the same NetId
  981. for (i = 0; i < pTable->dwNumEntries; i++)
  982. {
  983. // Compare NetIds.
  984. dwSubnetMask = pTable->table[i].dwMask;
  985. dwNetId = pTable->table[i].dwAddr & dwSubnetMask;
  986. SensPrintA(SENS_INFO, ("IPADDRESS(%d) - Mask %8x, IP %8x, NETID %8x, COMP %8x\n", i,
  987. pTable->table[i].dwMask,
  988. pTable->table[i].dwAddr,
  989. dwNetId,
  990. (DestIpAddr & dwSubnetMask))
  991. );
  992. if ( (pTable->table[i].dwAddr != 0x0)
  993. && ((DestIpAddr & dwSubnetMask) == dwNetId))
  994. {
  995. bSameNetId = TRUE;
  996. ifNum = pTable->table[i].dwIndex;
  997. SensPrintA(SENS_INFO, ("CheckForReachability(): Found entry in IPAddr Table with same NetId\n"));
  998. break;
  999. }
  1000. }
  1001. END_GETTABLE()
  1002. if (bSameNetId)
  1003. {
  1004. // Destination is in the same Subnet. Get stats from the IfTable.
  1005. bSuccess = GetIfEntryStats(ifNum, lpQOCInfo, lpdwLastError, &bIsWanIf);
  1006. ASSERT(bSuccess == TRUE);
  1007. if (bSuccess)
  1008. {
  1009. return TRUE;
  1010. }
  1011. }
  1012. //
  1013. // Entry is not in the IP AddrTable. We need to Ping. Search the Gateway
  1014. // table for default gateway and get it's interface statistics.
  1015. //
  1016. BEGIN_GETTABLE(MIB_IPFORWARDTABLE, MIB_IPFORWARDROW, GETIPFORWARDTABLE, MAX_IPFORWARDTABLE_ROWS)
  1017. for (i = 0; i < pTable->dwNumEntries; i++)
  1018. {
  1019. dwSubnetMask = pTable->table[i].dwForwardMask;
  1020. dwNetId = pTable->table[i].dwForwardDest & dwSubnetMask;
  1021. ifNum = pTable->table[i].dwForwardIfIndex;
  1022. SensPrintA(SENS_INFO, ("IPFORWARD(%d) - Mask %8x, IP %8x, NETID %8x, COMP %8x\n", i,
  1023. pTable->table[i].dwForwardMask,
  1024. pTable->table[i].dwForwardDest,
  1025. dwNetId,
  1026. (DestIpAddr & dwSubnetMask))
  1027. );
  1028. if (pTable->table[i].dwForwardDest == 0x0)
  1029. {
  1030. //
  1031. // Skip the default gateway 0.0.0.0. But, get the statistics
  1032. // anyways. The QOC of the default gateway is used if we have
  1033. // to Ping the destination.
  1034. //
  1035. bSuccess = GetIfEntryStats(ifNum, lpQOCInfo, lpdwLastError, &bIsWanIf);
  1036. SensPrintA(SENS_INFO, ("Default Gateway statistics (if = %d, "
  1037. "dwSpeed = %d, IsWanIf = %s)\n", ifNum, lpQOCInfo ?
  1038. lpQOCInfo->dwInSpeed : 0x0, bIsWanIf ? "TRUE" : "FALSE"));
  1039. ASSERT(bSuccess == TRUE);
  1040. break;
  1041. }
  1042. }
  1043. END_GETTABLE()
  1044. //
  1045. // Resort to a Ping
  1046. //
  1047. bReachable = GETRTTANDHOPCOUNT(
  1048. DestIpAddr,
  1049. &dwHopCount,
  1050. MAX_HOPS_COUNT,
  1051. &dwRtt
  1052. );
  1053. //
  1054. // If we got around to doing a Ping, QOC information will have been
  1055. // retrieved when we found the Default Gateway entry.
  1056. //
  1057. SensPrintA(SENS_INFO, ("CheckForReachability(): Ping returned %s with GLE of %d\n",
  1058. bReachable ? "TRUE" : "FALSE", GetLastError()));
  1059. if (bReachable == FALSE)
  1060. {
  1061. *lpdwLastError = ERROR_HOST_UNREACHABLE;
  1062. }
  1063. //
  1064. // P3 BUG:
  1065. //
  1066. // a. We determine whether the interface on which the Ping went is a LAN
  1067. // or WAN by checking the interface type of the default gateway. This
  1068. // is not TRUE!
  1069. //
  1070. return bReachable;
  1071. }
  1072. BOOL
  1073. GetActiveWanInterfaceStatistics(
  1074. OUT LPDWORD lpdwLastError,
  1075. OUT LPDWORD lpdwWanSpeed
  1076. )
  1077. /*++
  1078. Routine Description:
  1079. Get the Statistics field of the Interface entry
  1080. Arguments:
  1081. lpdwLastError - The GLE, if any.
  1082. lpdwWanSpeed - Speed of the WAN interface
  1083. Notes:
  1084. P3 BUG: Currently, this will return the speed of the first WAN interface
  1085. it finds. This won't work properly if there are multiple "active" WAN
  1086. interfaces.
  1087. Return Value:
  1088. TRUE, if statistics were successfully retrieved
  1089. FALSE, otherwise.
  1090. --*/
  1091. {
  1092. DWORD i;
  1093. BOOL bFound;
  1094. *lpdwLastError = ERROR_SUCCESS;
  1095. *lpdwWanSpeed = DEFAULT_WAN_BANDWIDTH;
  1096. bFound = FALSE;
  1097. BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
  1098. // Search the Interface table for the first active WAN interface.
  1099. for (i = 0; i < pTable->dwNumEntries; i++)
  1100. {
  1101. if ( (pTable->table[i].dwType == MIB_IF_TYPE_PPP)
  1102. || (pTable->table[i].dwType == MIB_IF_TYPE_SLIP))
  1103. {
  1104. bFound = TRUE;
  1105. if ( (pTable->table[i].dwInNUcastPkts != 0)
  1106. || (pTable->table[i].dwOutNUcastPkts != 0)
  1107. || (pTable->table[i].dwInErrors != 0)
  1108. || (pTable->table[i].dwOutErrors != 0)
  1109. || (pTable->table[i].dwInDiscards != 0)
  1110. || (pTable->table[i].dwOutDiscards != 0))
  1111. {
  1112. *lpdwWanSpeed = pTable->table[i].dwSpeed;
  1113. break;
  1114. }
  1115. }
  1116. } // for
  1117. END_GETTABLE()
  1118. return bFound;
  1119. }
  1120. BOOL
  1121. PurgeStaleInterfaces(
  1122. IN MIB_IFTABLE *pTable,
  1123. OUT LPDWORD lpdwLastError
  1124. )
  1125. /*++
  1126. Routine Description:
  1127. Remove statistics from the interfaces that went away.
  1128. Arguments:
  1129. pTable - The current If table.
  1130. lpdwLastError - The GLE, if any.
  1131. Return Value:
  1132. TRUE, always.
  1133. --*/
  1134. {
  1135. DWORD i;
  1136. DWORD j;
  1137. BOOL bFound;
  1138. *lpdwLastError = ERROR_SUCCESS;
  1139. RequestSensLock();
  1140. // Check if each valid interface in the cache still exists.
  1141. for (j = 0; j < MAX_IF_ENTRIES; j++)
  1142. {
  1143. if (gIfState[j].fValid == FALSE)
  1144. {
  1145. continue;
  1146. }
  1147. bFound = FALSE;
  1148. // Search if the interface in the cache is present in the IF_TABLE.
  1149. for (i = 0; i < pTable->dwNumEntries; i++)
  1150. {
  1151. if (pTable->table[i].dwIndex == gIfState[j].dwIndex)
  1152. {
  1153. bFound = TRUE;
  1154. }
  1155. } // for (i)
  1156. if (FALSE == bFound)
  1157. {
  1158. SensPrintA(SENS_ERR, ("******** PurgeStaleInterfaces(): Purging"
  1159. "interface with index %d\n", gIfState[j].dwIndex));
  1160. // Interface went away. So remove from Cache.
  1161. memset(&gIfState[j], 0x0, sizeof(IF_STATE));
  1162. gIfState[j].fValid = FALSE;
  1163. }
  1164. } // for (j)
  1165. ReleaseSensLock();
  1166. return TRUE;
  1167. }