|
|
/*++
Copyright (C) Microsoft Corporation, 1997 - 1999
Module Name:
lan.cxx
Abstract:
This is the source file relating to the LAN-specific routines of the Connectivity APIs implementation.
Author:
Gopal Parupudi <GopalP>
[Notes:]
optional-notes
Revision History:
GopalP 10/11/1997 Start.
--*/
#include <precomp.hxx>
//
// Constants
//
#define GETIFTABLE GetIfTable
#define GETIPADDRTABLE GetIpAddrTable
#define GETIPFORWARDTABLE GetIpForwardTable
#define GETRTTANDHOPCOUNT GetRTTAndHopCount
#define GETIPSTATISTICS GetIpStatistics
#define MAX_IFTABLE_ROWS 4
#define MAX_IPADDRTABLE_ROWS 6
#define MAX_IPNETTABLE_ROWS 8
#define MAX_IPFORWARDTABLE_ROWS 8
#define MAX_HOPS_COUNT 0xFFFF
#define BROADCAST_ACTIVITY_THRESHOLD 2 // +2 thru -2
#define MEDIASENSE_INITIALIZATION_DELAY 3*25*1000 // 1:15 minutes
#define MEDIASENSE_EVALUATE_LAN_DELAY 5*1000 // 5 seconds
#define SENS_WINSOCK_VERSION MAKEWORD( 2, 0 )
//
// Globals
//
BOOL gbIpInitSuccessful; long gdwLastLANTime; long gdwLANState; IF_STATE gIfState[MAX_IF_ENTRIES]; MIB_IPSTATS gIpStats; extern CRITICAL_SECTION gSensLock;
HANDLE ghMediaTimer; DWORD gdwMediaSenseState;
//
// Macros
//
/*++
Macro Description:
A macro to help in allocating tables when calling IP Helper APIs.
Arguments:
TABLE_TYPE - The type of the IP Table being queried.
ROW_TYPE - The type of the Row corresponding to the TABLE_TYPE.
FUNC_NAME - The IP API to be called to get the IP table.
MAX_NUM_ROWS - The default number of rows for the table which is being retrieved. These rows are allocated on the stack.
Notes:
o lpdwLastError should be defined as an LPDWORD in the code fragment that uses this macro.
--*/ #define \
BEGIN_GETTABLE( \ TABLE_TYPE, \ ROW_TYPE, \ FUNC_NAME, \ MAX_NUM_ROWS \ ) \ { \ DWORD dwOldSize; \ DWORD dwSize; \ DWORD dwStatus; \ \ BOOL bOrder; \ \ TABLE_TYPE *pTable; \ \ bOrder = FALSE; \ \ dwSize = sizeof(DWORD) + MAX_NUM_ROWS * sizeof(ROW_TYPE); \ pTable = (TABLE_TYPE *) new char[dwSize]; \ if (pTable == NULL) \ { \ SensPrintA(SENS_MEM, (#FUNC_NAME "(): failed to new %d bytes\n", \ dwSize)); \ *lpdwLastError = ERROR_OUTOFMEMORY; \ return FALSE; \ } \ \ dwOldSize = dwSize; \ \ dwStatus = FUNC_NAME( \ pTable, \ &dwSize, \ bOrder \ ); \ \ if ( (dwStatus == ERROR_INSUFFICIENT_BUFFER) \ || (dwStatus == ERROR_MORE_DATA)) \ { \ ASSERT(dwSize > dwOldSize); \ SensPrintA(SENS_WARN, (#FUNC_NAME "(%d): reallocing buffer to be %d bytes\n", \ dwOldSize, dwSize)); \ delete (char *)pTable; \ pTable = (TABLE_TYPE *) new char[dwSize]; \ if (pTable != NULL) \ { \ dwStatus = FUNC_NAME( \ pTable, \ &dwSize, \ bOrder \ ); \ } \ else \ { \ SensPrintA(SENS_MEM, (#FUNC_NAME "(): failed to new (%d) bytes\n", \ dwSize)); \ *lpdwLastError = ERROR_OUTOFMEMORY; \ return FALSE; \ } \ } \ \ if (dwStatus != 0) \ { \ ASSERT( (dwStatus != ERROR_INSUFFICIENT_BUFFER) \ && (dwStatus != ERROR_MORE_DATA)); \ \ SensPrintA(SENS_ERR, (#FUNC_NAME "() returned %d\n", dwStatus));\ *lpdwLastError = dwStatus; \ /* P3 BUG: Might need to fire ConnectionLost() here */ \ gdwLANState = FALSE; \ UpdateSensCache(LAN); \ delete pTable; \ return FALSE; \ }
/*++
Macro Description:
This macro ends the BEGIN_GETTABLE() macro.
Arguments:
None.
Notes:
a. If we have a return between BEGIN_XXX and END_XXX, we need to make sure that we free pTable.
--*/ #define \
END_GETTABLE() \ \ delete pTable; \ \ }
BOOL DoLanSetup( void ) /*++
Routine Description:
Arguments:
None.
Return Value:
--*/ { BOOL bRetValue; WORD wVersionRequested; WSADATA wsaData; int err;
bRetValue = FALSE;
//
// NT5-specific stuff
//
#if defined(SENS_NT5)
ghMediaTimer = NULL;
// Register for Media-sense notifications
if (FALSE == MediaSenseRegister()) { SensPrintA(SENS_ERR, ("%s MediaSenseRegister() failed.\n", SERVICE_NAME)); }
#endif // SENS_NT5
//
// AOL-specific code
//
#if defined(AOL_PLATFORM)
gdwAOLState = FALSE; #endif // AOL_PLATFORM
//
// Initialize Winsock.
//
wVersionRequested = SENS_WINSOCK_VERSION; err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { SensPrintA(SENS_ERR, ("WSAStartup() returned %d!\n", err)); bRetValue = FALSE; goto Cleanup; }
bRetValue = TRUE;
Cleanup: //
// Cleanup
//
#if defined(SENS_NT4)
if ( (FALSE == bRetValue) && (NULL != hDLL)) { FreeLibrary(hDLL); } #endif // SENS_NT4
return bRetValue; }
#ifdef DBG
inline void PrintIfState( void ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { int i;
SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n")); SensPrintA(SENS_INFO, ("| Valid Index UcastIN UcastOUT NUcastIN NUcastOUT ErrIN ErrOUT DiscIN DiscOUT |\n")); SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n"));
for (i = 0; i < MAX_IF_ENTRIES; i++) { SensPrintA(SENS_INFO, ("| %c %9d %7d %7d %9d %9d %5d %6d %6d %6d |\n", gIfState[i].fValid ? 'Y' : 'N', gIfState[i].dwIndex, gIfState[i].dwInUcastPkts, gIfState[i].dwOutUcastPkts, gIfState[i].dwInNUcastPkts, gIfState[i].dwOutNUcastPkts, gIfState[i].dwInErrors, gIfState[i].dwOutErrors, gIfState[i].dwInDiscards, gIfState[i].dwOutDiscards) ); }
SensPrintA(SENS_INFO, ("|---------------------------------------------------------------------------------------|\n")); }
#else // DBG
#define PrintIfState() // Nothing
#endif // DBG
#ifdef DETAIL_DEBUG
void PrintIfTable( MIB_IFTABLE *pTable ) { int i;
SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n")); SensPrintA(SENS_INFO, ("| Type Index Spd/1K UcastIN UcastOUT ErrorIN OUT DiscIN OUT Opr Adm |\n")); SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
for (i = 0; i < pTable->dwNumEntries; i++) { SensPrintA(SENS_INFO, ("| %4d %7d %6d %7d %8d %7d %3d %6d %3d %3d %3d |\n", pTable->table[i].dwType, pTable->table[i].dwIndex, pTable->table[i].dwSpeed/1000, pTable->table[i].dwInUcastPkts, pTable->table[i].dwOutUcastPkts, pTable->table[i].dwInErrors, pTable->table[i].dwOutErrors, pTable->table[i].dwInDiscards, pTable->table[i].dwOutDiscards, pTable->table[i].dwOperStatus, pTable->table[i].dwAdminStatus ) ); }
SensPrintA(SENS_INFO, ("|------------------------------------------------------------------------------|\n"));
}
void PrintIpStats( void ) {
SensPrintA(SENS_INFO, ("|------------------------------------|\n")); SensPrintA(SENS_INFO, ("| IP_STATS InReceives OutRequests |\n")); SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
SensPrintA(SENS_INFO, ("| %10d %10d |\n", gIpStats.dwInReceives, gIpStats.dwOutRequests) );
SensPrintA(SENS_INFO, ("|------------------------------------|\n"));
}
#else
#define PrintIfTable(_X_) // Nothing
#define PrintIpStats() // Nothing
#endif // DETAIL_DEBUG
BOOL WINAPI EvaluateLanConnectivityDelayed( LPDWORD ) { for (int i = 0; i < 4; i++) { Sleep(MEDIASENSE_EVALUATE_LAN_DELAY*i); // First time waits 0 ms, no delay
if (EvaluateLanConnectivity(NULL)) { SensPrintA(SENS_INFO, ("EvaluateLanConnectivity: Delayed eval successful (%d)\n", i)); return TRUE; } } return FALSE; }
BOOL WINAPI EvaluateLanConnectivity( OUT LPDWORD lpdwLastError ) /*++
Routine Description:
Evaluates LAN Connectivity.
Arguments:
lpdwLastError - if return value is FALSE, GetLastError is returned in this OUT parameter.
Notes:
a. This routine can be entered by multiple threads at the same time. Currently only very essential code is under a critical section.
b. This routine can be executed as a threadpool work item.
Return Value:
TRUE, if LAN connectivity is present.
FALSE, otherwise
--*/ { DWORD dwNow; DWORD dwLocalLastError; DWORD dwActiveInterfaceSpeed; WCHAR wszActiveInterfaceName[MAX_INTERFACE_NAME_LEN]; BOOL bLanAlive; BOOL bSomeInterfaceActive; BOOL bCheckCache;
dwNow = GetTickCount(); dwLocalLastError = ERROR_NO_NETWORK; dwActiveInterfaceSpeed = 0x0; bLanAlive = FALSE; bSomeInterfaceActive = FALSE; bCheckCache = FALSE;
if (lpdwLastError) { *lpdwLastError = dwLocalLastError; } else { lpdwLastError = &dwLocalLastError; }
//
// Get infomation about IP statistics.
//
BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
//
// PurgeStaleInterfaces
//
PurgeStaleInterfaces(pTable, lpdwLastError);
//
// Algorithm:
//
// o Create a record.
// o See if this record exists.
// o Save the record, if not and return success.
// o If it does exist, compare. If greater, then save record.
// o If not greater, try other entries.
// o All entries? return failure.
//
IF_STATE ifEntry; DWORD i;
SensPrintA(SENS_INFO, ("GetIfTable(): Number of entries - %d.\n", pTable->dwNumEntries));
PrintIfTable(pTable);
i = 0; while (i < pTable->dwNumEntries) { //
// Calculate only if it is a non-WAN and non-Loopback interface.
//
if ( (pTable->table[i].dwType != MIB_IF_TYPE_PPP) && (pTable->table[i].dwType != MIB_IF_TYPE_SLIP) && (pTable->table[i].dwType != MIB_IF_TYPE_LOOPBACK)) { BOOL bForceInvalid = FALSE;
//
// BOOT UP WITH NO NETWORK:
//
// Check to see if both UnicastIN and UnicastOUT are zero. If so,
// this interface is considered as not active and we skip it.
//
if ( (pTable->table[i].dwInUcastPkts == 0) && (pTable->table[i].dwOutUcastPkts == 0)) { bForceInvalid = TRUE; }
//
// Check if networking says it is connected, if not, skip it.
//
if (pTable->table[i].dwOperStatus < MIB_IF_OPER_STATUS_CONNECTING) { SensPrintA(SENS_INFO, ("GetIfTable: Found interface %d in < connecting state (%d), ignored\n", pTable->table[i].dwIndex, pTable->table[i].dwOperStatus) ); bForceInvalid = TRUE; }
//
// At this stage, there is some Unicast activity on this interface.
// So, we can skip the check for Unicast activity below.
//
// Fill the IF_STATE structure
ifEntry.dwIndex = pTable->table[i].dwIndex; ifEntry.dwInUcastPkts = pTable->table[i].dwInUcastPkts; ifEntry.dwOutUcastPkts = pTable->table[i].dwOutUcastPkts; ifEntry.dwInNUcastPkts = pTable->table[i].dwInNUcastPkts; ifEntry.dwOutNUcastPkts = pTable->table[i].dwOutNUcastPkts; ifEntry.dwInErrors = pTable->table[i].dwInErrors; ifEntry.dwOutErrors = pTable->table[i].dwOutErrors; ifEntry.dwInDiscards = pTable->table[i].dwInDiscards; ifEntry.dwOutDiscards = pTable->table[i].dwOutDiscards;
bSomeInterfaceActive = HasIfStateChanged(ifEntry, bForceInvalid); if (TRUE == bSomeInterfaceActive) { bLanAlive = TRUE;
// Save info about interface for later use.
dwActiveInterfaceSpeed = pTable->table[i].dwSpeed; wcscpy(wszActiveInterfaceName, pTable->table[i].wszName); } else { if (!bForceInvalid) { bCheckCache = TRUE; // Idle IF found but still valid (enable MAX_LAN_INTERNAL check below)
} } }
i++;
} // while ()
PrintIfState();
END_GETTABLE()
//
// RACE Condition Fix:
//
// If there are 2 threads that are in EvaluateLanConnectivity() and one
// of them updates the interface's packet cache, then there is a distinct
// possibility that the second thread will compare with the updated cache
// and wrongly conclude that there is no activity. We ignore any loss of
// connectivity that was evaluated before MAX_LAN_INTERVAL (ie., we should
// keep giving cached information for MAX_LAN_INTERVAL seconds).
//
if ( (TRUE == bCheckCache) && (FALSE == bLanAlive) ) {
dwNow = GetTickCount(); if ( ((dwNow - gdwLastLANTime) <= MAX_LAN_INTERVAL) && (gdwLastLANTime != 0) ) { SensPrintA(SENS_DBG, ("EvaluateLanConnectivity(): Returning TRUE " "(Now - %d sec, LastLANTime - %d sec)\n", dwNow/1000, gdwLastLANTime/1000)); return TRUE; } }
//
// NOTE: If we are doing DWORD InterlockedExchange, then assignment
// should suffice. Using InterlockedExchange is not a bug, though.
//
if (bLanAlive) { SensPrintA(SENS_DBG, ("**** EvaluateLanConnectivity(): Setting" " gdwLastLANTime to %d secs\n", dwNow/1000)); InterlockedExchange(&gdwLastLANTime, dwNow); } else { SensPrintA(SENS_DBG, ("**** EvaluateLanConnectivity(): Setting" " gdwLastLANTime to 0 secs\n")); InterlockedExchange(&gdwLastLANTime, 0x0); }
//
// Adjust LAN state and fire an event, if necessary.
//
if (InterlockedExchange(&gdwLANState, bLanAlive) != bLanAlive) { //
// LAN Connectivity state changed.
//
SENSEVENT_NETALIVE Data;
Data.eType = SENS_EVENT_NETALIVE; Data.bAlive = bLanAlive; memset(&Data.QocInfo, 0x0, sizeof(QOCINFO)); Data.QocInfo.dwSize = sizeof(QOCINFO); Data.QocInfo.dwFlags = NETWORK_ALIVE_LAN; Data.QocInfo.dwInSpeed = dwActiveInterfaceSpeed; Data.QocInfo.dwOutSpeed = dwActiveInterfaceSpeed; //
// NOTE: When dwActiveInterfaceName gets the right value from
// IPHLPAPIs we should use that name. Until then, we use a default.
//
Data.strConnection = DEFAULT_LAN_CONNECTION_NAME;
UpdateSensCache(LAN);
SensFireEvent((PVOID)&Data); }
return bLanAlive; }
BOOL HasIfStateChanged( IF_STATE ifEntry, BOOL bForceInvalid ) /*++
Routine Description:
Compares the current state of a remote network IF with the cached history to determine if it is active or not.
Arguments:
ifEntry - An interface that appears to have changed state and is "valid" as a remote LAN if. (ie, loopback, pptp, etc should be filtered out) bForceInvalid - If TRUE, don't bother to look at the stats; this interface is NOT valid.
Return Value: TRUE - ifEntry appears up and active FALSE - ifEntry inactive or down --*/ { int i, j; static int iLastActiveIndex = -1; BOOL bActive; BOOL bSeenButInactive; DWORD dwInDiff; DWORD dwOutDiff; int iNUcastDiff;
i = 0; bActive = FALSE; bSeenButInactive = FALSE; dwInDiff = 0; dwOutDiff = 0; iNUcastDiff = 0;
RequestSensLock();
//
// Compare the current snapshot with the saved snapshot
// for this interface.
//
while (i < MAX_IF_ENTRIES) { if ( (gIfState[i].fValid == TRUE) && (gIfState[i].dwIndex == ifEntry.dwIndex)) {
if (bForceInvalid) { gIfState[i].fValid = FALSE; break; }
if ( (ifEntry.dwInUcastPkts > gIfState[i].dwInUcastPkts) || (ifEntry.dwOutUcastPkts > gIfState[i].dwOutUcastPkts) || (ifEntry.dwInNUcastPkts > gIfState[i].dwInNUcastPkts) || (ifEntry.dwOutNUcastPkts > gIfState[i].dwOutNUcastPkts) || (ifEntry.dwInErrors > gIfState[i].dwInErrors) || (ifEntry.dwOutErrors > gIfState[i].dwOutErrors) || (ifEntry.dwInDiscards > gIfState[i].dwInDiscards) || (ifEntry.dwOutDiscards > gIfState[i].dwOutDiscards)) { //
// HEURISTIC:
//
// a. When the net tap is pulled out, it has been observed that
// the difference in the incoming non-Unicast packet count
// is within +1 thru -1 of the difference in the outgoing
// non-Unicast packet count. Most of the times the diff of
// these differences is 0. We don't count this as LAN alive
//
// b. Also, there should be no change in the unicast IN packet
// count. Unicast OUT packet count may change. This could be
// problematic.
//
dwInDiff = ifEntry.dwInNUcastPkts - gIfState[i].dwInNUcastPkts; dwOutDiff = ifEntry.dwOutNUcastPkts - gIfState[i].dwOutNUcastPkts; iNUcastDiff = dwOutDiff - dwInDiff; SensPrintA(SENS_INFO, ("HasIfStateChanged(): dwInDiff = %d, " "dwOutDiff = %d, dwNUcastDiff = %d, UcastINDiff = " "%d, UcastOUTDiff = %d\n", dwInDiff, dwOutDiff, iNUcastDiff, ifEntry.dwInUcastPkts - gIfState[i].dwInUcastPkts, ifEntry.dwOutUcastPkts - gIfState[i].dwOutUcastPkts));
if ( (ifEntry.dwInUcastPkts == gIfState[i].dwInUcastPkts) && (iNUcastDiff <= BROADCAST_ACTIVITY_THRESHOLD) && (iNUcastDiff >= -BROADCAST_ACTIVITY_THRESHOLD)) { SensPrintA(SENS_INFO, ("HasIfStateChanged(): Interface %d" " has only Broadcast activity (Diff is %d)!\n", gIfState[i].dwIndex, iNUcastDiff)); bSeenButInactive = TRUE; } else { //
// Unicast IN packet counts have changed or Broadcast
// activity is greater than the threshold.
//
iLastActiveIndex = i; bActive = TRUE; SensPrintA(SENS_INFO, ("HasStateChanged(): Interface %d " "has been active.\n", gIfState[i].dwIndex));
gdwLastLANTime = GetTickCount(); SensPrintA(SENS_DBG, ("**** HasIfStateChanged(): Setting " "gdwLastLANTime to %d secs\n", gdwLastLANTime/1000)); }
//
// Save the new values.
//
memcpy(&gIfState[i], &ifEntry, sizeof(IF_STATE)); gIfState[i].fValid = TRUE; } else { SensPrintA(SENS_INFO, ("HasStateChanged(): Interface %d has NO activity.\n", gIfState[i].dwIndex)); bSeenButInactive = TRUE; }
// Found the interface, so stop searching
break; }
i++;
} // while ()
ReleaseSensLock();
if ( (bSeenButInactive == TRUE) || (bForceInvalid) ) { return FALSE; }
if (bActive == TRUE) { return TRUE; }
//
// We are seeing this interface for the first time. Go ahead and save it
// in the global interface state array.
//
i = MAX_IF_ENTRIES; j = iLastActiveIndex;
RequestSensLock();
while (i > 0) { // Try to find a free slot starting from the last active slot.
j = (j+1) % MAX_IF_ENTRIES;
if (gIfState[j].fValid == FALSE) { // Found one!
break; }
i--; }
//
// NOTE: If there are more than MAX_IF_ENTRIES, we will start
// start reusing valid interface elements in gIfState array. This,
// I guess, is OK since we will have enough interfaces to figure
// out connectivity.
//
memcpy(&gIfState[j], &ifEntry, sizeof(IF_STATE)); gIfState[j].fValid = TRUE;
ReleaseSensLock();
SensPrintA(SENS_ERR, ("******** HasIfStateChanged(): Adding a new " "interface with index %d\n", gIfState[j].dwIndex));
return TRUE; }
BOOL MediaSenseRegister( void ) /*++
Routine Description:
Schedule a workitem to register for Media-sense notifications from WMI.
Arguments:
None.
Return Value:
TRUE, if success.
FALSE, otherwise.
--*/ { BOOL bRetVal;
bRetVal = TRUE;
ASSERT(gdwMediaSenseState == SENSSVC_START);
//
// Create a timer object to schedule (one-time only) Media-sense
// registration.
//
SensSetTimerQueueTimer( bRetVal, // Bool return on NT5
ghMediaTimer, // Handle return on IE5
NULL, // Use default process timer queue
MediaSenseRegisterHelper, // Callback
NULL, // Parameter
MEDIASENSE_INITIALIZATION_DELAY, // Time from now when timer should fire
0x0, // Time inbetween firings of this timer
0x0 // No Flags.
); if (SENS_TIMER_CREATE_FAILED(bRetVal, ghMediaTimer)) { SensPrintA(SENS_ERR, ("MediaSenseRegister(): SensSetTimerQueueTimer() failed with %d.\n", GetLastError())); bRetVal = FALSE; }
return bRetVal; }
SENS_TIMER_CALLBACK_RETURN MediaSenseRegisterHelper( PVOID pvIgnore, BOOLEAN bIgnore ) /*++
Routine Description:
Helper routine that is scheduled to the WMI registration.
Arguments:
pvIgnore - Ignored.
bIgnore - Ignored.
Return Value:
None (void).
--*/ { ULONG Status; GUID guid;
RequestSensLock(); if ( (SENSSVC_STOP == gdwMediaSenseState) || (UNREGISTERED == gdwMediaSenseState)) { goto Cleanup; }
//
// Enable the media disconnect event.
//
guid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
Status = WmiNotificationRegistrationW( &guid, // Event of interest
TRUE, // Enable Notification?
EventCallbackRoutine, // Callback function
0, // Callback context
NOTIFICATION_CALLBACK_DIRECT // Notification flags
); if (ERROR_SUCCESS != Status) { SensPrintA(SENS_ERR, ("Unable to enable media disconnect event: 0x%x!\n", Status)); goto Cleanup; }
//
// Enable the media connect event
//
guid = GUID_NDIS_STATUS_MEDIA_CONNECT;
Status = WmiNotificationRegistrationW( &guid, // Event of interest
TRUE, // Enable Notification?
EventCallbackRoutine, // Callback function
0, // Callback context
NOTIFICATION_CALLBACK_DIRECT // Notification flags
); if (ERROR_SUCCESS != Status) { SensPrintA(SENS_ERR, ("Unable to enable media connect event: 0x%x!\n", Status)); ASSERT(0); // If we hit this then we need to unregister the first registration above.
goto Cleanup; }
SensPrintA(SENS_ERR, ("MediaSenseRegister(): Media-sense registration successful.\n"));
gdwMediaSenseState = REGISTERED;
Cleanup: //
// Cleanup
//
ReleaseSensLock();
return; }
BOOL MediaSenseUnregister( void ) /*++
Routine Description:
Unregister from Media-sense notifications from WMI.
Arguments:
None.
Return Value:
TRUE, if success.
FALSE, otherwise.
--*/ { ULONG Status; GUID guid; BOOL bRetVal; BOOL bRegistered;
bRetVal = TRUE; bRegistered = FALSE;
RequestSensLock();
ASSERT(gdwMediaSenseState == REGISTERED || gdwMediaSenseState == SENSSVC_START);
if (gdwMediaSenseState == REGISTERED) { bRegistered = TRUE; }
gdwMediaSenseState = SENSSVC_STOP;
if (NULL != ghMediaTimer) { bRetVal = SensCancelTimerQueueTimer(NULL, ghMediaTimer, NULL); ghMediaTimer = NULL;
SensPrintA(SENS_INFO, ("[%d] MediaSensUnregister(): SensCancelTimer" "QueueTimer(Media) %s\n", GetTickCount(), bRetVal ? "succeeded" : "failed!")); }
if (!bRegistered) { // Should not do unregistration.
goto Cleanup; }
//
// Disable the media disconnect event.
//
guid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
Status = WmiNotificationRegistrationW( &guid, // Event of interest
FALSE, // Enable Notification?
EventCallbackRoutine, // Callback function
0, // Callback context
NOTIFICATION_CALLBACK_DIRECT // Notification flags
); if (ERROR_SUCCESS != Status) { SensPrintA(SENS_ERR, ("[%d] MediaSensUnregister(): Unable to disable " "media disconnect event: 0x%x!\n", GetTickCount(), Status)); ASSERT(0); // If this fails analyze if we should still to the second unregister
bRetVal = FALSE; }
//
// Disable the connect event
//
guid = GUID_NDIS_STATUS_MEDIA_CONNECT;
Status = WmiNotificationRegistrationW( &guid, // Event of interest
FALSE, // Enable Notification?
EventCallbackRoutine, // Callback function
0, // Callback context
NOTIFICATION_CALLBACK_DIRECT // Notification flags
); if (ERROR_SUCCESS != Status) { SensPrintA(SENS_ERR, ("[%d] MediaSensUnregister(): Unable to disable " "media disconnect event: 0x%x!\n", GetTickCount(), Status)); bRetVal = FALSE; }
Cleanup: //
//
//
gdwMediaSenseState = UNREGISTERED;
ReleaseSensLock();
return bRetVal; }
void EventCallbackRoutine( IN PWNODE_HEADER WnodeHeader, IN ULONG Context ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { PULONG Data; PWNODE_SINGLE_INSTANCE Wnode = (PWNODE_SINGLE_INSTANCE)WnodeHeader; PWCHAR Name; DWORD dwIgnore; ULONG NameLen; int result;
//
// Get the information for the media disconnect.
//
result = memcmp(&WnodeHeader->Guid, &GUID_NDIS_STATUS_MEDIA_DISCONNECT, sizeof(GUID)); if (0 == result) { SensPrintA(SENS_INFO, ("NDIS: received a media disconnect!\n")); EvaluateConnectivity(TYPE_LAN); } else { //
// Get the information for the media connect.
//
result = memcmp(&WnodeHeader->Guid, &GUID_NDIS_STATUS_MEDIA_CONNECT, sizeof(GUID)); if (0 == result) { SensPrintA(SENS_INFO, ("NDIS: received a media connect!\n")); EvaluateConnectivity(TYPE_DELAY_LAN); } else { SensPrintA(SENS_WARN, ("NDIS: Unknown event received!\n")); } }
Name = (PWCHAR)RtlOffsetToPointer(Wnode, Wnode->OffsetInstanceName);
SensPrintW(SENS_INFO, (L"NDIS: Instance: %ws\n", Name)); }
BOOL GetIfEntryStats( IN DWORD dwIfIndex, IN LPQOCINFO lpQOCInfo, OUT LPDWORD lpdwLastError, OUT LPBOOL lpbIsWanIf ) /*++
Routine Description:
Get the Statistics field of the Interface entry which has the given index.
Arguments:
dwIfIndex - The interface of interest.
lpQOCInfo - QOC Info structure whose fields are set when the interface entry is found in the interface table.
lpdwLastError - The GLE, if any.
lpbIsWanIf - Is the interface at this index a WAN interface or not.
Return Value:
TRUE, if we find the index.
FALSE, otherwise.
--*/ { DWORD i; BOOL bFound;
*lpdwLastError = ERROR_SUCCESS; *lpbIsWanIf = FALSE; bFound = FALSE;
BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
// Search the Interface table for the entry with the given index.
for (i = 0; i < pTable->dwNumEntries; i++) { if (pTable->table[i].dwIndex == dwIfIndex) { bFound = TRUE;
SensPrintA(SENS_INFO, ("GetIfEntryStats(): Interface %d is of " "type %d\n", dwIfIndex, pTable->table[i].dwType));
if ( (pTable->table[i].dwType == MIB_IF_TYPE_PPP) || (pTable->table[i].dwType == MIB_IF_TYPE_SLIP)) { *lpbIsWanIf = TRUE; } else { *lpbIsWanIf = FALSE; }
if (lpQOCInfo != NULL) { lpQOCInfo->dwSize = sizeof(QOCINFO); lpQOCInfo->dwInSpeed = pTable->table[i].dwSpeed; lpQOCInfo->dwOutSpeed = pTable->table[i].dwSpeed; lpQOCInfo->dwFlags = (*lpbIsWanIf) ? CONNECTION_WAN : CONNECTION_LAN; }
break; } }
END_GETTABLE()
return bFound; }
BOOL CheckForReachability( IN IPAddr DestIpAddr, IN OUT LPQOCINFO lpQOCInfo, OUT LPDWORD lpdwLastError ) /*++
Routine Description:
This helper function does all the dirty work in checking for Reachability of a particular destination.
Arguments:
DestIpAddr - The Destination of interest.
lpQOCInfo - The QOC Info structure.
lpdwLastError - Returns the GetLastError value when the destination is not reachable.
Return Value:
TRUE, if the destination IP Address is reachable
FALSE, otherwise. GLE returned in lpdwLastError.
--*/ { DWORD i; BOOL bSuccess; BOOL bSameNetId; BOOL bReachable; BOOL bIsWanIf; DWORD dwNetId; DWORD dwSubnetMask; DWORD ifNum; DWORD dwHopCount; DWORD dwRtt;
ifNum = -1; dwRtt = 0; bSuccess = FALSE; bIsWanIf = FALSE; bReachable = FALSE; bSameNetId = FALSE;
//
// On NT4, check to see if IPHLPAPI was present. If not, gracefully fail with
// default values.
//
#if defined(SENS_NT4)
if (FALSE == gbIpInitSuccessful) { lpdwLastError = 0x0; if (NULL != lpQOCInfo) { lpQOCInfo->dwSize = sizeof(QOCINFO); lpQOCInfo->dwFlags = CONNECTION_LAN; lpQOCInfo->dwInSpeed = DEFAULT_LAN_BANDWIDTH; lpQOCInfo->dwOutSpeed = DEFAULT_LAN_BANDWIDTH; }
return TRUE; } #endif // SENS_NT4
//
// Search the IP Address table for an entry with the same NetId as the
// Destination. If such an entry exists, the Destination is in the same
// sub-net and hence reachable.
//
BEGIN_GETTABLE(MIB_IPADDRTABLE, MIB_IPADDRROW, GETIPADDRTABLE, MAX_IPADDRTABLE_ROWS)
// Search for an entry with the same NetId
for (i = 0; i < pTable->dwNumEntries; i++) { // Compare NetIds.
dwSubnetMask = pTable->table[i].dwMask; dwNetId = pTable->table[i].dwAddr & dwSubnetMask;
SensPrintA(SENS_INFO, ("IPADDRESS(%d) - Mask %8x, IP %8x, NETID %8x, COMP %8x\n", i, pTable->table[i].dwMask, pTable->table[i].dwAddr, dwNetId, (DestIpAddr & dwSubnetMask)) );
if ( (pTable->table[i].dwAddr != 0x0) && ((DestIpAddr & dwSubnetMask) == dwNetId)) { bSameNetId = TRUE; ifNum = pTable->table[i].dwIndex; SensPrintA(SENS_INFO, ("CheckForReachability(): Found entry in IPAddr Table with same NetId\n")); break; } }
END_GETTABLE()
if (bSameNetId) { // Destination is in the same Subnet. Get stats from the IfTable.
bSuccess = GetIfEntryStats(ifNum, lpQOCInfo, lpdwLastError, &bIsWanIf); ASSERT(bSuccess == TRUE); if (bSuccess) { return TRUE; } }
//
// Entry is not in the IP AddrTable. We need to Ping. Search the Gateway
// table for default gateway and get it's interface statistics.
//
BEGIN_GETTABLE(MIB_IPFORWARDTABLE, MIB_IPFORWARDROW, GETIPFORWARDTABLE, MAX_IPFORWARDTABLE_ROWS)
for (i = 0; i < pTable->dwNumEntries; i++) { dwSubnetMask = pTable->table[i].dwForwardMask; dwNetId = pTable->table[i].dwForwardDest & dwSubnetMask; ifNum = pTable->table[i].dwForwardIfIndex;
SensPrintA(SENS_INFO, ("IPFORWARD(%d) - Mask %8x, IP %8x, NETID %8x, COMP %8x\n", i, pTable->table[i].dwForwardMask, pTable->table[i].dwForwardDest, dwNetId, (DestIpAddr & dwSubnetMask)) );
if (pTable->table[i].dwForwardDest == 0x0) { //
// Skip the default gateway 0.0.0.0. But, get the statistics
// anyways. The QOC of the default gateway is used if we have
// to Ping the destination.
//
bSuccess = GetIfEntryStats(ifNum, lpQOCInfo, lpdwLastError, &bIsWanIf); SensPrintA(SENS_INFO, ("Default Gateway statistics (if = %d, " "dwSpeed = %d, IsWanIf = %s)\n", ifNum, lpQOCInfo ? lpQOCInfo->dwInSpeed : 0x0, bIsWanIf ? "TRUE" : "FALSE")); ASSERT(bSuccess == TRUE); break; } }
END_GETTABLE()
//
// Resort to a Ping
//
bReachable = GETRTTANDHOPCOUNT( DestIpAddr, &dwHopCount, MAX_HOPS_COUNT, &dwRtt );
//
// If we got around to doing a Ping, QOC information will have been
// retrieved when we found the Default Gateway entry.
//
SensPrintA(SENS_INFO, ("CheckForReachability(): Ping returned %s with GLE of %d\n", bReachable ? "TRUE" : "FALSE", GetLastError()));
if (bReachable == FALSE) { *lpdwLastError = ERROR_HOST_UNREACHABLE; }
//
// P3 BUG:
//
// a. We determine whether the interface on which the Ping went is a LAN
// or WAN by checking the interface type of the default gateway. This
// is not TRUE!
//
return bReachable; }
BOOL GetActiveWanInterfaceStatistics( OUT LPDWORD lpdwLastError, OUT LPDWORD lpdwWanSpeed ) /*++
Routine Description:
Get the Statistics field of the Interface entry
Arguments:
lpdwLastError - The GLE, if any.
lpdwWanSpeed - Speed of the WAN interface
Notes:
P3 BUG: Currently, this will return the speed of the first WAN interface it finds. This won't work properly if there are multiple "active" WAN interfaces.
Return Value:
TRUE, if statistics were successfully retrieved
FALSE, otherwise.
--*/ { DWORD i; BOOL bFound;
*lpdwLastError = ERROR_SUCCESS; *lpdwWanSpeed = DEFAULT_WAN_BANDWIDTH; bFound = FALSE;
#if defined(SENS_NT4)
if (FALSE == gbIpInitSuccessful) { return TRUE; } #endif // SENS_NT4
BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
// Search the Interface table for the first active WAN interface.
for (i = 0; i < pTable->dwNumEntries; i++) { if ( (pTable->table[i].dwType == MIB_IF_TYPE_PPP) || (pTable->table[i].dwType == MIB_IF_TYPE_SLIP)) { bFound = TRUE;
if ( (pTable->table[i].dwInNUcastPkts != 0) || (pTable->table[i].dwOutNUcastPkts != 0) || (pTable->table[i].dwInErrors != 0) || (pTable->table[i].dwOutErrors != 0) || (pTable->table[i].dwInDiscards != 0) || (pTable->table[i].dwOutDiscards != 0)) { *lpdwWanSpeed = pTable->table[i].dwSpeed; break; } } } // for
END_GETTABLE()
return bFound; }
BOOL PurgeStaleInterfaces( IN MIB_IFTABLE *pTable, OUT LPDWORD lpdwLastError ) /*++
Routine Description:
Remove statistics from the interfaces that went away.
Arguments:
pTable - The current If table.
lpdwLastError - The GLE, if any.
Return Value:
TRUE, always.
--*/ { DWORD i; DWORD j; BOOL bFound;
*lpdwLastError = ERROR_SUCCESS;
RequestSensLock();
// Check if each valid interface in the cache still exists.
for (j = 0; j < MAX_IF_ENTRIES; j++) { if (gIfState[j].fValid == FALSE) { continue; }
bFound = FALSE;
// Search if the interface in the cache is present in the IF_TABLE.
for (i = 0; i < pTable->dwNumEntries; i++) { if (pTable->table[i].dwIndex == gIfState[j].dwIndex) { bFound = TRUE; } } // for (i)
if (FALSE == bFound) { SensPrintA(SENS_ERR, ("******** PurgeStaleInterfaces(): Purging" "interface with index %d\n", gIfState[j].dwIndex));
// Interface went away. So remove from Cache.
memset(&gIfState[j], 0x0, sizeof(IF_STATE)); gIfState[j].fValid = FALSE; }
} // for (j)
ReleaseSensLock();
return TRUE; }
#if defined(AOL_PLATFORM)
BOOL IsAOLInstalled( void ) /*++
Routine Description:
Try to determine if AOL is installed on this machine.
Arguments:
None.
Return Value:
TRUE, if AOL is installed.
FALSE, otherwise.
--*/ { if (AOL_NOT_INSTALLED == gAOLInstallState) { SensPrintA(SENS_ERR, ("IsAOLInstalled(): NOT INSTALLED.\n")); return FALSE; }
if (AOL_INSTALLED == gAOLInstallState) { SensPrintA(SENS_ERR, ("IsAOLInstalled(): INSTALLED !\n")); return TRUE; }
ASSERT(AOL_DETECT_PENDING == gAOLInstallState); gAOLInstallState = AOL_NOT_INSTALLED;
HKEY hKeyAOL; LONG lResult;
//
// Open AOL Key under HKCU.
//
hKeyAOL = NULL; lResult = RegOpenKeyEx( HKEY_CURRENT_USER, // Handle to the Parent
REGSZ_AOLKEY, // Name of the child key
0, // Reserved
KEY_ENUMERATE_SUB_KEYS, // Security Access Mask
&hKeyAOL // Handle to the opened key
); if (lResult != ERROR_SUCCESS) { SensPrintA(SENS_ERR, ("IsAOLInstalled(): RegOpenKeyEx(HKCU\\AOL) " "failed with %d\n,", lResult)); return FALSE; }
//
// To make sure, open AOL Key under HKLM.
//
RegCloseKey(hKeyAOL); hKeyAOL = NULL; lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // Handle to the Parent
REGSZ_AOLKEY, // Name of the child key
0, // Reserved
KEY_ENUMERATE_SUB_KEYS, // Security Access Mask
&hKeyAOL // Handle to the opened key
); if (lResult != ERROR_SUCCESS) { SensPrintA(SENS_ERR, ("IsAOLInstalled(): RegOpenKeyEx(HKLM\\AOL) " "failed with %d\n,", lResult)); return FALSE; }
RegCloseKey(hKeyAOL);
gAOLInstallState = AOL_INSTALLED;
SensPrintA(SENS_ERR, ("IsAOLInstalled(): Detected that AOL is installed" " !\n"));
return TRUE; }
BOOL WINAPI EvaluateAOLConnectivity( OUT LPDWORD lpdwLastError ) /*++
Routine Description:
Evaluates AOL WAN Connectivity.
Arguments:
lpdwLastError - if return value is FALSE, GetLastError is returned in this OUT parameter.
Notes:
a. This routine can be executed as a threadpool work item.
b. Currently, AOL can be installed only on Win9x platforms, not NTx. This code needs to be updated to handle NTx.
Return Value:
TRUE, if AOL WAN connectivity is present.
FALSE, otherwise
--*/ { DWORD i; DWORD dwNow; DWORD dwAolIfIndex; DWORD dwLocalLastError; BOOL bAolAlive;
dwNow = GetTickCount(); dwAolIfIndex = -1; dwLocalLastError = ERROR_NO_NETWORK; bAolAlive = FALSE; if (lpdwLastError) { *lpdwLastError = dwLocalLastError; } else { lpdwLastError = &dwLocalLastError; }
//
// Check if AOL is installed
//
if (FALSE == IsAOLInstalled()) { goto Cleanup; }
//
// Get IF_TABLE to retrieve the AOL Adapater's interface index.
//
BEGIN_GETTABLE(MIB_IFTABLE, MIB_IFROW, GETIFTABLE, MAX_IFTABLE_ROWS)
SensPrintA(SENS_INFO, ("GetIfTable(): Number of entries - %d.\n", pTable->dwNumEntries));
PrintIfTable(pTable);
for (i = 0; i < pTable->dwNumEntries; i++) { //
// AOL Adapter's description is atleast 3 characters long
//
if (pTable->table[i].dwDescrLen > AOL_ADAPTER_PREFIX_LEN) { if ( (pTable->table[i].bDescr[0] == AOL_ADAPTER_PREFIX[0]) && (pTable->table[i].bDescr[1] == AOL_ADAPTER_PREFIX[1]) && (pTable->table[i].bDescr[2] == AOL_ADAPTER_PREFIX[2])) { dwAolIfIndex = pTable->table[i].dwIndex; break; } } } // for ()
END_GETTABLE()
if (-1 == dwAolIfIndex) { SensPrintA(SENS_INFO, ("EvaluateAOLConnectivity(): No AOL adapter" " found!\n")); goto Cleanup; }
//
// Found an AOL Adapter. Now, see if this Adapter has a non-zero IP Address
//
BEGIN_GETTABLE(MIB_IPADDRTABLE, MIB_IPADDRROW, GETIPADDRTABLE, MAX_IPADDRTABLE_ROWS)
// Search for an entry for the AOL adapter
for (i = 0; i < pTable->dwNumEntries; i++) { if ( (pTable->table[i].dwIndex == dwAolIfIndex) && (pTable->table[i].dwAddr != 0x0)) { bAolAlive = TRUE; SensPrintA(SENS_INFO, ("EvaluateAOLConnectivity(): AOL Adapter's " "IP Address is 0x%x\n", pTable->table[i].dwAddr)); break; } } // for ()
END_GETTABLE()
Cleanup: //
// Cleanup
//
if (bAolAlive) { SensPrintA(SENS_INFO, ("EvalutateAOLConnectivity(): Setting AOL to TRUE\n")); *lpdwLastError = ERROR_SUCCESS; gdwAOLState = TRUE; } else { SensPrintA(SENS_INFO, ("EvalutateAOLConnectivity(): Setting AOL to FALSE\n")); gdwAOLState = FALSE; }
SensPrintA(SENS_INFO, ("EvaluateAOLConnectivity() returning %s, GLE of %d\n", bAolAlive ? "TRUE" : "FALSE", *lpdwLastError));
return bAolAlive; }
#endif // AOL_PLATFORM
|