Leaked source code of windows server 2003
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

/*++
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;
}
}
}