1606 lines
40 KiB
C
1606 lines
40 KiB
C
|
//=============================================================================
|
||
|
// Copyright (c) 1997 Microsoft Corporation
|
||
|
// Module Name: Api.c
|
||
|
// Abstract:
|
||
|
// This module implements some of the Igmp APIs
|
||
|
// RegisterProtocol, StartProtocol, StopProtocol,
|
||
|
// GetGlobalInfo, SetGlobalInfo, and GetEventMessage
|
||
|
//
|
||
|
// Author: K.S.Lokesh (lokeshs@) 11-1-97
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
#include "pchigmp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Global variables (see global.h for description)
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD g_Initialized;
|
||
|
|
||
|
|
||
|
// interface table, group table, global config, global stats
|
||
|
|
||
|
PIGMP_IF_TABLE g_pIfTable;
|
||
|
PGROUP_TABLE g_pGroupTable;
|
||
|
GLOBAL_CONFIG g_Config;
|
||
|
IGMP_GLOBAL_STATS g_Info;
|
||
|
|
||
|
// socket wait-event bindings
|
||
|
|
||
|
LIST_ENTRY g_ListOfSocketEvents;
|
||
|
READ_WRITE_LOCK g_SocketsRWLock;
|
||
|
|
||
|
// igmp global timer struct
|
||
|
|
||
|
IGMP_TIMER_GLOBAL g_TimerStruct;
|
||
|
|
||
|
// protocol handles returned by mgm
|
||
|
|
||
|
HANDLE g_MgmIgmprtrHandle;
|
||
|
HANDLE g_MgmProxyHandle;
|
||
|
|
||
|
// proxy table
|
||
|
|
||
|
DWORD g_ProxyIfIndex;
|
||
|
PIF_TABLE_ENTRY g_pProxyIfEntry;
|
||
|
CRITICAL_SECTION g_ProxyAlertCS;
|
||
|
LIST_ENTRY g_ProxyAlertsList;
|
||
|
|
||
|
|
||
|
// ras table
|
||
|
|
||
|
DWORD g_RasIfIndex;
|
||
|
PIF_TABLE_ENTRY g_pRasIfEntry;
|
||
|
|
||
|
// global lock, and dynamic locks
|
||
|
|
||
|
CRITICAL_SECTION g_CS;
|
||
|
DYNAMIC_LOCKS_STORE g_DynamicCSStore;
|
||
|
DYNAMIC_LOCKS_STORE g_DynamicRWLStore;
|
||
|
|
||
|
// enum lock
|
||
|
READ_WRITE_LOCK g_EnumRWLock;
|
||
|
|
||
|
// others
|
||
|
|
||
|
HANDLE g_ActivitySemaphore;
|
||
|
LONG g_ActivityCount;
|
||
|
DWORD g_RunningStatus;
|
||
|
HINSTANCE g_DllHandle;
|
||
|
|
||
|
HANDLE g_RtmNotifyEvent;
|
||
|
LOCKED_LIST g_RtmQueue;
|
||
|
|
||
|
HANDLE g_Heap;
|
||
|
DWORD g_TraceId=INVALID_TRACEID;
|
||
|
HANDLE g_LogHandle;
|
||
|
|
||
|
// signature for each enumeration
|
||
|
|
||
|
USHORT g_GlobalIfGroupEnumSignature;
|
||
|
|
||
|
#ifdef MIB_DEBUG
|
||
|
DWORD g_MibTraceId;
|
||
|
IGMP_TIMER_ENTRY g_MibTimer;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// #defines for g_Initialized
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
//
|
||
|
// the below flags are used to mark if the data has been initialized.
|
||
|
// If a flag is not set, the corresponding structure wont be deinitialized
|
||
|
//
|
||
|
|
||
|
#define TIMER_GLOBAL_INIT 0x00000002
|
||
|
#define WINSOCK_INIT 0x00000010
|
||
|
#define DYNAMIC_CS_LOCKS_INIT 0x00000020
|
||
|
#define DYNAMIC_RW_LOCKS_INIT 0x00000040
|
||
|
#define GROUP_TABLE_INIT 0x00000080
|
||
|
#define IF_TABLE_INIT 0x00000100
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// flags associated with dll and calls made before start protocol.
|
||
|
// these flags should not be reset by start protocol
|
||
|
//
|
||
|
#define DLL_STARTUP_FLAGS 0xFF000000
|
||
|
|
||
|
|
||
|
//
|
||
|
// Is StartProtocol being called immediately after DLL startup.
|
||
|
// Used to see if heap has to be destroyed and recreated.
|
||
|
// Set in DllStartup() and cleared in ProcolCleanup() as part of StopProtocol()
|
||
|
//
|
||
|
#define CLEAN_DLL_STARTUP 0x01000000
|
||
|
|
||
|
// flag set to prevent register_protocol from being called multiple times.
|
||
|
#define REGISTER_PROTOCOL 0x02000000
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _DLLMAIN
|
||
|
//
|
||
|
// Called immediately after igmpv2.dll is loaded for the first time by the
|
||
|
// process, and when the igmpv2.dll is unloaded by the process.
|
||
|
// It does some initialization/final cleanup.
|
||
|
//
|
||
|
// Calls: _DllStartup() or _DllCleanup()
|
||
|
//------------------------------------------------------------------------------
|
||
|
BOOL
|
||
|
WINAPI
|
||
|
DLLMAIN (
|
||
|
HINSTANCE hModule,
|
||
|
DWORD dwReason,
|
||
|
LPVOID lpvReserved
|
||
|
)
|
||
|
{
|
||
|
|
||
|
BOOL bNoErr;
|
||
|
DWORD Error=NO_ERROR;
|
||
|
|
||
|
|
||
|
switch (dwReason) {
|
||
|
|
||
|
//
|
||
|
// Startup Initialization of Dll
|
||
|
//
|
||
|
case DLL_PROCESS_ATTACH:
|
||
|
{
|
||
|
// disable per-thread initialization
|
||
|
DisableThreadLibraryCalls(hModule);
|
||
|
|
||
|
|
||
|
// create and initialize global data
|
||
|
bNoErr = DllStartup();
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Cleanup of Dll
|
||
|
//
|
||
|
case DLL_PROCESS_DETACH:
|
||
|
{
|
||
|
// free global data
|
||
|
bNoErr = DllCleanup();
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
default:
|
||
|
bNoErr = TRUE;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
return bNoErr;
|
||
|
|
||
|
} //end _DLLMAIN
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _DllStartup
|
||
|
//
|
||
|
// Sets the initial igmp status to IGMP_STATUS_STOPPED, creates a private heap,
|
||
|
// Does the initialization of the rtm queue and tracing/logging,
|
||
|
// and creates the global critical section.
|
||
|
//
|
||
|
// Note: no structures must be allocated from heap here, as StartProtocol()
|
||
|
// if called after StopProtocol() destroys the heap.
|
||
|
// Return Values: TRUE (if no error), else FALSE.
|
||
|
//------------------------------------------------------------------------------
|
||
|
BOOL
|
||
|
DllStartup(
|
||
|
)
|
||
|
{
|
||
|
BOOL bNoErr;
|
||
|
DWORD Error=NO_ERROR;
|
||
|
|
||
|
|
||
|
//not required to ZeroMemory igmp global struct as it is a global variable
|
||
|
|
||
|
|
||
|
//
|
||
|
// set the initial igmp status to stopped.
|
||
|
// The status is set to running, after the protocol specific initialization
|
||
|
// is completed as part of Start Protocol
|
||
|
//
|
||
|
g_RunningStatus = IGMP_STATUS_STOPPED;
|
||
|
|
||
|
|
||
|
bNoErr = FALSE;
|
||
|
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
|
||
|
// set the default logging level. It will be reset during
|
||
|
// StartProtocol(), when logging level is set as part of global config
|
||
|
|
||
|
g_Config.LoggingLevel = IGMP_LOGGING_WARN;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// create a private heap for Igmp
|
||
|
//
|
||
|
g_Heap = HeapCreate(0, 0, 0);
|
||
|
|
||
|
if (g_Heap == NULL) {
|
||
|
Error = GetLastError();
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
try {
|
||
|
// initialize the Router Manager event queue
|
||
|
|
||
|
CREATE_LOCKED_LIST(&g_RtmQueue);
|
||
|
|
||
|
|
||
|
// create global critical section
|
||
|
|
||
|
InitializeCriticalSection(&g_CS);
|
||
|
}
|
||
|
|
||
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
|
||
|
Error = GetExceptionCode();
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// igmp has a clean initialization from DLL startup. If StartProtocol
|
||
|
// is now called, it does not have to cleanup the heap.
|
||
|
|
||
|
g_Initialized |= CLEAN_DLL_STARTUP;
|
||
|
|
||
|
|
||
|
bNoErr = TRUE;
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
return bNoErr;
|
||
|
|
||
|
} //end _DllStartup
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _DllCleanup
|
||
|
//
|
||
|
// Called when the igmpv2 dll is being unloaded. StopProtocol() would have
|
||
|
// been called before, and that would have cleaned all the igmpv2 structures.
|
||
|
// This call frees the rtm queue, the global CS, destroys the local heap,
|
||
|
// and deregisters tracing/logging.
|
||
|
//
|
||
|
// Return Value: TRUE
|
||
|
//------------------------------------------------------------------------------
|
||
|
BOOL
|
||
|
DllCleanup(
|
||
|
)
|
||
|
{
|
||
|
|
||
|
// destroy the router manager event queue
|
||
|
|
||
|
if (LOCKED_LIST_CREATED(&g_RtmQueue)) {
|
||
|
|
||
|
DELETE_LOCKED_LIST(&g_RtmQueue, EVENT_QUEUE_ENTRY, Link);
|
||
|
}
|
||
|
|
||
|
|
||
|
//DebugCheck
|
||
|
//DebugScanMemory();
|
||
|
|
||
|
// delete global critical section
|
||
|
|
||
|
DeleteCriticalSection(&g_CS);
|
||
|
|
||
|
|
||
|
|
||
|
// destroy private heap
|
||
|
|
||
|
if (g_Heap != NULL) {
|
||
|
HeapDestroy(g_Heap);
|
||
|
}
|
||
|
|
||
|
|
||
|
// deregister tracing/error logging
|
||
|
|
||
|
if (g_LogHandle)
|
||
|
RouterLogDeregister(g_LogHandle);
|
||
|
if (g_TraceId!=INVALID_TRACEID)
|
||
|
TraceDeregister(g_TraceId);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
InitTracingAndLogging(
|
||
|
)
|
||
|
{
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
#define REGKEY_TRACING TEXT("Software\\Microsoft\\Tracing\\IGMPv2")
|
||
|
#define REGVAL_CONSOLETRACINGMASK TEXT("ConsoleTracingMask")
|
||
|
TCHAR szTracing[MAX_PATH];
|
||
|
HKEY pTracingKey;
|
||
|
DWORD Value, Error;
|
||
|
lstrcpy(szTracing, REGKEY_TRACING);
|
||
|
|
||
|
|
||
|
Error = RegOpenKeyEx(
|
||
|
HKEY_LOCAL_MACHINE, szTracing, 0, KEY_SET_VALUE, &pTracingKey
|
||
|
);
|
||
|
if (Error != ERROR_SUCCESS)
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
Value = 0x00ff0000;
|
||
|
RegSetValueEx(
|
||
|
pTracingKey, REGVAL_CONSOLETRACINGMASK, 0,
|
||
|
REG_DWORD, (LPBYTE)&Value, sizeof(DWORD)
|
||
|
);
|
||
|
|
||
|
RegCloseKey(pTracingKey);
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
|
||
|
// initialize tracing and error logging
|
||
|
|
||
|
if (g_TraceId==INVALID_TRACEID) {
|
||
|
g_TraceId = TraceRegister("IGMPv2");
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!g_LogHandle) {
|
||
|
g_LogHandle = RouterLogRegister("IGMPv2");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _RegisterProtocol
|
||
|
//
|
||
|
// This is the first function called by the IP Router Manager.
|
||
|
// The Router Manager tells the routing protocol its version and capabilities
|
||
|
// It also tells the DLL, the ID of the protocol it expects us to
|
||
|
// register. This allows one DLL to support multiple routing protocols.
|
||
|
// We return the functionality we support and a pointer to our functions.
|
||
|
// Return Value:
|
||
|
// Error: The error code returned by MGM if registering with it failed
|
||
|
// else NO_ERROR
|
||
|
//------------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
WINAPI
|
||
|
RegisterProtocol(
|
||
|
IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar,
|
||
|
IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar
|
||
|
)
|
||
|
{
|
||
|
DWORD Error = NO_ERROR;
|
||
|
|
||
|
// Note: There should not be any trace/logs before here
|
||
|
|
||
|
InitTracingAndLogging() ;
|
||
|
|
||
|
Trace0(ENTER, "RegisterProtocol()");
|
||
|
|
||
|
|
||
|
|
||
|
// cannot call RegisterProtocol multiple times
|
||
|
|
||
|
if (g_Initialized®ISTER_PROTOCOL) {
|
||
|
Trace0(ERR, "Error: _RegisterProtocol() called multiple times for igmp");
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
return ERROR_CAN_NOT_COMPLETE;
|
||
|
}
|
||
|
else {
|
||
|
g_Initialized |= REGISTER_PROTOCOL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// The Router Manager should be calling us to register our protocol.
|
||
|
// The Router Manager must be atleast the version we are compiled with
|
||
|
// The Router Manager must support routing and multicast.
|
||
|
//
|
||
|
|
||
|
if(pRoutingChar->dwProtocolId != MS_IP_IGMP)
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
|
||
|
if(pRoutingChar->dwVersion < MS_ROUTER_VERSION)
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
|
||
|
if(!(pRoutingChar->fSupportedFunctionality & RF_ROUTING)
|
||
|
|| !(pRoutingChar->fSupportedFunctionality & RF_MULTICAST) )
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// We setup our characteristics and function pointers
|
||
|
// All pointers should be set to NULL by the caller.
|
||
|
//
|
||
|
|
||
|
pServiceChar->fSupportedFunctionality = 0;
|
||
|
|
||
|
pRoutingChar->fSupportedFunctionality = RF_MULTICAST | RF_ROUTING;
|
||
|
|
||
|
pRoutingChar->pfnStartProtocol = StartProtocol;
|
||
|
pRoutingChar->pfnStartComplete = StartComplete;
|
||
|
pRoutingChar->pfnStopProtocol = StopProtocol;
|
||
|
pRoutingChar->pfnAddInterface = AddInterface;
|
||
|
pRoutingChar->pfnDeleteInterface = DeleteInterface;
|
||
|
pRoutingChar->pfnInterfaceStatus = InterfaceStatus;
|
||
|
pRoutingChar->pfnGetEventMessage = GetEventMessage;
|
||
|
pRoutingChar->pfnGetInterfaceInfo = GetInterfaceConfigInfo;
|
||
|
pRoutingChar->pfnSetInterfaceInfo = SetInterfaceConfigInfo;
|
||
|
pRoutingChar->pfnGetGlobalInfo = GetGlobalInfo;
|
||
|
pRoutingChar->pfnSetGlobalInfo = SetGlobalInfo;
|
||
|
pRoutingChar->pfnMibCreateEntry = MibCreate;
|
||
|
pRoutingChar->pfnMibDeleteEntry = MibDelete;
|
||
|
pRoutingChar->pfnMibGetEntry = MibGet;
|
||
|
pRoutingChar->pfnMibSetEntry = MibSet;
|
||
|
pRoutingChar->pfnMibGetFirstEntry = MibGetFirst;
|
||
|
pRoutingChar->pfnMibGetNextEntry = MibGetNext;
|
||
|
pRoutingChar->pfnUpdateRoutes = NULL;
|
||
|
pRoutingChar->pfnConnectClient = ConnectRasClient;
|
||
|
pRoutingChar->pfnDisconnectClient = DisconnectRasClient;
|
||
|
pRoutingChar->pfnGetNeighbors = GetNeighbors;
|
||
|
pRoutingChar->pfnGetMfeStatus = GetMfeStatus;
|
||
|
pRoutingChar->pfnQueryPower = NULL;
|
||
|
pRoutingChar->pfnSetPower = NULL;
|
||
|
|
||
|
Trace0(LEAVE, "Leaving RegisterProtocol():\n");
|
||
|
return NO_ERROR;
|
||
|
|
||
|
} //end _RegisterProtocol
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _StartProtocol
|
||
|
//
|
||
|
// Called after the _RegisterProtocol() API.
|
||
|
// Initializes the data structures used by the protocol. However, the
|
||
|
// protocol actually starts to run when it gets interface ownerships.
|
||
|
// Locks:
|
||
|
// runs completely in g_CS.
|
||
|
// Return Value:
|
||
|
// Error: if there is an error else NO_ERROR
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
WINAPI
|
||
|
StartProtocol(
|
||
|
IN HANDLE hRtmNotifyEvent, //notify Rtm when protocol stopped
|
||
|
IN PSUPPORT_FUNCTIONS pSupportFunctions, //NULL
|
||
|
IN PVOID pGlobalConfig,
|
||
|
IN ULONG ulStructureVersion,
|
||
|
IN ULONG ulStructureSize,
|
||
|
IN ULONG ulStructureCount
|
||
|
)
|
||
|
{
|
||
|
WSADATA WsaData;
|
||
|
DWORD Error = NO_ERROR;
|
||
|
BOOL bErr;
|
||
|
|
||
|
|
||
|
Trace0(ENTER, "Entering StartProtocol()");
|
||
|
|
||
|
// make sure it is not an unsupported igmp version structure
|
||
|
if (ulStructureVersion>=IGMP_CONFIG_VERSION_600) {
|
||
|
Trace1(ERR, "Unsupported IGMP version structure: %0x",
|
||
|
ulStructureVersion);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
return ERROR_CAN_NOT_COMPLETE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// lock retained for entire initialization. so api_entry not required
|
||
|
|
||
|
ACQUIRE_GLOBAL_LOCK("_StartProtocol");
|
||
|
|
||
|
|
||
|
//
|
||
|
// make certain igmp is not already running
|
||
|
//
|
||
|
if (g_RunningStatus != IGMP_STATUS_STOPPED) {
|
||
|
|
||
|
Trace0(START,
|
||
|
"Error: _StartProtocol called when Igmp is already running");
|
||
|
Logwarn0(IGMP_ALREADY_STARTED, NO_ERROR);
|
||
|
|
||
|
RELEASE_GLOBAL_LOCK("_StartProtocol");
|
||
|
|
||
|
return ERROR_CAN_NOT_COMPLETE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bErr = TRUE;
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
// clear initialization flags set during and after _startProtocol
|
||
|
|
||
|
g_Initialized &= DLL_STARTUP_FLAGS;
|
||
|
|
||
|
|
||
|
// g_RunningStatus, g_CS, g_TraceId, g_LogHandle, g_RtmQueue,
|
||
|
// g_Initialized & 0xFF000000 initialized in DllStartup/RegisterProtocol
|
||
|
|
||
|
|
||
|
//
|
||
|
// If start protocol has been called after a stop protocol.
|
||
|
//
|
||
|
|
||
|
if (!(g_Initialized & CLEAN_DLL_STARTUP)) {
|
||
|
|
||
|
|
||
|
// destroy private heap, so that there is no memory leak.
|
||
|
|
||
|
if (g_Heap != NULL) {
|
||
|
HeapDestroy(g_Heap);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Reset the igmp global structure.
|
||
|
// bugchk:make sure that all appropriate fields are being reset.
|
||
|
//
|
||
|
|
||
|
g_pIfTable = NULL;
|
||
|
g_pGroupTable = NULL;
|
||
|
ZeroMemory(&g_Config, sizeof(GLOBAL_CONFIG));
|
||
|
g_Config.LoggingLevel = IGMP_LOGGING_WARN;
|
||
|
ZeroMemory(&g_Info, sizeof(IGMP_GLOBAL_STATS));
|
||
|
InitializeListHead(&g_ListOfSocketEvents);
|
||
|
ZeroMemory(&g_SocketsRWLock, sizeof(READ_WRITE_LOCK));
|
||
|
ZeroMemory(&g_EnumRWLock, sizeof(READ_WRITE_LOCK));
|
||
|
ZeroMemory(&g_TimerStruct, sizeof(IGMP_TIMER_GLOBAL));
|
||
|
g_MgmIgmprtrHandle = g_MgmProxyHandle = NULL;
|
||
|
g_ProxyIfIndex = 0;
|
||
|
g_pProxyIfEntry = NULL;
|
||
|
ZeroMemory(&g_ProxyAlertCS, sizeof(CRITICAL_SECTION));
|
||
|
InitializeListHead(&g_ProxyAlertsList);
|
||
|
g_RasIfIndex = 0;
|
||
|
g_pRasIfEntry = NULL;
|
||
|
ZeroMemory(&g_DynamicCSStore, sizeof(DYNAMIC_LOCKS_STORE));
|
||
|
ZeroMemory(&g_DynamicRWLStore, sizeof(DYNAMIC_LOCKS_STORE));
|
||
|
g_ActivitySemaphore = NULL;
|
||
|
g_ActivityCount = 0;
|
||
|
g_RtmNotifyEvent = NULL;
|
||
|
g_Heap = NULL;
|
||
|
|
||
|
#ifdef MIB_DEBUG
|
||
|
g_MibTraceId = 0;
|
||
|
ZeroMemory(&g_MibTimer, sizeof(IGMP_TIMER_ENTRY));
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// create private heap again.
|
||
|
|
||
|
g_Heap = HeapCreate(0, 0, 0);
|
||
|
|
||
|
if (g_Heap == NULL) {
|
||
|
Error = GetLastError();
|
||
|
Trace1(ANY, "error %d creating Igmp private heap", Error);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// save the Router Manager notification event
|
||
|
|
||
|
g_RtmNotifyEvent = hRtmNotifyEvent;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// set the Global Config (after making validation changes)
|
||
|
//
|
||
|
|
||
|
if(pGlobalConfig == NULL) {
|
||
|
|
||
|
Trace0(ERR, "_StartProtocol: Called with NULL global config");
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
Error = ERROR_INVALID_PARAMETER;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
{
|
||
|
PIGMP_MIB_GLOBAL_CONFIG pGlobalConfigTmp;
|
||
|
|
||
|
pGlobalConfigTmp = (PIGMP_MIB_GLOBAL_CONFIG) pGlobalConfig;
|
||
|
|
||
|
|
||
|
// Check the global config, and correct if values are not correct.
|
||
|
// Not a fatal error.
|
||
|
|
||
|
if (! ValidateGlobalConfig(pGlobalConfigTmp)) {
|
||
|
Error = ERROR_INVALID_PARAMETER;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
g_Config.Version = pGlobalConfigTmp->Version;
|
||
|
g_Config.LoggingLevel = pGlobalConfigTmp->LoggingLevel;
|
||
|
g_Config.RasClientStats = 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// The Global Stats are set to all 0 as it is a global variable.
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize Winsock version 2.0
|
||
|
//
|
||
|
Error = (DWORD)WSAStartup(MAKEWORD(2,0), &WsaData);
|
||
|
|
||
|
if ( (Error!=0) || (LOBYTE(WsaData.wVersion)<2) ) {
|
||
|
|
||
|
Trace1(ERR,
|
||
|
"StartProtocol: Error %d :could not initialize winsock v-2.0",
|
||
|
Error);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
Logerr0(WSASTARTUP_FAILED, Error);
|
||
|
|
||
|
if (LOBYTE(WsaData.wVersion)<2)
|
||
|
WSACleanup();
|
||
|
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
g_Initialized |= WINSOCK_INIT;
|
||
|
|
||
|
|
||
|
//
|
||
|
// initialize list of SocketEvents
|
||
|
//
|
||
|
InitializeListHead(&g_ListOfSocketEvents);
|
||
|
|
||
|
Error = CreateReadWriteLock(&g_SocketsRWLock);
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
Error = CreateReadWriteLock(&g_EnumRWLock);
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// initialize the timer queues and other timer structures
|
||
|
//
|
||
|
Error = InitializeTimerGlobal();
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
g_Initialized |= TIMER_GLOBAL_INIT;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Create Interface Table
|
||
|
|
||
|
InitializeIfTable();
|
||
|
|
||
|
g_Initialized |= IF_TABLE_INIT;
|
||
|
|
||
|
|
||
|
|
||
|
// Create Group Table
|
||
|
|
||
|
InitializeGroupTable();
|
||
|
|
||
|
g_Initialized |= GROUP_TABLE_INIT;
|
||
|
|
||
|
|
||
|
|
||
|
// proxy, ras interface already set to 0/NULL in global structure.
|
||
|
InitializeListHead(&g_ProxyAlertsList);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialise the Dynamic CS and ReadWrite locks main struct
|
||
|
//
|
||
|
|
||
|
Error = InitializeDynamicLocksStore(&g_DynamicCSStore);
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
g_Initialized |= DYNAMIC_CS_LOCKS_INIT;
|
||
|
|
||
|
Error = InitializeDynamicLocksStore(&g_DynamicRWLStore);
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
g_Initialized |= DYNAMIC_RW_LOCKS_INIT;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// create the semaphore released by each thread when it is done
|
||
|
// g_ActivityCount already set to 0.
|
||
|
//
|
||
|
|
||
|
g_ActivityCount = 0;
|
||
|
|
||
|
g_ActivitySemaphore = CreateSemaphore(NULL, 0, 0xfffffff, NULL);
|
||
|
|
||
|
if (g_ActivitySemaphore == NULL) {
|
||
|
Error = GetLastError();
|
||
|
Trace1(ERR, "error %d creating semaphore for Igmp threads", Error);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
Logerr0(CREATE_SEMAPHORE_FAILED, Error);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// set the starting time for igmp. Should be done after global
|
||
|
// timer and global Info struct are initialized
|
||
|
//
|
||
|
g_Info.TimeWhenRtrStarted.QuadPart = GetCurrentIgmpTime();
|
||
|
|
||
|
|
||
|
|
||
|
// set igmp status to running
|
||
|
|
||
|
g_RunningStatus = IGMP_STATUS_RUNNING;
|
||
|
|
||
|
|
||
|
#ifdef MIB_DEBUG
|
||
|
//
|
||
|
// set delayed timer to display igmp's MIB tables periodically
|
||
|
//
|
||
|
|
||
|
g_MibTraceId = TraceRegisterEx("IGMPMIB", TRACE_USE_CONSOLE);
|
||
|
|
||
|
if (g_MibTraceId != INVALID_TRACEID) {
|
||
|
|
||
|
g_MibTimer.Context = NULL;
|
||
|
g_MibTimer.Status = TIMER_STATUS_CREATED;
|
||
|
g_MibTimer.Function = WT_MibDisplay;
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&g_MibTimer, 910, 0, 0, 0);
|
||
|
#endif
|
||
|
|
||
|
ACQUIRE_TIMER_LOCK("_StartProtocol");
|
||
|
InsertTimer(&g_MibTimer, 5000, TRUE, DBG_Y);
|
||
|
RELEASE_TIMER_LOCK("_StartProtocol");
|
||
|
}
|
||
|
#endif //MIB_DEBUG
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// register Igmp router with MGM. Proxy will be registered if there
|
||
|
// is an active proxy interface.
|
||
|
//
|
||
|
Error = RegisterProtocolWithMgm(PROTO_IP_IGMP);
|
||
|
|
||
|
|
||
|
// no error if I have reached here
|
||
|
|
||
|
bErr = FALSE;
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
if (bErr) {
|
||
|
Trace1(START, "Igmp could not be started: %d", Error);
|
||
|
ProtocolCleanup();
|
||
|
}
|
||
|
else {
|
||
|
Trace0(START, "Igmp started successfully");
|
||
|
Loginfo0(IGMP_STARTED, NO_ERROR);
|
||
|
}
|
||
|
|
||
|
g_DllHandle = LoadLibrary(TEXT("igmpv2.dll"));
|
||
|
|
||
|
|
||
|
RELEASE_GLOBAL_LOCK("_StartProtocol()");
|
||
|
|
||
|
|
||
|
Trace1(LEAVE, "Leaving StartProtocol():%d\n", Error);
|
||
|
return (Error);
|
||
|
|
||
|
} //end StartProtocol
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _StartComplete
|
||
|
//
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
APIENTRY
|
||
|
StartComplete(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _StopProtocol
|
||
|
//
|
||
|
// sets the igmp status to stopping, marks the current number of active igmp
|
||
|
// work items, and queues a worker that will wait till all those work items
|
||
|
// have completed and then clean up igmp structures. This function returns
|
||
|
// a pending status to the caller, while the queued work item will notify
|
||
|
// the rtm after the cleanup has been done.
|
||
|
// Locking:
|
||
|
// Runs completely in g_CS.
|
||
|
// Return Values:
|
||
|
// ERROR_CAN_NOT_COMPLETE, PENDING.
|
||
|
// Queues:
|
||
|
// _WF_FinishStopProtocol()
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
APIENTRY
|
||
|
StopProtocol(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
DWORD dwThreadCount, Error=NO_ERROR;
|
||
|
|
||
|
|
||
|
Trace0(ENTER, "entering _StopProtocol()");
|
||
|
|
||
|
//debugCheck
|
||
|
|
||
|
#if DEBUG_FLAGS_MEM_ALLOC
|
||
|
// make sure that no interface timers exist
|
||
|
#ifdef MIB_DEBUG
|
||
|
if (g_TimerStruct.NumTimers>1)
|
||
|
#else
|
||
|
if (g_TimerStruct.NumTimers>0)
|
||
|
#endif
|
||
|
{
|
||
|
IgmpAssert(FALSE);
|
||
|
DbgPrint("Cleanup: some igmp timers still exist\n");
|
||
|
DebugPrintTimerQueue();
|
||
|
}
|
||
|
// make sure that no groups exist
|
||
|
DebugForcePrintGroupsList(ENSURE_EMPTY);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
ACQUIRE_GLOBAL_LOCK("_StopProtocol");
|
||
|
|
||
|
|
||
|
//
|
||
|
// cannot stop if already stopped
|
||
|
//
|
||
|
if (g_RunningStatus != IGMP_STATUS_RUNNING) {
|
||
|
|
||
|
Trace0(ERR, "Trying to stop Igmp when it is already being stopped");
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
Logerr0(PROTO_ALREADY_STOPPING, NO_ERROR);
|
||
|
Trace0(LEAVE, "Leaving _StopProtocol()\n");
|
||
|
|
||
|
RELEASE_GLOBAL_LOCK("_StopProtocol");
|
||
|
return ERROR_CAN_NOT_COMPLETE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// set Igmp's status to STOPPING;
|
||
|
// this prevents any more work-items from being queued,
|
||
|
// and it prevents the one's already queued from executing
|
||
|
//
|
||
|
|
||
|
InterlockedExchange(&g_RunningStatus, IGMP_STATUS_STOPPING);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// find out how many threads are active in Igmp;
|
||
|
// we will have to wait for this many threads to exit
|
||
|
// before we clean up Igmp's resources
|
||
|
//
|
||
|
|
||
|
dwThreadCount = g_ActivityCount;
|
||
|
|
||
|
|
||
|
RELEASE_GLOBAL_LOCK("_StopProtocol");
|
||
|
|
||
|
Trace0(LEAVE, "leaving _StopProtocol");
|
||
|
|
||
|
|
||
|
//
|
||
|
// QueueUserWorkItem that waits for all active Igmp threads and then
|
||
|
// releases resources taken by this DLL.
|
||
|
// Note: I dont use QueueIgmpWorker as that would increment the
|
||
|
// ActivityCount.
|
||
|
//
|
||
|
|
||
|
QueueUserWorkItem(WF_FinishStopProtocol, (PVOID)(DWORD_PTR)dwThreadCount, 0);
|
||
|
|
||
|
|
||
|
|
||
|
// Note: to be safe, there should be no code after QueueUserWorkItem
|
||
|
|
||
|
|
||
|
return ERROR_PROTOCOL_STOP_PENDING;
|
||
|
|
||
|
} //end StopProtocol
|
||
|
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
FreeLibraryThread(
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
FreeLibraryAndExitThread(g_DllHandle, 0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// WF_FinishStopProtocol
|
||
|
//------------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
WF_FinishStopProtocol(
|
||
|
PVOID pContext //dwThreadCount
|
||
|
)
|
||
|
/*++
|
||
|
Routing Description:
|
||
|
Waits for all the current active igmp work items to complete. Follows that
|
||
|
by a call to ProtocolCleanup() to deregister and cleanup all igmp structures.
|
||
|
In the end, notifies RtrManager that the protocol has stopped.
|
||
|
Queued by: _StopProtocol()
|
||
|
Calls: _ProtocolCleanup()
|
||
|
Locks: no locks required as all igmp threads have stopped.
|
||
|
--*/
|
||
|
{
|
||
|
MESSAGE msg = {0, 0, 0};
|
||
|
DWORD dwThreadCount;
|
||
|
DWORD Error = NO_ERROR;
|
||
|
|
||
|
Trace0(ENTER1, "entering _WF_FinishStopProtocol()");
|
||
|
|
||
|
|
||
|
//
|
||
|
// NOTE: since this is called while the router is stopping,
|
||
|
// do not use EnterIgmpWorker()/LeaveIgmpWorker()
|
||
|
//
|
||
|
|
||
|
dwThreadCount = PtrToUlong(pContext);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// waits for API callers and worker functions to finish
|
||
|
//
|
||
|
while (dwThreadCount-- > 0) {
|
||
|
|
||
|
Trace1(STOP, "%d threads active in Igmp", dwThreadCount+1);
|
||
|
|
||
|
WaitForSingleObject(g_ActivitySemaphore, INFINITE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// enter the critical section and leave, just to be sure that
|
||
|
// all threads have quit their calls to LeaveIgmpWorker()
|
||
|
|
||
|
ACQUIRE_GLOBAL_LOCK("_WF_FinishStopProtocol");
|
||
|
RELEASE_GLOBAL_LOCK("_WF_FinishStopProtocol");
|
||
|
|
||
|
|
||
|
|
||
|
Trace0(STOP, "all threads stopped. Cleaning up resources");
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// This call deregisters with Wait-Server/MGM, and cleans up
|
||
|
// all structures
|
||
|
//
|
||
|
ProtocolCleanup();
|
||
|
|
||
|
|
||
|
|
||
|
Loginfo0(IGMP_STOPPED, NO_ERROR);
|
||
|
|
||
|
|
||
|
//
|
||
|
// notify Router Manager that protocol has been stopped
|
||
|
//
|
||
|
|
||
|
ACQUIRE_LIST_LOCK(&g_RtmQueue, "g_RtmQueue", "_WF_FinishStopProtocol");
|
||
|
EnqueueEvent(&g_RtmQueue, ROUTER_STOPPED, msg);
|
||
|
SetEvent(g_RtmNotifyEvent);
|
||
|
RELEASE_LIST_LOCK(&g_RtmQueue, "g_RtmQueue", "_WF_FinishStopProtocol");
|
||
|
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _WF_FinishStopProtocol()");
|
||
|
|
||
|
if (g_DllHandle) {
|
||
|
HANDLE h_Thread;
|
||
|
h_Thread = CreateThread(0,0,FreeLibraryThread, NULL, 0, NULL);
|
||
|
if (h_Thread != NULL)
|
||
|
CloseHandle(h_Thread);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
} //end _WF_FinishStopProtocol
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _ProtocolCleanup
|
||
|
//
|
||
|
// All active igmp work items have completed before this procedure is called.
|
||
|
// So it is safe to deregister with Wait-Server, and deregister all interfaces/
|
||
|
// RAS clients and Igmp router/proxy protocol with MGM. Then, all the structures
|
||
|
// are cleaned up.
|
||
|
//
|
||
|
// Called by:
|
||
|
// _WF_FinishStopProtocol() and _StartProtocol()
|
||
|
// Locks:
|
||
|
// No locks required as no api can enter when g_RunningStatus set to stopping.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
ProtocolCleanup(
|
||
|
)
|
||
|
{
|
||
|
DWORD Error = NO_ERROR;
|
||
|
|
||
|
Trace0(ENTER1, "entering _ProtocolCleanup()");
|
||
|
|
||
|
|
||
|
//
|
||
|
// Deregister Igmp Router from MGM. Proxy is deregistered if required in
|
||
|
// _DeinitializeIfTable
|
||
|
//
|
||
|
if (g_MgmIgmprtrHandle!=NULL) {
|
||
|
|
||
|
Error = MgmDeRegisterMProtocol(g_MgmIgmprtrHandle);
|
||
|
Trace1(MGM, "_MgmDeRegisterMProtocol(Igmp): returned %d", Error);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// deregister Mib display
|
||
|
|
||
|
#ifdef MIB_DEBUG
|
||
|
if (g_MibTraceId != INVALID_TRACEID)
|
||
|
TraceDeregister(g_MibTraceId);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
// close activity semaphore
|
||
|
|
||
|
if (g_ActivitySemaphore != NULL) {
|
||
|
|
||
|
CloseHandle(g_ActivitySemaphore);
|
||
|
g_ActivitySemaphore = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// DeInitialise the Dynamic CS and ReadWrite locks main struct
|
||
|
|
||
|
if (g_Initialized&DYNAMIC_RW_LOCKS_INIT)
|
||
|
DeInitializeDynamicLocksStore(&g_DynamicRWLStore, LOCK_TYPE_RW);
|
||
|
|
||
|
if (g_Initialized&DYNAMIC_CS_LOCKS_INIT)
|
||
|
DeInitializeDynamicLocksStore(&g_DynamicCSStore, LOCK_TYPE_CS);
|
||
|
|
||
|
|
||
|
|
||
|
// DeInitialize the group table. Delete the group bucket locks.
|
||
|
|
||
|
if (g_Initialized & GROUP_TABLE_INIT)
|
||
|
DeInitializeGroupTable();
|
||
|
|
||
|
|
||
|
|
||
|
// DeInitialize InterfaceTable. This call also deregister all interfaces &
|
||
|
// ras clients from MGM
|
||
|
|
||
|
if (g_Initialized & IF_TABLE_INIT)
|
||
|
DeInitializeIfTable();
|
||
|
|
||
|
|
||
|
|
||
|
// DeInitialize the global timer. Deletes the timer critical-section.
|
||
|
|
||
|
if (g_Initialized&TIMER_GLOBAL_INIT)
|
||
|
DeInitializeTimerGlobal();
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// delete sockets events and deregister them from wait thread.
|
||
|
// delete sockets read-write lock
|
||
|
//
|
||
|
{
|
||
|
PLIST_ENTRY pHead, ple;
|
||
|
PSOCKET_EVENT_ENTRY psee;
|
||
|
HANDLE WaitHandle;
|
||
|
|
||
|
pHead = &g_ListOfSocketEvents;
|
||
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
||
|
|
||
|
psee = CONTAINING_RECORD(ple,SOCKET_EVENT_ENTRY,LinkBySocketEvents);
|
||
|
if (psee->InputWaitEvent) {
|
||
|
WaitHandle = InterlockedExchangePointer(&psee->InputWaitEvent, NULL);
|
||
|
|
||
|
if (WaitHandle)
|
||
|
UnregisterWaitEx( WaitHandle, NULL ) ;
|
||
|
|
||
|
}
|
||
|
CloseHandle(psee->InputEvent);
|
||
|
}
|
||
|
|
||
|
DeleteReadWriteLock(&g_SocketsRWLock);
|
||
|
DeleteCriticalSection(&g_ProxyAlertCS);
|
||
|
DeleteReadWriteLock(&g_EnumRWLock);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// deinitialize winsock
|
||
|
|
||
|
if (g_Initialized & WINSOCK_INIT) {
|
||
|
WSACleanup();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Mark that _StopProtocol has been called once.
|
||
|
// If _StartProtocol is called again, igmp will have to Destroy/Create
|
||
|
// private heap and ZeroMemory parts of igmp global struct.
|
||
|
|
||
|
g_Initialized &= ~CLEAN_DLL_STARTUP;
|
||
|
|
||
|
Trace0(LEAVE1, "leaving _ProtocolCleanup()");
|
||
|
return;
|
||
|
|
||
|
} //end _ProtocolCleanup
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// DebugPrintGlobalConfig
|
||
|
//------------------------------------------------------------------------------
|
||
|
VOID
|
||
|
DebugPrintGlobalConfig (
|
||
|
PIGMP_MIB_GLOBAL_CONFIG pConfigExt
|
||
|
)
|
||
|
{
|
||
|
Trace0(CONFIG, "Printing Global Config");
|
||
|
Trace1(CONFIG, "Version: 0x%x", pConfigExt->Version);
|
||
|
Trace1(CONFIG, "LoggingLevel: %x\n", pConfigExt->LoggingLevel);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// GetGlobalInfo
|
||
|
//
|
||
|
// Return Values: ERROR_CAN_NOT_COMPLETE, ERROR_INVALID_DATA, NO_ERROR
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
WINAPI
|
||
|
GetGlobalInfo(
|
||
|
IN OUT PVOID pvConfig,
|
||
|
IN OUT PDWORD pdwSize,
|
||
|
IN OUT PULONG pulStructureVersion,
|
||
|
IN OUT PULONG pulStructureSize,
|
||
|
IN OUT PULONG pulStructureCount
|
||
|
)
|
||
|
{
|
||
|
|
||
|
DWORD Error=NO_ERROR, dwSize;
|
||
|
PIGMP_MIB_GLOBAL_CONFIG pGlobalConfig;
|
||
|
|
||
|
|
||
|
Trace2(ENTER1, "entering GetGlobalInfo(): pvConfig(%08x) pdwSize(%08x)",
|
||
|
pvConfig, pdwSize);
|
||
|
if (!EnterIgmpApi()) { return ERROR_CAN_NOT_COMPLETE; }
|
||
|
|
||
|
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
//
|
||
|
// check the buffer size and set to global config
|
||
|
//
|
||
|
|
||
|
if (pdwSize == NULL) {
|
||
|
Error = ERROR_INVALID_PARAMETER;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
if ( (*pdwSize < sizeof(IGMP_MIB_GLOBAL_CONFIG)) || (pvConfig==NULL) ) {
|
||
|
Error = ERROR_INSUFFICIENT_BUFFER;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
pGlobalConfig = (PIGMP_MIB_GLOBAL_CONFIG) pvConfig;
|
||
|
|
||
|
pGlobalConfig->Version = g_Config.Version;
|
||
|
|
||
|
pGlobalConfig->LoggingLevel = g_Config.LoggingLevel;
|
||
|
|
||
|
pGlobalConfig->RasClientStats = g_Config.RasClientStats;
|
||
|
}
|
||
|
|
||
|
*pdwSize = sizeof(IGMP_MIB_GLOBAL_CONFIG);
|
||
|
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
if (pulStructureCount)
|
||
|
*pulStructureCount = 1;
|
||
|
if (pulStructureSize)
|
||
|
*pulStructureSize = *pdwSize;
|
||
|
if (pulStructureVersion)
|
||
|
*pulStructureVersion = IGMP_CONFIG_VERSION_500;
|
||
|
|
||
|
Trace1(LEAVE1, "Leaving GetGlobalInfo(): %d\n", Error);
|
||
|
LeaveIgmpApi();
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// SetGlobalInfo
|
||
|
// Return Values: ERROR_CAN_NOT_COMPLETE, ERROR_INVALID_PARAMETER,
|
||
|
// ERROR_INVALID_DATA, NO_ERROR
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
WINAPI
|
||
|
SetGlobalInfo(
|
||
|
IN PVOID pvConfig,
|
||
|
IN ULONG ulStructureVersion,
|
||
|
IN ULONG ulStructureSize,
|
||
|
IN ULONG ulStructureCount
|
||
|
)
|
||
|
{
|
||
|
DWORD Error=NO_ERROR, dwSize;
|
||
|
PIGMP_MIB_GLOBAL_CONFIG pConfigSrc;
|
||
|
BOOL bValid;
|
||
|
|
||
|
|
||
|
if (!EnterIgmpApi()) { return ERROR_CAN_NOT_COMPLETE; }
|
||
|
|
||
|
|
||
|
// make sure it is not an unsupported igmp version structure
|
||
|
if (ulStructureVersion>=IGMP_CONFIG_VERSION_600) {
|
||
|
Trace1(ERR, "Unsupported IGMP version structure: %0x",
|
||
|
ulStructureVersion);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
LeaveIgmpApi();
|
||
|
return ERROR_CAN_NOT_COMPLETE;
|
||
|
}
|
||
|
|
||
|
Trace1(ENTER, "entering SetGlobalInfo: pvConfig(%08x)", pvConfig);
|
||
|
|
||
|
ASSERT(ulStructureSize == sizeof(IGMP_MIB_GLOBAL_CONFIG));
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
if (pvConfig == NULL) {
|
||
|
Error = ERROR_INVALID_PARAMETER;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
pConfigSrc = (PIGMP_MIB_GLOBAL_CONFIG) pvConfig;
|
||
|
|
||
|
|
||
|
// validate global config.
|
||
|
|
||
|
bValid = ValidateGlobalConfig(pConfigSrc);
|
||
|
if (!bValid) {
|
||
|
Error = ERROR_INVALID_DATA;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// copy from the buffer
|
||
|
|
||
|
InterlockedExchange(&g_Config.RasClientStats,
|
||
|
pConfigSrc->RasClientStats);
|
||
|
InterlockedExchange(&g_Config.LoggingLevel,
|
||
|
pConfigSrc->LoggingLevel);
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
Trace1(LEAVE, "leaving SetGlobalInfo(): %d\n", Error);
|
||
|
LeaveIgmpApi();
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// ValidateGlobalConfig
|
||
|
//
|
||
|
// validates the global config info. If values are not valid, then sets them to
|
||
|
// some default values.
|
||
|
//
|
||
|
// Return Values:
|
||
|
// TRUE: if the global config values are valid.
|
||
|
// FALSE: if the global config values are not valid. sets default values.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
BOOL
|
||
|
ValidateGlobalConfig(
|
||
|
PIGMP_MIB_GLOBAL_CONFIG pGlobalConfig
|
||
|
)
|
||
|
{
|
||
|
DebugPrintGlobalConfig(pGlobalConfig);
|
||
|
|
||
|
|
||
|
// check version
|
||
|
|
||
|
if (pGlobalConfig->Version>=IGMP_VERSION_3_5) {
|
||
|
|
||
|
Trace1(ERR, "Invalid version in global config.\n"
|
||
|
"Create the Igmp configuration again", pGlobalConfig->Version);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
Logerr0(INVALID_VERSION, ERROR_INVALID_DATA);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// check loggingLevel
|
||
|
|
||
|
switch (pGlobalConfig->LoggingLevel) {
|
||
|
case IGMP_LOGGING_NONE :
|
||
|
case IGMP_LOGGING_ERROR :
|
||
|
case IGMP_LOGGING_WARN :
|
||
|
case IGMP_LOGGING_INFO :
|
||
|
break;
|
||
|
|
||
|
default :
|
||
|
pGlobalConfig->LoggingLevel = IGMP_LOGGING_WARN;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// check RasClientStats value
|
||
|
|
||
|
if ((pGlobalConfig->RasClientStats!=0)&&(pGlobalConfig->RasClientStats!=1)){
|
||
|
pGlobalConfig->RasClientStats = 0;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// GetEventMessage
|
||
|
//
|
||
|
// This is called by the IP Router Manager if we indicate that we have
|
||
|
// a message in our queue to be delivered to it (by setting the
|
||
|
// g_RtmNotifyEvent)
|
||
|
// Return Value
|
||
|
// NO_ERROR
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
APIENTRY
|
||
|
GetEventMessage(
|
||
|
OUT ROUTING_PROTOCOL_EVENTS *pEvent,
|
||
|
OUT PMESSAGE pResult
|
||
|
)
|
||
|
{
|
||
|
DWORD Error;
|
||
|
|
||
|
//
|
||
|
// Note: _GetEventMessage() does not use the
|
||
|
// EnterIgmpApi()/LeaveIgmpApi() mechanism,
|
||
|
// since it may be called after Igmp has stopped, when the
|
||
|
// Router Manager is retrieving the ROUTER_STOPPED message
|
||
|
//
|
||
|
|
||
|
Trace2(ENTER, "entering _GetEventMessage: pEvent(%08x) pResult(%08x)",
|
||
|
pEvent, pResult);
|
||
|
|
||
|
|
||
|
|
||
|
ACQUIRE_LIST_LOCK(&g_RtmQueue, "RtmQueue", "_GetEventMessage");
|
||
|
|
||
|
Error = DequeueEvent(&g_RtmQueue, pEvent, pResult);
|
||
|
|
||
|
RELEASE_LIST_LOCK(&g_RtmQueue, "RtmQueue", "_GetEventMessage");
|
||
|
|
||
|
|
||
|
|
||
|
Trace1(LEAVE, "leaving _GetEventMessage: %d\n", Error);
|
||
|
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: EnqueueEvent
|
||
|
//
|
||
|
// This function adds an entry to the end of the queue of
|
||
|
// Router Manager events. It assumes the queue is locked.
|
||
|
//----------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
EnqueueEvent(
|
||
|
PLOCKED_LIST pQueue,
|
||
|
ROUTING_PROTOCOL_EVENTS EventType,
|
||
|
MESSAGE Msg
|
||
|
) {
|
||
|
|
||
|
|
||
|
DWORD Error;
|
||
|
PLIST_ENTRY phead;
|
||
|
PEVENT_QUEUE_ENTRY peqe;
|
||
|
|
||
|
|
||
|
phead = &pQueue->Link;
|
||
|
|
||
|
peqe = IGMP_ALLOC(sizeof(EVENT_QUEUE_ENTRY), 0x1, 0);
|
||
|
PROCESS_ALLOC_FAILURE2(peqe,
|
||
|
"error %d allocating %d bytes for event queue entry",
|
||
|
Error, sizeof(EVENT_QUEUE_ENTRY),
|
||
|
return Error);
|
||
|
|
||
|
peqe->EventType = EventType;
|
||
|
peqe->Msg = Msg;
|
||
|
|
||
|
InsertTailList(phead, &peqe->Link);
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Function: DequeueEvent
|
||
|
//
|
||
|
// This function removes an entry from the head of the queue
|
||
|
// of Router Manager events. It assumes the queue is locked
|
||
|
//------------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
DequeueEvent(
|
||
|
PLOCKED_LIST pQueue,
|
||
|
ROUTING_PROTOCOL_EVENTS *pEventType,
|
||
|
PMESSAGE pMsg
|
||
|
) {
|
||
|
|
||
|
PLIST_ENTRY phead, ple;
|
||
|
PEVENT_QUEUE_ENTRY peqe;
|
||
|
|
||
|
phead = &pQueue->Link;
|
||
|
if (IsListEmpty(phead)) {
|
||
|
return ERROR_NO_MORE_ITEMS;
|
||
|
}
|
||
|
|
||
|
ple = RemoveHeadList(phead);
|
||
|
peqe = CONTAINING_RECORD(ple, EVENT_QUEUE_ENTRY, Link);
|
||
|
|
||
|
*pEventType = peqe->EventType;
|
||
|
*pMsg = peqe->Msg;
|
||
|
|
||
|
IGMP_FREE(peqe);
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// GetNeighbors
|
||
|
// Return Values: ERROR_INSUFFICIENT_BUFFER, NO_ERROR
|
||
|
//------------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
APIENTRY
|
||
|
GetNeighbors(
|
||
|
IN DWORD dwInterfaceIndex,
|
||
|
IN PDWORD pdwNeighborList,
|
||
|
IN OUT PDWORD pdwNeighborListSize,
|
||
|
OUT PBYTE pbInterfaceFlags
|
||
|
)
|
||
|
{
|
||
|
PIF_TABLE_ENTRY pite = GetIfByIndex(dwInterfaceIndex);
|
||
|
|
||
|
if (IS_QUERIER(pite)) {
|
||
|
*pbInterfaceFlags |= MRINFO_QUERIER_FLAG;
|
||
|
*pdwNeighborListSize = 0;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
if (*pdwNeighborListSize < 4)
|
||
|
return ERROR_INSUFFICIENT_BUFFER;
|
||
|
|
||
|
*pdwNeighborListSize = 4;
|
||
|
*pdwNeighborList = pite->Info.QuerierIpAddr;
|
||
|
}
|
||
|
|
||
|
return NO_ERROR; // no neighbors
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
// GetMfeStatus
|
||
|
//
|
||
|
// set statusCode to MFE_OIF_PRUNED if the GroupAddr Mcast group is not
|
||
|
// joined on the interface, else set it to MFE_NO_ERROR
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
APIENTRY
|
||
|
GetMfeStatus(
|
||
|
IN DWORD IfIndex,
|
||
|
IN DWORD GroupAddr,
|
||
|
IN DWORD SourceAddr,
|
||
|
OUT PBYTE StatusCode
|
||
|
)
|
||
|
{
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
PGROUP_TABLE_ENTRY pge;
|
||
|
PGI_ENTRY pgie;
|
||
|
|
||
|
|
||
|
// by default, set code to group not found on the interface.
|
||
|
// if found, set it to MFE_NO_ERROR later on.
|
||
|
|
||
|
*StatusCode = MFE_OIF_PRUNED;
|
||
|
|
||
|
|
||
|
ACQUIRE_IF_LOCK_SHARED(IfIndex, "_GetMfeStatus");
|
||
|
|
||
|
pite = GetIfByIndex(IfIndex);
|
||
|
if (pite!=NULL) {
|
||
|
|
||
|
ACQUIRE_GROUP_LOCK(GroupAddr, "_GetMfeStatus");
|
||
|
pge = GetGroupFromGroupTable(GroupAddr, NULL, 0);
|
||
|
if (pge!=NULL) {
|
||
|
pgie = GetGIFromGIList(pge, pite, 0, ANY_GROUP_TYPE, NULL, 0);
|
||
|
if (pgie!=NULL)
|
||
|
*StatusCode = MFE_NO_ERROR;
|
||
|
}
|
||
|
RELEASE_GROUP_LOCK(GroupAddr, "_GetMfeStatus");
|
||
|
}
|
||
|
|
||
|
RELEASE_IF_LOCK_SHARED(IfIndex, "_GetMfeStatus");
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|