1806 lines
48 KiB
C++
1806 lines
48 KiB
C++
/*++
|
||
|
||
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
|