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.
3990 lines
149 KiB
3990 lines
149 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
net\rtm\rtm.c
|
|
|
|
Abstract:
|
|
Routing Table Manager DLL. Main module
|
|
|
|
|
|
Author:
|
|
|
|
Vadim Eydelman
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pchrtm.h"
|
|
#pragma hdrstop
|
|
|
|
/* ****** Global data ****** */
|
|
|
|
// Tables themselves
|
|
RTM_TABLE Tables[RTM_NUM_OF_PROTOCOL_FAMILIES];
|
|
|
|
|
|
|
|
MASK_ENTRY g_meMaskTable[ MAX_MASKS + 1 ] =
|
|
{
|
|
{ 0x00000000, 0 },
|
|
|
|
{ 0x00000001, 0 },
|
|
{ 0x00000003, 0 },
|
|
{ 0x00000007, 0 },
|
|
{ 0x0000000F, 0 },
|
|
|
|
{ 0x0000001F, 0 },
|
|
{ 0x0000003F, 0 },
|
|
{ 0x0000007F, 0 },
|
|
{ 0x000000FF, 0 },
|
|
|
|
{ 0x000080FF, 0 },
|
|
{ 0x0000C0FF, 0 },
|
|
{ 0x0000E0FF, 0 },
|
|
{ 0x0000F0FF, 0 },
|
|
|
|
{ 0x0000F8FF, 0 },
|
|
{ 0x0000FCFF, 0 },
|
|
{ 0x0000FEFF, 0 },
|
|
{ 0x0000FFFF, 0 },
|
|
|
|
{ 0x0080FFFF, 0 },
|
|
{ 0x00C0FFFF, 0 },
|
|
{ 0x00E0FFFF, 0 },
|
|
{ 0x00F0FFFF, 0 },
|
|
|
|
{ 0x00F8FFFF, 0 },
|
|
{ 0x00FCFFFF, 0 },
|
|
{ 0x00FEFFFF, 0 },
|
|
{ 0x00FFFFFF, 0 },
|
|
|
|
{ 0x80FFFFFF, 0 },
|
|
{ 0xC0FFFFFF, 0 },
|
|
{ 0xE0FFFFFF, 0 },
|
|
{ 0xF0FFFFFF, 0 },
|
|
|
|
{ 0xF8FFFFFF, 0 },
|
|
{ 0xFCFFFFFF, 0 },
|
|
{ 0xFEFFFFFF, 0 },
|
|
{ 0xFFFFFFFF, 0 }
|
|
};
|
|
|
|
#if DBG
|
|
DWORD dbgThreadId;
|
|
ULONG TracingHandle;
|
|
DWORD TracingInited;
|
|
HANDLE LoggingHandle;
|
|
ULONG LoggingLevel;
|
|
#endif
|
|
|
|
/* ***** Internal Function Prototypes ******* */
|
|
|
|
VOID
|
|
NotifyClients (
|
|
PRTM_TABLE Table,
|
|
HANDLE ClientHandle,
|
|
DWORD Flags,
|
|
PRTM_XX_ROUTE CurBestRoute,
|
|
PRTM_XX_ROUTE PrevBestRoute
|
|
);
|
|
|
|
VOID APIENTRY
|
|
ConsolidateNetNumberListsWI (
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
ConsolidateNetNumberLists (
|
|
PRTM_TABLE Table
|
|
);
|
|
|
|
VOID APIENTRY
|
|
ScheduleUpdate (
|
|
PVOID Context
|
|
);
|
|
|
|
VOID APIENTRY
|
|
ProcessExpirationQueueWI (
|
|
PVOID Table
|
|
);
|
|
|
|
VOID
|
|
ProcessExpirationQueue (
|
|
PRTM_TABLE Table
|
|
);
|
|
|
|
DWORD
|
|
ReadRegistry (
|
|
void
|
|
);
|
|
|
|
DWORD
|
|
DoEnumerate (
|
|
PRTM_TABLE Table,
|
|
PRTM_ENUMERATOR EnumPtr,
|
|
DWORD EnableFlag
|
|
);
|
|
|
|
VOID
|
|
SetMaskCount(
|
|
PIP_NETWORK pinNet,
|
|
BOOL bAdd
|
|
);
|
|
|
|
#if 0 // Replaced by RTMv2's DLLMain
|
|
|
|
// DLL main function. Called by crtdll startup routine that is
|
|
// designated as entry point for this dll.
|
|
//
|
|
// At startup (DLL_PROCESS_ATTACH): creates all tables and starts update
|
|
// thread
|
|
// At shutdown (DLL_PROCESS_DETACH): stops update thread and disposes of all
|
|
// resources
|
|
|
|
BOOL WINAPI DllMain(
|
|
HINSTANCE hinstDLL, // DLL instance handle
|
|
DWORD fdwReason, // Why is it called
|
|
LPVOID lpvReserved
|
|
) {
|
|
|
|
switch (fdwReason) {
|
|
case DLL_PROCESS_ATTACH: // We are being attached to a new process
|
|
// Create all we need to operate
|
|
DisableThreadLibraryCalls (hinstDLL);
|
|
|
|
return Rtmv1DllStartup(hinstDLL);
|
|
|
|
case DLL_PROCESS_DETACH: // The process is exiting
|
|
|
|
Rtmv1DllCleanup();
|
|
|
|
default: // Not interested in all other cases
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
Rtmv1DllStartup (
|
|
HINSTANCE hinstDLL // DLL instance handle
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
// Create all we need to operate
|
|
|
|
#if DBG
|
|
RTDlgThreadHdl = CreateThread (
|
|
NULL,
|
|
0,
|
|
&RTDialogThread,
|
|
(LPVOID)hinstDLL,
|
|
0,
|
|
&dbgThreadId);
|
|
ASSERTERR (RTDlgThreadHdl!=NULL);
|
|
#endif
|
|
|
|
for (i=0; i<RTM_NUM_OF_PROTOCOL_FAMILIES; i++) {
|
|
Tables[i].RT_APIclientCount = RTM_CLIENT_STOP_TRESHHOLD;
|
|
Tables[i].RT_Heap = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
Rtmv1DllCleanup (
|
|
)
|
|
{
|
|
DWORD status;
|
|
DWORD i;
|
|
|
|
#if DBG
|
|
PostThreadMessage (dbgThreadId, WM_QUIT, 0, 0);
|
|
status = WaitForSingleObject (RTDlgThreadHdl, 5*1000);
|
|
|
|
//
|
|
// Give some time to the RTDialogThread to process
|
|
// the WM_QUIT message and exit.
|
|
// If it is not quitting, its better to just
|
|
// leave it alone rather than terminating it.
|
|
//
|
|
|
|
// if (status!=WAIT_OBJECT_0)
|
|
// TerminateThread (RTDlgThreadHdl, 0);
|
|
CloseHandle (RTDlgThreadHdl);
|
|
|
|
// Deregister with tracing utils
|
|
STOP_TRACING();
|
|
#endif
|
|
|
|
// Dispose of all resources
|
|
for (i=0; i<RTM_NUM_OF_PROTOCOL_FAMILIES; i++) {
|
|
if (Tables[i].RT_Heap!=NULL)
|
|
RtmDeleteRouteTable (i);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*++
|
|
*******************************************************************
|
|
|
|
R t m C r e a t e R o u t e T a b l e
|
|
|
|
Routine Description:
|
|
Create route table for protocol family
|
|
Arguments:
|
|
ProtocolFamily - index that identifies protocol family
|
|
Config - protocol family table configuration parameters
|
|
Return Value:
|
|
NO_ERROR - table was created ok
|
|
ERROR_NOT_ENOUGH_MEMORY - could not allocate memory to perform
|
|
the operation
|
|
ERROR_NO_SYSTEM_RESOURCES - not enough resources to perform the operation,
|
|
try again later
|
|
|
|
*******************************************************************
|
|
--*/
|
|
DWORD
|
|
RtmCreateRouteTable (
|
|
IN DWORD ProtocolFamily,
|
|
IN PRTM_PROTOCOL_FAMILY_CONFIG Config
|
|
) {
|
|
INT i;
|
|
DWORD status;
|
|
PRTM_TABLE Table;
|
|
|
|
#if DBG
|
|
// Register with tracing utils
|
|
START_TRACING();
|
|
#endif
|
|
|
|
if (ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES) {
|
|
#if DBG
|
|
Trace2 ( ANY,
|
|
"Undefined Protocol Family.\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if (Table->RT_Heap!=NULL) {
|
|
#if DBG
|
|
Trace2 ( ANY,
|
|
"Table already exists for protocol family\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
return ERROR_ALREADY_EXISTS;
|
|
}
|
|
|
|
memcpy (&Table->RT_Config, Config, sizeof (Table->RT_Config));
|
|
|
|
status = NtCreateTimer (&Table->RT_ExpirationTimer,
|
|
TIMER_ALL_ACCESS,
|
|
NULL,
|
|
NotificationTimer);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
|
|
status = NtCreateTimer (&Table->RT_UpdateTimer,
|
|
TIMER_ALL_ACCESS,
|
|
NULL,
|
|
NotificationTimer);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
NtClose (Table->RT_ExpirationTimer);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
|
|
Table->RT_Heap = HeapCreate (0, 0, Table->RT_Config.RPFC_MaxTableSize);
|
|
if (Table->RT_Heap==NULL) {
|
|
NtClose (Table->RT_UpdateTimer);
|
|
NtClose (Table->RT_ExpirationTimer);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
Table->RT_NetNumberHash = (PRTM_SYNC_LIST)HeapAlloc (
|
|
Table->RT_Heap,
|
|
0,
|
|
sizeof (RTM_SYNC_LIST)*Table->RT_HashTableSize);
|
|
|
|
if (Table->RT_NetNumberHash==NULL) {
|
|
status = GetLastError ();
|
|
HeapDestroy (Table->RT_Heap);
|
|
NtClose (Table->RT_UpdateTimer);
|
|
NtClose (Table->RT_ExpirationTimer);
|
|
return status;
|
|
}
|
|
|
|
Table->RT_InterfaceHash = (PRTM_SYNC_LIST)HeapAlloc (
|
|
Table->RT_Heap,
|
|
0,
|
|
sizeof (RTM_SYNC_LIST)*RTM_INTF_HASH_SIZE);
|
|
|
|
if (Table->RT_InterfaceHash==NULL) {
|
|
status = GetLastError ();
|
|
HeapDestroy (Table->RT_Heap);
|
|
NtClose (Table->RT_UpdateTimer);
|
|
NtClose (Table->RT_ExpirationTimer);
|
|
return status;
|
|
}
|
|
|
|
try {
|
|
InitializeCriticalSection (&Table->RT_Lock);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetLastError();
|
|
}
|
|
|
|
Table->RT_SyncObjectList.Next = NULL;
|
|
|
|
for (i=0; i<Table->RT_HashTableSize; i++)
|
|
InitializeSyncList (&Table->RT_NetNumberHash[i]);
|
|
for (i=0; i<RTM_INTF_HASH_SIZE; i++)
|
|
InitializeSyncList (&Table->RT_InterfaceHash[i]);
|
|
|
|
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
InitializeSyncList (&Table->RT_ProtocolList);
|
|
#endif
|
|
InitializeSyncList (&Table->RT_NetNumberMasterList);
|
|
InitializeSyncList (&Table->RT_NetNumberTempList);
|
|
InitializeSyncList (&Table->RT_DeletedList);
|
|
|
|
InitializeSyncList (&Table->RT_ExpirationQueue);
|
|
InitializeSyncList (&Table->RT_RouteChangeQueue);
|
|
InitializeSyncList (&Table->RT_ClientList);
|
|
|
|
Table->RT_NetNumberTempCount = 0;
|
|
Table->RT_DeletedNodesCount = 0;
|
|
Table->RT_UpdateWorkerPending = -1;
|
|
Table->RT_ExpirationWorkerPending = -1;
|
|
|
|
Table->RT_NetworkCount = 0;
|
|
Table->RT_NumOfMessages = 0;
|
|
|
|
InterlockedIncrement (&Table->RT_UpdateWorkerPending);
|
|
status = RtlQueueWorkItem (ScheduleUpdate, Table, WT_EXECUTEINIOTHREAD);
|
|
ASSERTMSG ("Could not queue update scheduling work item ", status==STATUS_SUCCESS);
|
|
|
|
Table->RT_APIclientCount = 0;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
/*++
|
|
*******************************************************************
|
|
|
|
R t m D e l e t e R o u t e T a b l e
|
|
|
|
Routine Description:
|
|
Dispose of all resources allocated for the route table
|
|
Arguments:
|
|
ProtocolFamily - index that identifies protocol family
|
|
Return Value:
|
|
NO_ERROR - table was deleted ok
|
|
ERROR_INVALID_PARAMETER - no table to delete
|
|
|
|
*******************************************************************
|
|
--*/
|
|
DWORD
|
|
RtmDeleteRouteTable (
|
|
DWORD ProtocolFamily
|
|
) {
|
|
PSINGLE_LIST_ENTRY cur;
|
|
PRTM_TABLE Table;
|
|
LONG curAPIclientCount;
|
|
|
|
if (ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family.\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if (Table->RT_Heap==NULL) {
|
|
#if DBG
|
|
Trace3 (ANY,
|
|
"Table does not exist or already deleted for protocol family %d\n"
|
|
"\tat line %ld of %s\n",
|
|
ProtocolFamily, __LINE__, __FILE__);
|
|
#endif
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
while (!IsListEmpty (&Table->RT_ClientList.RSL_Head)) {
|
|
PRTM_CLIENT ClientPtr = CONTAINING_RECORD (
|
|
Table->RT_ClientList.RSL_Head.Flink,
|
|
RTM_CLIENT,
|
|
RC_Link);
|
|
RtmDeregisterClient ((HANDLE)ClientPtr);
|
|
}
|
|
|
|
curAPIclientCount = InterlockedExchange (&Table->RT_APIclientCount,
|
|
RTM_CLIENT_STOP_TRESHHOLD)
|
|
+ RTM_CLIENT_STOP_TRESHHOLD;
|
|
|
|
while (Table->RT_APIclientCount > curAPIclientCount)
|
|
Sleep (100);
|
|
|
|
while (InterlockedIncrement (&Table->RT_ExpirationWorkerPending)>0) {
|
|
while (Table->RT_ExpirationWorkerPending!=-1)
|
|
Sleep (100);
|
|
}
|
|
|
|
while (InterlockedIncrement (&Table->RT_UpdateWorkerPending)>0) {
|
|
while (Table->RT_UpdateWorkerPending!=-1)
|
|
Sleep (100);
|
|
}
|
|
NtCancelTimer (Table->RT_UpdateTimer, NULL);
|
|
NtCancelTimer (Table->RT_ExpirationTimer, NULL);
|
|
Sleep (100);
|
|
|
|
NtClose (Table->RT_UpdateTimer);
|
|
NtClose (Table->RT_ExpirationTimer);
|
|
Sleep (100);
|
|
|
|
cur = PopEntryList (&Table->RT_SyncObjectList);
|
|
while (cur!=NULL) {
|
|
GlobalFree (CONTAINING_RECORD (cur, RTM_SYNC_OBJECT, RSO_Link));
|
|
cur = PopEntryList (&Table->RT_SyncObjectList);
|
|
}
|
|
|
|
HeapFree (Table->RT_Heap, 0, Table->RT_InterfaceHash);
|
|
HeapFree (Table->RT_Heap, 0, Table->RT_NetNumberHash);
|
|
HeapDestroy (Table->RT_Heap);
|
|
Table->RT_Heap = NULL;
|
|
DeleteCriticalSection (&Table->RT_Lock);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Registers client as a handler of specified protocol
|
|
// Returns a HANDLE be used for all subsequent
|
|
// calls to identify which Protocol Family and Routing Protocol
|
|
// should be affected by the call
|
|
// Returns NULL in case of failure. Call GetLastError () to obtain
|
|
// extended error information.
|
|
// Error codes:
|
|
// ERROR_INVALID_PARAMETER - specified protocol family is not supported
|
|
// ERROR_CLIENT_ALREADY_EXISTS - another client already registered
|
|
// to handle specified protocol
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
// ERROR_NOT_ENOUGH_MEMORY - not enough memory to allocate client control block
|
|
HANDLE WINAPI
|
|
RtmRegisterClient (
|
|
IN DWORD ProtocolFamily, // IP, IPX, etc.
|
|
IN DWORD RoutingProtocol, // RIP, OSPF, etc.
|
|
IN HANDLE ChangeEvent OPTIONAL,// Notified when best
|
|
// routes change in the table (see
|
|
// RtmDequeueRouteChangeMessage
|
|
IN DWORD Flags
|
|
) {
|
|
HANDLE ClientHandle;
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle) // To access handle fields
|
|
// in this routine
|
|
PRTM_TABLE Table; // Table we associated with
|
|
DWORD status; // Operation result
|
|
PLIST_ENTRY cur;
|
|
|
|
// Check if we have the table of interest
|
|
Table = &Tables[ProtocolFamily];
|
|
|
|
if ((ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
|| !EnterTableAPI (Table)) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family.\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
if (Flags & (~RTM_PROTOCOL_SINGLE_ROUTE)) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Invalid registration flags\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
ExitTableAPI(Table);
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
// Allocate handle and initialize basic fields
|
|
ClientHandle = GlobalAlloc (GMEM_FIXED, sizeof (RTM_CLIENT));
|
|
if (ClientHandle==NULL) {
|
|
ExitTableAPI(Table);
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
ClientPtr->RC_RoutingProtocol = RoutingProtocol;
|
|
ClientPtr->RC_NotificationEvent = ChangeEvent;
|
|
ClientPtr->RC_Flags = Flags;
|
|
|
|
// Lock client list as we adding a new one
|
|
if (!EnterSyncList (Table, &Table->RT_ClientList, TRUE)) {
|
|
GlobalFree (ClientHandle);
|
|
ExitTableAPI (Table);
|
|
SetLastError (ERROR_NO_SYSTEM_RESOURCES);
|
|
return NULL;
|
|
}
|
|
// Check if we have another client with same
|
|
// Routing Protocol
|
|
|
|
cur = Table->RT_ClientList.RSL_Head.Flink;
|
|
while (cur!=&Table->RT_ClientList.RSL_Head) {
|
|
PRTM_CLIENT node = CONTAINING_RECORD (cur,
|
|
RTM_CLIENT,
|
|
RC_Link);
|
|
if (ClientPtr->RC_RoutingProtocol< node->RC_RoutingProtocol)
|
|
break;
|
|
else if (ClientPtr->RC_RoutingProtocol==node->RC_RoutingProtocol) {
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
GlobalFree (ClientHandle);
|
|
ExitTableAPI (Table);
|
|
SetLastError (ERROR_CLIENT_ALREADY_EXISTS);
|
|
return NULL;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
// Check if client needs notifications
|
|
if (ChangeEvent!= NULL) {
|
|
status = ResetEvent (ChangeEvent); // Nothing yet
|
|
ASSERTERRMSG ("Can't reset client's event.", status);
|
|
// Lock notification messages queue
|
|
if (!EnterSyncList (Table, &Table->RT_RouteChangeQueue, TRUE)) {
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
GlobalFree (ClientHandle);
|
|
ExitTableAPI (Table);
|
|
SetLastError (ERROR_NO_SYSTEM_RESOURCES);
|
|
return NULL;
|
|
}
|
|
|
|
// Point to the end of the queue: ignore
|
|
// all previous messages
|
|
ClientPtr->RC_PendingMessage = &Table->RT_RouteChangeQueue.RSL_Head;
|
|
LeaveSyncList (Table, &Table->RT_RouteChangeQueue);
|
|
}
|
|
// Add client to the list
|
|
InsertTailList (cur, &ClientPtr->RC_Link);
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
|
|
ClientPtr->RC_ProtocolFamily = ProtocolFamily|RTM_CLIENT_HANDLE_TAG;
|
|
ExitTableAPI (Table);
|
|
return ClientHandle;
|
|
#undef ClientPtr
|
|
}
|
|
|
|
// Frees resources and the HANDLE allocated above.
|
|
// Deletes all routes associated with Routing Protocol that was represented
|
|
// by the handle
|
|
// Returned error codes:
|
|
// NO_ERROR - handle was disposed of ok
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
// ERROR_NOT_ENOUGH_MEMORY - not enough memory to allocate client control block
|
|
DWORD WINAPI
|
|
RtmDeregisterClient (
|
|
IN HANDLE ClientHandle
|
|
) {
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle) // To access handle fields
|
|
// in this routine
|
|
RTM_XX_ROUTE Route;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
// Lock client list
|
|
if (!EnterSyncList (Table, &Table->RT_ClientList, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
// Check if we need to dispose of messages
|
|
// still waiting for this client
|
|
if (ClientPtr->RC_NotificationEvent!= NULL) {
|
|
if (!EnterSyncList (Table, &Table->RT_RouteChangeQueue, TRUE)) {
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
while (ClientPtr->RC_PendingMessage
|
|
!= &Table->RT_RouteChangeQueue.RSL_Head) {
|
|
PRTM_ROUTE_CHANGE_NODE node = CONTAINING_RECORD (
|
|
ClientPtr->RC_PendingMessage,
|
|
RTM_ROUTE_CHANGE_NODE,
|
|
RCN_Link);
|
|
ClientPtr->RC_PendingMessage = ClientPtr->RC_PendingMessage->Flink;
|
|
if (node->RCN_ResponsibleClient!=ClientHandle) {
|
|
// Tell that we processed this message so it can be freed
|
|
// if no more clients are interested
|
|
node->RCN_ReferenceCount -= 1;
|
|
if (node->RCN_ReferenceCount<=0) {
|
|
RemoveEntryList (&node->RCN_Link);
|
|
if (node->RCN_Route2!=NULL)
|
|
HeapFree (Table->RT_Heap, 0, node->RCN_Route2);
|
|
HeapFree (Table->RT_Heap, 0, node);
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveSyncList (Table, &Table->RT_RouteChangeQueue);
|
|
}
|
|
RemoveEntryList (&ClientPtr->RC_Link);
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
|
|
{
|
|
RTM_CLIENT DeadClient;
|
|
DeadClient.RC_ProtocolFamily = ClientPtr->RC_ProtocolFamily;
|
|
DeadClient.RC_RoutingProtocol = ClientPtr->RC_RoutingProtocol;
|
|
// Invlaidate client's handle memory block
|
|
ClientPtr->RC_ProtocolFamily ^= RTM_CLIENT_HANDLE_TAG;
|
|
GlobalFree (ClientHandle);
|
|
// Delete all routes associated with routing protocol
|
|
// controled by the client
|
|
RtmBlockDeleteRoutes ((HANDLE)&DeadClient, 0, &Route);
|
|
}
|
|
|
|
ExitTableAPI (Table);
|
|
return NO_ERROR;
|
|
#undef ClientPtr
|
|
}
|
|
|
|
// Dequeues and returns the first change message from the queue.
|
|
// Should be called if NotificationEvent is signalled to retrieve
|
|
// chage messages pending for the client
|
|
// Change messages are generated if best route to some destination
|
|
// or any of its routing parameters (metric or protocol specific fields)
|
|
// get changed as the result of some route being added, deleted, updated,
|
|
// disabled, reenabled, or aged out. Note that change in protocol specific fields
|
|
// or in TimeToLive parameters do not produce notification messages
|
|
// Returns NO_ERROR and resets the event if there are no more messages
|
|
// pending for the client,
|
|
// otherwise ERROR_MORE_MESSAGES is returned (client should keep calling
|
|
// until NO_ERROR is returned)
|
|
// ERROR_NO_MESSAGES will be returned if there were no messages
|
|
// to return (can happen if called when event was not signalled)
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
DWORD WINAPI
|
|
RtmDequeueRouteChangeMessage (
|
|
IN HANDLE ClientHandle, // Handle that identifies client
|
|
OUT DWORD *Flags, // Flags that indentify what
|
|
// is this message about:
|
|
// RTM_ROUTE_ADDED - this message informs
|
|
// of new route (CurBestRoute is filled with
|
|
// this route parameters if provided)
|
|
// RTM_ROUTE_DELETED - this message informs
|
|
// that route was deleted (PrevBestRoute is
|
|
// filled with this route parameters if provuded)
|
|
// RTM_ROUTE_CHANGED - best route to some network has
|
|
// changed, (CurBestRoute is filled with parameter
|
|
// of route that became the best, PrevBestRoute is
|
|
// filled with parameters of route that was best
|
|
// before this change)
|
|
OUT PVOID CurBestRoute OPTIONAL,
|
|
OUT PVOID PrevBestRoute OPTIONAL
|
|
){
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle) // To access handle fields
|
|
// in this routine
|
|
PRTM_ROUTE_CHANGE_NODE node=NULL;
|
|
DWORD status;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
// Events are reported only to the clients that
|
|
// requested them by providing notification event
|
|
if (ClientPtr->RC_NotificationEvent==NULL) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Dequeue message is called by the client that did not provide."
|
|
" notification event\n"
|
|
"\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
ExitTableAPI (Table);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_RouteChangeQueue, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
// Traverse the queue to find the message that was not caused
|
|
// by client's actions
|
|
while (ClientPtr->RC_PendingMessage
|
|
!= &Table->RT_RouteChangeQueue.RSL_Head) {
|
|
node = CONTAINING_RECORD (ClientPtr->RC_PendingMessage,
|
|
RTM_ROUTE_CHANGE_NODE,
|
|
RCN_Link);
|
|
if (node->RCN_ResponsibleClient!=ClientHandle)
|
|
break;
|
|
ClientPtr->RC_PendingMessage = ClientPtr->RC_PendingMessage->Flink;
|
|
}
|
|
|
|
if (ClientPtr->RC_PendingMessage!=&Table->RT_RouteChangeQueue.RSL_Head)
|
|
ClientPtr->RC_PendingMessage = ClientPtr->RC_PendingMessage->Flink;
|
|
else {
|
|
// There must be a pending message or we should have been
|
|
// called
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Dequeue message is called, but nothing is pending.\n"
|
|
"\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
status = ResetEvent (ClientPtr->RC_NotificationEvent);
|
|
ASSERTERRMSG ("Can't reset client's event.", status);
|
|
LeaveSyncList (Table, &Table->RT_RouteChangeQueue);
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_MESSAGES;
|
|
}
|
|
|
|
// Copy message to client's buffers
|
|
*Flags = node->RCN_Flags;
|
|
switch (node->RCN_Flags) {
|
|
case RTM_ROUTE_CHANGED:
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (PrevBestRoute, &node->RCN_Route2->RN_Route,
|
|
Table->RT_RouteSize);
|
|
break;
|
|
case RTM_ROUTE_ADDED:
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, &node->RCN_Route1, Table->RT_RouteSize);
|
|
break;
|
|
case RTM_ROUTE_DELETED:
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (PrevBestRoute, &node->RCN_Route1, Table->RT_RouteSize);
|
|
break;
|
|
default:
|
|
ASSERTMSG ("Invalid message flag", FALSE);
|
|
break;
|
|
}
|
|
|
|
|
|
// Tell that we processed this message so it can be freed if
|
|
// no more clients are interested
|
|
node->RCN_ReferenceCount -= 1;
|
|
if (node->RCN_ReferenceCount<=0) {
|
|
Table->RT_NumOfMessages -= 1;
|
|
RemoveEntryList (&node->RCN_Link);
|
|
if (node->RCN_Route2!=NULL)
|
|
HeapFree (Table->RT_Heap, 0, node->RCN_Route2);
|
|
HeapFree (Table->RT_Heap, 0, node);
|
|
}
|
|
|
|
// Traverse the queue to locate next pending message
|
|
// (not caused by the client)
|
|
while (ClientPtr->RC_PendingMessage
|
|
!= &Table->RT_RouteChangeQueue.RSL_Head) {
|
|
node = CONTAINING_RECORD (ClientPtr->RC_PendingMessage,
|
|
RTM_ROUTE_CHANGE_NODE,
|
|
RCN_Link);
|
|
if (node->RCN_ResponsibleClient!=ClientHandle)
|
|
break;
|
|
ClientPtr->RC_PendingMessage = ClientPtr->RC_PendingMessage->Flink;
|
|
}
|
|
|
|
if (ClientPtr->RC_PendingMessage==&Table->RT_RouteChangeQueue.RSL_Head) {
|
|
// All pending messages are processed: reset the event
|
|
status = ResetEvent (ClientPtr->RC_NotificationEvent);
|
|
ASSERTERRMSG ("Can't reset client's event.", status);
|
|
status = NO_ERROR;
|
|
}
|
|
else
|
|
status = ERROR_MORE_MESSAGES;
|
|
|
|
LeaveSyncList (Table, &Table->RT_RouteChangeQueue);
|
|
ExitTableAPI (Table);
|
|
return status;
|
|
#undef ClientPtr
|
|
}
|
|
|
|
|
|
// Adds new route change message to the queue and notifies
|
|
// all interesed clients
|
|
VOID
|
|
NotifyClients (
|
|
PRTM_TABLE Table, // Table to which this change applies
|
|
HANDLE ClientHandle, // Client that caused this change (can
|
|
// be NULL if this is a result of
|
|
// route aging)
|
|
DWORD Flags, // Change message flags
|
|
PRTM_XX_ROUTE CurBestRoute, // Current best route for the network
|
|
PRTM_XX_ROUTE PrevBestRoute // Previous best route for the network
|
|
) {
|
|
PRTM_ROUTE_CHANGE_NODE node;
|
|
PLIST_ENTRY cur;
|
|
BOOL nodeInserted = FALSE;
|
|
|
|
(*Table->RT_Config.RPFC_Change) (Flags, CurBestRoute, PrevBestRoute);
|
|
// Allocate and initialize queue node
|
|
node = (PRTM_ROUTE_CHANGE_NODE)HeapAlloc (
|
|
Table->RT_Heap,
|
|
0,
|
|
FIELD_OFFSET (RTM_ROUTE_NODE, RN_Route)+Table->RT_RouteSize);
|
|
if (node==NULL)
|
|
return;
|
|
|
|
if (Flags==RTM_ROUTE_CHANGED) {
|
|
node->RCN_Route2 = (PRTM_ROUTE_NODE)HeapAlloc (
|
|
Table->RT_Heap,
|
|
0,
|
|
FIELD_OFFSET (RTM_ROUTE_NODE, RN_Route)+Table->RT_RouteSize);
|
|
if (node->RCN_Route2==NULL) {
|
|
HeapFree (Table->RT_Heap, 0, node);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
node->RCN_Route2 = NULL;
|
|
|
|
node->RCN_ReferenceCount = 0;
|
|
node->RCN_ResponsibleClient = ClientHandle;
|
|
node->RCN_Flags = Flags;
|
|
switch (Flags) {
|
|
case RTM_ROUTE_CHANGED:
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (&node->RCN_Route2->RN_Route, PrevBestRoute,
|
|
Table->RT_RouteSize);
|
|
break;
|
|
case RTM_ROUTE_ADDED:
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (&node->RCN_Route1, CurBestRoute, Table->RT_RouteSize);
|
|
break;
|
|
case RTM_ROUTE_DELETED:
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (&node->RCN_Route1, PrevBestRoute, Table->RT_RouteSize);
|
|
break;
|
|
default:
|
|
ASSERTMSG ("Invalid message flag", FALSE);
|
|
break;
|
|
}
|
|
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_ClientList, TRUE)) {
|
|
if (node->RCN_Route2!=NULL)
|
|
HeapFree (Table->RT_Heap, 0, node->RCN_Route2);
|
|
HeapFree (Table->RT_Heap, 0, node);
|
|
return ;
|
|
}
|
|
|
|
// Find and notify interested clients
|
|
cur = Table->RT_ClientList.RSL_Head.Flink;
|
|
if (!EnterSyncList (Table, &Table->RT_RouteChangeQueue, TRUE)) {
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
if (node->RCN_Route2!=NULL)
|
|
HeapFree (Table->RT_Heap, 0, node->RCN_Route2);
|
|
HeapFree (Table->RT_Heap, 0, node);
|
|
return ;
|
|
}
|
|
|
|
while (cur!=&Table->RT_ClientList.RSL_Head) {
|
|
PRTM_CLIENT clientPtr = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_CLIENT,
|
|
RC_Link);
|
|
if (((HANDLE)clientPtr!=ClientHandle)
|
|
&& (clientPtr->RC_NotificationEvent!=NULL)) {
|
|
node->RCN_ReferenceCount += 1;
|
|
if (node->RCN_ReferenceCount==1) {
|
|
InsertTailList (&Table->RT_RouteChangeQueue.RSL_Head,
|
|
&node->RCN_Link);
|
|
Table->RT_NumOfMessages += 1;
|
|
}
|
|
|
|
if (clientPtr->RC_PendingMessage
|
|
==&Table->RT_RouteChangeQueue.RSL_Head) {
|
|
BOOL res = SetEvent (clientPtr->RC_NotificationEvent);
|
|
ASSERTERRMSG ("Can't set client notification event.", res);
|
|
clientPtr->RC_PendingMessage = &node->RCN_Link;
|
|
}
|
|
else if ((Table->RT_NumOfMessages>RTM_MAX_ROUTE_CHANGE_MESSAGES)
|
|
&& (clientPtr->RC_PendingMessage==
|
|
Table->RT_RouteChangeQueue.RSL_Head.Flink)) {
|
|
PRTM_ROUTE_CHANGE_NODE firstNode = CONTAINING_RECORD (
|
|
clientPtr->RC_PendingMessage,
|
|
RTM_ROUTE_CHANGE_NODE,
|
|
RCN_Link);
|
|
#if DBG
|
|
Trace3 (ANY,
|
|
"Dequeueing message for 'lazy' client %lx.\n"
|
|
"\tat line %ld of %s\n",
|
|
(ULONG_PTR)clientPtr, __LINE__, __FILE__);
|
|
#endif
|
|
clientPtr->RC_PendingMessage =
|
|
clientPtr->RC_PendingMessage->Flink;
|
|
firstNode->RCN_ReferenceCount -= 1;
|
|
if (firstNode->RCN_ReferenceCount==0) {
|
|
Table->RT_NumOfMessages -= 1;
|
|
RemoveEntryList (&firstNode->RCN_Link);
|
|
if (firstNode->RCN_Route2!=NULL)
|
|
HeapFree (Table->RT_Heap, 0, firstNode->RCN_Route2);
|
|
HeapFree (Table->RT_Heap, 0, firstNode);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
if (node->RCN_ReferenceCount==0) {
|
|
if (node->RCN_Route2!=NULL)
|
|
HeapFree (Table->RT_Heap, 0, node->RCN_Route2);
|
|
HeapFree (Table->RT_Heap, 0, node);
|
|
}
|
|
LeaveSyncList (Table, &Table->RT_RouteChangeQueue);
|
|
LeaveSyncList (Table, &Table->RT_ClientList);
|
|
}
|
|
|
|
|
|
PRTM_ROUTE_NODE
|
|
CreateRouteNode (
|
|
PRTM_TABLE Table,
|
|
PLIST_ENTRY hashLink,
|
|
PLIST_ENTRY intfLink,
|
|
BOOL intfLinkFinal,
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
PLIST_ENTRY protLink,
|
|
BOOL protLinkFinal,
|
|
#endif
|
|
PRTM_SYNC_LIST hashBasket,
|
|
PRTM_XX_ROUTE ROUTE
|
|
) {
|
|
PRTM_SYNC_LIST intfBasket;
|
|
PRTM_ROUTE_NODE theNode = (PRTM_ROUTE_NODE)HeapAlloc (Table->RT_Heap, 0,
|
|
FIELD_OFFSET (RTM_ROUTE_NODE, RN_Route)+Table->RT_RouteSize);
|
|
|
|
if (theNode==NULL) {
|
|
#if DBG
|
|
// Report error in debuging builds
|
|
Trace2 (ANY,
|
|
"Can't allocate route\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
theNode->RN_Flags = RTM_NODE_FLAGS_INIT;
|
|
theNode->RN_Hash = hashBasket;
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
InitializeListEntry (&theNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK]);
|
|
|
|
// Make sure we can lock all list before adding
|
|
// We'll keep them locked untill we are sure
|
|
// that route can be added to prevent "partially
|
|
// inserted" entries in case of memory allocation failure, etc.
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
if (!EnterSyncList (Table, &Table->RT_ProtocolList, TRUE)) {
|
|
HeapFree (Table->RT_Heap, 0, theNode);
|
|
SetLastError (ERROR_NO_SYSTEM_RESOURCES);
|
|
return NULL;
|
|
}
|
|
|
|
if (protLink==NULL) {// If we haven't seen any entries with same
|
|
// net number and protocol, we'll find the
|
|
// protocol list and insert at the end
|
|
protLink = FindProtocolList (Table, ROUTE->XX_PROTOCOL);
|
|
if (protLink==NULL) {
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
HeapFree (Table->RT_Heap, 0, theNode);
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
intfBasket = &Table->RT_InterfaceHash[IntfHashFunction(Table,
|
|
ROUTE->XX_INTERFACE)];
|
|
if (!EnterSyncList (Table, intfBasket, TRUE)) {
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
HeapFree (Table->RT_Heap, 0, theNode);
|
|
SetLastError (ERROR_NO_SYSTEM_RESOURCES);
|
|
return NULL;
|
|
}
|
|
|
|
if (intfLink==NULL) {
|
|
intfLink = FindInterfaceList (intfBasket, ROUTE->XX_INTERFACE, TRUE);
|
|
if (intfLink==NULL) {
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
LeaveSyncList (Table, intfBasket);
|
|
HeapFree (Table->RT_Heap, 0, theNode);
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_NetNumberTempList, TRUE)) {
|
|
LeaveSyncList (Table, intfBasket);
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
HeapFree (Table->RT_Heap, 0, theNode);
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
// Add route to hash basket list
|
|
InsertTailList (hashLink, &theNode->RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
// Add route to protocol list
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
if (protLinkFinal) {
|
|
InsertTailList (protLink,
|
|
&theNode->RN_Links[RTM_PROTOCOL_LIST_LINK]);
|
|
}
|
|
else {
|
|
InsertHeadList (protLink,
|
|
&theNode->RN_Links[RTM_PROTOCOL_LIST_LINK]);
|
|
}
|
|
#endif
|
|
// Add it to interface list
|
|
if (intfLinkFinal) {
|
|
InsertTailList (intfLink,
|
|
&theNode->RN_Links[RTM_INTERFACE_LIST_LINK]);
|
|
}
|
|
else {
|
|
InsertHeadList (intfLink,
|
|
&theNode->RN_Links[RTM_INTERFACE_LIST_LINK]);
|
|
}
|
|
|
|
// We can now release interface and procotol lists
|
|
// because we are sure that addition to net number sorted
|
|
// list won't fail
|
|
LeaveSyncList (Table, intfBasket);
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
|
|
// Add route to temporary net number list (to be later moved
|
|
// to the master list by the update thread)
|
|
AddNetNumberListNode (Table, theNode);
|
|
Table->RT_NetNumberTempCount += 1;
|
|
if (Table->RT_NetNumberTempCount==RTM_TEMP_LIST_MAX_COUNT) {
|
|
if (InterlockedIncrement (&Table->RT_UpdateWorkerPending)==0) {
|
|
DWORD status;
|
|
status = RtlQueueWorkItem (ConsolidateNetNumberListsWI, Table, 0);
|
|
ASSERTERRMSG ("Can't queue update work item", status==STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
LeaveSyncList (Table, &Table->RT_NetNumberTempList);
|
|
|
|
return theNode;
|
|
}
|
|
|
|
|
|
DWORD
|
|
RemoveRouteNode (
|
|
PRTM_TABLE Table,
|
|
PRTM_ROUTE_NODE theNode
|
|
) {
|
|
PLIST_ENTRY head;
|
|
PRTM_SYNC_LIST intfBasket
|
|
= &Table->RT_InterfaceHash[IntfHashFunction(Table,
|
|
theNode->RN_Route.XX_INTERFACE)];
|
|
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
if (!EnterSyncList (Table, &Table->RT_ProtocolList, TRUE)) {
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
#endif
|
|
if (!EnterSyncList (Table, intfBasket, TRUE)) {
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_ExpirationQueue, TRUE)) {
|
|
LeaveSyncList (Table, intfBasket);
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_DeletedList, TRUE)) {
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
LeaveSyncList (Table, intfBasket);
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
|
|
|
|
// Remove node from the interface list
|
|
head = theNode->RN_Links[RTM_INTERFACE_LIST_LINK].Flink;
|
|
RemoveEntryList (&theNode->RN_Links[RTM_INTERFACE_LIST_LINK]);
|
|
if (IsListEmpty (head)) {
|
|
PRTM_INTERFACE_NODE intfNode = CONTAINING_RECORD (head,
|
|
RTM_INTERFACE_NODE,
|
|
IN_Head);
|
|
RemoveEntryList (&intfNode->IN_Link);
|
|
GlobalFree (intfNode);
|
|
}
|
|
|
|
LeaveSyncList (Table, intfBasket);
|
|
|
|
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
RemoveEntryList (&theNode->RN_Links[RTM_PROTOCOL_LIST_LINK]);
|
|
// Remove node from the protocol list
|
|
LeaveSyncList (Table, &Table->RT_ProtocolList);
|
|
#endif
|
|
// Remove form expiration queue if it was there
|
|
if (IsListEntry (&theNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK])) {
|
|
RemoveEntryList (&theNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK]);
|
|
}
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
|
|
// Remove node from the hash basket list
|
|
RemoveEntryList (&theNode->RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
// let update thread take care of disposing
|
|
InsertHeadList (&Table->RT_DeletedList.RSL_Head,
|
|
&theNode->RN_Links[RTM_DELETED_LIST_LINK]);
|
|
Table->RT_DeletedNodesCount += 1;
|
|
if (Table->RT_DeletedNodesCount==RTM_DELETED_LIST_MAX_COUNT) {
|
|
if (InterlockedIncrement (&Table->RT_UpdateWorkerPending)==0) {
|
|
DWORD status = RtlQueueWorkItem (ConsolidateNetNumberListsWI, Table, 0);
|
|
ASSERTERRMSG ("Can't queue update work item", status==STATUS_SUCCESS);
|
|
}
|
|
}
|
|
LeaveSyncList (Table, &Table->RT_DeletedList);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Adds a given route or updates metric, TimeToLive, and reserved fields
|
|
// if route with same net number, interface, routing protocol,
|
|
// and next hop address already exists in the table
|
|
// Returns:
|
|
// NO_ERROR - if route was added OK or
|
|
// ERROR_INVALID_PARAMETER - if Route contains invalid parameter (suh as
|
|
// protocol does not match client's protocol)
|
|
// ERROR_NOT_ENOUGH_MEMORY - if route can not be inserted because of memory
|
|
// allocation problem
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
DWORD WINAPI
|
|
RtmAddRoute(
|
|
IN HANDLE ClientHandle, // Handle that identifies protocol family
|
|
// and routing protocol of the route
|
|
// to add/update (RoutingProtocol field
|
|
// of the Route parameter is ignored)
|
|
// and coordinates this operation with
|
|
// notifications
|
|
// through the event (notificanitons will not
|
|
// be sent to the caller)
|
|
IN PVOID Route, // Route to add
|
|
// Route fields used as input:
|
|
// Destination network
|
|
// Interface through which route was received
|
|
// Address of next hop router
|
|
// Three fields above combined with protocol id uniquely
|
|
// identify the route in the table
|
|
// Data specific to protocol family
|
|
// Protocol independent metric
|
|
// Any data specific to routing
|
|
// protocol (subject to size limitation
|
|
// defined by PROTOCOL_SPECIFIC_DATA
|
|
// structure above)
|
|
IN DWORD TimeToLive, // In seconds. INFINITE if route is not to
|
|
// be aged out. The maximum value for
|
|
// this parameter is 2147483 sec (that
|
|
// is 24+ days)
|
|
|
|
OUT DWORD *Flags, // If added/updated route is the best route to the
|
|
// destination RTM_CURRENT_BEST_ROUTE will be set,
|
|
// AND if added/updated route changed (or
|
|
// replaced alltogether) previous
|
|
// best route info for the destination,
|
|
// RTM_PREVIOUS_BEST_ROUTE will be set
|
|
OUT PVOID CurBestRoute OPTIONAL,// This buffer (if present) will
|
|
// receive the route that became the best as
|
|
// the result of this addition/update if
|
|
// RTM_CURRENT_BEST_ROUTE is set
|
|
OUT PVOID PrevBestRoute OPTIONAL// This buffer (if present) will
|
|
// receive the route that was the best before
|
|
// this addition/update if
|
|
// RTM_PREVIOUS_BEST_ROUTE is set
|
|
) {
|
|
#define ROUTE ((PRTM_XX_ROUTE)Route)
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle)
|
|
DWORD status; // Operation result
|
|
INT res; // Comparison result
|
|
PRTM_SYNC_LIST hashBasket; // Hash basket to which added route
|
|
// belongs
|
|
// Links in all mantained lists for added route
|
|
PLIST_ENTRY cur, hashLink=NULL, intfLink=NULL, protLink=NULL;
|
|
// Node created for added route and best node for the
|
|
// network
|
|
PRTM_ROUTE_NODE theNode=NULL, curBestNode=NULL;
|
|
// Flags that indicate that corresponing links are determined
|
|
BOOL intfLinkFinal=FALSE;
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
BOOL protLinkFinal=FALSE;
|
|
#endif
|
|
BOOL newRoute=FALSE, updatedRoute=FALSE;
|
|
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
ROUTE->XX_PROTOCOL = ClientPtr->RC_RoutingProtocol;
|
|
GetSystemTimeAsFileTime (&ROUTE->XX_TIMESTAMP);
|
|
|
|
status = ValidateRoute (Table, ROUTE);
|
|
if (status!=NO_ERROR)
|
|
return status;
|
|
|
|
// Find and lock the hash basket for added route
|
|
hashBasket = &Table->RT_NetNumberHash [HashFunction (Table,
|
|
((char *)ROUTE)
|
|
+sizeof(RTM_XX_ROUTE))];
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
ExitTableAPI(Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
// Traverse the list attached to the hash basket to
|
|
// find proper place for added route (entries in hash
|
|
// basket are ordered by network number and metric
|
|
cur = hashBasket->RSL_Head.Flink;
|
|
while (cur!=&hashBasket->RSL_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]
|
|
);
|
|
|
|
if (!IsEnumerator (node)) {
|
|
|
|
// Check if network numbers match
|
|
res = NetNumCmp (Table, ROUTE, &node->RN_Route);
|
|
|
|
|
|
if (res==0) { // We found block of entries with same net number
|
|
// We'll have to look through all of them
|
|
|
|
// Check all parameters of the node to see if we already
|
|
// have this route and this is just an update
|
|
if ((hashLink==NULL) && (theNode==NULL)) {
|
|
if (ROUTE->XX_PROTOCOL
|
|
== node->RN_Route.XX_PROTOCOL) {
|
|
if (ClientPtr->RC_Flags&RTM_PROTOCOL_SINGLE_ROUTE)
|
|
theNode = node;
|
|
else if (ROUTE->XX_INTERFACE
|
|
== node->RN_Route.XX_INTERFACE) {
|
|
res = NextHopCmp (Table, ROUTE, &node->RN_Route);
|
|
if (res == 0)
|
|
theNode = node;
|
|
else if (res < 0)
|
|
hashLink = cur;
|
|
}
|
|
else if (ROUTE->XX_INTERFACE
|
|
< node->RN_Route.XX_INTERFACE)
|
|
hashLink = cur;
|
|
}
|
|
else if (ROUTE->XX_PROTOCOL
|
|
< node->RN_Route.XX_PROTOCOL)
|
|
hashLink = cur;
|
|
}
|
|
|
|
// Just looking for current best route
|
|
// (not including added/updated route)
|
|
if ((node!=theNode)
|
|
&& IsEnabled(node)
|
|
&& ((curBestNode==NULL)
|
|
|| IsBest(node)
|
|
|| (MetricCmp (Table,
|
|
&curBestNode->RN_Route,
|
|
&node->RN_Route)>0)))
|
|
curBestNode = node;
|
|
|
|
|
|
// We have to check all entries with same net number
|
|
// anyway (to find the best route), so we might as
|
|
// well find links for the added route in protocol
|
|
// and interface list if such links exist (if not, we'll
|
|
// just insert new entry at the end of the list)
|
|
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
// If we need and haven't found yet a proper place to
|
|
// insert added route into the protocol list and this route
|
|
// has the same protocol as added route we should
|
|
// consider it.
|
|
if (!protLinkFinal && (theNode==NULL)
|
|
&& (ROUTE->XX_PROTOCOL
|
|
==node->RN_Route.XX_PROTOCOL)) {
|
|
protLink = &node->RN_Links[RTM_PROTOCOL_LIST_LINK];
|
|
// If added route has lower interface number than
|
|
// this one we'll insert it in protocol list right
|
|
// BEFORE this one, otherwise
|
|
// we are not sure if this is a proper place yet (there
|
|
// may be other routes with same protocol that have
|
|
// lower interface number), but we note the position
|
|
// and insert added route right AFTER this one if there
|
|
// are no more routes of this protocol.
|
|
protLinkFinal = ROUTE->XX_INTERFACE
|
|
< node->RN_Route.XX_INTERFACE;
|
|
}
|
|
#endif
|
|
|
|
// Same story with the interface list
|
|
if (!intfLinkFinal
|
|
&& (ROUTE->XX_INTERFACE
|
|
==node->RN_Route.XX_INTERFACE)) {
|
|
intfLink = &node->RN_Links[RTM_INTERFACE_LIST_LINK];
|
|
intfLinkFinal = ROUTE->XX_PROTOCOL
|
|
< node->RN_Route.XX_PROTOCOL;
|
|
}
|
|
}
|
|
else if (res < 0) // We must have seen all entries with
|
|
// matching network number -> nothing
|
|
// to look for anymore
|
|
break;
|
|
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
|
|
|
|
if (theNode!=NULL) {
|
|
// We found the route, so just need to update its parameters
|
|
|
|
if (ClientPtr->RC_Flags&RTM_PROTOCOL_SINGLE_ROUTE) {
|
|
updatedRoute = (MetricCmp (Table, &theNode->RN_Route, ROUTE)!=0)
|
|
|| (theNode->RN_Route.XX_INTERFACE!=ROUTE->XX_INTERFACE)
|
|
|| (NextHopCmp (Table, &theNode->RN_Route, ROUTE)!=0)
|
|
|| !FSDCmp (Table, &theNode->RN_Route, ROUTE);
|
|
|
|
if (ROUTE->XX_INTERFACE!=theNode->RN_Route.XX_INTERFACE) {
|
|
PRTM_SYNC_LIST intfBasketOld
|
|
= &Table->RT_InterfaceHash[IntfHashFunction(Table,
|
|
theNode->RN_Route.XX_INTERFACE)];
|
|
PRTM_SYNC_LIST intfBasketNew
|
|
= &Table->RT_InterfaceHash[IntfHashFunction(Table,
|
|
ROUTE->XX_INTERFACE)];
|
|
|
|
|
|
// Make sure we lock interface hash table basket
|
|
// in the same order to prevent possible deadlock
|
|
if (intfBasketOld<intfBasketNew) {
|
|
if (!EnterSyncList (Table, intfBasketOld, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitAddRoute;
|
|
}
|
|
if (!EnterSyncList (Table, intfBasketNew, TRUE)) {
|
|
LeaveSyncList (Table, intfBasketOld);
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitAddRoute;
|
|
}
|
|
}
|
|
else if (intfBasketOld>intfBasketNew) {
|
|
if (!EnterSyncList (Table, intfBasketNew, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitAddRoute;
|
|
}
|
|
if (!EnterSyncList (Table, intfBasketOld, TRUE)) {
|
|
LeaveSyncList (Table, intfBasketOld);
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitAddRoute;
|
|
}
|
|
}
|
|
else {
|
|
if (!EnterSyncList (Table, intfBasketOld, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitAddRoute;
|
|
}
|
|
}
|
|
|
|
|
|
if (intfLink==NULL) {
|
|
intfLink = FindInterfaceList (intfBasketNew, ROUTE->XX_INTERFACE, TRUE);
|
|
if (intfLink==NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
LeaveSyncList (Table, intfBasketOld);
|
|
if (intfBasketNew!=intfBasketOld)
|
|
LeaveSyncList (Table, intfBasketNew);
|
|
goto ExitAddRoute;
|
|
}
|
|
}
|
|
// Add it to interface list
|
|
RemoveEntryList (&theNode->RN_Links[RTM_INTERFACE_LIST_LINK]);
|
|
InsertTailList (intfLink,
|
|
&theNode->RN_Links[RTM_INTERFACE_LIST_LINK]);
|
|
LeaveSyncList (Table, intfBasketOld);
|
|
if (intfBasketNew!=intfBasketOld)
|
|
LeaveSyncList (Table, intfBasketNew);
|
|
}
|
|
}
|
|
else
|
|
updatedRoute = MetricCmp (Table, &theNode->RN_Route, ROUTE)
|
|
|| !FSDCmp (Table, &theNode->RN_Route, ROUTE)!=0;
|
|
|
|
}
|
|
|
|
else /*if (theNode==NULL)*/ { // We haven't found matching route,
|
|
// so we'll add a new one
|
|
// If we were not able to find place to insert added route
|
|
// into the list, we use the place where we stop
|
|
// the search (it is either end of the list or
|
|
// network with higher number if we did not see our
|
|
// network or all other entries had lower metric
|
|
if (hashLink==NULL)
|
|
hashLink = cur;
|
|
theNode = CreateRouteNode (Table,
|
|
hashLink,
|
|
intfLink,
|
|
intfLinkFinal,
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
protLink,
|
|
protLinkFinal,
|
|
#endif
|
|
hashBasket,
|
|
ROUTE);
|
|
if (theNode==NULL) {
|
|
status = GetLastError ();
|
|
goto ExitAddRoute;
|
|
}
|
|
|
|
if (curBestNode==NULL) {
|
|
InterlockedIncrement (&Table->RT_NetworkCount);
|
|
SetBest (theNode); // This is the first
|
|
// route to the network, and thus
|
|
// it is the best.
|
|
newRoute = TRUE;
|
|
}
|
|
else {
|
|
newRoute = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// All routes (new or old) need to be placed into the Expiration list
|
|
// to be properly aged out
|
|
if (!EnterSyncList (Table, &Table->RT_ExpirationQueue, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitAddRoute;
|
|
}
|
|
|
|
if (IsListEntry (&theNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK])) {
|
|
RemoveEntryList (&theNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK]);
|
|
}
|
|
|
|
if (TimeToLive!=INFINITE) {
|
|
TimeToLive *= 1000;
|
|
if (TimeToLive > (MAXTICKS/2-1))
|
|
TimeToLive = MAXTICKS/2-1;
|
|
theNode->RN_ExpirationTime = (GetTickCount () + TimeToLive)&0xFFFFFF00;
|
|
if (AddExpirationQueueNode (Table, theNode)) {
|
|
if (InterlockedIncrement (&Table->RT_ExpirationWorkerPending)==0) {
|
|
// New route expiration time comes before the update thread
|
|
// is scheduled to wakeup next time, so wake it up NOW
|
|
status = RtlQueueWorkItem (ProcessExpirationQueueWI, Table,
|
|
WT_EXECUTEINIOTHREAD);
|
|
ASSERTERRMSG ("Can't queue expiration work item", status==STATUS_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// Initilaize this list link, so we know it is not
|
|
// in the list and we do not have to remove it from
|
|
// there
|
|
InitializeListEntry (&theNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK]);
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
|
|
|
|
|
|
if (!IsEnabled(theNode)) {// Ignore disabled nodes
|
|
if (updatedRoute)
|
|
// Update the route data
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
else {
|
|
memcpy (&theNode->RN_Route.XX_TIMESTAMP,
|
|
&ROUTE->XX_TIMESTAMP,
|
|
sizeof (theNode->RN_Route.XX_TIMESTAMP));
|
|
memcpy (&theNode->RN_Route.XX_PSD,
|
|
&ROUTE->XX_PSD,
|
|
sizeof (theNode->RN_Route.XX_PSD));
|
|
}
|
|
*Flags = 0;
|
|
}
|
|
else if (curBestNode!=NULL) { // There is at least one other route to the
|
|
// same network as the route we're adding/updating
|
|
if (MetricCmp (Table, ROUTE, &curBestNode->RN_Route)<0) {
|
|
// Added/updated route metric is lower, it is the best
|
|
if (!IsBest(theNode)) {// The best route has changed, we need to
|
|
// update best route designation
|
|
ResetBest (curBestNode);
|
|
SetBest (theNode);
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
|
|
// include previous best route info
|
|
// in notificaion message
|
|
*Flags = RTM_PREVIOUS_BEST_ROUTE|RTM_CURRENT_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, ROUTE, Table->RT_RouteSize);
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (PrevBestRoute, &curBestNode->RN_Route, Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags, ROUTE,
|
|
&curBestNode->RN_Route);
|
|
}
|
|
else {
|
|
if (updatedRoute) {
|
|
*Flags = RTM_PREVIOUS_BEST_ROUTE|RTM_CURRENT_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, ROUTE, Table->RT_RouteSize);
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (PrevBestRoute, &theNode->RN_Route, Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags, ROUTE, &theNode->RN_Route);
|
|
// Update the route data
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
}
|
|
else {
|
|
memcpy (&theNode->RN_Route.XX_TIMESTAMP,
|
|
&ROUTE->XX_TIMESTAMP,
|
|
sizeof (theNode->RN_Route.XX_TIMESTAMP));
|
|
memcpy (&theNode->RN_Route.XX_PSD,
|
|
&ROUTE->XX_PSD,
|
|
sizeof (theNode->RN_Route.XX_PSD));
|
|
}
|
|
}
|
|
}
|
|
else if (IsBest(theNode)) {
|
|
if (MetricCmp (Table, ROUTE, &curBestNode->RN_Route)>0) {
|
|
// We are downgrading our best route,
|
|
// and new best route poped up.
|
|
// Update best route designation
|
|
ResetBest (theNode);
|
|
SetBest (curBestNode);
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
// Inform clients about the change
|
|
*Flags = RTM_CURRENT_BEST_ROUTE | RTM_PREVIOUS_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (PrevBestRoute, &curBestNode->RN_Route, Table->RT_RouteSize);
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, ROUTE, Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags, &curBestNode->RN_Route,
|
|
ROUTE);
|
|
}
|
|
else if (updatedRoute) {
|
|
*Flags = RTM_PREVIOUS_BEST_ROUTE|RTM_CURRENT_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, ROUTE, Table->RT_RouteSize);
|
|
if (ARGUMENT_PRESENT (PrevBestRoute))
|
|
memcpy (PrevBestRoute, &theNode->RN_Route, Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags, ROUTE, &theNode->RN_Route);
|
|
// Update the route data
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
}
|
|
else {
|
|
memcpy (&theNode->RN_Route.XX_TIMESTAMP,
|
|
&ROUTE->XX_TIMESTAMP,
|
|
sizeof (theNode->RN_Route.XX_TIMESTAMP));
|
|
memcpy (&theNode->RN_Route.XX_PSD,
|
|
&ROUTE->XX_PSD,
|
|
sizeof (theNode->RN_Route.XX_PSD));
|
|
}
|
|
}
|
|
else { // Added route metric was and is higher and thus has no
|
|
// effect on best route to the network
|
|
*Flags = 0;
|
|
// Update the route data
|
|
if (updatedRoute) {
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
}
|
|
else {
|
|
memcpy (&theNode->RN_Route.XX_TIMESTAMP,
|
|
&ROUTE->XX_TIMESTAMP,
|
|
sizeof (theNode->RN_Route.XX_TIMESTAMP));
|
|
memcpy (&theNode->RN_Route.XX_PSD,
|
|
&ROUTE->XX_PSD,
|
|
sizeof (theNode->RN_Route.XX_PSD));
|
|
}
|
|
}
|
|
}
|
|
else { // Not other node exist for this network
|
|
if (newRoute) {
|
|
*Flags = RTM_CURRENT_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, ROUTE, Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags, ROUTE, NULL);
|
|
}
|
|
else if (updatedRoute) {
|
|
*Flags = RTM_CURRENT_BEST_ROUTE | RTM_PREVIOUS_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (CurBestRoute, ROUTE, Table->RT_RouteSize);
|
|
if (ARGUMENT_PRESENT (CurBestRoute))
|
|
memcpy (PrevBestRoute, &theNode->RN_Route, Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags, ROUTE, &theNode->RN_Route);
|
|
// Update the route data
|
|
memcpy (&theNode->RN_Route, ROUTE, Table->RT_RouteSize);
|
|
}
|
|
else {
|
|
memcpy (&theNode->RN_Route.XX_TIMESTAMP,
|
|
&ROUTE->XX_TIMESTAMP,
|
|
sizeof (theNode->RN_Route.XX_TIMESTAMP));
|
|
memcpy (&theNode->RN_Route.XX_PSD,
|
|
&ROUTE->XX_PSD,
|
|
sizeof (theNode->RN_Route.XX_PSD));
|
|
*Flags = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// for each new route added the size of the net mask is noted.
|
|
//
|
|
// This is useful at route lookup time. For now since there
|
|
// is no efficient way to do a route lookup, it is necessary to
|
|
// guess the (sub)net mask associated with a destination to find
|
|
// the best route associated with it. By tracking the net mask
|
|
// for each added route the number of guesses for the mask can
|
|
// be minimized.
|
|
//
|
|
|
|
if ( newRoute )
|
|
{
|
|
#if ROUTE_LOOKUP_BDG
|
|
TRACE2(
|
|
ANY, "Network : %x %x",
|
|
((PIP_NETWORK) NNM(ROUTE))->N_NetNumber,
|
|
((PIP_NETWORK) NNM(ROUTE))->N_NetMask
|
|
);
|
|
|
|
TRACE1(
|
|
ANY, "Next Hop : %x",
|
|
((PRTM_IP_ROUTE) NNM(ROUTE))-> RR_NextHopAddress.N_NetNumber
|
|
);
|
|
#endif
|
|
SetMaskCount( (PIP_NETWORK) NNM( ROUTE ), TRUE );
|
|
}
|
|
|
|
status = NO_ERROR;
|
|
|
|
ExitAddRoute:
|
|
LeaveSyncList (Table, hashBasket);
|
|
ExitTableAPI(Table);
|
|
|
|
#undef ClientPtr
|
|
#undef ROUTE
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
// Deletes a given route
|
|
//
|
|
// Returns:
|
|
// NO_ERROR - if route was deleted OK or
|
|
// ERROR_NO_SUCH_ROUTE - if route to be deleted was not found in the table
|
|
DWORD WINAPI
|
|
RtmDeleteRoute (
|
|
IN HANDLE ClientHandle, // Handle to coordinate
|
|
// this operation with notifications
|
|
// through the event (notificanitons will not
|
|
// be sent to the caller)
|
|
IN PVOID Route, // ROUTE to delete
|
|
OUT DWORD *Flags, // If deleted route was the best
|
|
// route, RTM_PREVIOUS_BEST_ROUTE will be set
|
|
// AND if there is another route for the same
|
|
// network, RTM_CURRENT_BEST_ROUTE will be set
|
|
OUT PVOID CurBestRoute OPTIONAL// // This buffer will (optionally) receive
|
|
// the best route for the same network
|
|
// if RTM_CURRENT_BEST_ROUTE is set
|
|
) {
|
|
#define ROUTE ((PRTM_XX_ROUTE)Route)
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle)
|
|
DWORD status; // Operation result
|
|
INT res; // Comparison result
|
|
PRTM_SYNC_LIST hashBasket; // Hash basket to which the route belongs
|
|
PLIST_ENTRY cur;
|
|
PRTM_ROUTE_NODE theNode=NULL,// Table node associated with the route
|
|
curBestNode=NULL; // New best route for the
|
|
// network which route is deleted
|
|
// (if any)
|
|
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
ROUTE->XX_PROTOCOL = ClientPtr->RC_RoutingProtocol;
|
|
|
|
// Try locate the node in hash basket
|
|
hashBasket = &Table->RT_NetNumberHash [HashFunction (Table,
|
|
((char *)ROUTE)
|
|
+sizeof(RTM_XX_ROUTE))];
|
|
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
cur = hashBasket->RSL_Head.Flink;
|
|
while (cur!=&hashBasket->RSL_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]
|
|
);
|
|
if (!IsEnumerator (node)) {
|
|
|
|
// Check if network number matches
|
|
res = NetNumCmp (Table, ROUTE, &node->RN_Route);
|
|
|
|
if (res==0) {
|
|
// Go through entries for network of interest
|
|
|
|
// Try to locate the route to be deleted
|
|
if ((theNode==NULL)
|
|
&& (ROUTE->XX_INTERFACE
|
|
== node->RN_Route.XX_INTERFACE)
|
|
&& (ROUTE->XX_PROTOCOL
|
|
== node->RN_Route.XX_PROTOCOL)
|
|
&& (NextHopCmp (Table, ROUTE, &node->RN_Route)
|
|
==0)) {
|
|
theNode = node;
|
|
if (!IsBest(theNode))
|
|
break;
|
|
}
|
|
else if (IsEnabled(node)
|
|
&& ((curBestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&curBestNode->RN_Route,
|
|
&node->RN_Route)>0)))
|
|
curBestNode = node;
|
|
|
|
}
|
|
else if (res < 0)
|
|
// We passed the place where routes for our
|
|
// network are located
|
|
break;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
|
|
if (theNode!=NULL) { // Yes, we found the node
|
|
if (IsBest(theNode)) { // And it was the best,
|
|
// inform interested clients
|
|
if (curBestNode!=NULL) { // There is another best node
|
|
|
|
ResetBest (theNode);
|
|
SetBest (curBestNode);
|
|
|
|
*Flags = RTM_CURRENT_BEST_ROUTE | RTM_PREVIOUS_BEST_ROUTE;
|
|
if (ARGUMENT_PRESENT(CurBestRoute))
|
|
memcpy (CurBestRoute, &curBestNode->RN_Route,
|
|
Table->RT_RouteSize);
|
|
NotifyClients (Table, ClientHandle, *Flags,
|
|
&curBestNode->RN_Route,
|
|
&theNode->RN_Route);
|
|
}
|
|
else { // This one was the only available node
|
|
InterlockedDecrement (&Table->RT_NetworkCount);
|
|
*Flags = RTM_PREVIOUS_BEST_ROUTE;
|
|
NotifyClients (Table, ClientHandle, *Flags, NULL, &theNode->RN_Route);
|
|
|
|
|
|
//
|
|
// Decrement mask count
|
|
//
|
|
|
|
SetMaskCount( (PIP_NETWORK) NNM( ROUTE ), FALSE );
|
|
|
|
}
|
|
}
|
|
else // This was not the best node, nobody cares
|
|
*Flags = 0;
|
|
|
|
status = RemoveRouteNode (Table, theNode);
|
|
}
|
|
else
|
|
// Well, we don't have this node already (aged out ?)
|
|
status = ERROR_NO_SUCH_ROUTE;
|
|
|
|
LeaveSyncList (Table, hashBasket);
|
|
ExitTableAPI (Table);
|
|
#undef ClientPtr
|
|
#undef ROUTE
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
// Check if route exists and return it if so.
|
|
// Returns:
|
|
// TRUE if route exists for the given network
|
|
// FALSE otherwise
|
|
// If one of the parameters is invalid, the function returns FALSE
|
|
// and GetLastError() returns ERROR_INVALID_PARAMETER
|
|
BOOL WINAPI
|
|
RtmIsRoute (
|
|
IN DWORD ProtocolFamily,
|
|
IN PVOID Network, // Network whose existence is being checked
|
|
OUT PVOID BestRoute OPTIONAL // Returns the best route if the network
|
|
// is found
|
|
) {
|
|
INT res;
|
|
PRTM_TABLE Table;
|
|
PRTM_SYNC_LIST hashBasket;
|
|
PLIST_ENTRY cur;
|
|
PRTM_ROUTE_NODE bestNode = NULL;
|
|
BOOL result = FALSE;
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
|| !EnterTableAPI (Table)) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
// Locate the network in the hash basket
|
|
hashBasket = &Table->RT_NetNumberHash[HashFunction (Table, Network)];
|
|
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
SetLastError (ERROR_NO_SYSTEM_RESOURCES);
|
|
return FALSE;
|
|
}
|
|
|
|
cur = hashBasket->RSL_Head.Flink;
|
|
while (cur!=&hashBasket->RSL_Head) {
|
|
PRTM_ROUTE_NODE node;
|
|
node = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]
|
|
);
|
|
if (!IsEnumerator (node)
|
|
&& IsEnabled(node)) {
|
|
|
|
res = (*Table->RT_Config.RPFC_NNcmp) (
|
|
Network,
|
|
NNM(&node->RN_Route));
|
|
|
|
if ((res == 0)
|
|
&& IsBest(node)) {
|
|
bestNode = node;
|
|
break;
|
|
}
|
|
else if (res < 0)
|
|
break;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
|
|
|
|
if (bestNode!=NULL) { // We found a match
|
|
if (ARGUMENT_PRESENT(BestRoute)) {
|
|
memcpy (BestRoute, &bestNode->RN_Route, Table->RT_RouteSize);
|
|
}
|
|
LeaveSyncList (Table, hashBasket);
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
// We don't have one (result is FALSE by default)
|
|
LeaveSyncList (Table, hashBasket);
|
|
// This is not an error condition, we just do not have it
|
|
SetLastError (NO_ERROR);
|
|
}
|
|
|
|
ExitTableAPI (Table);
|
|
return result;
|
|
}
|
|
|
|
|
|
// Gets number of networks with known routes for a specific protocol family
|
|
ULONG WINAPI
|
|
RtmGetNetworkCount (
|
|
IN DWORD ProtocolFamily
|
|
) {
|
|
PRTM_TABLE Table;
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
|| !EnterTableAPI (&Tables[ProtocolFamily])) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
|
|
ExitTableAPI (Table);
|
|
return Table->RT_NetworkCount;
|
|
}
|
|
|
|
// Gets route age (time since it was created or updated last) in seconds
|
|
// from its time stamp.
|
|
// Rtm time stamps routes whenever they are added or updated.
|
|
// Note: that information returned by this routine is actually
|
|
// derived from TimeStamp field of the route structure, so it
|
|
// returns valid results only if route structure passed to was
|
|
// actually filled by Rtm
|
|
// If value in TimeStamp field is invalid this routing returns 0xFFFFFFFF
|
|
ULONG WINAPI
|
|
RtmGetRouteAge (
|
|
IN PVOID Route
|
|
) {
|
|
#define ROUTE ((PRTM_XX_ROUTE)Route)
|
|
ULONGLONG curTime;
|
|
GetSystemTimeAsFileTime ((FILETIME *)&curTime);
|
|
curTime -= *((PULONGLONG)&ROUTE->XX_TIMESTAMP);
|
|
if (((PULARGE_INTEGER)&curTime)->HighPart<10000000)
|
|
return (ULONG)(curTime/10000000);
|
|
else {
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return 0xFFFFFFFF;
|
|
}
|
|
#undef ROUTE
|
|
}
|
|
|
|
|
|
// Creates enumeration handle to start scan by specified criteria.
|
|
// Places a dummy node in the beginning of the table.
|
|
// Returns NULL in case of failure. Call GetLastError () to get extended
|
|
// error information
|
|
// Error codes:
|
|
// ERROR_INVALID_PARAMETER - specified protocol family is not supported or
|
|
// undefined enumeration flag
|
|
// ERROR_NO_ROUTES - no routes exist with specified criteria
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
// ERROR_NOT_ENOUGH_MEMORY - not enough memory to allocate client control block
|
|
HANDLE WINAPI
|
|
RtmCreateEnumerationHandle (
|
|
IN DWORD ProtocolFamily,
|
|
IN DWORD EnumerationFlags, // Limitation flags
|
|
IN PVOID CriteriaRoute // Criteria for limitation flags
|
|
// The following fields shout be set
|
|
// Protocol if interest if RTM_ONLY_THIS_PROTOCOL is set
|
|
// Network of interest if RTM_ONLY_THIS_NETWORK is set
|
|
// Interface of interest if RTM_ONLY_THIS_INTERFACE is set
|
|
) {
|
|
#define ROUTE ((PRTM_XX_ROUTE)CriteriaRoute)
|
|
HANDLE EnumerationHandle;
|
|
#define EnumPtr ((PRTM_ENUMERATOR)EnumerationHandle) // To access fields
|
|
// in this routine
|
|
PRTM_TABLE Table;
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
|| !EnterTableAPI (&Tables[ProtocolFamily])) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
if (EnumerationFlags &
|
|
(~(RTM_ONLY_THIS_NETWORK|RTM_ONLY_THIS_INTERFACE
|
|
|RTM_ONLY_THIS_PROTOCOL|RTM_ONLY_BEST_ROUTES
|
|
|RTM_INCLUDE_DISABLED_ROUTES))) {
|
|
ExitTableAPI (Table);
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate and initialize enumerator
|
|
EnumerationHandle = GlobalAlloc (GMEM_FIXED,
|
|
FIELD_OFFSET (RTM_ENUMERATOR, RE_Route)+Table->RT_RouteSize);
|
|
if (EnumerationHandle!=NULL) {
|
|
EnumPtr->RE_Flags = RTM_ENUMERATOR_FLAGS_INIT;
|
|
EnumPtr->RE_EnumerationFlags = EnumerationFlags;
|
|
if (EnumerationFlags
|
|
& (RTM_ONLY_THIS_NETWORK
|
|
|RTM_ONLY_THIS_INTERFACE
|
|
|RTM_ONLY_THIS_PROTOCOL))
|
|
memcpy (&EnumPtr->RE_Route, CriteriaRoute, Table->RT_RouteSize);
|
|
EnumPtr->RE_Hash = NULL;
|
|
EnumPtr->RE_Head = NULL;
|
|
// WHICH LIST TO USE ?
|
|
// In general we should have more interfaces than protocols,
|
|
// so:
|
|
// if they only want a specific interface, we'll use
|
|
// the interface list even if they want a specific protocol too
|
|
if (EnumerationFlags & RTM_ONLY_THIS_INTERFACE) {
|
|
EnumPtr->RE_Link = RTM_INTERFACE_LIST_LINK;
|
|
EnumPtr->RE_Lock = &Table->RT_InterfaceHash[IntfHashFunction(Table,
|
|
EnumPtr->RE_Route.XX_INTERFACE)];
|
|
if (EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
EnumPtr->RE_Head = FindInterfaceList (EnumPtr->RE_Lock,
|
|
EnumPtr->RE_Route.XX_INTERFACE, FALSE);
|
|
if (EnumPtr->RE_Head!=NULL) {
|
|
InsertTailList (EnumPtr->RE_Head,
|
|
&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
}
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
}
|
|
}
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
else if (EnumerationFlags & RTM_ONLY_THIS_PROTOCOL) {
|
|
// if they only want a specific protocol, we'll use
|
|
// the protocol list
|
|
EnumPtr->RE_Link = RTM_PROTOCOL_LIST_LINK;
|
|
EnumPtr->RE_Lock = &Table->RT_ProtocolList;
|
|
if (EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
EnumPtr->RE_Head = FindProtocolList (Table,
|
|
EnumPtr->RE_Route.XX_PROTOCOL, FALSE);
|
|
if (EnumPtr->RE_Head!=NULL) {
|
|
InsertTailList (EnumPtr->RE_Head,
|
|
&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
}
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
// otherwise, we have to use hash table
|
|
EnumPtr->RE_Link = RTM_NET_NUMBER_HASH_LINK;
|
|
// Now, if they want a specific network,
|
|
// we'll only search in one hash basket
|
|
if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) {
|
|
EnumPtr->RE_Lock = &Table->RT_NetNumberHash[HashFunction (
|
|
Table,
|
|
((char *)ROUTE)
|
|
+sizeof(RTM_XX_ROUTE))];
|
|
if (EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
if (!IsListEmpty (&EnumPtr->RE_Lock->RSL_Head)) {
|
|
EnumPtr->RE_Head = &EnumPtr->RE_Lock->RSL_Head;
|
|
InsertTailList (EnumPtr->RE_Head,
|
|
&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
}
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
}
|
|
}
|
|
else {
|
|
// Otherwise, we'll have to go through all of them
|
|
// starting with the first one
|
|
EnumPtr->RE_Lock = &Table->RT_NetNumberHash[0];
|
|
if (EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
EnumPtr->RE_Head = &EnumPtr->RE_Lock->RSL_Head;
|
|
InsertTailList (EnumPtr->RE_Head,
|
|
&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (EnumPtr->RE_Head!=NULL)
|
|
EnumPtr->RE_ProtocolFamily = ProtocolFamily | RTM_CLIENT_HANDLE_TAG;
|
|
else {
|
|
GlobalFree (EnumerationHandle);
|
|
EnumerationHandle = NULL;
|
|
SetLastError (ERROR_NO_ROUTES);
|
|
}
|
|
}
|
|
|
|
ExitTableAPI (Table);
|
|
return EnumerationHandle;
|
|
#undef EnumPtr
|
|
}
|
|
|
|
|
|
// Returns first route that satisfies criteria of the enumeration handle
|
|
// and advances handle's dummy node past the returned route.
|
|
// Routes are not returned in any particular order.
|
|
// Returns
|
|
// NO_ERROR - if next route was found in the table acording
|
|
// to specified criteria
|
|
// ERROR_NO_MORE_ROUTES - when end of the table is reached,
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
DWORD WINAPI
|
|
RtmEnumerateGetNextRoute (
|
|
IN HANDLE EnumerationHandle, // Handle returned by prev call
|
|
OUT PVOID Route // Next route found
|
|
) {
|
|
#define EnumPtr ((PRTM_ENUMERATOR)EnumerationHandle) // To access fields
|
|
// in this routine
|
|
DWORD status;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = EnumPtr->RE_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
status = DoEnumerate (Table, EnumPtr,
|
|
(EnumPtr->RE_EnumerationFlags&RTM_INCLUDE_DISABLED_ROUTES)
|
|
? RTM_ANY_ENABLE_STATE
|
|
: RTM_ENABLED_NODE_FLAG);
|
|
if (status==NO_ERROR) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
EnumPtr->RE_Links[EnumPtr->RE_Link].Flink,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[EnumPtr->RE_Link]
|
|
);
|
|
|
|
|
|
// Copy found route to the client's buffer
|
|
memcpy (Route, &node->RN_Route, Table->RT_RouteSize);
|
|
if (EnumPtr->RE_EnumerationFlags&RTM_ONLY_BEST_ROUTES) {
|
|
// Move past all entries of given network
|
|
// so we don't return more than one best route
|
|
// for same network in case best route gets reassigned
|
|
// while client is processing results of this call
|
|
// (because we enumerate in the direction opposite
|
|
// to the direction of insertion, new node can't
|
|
// be inserted before the enumerator)
|
|
PLIST_ENTRY cur = EnumPtr->RE_Links[EnumPtr->RE_Link].Blink;
|
|
while (cur!=EnumPtr->RE_Head) {
|
|
node = CONTAINING_RECORD (cur, RTM_ROUTE_NODE,
|
|
RN_Links[EnumPtr->RE_Link]);
|
|
|
|
if (!IsEnumerator (node)
|
|
&& (NetNumCmp (Table, Route, &node->RN_Route)!=0))
|
|
break;
|
|
cur = cur->Blink;
|
|
}
|
|
RemoveEntryList (&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
InsertHeadList (cur, &EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
}
|
|
|
|
}
|
|
else if (status==ERROR_NO_MORE_ROUTES) {
|
|
// We are at the end of the list, nothing to return
|
|
;
|
|
}
|
|
else {
|
|
// There was an error (DoEnumerate cleaned up everything itself)
|
|
ExitTableAPI (Table);
|
|
return status;
|
|
}
|
|
|
|
if (EnumPtr->RE_Hash!=NULL) {
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
EnumPtr->RE_Hash = NULL;
|
|
}
|
|
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
ExitTableAPI (Table);
|
|
return status;
|
|
#undef EnumPtr
|
|
}
|
|
|
|
// Frees resources allocated for enumeration handle
|
|
// Returned error codes:
|
|
// NO_ERROR - handle was disposed of ok
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
DWORD WINAPI
|
|
RtmCloseEnumerationHandle (
|
|
IN HANDLE EnumerationHandle
|
|
) {
|
|
#define EnumPtr ((PRTM_ENUMERATOR)EnumerationHandle) // To access fields
|
|
// in this routine
|
|
PLIST_ENTRY head;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = EnumPtr->RE_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
// Just pull out the enumeration node and dispose of it
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
head = EnumPtr->RE_Links[EnumPtr->RE_Link].Flink;
|
|
RemoveEntryList (&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
if (IsListEmpty (head)) {
|
|
if (EnumPtr->RE_Link==RTM_INTERFACE_LIST_LINK) {
|
|
PRTM_INTERFACE_NODE intfNode = CONTAINING_RECORD (head,
|
|
RTM_INTERFACE_NODE,
|
|
IN_Head);
|
|
RemoveEntryList (&intfNode->IN_Link);
|
|
GlobalFree (intfNode);
|
|
}
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
else if (EnumPtr->RE_Link==RTM_PROTOCOL_LIST_LINK) {
|
|
PRTM_PROTOCOL_NODE protNode = CONTAINING_RECORD (head,
|
|
RTM_PROTOCOL_NODE,
|
|
PN_Head);
|
|
RemoveEntryList (&protNode->PN_Link);
|
|
GlobalFree (protNode);
|
|
}
|
|
#endif
|
|
}
|
|
EnumPtr->RE_ProtocolFamily ^= RTM_CLIENT_HANDLE_TAG;
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
GlobalFree (EnumerationHandle);
|
|
ExitTableAPI (Table);
|
|
return NO_ERROR;
|
|
#undef EnumPtr
|
|
}
|
|
|
|
// Delete all routes as specified by enumeraion flags (same meaning as in
|
|
// enumeration calls above, but RTM_ONLY_THIS_PROTOCOL is always set and protocol
|
|
// family and protocol values are taken from Client Handle).
|
|
// Returned error codes:
|
|
// NO_ERROR - handle was disposed of ok
|
|
// ERROR_INVALID_PARAMETER - undefined or unsupported enumeration flag
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
// ERROR_NOT_ENOUGH_MEMORY - not enough memory no perform the operation
|
|
DWORD WINAPI
|
|
RtmBlockDeleteRoutes (
|
|
IN HANDLE ClientHandle, // Protocol family and protocol to
|
|
// which this operation applies
|
|
IN DWORD EnumerationFlags, // limitation flags
|
|
IN PVOID CriteriaRoute // Criteria for limitation flags
|
|
// The following fields shout be set
|
|
// Network of interest if RTM_ONLY_THIS_NETWORK is set
|
|
// Interface of interest if RTM_ONLY_THIS_INTERFACE is set
|
|
) {
|
|
#define ROUTE ((PRTM_XX_ROUTE)CriteriaRoute)
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle) // To access handle fields
|
|
// in this routine
|
|
HANDLE EnumerationHandle;
|
|
#define EnumPtr ((PRTM_ENUMERATOR)EnumerationHandle)
|
|
DWORD status;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (EnumerationFlags & (~(RTM_ONLY_THIS_NETWORK|RTM_ONLY_THIS_INTERFACE))) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
ROUTE->XX_PROTOCOL = ClientPtr->RC_RoutingProtocol;
|
|
EnumerationFlags |= RTM_ONLY_THIS_PROTOCOL;
|
|
EnumerationHandle = RtmCreateEnumerationHandle (
|
|
ProtocolFamily,
|
|
EnumerationFlags,
|
|
CriteriaRoute);
|
|
if (EnumerationHandle==NULL) {
|
|
ExitTableAPI (Table);
|
|
return GetLastError ();
|
|
}
|
|
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
RtmCloseEnumerationHandle (EnumerationHandle);
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
while ((status=DoEnumerate (Table, EnumPtr, RTM_ANY_ENABLE_STATE))==NO_ERROR) {
|
|
PRTM_ROUTE_NODE theNode = CONTAINING_RECORD (
|
|
EnumPtr->RE_Links[EnumPtr->RE_Link].Flink,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[EnumPtr->RE_Link]
|
|
);
|
|
if (EnumPtr->RE_Link!=RTM_NET_NUMBER_HASH_LINK)
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
|
|
if (IsBest(theNode)) {
|
|
// We'll look back and forward to check all nodes
|
|
// around us with same net number trying to find another best
|
|
// node
|
|
DWORD Flags;
|
|
PRTM_ROUTE_NODE curBestNode=NULL;
|
|
PLIST_ENTRY cur = theNode->RN_Links[RTM_NET_NUMBER_HASH_LINK].Blink;
|
|
while (cur!=&theNode->RN_Hash->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
if (!IsEnumerator (node1)
|
|
&& IsEnabled(node1)) {
|
|
if (NetNumCmp (Table, &theNode->RN_Route,
|
|
&node1->RN_Route)==0) {
|
|
if ((curBestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&curBestNode->RN_Route,
|
|
&node1->RN_Route)>0))
|
|
// Looking for the node with lowest
|
|
// metric that can replace disabled
|
|
// node
|
|
curBestNode = node1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
cur = cur->Blink;
|
|
}
|
|
|
|
cur = theNode->RN_Links[RTM_NET_NUMBER_HASH_LINK].Flink;
|
|
while (cur!=&theNode->RN_Hash->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
if (!IsEnumerator (node1)
|
|
&& IsEnabled(node1)) {
|
|
if (NetNumCmp (Table, &theNode->RN_Route,
|
|
&node1->RN_Route)==0) {
|
|
if ((curBestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&curBestNode->RN_Route,
|
|
&node1->RN_Route)>0))
|
|
curBestNode = node1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
if (curBestNode!=NULL) { // There is another best node
|
|
|
|
ResetBest (theNode);
|
|
SetBest (curBestNode);
|
|
|
|
Flags = RTM_CURRENT_BEST_ROUTE | RTM_PREVIOUS_BEST_ROUTE;
|
|
NotifyClients (Table, ClientHandle, Flags,
|
|
&curBestNode->RN_Route,
|
|
&theNode->RN_Route);
|
|
}
|
|
else { // This one was the only available node
|
|
InterlockedDecrement (&Table->RT_NetworkCount);
|
|
Flags = RTM_PREVIOUS_BEST_ROUTE;
|
|
NotifyClients (Table, ClientHandle, Flags, NULL, &theNode->RN_Route);
|
|
}
|
|
}
|
|
|
|
status = RemoveRouteNode (Table, theNode);
|
|
if (status!=NO_ERROR)
|
|
break;
|
|
|
|
if (EnumPtr->RE_Link!=RTM_NET_NUMBER_HASH_LINK) {
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status==ERROR_NO_MORE_ROUTES) {
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
|
|
status = NO_ERROR;
|
|
}
|
|
|
|
RtmCloseEnumerationHandle (EnumerationHandle);
|
|
ExitTableAPI (Table);
|
|
return status;
|
|
#undef EnumPtr
|
|
#undef ClientPtr
|
|
#undef ROUTE
|
|
}
|
|
|
|
// Converts all routes as specified by enumeration flags to routes of
|
|
// static protocol (as defined by ClientHandle)
|
|
// Returned error codes:
|
|
// NO_ERROR - routes were converted ok
|
|
// ERROR_INVALID_PARAMETER - undefined or unsupported enumeration flag
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
// ERROR_NOT_ENOUGH_MEMORY - not enough memory no perform the operation
|
|
DWORD WINAPI
|
|
RtmBlockConvertRoutesToStatic (
|
|
IN HANDLE ClientHandle, // Handle of client that registered
|
|
// to handle static protocol for
|
|
// specified protocol family
|
|
IN DWORD EnumerationFlags, // limitation flags
|
|
IN PVOID CriteriaRoute // Criteria for limitation flags
|
|
// The following fields shout be set
|
|
// Protocol of interest if RTM_ONLY_THIS_PROTOCOL is set
|
|
// Network of interest if RTM_ONLY_THIS_NETWORK is set
|
|
// Interface of interest if RTM_ONLY_THIS_INTERFACE is set
|
|
) {
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle) // To access handle fields
|
|
// in this routine
|
|
HANDLE EnumerationHandle;
|
|
#define EnumPtr ((PRTM_ENUMERATOR)EnumerationHandle)
|
|
DWORD status;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
|
|
EnumerationHandle = RtmCreateEnumerationHandle (
|
|
ProtocolFamily,
|
|
EnumerationFlags,
|
|
CriteriaRoute);
|
|
if (EnumerationHandle==NULL) {
|
|
ExitTableAPI(Table);
|
|
return GetLastError ();
|
|
}
|
|
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
RtmCloseEnumerationHandle (EnumerationHandle);
|
|
ExitTableAPI(Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
while ((status=DoEnumerate (Table, EnumPtr, RTM_ENABLED_NODE_FLAG))==NO_ERROR) {
|
|
PRTM_ROUTE_NODE theNode;
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
EnumPtr->RE_Links[EnumPtr->RE_Link].Flink,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[EnumPtr->RE_Link]
|
|
);
|
|
if (ClientPtr->RC_RoutingProtocol==node->RN_Route.XX_PROTOCOL)
|
|
continue;
|
|
|
|
if (EnumPtr->RE_Link!=RTM_NET_NUMBER_HASH_LINK)
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
|
|
if (ClientPtr->RC_RoutingProtocol>node->RN_Route.XX_PROTOCOL) {
|
|
PLIST_ENTRY cur = node->RN_Links[RTM_NET_NUMBER_HASH_LINK].Flink;
|
|
while (cur!=&node->RN_Hash->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]
|
|
);
|
|
if (!IsEnumerator (node1)) {
|
|
INT res = NetNumCmp (Table, &node->RN_Route, &node1->RN_Route);
|
|
if (res==0) {
|
|
if (ClientPtr->RC_RoutingProtocol
|
|
== node1->RN_Route.XX_PROTOCOL) {
|
|
if (node->RN_Route.XX_INTERFACE
|
|
== node1->RN_Route.XX_INTERFACE) {
|
|
res = NextHopCmp (Table, &node->RN_Route, &node1->RN_Route);
|
|
ASSERTMSG ("RtmBlockConvertRoutesToStatic:"
|
|
" Already have same static route ",
|
|
res != 0);
|
|
if (res <= 0)
|
|
break;
|
|
}
|
|
else if (node->RN_Route.XX_INTERFACE
|
|
< node1->RN_Route.XX_INTERFACE)
|
|
break;
|
|
}
|
|
else if (ClientPtr->RC_RoutingProtocol
|
|
< node1->RN_Route.XX_PROTOCOL)
|
|
break;
|
|
}
|
|
else if (res<0)
|
|
break;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
theNode = CreateRouteNode (Table,
|
|
cur,
|
|
&node->RN_Links[RTM_INTERFACE_LIST_LINK],
|
|
FALSE,
|
|
node->RN_Hash,
|
|
&node->RN_Route);
|
|
}
|
|
else {
|
|
PLIST_ENTRY cur = node->RN_Links[RTM_NET_NUMBER_HASH_LINK].Blink;
|
|
while (cur!=&node->RN_Hash->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]
|
|
);
|
|
if (!IsEnumerator (node1)) {
|
|
INT res = NetNumCmp (Table, &node->RN_Route, &node1->RN_Route);
|
|
if (res==0) {
|
|
if (ClientPtr->RC_RoutingProtocol
|
|
== node1->RN_Route.XX_PROTOCOL) {
|
|
if (node->RN_Route.XX_INTERFACE
|
|
== node1->RN_Route.XX_INTERFACE) {
|
|
res = NextHopCmp (Table, &node->RN_Route, &node1->RN_Route);
|
|
ASSERTMSG ("RtmBlockConvertRoutesToStatic:"
|
|
" Already have same static route ",
|
|
res != 0);
|
|
if (res >= 0)
|
|
break;
|
|
}
|
|
else if (node->RN_Route.XX_INTERFACE
|
|
> node1->RN_Route.XX_INTERFACE)
|
|
break;
|
|
}
|
|
else if (ClientPtr->RC_RoutingProtocol
|
|
> node1->RN_Route.XX_PROTOCOL)
|
|
break;
|
|
}
|
|
else if (res>0)
|
|
break;
|
|
}
|
|
cur = cur->Blink;
|
|
}
|
|
theNode = CreateRouteNode (Table,
|
|
cur->Flink,
|
|
&node->RN_Links[RTM_INTERFACE_LIST_LINK],
|
|
TRUE,
|
|
node->RN_Hash,
|
|
&node->RN_Route);
|
|
}
|
|
|
|
if (theNode==NULL) {
|
|
status = GetLastError ();
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
break;
|
|
}
|
|
|
|
theNode->RN_Route.XX_PROTOCOL = ClientPtr->RC_RoutingProtocol;
|
|
theNode->RN_Flags = node->RN_Flags;
|
|
status = RemoveRouteNode (Table, node);
|
|
if (status!=NO_ERROR)
|
|
break;
|
|
|
|
if (EnumPtr->RE_Link!=RTM_NET_NUMBER_HASH_LINK) {
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status==ERROR_NO_MORE_ROUTES) {
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
|
|
status = NO_ERROR;
|
|
}
|
|
|
|
RtmCloseEnumerationHandle (EnumerationHandle);
|
|
ExitTableAPI (Table);
|
|
return status;
|
|
#undef EnumPtr
|
|
#undef ClientPtr
|
|
}
|
|
|
|
// Disables/reenables all routes as specified by enumeraion flags
|
|
// (same meaning as in enumeration calls above, but RTM_ONLY_THIS_PROTOCOL
|
|
// is always set and protocol family and protocol values are taken from
|
|
// Client Handle).
|
|
|
|
// Disables/reenables all routes as specified by enumeraion flags
|
|
// (same meaning as in enumeration calls above, but RTM_ONLY_THIS_PROTOCOL
|
|
// is always set and protocol family and protocol values are taken from
|
|
// Client Handle). Currently the only flag supported is RTN_ONLY_THIS_INTERFACE
|
|
|
|
// Disabled routes are invisible, but still maintained by the RTM.
|
|
// E.g.: enumeration methods won't notice them;
|
|
// if disabled route was the best, other route will take its
|
|
// place (if there is one) and all clients will be
|
|
// notified of best route change;
|
|
// however: disabled route can still be deleted or updated using
|
|
// RtmDeleteRoute or RtmAddRoute correspondingly;
|
|
// they can also be aged out by the RTM itself.
|
|
// Returned error codes:
|
|
// NO_ERROR - routes were converted ok
|
|
// ERROR_INVALID_PARAMETER - undefined or unsupported enumeration flag
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
// ERROR_NOT_ENOUGH_MEMORY - not enough memory no perform the operation
|
|
DWORD WINAPI
|
|
RtmBlockSetRouteEnable (
|
|
IN HANDLE ClientHandle, // Protocol family and protocol to
|
|
// which this operation applies
|
|
IN DWORD EnumerationFlags, // limitation flags
|
|
IN PVOID CriteriaRoute, // Criteria for limitation flags
|
|
// The following fields shout be set
|
|
// Network of interest if RTM_ONLY_THIS_NETWORK is set
|
|
// Interface of interest if RTM_ONLY_THIS_INTERFACE is set
|
|
IN BOOL Enable // FALSE to disable routes, TRUE to
|
|
// reenable them
|
|
) {
|
|
#define ClientPtr ((PRTM_CLIENT)ClientHandle) // To access handle fields
|
|
// in this routine
|
|
#define ROUTE ((PRTM_XX_ROUTE)CriteriaRoute)
|
|
HANDLE EnumerationHandle;
|
|
#define EnumPtr ((PRTM_ENUMERATOR)EnumerationHandle)
|
|
DWORD status;
|
|
PRTM_TABLE Table;
|
|
DWORD ProtocolFamily;
|
|
DWORD EnableFlag;
|
|
|
|
try {
|
|
ProtocolFamily = ClientPtr->RC_ProtocolFamily ^ RTM_CLIENT_HANDLE_TAG;
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily<RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
&& EnterTableAPI (Table))
|
|
NOTHING;
|
|
else
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
if (EnumerationFlags & (~(RTM_ONLY_THIS_NETWORK|RTM_ONLY_THIS_INTERFACE))) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
ROUTE->XX_PROTOCOL = ClientPtr->RC_RoutingProtocol;
|
|
EnableFlag = Enable ? 0 : RTM_ENABLED_NODE_FLAG;
|
|
EnumerationHandle = RtmCreateEnumerationHandle (
|
|
ProtocolFamily,
|
|
EnumerationFlags|RTM_ONLY_THIS_PROTOCOL,
|
|
CriteriaRoute);
|
|
if (EnumerationHandle==NULL) {
|
|
ExitTableAPI (Table);
|
|
return GetLastError ();
|
|
}
|
|
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
RtmCloseEnumerationHandle (EnumerationHandle);
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
while ((status=DoEnumerate (Table, EnumPtr, EnableFlag))==NO_ERROR) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
EnumPtr->RE_Links[EnumPtr->RE_Link].Flink,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[EnumPtr->RE_Link]
|
|
);
|
|
|
|
// Update node status
|
|
SetEnable (node, Enable);
|
|
// If we enable this node, we'll have to check if it is the
|
|
// best one, if we disable this node and it was the best we'll
|
|
// try to locate another route. In both cases we'll have to
|
|
// locate and check all nodes to the destination
|
|
if (Enable || IsBest(node)) {
|
|
PRTM_ROUTE_NODE bestNode=NULL;
|
|
PLIST_ENTRY cur1;
|
|
|
|
|
|
// We'll look back and forward to check all nodes
|
|
// around us with same net number
|
|
cur1 = node->RN_Links[RTM_NET_NUMBER_HASH_LINK].Blink;
|
|
while (cur1!=&node->RN_Hash->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur1,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
if (!IsEnumerator (node1)
|
|
&& IsEnabled(node1)) {
|
|
if (NetNumCmp (Table, &node->RN_Route,
|
|
&node1->RN_Route)==0) {
|
|
if (Enable && IsBest(node1)) {
|
|
// Looking for current best node
|
|
// that we might have to replace
|
|
bestNode = node1;
|
|
break;
|
|
}
|
|
else if (!Enable
|
|
&& ((bestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&bestNode->RN_Route,
|
|
&node1->RN_Route)>0)))
|
|
// Looking for the node with lowest
|
|
// metric that can replace disabled
|
|
// node
|
|
bestNode = node1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
cur1 = cur1->Blink;
|
|
}
|
|
|
|
// If disabling, we need to check all nodes to find
|
|
// the best one
|
|
// if enabling we continue only if we haven't
|
|
// located the best node yet
|
|
if (!Enable || (bestNode==NULL)) {
|
|
cur1 = node->RN_Links[RTM_NET_NUMBER_HASH_LINK].Flink;
|
|
while (cur1!=&node->RN_Hash->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur1,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
if (!IsEnumerator (node1)
|
|
&& IsEnabled(node1)) {
|
|
// Looking for current best node
|
|
// that we might have to replace
|
|
if (NetNumCmp (Table, &node->RN_Route,
|
|
&node1->RN_Route)==0) {
|
|
if (Enable && IsBest(node1)) {
|
|
bestNode = node1;
|
|
break;
|
|
}
|
|
else if (!Enable
|
|
&& ((bestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&bestNode->RN_Route,
|
|
&node1->RN_Route)>0)))
|
|
// Looking for the node with lowest
|
|
// metric that can replace disabled
|
|
// node
|
|
bestNode = node1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
cur1 = cur1->Flink;
|
|
}
|
|
}
|
|
|
|
if (!Enable // Disabling: we already know that we're removing
|
|
// the best node (see above), so we'll have
|
|
// to notify clients whether or not we found the
|
|
// replacement
|
|
// Enabling: we'll have to notify only if there
|
|
// is no best route yet or if the route we're
|
|
// enabling is better then current best route
|
|
|| (bestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&node->RN_Route,
|
|
&bestNode->RN_Route)<0)) {
|
|
|
|
if (bestNode!=NULL) {
|
|
// There is another route that loses or gains
|
|
// best status as the result of our operation
|
|
if (Enable) {
|
|
ResetBest (bestNode);
|
|
SetBest (node);
|
|
// Enabling: node replaces bestNode
|
|
NotifyClients (Table,
|
|
NULL,
|
|
RTM_CURRENT_BEST_ROUTE|RTM_PREVIOUS_BEST_ROUTE,
|
|
&node->RN_Route,
|
|
&bestNode->RN_Route);
|
|
}
|
|
else {
|
|
ResetBest (node);
|
|
SetBest (bestNode);
|
|
// Disabling: bestNode replaces node
|
|
NotifyClients (Table,
|
|
NULL,
|
|
RTM_CURRENT_BEST_ROUTE|RTM_PREVIOUS_BEST_ROUTE,
|
|
&bestNode->RN_Route,
|
|
&node->RN_Route);
|
|
}
|
|
}
|
|
else /* if (bestNode==NULL) */ {
|
|
// No other node
|
|
if (Enable) {
|
|
SetBest (node);
|
|
// Enabling: our node becomes the best
|
|
NotifyClients (Table,
|
|
NULL,
|
|
RTM_CURRENT_BEST_ROUTE,
|
|
&node->RN_Route,
|
|
NULL);
|
|
}
|
|
else {
|
|
ResetBest (node);
|
|
// Disabling: we removed the only available
|
|
// route
|
|
NotifyClients (Table,
|
|
NULL,
|
|
RTM_PREVIOUS_BEST_ROUTE,
|
|
NULL,
|
|
&node->RN_Route);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status==ERROR_NO_MORE_ROUTES) {
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
|
|
status = NO_ERROR;
|
|
}
|
|
|
|
RtmCloseEnumerationHandle (EnumerationHandle);
|
|
ExitTableAPI (Table);
|
|
return status;
|
|
|
|
#undef EnumPtr
|
|
#undef ClientPtr
|
|
#undef ROUTE
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Slow enumeration that may require traversing up to all the entries in the
|
|
// table if route used to compute the next entry no longer exists.
|
|
// Routes are returned in the increasing net number order
|
|
|
|
// Get first route that matches specified criteria
|
|
// Returns:
|
|
// NO_ERROR - if matching route is found
|
|
// ERROR_NO_ROUTES - if no routes available with specified criteria
|
|
// ERROR_INVALID_PARAMETER - if one of the parameters is invalid
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
DWORD WINAPI
|
|
RtmGetFirstRoute (
|
|
IN DWORD ProtocolFamily,
|
|
IN DWORD EnumerationFlags,// Limiting flags
|
|
IN OUT PVOID Route // On Entry: if any of the EnumerationFlags are set,
|
|
// the corresponding fields of Route will
|
|
// be used to limit the search
|
|
// to the only table entries that have
|
|
// same value in the specified field.
|
|
// On Exit: contains first route in the table that
|
|
// matches specified criteria
|
|
){
|
|
#define ROUTE ((PRTM_XX_ROUTE)Route)
|
|
PRTM_TABLE Table;
|
|
PLIST_ENTRY cur, head;
|
|
INT res, link;
|
|
PRTM_SYNC_LIST hashBasket;
|
|
DWORD status = ERROR_NO_ROUTES;
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
|| !EnterTableAPI (Table)) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (EnumerationFlags &
|
|
(~(RTM_ONLY_THIS_NETWORK|RTM_ONLY_THIS_INTERFACE
|
|
|RTM_ONLY_THIS_PROTOCOL|RTM_ONLY_BEST_ROUTES
|
|
|RTM_INCLUDE_DISABLED_ROUTES))) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) {
|
|
hashBasket = &Table->RT_NetNumberHash [HashFunction (Table,
|
|
((char *)ROUTE)
|
|
+sizeof(RTM_XX_ROUTE))];
|
|
link = RTM_NET_NUMBER_HASH_LINK;
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
head = &hashBasket->RSL_Head;
|
|
}
|
|
else {
|
|
hashBasket = NULL;
|
|
link = RTM_NET_NUMBER_LIST_LINK;
|
|
head = &Table->RT_NetNumberMasterList.RSL_Head;
|
|
|
|
|
|
if (EnterSyncList (Table, &Table->RT_NetNumberMasterList, FALSE))
|
|
ConsolidateNetNumberLists (Table);
|
|
else if (!EnterSyncList (Table, &Table->RT_NetNumberMasterList, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
}
|
|
// Go through the list till entry that matches specified
|
|
// criteria is found
|
|
cur = head->Flink;
|
|
while (cur!=head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[link]);
|
|
if (!IsEnumerator (node)
|
|
&& ((EnumerationFlags&RTM_INCLUDE_DISABLED_ROUTES)
|
|
|| IsEnabled(node))) {
|
|
if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) {
|
|
// Check network number if asked
|
|
res = NetNumCmp (Table, ROUTE, &node->RN_Route);
|
|
if (res > 0) // It may be further ahead
|
|
goto DoNextNode;
|
|
else if (res < 0) // No chance to find it anymore
|
|
break;
|
|
}
|
|
|
|
// Check if it is the best route if asked
|
|
if (EnumerationFlags & RTM_ONLY_BEST_ROUTES) {
|
|
// We need to lock the hash list to make sure the
|
|
// best node designation won't change while we are
|
|
// scaning through the list
|
|
if (hashBasket!=node->RN_Hash) {
|
|
if (hashBasket!=NULL)
|
|
LeaveSyncList (Table, hashBasket);
|
|
hashBasket = node->RN_Hash;
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
hashBasket = NULL;
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitGetFirst;
|
|
}
|
|
}
|
|
|
|
if (!IsBest(node))
|
|
goto DoNextNode;
|
|
}
|
|
|
|
// Check protocol if asked
|
|
if ((EnumerationFlags & RTM_ONLY_THIS_PROTOCOL)
|
|
&& (ROUTE->XX_PROTOCOL
|
|
!=node->RN_Route.XX_PROTOCOL))
|
|
goto DoNextNode;
|
|
|
|
// Check interface if asked
|
|
if ((EnumerationFlags & RTM_ONLY_THIS_INTERFACE)
|
|
&& (ROUTE->XX_INTERFACE
|
|
!=node->RN_Route.XX_INTERFACE))
|
|
goto DoNextNode;
|
|
|
|
// Now we have it
|
|
memcpy (ROUTE, &node->RN_Route, Table->RT_RouteSize);
|
|
|
|
status = NO_ERROR;
|
|
break;
|
|
}
|
|
|
|
DoNextNode: // Continue searching
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
ExitGetFirst:
|
|
if (link==RTM_NET_NUMBER_HASH_LINK)
|
|
LeaveSyncList (Table, hashBasket);
|
|
else {
|
|
if (hashBasket!=NULL)
|
|
LeaveSyncList (Table, hashBasket);
|
|
LeaveSyncList (Table, &Table->RT_NetNumberMasterList);
|
|
}
|
|
ExitTableAPI (Table);
|
|
#undef ROUTE
|
|
return status;
|
|
}
|
|
|
|
// Compute and return route next to the input route limiting serach to the routes
|
|
// with specified criteria
|
|
// Returns:
|
|
// NO_ERROR - if matching route is found
|
|
// ERROR_NO_MORE_ROUTES - if no matching route was found while end of
|
|
// the table is reached and no route
|
|
// ERROR_INVALID_PARAMETER - if one of the parameters is invalid
|
|
// ERROR_NO_SYSTEM_RESOURCES - not enough resources to lock table content
|
|
DWORD WINAPI
|
|
RtmGetNextRoute (
|
|
IN DWORD ProtocolFamily,
|
|
IN DWORD EnumerationFlags,// Limiting flags
|
|
IN OUT PVOID Route // On Entry: contains the route from which to start
|
|
// the search.
|
|
// if any of the EnumerationFlags are set,
|
|
// the corresponding fields of Route will
|
|
// be used to limit the search
|
|
// to the only table entries that have
|
|
// same value in the specified field.
|
|
// On Exit: contains first route in the table that
|
|
// matches specified criteria
|
|
) {
|
|
#define ROUTE ((PRTM_XX_ROUTE)Route)
|
|
PRTM_TABLE Table;
|
|
PLIST_ENTRY cur, posLink = NULL;
|
|
INT res;
|
|
PRTM_SYNC_LIST hashBasket = NULL;
|
|
DWORD status = ERROR_NO_MORE_ROUTES;
|
|
|
|
Table = &Tables[ProtocolFamily];
|
|
if ((ProtocolFamily>=RTM_NUM_OF_PROTOCOL_FAMILIES)
|
|
|| !EnterTableAPI (Table)) {
|
|
#if DBG
|
|
Trace2 (ANY,
|
|
"Undefined Protocol Family\n\tat line %ld of %s\n",
|
|
__LINE__, __FILE__);
|
|
#endif
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (EnumerationFlags &
|
|
(~(RTM_ONLY_THIS_NETWORK|RTM_ONLY_THIS_INTERFACE
|
|
|RTM_ONLY_THIS_PROTOCOL|RTM_ONLY_BEST_ROUTES
|
|
|RTM_INCLUDE_DISABLED_ROUTES))) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (EnterSyncList (Table, &Table->RT_NetNumberMasterList, FALSE))
|
|
ConsolidateNetNumberLists (Table);
|
|
else if (!EnterSyncList (Table, &Table->RT_NetNumberMasterList, TRUE)) {
|
|
ExitTableAPI (Table);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
|
|
// First try to locate starting point for the serach
|
|
// using the hash table (should work most of the
|
|
// time unless route was deleted while client was
|
|
// processing it)
|
|
hashBasket = &Table->RT_NetNumberHash [HashFunction (Table,
|
|
((char *)ROUTE)
|
|
+sizeof(RTM_XX_ROUTE))];
|
|
|
|
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitGetNext;
|
|
}
|
|
|
|
|
|
cur = hashBasket->RSL_Head.Flink;
|
|
while (cur!=&hashBasket->RSL_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]
|
|
);
|
|
if (!IsEnumerator (node)
|
|
&& ((EnumerationFlags&RTM_INCLUDE_DISABLED_ROUTES)
|
|
|| IsEnabled(node))) {
|
|
// First check network number
|
|
// (lists are ordered by net number)
|
|
res = NetNumCmp (Table, ROUTE, &node->RN_Route);
|
|
if (res==0) {
|
|
if (ROUTE->XX_PROTOCOL
|
|
== node->RN_Route.XX_PROTOCOL) {
|
|
if (ROUTE->XX_INTERFACE
|
|
== node->RN_Route.XX_INTERFACE) {
|
|
res = NextHopCmp (Table, ROUTE, &node->RN_Route);
|
|
if ((res == 0)
|
|
&& IsSorted (node))
|
|
posLink = node->RN_Links[RTM_NET_NUMBER_LIST_LINK].Flink;
|
|
else if (res < 0)
|
|
break;
|
|
}
|
|
else if (ROUTE->XX_INTERFACE
|
|
< node->RN_Route.XX_INTERFACE)
|
|
break;
|
|
}
|
|
else if (ROUTE->XX_PROTOCOL
|
|
< node->RN_Route.XX_PROTOCOL)
|
|
break;
|
|
}
|
|
else if (res < 0)
|
|
break;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
LeaveSyncList (Table, hashBasket);
|
|
|
|
hashBasket = NULL;
|
|
|
|
if (posLink!=NULL)
|
|
cur = posLink; // Note the place to start with
|
|
else { // If we didn't find the entry in
|
|
// hash table, we'll have to go through
|
|
// the master net number list from the
|
|
// beginning
|
|
cur = Table->RT_NetNumberMasterList.RSL_Head.Flink;
|
|
while (cur!=&Table->RT_NetNumberMasterList.RSL_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_LIST_LINK]
|
|
);
|
|
if (!IsEnumerator (node)
|
|
&& ((EnumerationFlags&RTM_INCLUDE_DISABLED_ROUTES)
|
|
|| IsEnabled(node))) {
|
|
// Just do all the necessary comparisons to
|
|
// find the following entry
|
|
res = NetNumCmp (Table, ROUTE, &node->RN_Route);
|
|
if ((res < 0)
|
|
||((res == 0)
|
|
&&((ROUTE->XX_PROTOCOL
|
|
< node->RN_Route.XX_PROTOCOL)
|
|
||((ROUTE->XX_PROTOCOL
|
|
==node->RN_Route.XX_PROTOCOL)
|
|
&&((ROUTE->XX_INTERFACE
|
|
< node->RN_Route.XX_INTERFACE)
|
|
||((ROUTE->XX_INTERFACE
|
|
==node->RN_Route.XX_INTERFACE)
|
|
&& (NextHopCmp (Table, ROUTE,
|
|
&node->RN_Route)
|
|
< 0)))))))
|
|
break;
|
|
}
|
|
|
|
cur = cur->Flink;
|
|
}
|
|
}
|
|
|
|
// Now we need to locate first entry that satisfies all criteria
|
|
while (cur!=&Table->RT_NetNumberMasterList.RSL_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_LIST_LINK]
|
|
);
|
|
if (!IsEnumerator (node)
|
|
&& ((EnumerationFlags&RTM_INCLUDE_DISABLED_ROUTES)
|
|
|| IsEnabled(node))) {
|
|
|
|
if (EnumerationFlags & RTM_ONLY_BEST_ROUTES) {
|
|
// We need to lock the hash list to make sure the
|
|
// best node designation won't change while we are
|
|
// scaning through the list
|
|
if (hashBasket!=node->RN_Hash) {
|
|
if (hashBasket!=NULL)
|
|
LeaveSyncList (Table, hashBasket);
|
|
hashBasket = node->RN_Hash;
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
status = ERROR_NO_SYSTEM_RESOURCES;
|
|
goto ExitGetNext;
|
|
}
|
|
}
|
|
|
|
// For best routes we must check if the route is best
|
|
// and also make sure we do not return same net as in
|
|
// previous call in case the best route was moved
|
|
// while client was processing results of the
|
|
// previous call
|
|
if (!IsBest(node)
|
|
|| (NetNumCmp (Table, ROUTE, &node->RN_Route)==0))
|
|
goto DoNextNode;
|
|
}
|
|
|
|
if (EnumerationFlags & RTM_ONLY_THIS_NETWORK) {
|
|
// checking net number
|
|
res = NetNumCmp (Table, ROUTE, &node->RN_Route);
|
|
if (res > 0) // It is still ahead
|
|
goto DoNextNode;
|
|
else if (res < 0) // no chance to find it
|
|
break;
|
|
// else (res == 0), found it, continue
|
|
}
|
|
|
|
// Check interface if asked
|
|
if ((EnumerationFlags & RTM_ONLY_THIS_INTERFACE)
|
|
&& (node->RN_Route.XX_INTERFACE
|
|
!=ROUTE->XX_INTERFACE))
|
|
goto DoNextNode;
|
|
|
|
// Check protocol if asked
|
|
if ((EnumerationFlags & RTM_ONLY_THIS_PROTOCOL)
|
|
&& (node->RN_Route.XX_PROTOCOL
|
|
!=ROUTE->XX_PROTOCOL))
|
|
goto DoNextNode;
|
|
|
|
|
|
// Now we can return it
|
|
// Make sure nobody changes the route while we copy
|
|
memcpy (ROUTE, &node->RN_Route, Table->RT_RouteSize);
|
|
|
|
status = NO_ERROR;
|
|
break;
|
|
}
|
|
|
|
DoNextNode:
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
if (hashBasket!=NULL)
|
|
LeaveSyncList (Table, hashBasket);
|
|
|
|
ExitGetNext:
|
|
LeaveSyncList (Table, &Table->RT_NetNumberMasterList);
|
|
ExitTableAPI (Table);
|
|
#undef ROUTE
|
|
return status;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// RtmLookupIPDestination
|
|
//
|
|
// Given a destination address does a route lookup to get the best route
|
|
// to that destination.
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL WINAPI
|
|
RtmLookupIPDestination(
|
|
DWORD dwDestAddr,
|
|
PRTM_IP_ROUTE prir
|
|
)
|
|
{
|
|
INT nInd;
|
|
IP_NETWORK ipNet;
|
|
|
|
for ( nInd = MAX_MASKS; nInd >= 0; nInd-- )
|
|
{
|
|
if ( g_meMaskTable[ nInd ].dwCount == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
ipNet.N_NetNumber = dwDestAddr & g_meMaskTable[ nInd ].dwMask;
|
|
ipNet.N_NetMask = g_meMaskTable[ nInd ].dwMask;
|
|
|
|
if ( RtmIsRoute( RTM_PROTOCOL_FAMILY_IP, &ipNet, prir ) )
|
|
{
|
|
if ( IsRouteLoopback( prir ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
UpdateAPC (
|
|
PVOID Context,
|
|
ULONG TimeLow,
|
|
LONG TimeHigh
|
|
) {
|
|
#define Table ((PRTM_TABLE)Context)
|
|
if (InterlockedIncrement (&Table->RT_UpdateWorkerPending)==0) {
|
|
DWORD status = RtlQueueWorkItem (ConsolidateNetNumberListsWI, Context, 0);
|
|
if (status!=STATUS_SUCCESS) {
|
|
ASSERTERRMSG ("Can't queue update work item", FALSE);
|
|
ScheduleUpdate (Context);
|
|
}
|
|
}
|
|
#undef Table
|
|
}
|
|
|
|
VOID APIENTRY
|
|
ScheduleUpdate (
|
|
PVOID Context
|
|
) {
|
|
#define Table ((PRTM_TABLE)Context)
|
|
DWORD status;
|
|
static LARGE_INTEGER dueTime = RTM_NET_NUMBER_UPDATE_PERIOD;
|
|
|
|
if (InterlockedDecrement (&Table->RT_UpdateWorkerPending)>=0) {
|
|
status = RtlQueueWorkItem (ConsolidateNetNumberListsWI, Context, 0);
|
|
if (status==STATUS_SUCCESS)
|
|
return;
|
|
ASSERTERRMSG ("Can't queue update work item", FALSE);
|
|
InterlockedExchange (&Table->RT_UpdateWorkerPending, -1);
|
|
}
|
|
|
|
status = NtSetTimer (Table->RT_UpdateTimer,
|
|
&dueTime,
|
|
UpdateAPC,
|
|
Context,
|
|
FALSE,
|
|
0,
|
|
NULL);
|
|
ASSERTMSG ("Could not set expiration timer ", NT_SUCCESS (status));
|
|
#undef Table
|
|
}
|
|
|
|
VOID
|
|
ConsolidateNetNumberListsWI (
|
|
PVOID Context
|
|
) {
|
|
#define Table ((PRTM_TABLE)Context)
|
|
DWORD status;
|
|
|
|
if (EnterSyncList (Table, &Table->RT_NetNumberMasterList, TRUE)) {
|
|
InterlockedExchange (&Table->RT_UpdateWorkerPending, 0);
|
|
ConsolidateNetNumberLists (Table);
|
|
LeaveSyncList (Table, &Table->RT_NetNumberMasterList);
|
|
}
|
|
|
|
status = RtlQueueWorkItem (ScheduleUpdate, Context, WT_EXECUTEINIOTHREAD);
|
|
ASSERTERRMSG ("Can't queue update work item", status==STATUS_SUCCESS);
|
|
#undef Table
|
|
}
|
|
|
|
// This procedure merges temporary and master net number lists
|
|
// It also removes and disposes of nodes in the deleted list
|
|
VOID
|
|
ConsolidateNetNumberLists (
|
|
PRTM_TABLE Table // Table for which operation is performed
|
|
) {
|
|
PLIST_ENTRY curMaster, curTemp;
|
|
LIST_ENTRY tempHead;
|
|
PRTM_ROUTE_NODE tempNode;
|
|
INT res;
|
|
DWORD status;
|
|
#if DBG
|
|
INT curMasterIdx = 0;
|
|
#endif
|
|
|
|
// Temp and deleted lists are locked for a very short period
|
|
// of time so that overall performance should not
|
|
// degrade
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_NetNumberTempList, TRUE)) {
|
|
return;
|
|
}
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_DeletedList, TRUE)) {
|
|
LeaveSyncList (Table, &Table->RT_NetNumberTempList);
|
|
return;
|
|
}
|
|
|
|
// Process entries in deleted list
|
|
while (!IsListEmpty (&Table->RT_DeletedList.RSL_Head)) {
|
|
curTemp = RemoveHeadList (&Table->RT_DeletedList.RSL_Head);
|
|
tempNode = CONTAINING_RECORD (curTemp,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_DELETED_LIST_LINK]);
|
|
RemoveEntryList (&tempNode->RN_Links[RTM_NET_NUMBER_LIST_LINK]);
|
|
#if DBG
|
|
IF_DEBUG (DISPLAY_TABLE)
|
|
DeleteRouteFromLB (Table, tempNode);
|
|
#endif
|
|
HeapFree (Table->RT_Heap, 0, tempNode);
|
|
}
|
|
// Unlock the list
|
|
Table->RT_DeletedNodesCount = 0;
|
|
LeaveSyncList (Table, &Table->RT_DeletedList);
|
|
|
|
// Now, just copy the head of the temp list,
|
|
// so we won't delay others while processing it
|
|
if (!IsListEmpty (&Table->RT_NetNumberTempList.RSL_Head)) {
|
|
curTemp = Table->RT_NetNumberTempList.RSL_Head.Flink;
|
|
RemoveEntryList (&Table->RT_NetNumberTempList.RSL_Head);
|
|
InitializeListHead (&Table->RT_NetNumberTempList.RSL_Head);
|
|
InsertTailList (curTemp, &tempHead);
|
|
}
|
|
else
|
|
InitializeListHead (&tempHead);
|
|
|
|
Table->RT_NetNumberTempCount = 0;
|
|
LeaveSyncList (Table, &Table->RT_NetNumberTempList);
|
|
|
|
|
|
curMaster = Table->RT_NetNumberMasterList.RSL_Head.Flink;
|
|
|
|
// Merge master and temp lists (both are ordered by
|
|
// net number.interface.protocol.next hop address)
|
|
while (!IsListEmpty (&tempHead)) {
|
|
// Take the first entry
|
|
curTemp = RemoveHeadList (&tempHead);
|
|
tempNode = CONTAINING_RECORD (curTemp,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_LIST_LINK]);
|
|
|
|
// Find master list entry that should follow it
|
|
while (curMaster!=&Table->RT_NetNumberMasterList.RSL_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (curMaster,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_LIST_LINK]);
|
|
if (!IsEnumerator (node)) {
|
|
res = NetNumCmp (Table, &tempNode->RN_Route, &node->RN_Route);
|
|
if ((res < 0)
|
|
||((res == 0)
|
|
&&((tempNode->RN_Route.XX_PROTOCOL
|
|
< node->RN_Route.XX_PROTOCOL)
|
|
||((tempNode->RN_Route.XX_PROTOCOL
|
|
==node->RN_Route.XX_PROTOCOL)
|
|
&&((tempNode->RN_Route.XX_INTERFACE
|
|
< node->RN_Route.XX_INTERFACE)
|
|
||((tempNode->RN_Route.XX_INTERFACE
|
|
==node->RN_Route.XX_INTERFACE)
|
|
&& (NextHopCmp (Table, &tempNode->RN_Route,
|
|
&node->RN_Route)
|
|
< 0)))))))
|
|
break;
|
|
}
|
|
curMaster = curMaster->Flink;
|
|
#if DBG
|
|
IF_DEBUG (DISPLAY_TABLE)
|
|
curMasterIdx += 1;
|
|
#endif
|
|
}
|
|
// Insert at the located point
|
|
InsertTailList (curMaster, curTemp);
|
|
SetSorted (tempNode);
|
|
#if DBG
|
|
IF_DEBUG (DISPLAY_TABLE) {
|
|
AddRouteToLB (Table, tempNode, curMasterIdx);
|
|
curMasterIdx += 1;
|
|
}
|
|
#endif
|
|
}
|
|
// We are done now
|
|
}
|
|
|
|
VOID
|
|
ExpirationAPC (
|
|
PVOID Context,
|
|
ULONG TimeLow,
|
|
LONG TimeHigh
|
|
) {
|
|
#define Table ((PRTM_TABLE)Context)
|
|
if (InterlockedIncrement (&Table->RT_ExpirationWorkerPending)==0) {
|
|
do {
|
|
ProcessExpirationQueue (Table);
|
|
}
|
|
while (InterlockedDecrement (&Table->RT_ExpirationWorkerPending)>=0);
|
|
}
|
|
#undef Table
|
|
}
|
|
|
|
VOID APIENTRY
|
|
ProcessExpirationQueueWI (
|
|
PVOID Context
|
|
) {
|
|
#define Table ((PRTM_TABLE)Context)
|
|
do {
|
|
ProcessExpirationQueue (Table);
|
|
}
|
|
while (InterlockedDecrement (&Table->RT_ExpirationWorkerPending)>=0);
|
|
#undef Table
|
|
}
|
|
|
|
// Checks if any entries in expiration queue have expired and deletes them
|
|
VOID
|
|
ProcessExpirationQueue (
|
|
PRTM_TABLE Table // Affected table
|
|
) {
|
|
DWORD status;
|
|
ULONG tickCount = GetTickCount ();
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_ExpirationQueue, TRUE))
|
|
return;
|
|
|
|
// Check all relevant entries
|
|
while (!IsListEmpty (&Table->RT_ExpirationQueue.RSL_Head)) {
|
|
PRTM_SYNC_LIST hashBasket;
|
|
PLIST_ENTRY cur;
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
|
Table->RT_ExpirationQueue.RSL_Head.Flink,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_EXPIRATION_QUEUE_LINK]);
|
|
LONGLONG dueTime;
|
|
ULONG timeDiff = TimeDiff (node->RN_ExpirationTime,tickCount);
|
|
|
|
InterlockedExchange (&Table->RT_ExpirationWorkerPending, 0);
|
|
|
|
if (IsPositiveTimeDiff (timeDiff)) {
|
|
// The first entry in the queue is not due yet, so are
|
|
// the others (queue is ordered by expiration time)
|
|
|
|
dueTime = (LONGLONG)timeDiff*(-10000);
|
|
status = NtSetTimer (Table->RT_ExpirationTimer,
|
|
(PLARGE_INTEGER)&dueTime,
|
|
ExpirationAPC,
|
|
Table,
|
|
FALSE,
|
|
0,
|
|
NULL);
|
|
ASSERTMSG ("Could not set expiration timer ", NT_SUCCESS (status));
|
|
break;
|
|
}
|
|
|
|
|
|
hashBasket = node->RN_Hash;
|
|
// We need to lock the hash basket to delete the entry
|
|
if (!EnterSyncList (Table, hashBasket, FALSE)) {
|
|
// Can't do it at once, so we first release
|
|
// expiration queue lock (to prevent a deadlock)
|
|
// and then try again)
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
if (!EnterSyncList (Table, hashBasket, TRUE)) {
|
|
return;
|
|
}
|
|
|
|
if (!EnterSyncList (Table, &Table->RT_ExpirationQueue, TRUE)) {
|
|
LeaveSyncList (Table, hashBasket);
|
|
return;
|
|
}
|
|
// Now we have both of them, but is our route still there
|
|
if (node!=CONTAINING_RECORD (
|
|
Table->RT_ExpirationQueue.RSL_Head.Flink,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_EXPIRATION_QUEUE_LINK])) {
|
|
// Well, somebody took care of it while we were
|
|
// waiting
|
|
LeaveSyncList (Table, hashBasket);
|
|
// We'll try the next one
|
|
continue;
|
|
}
|
|
// Unlikely, but its due time could have changed
|
|
timeDiff = TimeDiff (node->RN_ExpirationTime,tickCount);
|
|
if (IsPositiveTimeDiff (timeDiff) ) {
|
|
// The first entry in the queue is not due yet, so are
|
|
// the others (queue is ordered by expiration time)
|
|
LeaveSyncList (Table, hashBasket);
|
|
dueTime = (LONGLONG)timeDiff*(-10000);
|
|
// Well, we are done then (this was the first entry
|
|
// in the queue (we just checked), so other are not
|
|
// due as well)
|
|
// Just make sure that updated thread returns soon enough
|
|
// to take care of our first entry
|
|
status = NtSetTimer (Table->RT_ExpirationTimer,
|
|
(PLARGE_INTEGER)&dueTime,
|
|
ExpirationAPC,
|
|
Table,
|
|
FALSE,
|
|
0,
|
|
NULL);
|
|
ASSERTMSG ("Could not set expiration timer ", NT_SUCCESS (status));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
|
|
if (IsBest(node)) {
|
|
// We need to locate the best node after this one is gone
|
|
PRTM_ROUTE_NODE bestNode = NULL;
|
|
|
|
cur = node->RN_Links[RTM_NET_NUMBER_HASH_LINK].Blink;
|
|
while (cur!=&hashBasket->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
if (!IsEnumerator (node1)
|
|
&& IsEnabled(node1)) {
|
|
if (NetNumCmp (Table, &node->RN_Route, &node1->RN_Route)==0) {
|
|
if ((bestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&bestNode->RN_Route,
|
|
&node1->RN_Route)>0))
|
|
bestNode = node1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
cur = cur->Blink;
|
|
}
|
|
|
|
cur = node->RN_Links[RTM_NET_NUMBER_HASH_LINK].Flink;
|
|
while (cur!=&hashBasket->RSL_Head) {
|
|
PRTM_ROUTE_NODE node1 = CONTAINING_RECORD (
|
|
cur,
|
|
RTM_ROUTE_NODE,
|
|
RN_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
if (!IsEnumerator (node1)
|
|
&& IsEnabled(node1)) {
|
|
if (NetNumCmp (Table, &node->RN_Route, &node1->RN_Route)==0) {
|
|
if ((bestNode==NULL)
|
|
|| (MetricCmp (Table,
|
|
&bestNode->RN_Route,
|
|
&node1->RN_Route)>0))
|
|
bestNode = node1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
cur = cur->Flink;
|
|
}
|
|
|
|
if (bestNode!=NULL) { // We did find another node
|
|
|
|
ResetBest (node);
|
|
SetBest (bestNode);
|
|
|
|
NotifyClients (Table,
|
|
NULL,
|
|
RTM_CURRENT_BEST_ROUTE|RTM_PREVIOUS_BEST_ROUTE,
|
|
&bestNode->RN_Route,
|
|
&node->RN_Route);
|
|
}
|
|
else {
|
|
InterlockedDecrement (&Table->RT_NetworkCount);
|
|
// No best node anymore
|
|
NotifyClients (Table,
|
|
NULL,
|
|
RTM_PREVIOUS_BEST_ROUTE,
|
|
NULL,
|
|
&node->RN_Route);
|
|
}
|
|
}
|
|
|
|
|
|
if (RemoveRouteNode (Table, node)!=NO_ERROR) {
|
|
LeaveSyncList (Table, hashBasket);
|
|
return;
|
|
}
|
|
|
|
LeaveSyncList (Table, hashBasket);
|
|
// Reenter expiration queue to continue
|
|
if (!EnterSyncList (Table, &Table->RT_ExpirationQueue, TRUE))
|
|
return;
|
|
}
|
|
|
|
LeaveSyncList (Table, &Table->RT_ExpirationQueue);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
DoEnumerate (
|
|
PRTM_TABLE Table,
|
|
PRTM_ENUMERATOR EnumPtr,
|
|
DWORD EnableFlag
|
|
) {
|
|
// Now, we'll go ahead and find an entry that satisfies
|
|
// specified criteria
|
|
while (1) { // This external loop is needed for the case
|
|
// of enumerating through the hash table when
|
|
// reaching the end of the list doesn't mean that process has
|
|
// to be stopped: we need to move the next basket till
|
|
// we've gone through all of them
|
|
|
|
PLIST_ENTRY cur = EnumPtr->RE_Links[EnumPtr->RE_Link].Blink;
|
|
while (cur!=EnumPtr->RE_Head) {
|
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (cur, RTM_ROUTE_NODE,
|
|
RN_Links[EnumPtr->RE_Link]);
|
|
INT res;
|
|
|
|
if (!IsEnumerator (node)
|
|
&& ((EnableFlag==RTM_ANY_ENABLE_STATE)
|
|
|| IsSameEnableState(node,EnableFlag))) {
|
|
|
|
|
|
if ((EnumPtr->RE_Link!=RTM_NET_NUMBER_HASH_LINK)
|
|
&& (EnumPtr->RE_Hash!=node->RN_Hash)) {
|
|
if (EnumPtr->RE_Hash!=NULL)
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
EnumPtr->RE_Hash = node->RN_Hash;
|
|
if (!EnterSyncList (Table, node->RN_Hash, FALSE)) {
|
|
RemoveEntryList (&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
InsertHeadList (cur, &EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Hash, TRUE)) {
|
|
EnumPtr->RE_Hash = NULL;
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
LeaveSyncList (Table, EnumPtr->RE_Hash);
|
|
EnumPtr->RE_Hash = NULL;
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
cur = EnumPtr->RE_Links[EnumPtr->RE_Link].Blink;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (EnumPtr->RE_Link) {
|
|
// Using the interface link:
|
|
case RTM_INTERFACE_LIST_LINK:
|
|
#if !RTM_USE_PROTOCOL_LISTS
|
|
case RTM_NET_NUMBER_HASH_LINK:
|
|
#endif
|
|
// Check protocol if necessary
|
|
if ((EnumPtr->RE_EnumerationFlags & RTM_ONLY_THIS_PROTOCOL)
|
|
&& (EnumPtr->RE_Route.XX_PROTOCOL
|
|
!=node->RN_Route.XX_PROTOCOL)) {
|
|
// Break out to move ahead if protocol
|
|
// check fails
|
|
break;
|
|
}
|
|
// else Pass through to do other checks
|
|
|
|
// Using the protocol link: (thus we don't
|
|
// care about interface or we would have used
|
|
// interface link - see RtmCreateEnumerationHandle).
|
|
#if RTM_USE_PROTOCOL_LISTS
|
|
case RTM_PROTOCOL_LIST_LINK:
|
|
// Using the hash link: (thus we don't
|
|
// care about interface and protocol or we would have
|
|
// used other links - see RtmCreateEnumerationHandle).
|
|
case RTM_NET_NUMBER_HASH_LINK:
|
|
#endif
|
|
// Check the network number if necessary
|
|
if (EnumPtr->RE_EnumerationFlags & RTM_ONLY_THIS_NETWORK) {
|
|
res = NetNumCmp (Table, &EnumPtr->RE_Route,
|
|
&node->RN_Route);
|
|
if (res == 0)
|
|
// Match, continue checks
|
|
;
|
|
else if ((res > 0)
|
|
&& (EnumPtr->RE_Link
|
|
==RTM_NET_NUMBER_HASH_LINK)) {
|
|
// Hash list are ordered by net
|
|
// number, so if we got network
|
|
// number that is less than ours
|
|
// we don't have search anymore
|
|
// (we are going backwards)
|
|
return ERROR_NO_MORE_ROUTES;
|
|
}
|
|
else // Otherwise break out of switch
|
|
// statement to continue the search
|
|
break;
|
|
}
|
|
// We didn't care about net number,
|
|
// so current entry will do
|
|
|
|
|
|
if (!(EnumPtr->RE_EnumerationFlags & RTM_ONLY_BEST_ROUTES)
|
|
|| IsBest(node)) {
|
|
RemoveEntryList (&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
InsertTailList (cur,
|
|
&EnumPtr->RE_Links[EnumPtr->RE_Link]);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
// Go get next entry
|
|
cur = cur->Blink;
|
|
}
|
|
|
|
// If we are not going through hash table or
|
|
// we just interested in one network
|
|
// or we've already been through all baskets
|
|
// call it quits
|
|
if ((EnumPtr->RE_Link!=RTM_NET_NUMBER_HASH_LINK)
|
|
|| (EnumPtr->RE_EnumerationFlags & RTM_ONLY_THIS_NETWORK)
|
|
|| (EnumPtr->RE_Lock
|
|
==&Table->RT_NetNumberHash[Table->RT_HashTableSize-1]))
|
|
break;
|
|
|
|
// Otherwise, go through the next basket
|
|
RemoveEntryList (&EnumPtr->RE_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
LeaveSyncList (Table, EnumPtr->RE_Lock);
|
|
EnumPtr->RE_Lock += 1;
|
|
EnumPtr->RE_Head = &EnumPtr->RE_Lock->RSL_Head;
|
|
if (!EnterSyncList (Table, EnumPtr->RE_Lock, TRUE)) {
|
|
InitializeListEntry (&EnumPtr->RE_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
}
|
|
|
|
InsertTailList (EnumPtr->RE_Head,
|
|
&EnumPtr->RE_Links[RTM_NET_NUMBER_HASH_LINK]);
|
|
}
|
|
return ERROR_NO_MORE_ROUTES;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// SetMaskCount
|
|
//
|
|
// Does a binary search of the g_meMaskTable to find the matching
|
|
// mask entry and increments the count for the specified mask
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
SetMaskCount(
|
|
PIP_NETWORK pinNet,
|
|
BOOL bAdd
|
|
)
|
|
{
|
|
|
|
DWORD dwLower, dwUpper, dwInd, dwMask;
|
|
|
|
|
|
dwLower = 0;
|
|
|
|
dwUpper = MAX_MASKS;
|
|
|
|
dwMask = pinNet-> N_NetMask;
|
|
|
|
while ( dwLower <= dwUpper )
|
|
{
|
|
dwInd = ( dwLower + dwUpper ) / 2;
|
|
|
|
if ( g_meMaskTable[ dwInd ].dwMask < dwMask )
|
|
{
|
|
//
|
|
// Match is to be found in upper half of search region.
|
|
//
|
|
|
|
dwLower = dwInd + 1;
|
|
}
|
|
|
|
else if ( g_meMaskTable[ dwInd ].dwMask > dwMask )
|
|
{
|
|
//
|
|
// Match is to be found in lower half of search region.
|
|
//
|
|
|
|
dwUpper = dwInd - 1;
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// Match found
|
|
//
|
|
|
|
if ( bAdd )
|
|
{
|
|
InterlockedIncrement( &g_meMaskTable[ dwInd ].dwCount );
|
|
}
|
|
|
|
else
|
|
{
|
|
InterlockedDecrement( &g_meMaskTable[ dwInd ].dwCount );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|