Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1805 lines
48 KiB

/*++
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