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