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