//============================================================================= // 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 #if DEBUG_FLAGS_MEM_ALLOC extern CRITICAL_SECTION g_MemCS; #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); #if DEBUG_FLAGS_MEM_ALLOC DeleteCriticalSection(&g_MemCS); #endif // 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 && pdwSize) *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; }