windows-nt/Source/XPSP1/NT/net/rras/mgm/scope.c

3207 lines
86 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//============================================================================
// Copyright (c) 1995, Microsoft Corporation
//
// File: scope.c
//
// History:
// V Raman June-25-1997 Created.
//
// Functions that deal with addition/deletion of scope-boundary.
//============================================================================
#include "pchmgm.h"
#pragma hdrstop
//
// prototypes for local functions
//
VOID
ScopeIfAndInvokeCallbacks(
PGROUP_ENTRY pge,
PSOURCE_ENTRY pse,
POUT_IF_ENTRY poie
);
VOID
UnScopeIfAndInvokeCallbacks(
PGROUP_ENTRY pge,
PSOURCE_ENTRY pse,
POUT_IF_ENTRY poie
);
PJOIN_ENTRY
GetNextJoinEntry(
);
BOOL
FindJoinEntry(
PLIST_ENTRY pleJoinList,
DWORD dwSourceAddr,
DWORD dwSourceMask,
DWORD dwGroupAddr,
DWORD dwGroupMask,
DWORD dwIfIndex,
DWORD dwIfNextHopAddr,
PJOIN_ENTRY * ppje
);
DWORD
APIENTRY
MgmBlockGroups(
IN DWORD dwFirstGroup,
IN DWORD dwLastGroup,
IN DWORD dwIfIndex,
IN DWORD dwIfNextHopAddr
)
/*++
Routine Description :
This function walks the Master group list and updates all group entries
that fall in the range specified by dwFirstGroup-dwLastGroup. It ensures
that the interface specified by dwifIndex is not present in the OIF list
of any MFE for groups in that range. In addition any memberships for
groups in the range on interface dwIfIndex are removed and added to the
scoped interface list for the corresponding group. The scoped i/f list
is maintained so that subsequent unblocking of a group is transparently
handled by MGM. The interfaces present in the scoped i/f list for
a group are automatically moved back to the OIF list when traffic for
that group is unblocked.
Arguements :
dwFirstGroup - Lower end of the range to be blocked
dwLastGroup - Upper end of the range to be blocked
dwIfIndex - Interface on which traffic is to be blocked
Return Value :
NO_ERROR - Success
Environment :
This routine is invoked by the IP RouterManager in response to setting
of a administrative scoped boundary on an interface.
--*/
{
INT iCmp;
DWORD dwIfBucket, dwTimeOut = 0;
BOOL bFound, bDeleteCallback, bNewComp = FALSE;
PIF_ENTRY pieIfEntry;
PPROTOCOL_ENTRY ppe;
PGROUP_ENTRY pge;
PSOURCE_ENTRY pse;
POUT_IF_ENTRY poie, poieTemp;
PLIST_ENTRY pleGrpHead, pleGrp, pleSrcHead, pleSrc, ple;
//
// Verify that MGM is up and running and update thread-count
//
if ( !ENTER_MGM_API() )
{
return ERROR_CAN_NOT_COMPLETE;
}
TRACE3(
SCOPE, "ENTERED MgmBlockGroups (%lx - %lx) on %lx",
dwFirstGroup, dwLastGroup, dwIfIndex
);
do
{
ACQUIRE_PROTOCOL_LOCK_SHARED();
//
// Verify that interface specified by dwIfIndex exists
//
dwIfBucket = IF_TABLE_HASH( dwIfIndex );
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
pieIfEntry = GetIfEntry(
IF_BUCKET_HEAD( dwIfBucket ), dwIfIndex,
dwIfNextHopAddr
);
if ( pieIfEntry == NULL )
{
TRACE1( SCOPE, "Interface %lx not found", dwIfIndex );
break;
}
//
// merge temp and master group lists
//
ACQUIRE_TEMP_GROUP_LOCK_EXCLUSIVE();
MergeTempAndMasterGroupLists( TEMP_GROUP_LIST_HEAD() );
RELEASE_TEMP_GROUP_LOCK_EXCLUSIVE();
//
// Lock master group list for reading
//
ACQUIRE_MASTER_GROUP_LOCK_SHARED();
for ( pleGrpHead = MASTER_GROUP_LIST_HEAD(),
pleGrp = pleGrpHead-> Flink;
pleGrp != pleGrpHead;
pleGrp = pleGrp-> Flink )
{
pge = CONTAINING_RECORD( pleGrp, GROUP_ENTRY, leGrpList );
//
// check if group is within range
//
if ( INET_CMP( pge-> dwGroupAddr, dwLastGroup, iCmp ) > 0 )
{
//
// The master group list is ordered by group number and
// the high end of the range to be blocked has been crossed.
//
break;
}
else if ( INET_CMP( pge-> dwGroupAddr, dwFirstGroup, iCmp ) < 0 )
{
//
// Skip group entries smaller than the lower end of the range
//
continue;
}
//
// Group Entry in range
//
//
// lock the entry and merge the temp and master source lists
//
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
MergeTempAndMasterSourceLists( pge );
//
// Walk the Master source list.
// For each source in the group
//
for ( pleSrcHead = MASTER_SOURCE_LIST_HEAD( pge ),
pleSrc = pleSrcHead-> Flink;
pleSrc != pleSrcHead;
pleSrc = pleSrc-> Flink )
{
pse = CONTAINING_RECORD( pleSrc, SOURCE_ENTRY, leSrcList );
//-----------------------------------------------------------
// Part 1 : Membership updates.
//-----------------------------------------------------------
//
// If there are any memberships for this group on this
// interface, move them to the scoped interface list
//
bFound = FindOutInterfaceEntry(
&pse-> leOutIfList, pieIfEntry-> dwIfIndex,
pieIfEntry-> dwIfNextHopAddr,
pieIfEntry-> dwOwningProtocol,
pieIfEntry-> dwOwningComponent, &bNewComp, &poie
);
if ( bFound )
{
//
// Move interface entry from OIF list to scoped list
// and invoke deletion alerts are per interop rules.
//
ScopeIfAndInvokeCallbacks( pge, pse, poie );
}
//-------------------------------------------------------
// Part 2 : MFE update.
//-------------------------------------------------------
bDeleteCallback = FALSE;
//
// Check if this source entry has an MFE.
//
if ( !IS_VALID_INTERFACE(
pse-> dwInIfIndex, pse-> dwInIfNextHopAddr
) )
{
//
// This source entry is not an MFE. No further
// processing required, Move to next source entry
//
continue;
}
//
// This source entry is also an MFE
//
//
// check if the boundary being added in on incoming
// interface. If so create negative MFE, and issue
// callbacks
//
if ( ( pse-> dwInIfIndex == pieIfEntry-> dwIfIndex ) &&
( pse-> dwInIfNextHopAddr ==
pieIfEntry-> dwIfNextHopAddr ) )
{
//
// Interface on which this group is to blocked is the
// incoming interface
//
//
// Check if this is already a negative MFE. If so
// nothing more to be done, move on to next source
// entry
//
if ( IsListEmpty( &pse-> leMfeIfList ) )
{
continue;
}
//
// Delete all the outgoing interfaces in the MFE OIF
// list
//
while ( !IsListEmpty( &pse-> leMfeIfList ) )
{
ple = RemoveHeadList( &pse-> leMfeIfList ) ;
poieTemp = CONTAINING_RECORD(
ple, OUT_IF_ENTRY, leIfList
);
MGM_FREE( poieTemp );
}
pse-> dwMfeIfCount = 0;
//
// this MFE is now a negative MFE. Make sure to
// invoke the deletion alert callback for the
// protocol component that owns the incoming
// interface
//
bDeleteCallback = TRUE;
}
else
{
//
// Check if interface is present in the OIF of MFE.
// If so remove interface from OIF and issue
// callbacks as appropriate
//
bFound = FindOutInterfaceEntry(
&pse-> leMfeIfList, pieIfEntry-> dwIfIndex,
pieIfEntry-> dwIfNextHopAddr,
pieIfEntry-> dwOwningProtocol,
pieIfEntry-> dwOwningComponent, &bNewComp,
&poie
);
if ( !bFound )
{
//
// interface not present in the OIF list of MFE
// move on to next entry
//
continue;
}
//
// Delete the outgoing interface
//
DeleteOutInterfaceEntry( poie );
pse-> dwMfeIfCount--;
if ( !pse-> dwMfeIfCount )
{
//
// MFE OIF list has no more outgoing interfaces.
// Need to issue deletion alert to protocol component
// owning incoming interface
//
bDeleteCallback = TRUE;
}
}
//
// If needed issue deletion alert to the protocol on the
// incoming interface
//
if ( bDeleteCallback )
{
ppe = GetProtocolEntry(
PROTOCOL_LIST_HEAD(), pse-> dwInProtocolId,
pse-> dwInComponentId
);
if ( ppe == NULL )
{
//
// Protocol owning incoming interface is not present
// in incoming list. Very strange and should not happen.
// Nothing to be done here, move on to next source.
//
TRACE3(
SCOPE,
"Protocol (%d, %d) not present for interface %d",
pse-> dwInProtocolId, pse-> dwInComponentId,
dwIfIndex
);
continue;
}
if ( IS_PRUNE_ALERT( ppe ) )
{
PRUNE_ALERT( ppe )(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
pse-> dwInIfIndex, pse-> dwInIfNextHopAddr,
FALSE, &dwTimeOut
);
}
}
//
// Update Kernel Mode forwarder
//
AddMfeToForwarder( pge, pse, dwTimeOut );
}
RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
}
RELEASE_MASTER_GROUP_LOCK_SHARED();
} while ( FALSE );
RELEASE_IF_LOCK_SHARED( dwIfBucket);
//
// Invoke pended Join/Prune alerts
//
InvokeOutstandingCallbacks();
RELEASE_PROTOCOL_LOCK_SHARED();
LEAVE_MGM_API();
TRACE3(
SCOPE, "LEAVING MgmBlockGroups (%lx - %lx) on %lx\n",
dwFirstGroup, dwLastGroup, dwIfIndex
);
return NO_ERROR;
}
VOID
ScopeIfAndInvokeCallbacks(
PGROUP_ENTRY pge,
PSOURCE_ENTRY pse,
POUT_IF_ENTRY poie
)
/*++
Routine Description :
This routine remove an interface entry from the outgoing interface
list for the specified source entry and puts it into the scoped
interface list. If the deletion of the interface to the OIF list
requires deletion alert callbacks to be issued to protocol components
these are issued by this routine.
Arguements :
pge - Group entry correspondong to the group being blocked.
pse - Source entry for the group being blocked
poie - Interface entry corresponding to the interface over which
the (source, group) entry is being blocked
Return Value :
None
Environment :
Invoked from MgmBlockGroups. Assumes the protocol list and
interface bucket are locked for read, and the group entry is
locked for write.
--*/
{
BOOL bFound, bNewComp;
PPROTOCOL_ENTRY ppe;
POUT_IF_ENTRY poieTemp = NULL;
do
{
//
// find the protocol component on the interface specified by poie
//
ppe = GetProtocolEntry(
PROTOCOL_LIST_HEAD(), poie-> dwProtocolId,
poie-> dwComponentId
);
if ( ppe == NULL )
{
//
// Outgoing interface entry but corresponding owning
// protocol is no present. This should not happen.
// Print a warning indicating bad state and return
//
TRACE3(
SCOPE, "Protocol (%d, %d) not present for interface %d",
poie-> dwProtocolId, poie-> dwComponentId,
poie-> dwIfIndex
);
break;
}
//
// Remove interface entry from the OIF list
//
RemoveEntryList( &poie-> leIfList );
//
// Find the locaion in the scoped i/f list and insert it
//
bFound = FindOutInterfaceEntry(
&pse-> leScopedIfList, poie-> dwIfIndex,
poie-> dwIfNextHopAddr, poie-> dwProtocolId,
poie-> dwComponentId, &bNewComp, &poieTemp
);
if ( bFound )
{
//
// Interface being scoped is already present in scoped
// i/f list. Strange. Print warning and quit.
//
TRACE4(
ANY, "Interface (%d, %d) already present in the scoped list"
" for (%x, %x)", poie-> dwIfIndex, poie-> dwIfNextHopAddr,
pse-> dwSourceAddr, pge-> dwGroupAddr
);
MGM_FREE( poie );
break;
}
InsertTailList(
( poieTemp == NULL ) ? &pse-> leScopedIfList :
&poieTemp-> leIfList,
&poie-> leIfList
);
//
// If group membership has been added by IGMP and this interface
// is owned by a different protocol, inform the protocol that IGMP
// has just left the interface. 'Hank you, 'hank you very much.
//
if ( IS_ADDED_BY_IGMP( poie ) && !IS_PROTOCOL_IGMP( ppe ) )
{
if ( IS_LOCAL_LEAVE_ALERT( ppe ) )
{
LOCAL_LEAVE_ALERT( ppe )(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
poie-> dwIfIndex, poie-> dwIfNextHopAddr
);
}
}
//
// Check if the removal of this interface from the OIF list
// resulted in decreasing the number of components present
// in the OIF list.
//
FindOutInterfaceEntry(
&pse-> leOutIfList, poie-> dwIfIndex, poie-> dwIfNextHopAddr,
poie-> dwProtocolId, poie-> dwComponentId, &bNewComp, &poieTemp
);
if ( bNewComp )
{
pse-> dwOutCompCount--;
//
// number of componets in OIF list has decreased.
// Invoke deletion alerts as per interop rules.
//
InvokePruneAlertCallbacks(
pge, pse, poie-> dwIfIndex, poie-> dwIfNextHopAddr, ppe
);
}
} while ( FALSE );
}
VOID
InvokePruneAlertCallbacks(
PGROUP_ENTRY pge,
PSOURCE_ENTRY pse,
DWORD dwIfIndex,
DWORD dwIfNextHopAddr,
PPROTOCOL_ENTRY ppe
)
/*++
Routine Description :
This routine invokes deletion alert callbacks of protocol components
in response to an interface being removed from the OIF list of a
source entry. Deletion alert callbacks are issued as per the
interop rules.
Arguements :
pge - Entry corresponding to group for which deletion alert callbacks
are being issued.
pse - Entry corresponding to source for which deletion alert callbacks
are being issued.
dwIfIndex - Index of interface that is being deleted (or scoped)
dwIfNextHopAddr - Next hop on interface that is being deleted (or scoped)
ppe - Protocol entry for the protocol component that owns the interface
corresponding to poie.
Return Value :
None
Environment :
Invoked from ScopeIfAndCanInvokeCallbacks and
DeleteInterfaceFromSourceEntry
--*/
{
PPROTOCOL_ENTRY ppeEntry;
POUT_IF_ENTRY poieTemp;
PLIST_ENTRY pleStart, pleProtocol;
//----------------------------------------------------------------
// Callback time
//----------------------------------------------------------------
//
// Check if Source specific join
//
if ( !IS_WILDCARD_SOURCE( pse-> dwSourceAddr, pse-> dwSourceMask ) )
{
if ( pse-> dwOutCompCount == 0 )
{
TRACESCOPE0( GROUP, "Last component in OIL for source specific" );
AddToOutstandingJoinList(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
dwIfIndex, dwIfNextHopAddr,
FALSE
);
}
}
else if ( pse-> dwOutCompCount == 1 )
{
TRACESCOPE0(
GROUP, "Number of components in the OIL is down to 1"
);
//
// Number of protocol components that have interfaces in the OIL
// has reduced from 2 to 1.
//
// invoke PRUNE_ALERT to the remaining protocol component
//
poieTemp = CONTAINING_RECORD(
pse-> leOutIfList.Flink, OUT_IF_ENTRY, leIfList
);
ppeEntry = GetProtocolEntry(
PROTOCOL_LIST_HEAD(), poieTemp-> dwProtocolId,
poieTemp-> dwComponentId
);
if ( ppeEntry == NULL )
{
TRACE2(
ANY, "InvokePruneAlertCallbacks : Could not"
" find protocol (%x, %x)", poieTemp-> dwProtocolId,
poieTemp-> dwComponentId
);
}
//
// invoke the delete membership callback for only the remaining
// interface.
//
else if ( IS_PRUNE_ALERT( ppeEntry ) )
{
PRUNE_ALERT( ppeEntry ) (
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
dwIfIndex, dwIfNextHopAddr, TRUE, NULL
);
}
}
else if ( pse-> dwOutCompCount == 0 )
{
TRACESCOPE0(
GROUP, "Number of components in the OIL is down to 0"
);
//
// Number of protocol components that have interfaces in the
// OIL has reduced from 1 to 0.
//
// invoke PRUNE_ALERT to all the other protocol
// components
//
for ( pleStart = PROTOCOL_LIST_HEAD(),
pleProtocol = pleStart-> Flink;
pleProtocol != pleStart;
pleProtocol = pleProtocol-> Flink )
{
ppeEntry = CONTAINING_RECORD(
pleProtocol, PROTOCOL_ENTRY, leProtocolList
);
if ( ( ppeEntry-> dwProtocolId == ppe-> dwProtocolId ) &&
( ppeEntry-> dwComponentId == ppe-> dwComponentId ) )
{
continue;
}
if ( IS_PRUNE_ALERT( ppeEntry ) )
{
PRUNE_ALERT( ppeEntry ) (
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
dwIfIndex, dwIfNextHopAddr, TRUE, NULL
);
}
}
}
}
DWORD
APIENTRY
MgmUnBlockGroups(
IN DWORD dwFirstGroup,
IN DWORD dwLastGroup,
IN DWORD dwIfIndex,
IN DWORD dwIfNextHopAddr
)
/*++
Routine Description :
This function walks the master group list and updates the memberships
of each group entry. If the interface (dwIfIndex) has previously been
removed from the outgoing list of the group entry (and placed in the
scoped i/f list) on account of a previous call to MgmBlockGroups
it is put back and all the MFEs for the group are
updated to reflect this addition.
In addition if this interface was the incoming interface for an MFE
update the timer to expire the MFE in short order (within a second).
This way we force the recreation of an MFE should there be traffic
for this group.
Arguements :
dwFirstGroup - Lower end of the range of groups to be unblocked
dwLastGroup - Upper end of the range of groups be be unblocked
dwIfIndex - Interface over which groups have to be unblocked.
Return Value :
NO_ERROR - Group range successfully unblocked
Environment :
This function is invoked by the IP RouterManager in response to
removal of a group boundary.
--*/
{
BOOL bNewComp = FALSE, bWCGrpLock = FALSE,
bUpdatePass = FALSE;
WORD wWCGroupAddedBy = 0, wWCGroupNumAddsByRP = 0,
wGroupAddedBy, wGroupNumAddsByRP, wGroupNumAddsByIGMP,
wSourceAddedBy, wSourceNumAddsByRP,
wSourceNumAddsByIGMP;
INT iCmp;
DWORD dwIfBucket, dwWCBucket, dwIfProtocol,
dwIfComponent, dwErr;
PIF_ENTRY pieIfEntry;
PGROUP_ENTRY pgeWC = NULL, pge;
PSOURCE_ENTRY pseWC = NULL, pse;
POUT_IF_ENTRY poie = NULL;
PLIST_ENTRY pleGrpHead, pleGrp, pleSrcHead, pleSrc;
LIST_ENTRY leForwardList;
//
// Ensure that MGM is running and increment count of threads
// exceuting in MGM
//
if ( !ENTER_MGM_API() )
{
return ERROR_CAN_NOT_COMPLETE;
}
TRACE3(
SCOPE, "ENTERED MgmUnblockGroups : (%lx - %lx) on %lx",
dwFirstGroup, dwLastGroup, dwIfIndex
);
ACQUIRE_PROTOCOL_LOCK_SHARED ();
InitializeListHead( &leForwardList );
do
{
//
// Pass I : aka Scan pass (bupdatePass == FALSE)
// Scan and Collect all MFEs for which CREATION_ALERTS need to
// invoked before updating the MFEs. Invoke the CREATION_ALERTS
// outside of any locks (which is why we need two passes).
//
// Pass II : Update pass (bupdatePass == TRUE)
// Update memberships and MFEs
//
//
// Verify dwIfIndex is a valid interface with MGM
//
dwIfBucket = IF_TABLE_HASH( dwIfIndex );
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
pieIfEntry = GetIfEntry(
IF_BUCKET_HEAD( dwIfBucket ), dwIfIndex,
dwIfNextHopAddr
);
if ( pieIfEntry == NULL )
{
TRACE2(
SCOPE, "Interface (%lx-%lx) not found", dwIfIndex,
dwIfNextHopAddr
);
RELEASE_IF_LOCK_SHARED( dwIfBucket );
break;
}
if ( bUpdatePass )
{
//
// Verify the interface is still owned by the same protocol
// as when you made the scan pass.
// If not the protocol on the interface (on the scan pass)
// has released the interface and there is no update to be done.
//
if ( ( pieIfEntry-> dwOwningProtocol != dwIfProtocol ) ||
( pieIfEntry-> dwOwningComponent != dwIfComponent ) )
{
TRACE2(
SCOPE, "Ne protocol on interface (%lx-%lx)", dwIfIndex,
dwIfNextHopAddr
);
RELEASE_IF_LOCK_SHARED( dwIfBucket );
break;
}
}
else
{
//
// On the scan pass, store the protocol on the interface.
// We need to verify that the protocol remains the same
// between the scan and update passes.
//
dwIfProtocol = pieIfEntry-> dwOwningProtocol;
dwIfComponent = pieIfEntry-> dwOwningComponent;
}
//
// Merge temp and master group lists
//
ACQUIRE_TEMP_GROUP_LOCK_EXCLUSIVE();
MergeTempAndMasterGroupLists( TEMP_GROUP_LIST_HEAD() );
RELEASE_TEMP_GROUP_LOCK_EXCLUSIVE();
//
// Lock the master group list for reading
//
ACQUIRE_MASTER_GROUP_LOCK_SHARED( );
//
// Check if wild card recevier (*, *) for this interface. If it is
// note this. i.e. mark as added by protocol and numaddsbyRp = 1.
//
// N.B.
// You are scanning the master group list for the
// WILDCARD_GROUP. This is not as expensive as it seems since the
// WC entry if present would right at the beginning of the master
// list.
//
if ( FindGroupEntry(
MASTER_GROUP_LIST_HEAD(), WILDCARD_GROUP,
WILDCARD_GROUP_MASK, &pgeWC, FALSE
) )
{
//
// Lock this group entry to prevent changes to its OIF list
// while unblokcing is in progress
//
ACQUIRE_GROUP_ENTRY_LOCK_SHARED( pgeWC );
bWCGrpLock = TRUE;
dwWCBucket = SOURCE_TABLE_HASH(
WILDCARD_SOURCE, WILDCARD_SOURCE_MASK
);
if ( FindSourceEntry(
SOURCE_BUCKET_HEAD( pgeWC, dwWCBucket ),
WILDCARD_SOURCE, WILDCARD_SOURCE_MASK, &pseWC, TRUE
) )
{
//
// (*, *) entry present, check if dwIfIndex is present in its
// OIF list
//
if ( FindOutInterfaceEntry(
&pseWC-> leOutIfList, pieIfEntry-> dwIfIndex,
pieIfEntry-> dwIfNextHopAddr,
pieIfEntry->dwOwningProtocol,
pieIfEntry-> dwOwningComponent, &bNewComp, &poie
) )
{
//
// This interface is a wildcard receiver. Note this as
// added by routing protocol since IGMP would never be
// a (*, *) receiver
//
wWCGroupAddedBy = poie-> wAddedByFlag;
wWCGroupNumAddsByRP = poie-> wNumAddsByRP;
}
}
}
for ( pleGrpHead = MASTER_GROUP_LIST_HEAD(),
pleGrp = pleGrpHead-> Flink;
pleGrp != pleGrpHead;
pleGrp = pleGrp-> Flink )
{
//
// For each group in the master list
//
pge = CONTAINING_RECORD( pleGrp, GROUP_ENTRY, leGrpList );
//
// Skip the (*, *) entry. i.e. Skip the wildcard group.
// This group entry has already been examined above (just
// before the for loop). There is no need to look at this
// entry as the ref. counts for this entry have been collected
// above. In addition the "group entry lock" for this entry
// has been acquired above and attempting to reacquire it will
// lead to DEAD-LOCK. This presents only a minor inconvience
// in this "for" loop i.e. having to check and skip this entry.
//
if ( IS_WILDCARD_GROUP( pge-> dwGroupAddr, pge-> dwGroupMask ) )
{
continue;
}
//
// check is in range specified
//
if ( INET_CMP( pge-> dwGroupAddr, dwLastGroup, iCmp ) > 0 )
{
//
// The master group list is ordered by group number and
// the high end of the range has been crossed. Quit
//
break;
}
if ( INET_CMP( pge-> dwGroupAddr, dwFirstGroup, iCmp ) < 0 )
{
//
// Skip groups entries smaller than the lower end of the
// range
//
continue;
}
//
// Group entry in range specified
//
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
//
// This group entry inherits the counts from the wildcard
// group entry
//
wGroupAddedBy = wWCGroupAddedBy;
wGroupNumAddsByRP = wWCGroupNumAddsByRP;
wGroupNumAddsByIGMP = 0;
//
// Check if there are group memberships for the wildcard source
// that have been scoped. Update interface counts appropriately
//
dwWCBucket = SOURCE_TABLE_HASH(
WILDCARD_SOURCE, WILDCARD_SOURCE_MASK
);
if ( FindSourceEntry(
SOURCE_BUCKET_HEAD( pge, dwWCBucket ), WILDCARD_SOURCE,
WILDCARD_SOURCE_MASK, &pseWC, TRUE
) )
{
//
// Wild card source present. Check if this interface
// is present in its scoped i/f list
//
if ( FindOutInterfaceEntry(
&pseWC-> leScopedIfList, pieIfEntry-> dwIfIndex,
pieIfEntry-> dwIfNextHopAddr,
pieIfEntry-> dwOwningProtocol,
pieIfEntry-> dwOwningComponent,
&bNewComp, &poie
) )
{
//
// Wildcard member ship present present on the interface.
// Note it by updating counts
//
wGroupAddedBy |= poie-> wAddedByFlag;
wGroupNumAddsByRP += poie-> wNumAddsByRP;
wGroupNumAddsByIGMP = poie-> wNumAddsByIGMP;
}
}
//
// Merge the temp and master source lists, before walking
// the source list
//
MergeTempAndMasterSourceLists( pge );
//
// for each source entry
//
pleSrcHead = MASTER_SOURCE_LIST_HEAD( pge );
for ( pleSrc = pleSrcHead-> Flink;
pleSrc != pleSrcHead;
pleSrc = pleSrc-> Flink )
{
pse = CONTAINING_RECORD( pleSrc, SOURCE_ENTRY, leSrcList );
//
// each source entry inherits the aggregated counts of
// the wildcard group (*, *) and the wildcard source (*, G)
// entry
//
wSourceAddedBy = wGroupAddedBy;
wSourceNumAddsByRP = wGroupNumAddsByRP;
wSourceNumAddsByIGMP = wGroupNumAddsByIGMP;
//
// Check if interface being unblocked is present in
// the scoped i/f list for this source
//
if ( FindOutInterfaceEntry(
&pse-> leScopedIfList, pieIfEntry-> dwIfIndex,
pieIfEntry-> dwIfNextHopAddr,
pieIfEntry-> dwOwningProtocol,
pieIfEntry-> dwOwningComponent, &bNewComp, &poie
) )
{
//
// If this is not the wildcard source entry, presence
// of this interface in the scoped i/f list indicates
// that a source specific join for this group was
// performed. Note the counts for this interface for
// the source specific join
//
if ( !IS_WILDCARD_SOURCE(
pse-> dwSourceAddr, pse-> dwSourceMask
) )
{
wSourceAddedBy |= poie-> wAddedByFlag;
wSourceNumAddsByRP += poie-> wNumAddsByRP;
wSourceNumAddsByIGMP += poie-> wNumAddsByIGMP;
}
//
// The function name says it.
//
if ( bUpdatePass )
{
UnScopeIfAndInvokeCallbacks( pge, pse, poie );
}
}
//-----------------------------------------------------------
// Part 2 : MFE Update
//-----------------------------------------------------------
if ( IS_VALID_INTERFACE(
pse-> dwInIfIndex, pse-> dwInIfNextHopAddr
) )
{
//
// This is an MFE
//
//
// Check if the interface being unblocked is the
// incoming interface for this MFE
//
if ( ( pse-> dwInIfIndex == pieIfEntry-> dwIfIndex ) &&
( pse-> dwInIfNextHopAddr ==
pieIfEntry-> dwIfNextHopAddr ) )
{
//
// The incoming interface is being unblocked.
// That implies this MFE is currently a negative.
// The easiest way to re-create the correct MFE
// is to delete the MFE and force its re-creation
// when the next packet arrives from the same
// (source, group). The simplest way to delete
// the MFE and references to it in the interface
// table is to update the expiry time (set
// arbitrarily to 2 seconds here) and let the
// deletion happen via the MFETimerProc (timer.c)
//
if ( bUpdatePass )
{
RtlUpdateTimer(
TIMER_QUEUE_HANDLE(
TIMER_TABLE_HASH( pge-> dwGroupAddr )
),
pse-> hTimer, 2000, 0
);
}
}
//
// ELSE clause comment
//
// Interface being unblocked is not the incoming
// interface. It could be an outgoing interface
// for this MFE. Check if any component is
// interested in traffic for this (S, G). To do
// this check the added by flag and if it is
// non-zero the interface should be added to the
// MFE OIF list.
//
// In addition, make sure that the incoming interface
// does not have a (scope) boundary on it. In that
// case, there is no MFE OIF list changes required.
//
else if ( wSourceAddedBy &&
( !( IS_HAS_BOUNDARY_CALLBACK() ) ||
( IS_HAS_BOUNDARY_CALLBACK() &&
!HAS_BOUNDARY_CALLBACK()(
dwIfIndex, pge-> dwGroupAddr
) ) ) )
{
if ( bUpdatePass &&
IsForwardingEnabled(
pge-> dwGroupAddr, pge-> dwGroupMask,
pse-> dwSourceAddr, pse-> dwSourceMask,
&leForwardList
) )
{
poie = NULL;
AddInterfaceToSourceMfe(
pge, pse, pieIfEntry-> dwIfIndex,
pieIfEntry-> dwIfNextHopAddr,
pieIfEntry-> dwOwningProtocol,
pieIfEntry-> dwOwningComponent, FALSE, &poie
);
//
// Update counts for the OIF in the MFE list
//
if ( poie != NULL )
{
poie-> wAddedByFlag = wSourceAddedBy;
poie-> wNumAddsByRP = wSourceNumAddsByRP;
poie-> wNumAddsByIGMP = wSourceNumAddsByIGMP;
}
}
else if ( !bUpdatePass )
{
AddToCheckForCreationAlertList(
pge-> dwGroupAddr, pge-> dwGroupMask,
pse-> dwSourceAddr, pse->dwSourceMask,
pse-> dwInIfIndex, pse-> dwInIfNextHopAddr,
&leForwardList
);
}
}
}
}
RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
}
//
// if lock on the Wildcard group entry is held, release it
//
if ( bWCGrpLock )
{
RELEASE_GROUP_ENTRY_LOCK_SHARED( pgeWC );
}
RELEASE_MASTER_GROUP_LOCK_SHARED( );
RELEASE_IF_LOCK_SHARED( dwIfBucket );
if ( !bUpdatePass )
{
dwErr = InvokeCreationAlertForList(
&leForwardList, dwIfProtocol, dwIfComponent,
dwIfIndex, dwIfNextHopAddr
);
if ( dwErr != NO_ERROR )
{
break;
}
bUpdatePass = TRUE;
}
else
{
break;
}
} while ( TRUE );
//
// Release all locks and decrement thread-counts etc.
//
//
// Invoke pended Join/Prune alerts
//
InvokeOutstandingCallbacks();
RELEASE_PROTOCOL_LOCK_SHARED();
FreeList( &leForwardList );
LEAVE_MGM_API();
TRACE0( SCOPE, "LEAVING MgmUnblockGroups" );
return NO_ERROR;
}
VOID
UnScopeIfAndInvokeCallbacks(
PGROUP_ENTRY pge,
PSOURCE_ENTRY pse,
POUT_IF_ENTRY poie
)
/*++
Routine Description :
This routine remove an interface entry from the scoped interface
list for the specified source entry and puts it into the outgoing
interface list. If the addition of the interface to the OIF list
requires new member callbacks to be issued to protocol components
these are issued by this routine.
Arguements :
pge - Group entry correspondong to the group being unblocked.
pse - Source entry for the group being unblocked
poie - Interface entry corresponding to the interface over which
the (source, group) entry is being unblocked
Return Value :
None
Environment :
Invoked from MgmUnBlockGroups. Assumes the protocol list and
interface bucket are locked for read, and the group entry is
locked for write.
--*/
{
BOOL bFound, bNewComp = FALSE;
PPROTOCOL_ENTRY ppe;
POUT_IF_ENTRY poieNext = NULL;
do
{
//
// Remove interface entry from the scoped I/f list
//
RemoveEntryList( &poie-> leIfList );
//
// Find its place in the OIF list for the source entry
// and insert it in
//
bFound = FindOutInterfaceEntry(
&pse-> leOutIfList, poie-> dwIfIndex,
poie-> dwIfNextHopAddr, poie-> dwProtocolId,
poie-> dwComponentId, &bNewComp, &poieNext
);
if ( bFound )
{
//
// Trouble. The interface to be inserted should not be
// present in the OIF list for the source entry. Since it
// print a warning message and move on.
//
TRACE4(
ANY, "Interface (%d-%d) present in OIF list of (%x-%x)"
" inspite of being scoped", poie-> dwIfIndex,
poie-> dwIfNextHopAddr, pse-> dwSourceAddr, pge-> dwGroupAddr
);
MGM_FREE( poie );
break;
}
InsertTailList(
( poieNext == NULL ) ? &pse-> leOutIfList :
&poieNext-> leIfList,
&poie-> leIfList
);
//
// if new component, update component count and
// call callback invoker
//
if ( bNewComp )
{
pse-> dwOutCompCount++;
ppe = GetProtocolEntry(
PROTOCOL_LIST_HEAD(), poie-> dwProtocolId,
poie-> dwComponentId
);
if ( ppe == NULL )
{
//
// Trouble. Interface present without any owning
// protocol component.
//
TRACE4(
ANY, "Owning protocol(%d, %) for interface(%d, %d)"
" not found", poie-> dwProtocolId, poie-> dwComponentId,
poie-> dwIfIndex, poie-> dwIfNextHopAddr
);
return;
}
InvokeJoinAlertCallbacks(
pge, pse, poie, IS_ADDED_BY_IGMP( poie ), ppe
);
}
} while ( FALSE );
}
VOID
InvokeJoinAlertCallbacks(
PGROUP_ENTRY pge,
PSOURCE_ENTRY pse,
POUT_IF_ENTRY poie,
BOOL bIGMP,
PPROTOCOL_ENTRY ppe
)
/*++
Routine Description :
This routine invokes New Member callbacks in response to a new
protocol component being added to the outgoing interface list
of a source entry. New member callbacks are issued according
to interop rules.
Argumements :
pge - Entry corresponding to group for which new member callbacks
are being issued.
pse - Entry corresponding to source for which new member callbacks
are being issued.
poie - Entry corresponding to interface whose addition triggered the
callback mechanism.
bIGMP - Indicates if IGMP is adding this interface.
ppe - Protocol entry for the protocol component that owns the interface
corresponding to poie.
Return Value :
None
Environment :
Invoked from AddInterfaceToSourceEntry and UnScopeIfAndInvokeCallbacks
--*/
{
PPROTOCOL_ENTRY ppeEntry;
POUT_IF_ENTRY poiePrev;
PLIST_ENTRY ple, pleStart;
//
// Check if Source specific join
//
if ( !IS_WILDCARD_SOURCE( pse-> dwSourceAddr, pse-> dwSourceMask ) )
{
if ( pse-> dwOutCompCount == 1 )
{
TRACESCOPE0( GROUP, "First component in OIL for source specific" );
AddToOutstandingJoinList(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
poie-> dwIfIndex, poie-> dwIfNextHopAddr,
TRUE
);
}
}
else if ( pse-> dwOutCompCount == 1 )
{
TRACESCOPE0( GROUP, "First component in OIL" );
//
// Interaction between routing protocols.
//
//
// first component in the OIL.
//
// Send new member callback to all the other (than the
// the one adding this group membership on
// this interface) routing protocol components
//
// At this point you have a read lock on the protocol list
// so you can walk the list
//
pleStart = PROTOCOL_LIST_HEAD();
for ( ple = pleStart-> Flink; ple != pleStart; ple = ple-> Flink )
{
ppeEntry = CONTAINING_RECORD(
ple, PROTOCOL_ENTRY, leProtocolList
);
//
// all OTHER protocol components need to be told of the
// interface addition. So skip the component adding
// this interface.
//
if ( ( ppeEntry-> dwProtocolId == ppe-> dwProtocolId ) &&
( ppeEntry-> dwComponentId == ppe-> dwComponentId ) )
{
continue;
}
//
// if routing protocol has requested new member callback
//
if ( IS_JOIN_ALERT( ppeEntry ) )
{
JOIN_ALERT( ppeEntry )(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask, TRUE
);
}
}
}
//
// if second component to add an interface to the OIL
// invoke new member callback to first component.
//
// Note :
// If the first component that added a group membership
// was IGMP skip JOIN_ALERT callback.
//
else if ( pse-> dwOutCompCount == 2 )
{
TRACESCOPE0( GROUP, "Second component in OIL" );
//
// find the "other (first)" routing protocol component to add
// an interface to the OIL
//
for ( ple = pse-> leOutIfList.Flink;
ple != &pse-> leOutIfList;
ple = ple-> Flink )
{
poiePrev = CONTAINING_RECORD(
ple, OUT_IF_ENTRY, leIfList
);
//
// if the protocol component that added this interface to
// the OIL is different indicating that it is the other
// component invoke its new member interface
//
if ( ( poiePrev-> dwProtocolId != ppe-> dwProtocolId ) ||
( poiePrev-> dwComponentId != ppe-> dwComponentId ) )
{
//
// Find the protocol entry for the other interface
//
ppeEntry = GetProtocolEntry(
&ig.mllProtocolList.leHead,
poiePrev-> dwProtocolId,
poiePrev-> dwComponentId
);
if ( ppeEntry == NULL )
{
TRACE2(
ANY, "InvokeJoinAlertCallbacks : Could not"
"find protocol %x, %x", poie-> dwProtocolId,
poie-> dwComponentId
);
}
else if ( IS_ROUTING_PROTOCOL( ppeEntry ) &&
IS_JOIN_ALERT( ppeEntry ) )
{
//
// JOIN_ALERT callback will be skipped if
// the first component is IGMP
//
JOIN_ALERT( ppeEntry )(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask, TRUE
);
}
break;
}
}
}
//
// if this group membership was added by IGMP, and
// a routing protocol co-exists with IGMP on this interface
// inform the routing protocol too.
//
if ( bIGMP && IS_ROUTING_PROTOCOL( ppe ) )
{
if ( IS_LOCAL_JOIN_ALERT( ppe ) )
{
LOCAL_JOIN_ALERT( ppe )(
pse-> dwSourceAddr, pse-> dwSourceMask,
pge-> dwGroupAddr, pge-> dwGroupMask,
poie-> dwIfIndex, poie-> dwIfNextHopAddr
);
}
}
}
DWORD
AddToOutstandingJoinList(
DWORD dwSourceAddr,
DWORD dwSourceMask,
DWORD dwGroupAddr,
DWORD dwGroupMask,
DWORD dwIfIndex,
DWORD dwIfNextHopAddr,
BOOL bJoin
)
/*++
Routine Description :
This routine adds a Join entry to the global outstanding join list.
Each entry in this list represents a "source specific" join/leave for
which the corresponding join/prune alerts have not yet been issued.
The reason for deferring the callbacks has to do with the order of
locking of buckets in the IF HASH table. When a membership is
added/deleted a lock is taken on the interface bucket that contains
the interface on which the membership is being changed. When the
source entry for which the membership is being changed has been
updated you determine (as per interop rules) whether
a join/prune needs to be issued to the protocol on the incoming
interface. If it must you need to look up the incoming interface
and then find the protocol on that interface and invoke its callbacks.
To do this you need to look up the incoming interface in the
IF hash table and locking the bucket for that IF entry. You lock two
buckets simultaneously. Hence the deferral
Arguements :
dwSourceAddr - Source address for which a join/leave has occured
dwSourceMask - Mask corresponding to dwSourceAddr
dwGroupAddr - Group for which a join/leave has occured
dwGroupMask - Mask corresponding to dwGroupAddr
dwIfIndex - Incoming interface index as per the MCAST RIB
dwIfNextHopAddr - Next hop address corresponding to dwIfIndex
bJoin - Indicates if an outstanding entry is being added because of a
join or leave
Return Value :
NO_ERROR - Success
ERROR_NOT_ENOUGH_MEMORY - failed to allocate a join entry
Environment :
Invoked in the context of Invoke[PruneAlert/JoinAlert]Callbacks
--*/
{
BOOL bFound;
DWORD dwErr = NO_ERROR;
PJOIN_ENTRY pje = NULL, pjeNew;
ACQUIRE_JOIN_LIST_LOCK_EXCLUSIVE();
do
{
bFound = FindJoinEntry(
JOIN_LIST_HEAD(), dwSourceAddr, dwSourceMask,
dwGroupAddr, dwGroupMask, dwIfIndex, dwIfNextHopAddr,
&pje
);
if ( bFound )
{
//
// Join entry already exists for this interface.
// Check if it is of the same type
//
if ( pje-> bJoin != bJoin )
{
//
// Join entries of different types, null each other
// remove this join entry
//
RemoveEntryList( &pje-> leJoinList );
MGM_FREE( pje );
}
}
else
{
//
// Join entry does not exist. Create one and insert it.
//
pjeNew = MGM_ALLOC( sizeof( JOIN_ENTRY ) );
if ( pjeNew == NULL )
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
TRACE1( ANY, "Failed to create Join Entry : %x", dwErr );
break;
}
InitializeListHead( &pjeNew-> leJoinList );
pjeNew-> dwSourceAddr = dwSourceAddr;
pjeNew-> dwSourceMask = dwSourceMask;
pjeNew-> dwGroupAddr = dwGroupAddr;
pjeNew-> dwGroupMask = dwGroupMask;
pjeNew-> dwIfIndex = dwIfIndex;
pjeNew-> dwIfNextHopAddr = dwIfNextHopAddr;
pjeNew-> bJoin = bJoin;
InsertTailList(
( pje == NULL ) ? JOIN_LIST_HEAD() : &pje-> leJoinList,
&pjeNew-> leJoinList
);
}
} while ( FALSE );
RELEASE_JOIN_LIST_LOCK_EXCLUSIVE();
return dwErr;
}
PJOIN_ENTRY
GetNextJoinEntry(
)
/*++
Routine Description :
This function removes the first outstanding join entry and returns it
Arguements :
Return Values :
NULL - if outstanding join list is empty
pointer to a join entry otherwise
Environment :
Invoked from InvokeOutstandingCallbacks
--*/
{
PLIST_ENTRY ple;
PJOIN_ENTRY pje = NULL;
ACQUIRE_JOIN_LIST_LOCK_EXCLUSIVE();
if ( !IsListEmpty( JOIN_LIST_HEAD() ) )
{
ple = RemoveHeadList( JOIN_LIST_HEAD() );
pje = CONTAINING_RECORD( ple, JOIN_ENTRY, leJoinList );
}
RELEASE_JOIN_LIST_LOCK_EXCLUSIVE();
return pje;
}
BOOL
FindJoinEntry(
PLIST_ENTRY pleJoinList,
DWORD dwSourceAddr,
DWORD dwSourceMask,
DWORD dwGroupAddr,
DWORD dwGroupMask,
DWORD dwIfIndex,
DWORD dwIfNextHopAddr,
PJOIN_ENTRY * ppje
)
/*++
Routine Description :
This routine finds a specified join entry in the outstanding join list.
Arguements :
pleJoinList - Join list to be searched
dwSourceAddr - Source address for which a join/leave has occured
dwSourceMask - Mask corresponding to dwSourceAddr
dwGroupAddr - Group for which a join/leave has occured
dwGroupMask - Mask corresponding to dwGroupAddr
dwIfIndex - Incoming interface index as per the MCAST RIB
dwIfNextHopAddr - Next hop address corresponding to dwIfIndex
ppje - a pointer to join entry if found or
a pointer to the next element in the join list if it exists or
NULL
Return Values :
TRUE - Join entry found
FALSE - Join entry not found
Environment :
Invoked from AddToOutstandingJoinList
--*/
{
INT iCmp;
PLIST_ENTRY ple = NULL;
PJOIN_ENTRY pje = NULL;
BOOL bFound = FALSE;
*ppje = NULL;
for ( ple = pleJoinList-> Flink; ple != pleJoinList; ple = ple-> Flink )
{
pje = CONTAINING_RECORD( ple, JOIN_ENTRY, leJoinList );
if ( INET_CMP( pje-> dwGroupAddr, dwGroupAddr, iCmp ) < 0 )
{
continue;
}
else if ( iCmp > 0 )
{
//
// you are now past the position where an existing
// entry would be.
//
*ppje = pje;
break;
}
if ( INET_CMP( pje-> dwSourceAddr, dwSourceAddr, iCmp ) < 0 )
{
continue;
}
else if ( iCmp > 0 )
{
//
// you are now past the position where an existing
// entry would be.
//
*ppje = pje;
break;
}
if ( pje-> dwIfIndex < dwIfIndex )
{
continue;
}
else if ( pje-> dwIfIndex > dwIfIndex )
{
//
// you are now past the position where an existing
// entry would be.
//
*ppje = pje;
break;
}
if ( INET_CMP( pje-> dwIfNextHopAddr, dwIfNextHopAddr, iCmp ) < 0 )
{
continue;
}
else if ( iCmp > 0 )
{
//
// you are now past the position where an existing
// entry would be.
//
*ppje = pje;
break;
}
//
// entry found
//
*ppje = pje;
bFound = TRUE;
break;
}
return bFound;
}
VOID
InvokeOutstandingCallbacks(
)
/*++
Routine Description :
This routine walks the global outstanding join list, and for each entry
finds the incoming interface and the protocol on it and invokes the
appropriate callback (JoinAlert/PruneAlert).
Arguements :
Return Values :
Environment :
Whenever a source specific join or leave occurs or when scoped boundaries
change.
--*/
{
BOOL bFound;
DWORD dwIfBucket;
PJOIN_ENTRY pje;
PIF_ENTRY pie;
PPROTOCOL_ENTRY ppe;
DWORD dwErr;
RTM_NET_ADDRESS rnaAddr;
RTM_DEST_INFO rdiDest;
RTM_NEXTHOP_INFO rniNextHop;
BOOL bRelDest, bRelNextHop, bRelIfLock;
HANDLE hNextHop;
//
// While there are join entries
// - Get the next join entry
// - Look source and find incoming interface
// - Find the interface entry and get the protocol on that i/f
// - invoke its callback
//
while ( ( pje = GetNextJoinEntry() ) != NULL )
{
bRelDest = bRelNextHop = bRelIfLock = FALSE;
do
{
//
// Get route to source
//
RTM_IPV4_MAKE_NET_ADDRESS(
&rnaAddr, pje-> dwSourceAddr, IPv4_ADDR_LEN
);
dwErr = RtmGetMostSpecificDestination(
g_hRtmHandle, &rnaAddr, RTM_BEST_PROTOCOL,
RTM_VIEW_MASK_MCAST, &rdiDest
);
if ( dwErr != NO_ERROR )
{
TRACE1(
ANY, "InvokeOutstandingCallbacks : Failed to lookup "
"route : %x", dwErr
);
break;
}
bRelDest = TRUE;
//
// Select next hop info
//
hNextHop = SelectNextHop( &rdiDest );
if ( hNextHop == NULL )
{
TRACE1(
ANY, "InvokeOutstandingCallbacks : Failed to select "
"next hop : %x", dwErr
);
break;
}
//
// Get nexthop info
//
dwErr = RtmGetNextHopInfo(
g_hRtmHandle, hNextHop, &rniNextHop
);
if ( dwErr != NO_ERROR )
{
TRACE1(
ANY, "InvokeOutstandingCallbacks : Failed to get "
"next hop info : %x", dwErr
);
break;
}
bRelNextHop = TRUE;
//
// Find the incming interface entry
//
dwIfBucket = IF_TABLE_HASH( rniNextHop.InterfaceIndex );
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
bRelIfLock = TRUE;
bFound = FindIfEntry(
IF_BUCKET_HEAD( dwIfBucket ), rniNextHop.InterfaceIndex,
0, &pie
);
if ( ( pie == NULL ) ||
( !bFound &&
pie-> dwIfIndex != rniNextHop.InterfaceIndex ) )
{
//
// No interface with the specified ID exists.
// Nothing to be done
//
break;
}
//
// Check if the interface on which JOIN/PRUNE occured is
// the same as the incoming interface.
//
// If so skip it.
//
if ( ( pje-> dwIfIndex == pie-> dwIfIndex ) &&
( pje-> dwIfNextHopAddr == pie-> dwIfNextHopAddr ) )
{
//
// No join/prune required
//
TRACEGROUP2(
GROUP, "No callback as incoming if == joined/pruned "
"if 0x%x 0x%x",
pje-> dwIfIndex, pje-> dwIfNextHopAddr
);
break;
}
ppe = GetProtocolEntry(
PROTOCOL_LIST_HEAD(), pie-> dwOwningProtocol,
pie-> dwOwningComponent
);
if ( ppe == NULL )
{
//
// No protocol present for interface entry. Strange
//
break;
}
if ( pje-> bJoin )
{
if ( IS_JOIN_ALERT( ppe ) )
{
JOIN_ALERT( ppe )(
pje-> dwSourceAddr, pje-> dwSourceMask,
pje-> dwGroupAddr, pje-> dwGroupMask,
TRUE
);
}
}
else
{
if ( IS_PRUNE_ALERT( ppe ) )
{
PRUNE_ALERT( ppe )(
pje-> dwSourceAddr, pje-> dwSourceMask,
pje-> dwGroupAddr, pje-> dwGroupMask,
pje-> dwIfIndex, pje-> dwIfNextHopAddr,
TRUE, NULL
);
}
}
} while ( FALSE );
MGM_FREE( pje );
if ( bRelIfLock )
{
RELEASE_IF_LOCK_SHARED( dwIfBucket );
}
if ( bRelDest )
{
dwErr = RtmReleaseDestInfo( g_hRtmHandle, &rdiDest );
if ( dwErr != NO_ERROR )
{
TRACE1( ANY, "Failed to release dest info : %x", dwErr );
}
}
if ( bRelNextHop )
{
dwErr = RtmReleaseNextHopInfo( g_hRtmHandle, &rniNextHop );
if ( dwErr != NO_ERROR )
{
TRACE1( ANY, "Failed to release dest info : %x", dwErr );
}
}
}
}
VOID
AddToCheckForCreationAlertList(
DWORD dwGroupAddr,
DWORD dwGroupMask,
DWORD dwSourceAddr,
DWORD dwSourceMask,
DWORD dwInIfIndex,
DWORD dwInIfNextHopAddr,
PLIST_ENTRY pleForwardList
)
/*++
Routine Description :
Arguements :
Return Values :
Environment :
--*/
{
PJOIN_ENTRY pje;
//
// Create an entry in the forward list
//
pje = MGM_ALLOC( sizeof( JOIN_ENTRY ) );
if ( pje == NULL )
{
TRACE0( ANY, "Failed to allocate forward list entry" );
return;
}
InitializeListHead( &pje-> leJoinList );
pje-> dwSourceAddr = dwSourceAddr;
pje-> dwSourceMask = dwSourceMask;
pje-> dwGroupAddr = dwGroupAddr;
pje-> dwGroupMask = dwGroupMask;
pje-> dwIfIndex = dwInIfIndex;
pje-> dwIfNextHopAddr = dwInIfNextHopAddr;
pje-> bJoin = TRUE;
//
// Insert at the end of the list
//
InsertTailList( pleForwardList, &pje-> leJoinList );
return;
}
BOOL
IsForwardingEnabled(
DWORD dwGroupAddr,
DWORD dwGroupMask,
DWORD dwSourceAddr,
DWORD dwSourceMask,
PLIST_ENTRY pleForwardList
)
/*++
--*/
{
PLIST_ENTRY ple, pleTemp;
PJOIN_ENTRY pje;
BOOL bEnable = FALSE;
INT iCmp;
//
// find the source group entry and
// check if forwarding is enabled for it
//
ple = pleForwardList-> Flink;
while ( ple != pleForwardList )
{
pje = CONTAINING_RECORD( ple, JOIN_ENTRY, leJoinList );
if ( INET_CMP( pje-> dwGroupAddr, dwGroupAddr, iCmp ) < 0 )
{
pleTemp = ple-> Flink;
RemoveEntryList( ple );
MGM_FREE( pje );
ple = pleTemp;
continue;
}
else if ( iCmp > 0 )
{
//
// you are now past the position where an existing
// entry would be.
//
break;
}
if ( INET_CMP( pje-> dwSourceAddr, dwSourceAddr, iCmp ) < 0 )
{
pleTemp = ple-> Flink;
RemoveEntryList( ple );
MGM_FREE( pje );
ple = pleTemp;
continue;
}
else if ( iCmp > 0 )
{
//
// you are now past the position where an existing
// entry would be.
//
break;
}
//
// found source-group entry
//
bEnable = pje-> bJoin;
RemoveEntryList( ple );
MGM_FREE( pje );
break;
}
return bEnable;
}
DWORD
InvokeCreationAlertForList(
PLIST_ENTRY pleForwardList,
DWORD dwProtocolId,
DWORD dwComponentId,
DWORD dwIfIndex,
DWORD dwIfNextHopAddr
)
{
PPROTOCOL_ENTRY ppe;
PLIST_ENTRY ple;
PJOIN_ENTRY pje;
MGM_IF_ENTRY mie;
//
// Get the protocol entry on which CREATION_ALERTs are to
// be invoked.
//
ppe = GetProtocolEntry(
PROTOCOL_LIST_HEAD(), dwProtocolId, dwComponentId
);
if ( ppe == NULL )
{
TRACE2(
ANY, "Could not invoke CREATION_ALERTs since protocol"
"(%ld, %ld) not found", dwProtocolId, dwComponentId
);
return ERROR_NOT_FOUND;
}
if ( !( IS_CREATION_ALERT( ppe ) ) )
{
TRACE2(
ANY, "Protocol (%ld, %ld) does not have a CREATION_ALERT",
dwProtocolId, dwComponentId
);
return NO_ERROR;
}
//
// for each member of the list invoke CREATION_ALERT
//
ple = pleForwardList-> Flink;
while ( ple != pleForwardList )
{
pje = CONTAINING_RECORD( ple, JOIN_ENTRY, leJoinList );
mie.dwIfIndex = dwIfIndex;
mie.dwIfNextHopAddr = dwIfNextHopAddr;
mie.bIGMP = TRUE;
mie.bIsEnabled = pje-> bJoin;
CREATION_ALERT( ppe )(
pje-> dwSourceAddr, pje-> dwSourceMask,
pje-> dwGroupAddr, pje-> dwGroupMask,
pje-> dwIfIndex, pje-> dwIfNextHopAddr,
1, &mie
);
pje-> bJoin = mie.bIsEnabled;
ple = ple-> Flink;
}
return NO_ERROR;
}
VOID
WorkerFunctionInvokeCreationAlert(
PVOID pvContext
)
/*++
Routine Description:
This routine invokes the creation alert for the protocol that
owns the interface specified in the context. This invocation
needs to happen from a worker thread for locking reasons.
For a group join the protocol calls into MGM via the
MgmAddGroupMembership API. We cannot call back into the protocol
in the context of this API call since the protocol could be holding
locks when invoking this API which in turn may be acquired in the
context of the callback. Hence the call back are invoked from a
worker thread
Parameters
pvContext - pointer to a CREATION_ALERT_CONTEXT structure
containing the source, group, and interface on which
a membership join occured.
Return Value:
None
Environment:
Invoked from MgmAddGroupMembership for (*, G) and (*, *) joins.
Calls in protocols to issue CREATION_ALERT_CALLBACK
--*/
{
DWORD dwInd, dwErr, dwIfBucket, dwGrpBucket, dwSrcBucket;
BOOL bNewComp, bIfLock = FALSE, bGrpLock = FALSE, bgeLock = FALSE;
PIF_ENTRY pieEntry;
PGROUP_ENTRY pge;
PSOURCE_ENTRY pse;
POUT_IF_ENTRY poie;
LIST_ENTRY leSourceList;
PCREATION_ALERT_CONTEXT pcac = (PCREATION_ALERT_CONTEXT) pvContext;
if (!ENTER_MGM_WORKER())
{
TRACE0(
ANY, "InvokeCreationAlert: Failed to enter"
);
MGM_FREE( pcac );
return;
}
TRACE0( GROUP, "ENTERED WorkerFunctionInvokeCreationAlert" );
//
// Acquire protocol lock first to maintain locking order
//
ACQUIRE_PROTOCOL_LOCK_SHARED();
do
{
dwIfBucket = IF_TABLE_HASH(
pcac-> dwIfIndex
);
//
// For wildcard group - i.e. (*, *) membership adds.
//
if ( IS_WILDCARD_GROUP( pcac-> dwGroupAddr, pcac-> dwGroupMask ) )
{
InitializeListHead( &leSourceList );
//
// Walk each bucket of the group table
//
for ( dwInd = 1; dwInd < GROUP_TABLE_SIZE; dwInd++ )
{
//
// Lock the interface to prevent the (*, *)
// membership from being deleted while MFEs
// are being updated.
//
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
pieEntry = GetIfEntry(
IF_BUCKET_HEAD( dwIfBucket ),
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
if ( pieEntry == NULL )
{
//
// Interface is no longer present with MGM.
// possibly deleted in another thread.
// There is no further MFE update to be performed.
// quit now.
//
RELEASE_IF_LOCK_SHARED( dwIfBucket );
break;
}
//
// Pass 1: Accumulate (S, G) values for all groups
// in this group bucket into leSourceList.
//
AddInterfaceToAllMfeInGroupBucket(
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr,
pcac-> dwProtocolId, pcac-> dwComponentId,
dwInd, pcac-> bIGMP, FALSE, &leSourceList
);
RELEASE_IF_LOCK_SHARED( dwIfBucket );
//
// Invoke CREATION_ALERTs on them outside locks
//
dwErr = InvokeCreationAlertForList(
&leSourceList,
pcac-> dwProtocolId, pcac-> dwComponentId,
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
if ( dwErr == NO_ERROR )
{
//
// Lock the interface to prevent the (*, *)
// membership from being deleted while MFEs
// are being updated.
//
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
pieEntry = GetIfEntry(
IF_BUCKET_HEAD( dwIfBucket ),
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
if ( pieEntry == NULL )
{
//
// Interface is no longer present with MGM.
// possibly deleted in another thread.
// There is no further MFE update to be performed.
// quit now.
//
RELEASE_IF_LOCK_SHARED( dwIfBucket );
break;
}
//
// Verify that the (*, *) membership on this interface
// is still present.
// It could have been deleted in a separate thread.
//
dwGrpBucket = GROUP_TABLE_HASH( 0, 0 );
ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket );
pge = GetGroupEntry(
GROUP_BUCKET_HEAD( dwGrpBucket ), 0, 0
);
if ( pge != NULL )
{
ACQUIRE_GROUP_ENTRY_LOCK_SHARED( pge );
dwSrcBucket = SOURCE_TABLE_HASH( 0, 0 );
if ( FindSourceEntry(
SOURCE_BUCKET_HEAD( pge, dwSrcBucket ),
0, 0, &pse, TRUE ) )
{
if ( FindOutInterfaceEntry(
&pse-> leOutIfList,
pcac-> dwIfIndex,
pcac-> dwIfNextHopAddr,
pcac-> dwProtocolId,
pcac-> dwComponentId,
&bNewComp,
&poie ) )
{
//
// (*, *) membership is present on
// this interface
//
//
// Pass 2 : Update all MFEs in this
// bucket as per the results
// of the CREATION_ALERTs
//
AddInterfaceToAllMfeInGroupBucket(
pcac-> dwIfIndex,
pcac-> dwIfNextHopAddr,
pcac-> dwProtocolId,
pcac-> dwComponentId,
dwInd,
pcac-> bIGMP,
TRUE,
&leSourceList
);
}
else
{
//
// (*, *) membership is NO longer
// present on this interface
//
dwInd = GROUP_TABLE_SIZE;
}
}
else
{
//
// (*, *) membership is no longer present
//
dwInd = GROUP_TABLE_SIZE;
}
RELEASE_GROUP_ENTRY_LOCK_SHARED( pge );
}
else
{
//
// (*, *) membership is no longer present
//
dwInd = GROUP_TABLE_SIZE;
}
RELEASE_GROUP_LOCK_SHARED( dwGrpBucket );
RELEASE_IF_LOCK_SHARED( dwIfBucket );
}
FreeList( &leSourceList );
}
FreeList( &leSourceList );
}
//
// For wildard sources i.e. (*, G) membership adds
//
else if ( IS_WILDCARD_SOURCE(
pcac-> dwSourceAddr, pcac-> dwSourceMask
) )
{
do
{
//
// Invoke CREATION_ALERTs for all MFEs for the group
//
dwErr = InvokeCreationAlertForList(
&(pcac-> leSourceList),
pcac-> dwProtocolId,
pcac-> dwComponentId,
pcac-> dwIfIndex,
pcac-> dwIfNextHopAddr
);
if ( dwErr != NO_ERROR )
{
break;
}
//
// Lock the interface to prevent the (*, G)
// membership from being deleted while MFEs
// are being updated.
//
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
bIfLock = TRUE;
pieEntry = GetIfEntry(
IF_BUCKET_HEAD( dwIfBucket ),
pcac-> dwIfIndex,
pcac-> dwIfNextHopAddr
);
if ( pieEntry == NULL )
{
//
// Interface is no longer present with MGM.
// possibly deleted in another thread.
// There is no further MFE update to be performed.
// quit now.
//
TRACE2(
ANY, "InvokeCreationAlert: Interface 0x%x 0x%x"
" is no longer present",
pcac-> dwIfIndex,
pcac-> dwIfNextHopAddr
);
break;
}
//
// Verify that the (*, G) membership is still
// present on the interface
//
dwGrpBucket = GROUP_TABLE_HASH(
pcac-> dwGroupAddr,
pcac-> dwGroupMask
);
ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket );
bGrpLock = TRUE;
pge = GetGroupEntry(
GROUP_BUCKET_HEAD( dwGrpBucket ),
pcac-> dwGroupAddr,
pcac-> dwGroupMask
);
if ( pge == NULL )
{
//
// Group entry no longer present, possibly
// deleted in some other thread
//
TRACE2(
ANY, "InvokeCreationAlert: Group 0x%x 0x%x "
"is no longer present",
pcac-> dwGroupAddr, pcac-> dwGroupMask
);
break;
}
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
bgeLock = TRUE;
dwSrcBucket = SOURCE_TABLE_HASH(
pcac-> dwSourceAddr,
pcac-> dwSourceMask
);
pse = GetSourceEntry(
SOURCE_BUCKET_HEAD( pge, dwSrcBucket ),
pcac-> dwSourceAddr,
pcac-> dwSourceMask
);
if ( pse == NULL )
{
//
// Source entry no longer present, possibly
// deleted in some other thread
//
TRACE2(
ANY, "InvokeCreationAlert: Source 0x%x 0x%x "
"is no longer present",
pcac-> dwSourceAddr, pcac-> dwSourceMask
);
break;
}
poie = GetOutInterfaceEntry(
&pse-> leOutIfList,
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr,
pcac-> dwProtocolId, pcac-> dwComponentId
);
if ( poie == NULL )
{
TRACE2(
ANY, "InvokeCreationAlert: Interface 0x%x 0x%x "
"is no longer present in OIF",
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
break;
}
//
// (*, G) present on this interface.
// Update all for group MFE as per results of
// creation alerts.
//
AddInterfaceToGroupMfe(
pge, pcac-> dwIfIndex, pcac-> dwIfNextHopAddr,
pcac-> dwProtocolId, pcac-> dwComponentId,
pcac-> bIGMP, TRUE, &(pcac-> leSourceList)
);
} while ( FALSE );
//
// release locks
//
if ( bgeLock )
{
RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
bgeLock = FALSE;
}
if ( bGrpLock )
{
RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket );
bgeLock = FALSE;
}
if ( bIfLock )
{
RELEASE_IF_LOCK_SHARED( dwIfBucket );
bIfLock = FALSE;
}
FreeList( &(pcac-> leSourceList) );
}
} while ( FALSE );
if ( bIfLock )
{
RELEASE_IF_LOCK_SHARED( dwIfBucket );
}
RELEASE_PROTOCOL_LOCK_SHARED();
MGM_FREE( pcac );
LEAVE_MGM_WORKER();
TRACE0( GROUP, "LEAVING WorkerFunctionInvokeCreationAlert" );
return;
}
#if 0
v()
{
//
// Ensure interface on which join occured is still present
//
dwIfBucket = IF_TABLE_HASH(
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
bIfLock = TRUE;
pieEntry = GetIfEntry(
IF_BUCKET_HEAD( dwIfBucket ),
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
if ( pieEntry == NULL )
{
TRACE2(
ANY,
"InvokeCreationAlert: Could not find interface 0x%x 0x%x",
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
break;
}
//
// Ensure that group is still joined on the interface. Since this
// is being executed asynchronously, it is possible that between
// the time this work item was queued and the time it gets executed
// the membership may have been deleted.
//
dwGrpBucket = GROUP_TABLE_HASH(
pcac-> dwGroupAddr, pcac-> dwGroupMask
);
ACQUIRE_GROUP_LOCK_SHARED( dwGrpBucket );
bGrpBucket = TRUE;
pge = GetGroupEntry(
GROUP_BUCKET_HEAD( dwGrpBucket ),
pcac-> dwGroupAddr, pcac-> dwGroupMask
);
if ( pge == NULL )
{
TRACE2(
ANY, "InvokeCreationAlert: Could not find group 0x%x 0x%x",
pcac-> dwGroupAddr, pcac-> dwGroupMask
);
break;
}
ACQUIRE_GROUP_ENTRY_LOCK_SHARED( pge );
bGrpLock = TRUE;
dwSrcBucket = SOURCE_TABLE_HASH(
pcac-> dwSourceAddr, pcac-> dwSourceMask
);
pse = GetSourceEntry(
SOURCE_BUCKET_HEAD( pge, dwSrcBucket ),
pcac-> dwSourceAddr, pcac-> dwSourceMask
);
if ( pse == NULL )
{
TRACE2(
ANY, "InvokeCreationAlert: Could not find source 0x%x "
"0x%x",
pcac-> dwSourceAddr, pcac-> dwSourceMask
);
break;
}
if (GetOutInterfaceEntry(
&pse-> leOutIfList,
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr,
pcac-> dwProtocolId, pcac-> dwComponentId
) == NULL)
{
TRACE2(
ANY, "InvokeCreationAlert: Interface 0x%x 0x%x not "
"present in OIF list",
pcac-> dwIfIndex, pcac-> dwIfNextHopAddr
);
break;
}
//
// release locks
//
RELEASE_GROUP_ENTRY_LOCK_SHARED( pge );
bGrpLock = FALSE;
RELEASE_GROUP_LOCK_SHARED( dwGrpBucket );
bGrpBucket = FALSE;
}
#endif