windows-nt/Source/XPSP1/NT/net/rras/mgm/group.c
2020-09-26 16:20:57 +08:00

3196 lines
77 KiB
C

//============================================================================
// 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" );
}