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.

1605 lines
40 KiB

  1. //=============================================================================
  2. // Copyright (c) 1997 Microsoft Corporation
  3. // Module Name: Api.c
  4. // Abstract:
  5. // This module implements some of the Igmp APIs
  6. // RegisterProtocol, StartProtocol, StopProtocol,
  7. // GetGlobalInfo, SetGlobalInfo, and GetEventMessage
  8. //
  9. // Author: K.S.Lokesh (lokeshs@) 11-1-97
  10. //=============================================================================
  11. #include "pchigmp.h"
  12. #pragma hdrstop
  13. //------------------------------------------------------------------------------
  14. // Global variables (see global.h for description)
  15. //------------------------------------------------------------------------------
  16. DWORD g_Initialized;
  17. // interface table, group table, global config, global stats
  18. PIGMP_IF_TABLE g_pIfTable;
  19. PGROUP_TABLE g_pGroupTable;
  20. GLOBAL_CONFIG g_Config;
  21. IGMP_GLOBAL_STATS g_Info;
  22. // socket wait-event bindings
  23. LIST_ENTRY g_ListOfSocketEvents;
  24. READ_WRITE_LOCK g_SocketsRWLock;
  25. // igmp global timer struct
  26. IGMP_TIMER_GLOBAL g_TimerStruct;
  27. // protocol handles returned by mgm
  28. HANDLE g_MgmIgmprtrHandle;
  29. HANDLE g_MgmProxyHandle;
  30. // proxy table
  31. DWORD g_ProxyIfIndex;
  32. PIF_TABLE_ENTRY g_pProxyIfEntry;
  33. CRITICAL_SECTION g_ProxyAlertCS;
  34. LIST_ENTRY g_ProxyAlertsList;
  35. // ras table
  36. DWORD g_RasIfIndex;
  37. PIF_TABLE_ENTRY g_pRasIfEntry;
  38. // global lock, and dynamic locks
  39. CRITICAL_SECTION g_CS;
  40. DYNAMIC_LOCKS_STORE g_DynamicCSStore;
  41. DYNAMIC_LOCKS_STORE g_DynamicRWLStore;
  42. // enum lock
  43. READ_WRITE_LOCK g_EnumRWLock;
  44. // others
  45. HANDLE g_ActivitySemaphore;
  46. LONG g_ActivityCount;
  47. DWORD g_RunningStatus;
  48. HINSTANCE g_DllHandle;
  49. HANDLE g_RtmNotifyEvent;
  50. LOCKED_LIST g_RtmQueue;
  51. HANDLE g_Heap;
  52. DWORD g_TraceId=INVALID_TRACEID;
  53. HANDLE g_LogHandle;
  54. // signature for each enumeration
  55. USHORT g_GlobalIfGroupEnumSignature;
  56. #ifdef MIB_DEBUG
  57. DWORD g_MibTraceId;
  58. IGMP_TIMER_ENTRY g_MibTimer;
  59. #endif
  60. //------------------------------------------------------------------------------
  61. // #defines for g_Initialized
  62. //------------------------------------------------------------------------------
  63. //
  64. // the below flags are used to mark if the data has been initialized.
  65. // If a flag is not set, the corresponding structure wont be deinitialized
  66. //
  67. #define TIMER_GLOBAL_INIT 0x00000002
  68. #define WINSOCK_INIT 0x00000010
  69. #define DYNAMIC_CS_LOCKS_INIT 0x00000020
  70. #define DYNAMIC_RW_LOCKS_INIT 0x00000040
  71. #define GROUP_TABLE_INIT 0x00000080
  72. #define IF_TABLE_INIT 0x00000100
  73. //
  74. // flags associated with dll and calls made before start protocol.
  75. // these flags should not be reset by start protocol
  76. //
  77. #define DLL_STARTUP_FLAGS 0xFF000000
  78. //
  79. // Is StartProtocol being called immediately after DLL startup.
  80. // Used to see if heap has to be destroyed and recreated.
  81. // Set in DllStartup() and cleared in ProcolCleanup() as part of StopProtocol()
  82. //
  83. #define CLEAN_DLL_STARTUP 0x01000000
  84. // flag set to prevent register_protocol from being called multiple times.
  85. #define REGISTER_PROTOCOL 0x02000000
  86. //------------------------------------------------------------------------------
  87. // _DLLMAIN
  88. //
  89. // Called immediately after igmpv2.dll is loaded for the first time by the
  90. // process, and when the igmpv2.dll is unloaded by the process.
  91. // It does some initialization/final cleanup.
  92. //
  93. // Calls: _DllStartup() or _DllCleanup()
  94. //------------------------------------------------------------------------------
  95. BOOL
  96. WINAPI
  97. DLLMAIN (
  98. HINSTANCE hModule,
  99. DWORD dwReason,
  100. LPVOID lpvReserved
  101. )
  102. {
  103. BOOL bNoErr;
  104. DWORD Error=NO_ERROR;
  105. switch (dwReason) {
  106. //
  107. // Startup Initialization of Dll
  108. //
  109. case DLL_PROCESS_ATTACH:
  110. {
  111. // disable per-thread initialization
  112. DisableThreadLibraryCalls(hModule);
  113. // create and initialize global data
  114. bNoErr = DllStartup();
  115. break;
  116. }
  117. //
  118. // Cleanup of Dll
  119. //
  120. case DLL_PROCESS_DETACH:
  121. {
  122. // free global data
  123. bNoErr = DllCleanup();
  124. break;
  125. }
  126. default:
  127. bNoErr = TRUE;
  128. break;
  129. }
  130. return bNoErr;
  131. } //end _DLLMAIN
  132. //------------------------------------------------------------------------------
  133. // _DllStartup
  134. //
  135. // Sets the initial igmp status to IGMP_STATUS_STOPPED, creates a private heap,
  136. // Does the initialization of the rtm queue and tracing/logging,
  137. // and creates the global critical section.
  138. //
  139. // Note: no structures must be allocated from heap here, as StartProtocol()
  140. // if called after StopProtocol() destroys the heap.
  141. // Return Values: TRUE (if no error), else FALSE.
  142. //------------------------------------------------------------------------------
  143. BOOL
  144. DllStartup(
  145. )
  146. {
  147. BOOL bNoErr;
  148. DWORD Error=NO_ERROR;
  149. //not required to ZeroMemory igmp global struct as it is a global variable
  150. //
  151. // set the initial igmp status to stopped.
  152. // The status is set to running, after the protocol specific initialization
  153. // is completed as part of Start Protocol
  154. //
  155. g_RunningStatus = IGMP_STATUS_STOPPED;
  156. bNoErr = FALSE;
  157. BEGIN_BREAKOUT_BLOCK1 {
  158. // set the default logging level. It will be reset during
  159. // StartProtocol(), when logging level is set as part of global config
  160. g_Config.LoggingLevel = IGMP_LOGGING_WARN;
  161. //
  162. // create a private heap for Igmp
  163. //
  164. g_Heap = HeapCreate(0, 0, 0);
  165. if (g_Heap == NULL) {
  166. Error = GetLastError();
  167. GOTO_END_BLOCK1;
  168. }
  169. try {
  170. // initialize the Router Manager event queue
  171. CREATE_LOCKED_LIST(&g_RtmQueue);
  172. // create global critical section
  173. InitializeCriticalSection(&g_CS);
  174. }
  175. except (EXCEPTION_EXECUTE_HANDLER) {
  176. Error = GetExceptionCode();
  177. GOTO_END_BLOCK1;
  178. }
  179. // igmp has a clean initialization from DLL startup. If StartProtocol
  180. // is now called, it does not have to cleanup the heap.
  181. g_Initialized |= CLEAN_DLL_STARTUP;
  182. bNoErr = TRUE;
  183. } END_BREAKOUT_BLOCK1;
  184. return bNoErr;
  185. } //end _DllStartup
  186. //------------------------------------------------------------------------------
  187. // _DllCleanup
  188. //
  189. // Called when the igmpv2 dll is being unloaded. StopProtocol() would have
  190. // been called before, and that would have cleaned all the igmpv2 structures.
  191. // This call frees the rtm queue, the global CS, destroys the local heap,
  192. // and deregisters tracing/logging.
  193. //
  194. // Return Value: TRUE
  195. //------------------------------------------------------------------------------
  196. BOOL
  197. DllCleanup(
  198. )
  199. {
  200. // destroy the router manager event queue
  201. if (LOCKED_LIST_CREATED(&g_RtmQueue)) {
  202. DELETE_LOCKED_LIST(&g_RtmQueue, EVENT_QUEUE_ENTRY, Link);
  203. }
  204. //DebugCheck
  205. //DebugScanMemory();
  206. // delete global critical section
  207. DeleteCriticalSection(&g_CS);
  208. // destroy private heap
  209. if (g_Heap != NULL) {
  210. HeapDestroy(g_Heap);
  211. }
  212. // deregister tracing/error logging
  213. if (g_LogHandle)
  214. RouterLogDeregister(g_LogHandle);
  215. if (g_TraceId!=INVALID_TRACEID)
  216. TraceDeregister(g_TraceId);
  217. return TRUE;
  218. }
  219. VOID
  220. InitTracingAndLogging(
  221. )
  222. {
  223. BEGIN_BREAKOUT_BLOCK1 {
  224. #define REGKEY_TRACING TEXT("Software\\Microsoft\\Tracing\\IGMPv2")
  225. #define REGVAL_CONSOLETRACINGMASK TEXT("ConsoleTracingMask")
  226. TCHAR szTracing[MAX_PATH];
  227. HKEY pTracingKey;
  228. DWORD Value, Error;
  229. lstrcpy(szTracing, REGKEY_TRACING);
  230. Error = RegOpenKeyEx(
  231. HKEY_LOCAL_MACHINE, szTracing, 0, KEY_SET_VALUE, &pTracingKey
  232. );
  233. if (Error != ERROR_SUCCESS)
  234. GOTO_END_BLOCK1;
  235. Value = 0x00ff0000;
  236. RegSetValueEx(
  237. pTracingKey, REGVAL_CONSOLETRACINGMASK, 0,
  238. REG_DWORD, (LPBYTE)&Value, sizeof(DWORD)
  239. );
  240. RegCloseKey(pTracingKey);
  241. } END_BREAKOUT_BLOCK1;
  242. // initialize tracing and error logging
  243. if (g_TraceId==INVALID_TRACEID) {
  244. g_TraceId = TraceRegister("IGMPv2");
  245. }
  246. if (!g_LogHandle) {
  247. g_LogHandle = RouterLogRegister("IGMPv2");
  248. }
  249. }
  250. //------------------------------------------------------------------------------
  251. // _RegisterProtocol
  252. //
  253. // This is the first function called by the IP Router Manager.
  254. // The Router Manager tells the routing protocol its version and capabilities
  255. // It also tells the DLL, the ID of the protocol it expects us to
  256. // register. This allows one DLL to support multiple routing protocols.
  257. // We return the functionality we support and a pointer to our functions.
  258. // Return Value:
  259. // Error: The error code returned by MGM if registering with it failed
  260. // else NO_ERROR
  261. //------------------------------------------------------------------------------
  262. DWORD
  263. WINAPI
  264. RegisterProtocol(
  265. IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar,
  266. IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar
  267. )
  268. {
  269. DWORD Error = NO_ERROR;
  270. // Note: There should not be any trace/logs before here
  271. InitTracingAndLogging() ;
  272. Trace0(ENTER, "RegisterProtocol()");
  273. // cannot call RegisterProtocol multiple times
  274. if (g_Initialized&REGISTER_PROTOCOL) {
  275. Trace0(ERR, "Error: _RegisterProtocol() called multiple times for igmp");
  276. IgmpAssertOnError(FALSE);
  277. return ERROR_CAN_NOT_COMPLETE;
  278. }
  279. else {
  280. g_Initialized |= REGISTER_PROTOCOL;
  281. }
  282. //
  283. // The Router Manager should be calling us to register our protocol.
  284. // The Router Manager must be atleast the version we are compiled with
  285. // The Router Manager must support routing and multicast.
  286. //
  287. if(pRoutingChar->dwProtocolId != MS_IP_IGMP)
  288. return ERROR_NOT_SUPPORTED;
  289. if(pRoutingChar->dwVersion < MS_ROUTER_VERSION)
  290. return ERROR_NOT_SUPPORTED;
  291. if(!(pRoutingChar->fSupportedFunctionality & RF_ROUTING)
  292. || !(pRoutingChar->fSupportedFunctionality & RF_MULTICAST) )
  293. return ERROR_NOT_SUPPORTED;
  294. //
  295. // We setup our characteristics and function pointers
  296. // All pointers should be set to NULL by the caller.
  297. //
  298. pServiceChar->fSupportedFunctionality = 0;
  299. pRoutingChar->fSupportedFunctionality = RF_MULTICAST | RF_ROUTING;
  300. pRoutingChar->pfnStartProtocol = StartProtocol;
  301. pRoutingChar->pfnStartComplete = StartComplete;
  302. pRoutingChar->pfnStopProtocol = StopProtocol;
  303. pRoutingChar->pfnAddInterface = AddInterface;
  304. pRoutingChar->pfnDeleteInterface = DeleteInterface;
  305. pRoutingChar->pfnInterfaceStatus = InterfaceStatus;
  306. pRoutingChar->pfnGetEventMessage = GetEventMessage;
  307. pRoutingChar->pfnGetInterfaceInfo = GetInterfaceConfigInfo;
  308. pRoutingChar->pfnSetInterfaceInfo = SetInterfaceConfigInfo;
  309. pRoutingChar->pfnGetGlobalInfo = GetGlobalInfo;
  310. pRoutingChar->pfnSetGlobalInfo = SetGlobalInfo;
  311. pRoutingChar->pfnMibCreateEntry = MibCreate;
  312. pRoutingChar->pfnMibDeleteEntry = MibDelete;
  313. pRoutingChar->pfnMibGetEntry = MibGet;
  314. pRoutingChar->pfnMibSetEntry = MibSet;
  315. pRoutingChar->pfnMibGetFirstEntry = MibGetFirst;
  316. pRoutingChar->pfnMibGetNextEntry = MibGetNext;
  317. pRoutingChar->pfnUpdateRoutes = NULL;
  318. pRoutingChar->pfnConnectClient = ConnectRasClient;
  319. pRoutingChar->pfnDisconnectClient = DisconnectRasClient;
  320. pRoutingChar->pfnGetNeighbors = GetNeighbors;
  321. pRoutingChar->pfnGetMfeStatus = GetMfeStatus;
  322. pRoutingChar->pfnQueryPower = NULL;
  323. pRoutingChar->pfnSetPower = NULL;
  324. Trace0(LEAVE, "Leaving RegisterProtocol():\n");
  325. return NO_ERROR;
  326. } //end _RegisterProtocol
  327. //------------------------------------------------------------------------------
  328. // _StartProtocol
  329. //
  330. // Called after the _RegisterProtocol() API.
  331. // Initializes the data structures used by the protocol. However, the
  332. // protocol actually starts to run when it gets interface ownerships.
  333. // Locks:
  334. // runs completely in g_CS.
  335. // Return Value:
  336. // Error: if there is an error else NO_ERROR
  337. //------------------------------------------------------------------------------
  338. DWORD
  339. WINAPI
  340. StartProtocol(
  341. IN HANDLE hRtmNotifyEvent, //notify Rtm when protocol stopped
  342. IN PSUPPORT_FUNCTIONS pSupportFunctions, //NULL
  343. IN PVOID pGlobalConfig,
  344. IN ULONG ulStructureVersion,
  345. IN ULONG ulStructureSize,
  346. IN ULONG ulStructureCount
  347. )
  348. {
  349. WSADATA WsaData;
  350. DWORD Error = NO_ERROR;
  351. BOOL bErr;
  352. Trace0(ENTER, "Entering StartProtocol()");
  353. // make sure it is not an unsupported igmp version structure
  354. if (ulStructureVersion>=IGMP_CONFIG_VERSION_600) {
  355. Trace1(ERR, "Unsupported IGMP version structure: %0x",
  356. ulStructureVersion);
  357. IgmpAssertOnError(FALSE);
  358. return ERROR_CAN_NOT_COMPLETE;
  359. }
  360. // lock retained for entire initialization. so api_entry not required
  361. ACQUIRE_GLOBAL_LOCK("_StartProtocol");
  362. //
  363. // make certain igmp is not already running
  364. //
  365. if (g_RunningStatus != IGMP_STATUS_STOPPED) {
  366. Trace0(START,
  367. "Error: _StartProtocol called when Igmp is already running");
  368. Logwarn0(IGMP_ALREADY_STARTED, NO_ERROR);
  369. RELEASE_GLOBAL_LOCK("_StartProtocol");
  370. return ERROR_CAN_NOT_COMPLETE;
  371. }
  372. bErr = TRUE;
  373. BEGIN_BREAKOUT_BLOCK1 {
  374. // clear initialization flags set during and after _startProtocol
  375. g_Initialized &= DLL_STARTUP_FLAGS;
  376. // g_RunningStatus, g_CS, g_TraceId, g_LogHandle, g_RtmQueue,
  377. // g_Initialized & 0xFF000000 initialized in DllStartup/RegisterProtocol
  378. //
  379. // If start protocol has been called after a stop protocol.
  380. //
  381. if (!(g_Initialized & CLEAN_DLL_STARTUP)) {
  382. // destroy private heap, so that there is no memory leak.
  383. if (g_Heap != NULL) {
  384. HeapDestroy(g_Heap);
  385. }
  386. //
  387. // Reset the igmp global structure.
  388. // bugchk:make sure that all appropriate fields are being reset.
  389. //
  390. g_pIfTable = NULL;
  391. g_pGroupTable = NULL;
  392. ZeroMemory(&g_Config, sizeof(GLOBAL_CONFIG));
  393. g_Config.LoggingLevel = IGMP_LOGGING_WARN;
  394. ZeroMemory(&g_Info, sizeof(IGMP_GLOBAL_STATS));
  395. InitializeListHead(&g_ListOfSocketEvents);
  396. ZeroMemory(&g_SocketsRWLock, sizeof(READ_WRITE_LOCK));
  397. ZeroMemory(&g_EnumRWLock, sizeof(READ_WRITE_LOCK));
  398. ZeroMemory(&g_TimerStruct, sizeof(IGMP_TIMER_GLOBAL));
  399. g_MgmIgmprtrHandle = g_MgmProxyHandle = NULL;
  400. g_ProxyIfIndex = 0;
  401. g_pProxyIfEntry = NULL;
  402. ZeroMemory(&g_ProxyAlertCS, sizeof(CRITICAL_SECTION));
  403. InitializeListHead(&g_ProxyAlertsList);
  404. g_RasIfIndex = 0;
  405. g_pRasIfEntry = NULL;
  406. ZeroMemory(&g_DynamicCSStore, sizeof(DYNAMIC_LOCKS_STORE));
  407. ZeroMemory(&g_DynamicRWLStore, sizeof(DYNAMIC_LOCKS_STORE));
  408. g_ActivitySemaphore = NULL;
  409. g_ActivityCount = 0;
  410. g_RtmNotifyEvent = NULL;
  411. g_Heap = NULL;
  412. #ifdef MIB_DEBUG
  413. g_MibTraceId = 0;
  414. ZeroMemory(&g_MibTimer, sizeof(IGMP_TIMER_ENTRY));
  415. #endif
  416. // create private heap again.
  417. g_Heap = HeapCreate(0, 0, 0);
  418. if (g_Heap == NULL) {
  419. Error = GetLastError();
  420. Trace1(ANY, "error %d creating Igmp private heap", Error);
  421. GOTO_END_BLOCK1;
  422. }
  423. }
  424. // save the Router Manager notification event
  425. g_RtmNotifyEvent = hRtmNotifyEvent;
  426. //
  427. // set the Global Config (after making validation changes)
  428. //
  429. if(pGlobalConfig == NULL) {
  430. Trace0(ERR, "_StartProtocol: Called with NULL global config");
  431. IgmpAssertOnError(FALSE);
  432. Error = ERROR_INVALID_PARAMETER;
  433. GOTO_END_BLOCK1;
  434. }
  435. {
  436. PIGMP_MIB_GLOBAL_CONFIG pGlobalConfigTmp;
  437. pGlobalConfigTmp = (PIGMP_MIB_GLOBAL_CONFIG) pGlobalConfig;
  438. // Check the global config, and correct if values are not correct.
  439. // Not a fatal error.
  440. if (! ValidateGlobalConfig(pGlobalConfigTmp)) {
  441. Error = ERROR_INVALID_PARAMETER;
  442. GOTO_END_BLOCK1;
  443. }
  444. g_Config.Version = pGlobalConfigTmp->Version;
  445. g_Config.LoggingLevel = pGlobalConfigTmp->LoggingLevel;
  446. g_Config.RasClientStats = 1;
  447. }
  448. //
  449. // The Global Stats are set to all 0 as it is a global variable.
  450. //
  451. //
  452. // Initialize Winsock version 2.0
  453. //
  454. Error = (DWORD)WSAStartup(MAKEWORD(2,0), &WsaData);
  455. if ( (Error!=0) || (LOBYTE(WsaData.wVersion)<2) ) {
  456. Trace1(ERR,
  457. "StartProtocol: Error %d :could not initialize winsock v-2.0",
  458. Error);
  459. IgmpAssertOnError(FALSE);
  460. Logerr0(WSASTARTUP_FAILED, Error);
  461. if (LOBYTE(WsaData.wVersion)<2)
  462. WSACleanup();
  463. GOTO_END_BLOCK1;
  464. }
  465. g_Initialized |= WINSOCK_INIT;
  466. //
  467. // initialize list of SocketEvents
  468. //
  469. InitializeListHead(&g_ListOfSocketEvents);
  470. Error = CreateReadWriteLock(&g_SocketsRWLock);
  471. if (Error!=NO_ERROR)
  472. GOTO_END_BLOCK1;
  473. Error = CreateReadWriteLock(&g_EnumRWLock);
  474. if (Error!=NO_ERROR)
  475. GOTO_END_BLOCK1;
  476. //
  477. // initialize the timer queues and other timer structures
  478. //
  479. Error = InitializeTimerGlobal();
  480. if (Error!=NO_ERROR)
  481. GOTO_END_BLOCK1;
  482. g_Initialized |= TIMER_GLOBAL_INIT;
  483. // Create Interface Table
  484. InitializeIfTable();
  485. g_Initialized |= IF_TABLE_INIT;
  486. // Create Group Table
  487. InitializeGroupTable();
  488. g_Initialized |= GROUP_TABLE_INIT;
  489. // proxy, ras interface already set to 0/NULL in global structure.
  490. InitializeListHead(&g_ProxyAlertsList);
  491. //
  492. // Initialise the Dynamic CS and ReadWrite locks main struct
  493. //
  494. Error = InitializeDynamicLocksStore(&g_DynamicCSStore);
  495. if (Error!=NO_ERROR)
  496. GOTO_END_BLOCK1;
  497. g_Initialized |= DYNAMIC_CS_LOCKS_INIT;
  498. Error = InitializeDynamicLocksStore(&g_DynamicRWLStore);
  499. if (Error!=NO_ERROR)
  500. GOTO_END_BLOCK1;
  501. g_Initialized |= DYNAMIC_RW_LOCKS_INIT;
  502. //
  503. // create the semaphore released by each thread when it is done
  504. // g_ActivityCount already set to 0.
  505. //
  506. g_ActivityCount = 0;
  507. g_ActivitySemaphore = CreateSemaphore(NULL, 0, 0xfffffff, NULL);
  508. if (g_ActivitySemaphore == NULL) {
  509. Error = GetLastError();
  510. Trace1(ERR, "error %d creating semaphore for Igmp threads", Error);
  511. IgmpAssertOnError(FALSE);
  512. Logerr0(CREATE_SEMAPHORE_FAILED, Error);
  513. GOTO_END_BLOCK1;
  514. }
  515. //
  516. // set the starting time for igmp. Should be done after global
  517. // timer and global Info struct are initialized
  518. //
  519. g_Info.TimeWhenRtrStarted.QuadPart = GetCurrentIgmpTime();
  520. // set igmp status to running
  521. g_RunningStatus = IGMP_STATUS_RUNNING;
  522. #ifdef MIB_DEBUG
  523. //
  524. // set delayed timer to display igmp's MIB tables periodically
  525. //
  526. g_MibTraceId = TraceRegisterEx("IGMPMIB", TRACE_USE_CONSOLE);
  527. if (g_MibTraceId != INVALID_TRACEID) {
  528. g_MibTimer.Context = NULL;
  529. g_MibTimer.Status = TIMER_STATUS_CREATED;
  530. g_MibTimer.Function = WT_MibDisplay;
  531. #if DEBUG_TIMER_TIMERID
  532. SET_TIMER_ID(&g_MibTimer, 910, 0, 0, 0);
  533. #endif
  534. ACQUIRE_TIMER_LOCK("_StartProtocol");
  535. InsertTimer(&g_MibTimer, 5000, TRUE, DBG_Y);
  536. RELEASE_TIMER_LOCK("_StartProtocol");
  537. }
  538. #endif //MIB_DEBUG
  539. //
  540. // register Igmp router with MGM. Proxy will be registered if there
  541. // is an active proxy interface.
  542. //
  543. Error = RegisterProtocolWithMgm(PROTO_IP_IGMP);
  544. // no error if I have reached here
  545. bErr = FALSE;
  546. } END_BREAKOUT_BLOCK1;
  547. if (bErr) {
  548. Trace1(START, "Igmp could not be started: %d", Error);
  549. ProtocolCleanup();
  550. }
  551. else {
  552. Trace0(START, "Igmp started successfully");
  553. Loginfo0(IGMP_STARTED, NO_ERROR);
  554. }
  555. g_DllHandle = LoadLibrary(TEXT("igmpv2.dll"));
  556. RELEASE_GLOBAL_LOCK("_StartProtocol()");
  557. Trace1(LEAVE, "Leaving StartProtocol():%d\n", Error);
  558. return (Error);
  559. } //end StartProtocol
  560. //------------------------------------------------------------------------------
  561. // _StartComplete
  562. //
  563. //------------------------------------------------------------------------------
  564. DWORD
  565. APIENTRY
  566. StartComplete(
  567. VOID
  568. )
  569. {
  570. return NO_ERROR;
  571. }
  572. //------------------------------------------------------------------------------
  573. // _StopProtocol
  574. //
  575. // sets the igmp status to stopping, marks the current number of active igmp
  576. // work items, and queues a worker that will wait till all those work items
  577. // have completed and then clean up igmp structures. This function returns
  578. // a pending status to the caller, while the queued work item will notify
  579. // the rtm after the cleanup has been done.
  580. // Locking:
  581. // Runs completely in g_CS.
  582. // Return Values:
  583. // ERROR_CAN_NOT_COMPLETE, PENDING.
  584. // Queues:
  585. // _WF_FinishStopProtocol()
  586. //------------------------------------------------------------------------------
  587. DWORD
  588. APIENTRY
  589. StopProtocol(
  590. VOID
  591. )
  592. {
  593. DWORD dwThreadCount, Error=NO_ERROR;
  594. Trace0(ENTER, "entering _StopProtocol()");
  595. //debugCheck
  596. #if DEBUG_FLAGS_MEM_ALLOC
  597. // make sure that no interface timers exist
  598. #ifdef MIB_DEBUG
  599. if (g_TimerStruct.NumTimers>1)
  600. #else
  601. if (g_TimerStruct.NumTimers>0)
  602. #endif
  603. {
  604. IgmpAssert(FALSE);
  605. DbgPrint("Cleanup: some igmp timers still exist\n");
  606. DebugPrintTimerQueue();
  607. }
  608. // make sure that no groups exist
  609. DebugForcePrintGroupsList(ENSURE_EMPTY);
  610. #endif
  611. ACQUIRE_GLOBAL_LOCK("_StopProtocol");
  612. //
  613. // cannot stop if already stopped
  614. //
  615. if (g_RunningStatus != IGMP_STATUS_RUNNING) {
  616. Trace0(ERR, "Trying to stop Igmp when it is already being stopped");
  617. IgmpAssertOnError(FALSE);
  618. Logerr0(PROTO_ALREADY_STOPPING, NO_ERROR);
  619. Trace0(LEAVE, "Leaving _StopProtocol()\n");
  620. RELEASE_GLOBAL_LOCK("_StopProtocol");
  621. return ERROR_CAN_NOT_COMPLETE;
  622. }
  623. //
  624. // set Igmp's status to STOPPING;
  625. // this prevents any more work-items from being queued,
  626. // and it prevents the one's already queued from executing
  627. //
  628. InterlockedExchange(&g_RunningStatus, IGMP_STATUS_STOPPING);
  629. //
  630. // find out how many threads are active in Igmp;
  631. // we will have to wait for this many threads to exit
  632. // before we clean up Igmp's resources
  633. //
  634. dwThreadCount = g_ActivityCount;
  635. RELEASE_GLOBAL_LOCK("_StopProtocol");
  636. Trace0(LEAVE, "leaving _StopProtocol");
  637. //
  638. // QueueUserWorkItem that waits for all active Igmp threads and then
  639. // releases resources taken by this DLL.
  640. // Note: I dont use QueueIgmpWorker as that would increment the
  641. // ActivityCount.
  642. //
  643. QueueUserWorkItem(WF_FinishStopProtocol, (PVOID)(DWORD_PTR)dwThreadCount, 0);
  644. // Note: to be safe, there should be no code after QueueUserWorkItem
  645. return ERROR_PROTOCOL_STOP_PENDING;
  646. } //end StopProtocol
  647. DWORD
  648. FreeLibraryThread(
  649. PVOID pvContext
  650. )
  651. {
  652. FreeLibraryAndExitThread(g_DllHandle, 0);
  653. return 0;
  654. }
  655. //------------------------------------------------------------------------------
  656. // WF_FinishStopProtocol
  657. //------------------------------------------------------------------------------
  658. DWORD
  659. WF_FinishStopProtocol(
  660. PVOID pContext //dwThreadCount
  661. )
  662. /*++
  663. Routing Description:
  664. Waits for all the current active igmp work items to complete. Follows that
  665. by a call to ProtocolCleanup() to deregister and cleanup all igmp structures.
  666. In the end, notifies RtrManager that the protocol has stopped.
  667. Queued by: _StopProtocol()
  668. Calls: _ProtocolCleanup()
  669. Locks: no locks required as all igmp threads have stopped.
  670. --*/
  671. {
  672. MESSAGE msg = {0, 0, 0};
  673. DWORD dwThreadCount;
  674. DWORD Error = NO_ERROR;
  675. Trace0(ENTER1, "entering _WF_FinishStopProtocol()");
  676. //
  677. // NOTE: since this is called while the router is stopping,
  678. // do not use EnterIgmpWorker()/LeaveIgmpWorker()
  679. //
  680. dwThreadCount = PtrToUlong(pContext);
  681. //
  682. // waits for API callers and worker functions to finish
  683. //
  684. while (dwThreadCount-- > 0) {
  685. Trace1(STOP, "%d threads active in Igmp", dwThreadCount+1);
  686. WaitForSingleObject(g_ActivitySemaphore, INFINITE);
  687. }
  688. // enter the critical section and leave, just to be sure that
  689. // all threads have quit their calls to LeaveIgmpWorker()
  690. ACQUIRE_GLOBAL_LOCK("_WF_FinishStopProtocol");
  691. RELEASE_GLOBAL_LOCK("_WF_FinishStopProtocol");
  692. Trace0(STOP, "all threads stopped. Cleaning up resources");
  693. //
  694. // This call deregisters with Wait-Server/MGM, and cleans up
  695. // all structures
  696. //
  697. ProtocolCleanup();
  698. Loginfo0(IGMP_STOPPED, NO_ERROR);
  699. //
  700. // notify Router Manager that protocol has been stopped
  701. //
  702. ACQUIRE_LIST_LOCK(&g_RtmQueue, "g_RtmQueue", "_WF_FinishStopProtocol");
  703. EnqueueEvent(&g_RtmQueue, ROUTER_STOPPED, msg);
  704. SetEvent(g_RtmNotifyEvent);
  705. RELEASE_LIST_LOCK(&g_RtmQueue, "g_RtmQueue", "_WF_FinishStopProtocol");
  706. Trace0(LEAVE1, "Leaving _WF_FinishStopProtocol()");
  707. if (g_DllHandle) {
  708. HANDLE h_Thread;
  709. h_Thread = CreateThread(0,0,FreeLibraryThread, NULL, 0, NULL);
  710. if (h_Thread != NULL)
  711. CloseHandle(h_Thread);
  712. }
  713. return 0;
  714. } //end _WF_FinishStopProtocol
  715. //------------------------------------------------------------------------------
  716. // _ProtocolCleanup
  717. //
  718. // All active igmp work items have completed before this procedure is called.
  719. // So it is safe to deregister with Wait-Server, and deregister all interfaces/
  720. // RAS clients and Igmp router/proxy protocol with MGM. Then, all the structures
  721. // are cleaned up.
  722. //
  723. // Called by:
  724. // _WF_FinishStopProtocol() and _StartProtocol()
  725. // Locks:
  726. // No locks required as no api can enter when g_RunningStatus set to stopping.
  727. //------------------------------------------------------------------------------
  728. VOID
  729. ProtocolCleanup(
  730. )
  731. {
  732. DWORD Error = NO_ERROR;
  733. Trace0(ENTER1, "entering _ProtocolCleanup()");
  734. //
  735. // Deregister Igmp Router from MGM. Proxy is deregistered if required in
  736. // _DeinitializeIfTable
  737. //
  738. if (g_MgmIgmprtrHandle!=NULL) {
  739. Error = MgmDeRegisterMProtocol(g_MgmIgmprtrHandle);
  740. Trace1(MGM, "_MgmDeRegisterMProtocol(Igmp): returned %d", Error);
  741. }
  742. // deregister Mib display
  743. #ifdef MIB_DEBUG
  744. if (g_MibTraceId != INVALID_TRACEID)
  745. TraceDeregister(g_MibTraceId);
  746. #endif
  747. // close activity semaphore
  748. if (g_ActivitySemaphore != NULL) {
  749. CloseHandle(g_ActivitySemaphore);
  750. g_ActivitySemaphore = NULL;
  751. }
  752. // DeInitialise the Dynamic CS and ReadWrite locks main struct
  753. if (g_Initialized&DYNAMIC_RW_LOCKS_INIT)
  754. DeInitializeDynamicLocksStore(&g_DynamicRWLStore, LOCK_TYPE_RW);
  755. if (g_Initialized&DYNAMIC_CS_LOCKS_INIT)
  756. DeInitializeDynamicLocksStore(&g_DynamicCSStore, LOCK_TYPE_CS);
  757. // DeInitialize the group table. Delete the group bucket locks.
  758. if (g_Initialized & GROUP_TABLE_INIT)
  759. DeInitializeGroupTable();
  760. // DeInitialize InterfaceTable. This call also deregister all interfaces &
  761. // ras clients from MGM
  762. if (g_Initialized & IF_TABLE_INIT)
  763. DeInitializeIfTable();
  764. // DeInitialize the global timer. Deletes the timer critical-section.
  765. if (g_Initialized&TIMER_GLOBAL_INIT)
  766. DeInitializeTimerGlobal();
  767. //
  768. // delete sockets events and deregister them from wait thread.
  769. // delete sockets read-write lock
  770. //
  771. {
  772. PLIST_ENTRY pHead, ple;
  773. PSOCKET_EVENT_ENTRY psee;
  774. HANDLE WaitHandle;
  775. pHead = &g_ListOfSocketEvents;
  776. for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
  777. psee = CONTAINING_RECORD(ple,SOCKET_EVENT_ENTRY,LinkBySocketEvents);
  778. if (psee->InputWaitEvent) {
  779. WaitHandle = InterlockedExchangePointer(&psee->InputWaitEvent, NULL);
  780. if (WaitHandle)
  781. UnregisterWaitEx( WaitHandle, NULL ) ;
  782. }
  783. CloseHandle(psee->InputEvent);
  784. }
  785. DeleteReadWriteLock(&g_SocketsRWLock);
  786. DeleteCriticalSection(&g_ProxyAlertCS);
  787. DeleteReadWriteLock(&g_EnumRWLock);
  788. }
  789. // deinitialize winsock
  790. if (g_Initialized & WINSOCK_INIT) {
  791. WSACleanup();
  792. }
  793. // Mark that _StopProtocol has been called once.
  794. // If _StartProtocol is called again, igmp will have to Destroy/Create
  795. // private heap and ZeroMemory parts of igmp global struct.
  796. g_Initialized &= ~CLEAN_DLL_STARTUP;
  797. Trace0(LEAVE1, "leaving _ProtocolCleanup()");
  798. return;
  799. } //end _ProtocolCleanup
  800. //------------------------------------------------------------------------------
  801. // DebugPrintGlobalConfig
  802. //------------------------------------------------------------------------------
  803. VOID
  804. DebugPrintGlobalConfig (
  805. PIGMP_MIB_GLOBAL_CONFIG pConfigExt
  806. )
  807. {
  808. Trace0(CONFIG, "Printing Global Config");
  809. Trace1(CONFIG, "Version: 0x%x", pConfigExt->Version);
  810. Trace1(CONFIG, "LoggingLevel: %x\n", pConfigExt->LoggingLevel);
  811. }
  812. //------------------------------------------------------------------------------
  813. // GetGlobalInfo
  814. //
  815. // Return Values: ERROR_CAN_NOT_COMPLETE, ERROR_INVALID_DATA, NO_ERROR
  816. //------------------------------------------------------------------------------
  817. DWORD
  818. WINAPI
  819. GetGlobalInfo(
  820. IN OUT PVOID pvConfig,
  821. IN OUT PDWORD pdwSize,
  822. IN OUT PULONG pulStructureVersion,
  823. IN OUT PULONG pulStructureSize,
  824. IN OUT PULONG pulStructureCount
  825. )
  826. {
  827. DWORD Error=NO_ERROR, dwSize;
  828. PIGMP_MIB_GLOBAL_CONFIG pGlobalConfig;
  829. Trace2(ENTER1, "entering GetGlobalInfo(): pvConfig(%08x) pdwSize(%08x)",
  830. pvConfig, pdwSize);
  831. if (!EnterIgmpApi()) { return ERROR_CAN_NOT_COMPLETE; }
  832. BEGIN_BREAKOUT_BLOCK1 {
  833. //
  834. // check the buffer size and set to global config
  835. //
  836. if (pdwSize == NULL) {
  837. Error = ERROR_INVALID_PARAMETER;
  838. GOTO_END_BLOCK1;
  839. }
  840. if ( (*pdwSize < sizeof(IGMP_MIB_GLOBAL_CONFIG)) || (pvConfig==NULL) ) {
  841. Error = ERROR_INSUFFICIENT_BUFFER;
  842. }
  843. else {
  844. pGlobalConfig = (PIGMP_MIB_GLOBAL_CONFIG) pvConfig;
  845. pGlobalConfig->Version = g_Config.Version;
  846. pGlobalConfig->LoggingLevel = g_Config.LoggingLevel;
  847. pGlobalConfig->RasClientStats = g_Config.RasClientStats;
  848. }
  849. *pdwSize = sizeof(IGMP_MIB_GLOBAL_CONFIG);
  850. } END_BREAKOUT_BLOCK1;
  851. if (pulStructureCount)
  852. *pulStructureCount = 1;
  853. if (pulStructureSize)
  854. *pulStructureSize = *pdwSize;
  855. if (pulStructureVersion)
  856. *pulStructureVersion = IGMP_CONFIG_VERSION_500;
  857. Trace1(LEAVE1, "Leaving GetGlobalInfo(): %d\n", Error);
  858. LeaveIgmpApi();
  859. return Error;
  860. }
  861. //------------------------------------------------------------------------------
  862. // SetGlobalInfo
  863. // Return Values: ERROR_CAN_NOT_COMPLETE, ERROR_INVALID_PARAMETER,
  864. // ERROR_INVALID_DATA, NO_ERROR
  865. //------------------------------------------------------------------------------
  866. DWORD
  867. WINAPI
  868. SetGlobalInfo(
  869. IN PVOID pvConfig,
  870. IN ULONG ulStructureVersion,
  871. IN ULONG ulStructureSize,
  872. IN ULONG ulStructureCount
  873. )
  874. {
  875. DWORD Error=NO_ERROR, dwSize;
  876. PIGMP_MIB_GLOBAL_CONFIG pConfigSrc;
  877. BOOL bValid;
  878. if (!EnterIgmpApi()) { return ERROR_CAN_NOT_COMPLETE; }
  879. // make sure it is not an unsupported igmp version structure
  880. if (ulStructureVersion>=IGMP_CONFIG_VERSION_600) {
  881. Trace1(ERR, "Unsupported IGMP version structure: %0x",
  882. ulStructureVersion);
  883. IgmpAssertOnError(FALSE);
  884. LeaveIgmpApi();
  885. return ERROR_CAN_NOT_COMPLETE;
  886. }
  887. Trace1(ENTER, "entering SetGlobalInfo: pvConfig(%08x)", pvConfig);
  888. ASSERT(ulStructureSize == sizeof(IGMP_MIB_GLOBAL_CONFIG));
  889. BEGIN_BREAKOUT_BLOCK1 {
  890. if (pvConfig == NULL) {
  891. Error = ERROR_INVALID_PARAMETER;
  892. GOTO_END_BLOCK1;
  893. }
  894. pConfigSrc = (PIGMP_MIB_GLOBAL_CONFIG) pvConfig;
  895. // validate global config.
  896. bValid = ValidateGlobalConfig(pConfigSrc);
  897. if (!bValid) {
  898. Error = ERROR_INVALID_DATA;
  899. GOTO_END_BLOCK1;
  900. }
  901. // copy from the buffer
  902. InterlockedExchange(&g_Config.RasClientStats,
  903. pConfigSrc->RasClientStats);
  904. InterlockedExchange(&g_Config.LoggingLevel,
  905. pConfigSrc->LoggingLevel);
  906. } END_BREAKOUT_BLOCK1;
  907. Trace1(LEAVE, "leaving SetGlobalInfo(): %d\n", Error);
  908. LeaveIgmpApi();
  909. return Error;
  910. }
  911. //------------------------------------------------------------------------------
  912. // ValidateGlobalConfig
  913. //
  914. // validates the global config info. If values are not valid, then sets them to
  915. // some default values.
  916. //
  917. // Return Values:
  918. // TRUE: if the global config values are valid.
  919. // FALSE: if the global config values are not valid. sets default values.
  920. //------------------------------------------------------------------------------
  921. BOOL
  922. ValidateGlobalConfig(
  923. PIGMP_MIB_GLOBAL_CONFIG pGlobalConfig
  924. )
  925. {
  926. DebugPrintGlobalConfig(pGlobalConfig);
  927. // check version
  928. if (pGlobalConfig->Version>=IGMP_VERSION_3_5) {
  929. Trace1(ERR, "Invalid version in global config.\n"
  930. "Create the Igmp configuration again", pGlobalConfig->Version);
  931. IgmpAssertOnError(FALSE);
  932. Logerr0(INVALID_VERSION, ERROR_INVALID_DATA);
  933. return FALSE;
  934. }
  935. // check loggingLevel
  936. switch (pGlobalConfig->LoggingLevel) {
  937. case IGMP_LOGGING_NONE :
  938. case IGMP_LOGGING_ERROR :
  939. case IGMP_LOGGING_WARN :
  940. case IGMP_LOGGING_INFO :
  941. break;
  942. default :
  943. pGlobalConfig->LoggingLevel = IGMP_LOGGING_WARN;
  944. return FALSE;
  945. }
  946. // check RasClientStats value
  947. if ((pGlobalConfig->RasClientStats!=0)&&(pGlobalConfig->RasClientStats!=1)){
  948. pGlobalConfig->RasClientStats = 0;
  949. return FALSE;
  950. }
  951. return TRUE;
  952. }
  953. //----------------------------------------------------------------------------
  954. // GetEventMessage
  955. //
  956. // This is called by the IP Router Manager if we indicate that we have
  957. // a message in our queue to be delivered to it (by setting the
  958. // g_RtmNotifyEvent)
  959. // Return Value
  960. // NO_ERROR
  961. //----------------------------------------------------------------------------
  962. DWORD
  963. APIENTRY
  964. GetEventMessage(
  965. OUT ROUTING_PROTOCOL_EVENTS *pEvent,
  966. OUT PMESSAGE pResult
  967. )
  968. {
  969. DWORD Error;
  970. //
  971. // Note: _GetEventMessage() does not use the
  972. // EnterIgmpApi()/LeaveIgmpApi() mechanism,
  973. // since it may be called after Igmp has stopped, when the
  974. // Router Manager is retrieving the ROUTER_STOPPED message
  975. //
  976. Trace2(ENTER, "entering _GetEventMessage: pEvent(%08x) pResult(%08x)",
  977. pEvent, pResult);
  978. ACQUIRE_LIST_LOCK(&g_RtmQueue, "RtmQueue", "_GetEventMessage");
  979. Error = DequeueEvent(&g_RtmQueue, pEvent, pResult);
  980. RELEASE_LIST_LOCK(&g_RtmQueue, "RtmQueue", "_GetEventMessage");
  981. Trace1(LEAVE, "leaving _GetEventMessage: %d\n", Error);
  982. return Error;
  983. }
  984. //----------------------------------------------------------------------------
  985. // Function: EnqueueEvent
  986. //
  987. // This function adds an entry to the end of the queue of
  988. // Router Manager events. It assumes the queue is locked.
  989. //----------------------------------------------------------------------------
  990. DWORD
  991. EnqueueEvent(
  992. PLOCKED_LIST pQueue,
  993. ROUTING_PROTOCOL_EVENTS EventType,
  994. MESSAGE Msg
  995. ) {
  996. DWORD Error;
  997. PLIST_ENTRY phead;
  998. PEVENT_QUEUE_ENTRY peqe;
  999. phead = &pQueue->Link;
  1000. peqe = IGMP_ALLOC(sizeof(EVENT_QUEUE_ENTRY), 0x1, 0);
  1001. PROCESS_ALLOC_FAILURE2(peqe,
  1002. "error %d allocating %d bytes for event queue entry",
  1003. Error, sizeof(EVENT_QUEUE_ENTRY),
  1004. return Error);
  1005. peqe->EventType = EventType;
  1006. peqe->Msg = Msg;
  1007. InsertTailList(phead, &peqe->Link);
  1008. return NO_ERROR;
  1009. }
  1010. //------------------------------------------------------------------------------
  1011. // Function: DequeueEvent
  1012. //
  1013. // This function removes an entry from the head of the queue
  1014. // of Router Manager events. It assumes the queue is locked
  1015. //------------------------------------------------------------------------------
  1016. DWORD
  1017. DequeueEvent(
  1018. PLOCKED_LIST pQueue,
  1019. ROUTING_PROTOCOL_EVENTS *pEventType,
  1020. PMESSAGE pMsg
  1021. ) {
  1022. PLIST_ENTRY phead, ple;
  1023. PEVENT_QUEUE_ENTRY peqe;
  1024. phead = &pQueue->Link;
  1025. if (IsListEmpty(phead)) {
  1026. return ERROR_NO_MORE_ITEMS;
  1027. }
  1028. ple = RemoveHeadList(phead);
  1029. peqe = CONTAINING_RECORD(ple, EVENT_QUEUE_ENTRY, Link);
  1030. *pEventType = peqe->EventType;
  1031. *pMsg = peqe->Msg;
  1032. IGMP_FREE(peqe);
  1033. return NO_ERROR;
  1034. }
  1035. //------------------------------------------------------------------------------
  1036. // GetNeighbors
  1037. // Return Values: ERROR_INSUFFICIENT_BUFFER, NO_ERROR
  1038. //------------------------------------------------------------------------------
  1039. DWORD
  1040. APIENTRY
  1041. GetNeighbors(
  1042. IN DWORD dwInterfaceIndex,
  1043. IN PDWORD pdwNeighborList,
  1044. IN OUT PDWORD pdwNeighborListSize,
  1045. OUT PBYTE pbInterfaceFlags
  1046. )
  1047. {
  1048. PIF_TABLE_ENTRY pite = GetIfByIndex(dwInterfaceIndex);
  1049. if (IS_QUERIER(pite)) {
  1050. *pbInterfaceFlags |= MRINFO_QUERIER_FLAG;
  1051. *pdwNeighborListSize = 0;
  1052. }
  1053. else {
  1054. if (*pdwNeighborListSize < 4)
  1055. return ERROR_INSUFFICIENT_BUFFER;
  1056. *pdwNeighborListSize = 4;
  1057. *pdwNeighborList = pite->Info.QuerierIpAddr;
  1058. }
  1059. return NO_ERROR; // no neighbors
  1060. }
  1061. //-------------------------------------------------------------------------
  1062. // GetMfeStatus
  1063. //
  1064. // set statusCode to MFE_OIF_PRUNED if the GroupAddr Mcast group is not
  1065. // joined on the interface, else set it to MFE_NO_ERROR
  1066. //-------------------------------------------------------------------------
  1067. DWORD
  1068. APIENTRY
  1069. GetMfeStatus(
  1070. IN DWORD IfIndex,
  1071. IN DWORD GroupAddr,
  1072. IN DWORD SourceAddr,
  1073. OUT PBYTE StatusCode
  1074. )
  1075. {
  1076. PIF_TABLE_ENTRY pite;
  1077. PGROUP_TABLE_ENTRY pge;
  1078. PGI_ENTRY pgie;
  1079. // by default, set code to group not found on the interface.
  1080. // if found, set it to MFE_NO_ERROR later on.
  1081. *StatusCode = MFE_OIF_PRUNED;
  1082. ACQUIRE_IF_LOCK_SHARED(IfIndex, "_GetMfeStatus");
  1083. pite = GetIfByIndex(IfIndex);
  1084. if (pite!=NULL) {
  1085. ACQUIRE_GROUP_LOCK(GroupAddr, "_GetMfeStatus");
  1086. pge = GetGroupFromGroupTable(GroupAddr, NULL, 0);
  1087. if (pge!=NULL) {
  1088. pgie = GetGIFromGIList(pge, pite, 0, ANY_GROUP_TYPE, NULL, 0);
  1089. if (pgie!=NULL)
  1090. *StatusCode = MFE_NO_ERROR;
  1091. }
  1092. RELEASE_GROUP_LOCK(GroupAddr, "_GetMfeStatus");
  1093. }
  1094. RELEASE_IF_LOCK_SHARED(IfIndex, "_GetMfeStatus");
  1095. return NO_ERROR;
  1096. }