3207 lines
86 KiB
C
3207 lines
86 KiB
C
//============================================================================
|
||
// 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
|