You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1632 lines
37 KiB
1632 lines
37 KiB
//============================================================================
|
|
// Copyright (c) 1995, Microsoft Corporation
|
|
//
|
|
// File: route.c
|
|
//
|
|
// History:
|
|
// V Raman Feb-5-1998 Created.
|
|
//
|
|
// Routines that manipulate routes entries
|
|
//============================================================================
|
|
|
|
|
|
#include "pchmgm.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Route reference operations
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------------
|
|
// AddSourceGroupToRouteRefList
|
|
//
|
|
// This function inserts a reference for each MFE that uses this route
|
|
// for it RPF check. It is invoked by the new packet function on creation
|
|
// of an MFE.
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
AddSourceGroupToRouteRefList(
|
|
DWORD dwSourceAddr,
|
|
DWORD dwSourceMask,
|
|
DWORD dwGroupAddr,
|
|
DWORD dwGroupMask,
|
|
HANDLE hNextHop,
|
|
PBYTE pbBuffer
|
|
)
|
|
{
|
|
BOOL bUnLock = FALSE, bMark = FALSE;
|
|
|
|
DWORD dwErr;
|
|
|
|
PMGM_LOCKED_LIST pmllMfeList;
|
|
|
|
PBYTE pbOpaqueInfo = NULL;
|
|
|
|
PRTM_DEST_INFO prdi = (PRTM_DEST_INFO) pbBuffer;
|
|
|
|
PROUTE_REFERENCE_ENTRY prre = NULL, prreNew = NULL;
|
|
|
|
|
|
|
|
TRACEROUTE0( ROUTE, "ENTERED AddSourceGroupToRouteRefList" );
|
|
|
|
do
|
|
{
|
|
//
|
|
// Create a route reference entry
|
|
//
|
|
|
|
prre = MGM_ALLOC( sizeof( ROUTE_REFERENCE_ENTRY ) );
|
|
|
|
if ( prre == NULL )
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
TRACE1(
|
|
ANY, "Failed to allocate %d bytes",
|
|
sizeof( ROUTE_REFERENCE_ENTRY )
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
prre-> dwSourceAddr = dwSourceAddr;
|
|
prre-> dwSourceMask = dwSourceMask;
|
|
|
|
prre-> dwGroupAddr = dwGroupAddr;
|
|
prre-> dwGroupMask = dwGroupMask;
|
|
|
|
prre-> hNextHop = hNextHop;
|
|
|
|
InitializeListHead ( &prre-> leRefList );
|
|
|
|
|
|
//
|
|
// Lock the dest
|
|
//
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, TRUE, TRUE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
bUnLock = TRUE;
|
|
|
|
|
|
//
|
|
// Get the opaque pointer
|
|
//
|
|
|
|
dwErr = RtmGetOpaqueInformationPointer(
|
|
g_hRtmHandle, prdi-> DestHandle, &pbOpaqueInfo
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to retrieve opaque pointer %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
if ( *( ( PBYTE * ) pbOpaqueInfo ) == NULL )
|
|
{
|
|
//
|
|
// NULL opaque pointer implies this is the first MFe that
|
|
// depends on this route
|
|
//
|
|
|
|
//
|
|
// create a locked list
|
|
//
|
|
|
|
pmllMfeList = MGM_ALLOC( sizeof( MGM_LOCKED_LIST ) );
|
|
|
|
if ( pmllMfeList == NULL )
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
TRACE1(
|
|
ANY, "AddSourceGroupToRouteRefList : "
|
|
"Failed to allocate route ref list %x", dwErr
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
CREATE_LOCKED_LIST( pmllMfeList );
|
|
|
|
//
|
|
// insert the element into the list
|
|
//
|
|
|
|
InsertTailList(
|
|
&( pmllMfeList-> leHead ), &( prre-> leRefList )
|
|
);
|
|
|
|
|
|
//
|
|
// set the opaque pointer
|
|
//
|
|
|
|
*( ( PBYTE *) pbOpaqueInfo ) = (PBYTE) pmllMfeList;
|
|
|
|
|
|
//
|
|
// Mark the destination
|
|
//
|
|
|
|
bMark = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
pmllMfeList = ( PMGM_LOCKED_LIST ) *( ( PBYTE *) pbOpaqueInfo );
|
|
|
|
//
|
|
// Acquire the list lock
|
|
//
|
|
|
|
ACQUIRE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
|
|
//
|
|
// release the dest lock
|
|
//
|
|
|
|
bUnLock = FALSE;
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, TRUE, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to release dest %x", dwErr );
|
|
}
|
|
|
|
|
|
//
|
|
// Insert the rre into the list (in its appropriate place)
|
|
//
|
|
|
|
if ( !FindRouteRefEntry(
|
|
&pmllMfeList-> leHead, dwSourceAddr, dwSourceMask,
|
|
dwGroupAddr, dwGroupMask, &prreNew
|
|
) )
|
|
{
|
|
InsertTailList(
|
|
( prreNew ) ? &prreNew-> leRefList :
|
|
&pmllMfeList-> leHead,
|
|
&prre-> leRefList
|
|
);
|
|
}
|
|
|
|
else
|
|
{
|
|
TRACE1(
|
|
ANY, "Reference already exists for source %x", dwSourceAddr
|
|
);
|
|
|
|
MGM_FREE( prre );
|
|
}
|
|
|
|
|
|
//
|
|
// release the list lock
|
|
//
|
|
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
dwErr = NO_ERROR;
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
//
|
|
// In case of error , free the allocation for route reference
|
|
//
|
|
|
|
if ( ( dwErr != NO_ERROR ) && ( prre != NULL ) )
|
|
{
|
|
MGM_FREE( prre );
|
|
}
|
|
|
|
|
|
//
|
|
// release the dest lock
|
|
//
|
|
|
|
if ( bUnLock )
|
|
{
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, TRUE, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to release dest %x", dwErr );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// mark dest if required
|
|
//
|
|
|
|
if ( bMark )
|
|
{
|
|
dwErr = RtmMarkDestForChangeNotification(
|
|
g_hRtmHandle, g_hNotificationHandle,
|
|
prdi-> DestHandle, TRUE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to mark destination %x:", dwErr );
|
|
}
|
|
}
|
|
|
|
TRACEROUTE0( ROUTE, "LEAVING AddSourceGroupToRouteRefList" );
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FindRouteRefEntry
|
|
//
|
|
// Finds a specified (source, group ) entry in the MFE reference list
|
|
// for a route.
|
|
//
|
|
// If the entry is found a pointer to the entry is returned in the parameter
|
|
// pprre.
|
|
// If the entry is not found a pointer to the "next" entry is returned.
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
FindRouteRefEntry(
|
|
PLIST_ENTRY pleRefList,
|
|
DWORD dwSourceAddr,
|
|
DWORD dwSourceMask,
|
|
DWORD dwGroupAddr,
|
|
DWORD dwGroupMask,
|
|
PROUTE_REFERENCE_ENTRY * pprre
|
|
)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
|
|
INT iCmp;
|
|
|
|
PLIST_ENTRY pleRef;
|
|
|
|
PROUTE_REFERENCE_ENTRY prre;
|
|
|
|
|
|
TRACEROUTE0( ROUTE, "ENTERED RouteRefEntry" );
|
|
|
|
do
|
|
{
|
|
*pprre = NULL;
|
|
|
|
pleRef = pleRefList-> Flink;
|
|
|
|
while ( pleRef != pleRefList )
|
|
{
|
|
prre = CONTAINING_RECORD(
|
|
pleRef, ROUTE_REFERENCE_ENTRY, leRefList
|
|
);
|
|
|
|
//
|
|
// is same group
|
|
//
|
|
|
|
if ( INET_CMP( prre-> dwGroupAddr, dwGroupAddr, iCmp ) < 0 )
|
|
{
|
|
pleRef = pleRef-> Flink;
|
|
|
|
continue;
|
|
}
|
|
|
|
else if ( iCmp > 0 )
|
|
{
|
|
//
|
|
// past possible group entry
|
|
//
|
|
|
|
*pprre = prre;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// same group, now look for source
|
|
//
|
|
|
|
if ( INET_CMP( prre-> dwSourceAddr, dwSourceAddr, iCmp ) < 0 )
|
|
{
|
|
pleRef = pleRef-> Flink;
|
|
|
|
continue;
|
|
}
|
|
|
|
else if ( iCmp > 0 )
|
|
{
|
|
//
|
|
// past possible source entry
|
|
//
|
|
|
|
*pprre = prre;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// found entry
|
|
//
|
|
|
|
*pprre = prre;
|
|
|
|
bFound = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
TRACEROUTE1( ROUTE, "LEAVING RouteRefEntry : %d", bFound );
|
|
|
|
return bFound;
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// DeletRouteRef
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DeleteRouteRef(
|
|
PROUTE_REFERENCE_ENTRY prre
|
|
)
|
|
{
|
|
TRACEROUTE0( ROUTE, "ENTERED DeleteRefEntry" );
|
|
|
|
RemoveEntryList( &prre-> leRefList );
|
|
|
|
MGM_FREE( prre );
|
|
|
|
TRACEROUTE0( ROUTE, "LEAVING DeleteRefEntry" );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
RtmChangeNotificationCallback(
|
|
RTM_ENTITY_HANDLE hRtmHandle,
|
|
RTM_EVENT_TYPE retEventType,
|
|
PVOID pvContext1,
|
|
PVOID pvContext2
|
|
)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
|
|
if ( !ENTER_MGM_API() )
|
|
{
|
|
TRACE0( ANY, "RtmChangeNotificationCallback : Failed to enter" );
|
|
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
|
|
|
|
TRACE0( ROUTE, "ENTERED RtmChangeNotificationCallback" );
|
|
|
|
|
|
do
|
|
{
|
|
//
|
|
// Ignore all notifications except change notifications
|
|
//
|
|
|
|
if ( retEventType != RTM_CHANGE_NOTIFICATION )
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Queue work function to process changed destinations
|
|
//
|
|
|
|
dwErr = QueueMgmWorker(
|
|
WorkerFunctionProcessRtmChangeNotification, NULL
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to queue work item", dwErr );
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
LEAVE_MGM_API();
|
|
|
|
|
|
TRACE1( ROUTE, "LEAVING RtmChangeNotificationCallback : %d", dwErr );
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
WorkerFunctionProcessRtmChangeNotification(
|
|
PVOID pvContext
|
|
)
|
|
{
|
|
BOOL bMarked = FALSE, bDone = FALSE;
|
|
|
|
DWORD dwErr, dwNumDests;
|
|
|
|
RTM_DEST_INFO rdi;
|
|
|
|
|
|
|
|
if ( !ENTER_MGM_WORKER() )
|
|
{
|
|
TRACE0(
|
|
ANY, "WorkerFunctionProcessRtmChangeNotification : Failed to enter"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
TRACE0( ROUTE, "ENTERED WorkerFunctionRtmChangeNotification" );
|
|
|
|
do
|
|
{
|
|
//
|
|
// Get route changes one at a time
|
|
//
|
|
|
|
dwNumDests = 1;
|
|
|
|
dwErr = RtmGetChangedDests(
|
|
g_hRtmHandle, g_hNotificationHandle, &dwNumDests, &rdi
|
|
);
|
|
|
|
if ( ( dwErr != NO_ERROR ) && ( dwErr != ERROR_NO_MORE_ITEMS ) )
|
|
{
|
|
TRACE1(
|
|
ANY, "RtmGetChangedDests failed with error : %x",
|
|
dwErr
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// if there are no changed dests, quit.
|
|
//
|
|
|
|
if ( dwNumDests == 0 )
|
|
{
|
|
TRACE0( ANY, "RtmGetChangedDests returns 0 dests" );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// There are dests. Check if there are no more dests.
|
|
// If so set a flag to quit processing after this one
|
|
//
|
|
|
|
if ( dwErr == ERROR_NO_MORE_ITEMS )
|
|
{
|
|
bDone = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if there any routes for this destination
|
|
//
|
|
|
|
if ( rdi.ViewInfo[ 0 ].Route == NULL )
|
|
{
|
|
//
|
|
// No routes, assume this to be a delete
|
|
//
|
|
|
|
dwErr = ProcessRouteDelete( &rdi );
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// Check if dest is marked for change notification
|
|
//
|
|
|
|
dwErr = RtmIsMarkedForChangeNotification(
|
|
g_hRtmHandle, g_hNotificationHandle, rdi.DestHandle,
|
|
&bMarked
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1(
|
|
ANY, "RtmIsMarkedForChangeNotification failed with error : %x",
|
|
dwErr
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Process this destination
|
|
//
|
|
|
|
( bMarked ) ? ProcessRouteUpdate( &rdi ) :
|
|
ProcessUnMarkedDestination( &rdi );
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
//
|
|
// Release changed destinations
|
|
//
|
|
|
|
dwErr = RtmReleaseChangedDests(
|
|
g_hRtmHandle, g_hNotificationHandle, 1, &rdi
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to released destination", dwErr );
|
|
}
|
|
|
|
} while ( !bDone );
|
|
|
|
|
|
LEAVE_MGM_WORKER();
|
|
|
|
TRACE0( ROUTE, "LEAVING WorkerFunctionRtmChangeNotification" );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ProcessUnMarkedDestination(
|
|
PRTM_DEST_INFO prdi
|
|
)
|
|
{
|
|
BOOL bRelDest = FALSE, bMarked = FALSE, bUnLock = FALSE,
|
|
bRelRouteRef = FALSE, bUnMark = FALSE;
|
|
|
|
DWORD dwErr, dwDestMask;
|
|
|
|
PBYTE pbOpaqueInfo = NULL;
|
|
|
|
PLIST_ENTRY ple, pleTemp;
|
|
|
|
PROUTE_REFERENCE_ENTRY prre;
|
|
|
|
PMGM_LOCKED_LIST pmllMfeList = NULL;
|
|
|
|
RTM_DEST_INFO rdiLessSpecificDest;
|
|
|
|
|
|
|
|
do
|
|
{
|
|
//
|
|
// Get next less specific destination
|
|
//
|
|
|
|
dwErr = RtmGetLessSpecificDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, RTM_BEST_PROTOCOL,
|
|
RTM_VIEW_MASK_MCAST, &rdiLessSpecificDest
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to get less specific destination", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
bRelDest = TRUE;
|
|
|
|
|
|
//
|
|
// Check if it is marked
|
|
//
|
|
|
|
dwErr = RtmIsMarkedForChangeNotification(
|
|
g_hRtmHandle, g_hNotificationHandle,
|
|
rdiLessSpecificDest.DestHandle, &bMarked
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to check if dest is marked", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// if marked
|
|
//
|
|
|
|
if ( bMarked )
|
|
{
|
|
//
|
|
// it is marked. Lock it
|
|
//
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle,
|
|
rdiLessSpecificDest.DestHandle,
|
|
TRUE, TRUE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock less specific dest : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
bUnLock = TRUE;
|
|
|
|
|
|
//
|
|
// Get its opaque pointer
|
|
//
|
|
|
|
dwErr = RtmGetOpaqueInformationPointer(
|
|
g_hRtmHandle, rdiLessSpecificDest.DestHandle,
|
|
&pbOpaqueInfo
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1(
|
|
ANY, "Failed to opaque ptr for less specific dest : %x",
|
|
dwErr
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if it is NULL
|
|
//
|
|
|
|
if ( *( ( PBYTE * ) pbOpaqueInfo ) == NULL )
|
|
{
|
|
bUnMark = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
pmllMfeList = ( PMGM_LOCKED_LIST ) *( ( PBYTE * ) pbOpaqueInfo );
|
|
|
|
|
|
//
|
|
// lock the route reference list
|
|
//
|
|
|
|
ACQUIRE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
bRelRouteRef = TRUE;
|
|
|
|
|
|
//
|
|
// Unlock the dest
|
|
//
|
|
|
|
bUnLock = FALSE;
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle,
|
|
rdiLessSpecificDest.DestHandle,
|
|
TRUE, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to unlock less specific dest : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Create MASK for new dest. from len
|
|
//
|
|
|
|
dwDestMask = RTM_IPV4_MASK_FROM_LEN(
|
|
prdi-> DestAddress.NumBits
|
|
);
|
|
|
|
//
|
|
// For each reference
|
|
//
|
|
|
|
for ( ple = pmllMfeList-> leHead.Flink;
|
|
ple != &pmllMfeList-> leHead; )
|
|
{
|
|
prre = CONTAINING_RECORD(
|
|
ple, ROUTE_REFERENCE_ENTRY, leRefList
|
|
);
|
|
|
|
//
|
|
// Check if this MFE would fall under the
|
|
// more specific route
|
|
//
|
|
|
|
if ( ( prre-> dwSourceAddr & dwDestMask ) ==
|
|
(( * ( PDWORD ) prdi-> DestAddress.AddrBits ) & dwDestMask) )
|
|
{
|
|
//
|
|
// if it does, delete the MFE. This will force its
|
|
// recreation, at which time it will be made dependent
|
|
// on the more specific route
|
|
//
|
|
|
|
pleTemp = ple-> Flink;
|
|
|
|
RemoveEntryList( ple );
|
|
|
|
DeleteMfeAndRefs( ple );
|
|
|
|
ple = pleTemp;
|
|
}
|
|
else
|
|
{
|
|
ple = ple-> Flink;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// if Ref list is empty, it needs to be deleted too.
|
|
//
|
|
|
|
if ( IsListEmpty( &pmllMfeList-> leHead ) )
|
|
{
|
|
//
|
|
// to delete the opaque pointer, the dest needs to be locked
|
|
// (via RtmLockDestination)
|
|
//
|
|
// the dest lock is held before locking the route reference
|
|
// list ( via ACQUIRE_ROUTE_LOCK_EXCLUSIVE )
|
|
//
|
|
// At this point in the code, the route reference is locked
|
|
// but the dest is not locked.
|
|
//
|
|
// To lock it, the route reference lock is first released
|
|
// (via RELEASE_ROUTE_LOCK_EXCLUSIVE).
|
|
//
|
|
// The opaque pointer is then acquired, route ref list locked,
|
|
// and double checked for emptiness. This round-about ensures
|
|
// that the route ref is not deleted while there are threads
|
|
// waiting on its lock. This can happen since the dest lock
|
|
// is not held for most of the operations here
|
|
//
|
|
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
bRelRouteRef = FALSE;
|
|
|
|
//
|
|
// Lock dest
|
|
//
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle,
|
|
rdiLessSpecificDest.DestHandle,
|
|
TRUE, TRUE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
bUnLock = TRUE;
|
|
|
|
|
|
//
|
|
// Get Opaque pointer again
|
|
//
|
|
|
|
dwErr = RtmGetOpaqueInformationPointer(
|
|
g_hRtmHandle, rdiLessSpecificDest.DestHandle,
|
|
&pbOpaqueInfo
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR || ((* ((PBYTE *)pbOpaqueInfo) == NULL)) )
|
|
{
|
|
TRACE1( ANY, "Failed to get opaque ptr : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Get ref. list and lock it.
|
|
//
|
|
|
|
pmllMfeList = ( PMGM_LOCKED_LIST ) * ( ( PBYTE * ) pbOpaqueInfo );
|
|
|
|
ACQUIRE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
bRelRouteRef = TRUE;
|
|
|
|
|
|
//
|
|
// If list is still empty
|
|
//
|
|
|
|
if ( IsListEmpty( &pmllMfeList-> leHead ) )
|
|
{
|
|
//
|
|
// Clear opaque pointer info
|
|
//
|
|
|
|
* ( PBYTE * )pbOpaqueInfo = NULL;
|
|
|
|
//
|
|
// release list lock
|
|
//
|
|
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
bRelRouteRef = FALSE;
|
|
|
|
MGM_FREE( pmllMfeList );
|
|
|
|
|
|
//
|
|
// unmark the dest. Change notifications for this
|
|
// dest are no longer required.
|
|
//
|
|
|
|
bUnMark = TRUE;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
bRelRouteRef = FALSE;
|
|
}
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
//
|
|
// release route ref list lock
|
|
//
|
|
|
|
if ( bRelRouteRef )
|
|
{
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
}
|
|
|
|
|
|
//
|
|
// Unlock dest
|
|
//
|
|
|
|
if ( bUnLock )
|
|
{
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle,
|
|
rdiLessSpecificDest.DestHandle,
|
|
TRUE, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest : %x", dwErr );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Unmark dest
|
|
//
|
|
|
|
if ( bUnMark )
|
|
{
|
|
dwErr = RtmMarkDestForChangeNotification(
|
|
g_hRtmHandle, g_hNotificationHandle,
|
|
rdiLessSpecificDest.DestHandle, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to unmark DEST: %x", dwErr );
|
|
}
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ProcessRouteDelete(
|
|
PRTM_DEST_INFO prdi
|
|
)
|
|
{
|
|
BOOL bMark = FALSE;
|
|
|
|
DWORD dwErr;
|
|
|
|
PMGM_LOCKED_LIST pmllMfeList;
|
|
|
|
PBYTE pbOpaqueInfo = NULL;
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
|
|
do
|
|
{
|
|
//
|
|
// Cannot lock dest. Is that OK ?
|
|
//
|
|
|
|
//
|
|
// Check if this is a marked destination
|
|
// Only marked destinations are processed
|
|
//
|
|
|
|
dwErr = RtmIsMarkedForChangeNotification(
|
|
g_hRtmHandle, g_hNotificationHandle,
|
|
prdi-> DestHandle, &bMark
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to check if dest marked", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
if ( !bMark )
|
|
{
|
|
TRACE0(
|
|
ANY, "Ignoring change notification for unmarked destination"
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Get Opaque pointer & the list of MFEs dependent
|
|
// on this dest
|
|
//
|
|
|
|
dwErr = RtmGetOpaqueInformationPointer(
|
|
g_hRtmHandle, prdi-> DestHandle, &pbOpaqueInfo
|
|
);
|
|
|
|
if ( (dwErr != NO_ERROR) || ((* ((PBYTE *)pbOpaqueInfo) == NULL)) )
|
|
{
|
|
TRACE1( ANY, "Failed to get opaque ptr", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Clear out the opaque pointer
|
|
//
|
|
|
|
pmllMfeList = (PMGM_LOCKED_LIST) *( ( PBYTE * ) pbOpaqueInfo );
|
|
|
|
*( ( PBYTE * ) pbOpaqueInfo ) = NULL;
|
|
|
|
|
|
//
|
|
// Cannot unlock dest. Is that ok ?
|
|
//
|
|
|
|
//
|
|
// Check if the opaque pointer is NULL
|
|
//
|
|
|
|
if ( pmllMfeList == NULL )
|
|
{
|
|
TRACE0( ANY, "Opaque pointer is NULL" );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Delete all the MFEs
|
|
//
|
|
|
|
ACQUIRE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
while ( !IsListEmpty( &pmllMfeList-> leHead ) )
|
|
{
|
|
ple = RemoveHeadList( &pmllMfeList-> leHead );
|
|
|
|
DeleteMfeAndRefs( ple );
|
|
}
|
|
|
|
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
MGM_FREE( pmllMfeList );
|
|
|
|
dwErr = NO_ERROR;
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ProcessRouteUpdate(
|
|
PRTM_DEST_INFO prdi
|
|
)
|
|
{
|
|
BOOL bUnLock = FALSE, bUnMark = FALSE,
|
|
bFound = FALSE;
|
|
|
|
DWORD dwSize, dwErr, dwInd;
|
|
|
|
PBYTE pbOpaqueInfo = NULL;
|
|
|
|
PMGM_LOCKED_LIST pmllMfeList;
|
|
|
|
PLIST_ENTRY ple, pleTemp;
|
|
|
|
PROUTE_REFERENCE_ENTRY prre;
|
|
|
|
PRTM_ROUTE_INFO prri;
|
|
|
|
|
|
//
|
|
// the processing goes as follows :
|
|
//
|
|
|
|
do
|
|
{
|
|
//
|
|
// Allocate route info structure
|
|
//
|
|
|
|
dwSize = sizeof ( RTM_ROUTE_INFO ) +
|
|
( g_rrpRtmProfile.MaxNextHopsInRoute - 1 ) *
|
|
sizeof( RTM_NEXTHOP_HANDLE );
|
|
|
|
prri = MGM_ALLOC( dwSize );
|
|
|
|
if ( prri == NULL )
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
TRACE1( ANY, "Failed to allocate route info, size : %x", dwSize );
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Lock destination
|
|
//
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, TRUE, TRUE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
bUnLock = TRUE;
|
|
|
|
|
|
//
|
|
// Get Opaque pointer
|
|
//
|
|
|
|
dwErr = RtmGetOpaqueInformationPointer(
|
|
g_hRtmHandle, prdi-> DestHandle, &pbOpaqueInfo
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to get opaque ptr : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Unmark dest if there are no MFEs that depend on it.
|
|
//
|
|
|
|
if ( *( ( PBYTE * ) pbOpaqueInfo ) == NULL )
|
|
{
|
|
bUnMark = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
pmllMfeList = (PMGM_LOCKED_LIST) *( ( PBYTE * ) pbOpaqueInfo );
|
|
|
|
|
|
//
|
|
// get route ref list lock
|
|
//
|
|
|
|
ACQUIRE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
|
|
//
|
|
// Unlock dest
|
|
//
|
|
|
|
bUnLock = FALSE;
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, TRUE, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the route info for the best UNICAST route on dest
|
|
//
|
|
|
|
dwErr = RtmGetRouteInfo(
|
|
g_hRtmHandle, prdi ->ViewInfo[ 0 ].Route, prri, NULL);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed route info : %x", dwErr);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// For each Reference, check if NEXTHOP is still present
|
|
//
|
|
|
|
for ( ple = pmllMfeList-> leHead.Flink;
|
|
ple != &pmllMfeList-> leHead; )
|
|
{
|
|
prre = CONTAINING_RECORD( ple, ROUTE_REFERENCE_ENTRY, leRefList );
|
|
|
|
for ( dwInd = 0; dwInd < prri-> NextHopsList.NumNextHops; dwInd++ )
|
|
{
|
|
bFound = FALSE;
|
|
|
|
if ( prre-> hNextHop == prri-> NextHopsList.NextHops[ dwInd ] )
|
|
{
|
|
//
|
|
// OK next hop still present, nothing further needs
|
|
// to be done
|
|
//
|
|
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// if NEXTHOP is not present
|
|
//
|
|
|
|
if ( !bFound )
|
|
{
|
|
pleTemp = ple-> Flink;
|
|
|
|
//
|
|
// Delete the reference and the corresponding MFE
|
|
//
|
|
|
|
RemoveEntryList( ple );
|
|
|
|
DeleteMfeAndRefs( ple );
|
|
|
|
ple = pleTemp;
|
|
}
|
|
|
|
else
|
|
{
|
|
ple = ple-> Flink;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Release the route info
|
|
//
|
|
|
|
dwErr = RtmReleaseRouteInfo( g_hRtmHandle, prri );
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to release route info : %x", dwErr );
|
|
}
|
|
|
|
//
|
|
// if Ref list is empty, it needs to be deleted too.
|
|
//
|
|
|
|
if ( IsListEmpty( &pmllMfeList-> leHead ) )
|
|
{
|
|
//
|
|
// to delete the opaque pointer, the dest needs to be locked
|
|
// (via RtmLockDestination)
|
|
//
|
|
// the dest lock is held before locking the route reference
|
|
// list ( via ACQUIRE_ROUTE_LOCK_EXCLUSIVE )
|
|
//
|
|
// At this point in the code, the route reference is locked
|
|
// but the dest is not locked.
|
|
//
|
|
// To lock it, the route reference lock is first released
|
|
// (via RELEASE_ROUTE_LOCK_EXCLUSIVE).
|
|
//
|
|
// The opaque pointer is then acquired, route ref list locked,
|
|
// and double checked for emptiness. This round-about ensures
|
|
// that the route ref is not deleted while there are threads
|
|
// waiting on its lock. This can happen since the dest lock
|
|
// is not held for most of the operations here
|
|
//
|
|
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
|
|
//
|
|
// Lock dest
|
|
//
|
|
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle, TRUE, TRUE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
bUnLock = TRUE;
|
|
|
|
|
|
//
|
|
// Get Opaque pointer again
|
|
//
|
|
|
|
dwErr = RtmGetOpaqueInformationPointer(
|
|
g_hRtmHandle, prdi-> DestHandle, &pbOpaqueInfo
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR || ((* ((PBYTE *)pbOpaqueInfo) == NULL)) )
|
|
{
|
|
TRACE1( ANY, "Failed to get opaque ptr : %x", dwErr );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Get ref. list and lock it.
|
|
//
|
|
|
|
pmllMfeList = ( PMGM_LOCKED_LIST ) *( ( PBYTE * ) pbOpaqueInfo );
|
|
|
|
//
|
|
// Ensure that the list still exists. it is possible (though
|
|
// the chances are small) that this list may have been freed
|
|
//
|
|
|
|
if ( pmllMfeList == NULL )
|
|
{
|
|
TRACE0(
|
|
ANY, "ProcessRouteUpdate : Route ref list already freed"
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
ACQUIRE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
|
|
//
|
|
// If list is still empty
|
|
//
|
|
|
|
if ( IsListEmpty( &pmllMfeList-> leHead ) )
|
|
{
|
|
//
|
|
// Clear opaque pointer info
|
|
//
|
|
|
|
*( ( PBYTE * ) pbOpaqueInfo ) = NULL;
|
|
|
|
//
|
|
// release list lock
|
|
//
|
|
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
|
|
MGM_FREE( pmllMfeList );
|
|
|
|
|
|
//
|
|
// unmark the dest. Change notifications for this
|
|
// dest are no longer required.
|
|
//
|
|
|
|
bUnMark = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
RELEASE_ROUTE_LOCK_EXCLUSIVE( pmllMfeList );
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
//
|
|
// Unlock dest
|
|
//
|
|
|
|
if ( bUnLock )
|
|
{
|
|
dwErr = RtmLockDestination(
|
|
g_hRtmHandle, prdi-> DestHandle,
|
|
TRUE, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to lock dest : %x", dwErr );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Unmark dest
|
|
//
|
|
|
|
if ( bUnMark )
|
|
{
|
|
dwErr = RtmMarkDestForChangeNotification(
|
|
g_hRtmHandle, g_hNotificationHandle,
|
|
prdi-> DestHandle, FALSE
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to unmark DEST: %x", dwErr );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free allocations
|
|
//
|
|
|
|
if ( prri )
|
|
{
|
|
MGM_FREE( prri );
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DeleteMfeAndRefs(
|
|
PLIST_ENTRY ple
|
|
)
|
|
{
|
|
DWORD dwInIfIndex = 0, dwInIfNextHopAddr = 0, dwIfBucket;
|
|
|
|
PROUTE_REFERENCE_ENTRY prre;
|
|
|
|
PIF_ENTRY pie = NULL;
|
|
|
|
PIF_REFERENCE_ENTRY pire = NULL;
|
|
|
|
|
|
//
|
|
// Get the reference entry
|
|
//
|
|
|
|
prre = CONTAINING_RECORD(
|
|
ple, ROUTE_REFERENCE_ENTRY, leRefList
|
|
);
|
|
|
|
//
|
|
// Look up and delete the MFE
|
|
//
|
|
|
|
LookupAndDeleteYourMfe(
|
|
prre-> dwSourceAddr, prre-> dwSourceMask,
|
|
prre-> dwGroupAddr, prre-> dwGroupMask,
|
|
TRUE, &dwInIfIndex, &dwInIfNextHopAddr
|
|
);
|
|
|
|
|
|
//
|
|
// Find incoming interface and delete ref from there too.
|
|
//
|
|
|
|
if ( dwInIfIndex != 0 )
|
|
{
|
|
dwIfBucket = IF_TABLE_HASH( dwInIfIndex );
|
|
|
|
ACQUIRE_IF_LOCK_EXCLUSIVE( dwIfBucket );
|
|
|
|
if ( FindIfEntry(
|
|
IF_BUCKET_HEAD( dwIfBucket ), dwInIfIndex,
|
|
dwInIfNextHopAddr, &pie
|
|
) )
|
|
{
|
|
if ( FindRefEntry(
|
|
&pie-> leInIfList, prre-> dwSourceAddr, prre-> dwSourceMask,
|
|
prre-> dwGroupAddr, prre-> dwGroupMask, &pire
|
|
) )
|
|
{
|
|
RemoveEntryList( &pire-> leRefList );
|
|
|
|
MGM_FREE( pire );
|
|
}
|
|
|
|
else
|
|
{
|
|
TRACE2(
|
|
ANY, "Could not find ref entry for %x, %x",
|
|
prre-> dwSourceAddr, prre-> dwGroupAddr
|
|
);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
TRACE2(
|
|
ANY, "Could not find i/f entry for %x, %x",
|
|
dwInIfIndex, dwInIfNextHopAddr
|
|
);
|
|
}
|
|
|
|
RELEASE_IF_LOCK_EXCLUSIVE( dwIfBucket );
|
|
|
|
MGM_FREE( prre );
|
|
}
|
|
}
|
|
|
|
|
|
HANDLE
|
|
SelectNextHop(
|
|
PRTM_DEST_INFO prdi
|
|
)
|
|
{
|
|
DWORD dwErr, dwSize;
|
|
|
|
HANDLE hNextHop;
|
|
|
|
PRTM_ROUTE_INFO prri;
|
|
|
|
|
|
//
|
|
// Allocate route info structure
|
|
//
|
|
|
|
dwSize = sizeof ( RTM_ROUTE_INFO ) +
|
|
( g_rrpRtmProfile.MaxNextHopsInRoute - 1 ) *
|
|
sizeof( RTM_NEXTHOP_HANDLE );
|
|
|
|
prri = MGM_ALLOC( dwSize );
|
|
|
|
if ( prri == NULL )
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
TRACE1( ANY, "Failed to allocate route info, size : %x", dwSize );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory( prri, dwSize );
|
|
|
|
|
|
//
|
|
// get route info
|
|
//
|
|
|
|
dwErr = RtmGetRouteInfo(
|
|
g_hRtmHandle, prdi-> ViewInfo[ 0 ].Route,
|
|
prri, NULL
|
|
);
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to get route info : %x", dwErr );
|
|
|
|
MGM_FREE( prri );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Pick the first next hop for now
|
|
//
|
|
|
|
hNextHop = prri-> NextHopsList.NextHops[0];
|
|
|
|
|
|
//
|
|
// Release the route info
|
|
//
|
|
|
|
dwErr = RtmReleaseRouteInfo( g_hRtmHandle, prri );
|
|
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
TRACE1( ANY, "Failed to release route info : %x", dwErr );
|
|
}
|
|
|
|
MGM_FREE( prri );
|
|
|
|
return hNextHop;
|
|
}
|
|
|
|
|