You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3195 lines
80 KiB
3195 lines
80 KiB
//============================================================================
|
|
// 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" );
|
|
}
|
|
|