//============================================================================ // Copyright (c) 1995, Microsoft Corporation // // File: Mgm.h // // History: // V Raman June-25-1997 Created. // // Entry points into MGM. //============================================================================ #include "pchmgm.h" #pragma hdrstop IPMGM_GLOBALS ig; RTM_ENTITY_INFO g_reiRtmEntity = { 0, AF_INET, MS_IP_MGM, 0 }; RTM_REGN_PROFILE g_rrpRtmProfile; RTM_ENTITY_HANDLE g_hRtmHandle; RTM_NOTIFY_HANDLE g_hNotificationHandle; RTM_REGN_PROFILE g_rrpRtmProfile; DWORD StopMgm( ); //---------------------------------------------------------------------------- // MgmDllStartup // // Invoked from DllMain, to initialize global critical section, set status and // register for tracing. //---------------------------------------------------------------------------- BOOL MgmDllStartup( ) { do { ZeroMemory( &ig, sizeof( IPMGM_GLOBALS ) ); ig.dwLogLevel = IPMGM_LOGGING_ERROR; // // Create private heap // ig.hIpMgmGlobalHeap = HeapCreate( 0, 0, 0 ); if ( ig.hIpMgmGlobalHeap == NULL ) { break; } // // initialize the lock list // ig.llStackOfLocks.sleHead.Next = NULL; try { InitializeCriticalSection( &ig.llStackOfLocks.csListLock ); } except ( EXCEPTION_EXECUTE_HANDLER ) { break; } ig.llStackOfLocks.bInit = TRUE; // // Initialize global critical section and set MGM status // try { InitializeCriticalSection( &ig.csGlobal ); } except ( EXCEPTION_EXECUTE_HANDLER ) { break; } ig.imscStatus = IPMGM_STATUS_STOPPED; return TRUE; } while ( FALSE ); // // error occurred - clean up and return FALSE // // // destroy the lock list // if ( ig.llStackOfLocks.bInit ) { DeleteCriticalSection( &ig.llStackOfLocks.csListLock ); } // // delete private heap // if ( ig.hIpMgmGlobalHeap != NULL ) { HeapDestroy( ig.hIpMgmGlobalHeap ); } return FALSE; } //---------------------------------------------------------------------------- // MgmDllCleanup // // Invoked from DllMain, to delete global critical section and // deregister for tracing. //---------------------------------------------------------------------------- VOID MgmDllCleanup( ) { DeleteCriticalSection( &ig.csGlobal ); // // delete lock list // DeleteLockList(); if ( ig.llStackOfLocks.bInit ) { DeleteCriticalSection( &ig.llStackOfLocks.csListLock ); } // // delete private heap // if ( ig.hIpMgmGlobalHeap != NULL ) { HeapDestroy( ig.hIpMgmGlobalHeap ); } return; } //---------------------------------------------------------------------------- // MgmInitialize // // This function is performs Mgm Initialization that includes allocating // a private heap, creating a activity count semaphores and the list // structures for protocol and interface entries. //---------------------------------------------------------------------------- DWORD MgmInitialize( IN PROUTER_MANAGER_CONFIG prmcRmConfig, IN OUT PMGM_CALLBACKS pmcCallbacks ) { DWORD dwErr = NO_ERROR, dwIndex; LARGE_INTEGER li; NTSTATUS nsStatus; ENTER_GLOBAL_SECTION(); // // verify MGM has not already been started. // if ( ig.imscStatus != IPMGM_STATUS_STOPPED ) { TRACE0( START, "MgmInitialize : MGM already running" ); LOGWARN0( IPMGM_ALREADY_STARTED, NO_ERROR ); LEAVE_GLOBAL_SECTION(); return ERROR_CAN_NOT_COMPLETE; } // // register for tracing // TRACESTART(); ig.hLogHandle = RouterLogRegister( "IPMGM" ); TRACE0( ENTER, "ENTERED MgmInitialize" ); do { // // Copy the Router manager callbacks. // ig.rmcRmConfig.pfnAddMfeCallback = prmcRmConfig-> pfnAddMfeCallback; ig.rmcRmConfig.pfnDeleteMfeCallback = prmcRmConfig-> pfnDeleteMfeCallback; ig.rmcRmConfig.pfnGetMfeCallback = prmcRmConfig-> pfnGetMfeCallback; ig.rmcRmConfig.pfnHasBoundaryCallback = prmcRmConfig-> pfnHasBoundaryCallback; // // Hash table sizes // ig.rmcRmConfig.dwIfTableSize = prmcRmConfig-> dwIfTableSize; ig.rmcRmConfig.dwGrpTableSize = prmcRmConfig-> dwGrpTableSize + 1; ig.rmcRmConfig.dwSrcTableSize = prmcRmConfig-> dwSrcTableSize + 1; ig.dwRouteTableSize = prmcRmConfig-> dwIfTableSize; ig.dwTimerQTableSize = min( prmcRmConfig-> dwGrpTableSize / 10 + 1, TIMER_TABLE_MAX_SIZE ); if ( prmcRmConfig-> dwLogLevel <= IPMGM_LOGGING_INFO ) { ig.dwLogLevel = prmcRmConfig-> dwLogLevel; } // // initialize the protocol list // ig.dwNumProtocols = 0; CREATE_LOCKED_LIST( &ig.mllProtocolList ); // // initialize the outstanding join list CREATE_LOCKED_LIST( &ig.mllOutstandingJoinList ); // // create and initialize the interface hash table // ig.pmllIfHashTable = MGM_ALLOC( sizeof( MGM_LOCKED_LIST ) * IF_TABLE_SIZE ); if ( ig.pmllIfHashTable == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "MgmInitialize : Failed to allocate interface table : %x", dwErr ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } ZeroMemory( ig.pmllIfHashTable, sizeof( MGM_LOCKED_LIST ) * IF_TABLE_SIZE ); for ( dwIndex = 0; dwIndex < IF_TABLE_SIZE; dwIndex++ ) { CREATE_LOCKED_LIST( &ig.pmllIfHashTable[ dwIndex ] ); } // // initialize the master group list and temp group list. // CREATE_LOCKED_LIST( &ig.mllGrpList ); CREATE_LOCKED_LIST( &ig.mllTempGrpList ); ig.dwNumTempEntries = 0; // // Create and Initialize group Hash table // ig.pmllGrpHashTable = MGM_ALLOC( sizeof( MGM_LOCKED_LIST ) * GROUP_TABLE_SIZE ); if ( ig.pmllGrpHashTable == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "MgmInitialize : Failed to allocate group table : %x", dwErr ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } ZeroMemory( ig.pmllGrpHashTable, sizeof( MGM_LOCKED_LIST ) * GROUP_TABLE_SIZE ); for ( dwIndex = 0; dwIndex < GROUP_TABLE_SIZE; dwIndex++ ) { CREATE_LOCKED_LIST( &ig.pmllGrpHashTable[ dwIndex ] ); } // // Set up the table of timer queues // ig.phTimerQHandleTable = MGM_ALLOC( TIMER_TABLE_SIZE * sizeof( HANDLE ) ); if ( ig.phTimerQHandleTable == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "MgmInitialize : Failed to allocate timer table : %x", dwErr ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } ZeroMemory( ig.phTimerQHandleTable, TIMER_TABLE_SIZE * sizeof( HANDLE ) ); for ( dwIndex = 0; dwIndex < TIMER_TABLE_SIZE; dwIndex++ ) { nsStatus = RtlCreateTimerQueue( &ig.phTimerQHandleTable[ dwIndex ] ); if ( !NT_SUCCESS( nsStatus ) ) { dwErr = ERROR_NO_SYSTEM_RESOURCES; break; } } if ( !NT_SUCCESS( nsStatus ) ) { break; } // // create activity count semaphore // ig.lActivityCount = 0; ig.hActivitySemaphore = CreateSemaphore( NULL, 0, 0x7FFFFFFF, NULL ); if ( ig.hActivitySemaphore == NULL ) { dwErr = GetLastError(); TRACE1( ANY, "MgmInitialize : Failed to create activity count semaphore : %x", dwErr ); LOGERR0( CREATE_SEMAPHORE_FAILED, dwErr ); break; } // // Register with RTMv2 as a client // dwErr = RtmRegisterEntity( &g_reiRtmEntity, NULL, RtmChangeNotificationCallback, TRUE, &g_rrpRtmProfile, &g_hRtmHandle ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "MgmInitialize : Failed to register with Rtm : %x", dwErr ); LOGERR0( RTM_REGISTER_FAILED, dwErr ); break; } // // Register for marked change notification only // dwErr = RtmRegisterForChangeNotification( g_hRtmHandle, RTM_VIEW_MASK_MCAST, RTM_CHANGE_TYPE_BEST | RTM_NOTIFY_ONLY_MARKED_DESTS, NULL, &g_hNotificationHandle ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "MgmInitialize : Failed to register with Rtm for change" "notification : %x", dwErr ); LOGERR0( RTM_REGISTER_FAILED, dwErr ); break; } // // set up callbacks into MGM for the router manager. // pmcCallbacks-> pfnMfeDeleteIndication = DeleteFromForwarder; pmcCallbacks-> pfnNewPacketIndication = MgmNewPacketReceived; pmcCallbacks-> pfnWrongIfIndication = WrongIfFromForwarder; pmcCallbacks-> pfnBlockGroups = MgmBlockGroups; pmcCallbacks-> pfnUnBlockGroups = MgmUnBlockGroups; // // set the status to running. All future API calls depend on this // ig.imscStatus = IPMGM_STATUS_RUNNING; } while ( FALSE ); LEAVE_GLOBAL_SECTION(); // // in case of error, cleanup all resources allocated // TRACE1( ENTER, "LEAVING MgmInitialize : %x\n", dwErr ); if ( dwErr != NO_ERROR ) { MgmDeInitialize(); } return dwErr; } //---------------------------------------------------------------------------- // MgmDeInitialize // // //---------------------------------------------------------------------------- DWORD MgmDeInitialize( ) { DWORD dwErr, dwInd; TRACE0( ENTER, "ENTERED MgmDeInitialize" ); do { //-------------------------------------------------------------------- // Terminate all activity //-------------------------------------------------------------------- dwErr = StopMgm(); if ( dwErr != NO_ERROR ) { break; } //-------------------------------------------------------------------- // Free all resources //-------------------------------------------------------------------- // // de-register from RTM // dwErr = RtmDeregisterFromChangeNotification( g_hRtmHandle, g_hNotificationHandle ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "Failed to de-register change notification : %x", dwErr ); } dwErr = RtmDeregisterEntity( g_hRtmHandle ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "Failed to de-register from RTM: %x", dwErr ); } // // delete activity semaphore // if ( ig.hActivitySemaphore != NULL ) { CloseHandle( ig.hActivitySemaphore ); ig.hActivitySemaphore = NULL; } // // delete group lists // for ( dwInd = 0; dwInd < GROUP_TABLE_SIZE; dwInd++ ) { DELETE_LOCKED_LIST( &ig.pmllGrpHashTable[ dwInd ] ); } MGM_FREE( ig.pmllGrpHashTable ); DELETE_LOCKED_LIST( &ig.mllGrpList ); // // delete interface lists // for ( dwInd = 0; dwInd < IF_TABLE_SIZE; dwInd++ ) { DELETE_LOCKED_LIST( &ig.pmllIfHashTable[ dwInd ] ); } MGM_FREE( ig.pmllIfHashTable ); // // delete protocol list // DELETE_LOCKED_LIST( &ig.mllProtocolList ); // // free timer resources // NtClose( ig.hRouteCheckTimer ); for ( dwInd = 0; dwInd < TIMER_TABLE_SIZE; dwInd++ ) { RtlDeleteTimerQueue( ig.phTimerQHandleTable[ dwInd ] ); } TRACE1( ENTER, "LEAVING MgmDeInitialize %x\n", dwErr ); // // trace deregister // RouterLogDeregister( ig.hLogHandle ); TRACESTOP(); ig.imscStatus = IPMGM_STATUS_STOPPED; } while ( FALSE ); return dwErr; } //---------------------------------------------------------------------------- // StopMgm // // This function waits for all the therads that are currently executing in // MGM to finish. In addition the status of MGM is marked as stopping which // prevents the further threads from executing MGM API. //---------------------------------------------------------------------------- DWORD StopMgm( ) { LONG lThreadCount = 0; TRACE0( STOP, "ENTERED StopMgm" ); // // Set status of MGM to be stopping // ENTER_GLOBAL_SECTION(); if ( ig.imscStatus != IPMGM_STATUS_RUNNING ) { LEAVE_GLOBAL_SECTION(); TRACE0( ANY, "Mgm is not running" ); return ERROR_CAN_NOT_COMPLETE; } ig.imscStatus = IPMGM_STATUS_STOPPING; lThreadCount = ig.lActivityCount; LEAVE_GLOBAL_SECTION(); TRACE1( STOP, "Number of threads in MGM : %x", lThreadCount ); // // Wait for all the threads in MGM to terminate. // while ( lThreadCount-- > 0 ) { WaitForSingleObject( ig.hActivitySemaphore, INFINITE ); } // // Acquire and release global critical section to ensure all // threads have finished LEAVE_MGM_API() // ENTER_GLOBAL_SECTION(); LEAVE_GLOBAL_SECTION(); TRACE0( STOP, "LEAVING StopMgm" ); return NO_ERROR; } //---------------------------------------------------------------------------- // MgmRegisterMProtocol // // This function is invoked by a routing protocol to obtain a handle. This // handle must be supplied to all subsequent MGM operations. When invoked // this function creates an entry in the list of clients. //---------------------------------------------------------------------------- DWORD MgmRegisterMProtocol( IN PROUTING_PROTOCOL_CONFIG prpcInfo, IN DWORD dwProtocolId, IN DWORD dwComponentId, OUT HANDLE * phProtocol ) { DWORD dwErr = NO_ERROR; PPROTOCOL_ENTRY ppeEntry = NULL; // // increment count of clients executing MGM apis // if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE2( ENTER, "ENTERED MgmRegisterMProtocol %x, %x", dwProtocolId, dwComponentId ); // // Lock Protocol list // ACQUIRE_PROTOCOL_LOCK_EXCLUSIVE(); do { // // check if the protocol already exists. // ppeEntry = GetProtocolEntry( &ig.mllProtocolList.leHead, dwProtocolId, dwComponentId ); if ( ppeEntry != NULL ) { // // valid entry is present. quit with error // TRACE2( ANY, "Entry already present for protocol : %x, %x", dwProtocolId, dwComponentId ); LOGERR0( PROTOCOL_ALREADY_PRESENT, dwProtocolId ); dwErr = ERROR_ALREADY_EXISTS; break; } // // create new protocol entry // dwErr = CreateProtocolEntry( &ig.mllProtocolList.leHead, dwProtocolId, dwComponentId, prpcInfo, &ppeEntry ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "Failed to create protocol entry %x", dwErr ); LOGERR0( CREATE_PROTOCOL_FAILED, dwErr ); break; } ig.dwNumProtocols++; // // return handle to client // *phProtocol = (HANDLE) ( ( (ULONG_PTR) ppeEntry ) ^ (ULONG_PTR)MGM_CLIENT_HANDLE_TAG ); dwErr = NO_ERROR; } while ( FALSE ); RELEASE_PROTOCOL_LOCK_EXCLUSIVE(); LEAVE_MGM_API(); TRACE1( ENTER, "LEAVING MgmRegisterMProtocol : %x\n", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // MgmRegisterMProtocol // // This function is invoked by a routing protocol to obtain a handle. This // handle must be supplied to all subsequent MGM operations. When invoked // this function creates an entry in the list of clients. //---------------------------------------------------------------------------- DWORD MgmDeRegisterMProtocol( IN HANDLE hProtocol ) { DWORD dwErr = NO_ERROR; PPROTOCOL_ENTRY ppeEntry = NULL; // // increment count of clients executing MGM apis // if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE0( ENTER, "ENTERED MgmDeRegisterMProtocol" ); // // Acquire write lock // ACQUIRE_PROTOCOL_LOCK_EXCLUSIVE(); do { // // retrieve entry from handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR)MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } // // Verify that the protocol entry does not own any interfaces // if ( ppeEntry-> dwIfCount != 0 ) { dwErr = ERROR_CAN_NOT_COMPLETE; TRACE1( ANY, "%x interfaces present for this protocol", dwErr ); LOGERR0( INTERFACES_PRESENT, dwErr ); break; } // // No interfaces for this protocol // DeleteProtocolEntry( ppeEntry ); ig.dwNumProtocols--; dwErr = NO_ERROR; } while ( FALSE ); RELEASE_PROTOCOL_LOCK_EXCLUSIVE(); TRACE1( ENTER, "LEAVING MgmDeRegisterMProtocol %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //============================================================================ // Interface ownership API // //============================================================================ //---------------------------------------------------------------------------- // MgmTakeInterfaceOwnership // // This function is invoked by a routing protocol when it is enabled on an // interface. This function creates an entry for the specified interface // and inserts it into the appropriate interface hash bucket. // // Only one protocol can take ownership of an interface at a time. The // only exception to this rule is IGMP. IGMP can co-exist with another // routing protocol on an interface. In this case, the routing protocol // should take ownership of the interface first. //---------------------------------------------------------------------------- DWORD MgmTakeInterfaceOwnership( IN HANDLE hProtocol, IN DWORD dwIfIndex, IN DWORD dwIfNextHopAddr ) { BOOL bFound = FALSE, bIfLock = FALSE; DWORD dwErr = NO_ERROR, dwBucket; PPROTOCOL_ENTRY ppeEntry = NULL; PIF_ENTRY pieEntry = NULL; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE2( ENTER, "ENTERED MgmTakeInterfaceOwnership : Interface %x, %x", dwIfIndex, dwIfNextHopAddr ); ACQUIRE_PROTOCOL_LOCK_SHARED(); do { // // verify protocol handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR)MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } TRACEIF2( IF, "Protocol id: 0x%x 0x%x", ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); // // Retrieve interface entry // dwBucket = IF_TABLE_HASH( dwIfIndex ); ACQUIRE_IF_LOCK_EXCLUSIVE( dwBucket ); bIfLock = TRUE; bFound = FindIfEntry( IF_BUCKET_HEAD( dwBucket ), dwIfIndex, dwIfNextHopAddr, &pieEntry ); if ( bFound ) { // // interface entry exists // if ( IS_PROTOCOL_IGMP( ppeEntry ) ) { // // IGMP is being enabled to this interface. // Set IGMP present flag on this interface entry. // SET_ADDED_BY_IGMP( pieEntry ); } // // A routing protocol is being enabled to this interface entry. // // // Check if interface is currently owned by IGMP. In this case // alone the routing protocol may be added to an existing (from // the MGM point of view) interface. // // If another routing protocol owns the interface that is // an error as per the interop rules for multicast protocols // on a border router. report the error. // else if ( IS_PROTOCOL_ID_IGMP( pieEntry-> dwOwningProtocol ) ) { // // Interface currently owned by IGMP // dwErr = TransferInterfaceOwnershipToProtocol( ppeEntry, pieEntry ); } else { // // Interface currently owned by another routing protocol. // This is an error. // dwErr = ERROR_ALREADY_EXISTS; TRACE2( ANY, "MgmTakeInterfaceOwnership : Already owned by routing protocol" " : %d, %d", pieEntry-> dwOwningProtocol, pieEntry-> dwOwningComponent ); LOGERR0( IF_ALREADY_PRESENT, dwErr ); } break; } // // No interface entry found. Create a new one. // if ( pieEntry == NULL ) { // // First interface in the hash bucket // dwErr = CreateIfEntry( &ig.pmllIfHashTable[ dwBucket ].leHead, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); } else { dwErr = CreateIfEntry( &pieEntry-> leIfHashList, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); } } while ( FALSE ); // // Increment interface count for the specified protocol // if ( dwErr == NO_ERROR ) { InterlockedIncrement( &ppeEntry-> dwIfCount ); } // // Release held locks. // if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwBucket ); } RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmTakeInterfaceOwnership %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // MgmReleaseInterfaceOwnership // // This function is invoked by a routing protocol when it is disabled // on an interface. This functions deletes the entry for the specified // interface. Before deleting the interface entry all the // fowarding entries created by the protocol that use this interface as // the incoming interface. Also remove all group memberships on this // interface. // // If IGMP and routing protocol are both enabled on this interface // IGMP should release this interface first followed by the routing // protocol. // //---------------------------------------------------------------------------- DWORD MgmReleaseInterfaceOwnership( IN HANDLE hProtocol, IN DWORD dwIfIndex, IN DWORD dwIfNextHopAddr ) { BOOL bFound = FALSE, bIGMP, bIfLock = FALSE; DWORD dwErr = NO_ERROR, dwBucket; PPROTOCOL_ENTRY ppeEntry = NULL, ppe; PIF_ENTRY pieEntry = NULL; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE2( ENTER, "ENTERED MgmReleaseInterfaceOwnership : Interface %x, %x", dwIfIndex, dwIfNextHopAddr ); ACQUIRE_PROTOCOL_LOCK_SHARED(); do { // // 1. parameter validation // // // verify protocol handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR) MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } TRACEIF2( IF, "Protocol id: 0x%x 0x%x", ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); // // Retrieve interface entry // dwBucket = IF_TABLE_HASH( dwIfIndex ); ACQUIRE_IF_LOCK_EXCLUSIVE( dwBucket ); bIfLock = TRUE; pieEntry = GetIfEntry( IF_BUCKET_HEAD( dwBucket ), dwIfIndex, dwIfNextHopAddr ); if ( pieEntry == NULL ) { // // no interface entry // dwErr = ERROR_INVALID_PARAMETER; TRACE2( ANY, "Interface entry %d, %x not found ", dwIfIndex, dwIfNextHopAddr ); LOGERR0( IF_NOT_FOUND, dwErr ); break; } // // Interface entry present. Make sure it is owned by the protocol // that is releasing it. // if ( IS_PROTOCOL_IGMP( ppeEntry ) && !IS_ADDED_BY_IGMP( pieEntry ) ) { // // trying to delete IGMP on an interface that // it is not present on. // dwErr = ERROR_INVALID_PARAMETER; TRACE2( ANY, "IGMP not running on interface %x, %x", pieEntry-> dwIfIndex, pieEntry-> dwIfNextHopAddr ); LOGERR0( IF_NOT_FOUND, dwErr ); break; } if ( IS_ROUTING_PROTOCOL( ppeEntry ) && ( ( ppeEntry-> dwProtocolId != pieEntry-> dwOwningProtocol ) || ( ppeEntry-> dwComponentId != pieEntry-> dwOwningComponent ) ) ) { // // interface entry not owned by routing protocol // dwErr = ERROR_INVALID_PARAMETER; TRACE2( ANY, "Routing protcol not running on interface %x, %x", pieEntry-> dwIfIndex, pieEntry-> dwIfNextHopAddr ); LOGERR0( IF_NOT_FOUND, dwErr ); break; } // // 2. Remove protocol state for the interface // ppe = ppeEntry; if ( IS_PROTOCOL_IGMP( ppeEntry ) ) { // // IGMP is releasing this interface // CLEAR_ADDED_BY_IGMP( pieEntry ); bIGMP = TRUE; if ( !IS_ADDED_BY_PROTOCOL( pieEntry ) ) { // // if IGMP is the only protocol on this interface, then delete // any MFEs that use this interface as the incoming interface. // (otherwise these MFEs were created by the routing protocol, // that co-exists with IGMP on this interface, leave them // alone) // DeleteInInterfaceRefs( &pieEntry-> leInIfList ); } else { // // Interface is shared by IGMP and Routing Protocol. // // Group memberships added on an interface shared by IGMP // and a routing protocol are owned by the routing protocol ( // with a bit field indicating whether they have been added by // IGMP ) // // To delete a group membership added by IGMP on a shared // interface, lookup the protocol on that interface and use that // as the protocol that added the group membership. // ppeEntry = GetProtocolEntry( PROTOCOL_LIST_HEAD(), pieEntry-> dwOwningProtocol, pieEntry-> dwOwningComponent ); } } else { // // Interface is being deleted by a routing protocol // if ( IS_ADDED_BY_IGMP( pieEntry ) ) { // // IGMP still exists on this interface // dwErr = TransferInterfaceOwnershipToIGMP( ppeEntry, pieEntry ); break; } // // only routing protocol existed on this interface. // CLEAR_ADDED_BY_PROTOCOL( pieEntry ); bIGMP = FALSE; // // delete all mfes that use this interface as the incoming interface // DeleteInInterfaceRefs( &pieEntry-> leInIfList ); } // // Walk the list of group/source entries that contain this // interface (for this protocol) and delete the references // to this interface. References in this case are nothing but // group memberships added on this interface. // DeleteOutInterfaceRefs( ppeEntry, pieEntry, bIGMP ); // // if neither IGMP nor a routing protocol remain on this interface // remove this interface entry. // if ( !IS_ADDED_BY_IGMP( pieEntry ) && !IS_ADDED_BY_PROTOCOL( pieEntry ) ) { if ( !IsListEmpty( &pieEntry-> leOutIfList ) || !IsListEmpty( &pieEntry-> leInIfList ) ) { dwErr = ERROR_CAN_NOT_COMPLETE; TRACE0( ANY, "References remain for interface" ); break; } DeleteIfEntry( pieEntry ); } } while ( FALSE ); // // release locks // if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwBucket ); } // // Ensure any callbacks for source specific leaves // (caused by the interface being deleted from MGM) // are issued. // // Bug : 154227 // InvokeOutstandingCallbacks(); // // decrement count of interfaces owned by protocol // if ( dwErr == NO_ERROR ) { InterlockedDecrement( &ppe-> dwIfCount ); } RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmReleaseInterfaceOwnership %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // MgmAddGroupMembershipEntry // // //---------------------------------------------------------------------------- DWORD MgmGetProtocolOnInterface( IN DWORD dwIfIndex, IN DWORD dwIfNextHopAddr, IN OUT PDWORD pdwIfProtocolId, IN OUT PDWORD pdwIfComponentId ) { DWORD dwErr = NO_ERROR, dwIfBucket; PLIST_ENTRY pleIfHead; PIF_ENTRY pie; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE2( ENTER, "ENTERED MgmGetProtocolOnInterface : Interface %x, %x", dwIfIndex, dwIfNextHopAddr ); dwIfBucket = IF_TABLE_HASH( dwIfIndex ); pleIfHead = IF_BUCKET_HEAD( dwIfBucket ); ACQUIRE_IF_LOCK_SHARED( dwIfBucket ); do { pie = GetIfEntry( pleIfHead, dwIfIndex, dwIfNextHopAddr ); if ( pie == NULL ) { dwErr = ERROR_NOT_FOUND; TRACE2( ANY, "No interface entry present for interface %x, %x", dwIfIndex, dwIfNextHopAddr ); LOGERR0( IF_NOT_FOUND, dwErr ); break; } *pdwIfProtocolId = pie-> dwOwningProtocol; *pdwIfComponentId = pie-> dwOwningComponent; } while ( FALSE ); RELEASE_IF_LOCK_SHARED( dwIfBucket ); TRACE1( ENTER, "LEAVING MgmGetProtocolOnInterface : %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //============================================================================ // Group membership manipulation API. (addition / deletion / retreival) //============================================================================ //---------------------------------------------------------------------------- // MgmAddGroupMembershipEntry // // //---------------------------------------------------------------------------- DWORD MgmAddGroupMembershipEntry( IN HANDLE hProtocol, IN DWORD dwSourceAddr, IN DWORD dwSourceMask, IN DWORD dwGroupAddr, IN DWORD dwGroupMask, IN DWORD dwIfIndex, IN DWORD dwIfNextHopAddr, IN DWORD dwFlags ) { BOOL bIfLock = FALSE, bgeLock = FALSE, bIGMP = FALSE, bUpdateMfe, bWCFound, bNewComp = FALSE; DWORD dwErr = NO_ERROR, dwIfBucket, dwGrpBucket, dwSrcBucket, dwInd, dwWCGrpBucket; WORD wSourceAddedBy = 0, wNumAddsByIGMP = 0, wNumAddsByRP = 0; PPROTOCOL_ENTRY ppeEntry = NULL; PIF_ENTRY pieEntry = NULL; PGROUP_ENTRY pge = NULL, pgeWC = NULL; PSOURCE_ENTRY pse = NULL; POUT_IF_ENTRY poie = NULL; PIF_REFERENCE_ENTRY pire = NULL; LIST_ENTRY leSourceList; PCREATION_ALERT_CONTEXT pcac; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE6( ENTER, "ENTERED MgmAddGroupMembershipEntry : Interface %x, %x : " "Source : %x, %x : Group : %x, %x", dwIfIndex, dwIfNextHopAddr, dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask ); ACQUIRE_PROTOCOL_LOCK_SHARED(); // // I. Verify input parameters // do { // // verify protocol handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR) MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } // // Retrieve interface entry // dwIfBucket = IF_TABLE_HASH( dwIfIndex ); ACQUIRE_IF_LOCK_EXCLUSIVE( dwIfBucket ); bIfLock = TRUE; pieEntry = GetIfEntry( IF_BUCKET_HEAD( dwIfBucket ), dwIfIndex, dwIfNextHopAddr ); if ( pieEntry == NULL ) { dwErr = ERROR_INVALID_PARAMETER; TRACE2( ANY, "Specified interface was not found : %d, %d", dwIfIndex, dwIfNextHopAddr ); LOGERR0( IF_NOT_FOUND, dwErr ); break; } // // Verify interface is owned by protocol making this call, // or // if this operation is being perfomed by IGMP, verify IGMP is // enabled on this interface (in this case the interface may // be owned by another routing protocol) // if ( ( pieEntry-> dwOwningProtocol != ppeEntry-> dwProtocolId || pieEntry-> dwOwningComponent != ppeEntry-> dwComponentId ) && ( !IS_PROTOCOL_IGMP( ppeEntry ) || !IS_ADDED_BY_IGMP( pieEntry ) ) ) { dwErr = ERROR_INVALID_PARAMETER; TRACE4( ANY, "Interface %d, %d is not owned by protocol %d, %d", dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); LOGERR0( IF_DIFFERENT_OWNER, dwErr ); break; } bIGMP = IS_PROTOCOL_IGMP( ppeEntry ); if ( bIGMP ) { // // if this operation has been invoked by IGMP, // retrieve the entry for the routing protocol component // that owns this interface. // ppeEntry = GetProtocolEntry( PROTOCOL_LIST_HEAD(), pieEntry-> dwOwningProtocol, pieEntry-> dwOwningComponent ); if ( ppeEntry == NULL ) { dwErr = ERROR_CAN_NOT_COMPLETE; TRACE2( ANY, "IGMP join failed because owning protocol entry" " (%x, %x) not found", pieEntry-> dwOwningProtocol, pieEntry-> dwOwningComponent ); break; } } } while ( FALSE ); // // return, if parameter validation fails // if ( dwErr != NO_ERROR ) { if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket ); } RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmAddGroupMembership %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } // // for JOIN STATE changes, i.e for group membership additions // if ( dwFlags & MGM_JOIN_STATE_FLAG ) { // // Add membership entry // InitializeListHead( &leSourceList ); dwErr = AddInterfaceToSourceEntry( ppeEntry, dwGroupAddr, dwGroupMask, dwSourceAddr, dwSourceMask, dwIfIndex, dwIfNextHopAddr, bIGMP, &bUpdateMfe, &leSourceList ); if ( dwErr == NO_ERROR ) { // // Add to an outgoing interface reference for this group // AddSourceToRefList( &pieEntry-> leOutIfList, dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, bIGMP ); } // // release locks in prepapation for updating MFEs // (invoking creation alerts when updating MFEs requires // all locks to be released) // // When locks are re-acquired you need to verify that // the interface (dwIfIndex, dwIfNextHopAddr) and the // group membership being added is still present. // Any one of those could be deleted in another thread // when the locks are released. // if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket ); bIfLock = FALSE; } // // Invoke pended Join/Prune alerts // InvokeOutstandingCallbacks(); // // Update MFEs if required // if ( ( dwErr == NO_ERROR ) && bUpdateMfe ) { // // Queue a work item to update the MFEs // Creation alerts have to be invoked from a separate // thread. This is done to avoid calling back into the // protocol (from MGM) in the context of an add membership // call from the the protocol (into MGM). Doing so results // in deadlocks (bug #323388) // pcac = MGM_ALLOC( sizeof( CREATION_ALERT_CONTEXT ) ); if ( pcac != NULL ) { pcac-> dwSourceAddr = dwSourceAddr; pcac-> dwSourceMask = dwSourceMask; pcac-> dwGroupAddr = dwGroupAddr; pcac-> dwGroupMask = dwGroupMask; pcac-> dwIfIndex = dwIfIndex; pcac-> dwIfNextHopAddr = dwIfNextHopAddr; pcac-> dwProtocolId = ppeEntry-> dwProtocolId; pcac-> dwComponentId = ppeEntry-> dwComponentId; pcac-> bIGMP = bIGMP; pcac-> leSourceList = leSourceList; leSourceList.Flink-> Blink = &(pcac-> leSourceList); leSourceList.Blink-> Flink = &(pcac-> leSourceList); dwErr = QueueMgmWorker( WorkerFunctionInvokeCreationAlert, (PVOID)pcac ); if ( dwErr != NO_ERROR ) { TRACE1( ANY, "Failed to queue " "WorkerFunctionInvokeCreationAlert", dwErr ); MGM_FREE( pcac ); dwErr = NO_ERROR; } } else { TRACE1( ANY, "Failed to allocate %d bytes for work item " "context", sizeof( CREATION_ALERT_CONTEXT ) ); } } } // // For FORWARD state changes only. // else if ( ( dwFlags & MGM_FORWARD_STATE_FLAG ) && !IS_WILDCARD_GROUP( dwGroupAddr, dwGroupMask ) && !IS_WILDCARD_SOURCE( dwSourceAddr, dwSourceMask ) ) { // // Forward state changes are for MFEs only. // No (*, G) or (*, *) entries are updated // do { // // Check for boundaries // if ( IS_HAS_BOUNDARY_CALLBACK() && HAS_BOUNDARY_CALLBACK()( dwIfIndex, dwGroupAddr ) ) { TRACE0( ANY, "Boundary present of group on interface" ); break; } // // Check for (*, *) membership // bWCFound = FindRefEntry( &pieEntry-> leOutIfList, WILDCARD_SOURCE, WILDCARD_SOURCE_MASK, WILDCARD_GROUP, WILDCARD_GROUP_MASK, &pire ); if ( bWCFound ) { // // (*, *) entry present, // get counts for (*, *) membership on interface // dwWCGrpBucket = GROUP_TABLE_HASH( WILDCARD_GROUP, WILDCARD_GROUP_MASK ); ACQUIRE_GROUP_LOCK_SHARED( dwWCGrpBucket ); pgeWC = GetGroupEntry( GROUP_BUCKET_HEAD( dwWCGrpBucket ), WILDCARD_GROUP, WILDCARD_GROUP_MASK ); if ( pgeWC != NULL ) { ACQUIRE_GROUP_ENTRY_LOCK_SHARED( pgeWC ); dwSrcBucket = SOURCE_TABLE_HASH( WILDCARD_SOURCE, WILDCARD_SOURCE_MASK ); pse = GetSourceEntry( SOURCE_BUCKET_HEAD( pgeWC, dwSrcBucket ), WILDCARD_SOURCE, WILDCARD_SOURCE_MASK ); if ( pse != NULL ) { poie = GetOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); if ( poie != NULL ) { wSourceAddedBy |= poie-> wAddedByFlag; wNumAddsByRP = poie-> wNumAddsByRP; } } } } // // Check for (*, G) membership // dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); pge = GetGroupEntry( GROUP_BUCKET_HEAD( dwGrpBucket), dwGroupAddr, dwGroupMask ); if ( pge != NULL ) { ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); dwSrcBucket = SOURCE_TABLE_HASH( WILDCARD_SOURCE, WILDCARD_SOURCE_MASK ); pse = GetSourceEntry( SOURCE_BUCKET_HEAD( pge, dwSrcBucket ), WILDCARD_SOURCE, WILDCARD_SOURCE_MASK ); if ( pse != NULL ) { // // get counts for interface // if (*, G) is present on the interface // poie = GetOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); if ( poie != NULL ) { wSourceAddedBy |= poie-> wAddedByFlag; wNumAddsByIGMP = poie-> wNumAddsByIGMP; wNumAddsByRP += poie-> wNumAddsByRP; } } // // Get (S, G) entry // dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask ); pse = GetSourceEntry( SOURCE_BUCKET_HEAD( pge, dwSrcBucket ), dwSourceAddr, dwSourceMask ); if ( pse != NULL ) { poie = GetOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); if ( poie != NULL ) { // // Get counts for (S, G) membership if // present on interface // wSourceAddedBy |= poie-> wAddedByFlag; wNumAddsByIGMP += poie-> wNumAddsByIGMP; wNumAddsByRP += poie-> wNumAddsByRP; } // // Add interface to MFE OIF list if any // membership // if ( wSourceAddedBy ) { AddInterfaceToSourceMfe( pge, pse, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId, IS_PROTOCOL_IGMP( ppeEntry ), &poie ); poie-> wAddedByFlag |= wSourceAddedBy; poie-> wNumAddsByIGMP = wNumAddsByIGMP; poie-> wNumAddsByRP = wNumAddsByRP; } else { TRACE0( ANY, "Forward state not updated as no" " memberships present on interface" ); } } RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); if ( bWCFound ) { if ( pgeWC ) { RELEASE_GROUP_ENTRY_LOCK_SHARED( pgeWC ); } RELEASE_GROUP_LOCK_SHARED( dwWCGrpBucket ); } } while ( FALSE ); } if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket ); } RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmAddGroupMembership %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // MgmDeleteGroupMembershipEntry // // //---------------------------------------------------------------------------- DWORD MgmDeleteGroupMembershipEntry( IN HANDLE hProtocol, IN DWORD dwSourceAddr, IN DWORD dwSourceMask, IN DWORD dwGroupAddr, IN DWORD dwGroupMask, IN DWORD dwIfIndex, IN DWORD dwIfNextHopAddr, IN DWORD dwFlags ) { BOOL bIfLock = FALSE, bIGMP; DWORD dwErr = NO_ERROR, dwIfBucket, dwGrpBucket, dwSrcBucket; PGROUP_ENTRY pge = NULL; PSOURCE_ENTRY pse = NULL; PPROTOCOL_ENTRY ppeEntry = NULL; PIF_ENTRY pieEntry = NULL; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE6( ENTER, "ENTERED MgmDeleteGroupMembership : Interface %x, %x : " "Source : %x, %x : Group : %x, %x", dwIfIndex, dwIfNextHopAddr, dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask ); ACQUIRE_PROTOCOL_LOCK_SHARED(); // // Verify input parameters. // do { // // verify protocol handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR) MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } // // Retrieve interface entry // dwIfBucket = IF_TABLE_HASH( dwIfIndex ); ACQUIRE_IF_LOCK_EXCLUSIVE( dwIfBucket ); bIfLock = TRUE; pieEntry = GetIfEntry( IF_BUCKET_HEAD( dwIfBucket ), dwIfIndex, dwIfNextHopAddr ); if ( pieEntry == NULL ) { dwErr = ERROR_NOT_FOUND; TRACE0( ANY, "Specified interface was not found" ); break; } // // Verify interface is owned by protocol making this call. // or // if IGMP has invoked this api, verify that IGMP is present // on this interface. // if ( ( pieEntry-> dwOwningProtocol != ppeEntry-> dwProtocolId || pieEntry-> dwOwningComponent != ppeEntry-> dwComponentId ) && ( !IS_PROTOCOL_IGMP( ppeEntry ) || !IS_ADDED_BY_IGMP( pieEntry ) ) ) { dwErr = ERROR_INVALID_PARAMETER; TRACE4( ANY, "Interface %x, %x is not owned by %x, %x", dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId ); LOGERR0( IF_DIFFERENT_OWNER, dwErr ); break; } // // in case this operation is being performed by IGMP // get the routing protocol that co-exists with IGMP // on this interface // bIGMP = IS_PROTOCOL_IGMP( ppeEntry ); if ( bIGMP ) { // // if this operation has been invoked by IGMP, // retrieve the entry for the routing protocol component // that owns this interface. // ppeEntry = GetProtocolEntry( PROTOCOL_LIST_HEAD(), pieEntry-> dwOwningProtocol, pieEntry-> dwOwningComponent ); if ( ppeEntry == NULL ) { dwErr = ERROR_CAN_NOT_COMPLETE; TRACE2( ANY, "IGMP join failed because owning protocol entry" " (%x, %x) not found", pieEntry-> dwOwningProtocol, pieEntry-> dwOwningComponent ); break; } } } while ( FALSE ); // // in case of error, release locks and return // if ( dwErr != NO_ERROR ) { if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket ); } RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmDeleteGroupMembership %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } // // For JOIN state change // if ( dwFlags & MGM_JOIN_STATE_FLAG ) { // // delete interface from source entry // DeleteInterfaceFromSourceEntry( ppeEntry, dwGroupAddr, dwGroupMask, dwSourceAddr, dwSourceMask, dwIfIndex, dwIfNextHopAddr, bIGMP ); // // delete reference entry // DeleteSourceFromRefList( &pieEntry-> leOutIfList, dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, bIGMP ); // // release locks // if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket ); bIfLock = FALSE; } // // Invoke pended Join/Prune alerts // InvokeOutstandingCallbacks(); } // // For FORWARD state changes // else if ( ( dwFlags & MGM_FORWARD_STATE_FLAG ) && !IS_WILDCARD_GROUP( dwGroupAddr, dwGroupMask ) && !IS_WILDCARD_SOURCE( dwSourceAddr, dwSourceMask ) ) { // // FORWARD state changes are for MFEs only. // No (*, *) or (*, G) entries are updated // // // Find the (S, G) entry and delete the group membership // dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); pge = GetGroupEntry( GROUP_BUCKET_HEAD( dwGrpBucket ), dwGroupAddr, dwGroupMask ); if ( pge != NULL ) { ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask ); pse = GetSourceEntry( SOURCE_BUCKET_HEAD( pge, dwSrcBucket ), dwSourceAddr, dwSourceMask ); if ( pse != NULL ) { DeleteInterfaceFromSourceMfe( pge, pse, dwIfIndex, dwIfNextHopAddr, ppeEntry-> dwProtocolId, ppeEntry-> dwComponentId, bIGMP, TRUE ); } RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); } // // release locks // if ( bIfLock ) { RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket ); } RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmDeleteGroupMembership %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE Update API. // //---------------------------------------------------------------------------- DWORD MgmSetMfe( IN HANDLE hProtocol, IN PMIB_IPMCAST_MFE pmimm ) { BOOL bGrpLock = FALSE, bgeLock = FALSE; DWORD dwErr = NO_ERROR, dwGrpBucket, dwSrcBucket; PPROTOCOL_ENTRY ppeEntry; PGROUP_ENTRY pge; PSOURCE_ENTRY pse; // // Check if MGM is still running and increment counts // if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE2( ENTER, "ENTERED MgmSetMfe : (%lx, %lx)", pmimm-> dwSource, pmimm-> dwGroup ); ACQUIRE_PROTOCOL_LOCK_SHARED(); do { // // Verify protocol handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR) MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } // // Get group bucket and find group entry // dwGrpBucket = GROUP_TABLE_HASH( pmimm-> dwGroup, 0 ); ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); bGrpLock = TRUE; pge = GetGroupEntry( GROUP_BUCKET_HEAD( dwGrpBucket ), pmimm-> dwGroup, 0 ); if ( pge == NULL ) { dwErr = ERROR_NOT_FOUND; TRACE1( ANY, "Group %lx not found", pmimm-> dwGroup ); break; } ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bgeLock = TRUE; // // Find source with group entry // dwSrcBucket = SOURCE_TABLE_HASH( pmimm-> dwSource, pmimm-> dwSrcMask ); pse = GetSourceEntry( SOURCE_BUCKET_HEAD( pge, dwSrcBucket ), pmimm-> dwSource, pmimm-> dwSrcMask ); if ( pse == NULL ) { dwErr = ERROR_NOT_FOUND; TRACE1( ANY, "Source %lx not found", pmimm-> dwSource ); break; } // // Update the source entry // pse-> dwUpstreamNeighbor = pmimm-> dwUpStrmNgbr; } while ( FALSE ); if ( bgeLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } if ( bGrpLock ) { RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); } RELEASE_PROTOCOL_LOCK_SHARED(); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE enumeration API. // //---------------------------------------------------------------------------- DWORD MgmGetMfe( IN PMIB_IPMCAST_MFE pmimm, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer ) { DWORD dwErr; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE1( ENTER, "ENTERED MgmGetMfe", *pdwBufferSize ); dwErr = GetMfe( pmimm, pdwBufferSize, pbBuffer, 0 ); TRACE1( ENTER, "LEAVING MgmGetMfe %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE enumeration API. // //---------------------------------------------------------------------------- DWORD MgmGetFirstMfe( IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) { DWORD dwErr; MIB_IPMCAST_MFE mimm; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE1( ENTER, "ENTERED MgmGetFirstMfe", *pdwBufferSize ); mimm.dwGroup = 0; mimm.dwSource = 0; mimm.dwSrcMask = 0; dwErr = GetNextMfe( &mimm, pdwBufferSize, pbBuffer, pdwNumEntries, TRUE, 0 ); TRACE1( ENTER, "LEAVING MgmGetFirstMfe %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE enumeration API. // //---------------------------------------------------------------------------- DWORD MgmGetNextMfe( IN PMIB_IPMCAST_MFE pmimmStart, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) { DWORD dwErr; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE1( ENTER, "ENTERED MgmGetNextMfe", *pdwBufferSize ); dwErr = GetNextMfe( pmimmStart, pdwBufferSize, pbBuffer, pdwNumEntries, FALSE, 0 ); TRACE1( ENTER, "LEAVING MgmGetNextMfe %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE Statistics enumeration API. // //---------------------------------------------------------------------------- DWORD MgmGetMfeStats( IN PMIB_IPMCAST_MFE pmimm, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN DWORD dwFlags ) { DWORD dwErr; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE1( ENTER, "ENTERED MgmGetMfeStats", *pdwBufferSize ); dwErr = GetMfe( pmimm, pdwBufferSize, pbBuffer, dwFlags ); TRACE1( ENTER, "LEAVING MgmGetMfeStats %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE Statistics enumeration API. // //---------------------------------------------------------------------------- DWORD MgmGetFirstMfeStats( IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries, IN DWORD dwFlags ) { DWORD dwErr; MIB_IPMCAST_MFE mimm; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE1( ENTER, "ENTERED MgmGetFirstMfeStats", *pdwBufferSize ); mimm.dwGroup = 0; mimm.dwSource = 0; mimm.dwSrcMask = 0; dwErr = GetNextMfe( &mimm, pdwBufferSize, pbBuffer, pdwNumEntries, TRUE, dwFlags ); TRACE1( ENTER, "LEAVING MgmGetFirstMfeStats %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Mgm MFE Statistics enumeration API. // //---------------------------------------------------------------------------- DWORD MgmGetNextMfeStats( IN PMIB_IPMCAST_MFE pmimmStart, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries, IN DWORD dwFlags ) { DWORD dwErr; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE1( ENTER, "ENTERED MgmGetNextMfeStats", *pdwBufferSize ); dwErr = GetNextMfe( pmimmStart, pdwBufferSize, pbBuffer, pdwNumEntries, FALSE, dwFlags ); TRACE1( ENTER, "LEAVING MgmGetNextMfeStats %x\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // Group menbership entry enumeration API //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // MgmGroupEnumerationStart // // //---------------------------------------------------------------------------- DWORD MgmGroupEnumerationStart( IN HANDLE hProtocol, IN MGM_ENUM_TYPES metEnumType, OUT HANDLE * phEnumHandle ) { DWORD dwErr = NO_ERROR; PPROTOCOL_ENTRY ppeEntry; PGROUP_ENUMERATOR pgeEnum; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE0( ENTER, "ENTERED MgmGroupEnumerationStart" ); ACQUIRE_PROTOCOL_LOCK_SHARED(); do { // // verify protocol handle // ppeEntry = (PPROTOCOL_ENTRY) ( ( (ULONG_PTR) hProtocol ) ^ (ULONG_PTR) MGM_CLIENT_HANDLE_TAG ); dwErr = VerifyProtocolHandle( ppeEntry ); if ( dwErr != NO_ERROR ) { break; } // // create an enumerator. // pgeEnum = MGM_ALLOC( sizeof( GROUP_ENUMERATOR ) ); if ( pgeEnum == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "Failed to allocate group enumerator of size : %d", sizeof( GROUP_ENUMERATOR ) ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } ZeroMemory( pgeEnum, sizeof( GROUP_ENUMERATOR ) ); pgeEnum-> dwSignature = MGM_ENUM_SIGNATURE; // // return handle to the enumerator. // *phEnumHandle = (HANDLE) ( ( (ULONG_PTR) pgeEnum ) ^ (ULONG_PTR) MGM_ENUM_HANDLE_TAG ); } while ( FALSE ); RELEASE_PROTOCOL_LOCK_SHARED(); TRACE1( ENTER, "LEAVING MgmGroupEnumerationStart\n", dwErr ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // MgmGroupEnumerationGetNext // // //---------------------------------------------------------------------------- DWORD MgmGroupEnumerationGetNext( IN HANDLE hEnum, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) { DWORD dwErr; PGROUP_ENUMERATOR pgeEnum; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE0( ENTER, "ENTERED MgmGroupEnumerationGetNext" ); do { // // verify enumeration handle // pgeEnum = VerifyEnumeratorHandle( hEnum ); if ( pgeEnum == NULL ) { dwErr = ERROR_INVALID_PARAMETER; break; } // // verify buffer has space for atleast one entry. // Otherwise return error and note size required for // atleast one entry. // if ( *pdwBufferSize < sizeof( SOURCE_GROUP_ENTRY ) ) { dwErr = ERROR_INSUFFICIENT_BUFFER; TRACE1( ANY, "Insufficient buffer size", dwErr ); *pdwBufferSize = sizeof( SOURCE_GROUP_ENTRY ); break; } *pdwNumEntries = 0; dwErr = GetNextGroupMemberships( pgeEnum, pdwBufferSize, pbBuffer, pdwNumEntries ); // // This comment is to be moved, ignore it. // // If this is the first enumeration call (i.e this is // the beginning of the enumeration) then include the // (S, G) == (0, 0) entry if present. // // Usually this call would start with the (source, group) // entry following the one mentioned in dwLastSource and // dwLastGroup. This would result in the skipping of the // entry at (0, 0) since the (dwLastSource, dwLastGroup) are // initialized to (0, 0). To overcome this a special flag // field is used to note the beginning of the enumeration. // // // Check if this is the first enumeration call. // If so include the (S, G) == (0, 0) entry. // } while ( FALSE ); TRACE0( ENTER, "LEAVING MgmGroupEnumerationGetNext\n" ); LEAVE_MGM_API(); return dwErr; } //---------------------------------------------------------------------------- // MgmGroupEnumerationEnd // // //---------------------------------------------------------------------------- DWORD MgmGroupEnumerationEnd( IN HANDLE hEnum ) { DWORD dwErr = ERROR_INVALID_PARAMETER; PGROUP_ENUMERATOR pgeEnum; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; } TRACE0( ENTER, "ENTERED MgmGroupEnumerationEnd" ); pgeEnum = VerifyEnumeratorHandle( hEnum ); if ( pgeEnum != NULL ) { MGM_FREE( pgeEnum ); dwErr = NO_ERROR; } TRACE1( ENTER, "LEAVING MgmGroupEnumerationEnd\n", dwErr ); LEAVE_MGM_API(); return dwErr; } VOID DisplayGroupTable( ) { #if UNIT_DBG DWORD dwErr, dwBufSize, dwNumEntries; PLIST_ENTRY pleGrp, pleGrpHead, pleSrc, pleSrcHead, pleIf, pleIfHead; PGROUP_ENTRY pge; PSOURCE_ENTRY pse; POUT_IF_ENTRY poie; PBYTE pbBuffer = NULL; MIB_IPMCAST_MFE imm; PMIB_IPMCAST_MFE_STATS pimms; // // Enumerate the MFEs // Since the forwarder is not present, stats are junk. // so all mfe enum does is exercise the API and merge // the master and temp lists so that the subsequent // walks of this list can be done. // dwBufSize = 1024; pbBuffer = HeapAlloc( GetProcessHeap(), 0, dwBufSize ); RtlZeroMemory( pbBuffer, dwBufSize ); dwErr = MgmGetFirstMfe( &dwBufSize, pbBuffer, &dwNumEntries ); if ( dwErr != NO_ERROR ) { printf( "MgmGetFirstMfe returned error : %d\n", dwErr ); } imm.dwSource = 0; imm.dwSrcMask = 0xffffffff; imm.dwGroup = 0; RtlZeroMemory( pbBuffer, dwBufSize ); dwNumEntries = 0; while ( MgmGetNextMfe( &imm, &dwBufSize, pbBuffer, &dwNumEntries ) == NO_ERROR ) { if ( dwNumEntries == 0 ) { break; } pimms = (PMIB_IPMCAST_MFE_STATS) pbBuffer; imm.dwSource = pimms-> dwSource; imm.dwSrcMask = pimms-> dwSrcMask; imm.dwGroup = pimms-> dwGroup; pimms = (PMIB_IPMCAST_MFE_STATS) ( (PBYTE) pimms + SIZEOF_MIB_MFE_STATS( pimms-> ulNumOutIf ) ); } if ( dwErr != NO_ERROR ) { printf( "MgmGetNextMfe returned error : %d\n", dwErr ); } // // since there is no kernel mode forwarder, just walk the master // list of group entries and display all the group entries // pleGrpHead = MASTER_GROUP_LIST_HEAD(); pleGrp = pleGrpHead-> Flink; while ( pleGrp != pleGrpHead ) { // // display the group entry // pge = CONTAINING_RECORD( pleGrp, GROUP_ENTRY, leGrpList ); printf( "\n\n====================================================\n" ); printf( "Group Addr\t: %x, %x\n", pge-> dwGroupAddr, pge-> dwGroupMask ); printf( "Num Sources\t: %d\n", pge-> dwSourceCount ); printf( "====================================================\n\n" ); pleSrcHead = MASTER_SOURCE_LIST_HEAD( pge ); pleSrc = pleSrcHead-> Flink; while ( pleSrc != pleSrcHead ) { pse = CONTAINING_RECORD( pleSrc, SOURCE_ENTRY, leSrcList ); printf( "\n-----------------------Source----------------------------------" ); printf( "\nSource Addr\t: %x, %x\n", pse-> dwSourceAddr, pse-> dwSourceMask ); printf( "Route Addr\t: %x, %x\n", pse-> dwRouteNetwork, pse-> dwRouteMask ); printf( "Out if component: %d\nOut if count\t: %d\n\n", pse-> dwOutCompCount, pse-> dwOutIfCount ); printf( "In coming interface : %d, %x\n", pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ); printf( "In Protocol id : %x, %x\n\n", pse-> dwInProtocolId, pse-> dwInComponentId ); // // list all outgoing interfaces // pleIfHead = &pse-> leOutIfList; pleIf = pleIfHead-> Flink; printf( "\n----------------------Out Interfaces-----------------\n" ); while ( pleIf != pleIfHead ) { poie = CONTAINING_RECORD( pleIf, OUT_IF_ENTRY, leIfList ); printf( "Out interface\t: %d, %x\n", poie-> dwIfIndex, poie-> dwIfNextHopAddr ); printf( "Out Protocol id\t: %x, %x\n", poie-> dwProtocolId, poie-> dwComponentId ); printf( "Added by\t: %x\n", poie-> wAddedByFlag ); printf( "Num adds (IGMP, RP)\t: (%d, %d)\n\n", poie-> wNumAddsByIGMP, poie-> wNumAddsByRP ); pleIf = pleIf-> Flink; } // // list mfe oil // pleIfHead = &pse-> leMfeIfList; pleIf = pleIfHead-> Flink; printf( "\n------------------Mfe Out Interfaces-----------------\n" ); while ( pleIf != pleIfHead ) { poie = CONTAINING_RECORD( pleIf, OUT_IF_ENTRY, leIfList ); printf( "Out interface\t: %d, %x\n", poie-> dwIfIndex, poie-> dwIfNextHopAddr ); printf( "Out Protocol id\t: %x, %x\n", poie-> dwProtocolId, poie-> dwComponentId ); printf( "Added by\t:%x\n", poie-> wAddedByFlag ); printf( "Num adds (IGMP, RP)\t: (%d, %d)\n\n", poie-> wNumAddsByIGMP, poie-> wNumAddsByRP ); pleIf = pleIf-> Flink; } pleSrc = pleSrc-> Flink; } pleGrp = pleGrp-> Flink; } #endif }