/*++

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->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->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 = GetLastError();
                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 = GetLastError();
                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 = GetLastError();
                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;

                continue;
            }
            except(EXCEPTION_EXECUTE_HANDLER)
                {
                    Status = GetLastError();
                }

            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 = GetLastError();
                break;
            }

        //
        // Initialize the list of open handles and corresponding lock
        //

        try
        {
            InitializeCriticalSection(&Entity->OpenHandlesLock);

            Entity->HandlesLockInited = TRUE;
        }
        except(EXCEPTION_EXECUTE_HANDLER)
            {
                Status = GetLastError();
                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 = GetLastError();
                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 = GetLastError();
                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

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