381 lines
9.7 KiB
C
381 lines
9.7 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1995 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
net\rtm\rtm.c
|
||
|
|
||
|
Abstract:
|
||
|
Routing Table Manager DLL. Helper routines
|
||
|
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Vadim Eydelman
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "pchrtm.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
// Initializes sync list object
|
||
|
VOID
|
||
|
InitializeSyncList (
|
||
|
PRTM_SYNC_LIST list
|
||
|
) {
|
||
|
list->RSL_Sync = NULL;
|
||
|
list->RSL_UseCount = 0;
|
||
|
InitializeListHead (&list->RSL_Head);
|
||
|
}
|
||
|
|
||
|
// Get mutually exclusive access to the sync list obect
|
||
|
// Returns TRUE if access if obtained, FALSE otherwise
|
||
|
BOOLEAN
|
||
|
DoEnterSyncList (
|
||
|
PRTM_TABLE table, // Table this list belongs to
|
||
|
PRTM_SYNC_LIST list, // List of interest
|
||
|
BOOLEAN wait // True if caller wants to wait
|
||
|
// until list becomes available
|
||
|
#if DBG
|
||
|
, LPSTR file,
|
||
|
ULONG line
|
||
|
#endif
|
||
|
) {
|
||
|
DWORD status; // Status of OS calls
|
||
|
BOOLEAN result; // Result of operation
|
||
|
|
||
|
EnterCriticalSection (&table->RT_Lock);
|
||
|
// Protect manipilation by table-wide critical section
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace4 (ANY, "%08lx (%s,%ld) - trying to enter sync list: %08x\n",
|
||
|
GetCurrentThreadId (), file, line, (ULONG_PTR)list);
|
||
|
#endif
|
||
|
if (list->RSL_UseCount<=0) {
|
||
|
// Nobody uses the list -> get it and return ok
|
||
|
list->RSL_UseCount = 1;
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace0 (ANY, "\t - first to enter\n");
|
||
|
#endif
|
||
|
result = TRUE;
|
||
|
}
|
||
|
else if (wait) { // Somebody is using it, but caller agrees to wait
|
||
|
list->RSL_UseCount += 1; // Increment usage count
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace1 (ANY, "\t - list in use: %d\n", list->RSL_UseCount);
|
||
|
#endif
|
||
|
if (list->RSL_Sync==NULL) { // if there is no event to wait on,
|
||
|
// get one
|
||
|
// First see if one is available
|
||
|
// in the stack
|
||
|
PSINGLE_LIST_ENTRY cur = PopEntryList (&table->RT_SyncObjectList);
|
||
|
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace0 (ANY, "\t - need event\n");
|
||
|
#endif
|
||
|
if (cur==NULL) { // No, we'll have to create one
|
||
|
PRTM_SYNC_OBJECT sync;
|
||
|
sync = (PRTM_SYNC_OBJECT)GlobalAlloc (
|
||
|
GMEM_FIXED,
|
||
|
sizeof (RTM_SYNC_OBJECT));
|
||
|
if (sync==NULL) {
|
||
|
#if DBG
|
||
|
Trace2 (ANY,
|
||
|
"Can't allocate synchronization object.\n"
|
||
|
"\tat line %ld of %s\n",
|
||
|
__LINE__, __FILE__);
|
||
|
#endif
|
||
|
list->RSL_UseCount -= 1;
|
||
|
LeaveCriticalSection (&table->RT_Lock);
|
||
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
sync->RSO_Event = CreateEvent (NULL,
|
||
|
FALSE, // Auto reset event
|
||
|
FALSE, // Initially nonsignaled
|
||
|
NULL);
|
||
|
if (sync->RSO_Event==NULL) {
|
||
|
status = GetLastError ();
|
||
|
#if DBG
|
||
|
Trace2 (ANY,
|
||
|
"Can't allocate synchronization event.\n"
|
||
|
"\tat line %ld of %s\n",
|
||
|
__LINE__, __FILE__);
|
||
|
#endif
|
||
|
list->RSL_UseCount -= 1;
|
||
|
GlobalFree (sync);
|
||
|
LeaveCriticalSection (&table->RT_Lock);
|
||
|
SetLastError (status);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
list->RSL_Sync = sync;
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace0 (ANY, "\t - event created\n");
|
||
|
#endif
|
||
|
}
|
||
|
else { // Yes, make sure it is reset
|
||
|
list->RSL_Sync = CONTAINING_RECORD (cur, RTM_SYNC_OBJECT, RSO_Link);
|
||
|
// Autoreset event gets reset after releasing a thread anyway
|
||
|
// status = ResetEvent (list->RSL_Sync->RSO_Event);
|
||
|
// ASSERTERRMSG ("Can't reset event.", status);
|
||
|
}
|
||
|
}
|
||
|
// Now as we set up the object to wait, we can leave critical
|
||
|
// section and wait on event
|
||
|
LeaveCriticalSection (&table->RT_Lock);
|
||
|
status = WaitForSingleObject (
|
||
|
list->RSL_Sync->RSO_Event,
|
||
|
INFINITE
|
||
|
);
|
||
|
ASSERTERRMSG ("Wait event failed.", status==WAIT_OBJECT_0);
|
||
|
|
||
|
// Event is signaled, we may now access the list (auto reset event
|
||
|
// releases only one thread
|
||
|
EnterCriticalSection (&table->RT_Lock);
|
||
|
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace1 (ANY, "%08lx - wait completed\n", GetCurrentThreadId ());
|
||
|
#endif
|
||
|
|
||
|
// If our caller was the only one waiting,
|
||
|
// we can release the event
|
||
|
if (list->RSL_UseCount==1) {
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace0 (ANY, "\t - restocking event\n");
|
||
|
#endif
|
||
|
PushEntryList (&table->RT_SyncObjectList, &list->RSL_Sync->RSO_Link);
|
||
|
list->RSL_Sync = NULL;
|
||
|
}
|
||
|
result = TRUE;
|
||
|
}
|
||
|
else {
|
||
|
// Caller does not want to wait
|
||
|
result = FALSE;
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace0 (ANY, "\t - doesn't want to wait\n");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection (&table->RT_Lock);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Release previously owned sync list object
|
||
|
VOID
|
||
|
LeaveSyncList (
|
||
|
PRTM_TABLE table, // Table to which this object belongs
|
||
|
PRTM_SYNC_LIST list // List to release
|
||
|
) {
|
||
|
DWORD status;
|
||
|
|
||
|
EnterCriticalSection (&table->RT_Lock);
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace2 (ANY, "%08lx - leaving sync list: %08x\n",
|
||
|
GetCurrentThreadId (), (ULONG_PTR)list);
|
||
|
#endif
|
||
|
// Decrement the count and signal the event (only one thread
|
||
|
// will be released for the auto-reset events
|
||
|
list->RSL_UseCount -= 1;
|
||
|
if (list->RSL_UseCount>0) {
|
||
|
#if DBG
|
||
|
IF_DEBUG (SYNCHRONIZATION)
|
||
|
Trace1 (ANY, "%\t - releasing one of %d waiting threads\n",
|
||
|
list->RSL_UseCount);
|
||
|
#endif
|
||
|
status = SetEvent (list->RSL_Sync->RSO_Event);
|
||
|
ASSERTERRMSG ("Can't signal event.", status);
|
||
|
}
|
||
|
LeaveCriticalSection (&table->RT_Lock);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Finds list of routes that are associated with given interface and returns
|
||
|
// pointer to its head
|
||
|
// Creates new list of none exists yet
|
||
|
PLIST_ENTRY
|
||
|
FindInterfaceList (
|
||
|
PRTM_SYNC_LIST intfHash,
|
||
|
DWORD InterfaceID, // Interface to look for
|
||
|
BOOL CreateNew
|
||
|
) {
|
||
|
PRTM_INTERFACE_NODE intfNode;
|
||
|
PLIST_ENTRY cur;
|
||
|
|
||
|
// First try to find existing one in the list of interface lists
|
||
|
cur = intfHash->RSL_Head.Flink;
|
||
|
while (cur!=&intfHash->RSL_Head) {
|
||
|
intfNode = CONTAINING_RECORD (cur, RTM_INTERFACE_NODE, IN_Link);
|
||
|
if (InterfaceID<=intfNode->IN_InterfaceID) // List is ordered
|
||
|
// so we can stop
|
||
|
// if bigger number is reached
|
||
|
break;
|
||
|
cur = cur->Flink;
|
||
|
}
|
||
|
|
||
|
|
||
|
if ((cur==&intfHash->RSL_Head)
|
||
|
|| (InterfaceID!=intfNode->IN_InterfaceID)) { // Create new interface
|
||
|
// list
|
||
|
if (!CreateNew)
|
||
|
return NULL;
|
||
|
|
||
|
intfNode = (PRTM_INTERFACE_NODE)GlobalAlloc (
|
||
|
GMEM_FIXED,
|
||
|
sizeof (RTM_INTERFACE_NODE));
|
||
|
if (intfNode==NULL) {
|
||
|
#if DBG
|
||
|
// Report error in debuging builds
|
||
|
Trace2 (ANY,
|
||
|
"Can't allocate interface node\n\tat line %ld of %s\n",
|
||
|
__LINE__, __FILE__);
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
intfNode->IN_InterfaceID = InterfaceID;
|
||
|
InitializeListHead (&intfNode->IN_Head); // Insert it in
|
||
|
// list of interface lists
|
||
|
InsertTailList (cur, &intfNode->IN_Link);
|
||
|
}
|
||
|
|
||
|
return &intfNode->IN_Head;
|
||
|
}
|
||
|
|
||
|
#if RTM_USE_PROTOCOL_LISTS
|
||
|
// Finds list of routes that are associated with given iprotocol and returns
|
||
|
// pointer to its head
|
||
|
// Creates new list of none exists yet
|
||
|
PLIST_ENTRY
|
||
|
FindProtocolList (
|
||
|
PRTM_TABLE Table,
|
||
|
DWORD RoutingProtocol,
|
||
|
BOOL CreateNew
|
||
|
) {
|
||
|
PRTM_PROTOCOL_NODE protNode;
|
||
|
PLIST_ENTRY cur;
|
||
|
|
||
|
cur = Table->RT_ProtocolList.RSL_Head.Flink;
|
||
|
while (cur!=&Table->RT_ProtocolList.RSL_Head) {
|
||
|
protNode = CONTAINING_RECORD (cur, RTM_PROTOCOL_NODE, PN_Link);
|
||
|
if (RoutingProtocol<=protNode->PN_RoutingProtocol)
|
||
|
break;
|
||
|
cur = cur->Flink;
|
||
|
}
|
||
|
|
||
|
if ((cur==&Table->RT_ProtocolList.RSL_Head)
|
||
|
|| (RoutingProtocol!=protNode->PN_RoutingProtocol)) {
|
||
|
|
||
|
if (!CreateNew)
|
||
|
return NULL;
|
||
|
|
||
|
protNode = (PRTM_PROTOCOL_NODE)GlobalAlloc (
|
||
|
GMEM_FIXED,
|
||
|
sizeof (RTM_PROTOCOL_NODE));
|
||
|
if (protNode==NULL) {
|
||
|
#if DBG
|
||
|
// Report error in debuging builds
|
||
|
Trace2 (ANY,
|
||
|
"Can't allocate protocol node\n\tat line %ld of %s\n",
|
||
|
__LINE__, __FILE__);
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
protNode->PN_RoutingProtocol = RoutingProtocol;
|
||
|
InitializeListHead (&protNode->PN_Head);
|
||
|
InsertTailList (cur, &protNode->PN_Link);
|
||
|
}
|
||
|
|
||
|
return &protNode->PN_Head;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Adds node to temporary net number list (to be later merged with master list)
|
||
|
// Both lists are ordered by net number.interface.protocol.next hop address
|
||
|
VOID
|
||
|
AddNetNumberListNode (
|
||
|
PRTM_TABLE Table,
|
||
|
PRTM_ROUTE_NODE newNode
|
||
|
) {
|
||
|
PLIST_ENTRY cur;
|
||
|
INT res;
|
||
|
|
||
|
cur = Table->RT_NetNumberTempList.RSL_Head.Flink;
|
||
|
while (cur!=&Table->RT_NetNumberTempList.RSL_Head) {
|
||
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
||
|
cur,
|
||
|
RTM_ROUTE_NODE,
|
||
|
RN_Links[RTM_NET_NUMBER_LIST_LINK]
|
||
|
);
|
||
|
res = NetNumCmp (Table, &newNode->RN_Route, &node->RN_Route);
|
||
|
if ((res<0)
|
||
|
||((res==0)
|
||
|
&&((newNode->RN_Route.XX_PROTOCOL
|
||
|
< node->RN_Route.XX_PROTOCOL)
|
||
|
||((newNode->RN_Route.XX_PROTOCOL
|
||
|
==node->RN_Route.XX_PROTOCOL)
|
||
|
&&((newNode->RN_Route.XX_INTERFACE
|
||
|
< node->RN_Route.XX_INTERFACE)
|
||
|
||((newNode->RN_Route.XX_INTERFACE
|
||
|
== node->RN_Route.XX_INTERFACE)
|
||
|
&& (NextHopCmp (Table, &newNode->RN_Route,
|
||
|
&node->RN_Route)
|
||
|
< 0)))))))
|
||
|
break;
|
||
|
cur = cur->Flink;
|
||
|
}
|
||
|
|
||
|
InsertTailList (cur, &newNode->RN_Links[RTM_NET_NUMBER_LIST_LINK]);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Adds node to expiration time queue. (Queue is ordered by expiration time)
|
||
|
// Return TRUE if new node is the first in the queue
|
||
|
BOOL
|
||
|
AddExpirationQueueNode (
|
||
|
PRTM_TABLE Table,
|
||
|
PRTM_ROUTE_NODE newNode
|
||
|
) {
|
||
|
PLIST_ENTRY cur;
|
||
|
BOOL res = TRUE;
|
||
|
|
||
|
// We'll travers the queue from the back, because normally
|
||
|
// new entries are added closer the end of the queue
|
||
|
cur = Table->RT_ExpirationQueue.RSL_Head.Blink;
|
||
|
while (cur!=&Table->RT_ExpirationQueue.RSL_Head) {
|
||
|
PRTM_ROUTE_NODE node = CONTAINING_RECORD (
|
||
|
cur,
|
||
|
RTM_ROUTE_NODE,
|
||
|
RN_Links[RTM_EXPIRATION_QUEUE_LINK]
|
||
|
);
|
||
|
if (IsLater(newNode->RN_ExpirationTime, node->RN_ExpirationTime)) {
|
||
|
res = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
cur = cur->Blink;
|
||
|
}
|
||
|
|
||
|
InsertHeadList (cur, &newNode->RN_Links[RTM_EXPIRATION_QUEUE_LINK]);
|
||
|
return res;
|
||
|
}
|