|
|
//============================================================================
// Copyright (c) 1995, Microsoft Corporation
//
// File: packet.c
//
// History:
// V Raman June-25-1997 Created.
//
// New packet processing.
//============================================================================
#include "pchmgm.h"
#pragma hdrstop
BOOL IsMFEPresent( DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, BOOL bAddToForwarder );
DWORD InvokeRPFCallbacks( PPROTOCOL_ENTRY * pppe, PIF_ENTRY * ppieInIf, PDWORD pdwIfBucket, DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, PDWORD pdwInIfIndex, PDWORD pdwInIfNextHopAddr, PDWORD pdwUpstreamNbr, DWORD dwHdrSize, PBYTE pbPacketHdr, PHANDLE phNextHop, PBYTE pbBuffer );
VOID CopyAndMergeIfLists( PLIST_ENTRY pleMfeOutIfList, PLIST_ENTRY pleOutIfList );
VOID CopyAndAppendIfList( PLIST_ENTRY pleMfeIfList, PLIST_ENTRY pleOutIfList, PLIST_ENTRY pleOutIfHead );
VOID CopyAndAppendIfList( PLIST_ENTRY pleMfeIfList, PLIST_ENTRY pleOutIfList, PLIST_ENTRY pleOutIfHead );
VOID InvokeCreationAlert( DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, DWORD dwInIfIndex, DWORD dwInIfNextHopAddr, PLIST_ENTRY pleMfeOutIfList, PDWORD pdwMfeOutIfCount );
BOOL IsListSame( IN PLIST_ENTRY pleHead1, IN PLIST_ENTRY pleHead2 );
VOID FreeList ( IN PLIST_ENTRY pleHead );
//----------------------------------------------------------------------------
// MgmNewPacketReceived
//
//----------------------------------------------------------------------------
DWORD MgmNewPacketReceived( DWORD dwSourceAddr, DWORD dwGroupAddr, DWORD dwInIfIndex, DWORD dwInIfNextHopAddr, DWORD dwHdrSize, PBYTE pbPacketHdr ) {
BOOL bGrpEntryLock = FALSE, bGrpLock = FALSE, bWCGrpEntryLock = FALSE, bWCGrpLock = FALSE,
bGrpFound = FALSE, bSrcFound = FALSE,
bIfLock = FALSE;
DWORD dwErr = NO_ERROR, dwIfBucket, dwUpStreamNbr = 0, dwGroupMask = 0, dwGrpBucket, dwWCGrpBucket, dwSrcBucket, dwWCSrcBucket, dwSourceMask = 0xFFFFFFFF, dwTimeOut = EXPIRY_INTERVAL, dwTimerQ, dwOutIfCount = 0;
PPROTOCOL_ENTRY ppe = NULL;
PIF_ENTRY pieInIf = NULL;
PGROUP_ENTRY pge = NULL, pgeWC = NULL, pgeNew = NULL;
PSOURCE_ENTRY pse = NULL, pseWC = NULL, pseNew = NULL;
POUT_IF_ENTRY poie; PLIST_ENTRY pleGrpList = NULL, pleSrcList = NULL, pleWCGrpList = NULL, pleWCSrcList = NULL, ple, pleTemp;
PTIMER_CONTEXT ptwc = NULL; LIST_ENTRY leMfeOutIfList, lePrevMfeOutIfList;
NTSTATUS ntStatus;
RTM_ENTITY_INFO reiEntityInfo; RTM_DEST_INFO rdiDestInfo; HANDLE hNextHop; BOOL bRelDest = FALSE;
if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; }
TRACE6( ENTER, "ENTERED MgmNewPacketReceived : Source %x, %x : " "Group %x, %x : In Interface : %x, %x", dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwInIfIndex, dwInIfNextHopAddr );
//--------------------------------------------------------------------
// Check if Mfe is already present for this ( source, group ).
// If so add it to the Kernel mode forwarder.
//--------------------------------------------------------------------
if ( IsMFEPresent( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, TRUE ) ) { TRACE1( ENTER, "LEAVING MgmNewPacketReceived %x\n", dwErr );
LEAVE_MGM_API();
return dwErr; }
//--------------------------------------------------------------------
// No Mfe is present for this ( source, group )
//--------------------------------------------------------------------
ACQUIRE_PROTOCOL_LOCK_SHARED();
do { //
// Perform RPF check on the incoming interface.
//
RtlZeroMemory( &rdiDestInfo, sizeof( RTM_DEST_INFO ) ); dwErr = InvokeRPFCallbacks( &ppe, &pieInIf, &dwIfBucket, dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, &dwInIfIndex, &dwInIfNextHopAddr, &dwUpStreamNbr, dwHdrSize, pbPacketHdr, &hNextHop, (PBYTE) &rdiDestInfo );
//
// Something is hosed here.
//
if ( dwErr != NO_ERROR ) { break; }
bRelDest = TRUE; bIfLock = TRUE;
//--------------------------------------------------------------------
// In one of the most dramatic events in the multicast world
// scattered membership entries now morph into an MFE, capable of
// sustaining traffic and bringing multicast applications to life.
//
// Gag-gag-gag-uggggghh. Ok enough bad poetic license.
// Just create the MFE asap. (and get a life please)
//--------------------------------------------------------------------
InitializeListHead( &leMfeOutIfList ); InitializeListHead( &lePrevMfeOutIfList );
//
// Check if there is administrative-scoped boundary for this
// group on the incoming interface
//
if ( IS_HAS_BOUNDARY_CALLBACK() && HAS_BOUNDARY_CALLBACK()( dwInIfIndex, dwGroupAddr ) ) { //
// Admin-scoped bounday exists on incoming interface.
// Create a negative MFE to prevent forwarding of
// traffic for this (S, G)
//
TRACEPACKET2( GROUP, "Admin-scope on for group %lx, incoming interface", dwInIfIndex, dwGroupAddr );
//
// find the group entry
//
dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); bGrpLock = TRUE;
//
// acquire group lock and find group entry in the hash bucket again
//
pleGrpList = GROUP_BUCKET_HEAD( dwGrpBucket ); bGrpFound = FindGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pge, TRUE );
if ( bGrpFound ) { //
// Found group, look up source entry
//
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = TRUE;
dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask );
pleSrcList = SOURCE_BUCKET_HEAD( pge, dwSrcBucket );
bSrcFound = FindSourceEntry( pleSrcList, dwSourceAddr, dwSourceMask, &pse, TRUE ); } }
else { do { //
// No admin-scope on incoming interface. Proceed to create
// the OIF list for this MFE.
//
//
// 1. check if (*, *) entry is present
//
dwWCGrpBucket = GROUP_TABLE_HASH( 0, 0 );
ACQUIRE_GROUP_LOCK_SHARED( dwWCGrpBucket ); bWCGrpLock = TRUE;
pleWCGrpList = GROUP_BUCKET_HEAD( dwWCGrpBucket );
if ( FindGroupEntry( pleWCGrpList, 0, 0, &pgeWC, TRUE ) ) { //
// ok wildcard group entry exists.
// find the wildcard source entry.
//
ACQUIRE_GROUP_ENTRY_LOCK_SHARED( pgeWC ); bWCGrpEntryLock = TRUE; dwWCSrcBucket = SOURCE_TABLE_HASH( 0, 0 );
pleWCSrcList = SOURCE_BUCKET_HEAD( pgeWC, dwWCSrcBucket );
if ( FindSourceEntry( pleWCSrcList, 0, 0, &pseWC, TRUE ) ) { //
// Copy the outgoing interface list for the (*, *) entry
//
InterlockedExchange( &pseWC-> dwInUse, 1 ); CopyAndMergeIfLists( &leMfeOutIfList, &pseWC-> leOutIfList ); } }
//
// 2. check if a (*, G) entry is present.
//
dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask ); ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); bGrpLock = TRUE;
//
// acquire group lock and find group entry in the hash bucket again
//
pleGrpList = GROUP_BUCKET_HEAD( dwGrpBucket ); bGrpFound = FindGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pge, TRUE ); if ( bGrpFound ) { pseWC = NULL; //
// group entry present, check if wildcard source is present
//
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = TRUE;
dwWCSrcBucket = SOURCE_TABLE_HASH( 0, 0 );
pleWCSrcList = SOURCE_BUCKET_HEAD( pge, dwWCSrcBucket );
if ( FindSourceEntry( pleWCSrcList, 0, 0, &pseWC, TRUE ) ) { //
// Merge the OIL of the (*, G) entry with the OIL of
// the (*, *) entry
//
pseWC-> dwInUse = 1; CopyAndMergeIfLists( &leMfeOutIfList, &pseWC-> leOutIfList ); }
//
// 3. Check if (S, G) entry is present
//
dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask );
pleSrcList = SOURCE_BUCKET_HEAD( pge, dwSrcBucket );
bSrcFound = FindSourceEntry( pleSrcList, dwSourceAddr, dwSourceMask, &pse, TRUE );
if ( bSrcFound ) { //
// Source Entry present. Merge with source OIL
//
pse-> dwInUse = 1; CopyAndMergeIfLists( &leMfeOutIfList, &pse-> leOutIfList ); } } //
// If OIF list is empty, no CREATION_ALERTs required.
//
if ( IsListEmpty( &leMfeOutIfList ) ) { FreeList( &lePrevMfeOutIfList ); InitializeListHead( &lePrevMfeOutIfList ); break; }
//
// Check if OIF list is the same as previous iteration
//
if ( IsListSame( &lePrevMfeOutIfList, &leMfeOutIfList ) ) { FreeList( &leMfeOutIfList ); break; }
//--------------------------------------------------------------------
// It's callback time
//--------------------------------------------------------------------
//
// release all locks before invoking the CREATION_ALERT callback
//
if ( bGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = FALSE; }
if ( bGrpLock ) { RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); bGrpLock = FALSE; }
if ( bWCGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_SHARED( pgeWC ); bWCGrpEntryLock = FALSE; } if ( bWCGrpLock ) { RELEASE_GROUP_LOCK_SHARED( dwWCGrpBucket ); bWCGrpLock = FALSE; }
RELEASE_IF_LOCK_SHARED( dwIfBucket );
bGrpFound = FALSE; bSrcFound = FALSE; //
// invoked creation alert for each protocol component that
// has an interface in the OIL.
//
InvokeCreationAlert( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwInIfIndex, dwInIfNextHopAddr, &leMfeOutIfList, &dwOutIfCount );
//
// Save list from previous iteration
//
FreeList( &lePrevMfeOutIfList );
lePrevMfeOutIfList = leMfeOutIfList;
leMfeOutIfList.Flink-> Blink = &lePrevMfeOutIfList;
leMfeOutIfList.Blink-> Flink = &lePrevMfeOutIfList;
InitializeListHead( &leMfeOutIfList );
ACQUIRE_IF_LOCK_SHARED( dwIfBucket );
} while (TRUE); } //
// if OIL is empty, invoke deletion alert for the protocol component
// on the incoming interface interface
//
if ( IsListEmpty( &lePrevMfeOutIfList ) ) { //
// Outgoing interface list is empty for this MFE
// Invoke deleteion alert on the protocol component on the
// incoming interface
//
if ( IS_PRUNE_ALERT( ppe ) ) { PRUNE_ALERT( ppe ) ( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwInIfIndex, dwInIfNextHopAddr, FALSE, &dwTimeOut ); } }
//
// if there was no group entry, create one
//
if ( !bGrpFound ) { if ( pge != NULL ) { dwErr = CreateGroupEntry( &pge-> leGrpHashList, dwGroupAddr, dwGroupMask, &pgeNew ); }
else { dwErr = CreateGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pgeNew ); } if ( dwErr != NO_ERROR ) { break; }
pge = pgeNew;
//
// find source hash bucket
//
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); bGrpEntryLock = TRUE; dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask );
pleSrcList = SOURCE_BUCKET_HEAD( pge, dwSrcBucket ); }
//
// if there was no source entry
//
if ( !bSrcFound ) { if ( pse != NULL ) { dwErr = CreateSourceEntry( pge, &pse-> leSrcHashList, dwSourceAddr, dwSourceMask, &pseNew ); }
else { dwErr = CreateSourceEntry( pge, pleSrcList, dwSourceAddr, dwSourceMask, &pseNew ); }
if ( dwErr != NO_ERROR ) { break; }
pse = pseNew;
pge-> dwSourceCount++; }
//
// Set incoming interface
//
pse-> dwInIfIndex = dwInIfIndex;
pse-> dwInIfNextHopAddr = dwInIfNextHopAddr;
pse-> dwUpstreamNeighbor = dwUpStreamNbr;
pse-> dwInProtocolId = ppe-> dwProtocolId;
pse-> dwInComponentId = ppe-> dwComponentId;
//
// Set route information
//
dwErr = RtmGetEntityInfo( g_hRtmHandle, rdiDestInfo.ViewInfo[ 0 ].Owner, &reiEntityInfo );
if ( dwErr != NO_ERROR ) { TRACEPACKET1( ANY, "failed to get entity info : %x", dwErr );
pse-> dwRouteProtocol = 0; } else { pse-> dwRouteProtocol = reiEntityInfo.EntityId.EntityProtocolId; }
pse-> dwRouteNetwork = *( (PDWORD) rdiDestInfo.DestAddress.AddrBits ); pse-> dwRouteMask = RTM_IPV4_MASK_FROM_LEN( rdiDestInfo.DestAddress.NumBits ); pse-> bInForwarder = TRUE; //
// save timeout in seconds and creation time
//
pse-> dwTimeOut = dwTimeOut / 1000; NtQuerySystemTime( &pse-> liCreationTime );
//
// save the MFE OIL
//
if ( !IsListEmpty( &lePrevMfeOutIfList ) ) { pse-> dwMfeIfCount = dwOutIfCount;
pse-> leMfeIfList = lePrevMfeOutIfList;
lePrevMfeOutIfList.Flink-> Blink = &pse-> leMfeIfList;
lePrevMfeOutIfList.Blink-> Flink = &pse-> leMfeIfList;
//
// Free OIF entries on which forwarding is disabled
//
ple = pse-> leMfeIfList.Flink;
while ( ple != &pse-> leMfeIfList ) { poie = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList );
pleTemp = ple-> Flink; if ( !poie-> wForward ) { RemoveEntryList( ple );
MGM_FREE( poie ); }
ple = pleTemp; } }
//
// add a reference for the incoming interface
//
AddSourceToRefList( &pieInIf-> leInIfList, dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, IS_PROTOCOL_IGMP( ppe ) ); //
// Set the MFE in the forwarder.
//
AddMfeToForwarder( pge, pse, dwTimeOut );
//
// create timer entry and store timer object
//
//
// allocate a timer context structure
//
ptwc = MGM_ALLOC( sizeof( TIMER_CONTEXT ) );
if ( ptwc == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY;
TRACE1( ANY, "Failed to allocate timer context of size : %d", sizeof( TIMER_CONTEXT ) );
LOGERR0( HEAP_ALLOC_FAILED, dwErr );
break; }
ptwc-> dwSourceAddr = pse-> dwSourceAddr; ptwc-> dwSourceMask = pse-> dwSourceMask;
ptwc-> dwGroupAddr = pge-> dwGroupAddr; ptwc-> dwGroupMask = pge-> dwGroupMask;
ptwc-> dwIfIndex = pse-> dwInIfIndex;
ptwc-> dwIfNextHopAddr = pse-> dwInIfNextHopAddr;
//
// Add timer to appropriate timer Q
//
dwTimerQ = TIMER_TABLE_HASH( pge-> dwGroupAddr ); ntStatus = RtlCreateTimer( TIMER_QUEUE_HANDLE( dwTimerQ ), &pse-> hTimer, MFETimerProc, ptwc, dwTimeOut, 0, 0 );
if ( !NT_SUCCESS( ntStatus ) ) { TRACE1( ANY, "Timer set failed with status %lx", ntStatus );
LOGERR0( INVALID_TIMER_HANDLE, ntStatus ); }
} while ( FALSE );
//
// Release locks and quit
//
if ( bGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); }
if ( bGrpLock ) { RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket ); } if ( bWCGrpEntryLock ) { RELEASE_GROUP_ENTRY_LOCK_SHARED( pgeWC ); } if ( bWCGrpLock ) { RELEASE_GROUP_LOCK_SHARED( dwWCGrpBucket ); }
if ( bIfLock ) { RELEASE_IF_LOCK_SHARED( dwIfBucket ); }
//
// Add route retuned by RPF check to route table.
//
if ( dwErr == NO_ERROR ) { AddSourceGroupToRouteRefList( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, hNextHop, (PBYTE) &rdiDestInfo ); }
if ( bRelDest ) { RtmReleaseDestInfo( g_hRtmHandle, &rdiDestInfo ); }
RELEASE_PROTOCOL_LOCK_SHARED();
TRACE1( ENTER, "LEAVING MgmNewPacketReceived %x\n", dwErr );
LEAVE_MGM_API();
return dwErr; }
//----------------------------------------------------------------------------
// IsMFEPresent
//
// Check if MFE is present for a given (source, group). If it is add it to
// to the kernel mode forwarder.
//----------------------------------------------------------------------------
BOOL IsMFEPresent( DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, BOOL bAddToForwarder ) {
BOOL bMfeFound = FALSE;
DWORD dwGrpBucket, dwSrcBucket;
PLIST_ENTRY pleGrpList, pleSrcList;
PGROUP_ENTRY pge = NULL;
PSOURCE_ENTRY pse = NULL;
//
// check MFE is present for the specified (source, group)
//
dwGrpBucket = GROUP_TABLE_HASH( dwGroupAddr, dwGroupMask );
ACQUIRE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket );
pleGrpList = GROUP_BUCKET_HEAD( dwGrpBucket ); if ( FindGroupEntry( pleGrpList, dwGroupAddr, dwGroupMask, &pge, TRUE ) ) { //
// group entry exists, find source entry
//
ACQUIRE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge );
dwSrcBucket = SOURCE_TABLE_HASH( dwSourceAddr, dwSourceMask );
pleSrcList = SOURCE_BUCKET_HEAD( pge, dwSrcBucket );
if ( FindSourceEntry( pleSrcList, dwSourceAddr, dwSourceMask, &pse, TRUE ) ) { //
// Source entry exists, Is this source entry an MFE ?
//
if ( IS_VALID_INTERFACE( pse-> dwInIfIndex, pse-> dwInIfNextHopAddr ) ) { if ( bAddToForwarder ) {
//
// MFE exists, set it to the forwarder
//
AddMfeToForwarder( pge, pse, 0 );
pse-> bInForwarder = TRUE; } bMfeFound = TRUE;
} }
RELEASE_GROUP_ENTRY_LOCK_EXCLUSIVE( pge ); }
RELEASE_GROUP_LOCK_EXCLUSIVE( dwGrpBucket );
return bMfeFound; }
//----------------------------------------------------------------------------
// InvokeRPFCallbacks
//
// Assumes that the protocol list and the interface bucket are read locked
//----------------------------------------------------------------------------
DWORD InvokeRPFCallbacks( PPROTOCOL_ENTRY * pppe, PIF_ENTRY * ppieInIf, PDWORD pdwIfBucket, DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, PDWORD pdwInIfIndex, PDWORD pdwInIfNextHopAddr, PDWORD pdwUpStreamNbr, DWORD dwHdrSize, PBYTE pbPacketHdr, PHANDLE phNextHop, PBYTE pbBuffer ) { BOOL bFound = FALSE, bIfLock = FALSE; DWORD dwErr, dwCount = 0, dwNewIfBucket; PPROTOCOL_ENTRY ppe = NULL;
PLIST_ENTRY pleIfList; BOOL bRelNextHop = FALSE; RTM_NET_ADDRESS rnaSource;
PRTM_DEST_INFO prdiDestInfo = (PRTM_DEST_INFO) pbBuffer;
RTM_NEXTHOP_INFO rniNextHopInfo;
TRACEPACKET2( PACKET, "ENTERED InvokeRPFCallbacks : In interface : %x, %x", *pdwInIfIndex, *pdwInIfNextHopAddr ); *pppe = NULL; do { //
// format the address
//
RTM_IPV4_MAKE_NET_ADDRESS( &rnaSource, dwSourceAddr, IPv4_ADDR_LEN );
//
// lookup route
//
dwErr = RtmGetMostSpecificDestination( g_hRtmHandle, &rnaSource, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_MCAST, prdiDestInfo ); if ( dwErr != NO_ERROR ) { dwErr = ERROR_CAN_NOT_COMPLETE;
TRACE1( ANY, "No Route to source %x", dwSourceAddr );
break; }
//
// Pick NHOP
//
*phNextHop = SelectNextHop( prdiDestInfo );
if ( *phNextHop == NULL ) { dwErr = ERROR_CAN_NOT_COMPLETE;
TRACE1( ANY, "No NextHop to source %x", dwSourceAddr );
break; }
//
// Get NHOP info
//
dwErr = RtmGetNextHopInfo( g_hRtmHandle, *phNextHop, &rniNextHopInfo );
if ( ( dwErr != NO_ERROR ) || ( rniNextHopInfo.State != RTM_NEXTHOP_STATE_CREATED ) ) { dwErr = ERROR_CAN_NOT_COMPLETE;
TRACE1( ANY, "No Nexthop info to source %x", dwSourceAddr );
break; }
bRelNextHop = TRUE;
//
// Set the incoming interface as per the route table
//
*pdwInIfIndex = rniNextHopInfo.InterfaceIndex;
//
// The next hop is set to zero by default. This is fine for
// Ethernet and P2P interfaces where this value is 0 for the
// the corresponding IF entries in the IF table.
// But for Point to Multi Point interfaces such
// as the RAS server (internal) interface or NBMA interfaces,
// the NHOP field is used to distinguish interfaces that
// share an IF index. e.g. RAS clients all connect on the
// same interface and are distinguished by different NHOP
// values. Consequently to find an entry in the IF hash
// table we need the (IF index, NHOP) pair.
//
// Here we run into a special case. The new interface as
// determined by the route lookup above gives just an IF
// index. So we have an IF index.
// How do we get a NHOP on this interface ?
//
// (The reason for looking up an interface here is that we
// would like to determine the protocol component that owns
// it and then invoke the RPF callback of that protocol
// component).
//
// The solution to this is based on two assumptions.
// One is
// that only one protocol runs on an interface (single IF
// index). This is true for P2MP interfaces too. So to
// determine the protocol on an interface all one needs to
// do is to find any IF entry that has the same IF index
// (immaterial of the NEXT HOP).
//
// Second is that, all interfaces with the same IF index
// hash to the same bucket in the IF table.
// So all the NHOP on a P2MP will be present in the same
// hash bucket. Also if the route lookup yields say
// IF index X, then looking up (X, (NHOP) 0) in the IF hash
// table will result in finding either IF entry (X, 0) for
// ethernet or P2P interfaces OR IF entry (X, Y) for P2MP
// interfaces where Y is the first among the multiple NHOPs
// that share the same IF index. If neither exists then
// we assume that no entry exits for an interface with
// IF index X and we report an error and quit.
//
// On success we can determine the protocol on IF index X.
//
// All this since we have a hash table index of (IF index,
// NHOP) and we need to look having only a partial key.
//
*pdwInIfNextHopAddr = 0;
TRACEPACKET2( PACKET, "New incoming interface : %d, %d", *pdwInIfIndex, *pdwInIfNextHopAddr ); //
// get the new incoming interface entry
//
dwNewIfBucket = IF_TABLE_HASH( *pdwInIfIndex );
ACQUIRE_IF_LOCK_SHARED( dwNewIfBucket ); bIfLock = TRUE;
*pdwIfBucket = dwNewIfBucket;
bFound = FindIfEntry( IF_BUCKET_HEAD( dwNewIfBucket), *pdwInIfIndex, *pdwInIfNextHopAddr, ppieInIf );
//
// Check if the interface index of this interface is the same
// as that of the incoming interface. Since we are looking
// up the interface purely on IF index and not on
// IF index/NEXTHOP,
// there is a chance for point to multipoint interface e.g.
// RAS server interface, that we could have found a different
// interface
//
if ( ( *ppieInIf == NULL ) || ( (*ppieInIf)-> dwIfIndex != *pdwInIfIndex ) ) { dwErr = ERROR_CAN_NOT_COMPLETE; TRACE3( ANY, "InvokeRPFCallbacks : New incoming Interface not" " found : %x, %x, %x", *pdwInIfIndex, *pdwInIfNextHopAddr, *ppieInIf );
LOGWARN0( IF_NOT_FOUND, dwErr );
break; }
//
// The incoming interface is now correct as per the route table
// Look up the protocol on this interface.
//
ppe = GetProtocolEntry( PROTOCOL_LIST_HEAD(), (*ppieInIf)-> dwOwningProtocol, (*ppieInIf)-> dwOwningComponent );
if ( ppe == NULL ) { //
// Internal MGM inconsistency. Interface exists
// but the protocol on it does not. Should not
// happen.
//
dwErr = ERROR_CAN_NOT_COMPLETE;
TRACE2( ANY, "InvokeRPFCallbacks : No protocol entry for" "incoming interface : %x, %x", (*ppieInIf)-> dwOwningProtocol, (*ppieInIf)-> dwOwningComponent );
break; }
TRACEPACKET2( PACKET, "ProtocolEntry for packet %x, %x", ppe-> dwProtocolId, ppe-> dwComponentId ); //
// Protocol entry found. Invoke its RPF callback
//
if ( !( IS_RPF_CALLBACK( ppe ) ) ) { //
// No RPF callback provided by the protocol on the
// incoming interface.
//
dwErr = NO_ERROR;
TRACEPACKET4( ANY, "InvokeRPFCallbacks : No RPF callback for " "protocol %x, %x on incoming interface %x, %x", (*ppieInIf)-> dwOwningProtocol, (*ppieInIf)-> dwOwningComponent, (*ppieInIf)-> dwIfIndex, (*ppieInIf)-> dwIfNextHopAddr );
break; }
dwErr = RPF_CALLBACK( ppe )( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, pdwInIfIndex, pdwInIfNextHopAddr, pdwUpStreamNbr, dwHdrSize, pbPacketHdr, pbBuffer );
if ( dwErr == ERROR_INVALID_PARAMETER ) { //
// In the RPF callback the protocol component has
// changed the incoming interface again. Make sure
// to set the IF bucket value correctly
//
dwNewIfBucket = IF_TABLE_HASH( *pdwInIfIndex );
//
// if this interface is in another hash bucket
//
if ( *pdwIfBucket != dwNewIfBucket ) { RELEASE_IF_LOCK_SHARED( *pdwIfBucket ); ACQUIRE_IF_LOCK_SHARED( dwNewIfBucket );
*pdwIfBucket = dwNewIfBucket; }
//
// Find the interface entry corresp. to the
// IF/NHOP as per the protocol
//
TRACEPACKET2( PACKET, "RPF check returned interface : %x, %x", *pdwInIfIndex, *pdwInIfNextHopAddr ); if ( FindIfEntry( IF_BUCKET_HEAD( dwNewIfBucket ), *pdwInIfIndex, *pdwInIfNextHopAddr, ppieInIf ) ) { dwErr = NO_ERROR; } else { dwErr = ERROR_CAN_NOT_COMPLETE; } } } while ( FALSE );
//
// Clean up
//
if ( bRelNextHop ) { if ( RtmReleaseNextHopInfo( g_hRtmHandle, &rniNextHopInfo ) != NO_ERROR ) { TRACE1( ANY, "Failed to release next hop : %x", dwErr ); } }
if ( ( dwErr != NO_ERROR ) && ( bIfLock ) ) { RELEASE_IF_LOCK_SHARED( dwNewIfBucket ); }
//
// set up the return parameters
//
//
// TDB : in that we need to set a negative MFE if the RPF callback fails
// without generating a route
*pppe = ppe;
TRACE1( PACKET, "LEAVING RPF Callback : %d", dwErr ); return dwErr; }
//----------------------------------------------------------------------------
// CopyAndMergeIfLists
//
//----------------------------------------------------------------------------
VOID CopyAndMergeIfLists( PLIST_ENTRY pleMfeOutIfList, PLIST_ENTRY pleOutIfList ) { BOOL bFound = FALSE;
INT iCmp = 0; DWORD dwErr = NO_ERROR; POUT_IF_ENTRY poieOut = NULL, poieMfe = NULL, poie = NULL; PLIST_ENTRY pleMfe = NULL, pleOut = NULL;
do { if ( IsListEmpty( pleOutIfList ) ) { break; }
if ( IsListEmpty( pleMfeOutIfList ) ) { CopyAndAppendIfList( pleMfeOutIfList, pleOutIfList->Flink, pleOutIfList );
break; }
pleMfe = pleMfeOutIfList-> Flink;
pleOut = pleOutIfList-> Flink;
while ( pleMfe != pleMfeOutIfList && pleOut != pleOutIfList && dwErr == NO_ERROR ) { poieOut = CONTAINING_RECORD( pleOut, OUT_IF_ENTRY, leIfList );
//
// find location to insert new entry
//
bFound = FALSE; for ( ; pleMfe != pleMfeOutIfList; pleMfe = pleMfe-> Flink ) { poieMfe = CONTAINING_RECORD( pleMfe, OUT_IF_ENTRY, leIfList ); if ( poieMfe-> dwProtocolId < poieOut-> dwProtocolId ) { continue; }
else if ( poieMfe-> dwProtocolId > poieOut-> dwProtocolId ) { //
// Interface entry not found
//
break; }
//
// same protocol
//
//
// is same component
//
if ( poieMfe-> dwComponentId < poieOut-> dwComponentId ) { continue; }
else if ( poieMfe-> dwComponentId > poieOut-> dwComponentId ) { //
// Interface entry not found
//
break; }
//
// same component
//
//
// is same interface
//
if ( poieMfe-> dwIfIndex < poieOut-> dwIfIndex ) { continue; }
else if ( poieMfe-> dwIfIndex > poieOut-> dwIfIndex ) { //
// interface not found
//
break; }
//
// is same next hop addr
// to do IP address comparison function.
//
if ( INET_CMP( poieMfe-> dwIfNextHopAddr, poieOut-> dwIfNextHopAddr, iCmp ) < 0 ) { continue; }
else if ( iCmp > 0 ) { //
// interface not found
//
break; }
//
// Interface found
//
bFound = TRUE; break; }
if ( bFound ) { //
// Update entry in the Mfe out list
//
if ( IS_ADDED_BY_IGMP( poieOut ) ) { SET_ADDED_BY_IGMP( poieMfe );
poieMfe-> wNumAddsByIGMP += poieOut-> wNumAddsByIGMP; }
if ( IS_ADDED_BY_PROTOCOL( poieOut ) ) { SET_ADDED_BY_PROTOCOL( poieMfe );
poieMfe-> wNumAddsByRP += poieOut-> wNumAddsByRP; }
pleMfe = pleMfe-> Flink; }
else { //
// no matching entry in the mfe list
//
poie = MGM_ALLOC( sizeof( OUT_IF_ENTRY ) );
if ( poie == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY;
TRACE1( ANY, "CreateOutInterfaceEntry : Could not allocate" "out interface entry %x", dwErr );
LOGERR0( HEAP_ALLOC_FAILED, dwErr );
break; }
CopyMemory( poie, poieOut, sizeof( OUT_IF_ENTRY ) );
InsertTailList( pleMfe, &poie-> leIfList ); }
pleOut = pleOut-> Flink; }
if ( dwErr != NO_ERROR ) { break; }
//
// if entries remain in the out list
//
if ( pleOut != pleOutIfList ) { CopyAndAppendIfList( pleMfeOutIfList, pleOut, pleOutIfList ); } } while ( FALSE );
return; }
//----------------------------------------------------------------------------
// CopyAndAppendIfList
//
//----------------------------------------------------------------------------
VOID CopyAndAppendIfList( PLIST_ENTRY pleMfeIfList, PLIST_ENTRY pleOutIfList, PLIST_ENTRY pleOutIfHead ) { DWORD dwErr = NO_ERROR; POUT_IF_ENTRY poieOut = NULL, poie = NULL;
for ( ;pleOutIfList != pleOutIfHead; pleOutIfList = pleOutIfList-> Flink ) { poieOut = CONTAINING_RECORD( pleOutIfList, OUT_IF_ENTRY, leIfList );
poie = MGM_ALLOC( sizeof( OUT_IF_ENTRY ) );
if ( poie == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY;
TRACE1( ANY, "CopyAndAppendIfList : Could not allocate" "out interface entry %x", dwErr );
LOGERR0( HEAP_ALLOC_FAILED, dwErr );
break; }
CopyMemory( poie, poieOut, sizeof( OUT_IF_ENTRY ) );
InsertTailList( pleMfeIfList, &poie-> leIfList ); } }
//----------------------------------------------------------------------------
// InvokeCreationAlert
//
//----------------------------------------------------------------------------
VOID InvokeCreationAlert( DWORD dwSourceAddr, DWORD dwSourceMask, DWORD dwGroupAddr, DWORD dwGroupMask, DWORD dwInIfIndex, DWORD dwInIfNextHopAddr, PLIST_ENTRY pleMfeOutIfList, PDWORD pdwMfeOutIfCount ) { DWORD dwCount = 0, dwErr = NO_ERROR, dwInd;
PPROTOCOL_ENTRY ppe = NULL; POUT_IF_ENTRY poieFirst, poieNext, poieTemp;
PMGM_IF_ENTRY pmie = NULL; PLIST_ENTRY ple = NULL, pleFirst = NULL, pleTemp = NULL;
TRACEPACKET6( PACKET, "ENTERED InvokeCreationAlert : Source %x, %x : Group : %x, %x" " : Interface %x, %x", dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwInIfIndex, dwInIfNextHopAddr );
//
// remove the incoming interface from the list of outgoing interfaces.
// remove all interfaces that have have an scope-boundary for this group.
//
ple = pleMfeOutIfList-> Flink;
while ( ple != pleMfeOutIfList ) { poieTemp = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList );
ple = ple-> Flink; //
// Check if this is the incoming interface or
// if this interface has a scope-boundary for this group
//
if ( ( ( poieTemp-> dwIfIndex == dwInIfIndex ) && ( poieTemp-> dwIfNextHopAddr == dwInIfNextHopAddr ) ) || ( IS_HAS_BOUNDARY_CALLBACK() && HAS_BOUNDARY_CALLBACK()( poieTemp-> dwIfIndex, dwGroupAddr ) ) ) { #if 1
poieTemp-> wForward = 0; #else
RemoveEntryList( &poieTemp-> leIfList );
MGM_FREE( poieTemp ); #endif
} }
//
// invoke creation alerts for all components with interfaces in the OIL
//
ple = pleMfeOutIfList-> Flink; while ( ple != pleMfeOutIfList ) { //
// The OIL is sorted by components i.e. all interfaces for
// a component are bunched together.
//
// Save the start of the interfaces for current component
//
pleFirst = ple;
poieFirst = CONTAINING_RECORD( pleFirst, OUT_IF_ENTRY, leIfList );
//
// Count all interfaces for same component
//
dwCount = 0;
while ( ple != pleMfeOutIfList ) { poieNext = CONTAINING_RECORD( ple, OUT_IF_ENTRY, leIfList );
#if 1
if ( !poieNext-> wForward ) { ple = ple-> Flink; continue; } #endif
if ( poieNext-> dwProtocolId != poieFirst-> dwProtocolId || poieNext-> dwComponentId != poieFirst-> dwComponentId ) { break; }
//
// another outgoing interface for the same protocol
//
dwCount++;
ple = ple-> Flink; }
//
// check if we have atleast one out interface entry
// If not move to next protocol component in the OIL
//
if ( dwCount == 0 ) { continue; }
TRACEPACKET3( PACKET, "Out If count %d for component %x %x", dwCount, poieFirst-> dwProtocolId, poieFirst-> dwComponentId );
pmie = MGM_ALLOC( sizeof( MGM_IF_ENTRY ) * dwCount );
if ( pmie == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY;
TRACE1( ANY, "CopyAndAppendIfList : Could not allocate" "out interface entry %x", dwErr );
LOGERR0( HEAP_ALLOC_FAILED, dwErr );
break; }
//
// fill up buffer with list of interfaces for the
// the protocol component and invoke its creation alert.
//
pleTemp = pleFirst; for ( dwInd = 0; dwInd < dwCount; dwInd++ ) { poieTemp = CONTAINING_RECORD( pleTemp, OUT_IF_ENTRY, leIfList );
#if 1
if ( !poieTemp-> wForward ) { pleTemp = pleTemp-> Flink; continue; } #endif
pmie[ dwInd ].dwIfIndex = poieTemp-> dwIfIndex; pmie[ dwInd ].dwIfNextHopAddr = poieTemp-> dwIfNextHopAddr;
pmie[ dwInd ].bIsEnabled = TRUE;
pmie[ dwInd ].bIGMP = IS_ADDED_BY_IGMP( poieTemp );
pleTemp = pleTemp-> Flink; }
ppe = GetProtocolEntry( PROTOCOL_LIST_HEAD(), poieFirst-> dwProtocolId, poieFirst-> dwComponentId ); if ( IS_CREATION_ALERT( ppe ) ) { CREATION_ALERT( ppe )( dwSourceAddr, dwSourceMask, dwGroupAddr, dwGroupMask, dwInIfIndex, dwInIfNextHopAddr, dwCount, pmie ); }
//
// Accumulate the count of OIF
//
*pdwMfeOutIfCount += dwCount;
//
// remove the interface the are flaged as disabled
//
pleTemp = pleFirst; for ( dwInd = 0; dwInd < dwCount; dwInd++ ) { poieTemp = CONTAINING_RECORD( pleTemp, OUT_IF_ENTRY, leIfList ); ple = pleTemp-> Flink; if ( !pmie[ dwInd ].bIsEnabled ) { //
// Forwarding for this (S, G) for this interface has been
// disabled by the protocol
//
#if 1
poieTemp-> wForward = 0; #else
RemoveEntryList( pleTemp );
MGM_FREE( poieTemp ); #endif
(*pdwMfeOutIfCount)--; }
pleTemp = ple; }
MGM_FREE( pmie ); }
TRACEPACKET2( PACKET, "LEAVING InvokeCreationAlert : count %x, error : %x", *pdwMfeOutIfCount, dwErr ); }
//----------------------------------------------------------------------------
// WrongIfFromForwarder
//
//----------------------------------------------------------------------------
DWORD WrongIfFromForwarder( IN DWORD dwSourceAddr, IN DWORD dwGroupAddr, IN DWORD dwInIfIndex, IN DWORD dwInIfNextHopAddr, IN DWORD dwHdrSize, IN PBYTE pbPacketHdr ) { DWORD dwErr = NO_ERROR; if ( !ENTER_MGM_API() ) { return ERROR_CAN_NOT_COMPLETE; }
TRACE3( PACKET, "ENTERED WrongIfFromForwarder for (%lx, %lx) on interface " " %lx", dwSourceAddr, dwGroupAddr, dwInIfIndex );
TRACE1( PACKET, "LEAVING WrongIfFromForwarder : %lx\n", dwErr );
LEAVE_MGM_API(); return dwErr; }
//----------------------------------------------------------------------------
// FreeList
//
//----------------------------------------------------------------------------
VOID FreeList ( IN PLIST_ENTRY pleHead ) { PLIST_ENTRY ple, pleTemp;
if ( IsListEmpty( pleHead ) ) { return; } ple = pleHead-> Flink;
while ( ple != pleHead ) { pleTemp = ple-> Flink;
RemoveEntryList( ple );
MGM_FREE( ple );
ple = pleTemp; } }
//----------------------------------------------------------------------------
// IsListSame
//
//----------------------------------------------------------------------------
BOOL IsListSame( IN PLIST_ENTRY pleHead1, IN PLIST_ENTRY pleHead2 ) { PLIST_ENTRY ple1, ple2;
POUT_IF_ENTRY poif1, poif2;
//
// Check for empty lists
//
if ( ( IsListEmpty( pleHead1 ) && !IsListEmpty( pleHead2 ) ) || ( !IsListEmpty( pleHead1 ) && IsListEmpty( pleHead2 ) ) ) { return FALSE; }
if ( IsListEmpty( pleHead1 ) && IsListEmpty( pleHead2 ) ) { return TRUE; }
//
// walk lists in tandem and verify equality
//
ple1 = pleHead1-> Flink; ple2 = pleHead2-> Flink;
do { poif1 = CONTAINING_RECORD( ple1, OUT_IF_ENTRY, leIfList ); poif2 = CONTAINING_RECORD( ple2, OUT_IF_ENTRY, leIfList );
if ( ( poif1-> dwIfIndex != poif2-> dwIfIndex ) || ( poif1-> dwIfNextHopAddr != poif2-> dwIfNextHopAddr ) ) { return FALSE; }
ple1 = ple1-> Flink; ple2 = ple2-> Flink; } while ( ( ple1 != pleHead1 ) && ( ple2 != pleHead2 ) );
//
// If both lists have reached their ends, they match else they don't
//
if ( ( ( ple1 != pleHead1 ) && ( ple2 == pleHead2 ) ) || ( ( ple1 == pleHead1 ) && ( ple2 != pleHead2 ) ) ) { return FALSE; }
else { return TRUE; } }
|