/*++ Copyright (c) 1997 - 98, Microsoft Corporation Module Name: rtmobj1.c Abstract: Contains routines for managing RTM objects like Instances, AddrFamilies and Entities. Author: Chaitanya Kodeboyina (chaitk) 21-Aug-1998 Revision History: --*/ #include "pchrtm.h" #pragma hdrstop DWORD GetInstance ( IN USHORT RtmInstanceId, IN BOOL ImplicitCreate, OUT PINSTANCE_INFO *RtmInstance ) /*++ Routine Description: Searches for an RTM instance with the input instance id. If an instance is not found and ImplicitCreate is TRUE, then a new instance is created and added to the table of instances. Arguments: RtmInstanceId - Id for RTM Instance being searched for, ImplicitCreate - Create a new instance if not found or not, RtmInstance - Pointer to the Instance Info Structure will be returned through this parameter. Return Value: Status of the operation Locks: The InstancesLock in RtmGlobals should be held while calling this function. If ImplicitCreate is FALSE, a read lock would do, but if it is TRUE then a write lock should be held as we would need to insert a new instance into the instances list. --*/ { PLIST_ENTRY Instances; PINSTANCE_INFO Instance; PLIST_ENTRY p; DWORD Status; Instances = &RtmGlobals.InstanceTable[RtmInstanceId % INSTANCE_TABLE_SIZE]; #if WRN Instance = NULL; #endif do { // Search the global list for a matching instance for (p = Instances->Flink; p != Instances; p = p->Flink) { Instance = CONTAINING_RECORD(p, INSTANCE_INFO, InstTableLE); if (Instance->RtmInstanceId >= RtmInstanceId) { break; } } if ((p == Instances) || (Instance == NULL) || (Instance->RtmInstanceId != RtmInstanceId)) { // We did not find an instance - create new one ? if (!ImplicitCreate) { Status = ERROR_NOT_FOUND; break; } // Create a new instance with input Instance id Status = CreateInstance(RtmInstanceId, &Instance); if (Status != NO_ERROR) { break; } // Insert into list in sorted Instance Id order InsertTailList(p, &Instance->InstTableLE); } Status = NO_ERROR; *RtmInstance = Instance; } while (FALSE); return Status; } DWORD CreateInstance ( IN USHORT RtmInstanceId, OUT PINSTANCE_INFO *NewInstance ) /*++ Routine Description: Creates a new instance info structure and initializes it. Arguments: RtmInstanceId - RTM Instance Id for the new RTM instance, InstConfig - Configuration Info for the new instance, NewInstance - Pointer to the Instance Info Structure will be returned through this parameter. Return Value: Status of the operation Locks: Need to be called with the instances WRITE lock as we are incrementing the number of instances here. --*/ { RTM_INSTANCE_CONFIG InstConfig; PINSTANCE_INFO Instance; DWORD Status; *NewInstance = NULL; // // Read Instance Configuration from the registry // Status = RtmReadInstanceConfig(RtmInstanceId, &InstConfig); if (Status != NO_ERROR) { return Status; } // // Allocate and initialize a new instance info // Instance = (PINSTANCE_INFO) AllocNZeroObject(sizeof(INSTANCE_INFO)); if (Instance == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } #if DBG_HDL Instance->ObjectHeader.TypeSign = INSTANCE_ALLOC; #endif // Will be removed when last addr family goes INITIALIZE_INSTANCE_REFERENCE(Instance, CREATION_REF); Instance->RtmInstanceId = RtmInstanceId; // // Linking instance to global list of instances is // done by caller, but pretend it is already done // RtmGlobals.NumInstances++; InitializeListHead(&Instance->InstTableLE); // // Initialize the table of address families // Instance->NumAddrFamilies = 0; InitializeListHead(&Instance->AddrFamilyTable); *NewInstance = Instance; return NO_ERROR; } DWORD DestroyInstance ( IN PINSTANCE_INFO Instance ) /*++ Routine Description: Destroys an existing instance info structure. Assumes that no registered entities exist on this instance when called. Arguments: Instance - Pointer to the Instance Info Structure. Return Value: Status of the operation Locks: The InstancesLock in RtmGlobals should be held while calling this function as it removes an instance from that list. This is typically taken in DestroyEntity, but it can also happen that the lock is acquired in RtmRegisterEntity and an error occured. --*/ { ASSERT(Instance->ObjectHeader.RefCount == 0); ASSERT(Instance->NumAddrFamilies == 0); // // Remove this instance from list of instances // RemoveEntryList(&Instance->InstTableLE); RtmGlobals.NumInstances--; // // Free resources allocated for this instance // #if DBG_HDL Instance->ObjectHeader.TypeSign = INSTANCE_FREED; #endif FreeObject(Instance); return NO_ERROR; } DWORD GetAddressFamily ( IN PINSTANCE_INFO Instance, IN USHORT AddressFamily, IN BOOL ImplicitCreate, OUT PADDRFAM_INFO *AddrFamilyInfo ) /*++ Routine Description: Searches for an address family in an RTM instance. If it is not found and ImplicitCreate is TRUE, then a new address family info is created and added to the list of address families. Arguments: Instance - RTM Instance that holds the address family, AddressFamily - Address family for info being searched for, ImplicitCreate - Create an addr family info if not found or not, AddrFamilyInfo - Pointer to the new Address Family Info will be returned through this parameter. Return Value: Status of the operation Locks: The InstancesLock in RtmGlobals should be held while calling this function. If ImplicitCreate is FALSE, a read lock would do, but if it is TRUE then a write lock should be held as we will need it to insert a new address family info into a list. --*/ { PLIST_ENTRY AddrFams; PADDRFAM_INFO AddrFamInfo; PLIST_ENTRY q; DWORD Status; AddrFams = &Instance->AddrFamilyTable; #if WRN AddrFamInfo = NULL; #endif do { // Search the list of addr families on instance for (q = AddrFams->Flink; q != AddrFams; q = q->Flink) { AddrFamInfo = CONTAINING_RECORD(q, ADDRFAM_INFO, AFTableLE); if (AddrFamInfo->AddressFamily >= AddressFamily) { break; } } if ((q == AddrFams) || (AddrFamInfo == NULL) || (AddrFamInfo->AddressFamily != AddressFamily)) { // We did not find an instance - create new one ? if (!ImplicitCreate) { Status = ERROR_NOT_FOUND; break; } // Create a new addr family info with input family Status = CreateAddressFamily(Instance,AddressFamily, &AddrFamInfo); if (Status != NO_ERROR) { break; } // Insert into list sorted in Address Family order InsertTailList(q, &AddrFamInfo->AFTableLE); } Status = NO_ERROR; *AddrFamilyInfo = AddrFamInfo; } while (FALSE); return Status; } DWORD CreateAddressFamily ( IN PINSTANCE_INFO Instance, IN USHORT AddressFamily, OUT PADDRFAM_INFO *NewAddrFamilyInfo ) /*++ Routine Description: Creates a new address family info and initializes it Arguments: Instance - RTM Instance that owns addr family info, AddressFamily - Address family for the new info block, AddrFamilyInfo - Pointer to the new Address Family Info will be returned through this parameter. Return Value: Status of the operation Locks: Need to be called with the instances WRITE lock as we are are incrementing number of address families on instance. --*/ { RTM_ADDRESS_FAMILY_CONFIG AddrFamConfig; PADDRFAM_INFO AddrFamilyInfo; RTM_VIEW_SET ViewsSupported; PSINGLE_LIST_ENTRY ListPtr; UINT i; DWORD Status; *NewAddrFamilyInfo = NULL; // // Read AddressFamily Configuration from the registry // Status = RtmReadAddressFamilyConfig(Instance->RtmInstanceId, AddressFamily, &AddrFamConfig); if (Status != NO_ERROR) { if (Instance->NumAddrFamilies == 0) { DEREFERENCE_INSTANCE(Instance, CREATION_REF); } return Status; } // // Allocate and initialize a new address family info // AddrFamilyInfo = (PADDRFAM_INFO) AllocNZeroObject(sizeof(ADDRFAM_INFO)); if (AddrFamilyInfo == NULL) { if (Instance->NumAddrFamilies == 0) { DEREFERENCE_INSTANCE(Instance, CREATION_REF); } return ERROR_NOT_ENOUGH_MEMORY; } do { #if DBG_HDL AddrFamilyInfo->ObjectHeader.TypeSign = ADDRESS_FAMILY_ALLOC; #endif // Will be removed when last entity deregisters INITIALIZE_ADDR_FAMILY_REFERENCE(AddrFamilyInfo, CREATION_REF); AddrFamilyInfo->AddressFamily = AddressFamily; AddrFamilyInfo->AddressSize = AddrFamConfig.AddressSize; AddrFamilyInfo->Instance = Instance; REFERENCE_INSTANCE(Instance, ADDR_FAMILY_REF); // // Linking the address family to its owning instance // is done by caller, but pretend it is already done // Instance->NumAddrFamilies++; InitializeListHead(&AddrFamilyInfo->AFTableLE); // // Count number of views supported by this addr family // & setup the view id <-> view index in dest mappings // AddrFamilyInfo->ViewsSupported = AddrFamConfig.ViewsSupported; ViewsSupported = AddrFamConfig.ViewsSupported; AddrFamilyInfo->NumberOfViews = 0; for (i = 0; i < RTM_MAX_VIEWS; i++) { AddrFamilyInfo->ViewIdFromIndex[i] = -1; AddrFamilyInfo->ViewIndexFromId[i] = -1; } for (i = 0; (i < RTM_MAX_VIEWS) && ViewsSupported; i++) { if (ViewsSupported & 0x01) { AddrFamilyInfo->ViewIdFromIndex[AddrFamilyInfo->NumberOfViews] = i; AddrFamilyInfo->ViewIndexFromId[i] = AddrFamilyInfo->NumberOfViews; AddrFamilyInfo->NumberOfViews++; } ViewsSupported >>= 1; } AddrFamilyInfo->MaxHandlesInEnum = AddrFamConfig.MaxHandlesInEnum; AddrFamilyInfo->MaxNextHopsInRoute = AddrFamConfig.MaxNextHopsInRoute; // // Initialize the opaque pointer's directory // AddrFamilyInfo->MaxOpaquePtrs = AddrFamConfig.MaxOpaqueInfoPtrs; AddrFamilyInfo->NumOpaquePtrs = 0; AddrFamilyInfo->OpaquePtrsDir = AllocNZeroMemory(AddrFamilyInfo->MaxOpaquePtrs * sizeof(PVOID)); if (AddrFamilyInfo->OpaquePtrsDir == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } // // Initialize the list of entities on this address family // AddrFamilyInfo->NumEntities = 0; for (i = 0; i < ENTITY_TABLE_SIZE; i++) { InitializeListHead(&AddrFamilyInfo->EntityTable[i]); } // // Init list of entities de-registered but not destroyed // InitializeListHead(&AddrFamilyInfo->DeregdEntities); // // Initialize the route table and route table lock // try { CREATE_READ_WRITE_LOCK(&AddrFamilyInfo->RouteTableLock); AddrFamilyInfo->RoutesLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } Status = CreateTable(AddrFamilyInfo->AddressSize, &AddrFamilyInfo->RouteTable); if (Status != NO_ERROR) { break; } // // Initialize queue to hold notification timers // AddrFamilyInfo->NotifTimerQueue = CreateTimerQueue(); if (AddrFamilyInfo->NotifTimerQueue == NULL) { Status = GetLastError(); break; } // // Initialize queue to hold route timers on AF // AddrFamilyInfo->RouteTimerQueue = CreateTimerQueue(); if (AddrFamilyInfo->RouteTimerQueue == NULL) { Status = GetLastError(); break; } // // Initialize the change notification info and lock // try { CREATE_READ_WRITE_LOCK(&AddrFamilyInfo->ChangeNotifsLock); AddrFamilyInfo->NotifsLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } AddrFamilyInfo->MaxChangeNotifs = AddrFamConfig.MaxChangeNotifyRegns; AddrFamilyInfo->NumChangeNotifs = 0; // // Allocate memory for the max number of notifications // AddrFamilyInfo->ChangeNotifsDir = AllocNZeroMemory(AddrFamilyInfo->MaxChangeNotifs * sizeof(PVOID)); if (AddrFamilyInfo->ChangeNotifsDir == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } // // Initialize lock protecting the notification timer // try { InitializeCriticalSection(&AddrFamilyInfo->NotifsTimerLock); AddrFamilyInfo->TimerLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } // // Initialize each change list in the change list table // for (i = 0; i < NUM_CHANGED_DEST_LISTS; i++) { // // Initialize the list of changed dests and lock // // Init the change list to an empty circular list ListPtr = &AddrFamilyInfo->ChangeLists[i].ChangedDestsHead; ListPtr->Next = ListPtr; AddrFamilyInfo->ChangeLists[i].ChangedDestsTail = ListPtr; try { InitializeCriticalSection (&AddrFamilyInfo->ChangeLists[i].ChangesListLock); AddrFamilyInfo->ChangeLists[i].ChangesLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } } if (Status != NO_ERROR) { break; } *NewAddrFamilyInfo = AddrFamilyInfo; return NO_ERROR; } while (FALSE); // // Something failed - undo work done and return status // DEREFERENCE_ADDR_FAMILY(AddrFamilyInfo, CREATION_REF); return Status; } DWORD DestroyAddressFamily ( IN PADDRFAM_INFO AddrFamilyInfo ) /*++ Routine Description: Destroys an address family info in an RTM instance. Assumes that no registered entities exist with this address family in this RTM instance when invoked. This function has been written such that it can be called when an error occurs in CreateAddressFamily. Arguments: AddrFamilyInfo - Pointer to the Rib Info Structure. Return Value: Status of the operation Locks: The InstancesLock in RtmGlobals should be held while calling this function as it removes an address family from the list of address families on the instance. This lock is typically taken in DestroyEntity, but it can also happen that the lock is acquired in RtmRegisterEntity and an error occured in the CreateAddressFamily function. --*/ { PINSTANCE_INFO Instance; PSINGLE_LIST_ENTRY ListPtr; UINT i; ASSERT(AddrFamilyInfo->ObjectHeader.RefCount == 0); ASSERT(AddrFamilyInfo->NumEntities == 0); ASSERT(IsListEmpty(&AddrFamilyInfo->DeregdEntities)); // // Block until timers on address family are cleaned up // if (AddrFamilyInfo->RouteTimerQueue) { DeleteTimerQueueEx(AddrFamilyInfo->RouteTimerQueue, (HANDLE) -1); } if (AddrFamilyInfo->NotifTimerQueue) { DeleteTimerQueueEx(AddrFamilyInfo->NotifTimerQueue, (HANDLE) -1); } // // Free resources allocated to the change lists (locks ..) // // No more dests in change list as all entities are gone ASSERT(AddrFamilyInfo->NumChangedDests == 0); for (i = 0; i < NUM_CHANGED_DEST_LISTS; i++) { ListPtr = &AddrFamilyInfo->ChangeLists[i].ChangedDestsHead; ASSERT(ListPtr->Next == ListPtr); ASSERT(AddrFamilyInfo->ChangeLists[i].ChangedDestsTail == ListPtr); if (AddrFamilyInfo->ChangeLists[i].ChangesLockInited) { DeleteCriticalSection (&AddrFamilyInfo->ChangeLists[i].ChangesListLock); } } // // Free the change notification info and the guarding lock // ASSERT(AddrFamilyInfo->NumChangeNotifs == 0); if (AddrFamilyInfo->ChangeNotifsDir) { FreeMemory(AddrFamilyInfo->ChangeNotifsDir); } if (AddrFamilyInfo->NotifsLockInited) { DELETE_READ_WRITE_LOCK(&AddrFamilyInfo->ChangeNotifsLock); } // // Free the lock guarding the notification timer // if (AddrFamilyInfo->TimerLockInited) { DeleteCriticalSection(&AddrFamilyInfo->NotifsTimerLock); } // // Free the route table and the route table lock // ASSERT(AddrFamilyInfo->NumRoutes == 0); // // Because some hold's are left out - this count // might not be equal to zero. Need to fix this // memory leak by cleaning up before this point // // ASSERT(AddrFamilyInfo->NumDests == 0); if (AddrFamilyInfo->RouteTable) { DestroyTable(AddrFamilyInfo->RouteTable); } if (AddrFamilyInfo->RoutesLockInited) { DELETE_READ_WRITE_LOCK(&AddrFamilyInfo->RouteTableLock); } // // Free Opaque Ptrs directory (if it is allocated) // if (AddrFamilyInfo->OpaquePtrsDir) { FreeMemory(AddrFamilyInfo->OpaquePtrsDir); } // // Remove the address family from owning instance // Instance = AddrFamilyInfo->Instance; RemoveEntryList(&AddrFamilyInfo->AFTableLE); Instance->NumAddrFamilies--; DEREFERENCE_INSTANCE(Instance, ADDR_FAMILY_REF); // Reclaim the instance if it has no addr familes if (Instance->NumAddrFamilies == 0) { DEREFERENCE_INSTANCE(Instance, CREATION_REF); } #if DBG_HDL AddrFamilyInfo->ObjectHeader.TypeSign = ADDRESS_FAMILY_FREED; #endif FreeObject(AddrFamilyInfo); return NO_ERROR; } DWORD GetEntity ( IN PADDRFAM_INFO AddrFamilyInfo, IN ULONGLONG EntityId, IN BOOL ImplicitCreate, IN PRTM_ENTITY_INFO RtmEntityInfo OPTIONAL, IN BOOL ReserveOpaquePtr OPTIONAL, IN PRTM_ENTITY_EXPORT_METHODS ExportMethods OPTIONAL, IN RTM_EVENT_CALLBACK EventCallback OPTIONAL, OUT PENTITY_INFO *EntityInfo ) /*++ Routine Description: Searches for an entity with a certain protocol id and protocol instance. If it is not found and ImplicitCreate is TRUE, then a new entity is created and added to the table of entities on address family. Arguments: AddrFamilyInfo - Address family block that we are seaching, EntityId - Entity protocol id and protocol instance, ImplicitCreate - Create a new entity if not found or not, For all others - See corresponding parametes in CreateEntity EntityInfo - The entity info is returned in this param. Return Value: Status of the operation Locks: The InstancesLock in RtmGlobals should be held while calling this function. If ImplicitCreate is FALSE, a read lock would do, but if it is TRUE then a write lock should be held as we would need it to insert a new entity into the entities list. --*/ { PLIST_ENTRY Entities; PENTITY_INFO Entity; PLIST_ENTRY r; DWORD Status; Entities = &AddrFamilyInfo->EntityTable[EntityId % ENTITY_TABLE_SIZE]; #if WRN Entity = NULL; #endif do { // Search for an entity with the input Entity Id for (r = Entities->Flink; r != Entities; r = r->Flink) { Entity = CONTAINING_RECORD(r, ENTITY_INFO, EntityTableLE); if (Entity->EntityId.EntityId >= EntityId) { break; } } if ((r != Entities) && (Entity->EntityId.EntityId == EntityId)) { Status = ERROR_ALREADY_EXISTS; break; } // We did not find an entity - create a new one ? if (!ImplicitCreate) { Status = ERROR_NOT_FOUND; break; } // Create a new entity with all the input RTM parameters Status = CreateEntity(AddrFamilyInfo, RtmEntityInfo, ReserveOpaquePtr, ExportMethods, EventCallback, &Entity); if (Status != NO_ERROR) { break; } // // Inform all existing entities of this new entity // InformEntitiesOfEvent(AddrFamilyInfo->EntityTable, RTM_ENTITY_REGISTERED, Entity); // Insert to keep the list sorted Entity Id Order InsertTailList(r, &Entity->EntityTableLE); *EntityInfo = Entity; } while (FALSE); return Status; } DWORD CreateEntity ( IN PADDRFAM_INFO AddrFamilyInfo, IN PRTM_ENTITY_INFO EntityInfo, IN BOOL ReserveOpaquePtr, IN PRTM_ENTITY_EXPORT_METHODS ExportMethods, IN RTM_EVENT_CALLBACK EventCallback, OUT PENTITY_INFO *NewEntity ) /*++ Routine Description: Creates a new entity info structure and initializes it. Arguments: AddrFamilyInfo - Address Family the entity is registering with, EntityInfo - Information for the entity being created, ReserveOpaquePtr - Reserve a ptr in each destination or not, ExportMethods - List of methods exported by this entity, EventCallback - Callback invoked to inform of certain events like entity registrations, de-registrations, NewEntity - Pointer to the new Entity Info structure will be returned through this parameter. Return Value: Status of the operation --*/ { PENTITY_INFO Entity; UINT NumMethods, i; DWORD Status; *NewEntity = NULL; // // Allocate and initialize a new entity info structure // NumMethods = ExportMethods ? ExportMethods->NumMethods : 0; Entity = (PENTITY_INFO) AllocNZeroObject( sizeof(ENTITY_INFO) + (NumMethods ? (NumMethods - 1) : 0 ) * sizeof(RTM_ENTITY_EXPORT_METHOD)); if (Entity == NULL) { if (AddrFamilyInfo->NumEntities == 0) { DEREFERENCE_ADDR_FAMILY(AddrFamilyInfo, CREATION_REF); } return ERROR_NOT_ENOUGH_MEMORY; } do { #if DBG_HDL Entity->ObjectHeader.TypeSign = ENTITY_ALLOC; #endif INITIALIZE_ENTITY_REFERENCE(Entity, CREATION_REF); Entity->EntityId = EntityInfo->EntityId; Entity->OwningAddrFamily = AddrFamilyInfo; REFERENCE_ADDR_FAMILY(AddrFamilyInfo, ENTITY_REF); // // Linking the entity to its owning address family is // done by caller,but pretend that it is already done // AddrFamilyInfo->NumEntities++; InitializeListHead(&Entity->EntityTableLE); // // Allocate an opaque pointer index if asked for // Entity->OpaquePtrOffset = -1; if (ReserveOpaquePtr) { if (AddrFamilyInfo->NumOpaquePtrs >= AddrFamilyInfo->MaxOpaquePtrs) { Status = ERROR_NO_SYSTEM_RESOURCES; break; } for (i = 0; i < AddrFamilyInfo->MaxOpaquePtrs; i++) { if (AddrFamilyInfo->OpaquePtrsDir[i] == NULL) { break; } } AddrFamilyInfo->OpaquePtrsDir[i] = (PVOID) Entity; AddrFamilyInfo->NumOpaquePtrs++; Entity->OpaquePtrOffset = i; ASSERT(Entity->OpaquePtrOffset != -1); } // // Initialize lock guarding entity-specific route lists // try { CREATE_READ_WRITE_LOCK(&Entity->RouteListsLock); Entity->ListsLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } // // Initialize the list of open handles and corresponding lock // try { InitializeCriticalSection(&Entity->OpenHandlesLock); Entity->HandlesLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } InitializeListHead(&Entity->OpenHandles); // // Initialize the next hop table and the next hop table lock // try { CREATE_READ_WRITE_LOCK(&Entity->NextHopTableLock); Entity->NextHopsLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } Status = CreateTable(AddrFamilyInfo->AddressSize, &Entity->NextHopTable); if (Status != NO_ERROR) { break; } Entity->NumNextHops = 0; // // Initialize entity methods and the entity methods lock // try { CREATE_READ_WRITE_LOCK(&Entity->EntityMethodsLock); Entity->MethodsLockInited = TRUE; } except(EXCEPTION_EXECUTE_HANDLER) { Status = ERROR_NOT_ENOUGH_MEMORY; break; } Entity->EventCallback = EventCallback; Entity->EntityMethods.NumMethods = NumMethods; if (ExportMethods) { CopyMemory(Entity->EntityMethods.Methods, ExportMethods->Methods, NumMethods * sizeof(RTM_ENTITY_EXPORT_METHOD)); } *NewEntity = Entity; return NO_ERROR; } while(FALSE); // // Something failed - undo work done and return status // DEREFERENCE_ENTITY(Entity, CREATION_REF); return Status; } DWORD DestroyEntity ( IN PENTITY_INFO Entity ) /*++ Routine Description: Destroys an existing entity info structure. Frees all associated resources before de-allocation. This function has been written such that it can be called when an error occurs during CreateEntity. Arguments: EntityInfo - Pointer to the Entity Info Structure. Return Value: Status of the operation --*/ { PADDRFAM_INFO AddrFamilyInfo; ASSERT(Entity->ObjectHeader.RefCount == 0); // // Take globals registrations lock while cleaning up // ACQUIRE_INSTANCES_WRITE_LOCK(); // // Free lock used to block exported entity methods // if (Entity->MethodsLockInited) { DELETE_READ_WRITE_LOCK(&Entity->EntityMethodsLock); } // // Free the next hop table and the lock guarding it // ASSERT(Entity->NumNextHops == 0); if (Entity->NextHopTable) { DestroyTable(Entity->NextHopTable); } if (Entity->NextHopsLockInited) { DELETE_READ_WRITE_LOCK(&Entity->NextHopTableLock); } if (Entity->HandlesLockInited) { // There should not be any handles opened by entity ASSERT(IsListEmpty(&Entity->OpenHandles)); DeleteCriticalSection(&Entity->OpenHandlesLock); } // // Free lock used to perform route list operations // if (Entity->ListsLockInited) { DELETE_READ_WRITE_LOCK(&Entity->RouteListsLock); } // // Free the opaque ptr index in the address family // AddrFamilyInfo = Entity->OwningAddrFamily; if (Entity->OpaquePtrOffset != -1) { AddrFamilyInfo->OpaquePtrsDir[Entity->OpaquePtrOffset] = NULL; AddrFamilyInfo->NumOpaquePtrs--; } #if DBG_REF_BLOCKING // // Signal event on entity to unblock de-register // The evnt will be freed in RtmDeregisterEntity // if (Entity->BlockingEvent) { SetEvent(Entity->BlockingEvent); } #endif // // Remove the entity from the owning address family // RemoveEntryList(&Entity->EntityTableLE); AddrFamilyInfo->NumEntities--; DEREFERENCE_ADDR_FAMILY(AddrFamilyInfo, ENTITY_REF); // Reclaim the addr family if it has no entities if (AddrFamilyInfo->NumEntities == 0) { DEREFERENCE_ADDR_FAMILY(AddrFamilyInfo, CREATION_REF); } #if DBG_HDL Entity->ObjectHeader.TypeSign = ENTITY_FREED; #endif FreeObject(Entity); RELEASE_INSTANCES_WRITE_LOCK(); return NO_ERROR; } VOID InformEntitiesOfEvent ( IN PLIST_ENTRY EntityTable, IN RTM_EVENT_TYPE EventType, IN PENTITY_INFO EntityThis ) /*++ Routine Description: Informs all entities in the entity table that a certain event has occured - like a new entity registered, or an existing entity de-registered. Arguments: EntityTable - Pointer to the hash table of entities, EventType - Type of the event being notified about, EntityThis - Entity that caused the event to occur. Return Value: None Locks: The instances lock has to be held in either write or read mode as we are traversing the list of entities on the address family. --*/ { RTM_ENTITY_HANDLE EntityHandle; PADDRFAM_INFO AddrFamInfo; RTM_ENTITY_INFO EntityInfo; PENTITY_INFO Entity; UINT i; PLIST_ENTRY Entities, q; // // Prepare arguments for the Event Callbacks in loop // AddrFamInfo = EntityThis->OwningAddrFamily; EntityInfo.RtmInstanceId = AddrFamInfo->Instance->RtmInstanceId; EntityInfo.AddressFamily = AddrFamInfo->AddressFamily; EntityInfo.EntityId = EntityThis->EntityId; EntityHandle = MAKE_HANDLE_FROM_POINTER(EntityThis); // // For each entity in table, call its event callback // for (i = 0; i < ENTITY_TABLE_SIZE; i++) { Entities = &EntityTable[i]; for (q = Entities->Flink; q != Entities; q = q->Flink) { Entity = CONTAINING_RECORD(q, ENTITY_INFO, EntityTableLE); // // Inform the current entity of the event // if it has an event handler registered // if (Entity->EventCallback) { // // This callback should not call any of the registration // APIs as it might result in corrupting the entity list // Entity->EventCallback(MAKE_HANDLE_FROM_POINTER(Entity), EventType, EntityHandle, &EntityInfo); } } } } VOID CleanupAfterDeregister ( IN PENTITY_INFO Entity ) /*++ Routine Description: Cleans up all enums, notifications and entity lists opened by an entity. Also deletes all nexthops and routes owned by this entity. Assumes that the entity is not making any other operations in parallel. Arguments: Entity - Pointer to the entity registration info. Return Value: None --*/ { RTM_ENTITY_HANDLE RtmRegHandle; PADDRFAM_INFO AddrFamInfo; PHANDLE Handles; RTM_ENUM_HANDLE EnumHandle; UINT NumHandles, i; DWORD ChangeFlags; DWORD Status; AddrFamInfo = Entity->OwningAddrFamily; RtmRegHandle = MAKE_HANDLE_FROM_POINTER(Entity); #if DBG_HDL // ACQUIRE_OPEN_HANDLES_LOCK(Entity); while (!IsListEmpty(&Entity->OpenHandles)) { POPEN_HEADER OpenHeader; HANDLE OpenHandle; PLIST_ENTRY p; p = RemoveHeadList(&Entity->OpenHandles); OpenHeader = CONTAINING_RECORD(p, OPEN_HEADER, HandlesLE); OpenHandle = MAKE_HANDLE_FROM_POINTER(OpenHeader); switch (OpenHeader->HandleType) { case DEST_ENUM_TYPE: case ROUTE_ENUM_TYPE: case NEXTHOP_ENUM_TYPE: case LIST_ENUM_TYPE: Status = RtmDeleteEnumHandle(RtmRegHandle, OpenHandle); break; case NOTIFY_TYPE: Status = RtmDeregisterFromChangeNotification(RtmRegHandle, OpenHandle); break; case ROUTE_LIST_TYPE: Status = RtmDeleteRouteList(RtmRegHandle, OpenHandle); break; default: Status = ERROR_INVALID_DATA; } ASSERT(Status == NO_ERROR); } // RELEASE_OPEN_HANDLES_LOCK(Entity); #endif // DBG_HDL Handles = AllocMemory(AddrFamInfo->MaxHandlesInEnum * sizeof(HANDLE)); if ( Handles == NULL ) { return; } // // Delete all routes created by this entity regn // Status = RtmCreateRouteEnum(RtmRegHandle, NULL, RTM_VIEW_MASK_ANY, RTM_ENUM_OWN_ROUTES, NULL, 0, NULL, 0, &EnumHandle); while (Status == NO_ERROR) { NumHandles = AddrFamInfo->MaxHandlesInEnum; Status = RtmGetEnumRoutes(RtmRegHandle, EnumHandle, &NumHandles, Handles); for (i = 0; i < NumHandles; i++) { Status = RtmDeleteRouteToDest(RtmRegHandle, Handles[i], &ChangeFlags); ASSERT(Status == NO_ERROR); } } Status = RtmDeleteEnumHandle(RtmRegHandle, EnumHandle); ASSERT(Status == NO_ERROR); // // Delete all nexthops created by this entity regn // Status = RtmCreateNextHopEnum(RtmRegHandle, 0, NULL, &EnumHandle); while (Status == NO_ERROR) { NumHandles = AddrFamInfo->MaxHandlesInEnum; Status = RtmGetEnumNextHops(RtmRegHandle, EnumHandle, &NumHandles, Handles); for (i = 0; i < NumHandles; i++) { Status = RtmDeleteNextHop(RtmRegHandle, Handles[i], NULL); ASSERT(Status == NO_ERROR); } } Status = RtmDeleteEnumHandle(RtmRegHandle, EnumHandle); ASSERT(Status == NO_ERROR); return; }