//============================================================================ // Copyright (c) 1995, Microsoft Corporation // // File: group.c // // History: // V Raman June-25-1997 Created. // // routines that manipulate (source, group) entries //============================================================================ #include "pchmgm.h" #pragma hdrstop DWORD AddToGroupList( PGROUP_ENTRY pge ); DWORD AddToSourceList( PGROUP_ENTRY pge, PSOURCE_ENTRY pse ); //---------------------------------------------------------------------------- // CreateGroupEntry // // Creates a new group entry and inserts it into the appropriate location. // // Assumes that the group bucket is locked. //---------------------------------------------------------------------------- DWORD CreateGroupEntry( PLIST_ENTRY pleHashList, DWORD dwGroupAddr, DWORD dwGroupMask, PGROUP_ENTRY * ppge ) { PGROUP_ENTRY pge = NULL; DWORD dwErr = NO_ERROR, dwInd, dwSize; TRACEGROUP2( GROUP, "ENTERED CreateGroupEntry : %x, %x", dwGroupAddr, dwGroupMask ); do { // // Allocate and initialize a new entry // dwSize = sizeof( GROUP_ENTRY ) + ( SOURCE_TABLE_SIZE - 1) * sizeof( LIST_ENTRY ); pge = MGM_ALLOC( dwSize ); if ( pge == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "CreateGroupEntry : failed to allocate group entry %x", dwErr ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } ZeroMemory( pge, dwSize ); pge-> dwGroupAddr = dwGroupAddr; pge-> dwGroupMask = dwGroupMask; pge-> dwSourceCount = 0; pge-> dwNumTempEntries = 0; pge-> pmrwlLock = NULL; // // Initialize all source lists // for ( dwInd = 0; dwInd < SOURCE_TABLE_SIZE; dwInd++ ) { InitializeListHead( &( pge-> pleSrcHashTable[ dwInd ] ) ); } InitializeListHead( &( pge-> leSourceList ) ); InitializeListHead( &( pge-> leTempSrcList ) ); // // Insert into the group hash list // InitializeListHead( &(pge-> leGrpHashList ) ); InsertTailList( pleHashList, &( pge-> leGrpHashList ) ); //-------------------------------------------------------------------- // Insert group entry into the lexicographically sorted list //-------------------------------------------------------------------- InitializeListHead( &( pge-> leGrpList ) ); // // Insert into temp list. // AddToGroupList( pge ); *ppge = pge; } while( FALSE ); TRACEGROUP1( GROUP, "LEAVING CreateGroupEntry : %x", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // GetGroupEntry // // retrieves specified entry. NULL if not present. // Assumes that the group bucket is locked. //---------------------------------------------------------------------------- PGROUP_ENTRY GetGroupEntry( PLIST_ENTRY pleGroupList, DWORD dwGroupAddr, DWORD dwGroupMask ) { PGROUP_ENTRY pge = NULL; if ( FindGroupEntry( pleGroupList, dwGroupAddr, dwGroupMask, &pge, TRUE ) ) { return pge; } return NULL; } //---------------------------------------------------------------------------- // DeleteGroupEntry // // Assumes all sources for this group have been deleted. //---------------------------------------------------------------------------- VOID DeleteGroupEntry( PGROUP_ENTRY pge ) { TRACEGROUP2( GROUP, "ENTERED DeleteGroupEntry : %x, %x", pge-> dwGroupAddr, pge-> dwGroupMask ); RemoveEntryList( &pge-> leGrpHashList ); // // remove from lex. list // ACQUIRE_TEMP_GROUP_LOCK_EXCLUSIVE(); ACQUIRE_MASTER_GROUP_LOCK_EXCLUSIVE(); ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); RemoveEntryList( &pge-> leGrpList ); RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); RELEASE_MASTER_GROUP_LOCK_EXCLUSIVE(); RELEASE_TEMP_GROUP_LOCK_EXCLUSIVE(); MGM_FREE( pge ); TRACEGROUP0( GROUP, "LEAVING DeleteGroupEntry" ); } //---------------------------------------------------------------------------- // FindGroupEntry // // Finds the entry for the specified group. // // If entry is found the ppge parameter returns a pointer to the // specified group entry. // // If entry is not found the ppge parameter is set to the "following" entry. // This serves as an insertion spot in case a new entry is to inserted when // none is found. // // if the group list specified by pleGroupList is empty then ppge is set // to NULL. // // Assumes that the group bucket is locked. //---------------------------------------------------------------------------- BOOL FindGroupEntry( PLIST_ENTRY pleGroupList, DWORD dwGroupAddr, DWORD dwGroupMask, PGROUP_ENTRY * ppge, BOOL bHashList ) { PLIST_ENTRY ple = NULL; PGROUP_ENTRY pge = NULL; BOOL bFound = FALSE; INT iCmp; TRACEGROUP2( GROUP, "ENTERED FindGroupEntry : %x, %x", dwGroupAddr, dwGroupMask ); *ppge = NULL; // // scan group bucket. Group entries are arranged in increasing order // of group addr. // for ( ple = pleGroupList-> Flink; ple != pleGroupList; ple = ple-> Flink ) { if ( bHashList ) { pge = CONTAINING_RECORD( ple, GROUP_ENTRY, leGrpHashList ); } else { pge = CONTAINING_RECORD( ple, GROUP_ENTRY, leGrpList ); } if ( INET_CMP( pge-> dwGroupAddr, dwGroupAddr, iCmp ) < 0 ) { continue; } else if ( iCmp > 0 ) { bFound = FALSE; } else { bFound = TRUE; } *ppge = pge; break; } while ( FALSE ); TRACEGROUP1( GROUP, "LEAVING FindGroupEntry : %x", bFound ); return bFound; } //---------------------------------------------------------------------------- // CreateSourceEntry // // Creates a new source entry and inserts it into its appropriate location. //---------------------------------------------------------------------------- DWORD CreateSourceEntry( PGROUP_ENTRY pge, PLIST_ENTRY pleSrcList, DWORD dwSourceAddr, DWORD dwSourceMask, PSOURCE_ENTRY * ppse ) { DWORD dwErr = NO_ERROR; PSOURCE_ENTRY pse = NULL; TRACEGROUP2( GROUP, "ENTERED CreateSourceEntry : %x %x", dwSourceAddr, dwSourceMask ); do { // // allocate group entry. // pse = MGM_ALLOC( sizeof( SOURCE_ENTRY ) ); if ( pse == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "CreateSourceEntry : failed to allocate source entry %x", dwErr ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } ZeroMemory( pse, sizeof( SOURCE_ENTRY ) ); // // Init. fields // pse-> dwSourceAddr = dwSourceAddr; pse-> dwSourceMask = dwSourceMask; pse-> dwInIfIndex = INVALID_INTERFACE_INDEX; pse-> dwInIfNextHopAddr = INVALID_NEXT_HOP_ADDR; pse-> dwUpstreamNeighbor = 0; pse-> dwInProtocolId = INVALID_PROTOCOL_ID; pse-> dwInComponentId = INVALID_COMPONENT_ID; pse-> bInForwarder = FALSE; pse-> dwInUse = 0; pse-> dwTimeOut = 0; pse-> liCreationTime.QuadPart = 0; RtlZeroMemory( &pse-> imsStatistics, sizeof( IPMCAST_MFE_STATS ) ); // // Outgoing interface list, mfe list are empty. // pse-> dwOutIfCount = 0; pse-> dwOutCompCount = 0; InitializeListHead( &pse-> leOutIfList ); InitializeListHead( &pse-> leScopedIfList ); pse-> dwMfeIfCount = 0; InitializeListHead( &pse-> leMfeIfList ); // // Insert entry into appropriate source lists // InitializeListHead( &pse-> leSrcHashList ); InsertTailList( pleSrcList, &pse-> leSrcHashList ); //-------------------------------------------------------------------- // Insert source entry into the lexicographically sorted list //-------------------------------------------------------------------- InitializeListHead( &pse-> leSrcList ); AddToSourceList( pge, pse ); *ppse = pse; dwErr = NO_ERROR; } while ( FALSE ); TRACEGROUP1( GROUP, "LEAVING CreateSourceEntry : %x", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // GetSourceEntry // // //---------------------------------------------------------------------------- PSOURCE_ENTRY GetSourceEntry( PLIST_ENTRY pleSrcList, DWORD dwSourceAddr, DWORD dwSourceMask ) { PSOURCE_ENTRY pse = NULL; if ( FindSourceEntry( pleSrcList, dwSourceAddr, dwSourceMask, &pse, TRUE ) ) { return pse; } return NULL; } //---------------------------------------------------------------------------- // DeleteSourceEntry // // //---------------------------------------------------------------------------- VOID DeleteSourceEntry( PSOURCE_ENTRY pse ) { TRACEGROUP2( GROUP, "ENTERED DeleteSourceEntry : %x, %x", pse-> dwSourceAddr, pse-> dwSourceMask ); RemoveEntryList( &pse-> leSrcHashList ); RemoveEntryList( &pse-> leSrcList ); MGM_FREE( pse ); TRACEGROUP0( GROUP, "LEAVING DeleteSourceEntry" ); } //---------------------------------------------------------------------------- // FindSourceEntry // // Find specified source entry in the bucket. // // If entry is found the ppse parameter returns a pointer to the // specified source entry. // // If entry is not found the ppse parameter is set to the "following" entry. // This serves as an insertion spot in case a new entry is to inserted when // none is found. // // if the source list specified by pleSrcList is empty then ppse is set // to NULL. // //---------------------------------------------------------------------------- BOOL FindSourceEntry( PLIST_ENTRY pleSrcList, DWORD dwSourceAddr, DWORD dwSourceMask, PSOURCE_ENTRY * ppse, BOOL bHashList ) { BOOL bFound = FALSE; INT iCmp; PLIST_ENTRY ple = NULL; PSOURCE_ENTRY pse = NULL; TRACEGROUP3( GROUP, "ENTERED FindSourceEntry : %x, %x, %x", dwSourceAddr, dwSourceMask, bHashList ); *ppse = NULL; // // walk the source list and find the specified source entry // for ( ple = pleSrcList-> Flink; ple != pleSrcList; ple = ple-> Flink ) { if ( bHashList ) { pse = CONTAINING_RECORD( ple, SOURCE_ENTRY, leSrcHashList ); } else { pse = CONTAINING_RECORD( ple, SOURCE_ENTRY, leSrcList ); } if ( INET_CMP( pse-> dwSourceAddr, dwSourceAddr, iCmp ) < 0 ) { continue; } else if ( iCmp > 0 ) { bFound = FALSE; } else { bFound = TRUE; } *ppse = pse; break; } while ( FALSE ); TRACEGROUP1( GROUP, "LEAVING FindSourceEntry : %x", bFound ); return bFound; } //---------------------------------------------------------------------------- // CreateOutInterfaceEntry // // This function creates an outgoing interface entry for source. //---------------------------------------------------------------------------- DWORD CreateOutInterfaceEntry( PLIST_ENTRY pleOutIfList, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, BOOL bIGMP, POUT_IF_ENTRY * ppoie ) { POUT_IF_ENTRY poie = NULL; DWORD dwErr = NO_ERROR; TRACEGROUP5( GROUP, "ENTERED CreateOutInterfaceEntry : Interface : %x, %x : " "Protocol : %x, %x, IGMP : %x", dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP ); do { *ppoie = NULL; // // allocate out interface entry // poie = MGM_ALLOC( sizeof( OUT_IF_ENTRY ) ); if ( poie == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; TRACE1( ANY, "CreateOutInterfaceEntry : Could not allocate" "out interface entry %x", dwErr ); LOGERR0( HEAP_ALLOC_FAILED, dwErr ); break; } // // initialize entry // ZeroMemory( poie, sizeof( OUT_IF_ENTRY ) ); poie-> dwIfIndex = dwIfIndex; poie-> dwIfNextHopAddr = dwIfNextHopAddr; poie-> dwProtocolId = dwProtocolId; poie-> dwComponentId = dwComponentId; poie-> wForward = 1; if ( bIGMP ) { SET_ADDED_BY_IGMP( poie ); poie-> wNumAddsByIGMP = 1; } else { SET_ADDED_BY_PROTOCOL( poie ); poie-> wNumAddsByRP = 1; } // // insert into the out interface list // InsertTailList( pleOutIfList, &poie-> leIfList ); *ppoie = poie; } while ( FALSE ); TRACEGROUP1( GROUP, "LEAVING CreateOutInterfaceEntry : %x", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // // // //---------------------------------------------------------------------------- POUT_IF_ENTRY GetOutInterfaceEntry( PLIST_ENTRY pleOutIfList, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId ) { POUT_IF_ENTRY poie = NULL; BOOL bNewComp = FALSE; if ( FindOutInterfaceEntry( pleOutIfList, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, &bNewComp, &poie ) ) { return poie; } return NULL; } //---------------------------------------------------------------------------- // DeleteOutInterfaceEntry // // Deletes an outgoing interface entry from the OIL of a source entry. //---------------------------------------------------------------------------- VOID DeleteOutInterfaceEntry( POUT_IF_ENTRY poie ) { TRACEGROUP2( GROUP, "ENTERED DeleteOutInterfaceEntry : Interface %x, %x", poie-> dwIfIndex, poie-> dwIfNextHopAddr ); RemoveEntryList( &poie-> leIfList ); MGM_FREE( poie ); TRACEGROUP0( GROUP, "LEAVING DeleteOutInterfaceEntry" ); } //---------------------------------------------------------------------------- // FindOutInterfaceEntry // // If entry is found the ppoie parameter returns a pointer to the // specified interface entry. // // If entry is not found the ppoie parameter is set to the "following" entry. // This serves as an insertion spot in case a new entry is to inserted when // none is found. // // if the interface list specified by pleOutIfList is empty then ppoie is set // to NULL. // //---------------------------------------------------------------------------- BOOL FindOutInterfaceEntry( PLIST_ENTRY pleIfList, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, PBOOL pbNewComponent, POUT_IF_ENTRY * ppoie ) { BOOL bFound = FALSE; INT iCmp = 0; PLIST_ENTRY ple = NULL; POUT_IF_ENTRY poie = NULL; TRACEGROUP4( GROUP, "ENTERED FindOutInterfaceEntry : %x %x, Protocol %x %x", dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId ); *ppoie = NULL; *pbNewComponent = TRUE; // // Scan the out going interface list. // The outgoing interface list is ordered by ( protocol, component) Id // and within each protocol component by (interface id, next hop addr) // for ( ple = pleIfList-> Flink; ple != pleIfList; ple = ple-> Flink ) { poie = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList ); // // is same protocol // if ( poie-> dwProtocolId < dwProtocolId ) { continue; } else if ( poie-> dwProtocolId > dwProtocolId ) { // // Interface entry not found // *ppoie = poie; break; } // // same protocol // // // is same component // if ( poie-> dwComponentId < dwComponentId ) { continue; } else if ( poie-> dwComponentId > dwComponentId ) { // // Interface entry not found // *ppoie = poie; break; } // // same component // *pbNewComponent = FALSE; // // is same interface // if ( poie-> dwIfIndex < dwIfIndex ) { continue; } else if ( poie-> dwIfIndex > dwIfIndex ) { // // interface not found // *ppoie = poie; break; } // // is same next hop addr // to do IP address comparison function. // if ( INET_CMP( poie-> dwIfNextHopAddr, dwIfNextHopAddr, iCmp ) < 0 ) { continue; } else if ( iCmp > 0 ) { // // interface not found // *ppoie = poie; break; } // // at last, got the interface // *ppoie = poie; bFound = TRUE; break; } TRACEGROUP1( GROUP, "LEAVING FindOutInterfaceEntry : %x", bFound ); return bFound; } //---------------------------------------------------------------------------- // AddInterfaceToSourceEntry // // This function adds an interface to the outgoing interface list of a // (source, group) entry. For an (S, G) entry the corresponding mfe outgoing // interface list is also updated to reflect this addition. For a (*, G) enry, // the mfe outgoing interface list for all source entries is updated, // and for a (*, *) entry mfes for all sources, for all groups are updated. // //---------------------------------------------------------------------------- DWORD AddInterfaceToSourceEntry( PPROTOCOL_ENTRY ppe, DWORD dwGroupAddr, DWORD dwGroupMask, DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwIfIndex, DWORD dwIfNextHopAddr, BOOL bIGMP, PBOOL pbUpdateMfe, PLIST_ENTRY pleSourceList ) { DWORD dwGrpBucket, dwSrcBucket; DWORD dwErr = NO_ERROR; BOOL bFound = FALSE, bNewGrp = FALSE, bNewSrc = FALSE, bNewComp = FALSE, bUpdateMfe = TRUE, bgeLock = FALSE; PPROTOCOL_ENTRY ppeEntry = NULL; PGROUP_ENTRY pge = NULL, pgeNew = NULL; PSOURCE_ENTRY pse = NULL, pseNew = NULL; POUT_IF_ENTRY poie = NULL, poiePrev = NULL; PLIST_ENTRY pleGrpList = NULL, pleSrcList = NULL, ple = NULL; TRACEGROUP2( GROUP, "ENTERED AddInterfaceToSourceEntry : Group %x, %x", dwGroupAddr, dwGroupMask ); TRACEGROUP2( GROUP, "Source : %x, %x", dwSourceAddr, dwSourceMask ); TRACEGROUP2( GROUP, "Interface : %x, %x", dwIfIndex, dwIfNextHopAddr ); do { *pbUpdateMfe = FALSE; // // Lock group bucket // dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); // // find group entry // pleGrpList = GROUP_BUCKET_HEAD( dwGrpBucket ); bFound = FindGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pge, TRUE ); if ( !bFound ) { // // No existing entry for this group // create a group entry. // if ( pge == NULL ) { // // group bucket is null // dwErr = CreateGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pgeNew ); } else { dwErr = CreateGroupEntry( &pge-> leGrpHashList, dwGroupAddr, dwGroupMask, &pgeNew ); } if ( dwErr != NO_ERROR ) { break; } pge = pgeNew; bNewGrp = TRUE; } // // find source entry // // // lock the group entry first // ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bgeLock = TRUE; dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask ); pleSrcList = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); bFound = FindSourceEntry( pleSrcList, dwSourceAddr, dwSourceMask, &pse, TRUE ); if ( !bFound ) { // // create the source entry // if ( pse == NULL ) { // // source bucket is null // dwErr = CreateSourceEntry( pge, pleSrcList, dwSourceAddr, dwSourceMask, &pseNew ); } else { dwErr = CreateSourceEntry( pge, &pse-> leSrcHashList, dwSourceAddr, dwSourceMask, &pseNew ); } if ( dwErr != NO_ERROR ) { break; } pse = pseNew; pge-> dwSourceCount++; bNewSrc = TRUE; } // // Check if the group been added falls with a scoped boundary // on this interface // if ( IS_HAS_BOUNDARY_CALLBACK() && HAS_BOUNDARY_CALLBACK() ( dwIfIndex, dwGroupAddr ) ) { // // Group is administratively scoped on this interface // Insert the interface into the list of scoped interfaces // bFound = FindOutInterfaceEntry( &pse-> leScopedIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, &bNewComp, &poie ); if ( !bFound ) { // // Interface not present in scoped interfaces list. // add it. // TRACEGROUP0( GROUP, "Group entry scoped & added" ); ple = ( poie == NULL ) ? &pse-> leScopedIfList : &poie-> leIfList; dwErr = CreateOutInterfaceEntry( ple, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP, &poie ); if ( dwErr == NO_ERROR ) { // // increment the out i/f count // pse-> dwOutIfCount++; } } else { // // Interface already present in scoped interface list. // Since IGMP and a Routing protocol could be running // on this interface, it is possibly that this interface // was added by IGMP and is now being added by the routing // protocol or vice versa. Make sure to set the right // flags and update join counts. // TRACEGROUP0( GROUP, "Group entry scoped & updated" ); if ( bIGMP ) { SET_ADDED_BY_IGMP( poie ); poie-> wNumAddsByIGMP = 1; } else { SET_ADDED_BY_PROTOCOL( poie ); poie-> wNumAddsByRP = 1; } dwErr = NO_ERROR; } TRACEGROUP1( GROUP, "Group entry scoped : %lx", dwErr ); break; } // // Find interface entry in OIL // bFound = FindOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, &bNewComp, &poie ); if ( !bFound ) { // // Create interface entry // if ( poie == NULL ) { dwErr = CreateOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP, &poie ); } else { dwErr = CreateOutInterfaceEntry( &poie-> leIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP, &poie ); } if ( dwErr != NO_ERROR ) { break; } // // update count of number of outgoing interfaces and // count of number of routing protocol components that // have added interfaces to the out going i/f list // pse-> dwOutIfCount++; if ( bNewComp ) { pse-> dwOutCompCount++; InvokeJoinAlertCallbacks( pge, pse, poie, bIGMP, ppe ); } } else { // // interface entry found in the out interface list // if ( bIGMP ) { // // interface entry is being added by IGMP // // // if interface entry was previously added by // IGMP, no further processing is necessary (no mfe updates) // if ( IS_ADDED_BY_IGMP( poie ) ) { bUpdateMfe = FALSE; } else { // // flag interface as added by IGMP // SET_ADDED_BY_IGMP( poie ); poie-> wNumAddsByIGMP = 1; // // inform routing protocol (if any) that co-exists with IGMP // on this interface // if ( IS_ROUTING_PROTOCOL( ppe ) && IS_LOCAL_JOIN_ALERT( ppe ) ) { LOCAL_JOIN_ALERT( ppe )( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwIfIndex, dwIfNextHopAddr ); } } } else { // // Interface is being added by routing protocol // // // if interface entry was previously added by the // routing protocol, no further processing is necessary. // if ( IS_ADDED_BY_PROTOCOL( poie ) ) { bUpdateMfe = FALSE; } // // flag interface as added by routing protocol // SET_ADDED_BY_PROTOCOL( poie ); poie-> wNumAddsByRP = 1; } } } while ( FALSE ); // // error finding/creating the entry // if ( dwErr != NO_ERROR ) { if ( bNewSrc ) { DeleteSourceEntry( pse ); } if ( bgeLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } if ( bNewGrp ) { DeleteGroupEntry( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); return dwErr; } //------------------------------------------------------------------------ // // MFE Update // //------------------------------------------------------------------------ if ( !bUpdateMfe ) { if ( bgeLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); return dwErr; } // // Is the source entry that was updated an MFE ? // // If so update the OIL for the MFE. // if ( IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { // // TO BE DONE : // Invoke CREATION_ALERT for MFE. // AddInterfaceToSourceMfe( pge, pse, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP, NULL ); } // // Is this a wildcard (source, group) entry, if so you // need update the OIL of all (source, group) with this // interface. // if ( IS_WILDCARD_GROUP( dwGroupAddr, dwGroupMask ) ) { // // you are in for the big kahuna // RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); *pbUpdateMfe = TRUE; } else if ( IS_WILDCARD_SOURCE( dwSourceAddr, dwSourceMask ) ) { // // you 're in for a kahuna all right. But big nahh. // *pbUpdateMfe = TRUE; AddInterfaceToGroupMfe ( pge, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP, FALSE, pleSourceList ); RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); } else { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); } TRACEGROUP1( GROUP, "LEAVING AddInterfaceToSourceEntry %x", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // AddInterfaceToAllMfe // // This functions adds an interface the outgoing interface of a MFE. Duh //---------------------------------------------------------------------------- VOID AddInterfaceToAllMfeInGroupBucket( DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, DWORD dwInd, BOOL bIGMP, BOOL bAdd, PLIST_ENTRY pleSourceList ) { PLIST_ENTRY ple = NULL, pleGrpList = NULL; PGROUP_ENTRY pge = NULL; TRACEGROUP3( GROUP, "ENTERED (%d) AddInterfaceToAllMfeInGroupBucket : %x, %x", dwInd, dwIfIndex, dwIfNextHopAddr ); // // lock the group bucket // ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwInd ); // // for each group entry in the bucket // pleGrpList = GROUP_BUCKET_HEAD( dwInd ); for ( ple = pleGrpList-> Flink; ple != pleGrpList; ple = ple-> Flink ) { pge = CONTAINING_RECORD( ple, GROUP_ENTRY, leGrpHashList ); ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); AddInterfaceToGroupMfe( pge, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP, bAdd, pleSourceList ); RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } // // release group lock // RELEASE_GROUP_LOCK_EXCLUSIVE( dwInd ); TRACEGROUP0( GROUP, "LEAVING AddInterfaceToAllMfeInGroupBucket" ); return; } //---------------------------------------------------------------------------- // AddInterfaceToAllGroupMfe // // This functions adds an interface the outgoing interface of a MFE. Duh // // Assumes that the group bucket is locked. //---------------------------------------------------------------------------- VOID AddInterfaceToGroupMfe( PGROUP_ENTRY pge, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, BOOL bIGMP, BOOL bAdd, PLIST_ENTRY pleSourceList ) { PLIST_ENTRY pleSource, pleSrcHead; PSOURCE_ENTRY pse = NULL; TRACEGROUP2( GROUP, "ENTERED AddInterfaceToGroupMfe : Group %x, %x", pge-> dwGroupAddr, pge-> dwGroupMask ); MergeTempAndMasterSourceLists( pge ); // // For each source in this bucket // pleSrcHead = MASTER_SOURCE_LIST_HEAD( pge ); for ( pleSource = pleSrcHead-> Flink; pleSource != pleSrcHead; pleSource = pleSource-> Flink ) { pse = CONTAINING_RECORD( pleSource, SOURCE_ENTRY, leSrcList ); // // check for valid incoming interface ==> this // is an MFE too. // if ( !IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { continue; } if ( bAdd ) { if ( IsForwardingEnabled( pge-> dwGroupAddr, pge-> dwGroupMask, pse-> dwSourceAddr, pse-> dwSourceMask, pleSourceList ) ) { AddInterfaceToSourceMfe( pge, pse, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP, NULL ); } } else { AddToCheckForCreationAlertList( pge-> dwGroupAddr, pge-> dwGroupMask, pse-> dwSourceAddr, pse-> dwSourceMask, pse-> dwInIfIndex, pse-> dwInIfNextHopAddr, pleSourceList ); } } TRACEGROUP0( GROUP, "LEAVING AddInterfaceToGroupMfe" ); return; } //---------------------------------------------------------------------------- // AddInterfaceToSourceMfe // // This functions adds an interface the outgoing interface of a MFE. Duh //---------------------------------------------------------------------------- VOID AddInterfaceToSourceMfe( PGROUP_ENTRY pge, PSOURCE_ENTRY pse, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, BOOL bIGMP, POUT_IF_ENTRY * ppoie ) { BOOL bFound = FALSE, bNegativeEntry = FALSE, bNewComp = FALSE; DWORD dwErr = NO_ERROR; PPROTOCOL_ENTRY ppe = NULL; POUT_IF_ENTRY poie = NULL, poieNew = NULL; PLIST_ENTRY pleOutList = NULL; MGM_IF_ENTRY mie; TRACEGROUP2( GROUP, "ENTERED AddInterfaceToSourecMfe : Source : %x, %x", pse-> dwSourceAddr, pse-> dwSourceMask ); do { // // check if the interface being added to the MFE is the same // as the incoming interface. If so quit. // if ( ( pse-> dwInIfIndex == dwIfIndex ) && ( pse-> dwInIfNextHopAddr == dwIfNextHopAddr ) ) { break; } // // Check if the incoming interface has a scoped boundary on it. // If it is, then this is a negative MFE that should remain // negative, even if outgoing interfaces are present for this // group. This ensures that group traffic is not forwarded from // outside the scope into the scope. // if ( IS_HAS_BOUNDARY_CALLBACK() && HAS_BOUNDARY_CALLBACK()( pse-> dwInIfIndex, pge-> dwGroupAddr ) ) { TRACE2( GROUP, "Group %lx scoped on incoming i/f %lx", pge-> dwGroupAddr, pse-> dwInIfIndex ); break; } #if 0 // // invoke creation alert to the protocol on the interface (being // added to the MFE) to make sure that we should be adding this // interface to the OIL of the MFE) // ppe = GetProtocolEntry( PROTOCOL_LIST_HEAD(), dwProtocolId, dwComponentId ); if ( ppe == NULL ) { break; } mie.dwIfIndex = dwIfIndex; mie.dwIfNextHopAddr = dwIfNextHopAddr; mie.bIsEnabled = TRUE; if ( IS_CREATION_ALERT( ppe ) ) { CREATION_ALERT( ppe ) ( pse-> dwSourceAddr, pse-> dwSourceMask, pge-> dwGroupAddr, pge-> dwGroupMask, pse-> dwInIfIndex, pse-> dwInIfNextHopAddr, 1, &mie ); if ( !mie.bIsEnabled ) { TRACE2( GROUP, "Interface %x, %x pruned by protocol", pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ); break; } } #endif // // check if interface already exists in OIL // pleOutList = &pse-> leMfeIfList; bFound = FindOutInterfaceEntry( pleOutList, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, &bNewComp, &poie ); if ( !bFound ) { // // create a new entry // if ( poie == NULL ) { // // This is the first interface in the outgoing list. // This implies that the entry was previously a NEGATIVE mfe // bNegativeEntry = TRUE; dwErr = CreateOutInterfaceEntry( pleOutList, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP, &poieNew ); } else { dwErr = CreateOutInterfaceEntry( &poie-> leIfList, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP, &poieNew ); } if ( dwErr != NO_ERROR ) { break; } pse-> dwMfeIfCount++; } else { // // Interface entry already exists in the outgoing interface // list of the mfe. // // update reference counts // if ( bIGMP ) { // // Interface added by IGMP // SET_ADDED_BY_IGMP( poie ); poie-> wNumAddsByIGMP++; } else { SET_ADDED_BY_PROTOCOL( poie ); poie-> wNumAddsByRP++; } break; } // // If the outgoing interface list was empty before this interface // entry was added implying a negative mfe, send JOIN_ALERT callback // to the protocol owning the incoming interface // if ( bNegativeEntry ) { TRACEGROUP0( GROUP, "MFE was preivously a negative mfe" ); // // get the protocol component owning the incoming interface // ppe = GetProtocolEntry( &ig.mllProtocolList.leHead, pse-> dwInProtocolId, pse-> dwInComponentId ); if ( ppe == NULL ) { TRACE2( ANY, "AddInterfaceToSourceMfe : cannot find protocol component :" " %x, %x", pse-> dwInProtocolId, pse-> dwInComponentId ); LOGERR0( PROTOCOL_NOT_FOUND, ERROR_NOT_FOUND ); break; } // // invoke the new member alert // if ( IS_JOIN_ALERT( ppe ) ) { JOIN_ALERT( ppe )( pse-> dwSourceAddr, pse-> dwSourceMask, pge-> dwGroupAddr, pge-> dwGroupMask, FALSE ); } } // // If a new interface was added to the OIL of the MFE && // if MFE is present in the forwarder, // update the forwarder entry // if ( !bFound && pse-> bInForwarder ) { AddMfeToForwarder( pge, pse, 0 ); } } while ( FALSE ); if ( ppoie != NULL ) { *ppoie = poieNew; } TRACEGROUP0( GROUP, "LEAVING AddInterfacetoSourceMfe" ); return; } //---------------------------------------------------------------------------- // DeleteInterfaceFromSource // // // This function deletes an interface from the outgoing interface list of a // (source, group) entry. For an (S, G) entry the corresponding mfe outgoing // interface list is also updated to reflect this deletion. For a (*, G) enry, // the mfe outgoing interface list for all source entries is updated, // and for a (*, *) entry mfes for all sources, for all groups are updated. //---------------------------------------------------------------------------- VOID DeleteInterfaceFromSourceEntry( PPROTOCOL_ENTRY ppe, DWORD dwGroupAddr, DWORD dwGroupMask, DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwIfIndex, DWORD dwIfNextHopAddr, BOOL bIGMP ) { DWORD dwGrpBucket, dwSrcBucket; BOOL bFound = FALSE, bNewComp = FALSE, bUpdateMfe = FALSE, bGrpLock = FALSE, bGrpEntryLock = FALSE; PPROTOCOL_ENTRY ppeEntry = NULL; PGROUP_ENTRY pge = NULL; PSOURCE_ENTRY pse = NULL; POUT_IF_ENTRY poie = NULL; PLIST_ENTRY pleGrpList = NULL, pleSrcList = NULL, ple = NULL, pleProtocol = NULL; TRACEGROUP2( GROUP, "ENTERED DeleteInterfaceFromSourceEntry : Group %x, %x", dwGroupAddr, dwGroupMask ); TRACEGROUP2( GROUP, "Source : %x, %x", dwSourceAddr, dwSourceMask ); TRACEGROUP2( GROUP, "Interface : %x, %x", dwIfIndex, dwIfNextHopAddr ); do { //-------------------------------------------------------------------- // Interface deletion from source entry //-------------------------------------------------------------------- // // Lock group bucket // dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); bGrpLock = TRUE; // // Find group entry // pleGrpList = GROUP_BUCKET_HEAD( dwGrpBucket ); bFound = FindGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pge, TRUE ); if ( !bFound ) { break; } ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = TRUE; // // Found group entry, find source entry // dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask ); pleSrcList = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); bFound = FindSourceEntry( pleSrcList, dwSourceAddr, dwSourceMask, &pse, TRUE ); if ( !bFound ) { break; } // // Found source entry, find interface entry in the // outgoing list // bFound = FindOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, &bNewComp, &poie ); if ( !bFound ) { // // Interface not found in OIL. Check if this interface // has a scoped boundary for this group. If so delete it // from the scoped list and quit. // bFound = FindOutInterfaceEntry( &pse-> leScopedIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, &bNewComp, &poie ); if ( bFound ) { // // clear appropriate counts/flags on the interface // TRACEGROUP0( GROUP, "Scoped interface" ); if ( bIGMP ) { poie-> wNumAddsByIGMP = 0; CLEAR_ADDED_BY_IGMP( poie ); } else { poie-> wNumAddsByRP = 0; CLEAR_ADDED_BY_PROTOCOL( poie ); } // // Delete this interface if counts are zero // if ( !IS_ADDED_BY_IGMP( poie ) && !IS_ADDED_BY_PROTOCOL( poie ) ) { TRACEGROUP0( GROUP, "Scoped interface deleted" ); DeleteOutInterfaceEntry( poie ); poie = NULL; // // Decrement OIF count. If count is 0, and this // source is not an MFE, delete the source entry // pse-> dwOutIfCount--; if ( ( pse-> dwOutIfCount == 0 ) && !IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { DeleteSourceEntry( pse ); pse = NULL; pge-> dwSourceCount--; } // // if there are no more sources for this group, remove // group entry // if ( pge-> dwSourceCount == 0 ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = FALSE; DeleteGroupEntry( pge ); pge = NULL; } } } break; } // // Outgoing interface found. decrement ref counts. // if ( bIGMP && IS_ADDED_BY_IGMP( poie ) ) { poie-> wNumAddsByIGMP = 0; CLEAR_ADDED_BY_IGMP( poie ); bUpdateMfe = TRUE; if ( IS_LOCAL_LEAVE_ALERT( ppe ) ) { LOCAL_LEAVE_ALERT( ppe )( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwIfIndex, dwIfNextHopAddr ); } } else if ( !bIGMP && IS_ADDED_BY_PROTOCOL( poie ) ) { poie-> wNumAddsByRP = 0; CLEAR_ADDED_BY_PROTOCOL( poie ); bUpdateMfe = TRUE; } } while( FALSE ); // // if interface was not found in the outgoing interface list // of specified (source, group) entry OR // No interface was deleted // return right here. // if ( !bFound || !bUpdateMfe ) { if ( bGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } if ( bGrpLock ) { RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); } return; } do { // // if no more reference to this interface entry, delete it. // if ( !IS_ADDED_BY_IGMP( poie ) && !IS_ADDED_BY_PROTOCOL( poie ) ) { DeleteOutInterfaceEntry( poie ); poie = NULL; // // Update interface and component counts // pse-> dwOutIfCount--; // // check if this interface deletion has resulted in decreasing // the number of protocol components that have added interfaces // to the OIL. // // To do this try to find the interface we just deleted again, in // the OIL and see if bNewComp is set to TRUE. // // if bNewComp == TRUE, then the interface just deleted was // the last interface in the OIL for the protocol component. // bNewComp = FALSE; FindOutInterfaceEntry( &pse-> leOutIfList, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, &bNewComp, &poie ); if ( bNewComp ) { pse-> dwOutCompCount--; InvokePruneAlertCallbacks( pge, pse, dwIfIndex, dwIfNextHopAddr, ppe ); } } //-------------------------------------------------------------------- // source/group entry deletion //-------------------------------------------------------------------- // // If there are no more interfaces in the OIL and this source // is not an MFE, the source entry can be deleted // if ( ( pse-> dwOutIfCount == 0 ) && !IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { DeleteSourceEntry( pse ); pse = NULL; pge-> dwSourceCount--; } // // if there are no more sources for this group, remove group entry // if ( pge-> dwSourceCount == 0 ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); DeleteGroupEntry( pge ); pge = NULL; } //-------------------------------------------------------------------- // MFE update //-------------------------------------------------------------------- if ( IS_WILDCARD_GROUP( dwGroupAddr, dwGroupMask ) ) { // // (*, *) entry // if ( pge != NULL ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); DeleteInterfaceFromAllMfe( dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP ); } else if ( IS_WILDCARD_SOURCE( dwSourceAddr, dwSourceMask ) ) { // // (*, G) entry // if ( pge != NULL ) { DeleteInterfaceFromGroupMfe( pge, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP ); RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); } else { // // (S, G) entry. // // // Does this (S, G) entry have a corresponding MFE ? // Check to see if it has a valid incoming interface // if ( pse != NULL && IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { DeleteInterfaceFromSourceMfe( pge, pse, dwIfIndex, dwIfNextHopAddr, ppe-> dwProtocolId, ppe-> dwComponentId, bIGMP, FALSE ); } if ( pge != NULL ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); } } while ( FALSE ); TRACEGROUP0( GROUP, "LEAVING DeleteInterfaceFromSourceEntry" ); return; } //---------------------------------------------------------------------------- // DeleteInterfaceFromAllMfe // // This function is invoked when an interface is deleted from the outgoing // list of a (*, *) entry. It walks the entire group table and updates // every mfe for every source to reflect the deletion of this interface. //---------------------------------------------------------------------------- VOID DeleteInterfaceFromAllMfe( DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, BOOL bIGMP ) { DWORD dwInd; PGROUP_ENTRY pge = NULL; PLIST_ENTRY ple = NULL; TRACEGROUP2( GROUP, "ENTERED DeleteInterfaceFromAllMfe : %x, %x", dwIfIndex, dwIfNextHopAddr ); // // for each group bucket // for ( dwInd = 0; dwInd < GROUP_TABLE_SIZE; dwInd++ ) { // // for each group // ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwInd ); for ( ple = ig.pmllGrpHashTable[ dwInd ].leHead.Flink; ple != &ig.pmllGrpHashTable[ dwInd ].leHead; ple = ple-> Flink ) { pge = CONTAINING_RECORD( ple, GROUP_ENTRY, leGrpHashList ); ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); DeleteInterfaceFromGroupMfe( pge, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP ); RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwInd ); } TRACEGROUP0( GROUP, "LEAVING DeleteInterfaceFromAllMfe" ); } //---------------------------------------------------------------------------- // DeleteInterfaceFromGroupMfe // // This function is invoked when an interface is deleted from the outgoing // list of a (*, G) or (*, *) entry. It walks all the sources for a group // and updates every mfe to reflect the deletion of this interface. //---------------------------------------------------------------------------- VOID DeleteInterfaceFromGroupMfe( PGROUP_ENTRY pge, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, BOOL bIGMP ) { DWORD dwInd = 0; PLIST_ENTRY ple = NULL; PSOURCE_ENTRY pse = NULL; TRACEGROUP2( GROUP, "ENTERED DeleteInterfaceFromGroupMfe : Group : %x, %x", pge-> dwGroupAddr, pge-> dwGroupMask ); // // for each bucket // for ( dwInd = 0; dwInd < SOURCE_TABLE_SIZE; dwInd++ ) { // // for each source entry. // for ( ple = pge-> pleSrcHashTable[ dwInd ].Flink; ple != &pge-> pleSrcHashTable[ dwInd ]; ple = ple-> Flink ) { pse = CONTAINING_RECORD( ple, SOURCE_ENTRY, leSrcHashList ); if ( !IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { continue; } DeleteInterfaceFromSourceMfe( pge, pse, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, bIGMP, FALSE ); } } TRACEGROUP0( GROUP, "LEAVING DeleteInterfaceFromGroupMfe" ); } //---------------------------------------------------------------------------- // DeleteInterfaceFromSourceMfe // // This function deletes an interface from the mfe outgoing list //---------------------------------------------------------------------------- VOID DeleteInterfaceFromSourceMfe( PGROUP_ENTRY pge, PSOURCE_ENTRY pse, DWORD dwIfIndex, DWORD dwIfNextHopAddr, DWORD dwProtocolId, DWORD dwComponentId, BOOL bIGMP, BOOL bDel ) { BOOL bFound, bNewComp, bUpdateForwarder = FALSE; DWORD dwTimeOut = 0, dwTimerQ; POUT_IF_ENTRY poie = NULL; PPROTOCOL_ENTRY ppe = NULL; TRACEGROUP4( GROUP, "ENTERED DeleteInterfaceFromSourceMfe : Source %x, %x" "Interface %x, %x", pse-> dwSourceAddr, pse-> dwSourceMask, dwIfIndex, dwIfNextHopAddr ); // // delete interface from the mfe outgoing interface list // bFound = FindOutInterfaceEntry( &pse-> leMfeIfList, dwIfIndex, dwIfNextHopAddr, dwProtocolId, dwComponentId, &bNewComp, &poie ); if ( bFound ) { // // decrement the reference counts // if ( bIGMP && IS_ADDED_BY_IGMP( poie ) ) { poie-> wNumAddsByIGMP--; if ( poie-> wNumAddsByIGMP == 0 ) { CLEAR_ADDED_BY_IGMP( poie ); } } else if ( !bIGMP && IS_ADDED_BY_PROTOCOL( poie ) ) { poie-> wNumAddsByRP--; if ( poie-> wNumAddsByRP == 0 ) { CLEAR_ADDED_BY_PROTOCOL( poie ); } } // // This interface is not required by either IGMP or the // routing protocol on the interface, delete it // if ( bDel || ( !IS_ADDED_BY_IGMP( poie ) && !IS_ADDED_BY_PROTOCOL( poie ) ) ) { DeleteOutInterfaceEntry( poie ); poie = NULL; bUpdateForwarder = pse-> bInForwarder; pse-> dwMfeIfCount--; } //-------------------------------------------------------------------- // NEGATIVE mfe check // if mfe out interface list is empty //-------------------------------------------------------------------- if ( IsListEmpty( &pse-> leMfeIfList ) ) { TRACEGROUP0( GROUP, "MFE OIL is empty ==> Negative Mfe" ); // // Invoke delete member callback for component that // owns the incoming interface. // ppe = GetProtocolEntry( &ig.mllProtocolList.leHead, pse-> dwInProtocolId, pse-> dwInComponentId ); if ( ppe == NULL ) { TRACE2( ANY, "DeleteInterfaceFromSourceMfe : Protocol not found" "%x, %x", pse-> dwInProtocolId, pse-> dwInComponentId ); } else if ( IS_PRUNE_ALERT( ppe ) ) { PRUNE_ALERT( ppe ) ( pse-> dwSourceAddr, pse-> dwSourceMask, pge-> dwGroupAddr, pge-> dwGroupMask, pse-> dwInIfIndex, pse-> dwInIfNextHopAddr, FALSE, &dwTimeOut ); // // Reset the timerout value for this MFE to reflect // the timer value for the negative MFE // dwTimerQ = TIMER_TABLE_HASH( pge-> dwGroupAddr ); RtlUpdateTimer( TIMER_QUEUE_HANDLE( dwTimerQ ), pse-> hTimer, dwTimeOut, 0 ); } } //-------------------------------------------------------------------- // Forwarder update //-------------------------------------------------------------------- if ( bUpdateForwarder ) { // // router manager callback to set updated mfe to forwarder // AddMfeToForwarder( pge, pse, dwTimeOut ); } } TRACEGROUP0( GROUP, "LEAVING DeleteInterfaceFromSourceMfe" ); } //---------------------------------------------------------------------------- // LookupAndDeleteYourMfe // // //---------------------------------------------------------------------------- VOID LookupAndDeleteYourMfe( DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, BOOL bDeleteTimer, PDWORD pdwInIfIndex OPTIONAL, PDWORD pdwInIfNextHopAddr OPTIONAL ) { BOOL bGrpEntryLock = FALSE; DWORD dwGrpBucket, dwSrcBucket, dwTimerQ; PLIST_ENTRY pleBucket = NULL; PGROUP_ENTRY pge = NULL; PSOURCE_ENTRY pse = NULL; TRACEGROUP4( GROUP, "ENTERED LookupAndDeleteYourMfe : " "Group %x, %x, Source %x, %x", dwGroupAddr, dwGroupMask, dwSourceAddr, dwSourceMask ); do { // // lock group bucket // dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); pleBucket = GROUP_BUCKET_HEAD( dwGrpBucket ); // // get group entry // pge = GetGroupEntry( pleBucket, dwGroupAddr, dwGroupMask ); if ( pge == NULL ) { TRACE2( ANY, "LookupAndDeleteYourMfe : Could not find group entry" "%x, %x", dwGroupAddr, dwGroupMask ); break; } // // get source entry // ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = TRUE; dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask ); pleBucket = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); pse = GetSourceEntry( pleBucket, dwSourceAddr, dwSourceMask ); if ( pse == NULL ) { TRACE2( ANY, "LookupAndDeleteYourMfe : Could not find source entry" "%x, %x", dwGroupAddr, dwGroupMask ); break; } // // save in i/f index/nhop addr if required // if ( pdwInIfIndex != NULL ) { *pdwInIfIndex = pse-> dwInIfIndex; } if ( pdwInIfIndex != NULL ) { *pdwInIfNextHopAddr = pse-> dwInIfNextHopAddr; } // // remove Mfe // DeleteMfe( pge, pse ); // // Cancel the expiry timer for the MFE is required // if ( bDeleteTimer && ( pse-> hTimer != NULL ) ) { dwTimerQ = TIMER_TABLE_HASH( dwGroupAddr ); RtlDeleteTimer( TIMER_QUEUE_HANDLE( dwTimerQ ), pse-> hTimer, NULL ); pse-> hTimer = NULL; } // // if there are no source specific joins for this source, // the this source entry is no longer required. // if ( IsListEmpty( &pse-> leOutIfList ) ) { DeleteSourceEntry( pse ); pge-> dwSourceCount--; } // // if there are no sources remaining for this group // delete the group entry // if ( pge-> dwSourceCount == 0 ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = FALSE; DeleteGroupEntry( pge ); } } while ( FALSE ); if ( bGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); TRACEGROUP0( GROUP, "LEAVING LookupAndDeleteYourMfe" ); } //---------------------------------------------------------------------------- // DeleteMfe // // //---------------------------------------------------------------------------- VOID DeleteMfe( PGROUP_ENTRY pge, PSOURCE_ENTRY pse ) { PLIST_ENTRY ple = NULL; POUT_IF_ENTRY poie = NULL; // // Delete all outgoing interfaces from the MFE outgoing list // while ( !IsListEmpty( &pse-> leMfeIfList ) ) { ple = RemoveHeadList( &pse-> leMfeIfList ); poie = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList ); DeleteOutInterfaceEntry( poie ); } // // reset incoming interface and protocol component // pse-> dwInIfIndex = INVALID_INTERFACE_INDEX; pse-> dwInIfNextHopAddr = INVALID_NEXT_HOP_ADDR; pse-> dwInProtocolId = INVALID_PROTOCOL_ID; pse-> dwInComponentId = INVALID_COMPONENT_ID; // // Update mfe // if ( pse-> bInForwarder ) { DeleteMfeFromForwarder( pge, pse ); } } //---------------------------------------------------------------------------- // AddToGroupList // // //---------------------------------------------------------------------------- DWORD AddToGroupList( PGROUP_ENTRY pge ) { DWORD dwErr = NO_ERROR; PGROUP_ENTRY pgeNext = NULL; PLIST_ENTRY pleTempGrpList = NULL; TRACEGROUP2( GROUP, "ENTERED AddToGroupList : %x, %x", pge-> dwGroupAddr, pge-> dwGroupMask ); // // Lock Temp List // ACQUIRE_TEMP_GROUP_LOCK_EXCLUSIVE(); do { // // Find appropriate place to insert new entry. // pleTempGrpList = TEMP_GROUP_LIST_HEAD(); if ( FindGroupEntry( pleTempGrpList, pge-> dwGroupAddr, pge-> dwGroupMask, &pgeNext, FALSE ) ) { dwErr = ERROR_ALREADY_EXISTS; TRACE2( GROUP, "AddToGroupList Group Entry already exists for : %x, %x", pge-> dwGroupAddr, pge-> dwGroupMask ); break; } // // Insert new group entry into temp list // if ( pgeNext != NULL ) { InsertTailList( &pgeNext-> leGrpList, &pge-> leGrpList ); } else { InsertTailList( pleTempGrpList, &pge-> leGrpList ); } ig.dwNumTempEntries++; // // if temp list size exceeds thresholds // - merge temp list with master group list // if ( ig.dwNumTempEntries > TEMP_GROUP_LIST_MAXSIZE ) { MergeTempAndMasterGroupLists( pleTempGrpList ); } } while ( FALSE ); // // Unlock temp list // RELEASE_TEMP_GROUP_LOCK_EXCLUSIVE(); TRACEGROUP1( GROUP, "LEAVING AddToGroupList %d", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // MergeWithMasterGroupList // // Assumes the temp list is exclusively locked //---------------------------------------------------------------------------- VOID MergeTempAndMasterGroupLists( PLIST_ENTRY pleTempList ) { PLIST_ENTRY pleMasterHead = NULL, pleMaster = NULL, pleTempHead = NULL; PGROUP_ENTRY pgeMaster = NULL, pgeTemp = NULL; INT iCmp; TRACEGROUP0( GROUP, "ENTERED MergeTempAndMasterGroupLists" ); // // Lock Master Group List // ACQUIRE_MASTER_GROUP_LOCK_EXCLUSIVE(); do { // // Merge temp list // if ( IsListEmpty( pleTempList ) ) { break; } pleMasterHead = MASTER_GROUP_LIST_HEAD(); pleMaster = pleMasterHead-> Flink; // // for each entry in the temp list // while ( !IsListEmpty( pleTempList ) ) { // // Remove entry from the temp list // pleTempHead = RemoveHeadList( pleTempList ); // // Insert entry from temp list into the master list // pgeTemp = CONTAINING_RECORD( pleTempHead, GROUP_ENTRY, leGrpList ); // // find its location in the master list // if ( IsListEmpty( pleMasterHead ) ) { // // first element in master list, insert w/o searching // InsertTailList( pleMasterHead, pleTempHead ); pleMaster = pleMasterHead-> Flink; continue; } // // At least one element present in the Master list // while ( pleMaster != pleMasterHead ) { pgeMaster = CONTAINING_RECORD( pleMaster, GROUP_ENTRY, leGrpList ); if ( INET_CMP( pgeTemp-> dwGroupAddr, pgeMaster-> dwGroupAddr, iCmp ) < 0 ) { break; } pleMaster = pleMaster-> Flink; } InsertTailList( pleMaster, pleTempHead ); } ig.dwNumTempEntries = 0; } while ( FALSE ); // // Unlock master list // RELEASE_MASTER_GROUP_LOCK_EXCLUSIVE(); TRACEGROUP0( GROUP, "LEAVING MergeTempAndMasterGroupLists" ); } //---------------------------------------------------------------------------- // AddToSourceList // // Assumes the group entry is exclusively locked //---------------------------------------------------------------------------- DWORD AddToSourceList( PGROUP_ENTRY pge, PSOURCE_ENTRY pse ) { DWORD dwErr = NO_ERROR; PLIST_ENTRY pleTempSrcList; PSOURCE_ENTRY pseTemp = NULL; TRACEGROUP2( GROUP, "ENTERED AddToSourceList : %x, %x", pse-> dwSourceAddr, pse-> dwSourceMask ); do { // // Insert source entry into temp list // pleTempSrcList = TEMP_SOURCE_LIST_HEAD( pge ); if ( FindSourceEntry( pleTempSrcList, pse-> dwSourceAddr, pse-> dwSourceMask, &pseTemp, FALSE ) ) { dwErr = ERROR_ALREADY_EXISTS; TRACE2( GROUP, "AddToGroupList Source Entry already exists for : %x, %x", pse-> dwSourceAddr, pse-> dwSourceMask ); break; } if ( pseTemp != NULL ) { InsertTailList( &pseTemp-> leSrcList, &pse-> leSrcList ); } else { InsertTailList( &pge-> leTempSrcList, &pse-> leSrcList ); } // // if temp source list size if larger than the threshold // pge-> dwNumTempEntries++; if ( pge-> dwNumTempEntries > TEMP_SOURCE_LIST_MAXSIZE ) { MergeTempAndMasterSourceLists( pge ); } } while ( FALSE ); TRACEGROUP1( GROUP, "LEAVING AddToSourceList : %d", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // MergeWithMasterSourceList // // Assumes the group entry is exclusively locked //---------------------------------------------------------------------------- VOID MergeTempAndMasterSourceLists( PGROUP_ENTRY pge ) { INT iCmp; PSOURCE_ENTRY pseTemp = NULL, pseMaster = NULL; PLIST_ENTRY pleTemp, pleSrcHead, pleSrc, pleHead; TRACEGROUP2( GROUP, "ENTERED MergeWithMasterSourceList : %x, %x", pge-> dwGroupAddr, pge-> dwGroupMask ); do { // // if temp list is entry, quit. // pleTemp = TEMP_SOURCE_LIST_HEAD( pge ); if ( pge-> dwNumTempEntries == 0 ) { break; } // // Remove each entry from the temp list and // insert it into the master list in order // pleSrcHead = MASTER_SOURCE_LIST_HEAD( pge ); pleSrc = pleSrcHead-> Flink; while ( !IsListEmpty( pleTemp ) ) { pleHead = RemoveHeadList( pleTemp ); pseTemp = CONTAINING_RECORD( pleHead, SOURCE_ENTRY, leSrcList ); if ( IsListEmpty( pleSrcHead ) ) { // // first element in source master list // InsertTailList( pleSrcHead, pleHead ); pleSrc = pleSrcHead-> Flink; continue; } // // at least one source present in master source list // while ( pleSrc != pleSrcHead ) { pseMaster = CONTAINING_RECORD( pleSrc, SOURCE_ENTRY, leSrcList ); if ( INET_CMP( pseTemp-> dwSourceAddr, pseMaster-> dwSourceAddr, iCmp ) < 0 ) { break; } pleSrc = pleSrc-> Flink; } InsertTailList( pleSrc, pleHead ); } pge-> dwNumTempEntries = 0; } while ( TRUE ); TRACEGROUP0( GROUP, "LEAVING MergeWithMasterSourceList" ); }