//============================================================================ // Copyright (c) 1995, Microsoft Corporation // // File: enum.c // // History: // V Raman June-25-1997 Created. // // Enumeration functions exported to IP Router Manager. //============================================================================ #include "pchmgm.h" #pragma hdrstop DWORD GetGroupMfes( IN PGROUP_ENTRY pge, IN DWORD dwStartSource, IN OUT PBYTE pbBuffer, IN DWORD dwBufferSize, IN OUT PDWORD pdwSize, IN OUT PDWORD pdwNumEntries, IN BOOL bIncludeFirst, IN DWORD dwFlags ); VOID CopyMfe( IN PGROUP_ENTRY pge, IN PSOURCE_ENTRY pse, IN OUT PBYTE pb, IN DWORD dwFlags ); // // MFE enumeration // //---------------------------------------------------------------------------- // GetNextMfe // //---------------------------------------------------------------------------- DWORD GetMfe( IN PMIB_IPMCAST_MFE pmimm, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN DWORD dwFlags ) { BOOL bGrpLock = FALSE, bGrpEntryLock = FALSE; DWORD dwErr = NO_ERROR, dwGrpBucket, dwSrcBucket, dwSizeReqd, dwInd; PGROUP_ENTRY pge; PSOURCE_ENTRY pse; POUT_IF_ENTRY poie; PLIST_ENTRY ple, pleHead; TRACEENUM3( ENUM, "ENTERED GetMfe : %x, %x, Stats : %x", pmimm-> dwGroup, pmimm-> dwSource, dwFlags ); do { // // Find group entry // dwGrpBucket = GROUP_TABLE_HASH( pmimm-> dwGroup, 0 ); ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); bGrpLock = TRUE; pleHead = GROUP_BUCKET_HEAD( dwGrpBucket ); pge = GetGroupEntry( pleHead, pmimm-> dwGroup, 0 ); if ( pge == NULL ) { // // group entry not found, quit // dwErr = ERROR_NOT_FOUND; break; } // // acquire group entry lock and release group bucket lock // ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = TRUE; RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); bGrpLock = FALSE; // // Find Source entry // dwSrcBucket = SOURCE_TABLE_HASH( pmimm-> dwSource, pmimm-> dwSrcMask ); pleHead = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); pse = GetSourceEntry( pleHead, pmimm-> dwSource, pmimm-> dwSrcMask ); if ( pse == NULL ) { // // Source entry not found, quit // dwErr = ERROR_NOT_FOUND; break; } // // check buffersize requirements // dwSizeReqd = ( dwFlags ) ? ( (dwFlags == MGM_MFE_STATS_0) ? SIZEOF_MIB_MFE_STATS( pse-> dwMfeIfCount ) : SIZEOF_MIB_MFE_STATS_EX( pse-> dwMfeIfCount ) ) : SIZEOF_MIB_MFE( pse-> dwMfeIfCount ); if ( *pdwBufferSize < dwSizeReqd ) { // // buffer supplied is too small to fit the MFE // *pdwBufferSize = dwSizeReqd; dwErr = ERROR_INSUFFICIENT_BUFFER; break; } // // if mfe statistics have been requested and // mfe is in the kernel // get it // if ( dwFlags && pse-> bInForwarder ) { GetMfeFromForwarder( pge, pse ); } #if 1 CopyMfe( pge, pse, pbBuffer, dwFlags ); #else // // copy base MFE into user supplied buffer // pmimms = ( PMIB_IPMCAST_MFE_STATS ) pbBuffer; pmimms-> dwGroup = pge-> dwGroupAddr; pmimms-> dwSource = pse-> dwSourceAddr; pmimms-> dwSrcMask = pse-> dwSourceMask; pmimms-> dwInIfIndex = pse-> dwInIfIndex; pmimms-> dwUpStrmNgbr = pse-> dwUpstreamNeighbor; pmimms-> dwInIfProtocol = pse-> dwInProtocolId; pmimms-> dwRouteProtocol = pse-> dwRouteProtocol; pmimms-> dwRouteNetwork = pse-> dwRouteNetwork; pmimms-> dwRouteMask = pse-> dwRouteMask; pmimms-> ulNumOutIf = pse-> imsStatistics.ulNumOutIf; pmimms-> ulInPkts = pse-> imsStatistics.ulInPkts; pmimms-> ulInOctets = pse-> imsStatistics.ulInOctets; pmimms-> ulPktsDifferentIf = pse-> imsStatistics.ulPktsDifferentIf; pmimms-> ulQueueOverflow = pse-> imsStatistics.ulQueueOverflow; MgmElapsedSecs( &pse-> liCreationTime, &pmimms-> ulUpTime ); pmimms-> ulExpiryTime = pse-> dwTimeOut - pmimms-> ulUpTime; // // copy all the OIL entries // pleHead = &pse-> leMfeIfList; for ( ple = pleHead-> Flink, dwInd = 0; ple != pleHead; ple = ple-> Flink, dwInd++ ) { poie = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList ); pmimms-> rgmiosOutStats[ dwInd ].dwOutIfIndex = poie-> imosIfStats.dwOutIfIndex; pmimms-> rgmiosOutStats[ dwInd ].dwNextHopAddr = poie-> imosIfStats.dwNextHopAddr; pmimms-> rgmiosOutStats[ dwInd ].ulTtlTooLow = poie-> imosIfStats.ulTtlTooLow; pmimms-> rgmiosOutStats[ dwInd ].ulFragNeeded = poie-> imosIfStats.ulFragNeeded; pmimms-> rgmiosOutStats[ dwInd ].ulOutPackets = poie-> imosIfStats.ulOutPackets; pmimms-> rgmiosOutStats[ dwInd ].ulOutDiscards = poie-> imosIfStats.ulOutDiscards; } #endif } while ( FALSE ); // // release locks are appropriate // if ( bGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } if ( bGrpLock ) { RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); } TRACEENUM1( ENUM, "LEAVING GetMfe :: %x", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // GetNextMfe // //---------------------------------------------------------------------------- DWORD GetNextMfe( IN PMIB_IPMCAST_MFE pmimmStart, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries, IN BOOL bIncludeFirst, IN DWORD dwFlags ) { BOOL bFound, bgeLock = FALSE; DWORD dwGrpBucket, dwErr = NO_ERROR, dwBufferLeft, dwStartSource, dwSize; PBYTE pbStart; PGROUP_ENTRY pge; PLIST_ENTRY ple, pleMasterHead, pleGrpBucket; TRACEENUM2( ENUM, "ENTERED GetNextMfe (G, S) = (%x, %x)", pmimmStart-> dwGroup, pmimmStart-> dwSource ); do { // // 1. Lock group hash bucket. // dwGrpBucket = GROUP_TABLE_HASH( pmimmStart-> dwGroup, 0 ); ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); // // 2. merge temp and master lists // - Lock temp list // - merge temp with master list // - unlock temp list // ACQUIRE_TEMP_GROUP_LOCK_EXCLUSIVE(); MergeTempAndMasterGroupLists( TEMP_GROUP_LIST_HEAD() ); ACQUIRE_MASTER_GROUP_LOCK_SHARED(); RELEASE_TEMP_GROUP_LOCK_EXCLUSIVE(); pleMasterHead = MASTER_GROUP_LIST_HEAD(); ple = pleMasterHead-> Flink; // // To retrieve the next set of group entries in lexicographic order, // given a group entry (in this case specified by pmimmStart-> dwGroup) // the master group list must be walked from the head until either // the group entry specified is found or the next "higher" group entry // is found. This is expensive. // // As an optimization the group specified (pmimmStart-> dwGroup) is // looked up in the group hash table. If an entry is found, then the // group entry contains links into the master (lexicographic) group // list. These links can the used to determine the next entries in // the group list. This way we can quickly find an group entry in // the master list rather than walk the master group list from the // beginning. // // It should be noted that in case the group entry specified in not // present in the group hash table, it will be necessary to walk the // master group list from the start. // // Each group entry is present in two lists, the hash bucket list // and either temp group list or the master group list. // // For this optimization to "work", it must be ensured that an entry // present in the hash table is also present in the master // group list. To ensure this the temp group list is merged into // the master group list before searching the group hash table for // the specified entry. // // // At this point the group under consideration (pmimmStart-> dwGroup), // cannot be added to either the hash bucket or master group list // if it is not already present because both the group hash bucket lock // and the master list lock have been acquired. // // // 3. find group entry in the hash list // pleGrpBucket = GROUP_BUCKET_HEAD( dwGrpBucket ); pge = GetGroupEntry( pleGrpBucket, pmimmStart-> dwGroup, 0 ); if ( pge != NULL ) { // // group entry for pmimmStart-> dwGroup is present. lock the entry. // ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bgeLock = TRUE; // // release group hash bucket lock // RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); } else { // // group entry is not present in the hash table, which implies // that the group entry is not present at all. // // // release group hash bucket lock // RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); // // 3.1 Walk master list from the start to determine the next // highest group entry. // bFound = FindGroupEntry( pleMasterHead, pmimmStart-> dwGroup, 0, &pge, FALSE ); if ( !bFound && pge == NULL ) { // // No more group entries left to enumerate // dwErr = ERROR_NO_MORE_ITEMS; RELEASE_MASTER_GROUP_LOCK_SHARED(); break; } // // Next group entry found. lock it // ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bgeLock = TRUE; bIncludeFirst = TRUE; } // // At this point we have the group entry we want which is // either the one for pmimmStart-> dwGroup OR the next higher // one (if there is no group entry for pmimmStart-> Group). // // // 4. Now get as many source entries as will fit into // the buffer provided. // dwBufferLeft = *pdwBufferSize; pbStart = pbBuffer; *pdwNumEntries = 0; dwStartSource = ( bIncludeFirst ) ? 0 : pmimmStart-> dwSource; dwSize = 0; while ( ( dwErr = GetGroupMfes( pge, dwStartSource, pbStart, dwBufferLeft, &dwSize, pdwNumEntries, bIncludeFirst, dwFlags ) ) == ERROR_MORE_DATA ) { // // more data items will fit into this buffer, but no more // source entries available in this group entry // // 4.1 Move forward to next group entry. // pbStart += dwSize; dwBufferLeft -= dwSize; dwSize = 0; dwStartSource = 0; // // 4.1.1 Release this group entry lock // RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); // // 4.1.2 get next entry lock // ple = pge-> leGrpList.Flink; if ( ple == pleMasterHead ) { // // No more group entries in the master group list. // All MFEs have been exhausted. So quit. // dwErr = ERROR_NO_MORE_ITEMS; bgeLock = FALSE; break; } pge = CONTAINING_RECORD( ple, GROUP_ENTRY, leGrpList ); ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); dwStartSource = 0; bIncludeFirst = TRUE; } // // 5. you have packed as much as possible into the buffer // // Clean up and return the correct error code. // if ( bgeLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); } if ( dwErr == ERROR_INSUFFICIENT_BUFFER ) { // // ran out of buffer. If there is at least one Mfe // packed into the buffer provided then it is ok. // if ( *pdwNumEntries != 0 ) { dwErr = ERROR_MORE_DATA; } else { // // not even one entry could be packed into the buffer // return the size required for this so that an // appropriately sized buffer can be allocated for the // next call. // *pdwBufferSize = dwSize; } } RELEASE_MASTER_GROUP_LOCK_SHARED(); } while ( FALSE ); TRACEENUM1( ENUM, "LEAVING GetNextMfe : %x", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // // GetGroupMfes // // Retrieves as many MFEs for a group starting at the specified source. // Assumes that the group entry is locked. //---------------------------------------------------------------------------- DWORD GetGroupMfes( IN PGROUP_ENTRY pge, IN DWORD dwStartSource, IN OUT PBYTE pbBuffer, IN DWORD dwBufferSize, IN OUT PDWORD pdwSize, IN OUT PDWORD pdwNumEntries, IN BOOL bIncludeFirst, IN DWORD dwFlags ) { BOOL bFound; DWORD dwErr = ERROR_MORE_DATA, dwSrcBucket, dwSizeReqd, dwInd; PSOURCE_ENTRY pse = NULL; PLIST_ENTRY pleMasterHead, pleSrcBucket, ple = NULL, pleSrc; POUT_IF_ENTRY poie = NULL; TRACEENUM2( ENUM, "ENTERED GetGroupMfes : %x, %x", pge-> dwGroupAddr, dwStartSource ); do { // // merge temp and group source lists // MergeTempAndMasterSourceLists( pge ); // // similar to the group lookup, optimize the source lookup // by first trying to find the source entry in the source // hash table. // // If found in the hash table then use the entry's links // the into master source list to find next entry. // // if not found in the hash table walk the master list from // the beginning to determine the next entry. // pleMasterHead = MASTER_SOURCE_LIST_HEAD( pge ); dwSrcBucket = SOURCE_TABLE_HASH( dwStartSource, 0 ); pleSrcBucket = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); bFound = FindSourceEntry( pleSrcBucket, dwStartSource, 0, &pse, TRUE ); if ( !bFound ) { // // source entry is not present in the hash table // Walk the master source list from the start. // pse = NULL; FindSourceEntry( pleMasterHead, 0, 0, &pse, FALSE ); // // No next entry found in the master list. Implies // no more sources in the master source list for this group. // if ( pse == NULL ) { break; } } else { // // Entry for starting source found in hash table. // Use its links into the master list to get next entry. // if ( !bIncludeFirst ) { ple = pse-> leSrcList.Flink; pse = CONTAINING_RECORD( ple, SOURCE_ENTRY, leSrcList ); } } // // At this point the entry pointed to by pse is the first entry // the needs to be packed into the buffer supplied. Starting // with this source entry keep packing MFEs into the // buffer till there are no more MFEs for this group. // pleSrc = &pse-> leSrcList; // // while there are source entries for this group entry // while ( pleSrc != pleMasterHead ) { pse = CONTAINING_RECORD( pleSrc, SOURCE_ENTRY, leSrcList ); // // Is this source entry an MFE // if ( !IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { pleSrc = pleSrc-> Flink; continue; } // // This source entry is an MFE also. // // // Check if enough space left in the buffer to fit this MFE. // // If not and not a single MFE is present in the buffer then // return the size required to fit this MFE. // dwSizeReqd = ( dwFlags ) ? ( ( dwFlags == MGM_MFE_STATS_0 ) ? SIZEOF_MIB_MFE_STATS( pse-> dwMfeIfCount ) : SIZEOF_MIB_MFE_STATS_EX( pse-> dwMfeIfCount ) ) : SIZEOF_MIB_MFE( pse-> dwMfeIfCount ); if ( dwBufferSize < dwSizeReqd ) { dwErr = ERROR_INSUFFICIENT_BUFFER; if ( *pdwNumEntries == 0 ) { *pdwSize = dwSizeReqd; } break; } // // If MFE stats have been requested and // MFE is present in the forwarder // get them. // if ( dwFlags && pse-> bInForwarder ) { // // MFE is currently in the forwarder. Query it and update // stats user mode. // GetMfeFromForwarder( pge, pse ); } // // copy base MFE into user supplied buffer // CopyMfe( pge, pse, pbBuffer, dwFlags ); pbBuffer += dwSizeReqd; dwBufferSize -= dwSizeReqd; *pdwSize += dwSizeReqd; (*pdwNumEntries)++; pleSrc = pleSrc-> Flink; } } while ( FALSE ); TRACEENUM2( ENUM, "LEAVING GetGroupsMfes : %d %d", *pdwNumEntries, dwErr ); return dwErr; } //============================================================================ // Group Enumeration // //============================================================================ PGROUP_ENUMERATOR VerifyEnumeratorHandle( IN HANDLE hEnum ) { DWORD dwErr; PGROUP_ENUMERATOR pgeEnum; pgeEnum = (PGROUP_ENUMERATOR) ( ( (ULONG_PTR) hEnum ) ^ (ULONG_PTR) MGM_ENUM_HANDLE_TAG ); try { if ( pgeEnum-> dwSignature != MGM_ENUM_SIGNATURE ) { dwErr = ERROR_INVALID_PARAMETER; TRACE0( ANY, "Invalid Enumeration handle" ); pgeEnum = NULL; } } except ( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { dwErr = ERROR_INVALID_PARAMETER; TRACE0( ANY, "Invalid enumeration handle" ); pgeEnum = NULL; } return pgeEnum; } // // Get Memberships for buckets // DWORD GetNextGroupMemberships( IN PGROUP_ENUMERATOR pgeEnum, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) { BOOL bIncludeFirst = TRUE, bFound; DWORD dwMaxEntries, dwGrpBucket, dwErr = ERROR_NO_MORE_ITEMS; PGROUP_ENTRY pge = NULL; PSOURCE_GROUP_ENTRY psge; PLIST_ENTRY ple, pleGrpHead; do { // // Compute the number of entries that will fit into the buffer // dwMaxEntries = (*pdwBufferSize) / sizeof( SOURCE_GROUP_ENTRY ); // // STEP I : // // // position the start of the GetNext to the group entry that was // the last enumerated by the previous GetNext operation // // // Find the last group entry retrieved by the previous get operation. // dwGrpBucket = GROUP_TABLE_HASH( pgeEnum-> dwLastGroup, pgeEnum-> dwLastGroupMask ); ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); pleGrpHead = GROUP_BUCKET_HEAD( dwGrpBucket ); bFound = FindGroupEntry( pleGrpHead, pgeEnum-> dwLastGroup, pgeEnum-> dwLastGroupMask, &pge, TRUE ); if ( bFound ) { // // group entry found // bIncludeFirst = !pgeEnum-> bEnumBegun; } // // last group entry retrieved by previous getnext is no // longer present // // // check if there are any more group entries present in // the same bucket // else if ( pge != NULL ) { // // Next group entry in the same group bucket. // For a new group start from the first source bucket, // first source entry. // pgeEnum-> dwLastSource = 0; pgeEnum-> dwLastSourceMask = 0; } else // ( pge == NULL ) { // // no more entries in this group bucket, move to next // non-empty group bucket entry. // // // skip empty buckets in the group hash table // do { RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); dwGrpBucket++; if ( dwGrpBucket >= GROUP_TABLE_SIZE ) { // // Entire hash table has been traversed, quit // break; } // // Move to next group bucket // ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); pleGrpHead = GROUP_BUCKET_HEAD( dwGrpBucket ); // // Check if any group entries present // if ( !IsListEmpty( pleGrpHead ) ) { // // group bucket has at least on group entry // pge = CONTAINING_RECORD( pleGrpHead-> Flink, GROUP_ENTRY, leGrpHashList ); // // For a new group start from the first source bucket, // first source entry. // pgeEnum-> dwLastSource = 0; pgeEnum-> dwLastSourceMask = 0; break; } // // Empty group bucket, move to next one // } while ( TRUE ); } // // if all hash buckets have been traversed, quit. // if ( dwGrpBucket >= GROUP_TABLE_SIZE ) { break; } // // STEP II: // // // start retrieving group membership entries // ple = &pge-> leGrpHashList; // // Walk each hash bucket starting from dwGrpBucket to GROUP_TABLE_SIZE // while ( dwGrpBucket < GROUP_TABLE_SIZE ) { // // For each group hash table bucket // while ( ple != pleGrpHead ) { // // For each group entry in the bucket // pge = CONTAINING_RECORD( ple, GROUP_ENTRY, leGrpHashList ); ACQUIRE_GROUP_ENTRY_LOCK_SHARED( pge ); dwErr = GetNextMembershipsForThisGroup( pge, pgeEnum, bIncludeFirst, pbBuffer, pdwNumEntries, dwMaxEntries ); RELEASE_GROUP_ENTRY_LOCK_SHARED( pge ); if ( dwErr == ERROR_MORE_DATA ) { // // User supplied buffer is full. // break; } // // Move to next entry // ple = ple-> Flink; // // Next group entry in the same group bucket. // For a new group start from the first source bucket, // first source entry. // pgeEnum-> dwLastSource = 0; pgeEnum-> dwLastSourceMask = 0; bIncludeFirst = TRUE; } RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); if ( dwErr == ERROR_MORE_DATA ) { break; } // // Move to next group bucket // dwGrpBucket++; // // skip empty group hash buckets // while ( dwGrpBucket < GROUP_TABLE_SIZE ) { ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket ); pleGrpHead = GROUP_BUCKET_HEAD( dwGrpBucket ); if ( !IsListEmpty( pleGrpHead ) ) { break; } RELEASE_GROUP_LOCK_SHARED( dwGrpBucket ); dwGrpBucket++; } if ( dwGrpBucket >= GROUP_TABLE_SIZE ) { // // All group buckets have traversed. End of enumeration // dwErr = ERROR_NO_MORE_ITEMS; } else { // // New group hash bucket, start from source entry 0. // ple = pleGrpHead-> Flink; pgeEnum-> dwLastSource = 0; pgeEnum-> dwLastSourceMask = 0; bIncludeFirst = TRUE; } } } while ( FALSE ); pgeEnum-> bEnumBegun = TRUE; // // Store the position where the enumeration ended // psge = (PSOURCE_GROUP_ENTRY) pbBuffer; if ( *pdwNumEntries ) { pgeEnum-> dwLastSource = psge[ *pdwNumEntries - 1 ].dwSourceAddr; pgeEnum-> dwLastSourceMask = psge[ *pdwNumEntries - 1 ].dwSourceMask; pgeEnum-> dwLastGroup = psge[ *pdwNumEntries - 1 ].dwGroupAddr; pgeEnum-> dwLastGroupMask = psge[ *pdwNumEntries - 1 ].dwGroupMask; } else { pgeEnum-> dwLastSource = 0xFFFFFFFF; pgeEnum-> dwLastSourceMask = 0xFFFFFFFF; pgeEnum-> dwLastGroup = 0xFFFFFFFF; pgeEnum-> dwLastGroupMask = 0xFFFFFFFF; } return dwErr; } //---------------------------------------------------------------------------- // GetMemberships for Group // //---------------------------------------------------------------------------- DWORD GetNextMembershipsForThisGroup( IN PGROUP_ENTRY pge, IN OUT PGROUP_ENUMERATOR pgeEnum, IN BOOL bIncludeFirst, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries, IN DWORD dwMaxEntries ) { BOOL bFound; DWORD dwErr = ERROR_NO_MORE_ITEMS, dwSrcBucket; PSOURCE_GROUP_ENTRY psgBuffer; PSOURCE_ENTRY pse = NULL; PLIST_ENTRY pleSrcHead, ple; do { if ( *pdwNumEntries >= dwMaxEntries ) { // // quit here. // dwErr = ERROR_MORE_DATA; break; } psgBuffer = (PSOURCE_GROUP_ENTRY) pbBuffer; // // STEP I: // Position start of enumeration // dwSrcBucket = SOURCE_TABLE_HASH( pgeEnum-> dwLastSource, pgeEnum-> dwLastSourceMask ); pleSrcHead = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); bFound = FindSourceEntry( pleSrcHead, pgeEnum-> dwLastSource, pgeEnum-> dwLastSourceMask, &pse, TRUE ); if ( bFound ) { if ( ( bIncludeFirst ) && !IsListEmpty( &pse-> leOutIfList ) ) { // // the first group membership found. // psgBuffer[ *pdwNumEntries ].dwSourceAddr = pse-> dwSourceAddr; psgBuffer[ *pdwNumEntries ].dwSourceMask = pse-> dwSourceMask; psgBuffer[ *pdwNumEntries ].dwGroupAddr = pge-> dwGroupAddr; psgBuffer[ (*pdwNumEntries)++ ].dwGroupMask = pge-> dwGroupMask; if ( *pdwNumEntries >= dwMaxEntries ) { // // buffer full. quit here. // dwErr = ERROR_MORE_DATA; break; } // // move to next source // ple = pse-> leSrcHashList.Flink; } else { ple = pse-> leSrcHashList.Flink; } } else if ( pse != NULL ) { ple = &pse-> leSrcHashList; } else { ple = pleSrcHead-> Flink; } // // STEP II: // // enumerate group memberships // while ( *pdwNumEntries < dwMaxEntries ) { // // for each source bucket // while ( ( ple != pleSrcHead ) && ( *pdwNumEntries < dwMaxEntries ) ) { // // for each source entry in the bucket // // // if group membership exists for this source // pse = CONTAINING_RECORD( ple, SOURCE_ENTRY, leSrcHashList ); if ( !IsListEmpty( &pse-> leOutIfList ) ) { psgBuffer[ *pdwNumEntries ].dwSourceAddr = pse-> dwSourceAddr; psgBuffer[ *pdwNumEntries ].dwSourceMask = pse-> dwSourceMask; psgBuffer[ *pdwNumEntries ].dwGroupAddr = pge-> dwGroupAddr; psgBuffer[ (*pdwNumEntries)++ ].dwGroupMask = pge-> dwGroupMask; if ( *pdwNumEntries >= dwMaxEntries ) { dwErr = ERROR_MORE_DATA; } } ple = ple-> Flink; } dwSrcBucket++; if ( dwSrcBucket < SOURCE_TABLE_SIZE ) { pleSrcHead = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); ple = pleSrcHead-> Flink; } else { // // all source buckets for this group have been // traversed. quit this group entry // break; } } } while ( FALSE ); return dwErr; } //---------------------------------------------------------------------------- // Copy the MFE (optionally with stats) // //---------------------------------------------------------------------------- VOID CopyMfe( IN PGROUP_ENTRY pge, IN PSOURCE_ENTRY pse, IN OUT PBYTE pb, IN DWORD dwFlags ) { DWORD dwInd; PLIST_ENTRY ple, pleHead; POUT_IF_ENTRY poie; PMIB_IPMCAST_MFE pmimm = NULL; PMIB_IPMCAST_MFE_STATS pmimms = NULL; PMIB_IPMCAST_OIF_STATS pmimos = NULL; // // copy base MFE into user supplied buffer // if ( dwFlags ) { // // Need to base MFE // pmimms = ( PMIB_IPMCAST_MFE_STATS ) pb; pmimms-> dwGroup = pge-> dwGroupAddr; pmimms-> dwSource = pse-> dwSourceAddr; pmimms-> dwSrcMask = pse-> dwSourceMask; pmimms-> dwInIfIndex = pse-> dwInIfIndex; pmimms-> dwUpStrmNgbr = pse-> dwUpstreamNeighbor; pmimms-> dwInIfProtocol = pse-> dwInProtocolId; pmimms-> dwRouteProtocol = pse-> dwRouteProtocol; pmimms-> dwRouteNetwork = pse-> dwRouteNetwork; pmimms-> dwRouteMask = pse-> dwRouteMask; MgmElapsedSecs( &pse-> liCreationTime, &pmimms-> ulUpTime ); pmimms-> ulExpiryTime = pse-> dwTimeOut - pmimms-> ulUpTime; // // Copy incoming stats // pmimms-> ulNumOutIf = pse-> dwMfeIfCount; pmimms-> ulInPkts = pse-> imsStatistics.ulInPkts; pmimms-> ulInOctets = pse-> imsStatistics.ulInOctets; pmimms-> ulPktsDifferentIf = pse-> imsStatistics.ulPktsDifferentIf; pmimms-> ulQueueOverflow = pse-> imsStatistics.ulQueueOverflow; if ( dwFlags & MGM_MFE_STATS_1 ) { PMIB_IPMCAST_MFE_STATS_EX pmimmsex = ( PMIB_IPMCAST_MFE_STATS_EX ) pb; pmimmsex-> ulUninitMfe = pse-> imsStatistics.ulUninitMfe; pmimmsex-> ulNegativeMfe = pse-> imsStatistics.ulNegativeMfe; pmimmsex-> ulInDiscards = pse-> imsStatistics.ulInDiscards; pmimmsex-> ulInHdrErrors = pse-> imsStatistics.ulInHdrErrors; pmimmsex-> ulTotalOutPackets= pse-> imsStatistics.ulTotalOutPackets; pmimos = pmimmsex-> rgmiosOutStats; } else { pmimos = pmimms-> rgmiosOutStats; } // // copy all the OIL entries // pleHead = &pse-> leMfeIfList; for ( ple = pleHead-> Flink, dwInd = 0; ple != pleHead; ple = ple-> Flink, dwInd++ ) { poie = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList ); pmimos[ dwInd ].dwOutIfIndex = poie-> dwIfIndex; pmimos[ dwInd ].dwNextHopAddr = poie-> dwIfNextHopAddr; // // Copy outgoing stats // pmimos[ dwInd ].ulTtlTooLow = poie-> imosIfStats.ulTtlTooLow; pmimos[ dwInd ].ulFragNeeded = poie-> imosIfStats.ulFragNeeded; pmimos[ dwInd ].ulOutPackets = poie-> imosIfStats.ulOutPackets; pmimos[ dwInd ].ulOutDiscards = poie-> imosIfStats.ulOutDiscards; } } else { // // Need to copy non-stats MFE structure only // pmimm = (PMIB_IPMCAST_MFE) pb; pmimm-> dwGroup = pge-> dwGroupAddr; pmimm-> dwSource = pse-> dwSourceAddr; pmimm-> dwSrcMask = pse-> dwSourceMask; pmimm-> dwInIfIndex = pse-> dwInIfIndex; pmimm-> dwUpStrmNgbr = pse-> dwUpstreamNeighbor; pmimm-> dwInIfProtocol = pse-> dwInProtocolId; pmimm-> dwRouteProtocol = pse-> dwRouteProtocol; pmimm-> dwRouteNetwork = pse-> dwRouteNetwork; pmimm-> dwRouteMask = pse-> dwRouteMask; pmimm-> ulNumOutIf = pse-> dwMfeIfCount; MgmElapsedSecs( &pse-> liCreationTime, &pmimm-> ulUpTime ); pmimm-> ulExpiryTime = pse-> dwTimeOut - pmimm-> ulUpTime; // // copy all the OIL entries minus the stats // pleHead = &pse-> leMfeIfList; for ( ple = pleHead-> Flink, dwInd = 0; ple != pleHead; ple = ple-> Flink, dwInd++ ) { poie = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList ); pmimm-> rgmioOutInfo[ dwInd ].dwOutIfIndex = poie-> dwIfIndex; pmimm-> rgmioOutInfo[ dwInd ].dwNextHopAddr = poie-> dwIfNextHopAddr; } } }