/*++

Copyright (c) 1999, Microsoft Corporation

Module Name:

    qosminfo.c

Abstract:

    The file contains global and interface
    config functions for QOS Mgr protocol.

Revision History:

--*/

#include "pchqosm.h"

#pragma hdrstop

DWORD
WINAPI
QosmGetGlobalInfo (
    IN      PVOID                          GlobalInfo,
    IN OUT  PULONG                         BufferSize,
    OUT     PULONG                         InfoSize
    )

/*++
  
Routine Description:

    Returns the global config info for this protocol.

Arguments:

    See corr header file.

Return Value:
    
    Status of the operation
  
--*/

{
    PIPQOS_GLOBAL_CONFIG GlobalConfig;
    DWORD                Status;

    //
    // Validate all input params before reading the global info
    //

    if (BufferSize == NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    ACQUIRE_GLOBALS_READ_LOCK();

    do
    {
        *InfoSize = Globals.ConfigSize;

        if ((*BufferSize < *InfoSize) || 
            (GlobalInfo == NULL))
        {
            //
            // Either the size was too small or there was no storage
            //

            Trace1(CONFIG, 
                   "GetGlobalInfo: Buffer size too small: %u",
                   *BufferSize);

            *BufferSize = *InfoSize;

            Status = ERROR_INSUFFICIENT_BUFFER;

            break;
        }

        *BufferSize = *InfoSize;

        GlobalConfig = (PIPQOS_GLOBAL_CONFIG) GlobalInfo;

        CopyMemory(GlobalConfig,
                   Globals.GlobalConfig,
                   *InfoSize);

        Status = NO_ERROR;
    }
    while (FALSE);

    RELEASE_GLOBALS_READ_LOCK();

    return Status;
}


DWORD
WINAPI
QosmSetGlobalInfo (
    IN      PVOID                          GlobalInfo,
    IN      ULONG                          InfoSize
    )

/*++
  
Routine Description:

    Sets the global config info for this protocol.

Arguments:

    See corr header file.

Return Value:
    
    Status of the operation
  
--*/

{
    PIPQOS_GLOBAL_CONFIG GlobalConfig;
    DWORD                Status;

    //
    // Update the global config information.
    //

    ACQUIRE_GLOBALS_WRITE_LOCK();

    do
    {
        GlobalConfig = AllocMemory(InfoSize);

        if (GlobalConfig == NULL)
        {
            Status = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        //
        // Copy the new config information
        //

        CopyMemory(GlobalConfig, GlobalInfo, InfoSize);

        Globals.ConfigSize = InfoSize;

        //
        // Set up rest of the global state
        //

        if (GlobalConfig->LoggingLevel <= IPQOS_LOGGING_INFO)
        {
            Globals.LoggingLevel = GlobalConfig->LoggingLevel;
        }

        //
        // Cleanup old global information
        //

        if (Globals.GlobalConfig)
        {
            FreeMemory(Globals.GlobalConfig);
        }

        Globals.GlobalConfig = GlobalConfig;

        Status = NO_ERROR;
    }
    while (FALSE);

    RELEASE_GLOBALS_WRITE_LOCK();

    return Status;
}

DWORD
WINAPI
QosmGetInterfaceInfo (
    IN      QOSMGR_INTERFACE_ENTRY        *Interface,
    IN      PVOID                          InterfaceInfo,
    IN OUT  PULONG                         BufferSize,
    OUT     PULONG                         InfoSize
    )

/*++
  
Routine Description:

    Gets the inteface config info for this protocol
    for this interface.

Arguments:

    See corr header file.

Return Value:
    
    Status of the operation

--*/

{
    PIPQOS_IF_CONFIG InterfaceConfig;
    DWORD            Status;

    //
    // Validate all input params before reading interface info
    //

    if (BufferSize == NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    ACQUIRE_INTERFACE_READ_LOCK(Interface);

    do
    {
        *InfoSize = Interface->ConfigSize;

        if ((*BufferSize < *InfoSize) || 
            (InterfaceInfo == NULL))
        {
            //
            // Either the size was too small or there was no storage
            //

            Trace1(CONFIG, 
                   "GetInterfaceInfo: Buffer size too small: %u",
                   *BufferSize);

            *BufferSize = *InfoSize;

            Status = ERROR_INSUFFICIENT_BUFFER;

            break;
        }

        *BufferSize = *InfoSize;

        InterfaceConfig = (PIPQOS_IF_CONFIG) InterfaceInfo;

        CopyMemory(InterfaceConfig,
                   Interface->InterfaceConfig,
                   *InfoSize);

        Status = NO_ERROR;
    }
    while (FALSE);

    RELEASE_INTERFACE_READ_LOCK(Interface);

    return Status;
}

DWORD
WINAPI
QosmSetInterfaceInfo (
    IN      QOSMGR_INTERFACE_ENTRY        *Interface,
    IN      PVOID                          InterfaceInfo,
    IN      ULONG                          InfoSize
    )

/*++
  
Routine Description:

    Sets the interface config info for this protocol
    on this interface.

Arguments:

    See corr header file.

Return Value:
    
    Status of the operation
  
--*/

{
    PIPQOS_IF_CONFIG     InterfaceConfig;
    PIPQOS_IF_FLOW       FlowConfig;
    PQOSMGR_FLOW_ENTRY   Flow;
    UINT                 i;
    PLIST_ENTRY          p, q;
    PTC_GEN_FLOW         FlowInfo;
    ULONG                FlowSize;
    HANDLE               FlowHandle;
    DWORD                Status;

    //
    // Update the interface config information.
    //

    ACQUIRE_INTERFACE_WRITE_LOCK(Interface);

    do
    {
        //
        // Allocate memory to store new config
        //

        InterfaceConfig = AllocMemory(InfoSize);

        if (InterfaceConfig == NULL)
        {
            Status = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        //
        // Copy the new config information
        //

        CopyMemory(InterfaceConfig, InterfaceInfo, InfoSize);

        Interface->ConfigSize = InfoSize;

        //
        // Set up rest of interface state
        //

        if (Interface->State != InterfaceConfig->QosState)
        {
            if (InterfaceConfig->QosState == IPQOS_STATE_DISABLED)
            {
                //
                // Disable all flows on this interface
                //

                ;
            }
            else
            {
                //
                // Renable all flows on this interface
                //

                ;
            }

            Interface->State = InterfaceConfig->QosState;
        }

        //
        // Update the flow information on if
        //

        //
        // First mark all flows as needing refresh
        //

        for (p = Interface->FlowList.Flink;
             p != &Interface->FlowList;
             p = p->Flink)
        {
            Flow = CONTAINING_RECORD(p, QOSMGR_FLOW_ENTRY, OnInterfaceLE);

            ASSERT(!(Flow->Flags & FLOW_FLAG_DELETE));

            Flow->Flags |= FLOW_FLAG_DELETE;
        }

        //
        // If we do not have an TC interface handle,
        // we delete all flows as they are obsolete
        //

        if (Interface->TciIfHandle)
        {
            //
            // Set each flow if it has changed from before
            //

            FlowConfig = IPQOS_GET_FIRST_FLOW_ON_IF(InterfaceConfig);

            for (i = 0; i < InterfaceConfig->NumFlows; i++)
            {
                //
                // Search for a flow with the same name
                //

                for (p = Interface->FlowList.Flink;
                     p != &Interface->FlowList;
                     p = p->Flink)
                {
                    Flow = 
                        CONTAINING_RECORD(p, QOSMGR_FLOW_ENTRY, OnInterfaceLE);

                    if (!_wcsicmp(Flow->FlowName, FlowConfig->FlowName))
                    {
                        break;
                    }
                }

                if (p == &Interface->FlowList)
                {
                    //
                    // No flow by this name - add new one
                    //

                    Flow = NULL;
                }

                //
                // Get a flow info from description
                //

                Status = GetFlowFromDescription(&FlowConfig->FlowDesc, 
                                                &FlowInfo, 
                                                &FlowSize);

                if (Status == NO_ERROR)
                {
                    do
                    {
                        if ((Flow) && 
                            (FlowSize == Flow->FlowSize) &&
                            (EqualMemory(FlowInfo, Flow->FlowInfo, FlowSize)))
                        {
                            //
                            // No change in the flow info yet,
                            // this flow still remains valid
                            //

                            Flow->Flags &= ~FLOW_FLAG_DELETE;

                            Status = ERROR_ALREADY_EXISTS;

                            break;
                        }

                        if (Flow)
                        {
                            //
                            // Flow info changed - modify flow
                            //

                            Status = TcModifyFlow(Flow->TciFlowHandle,
                                                  FlowInfo);

                            if (Status != NO_ERROR)
                            {
                                break;
                            }

                            Flow->Flags &= ~FLOW_FLAG_DELETE;

                            //
                            // Update cached flow info
                            //

                            FreeMemory(Flow->FlowInfo);
                            Flow->FlowInfo = FlowInfo;
                            Flow->FlowSize = FlowSize;
                        }
                        else
                        {
                            //
                            // Add the new flow using the TC API
                            //

                            Status = TcAddFlow(Interface->TciIfHandle,
                                               NULL,
                                               0,
                                               FlowInfo,
                                               &FlowHandle);

                            if (Status != NO_ERROR)
                            {
                                break;
                            }

                            //
                            // Addition of a new flow in TC
                            //

                            Flow = AllocMemory(sizeof(QOSMGR_FLOW_ENTRY));

                            if (Flow == NULL)
                            {
                                Status = TcDeleteFlow(FlowHandle);

                                ASSERT(Status);

                                Status = ERROR_NOT_ENOUGH_MEMORY;

                                break;
                            }

                            //
                            // Initialize flow and insert in list
                            //

                            Flow->TciFlowHandle = FlowHandle;

                            Flow->Flags = 0;

                            Flow->FlowInfo = FlowInfo;
                            Flow->FlowSize = FlowSize;

                            wcscpy(Flow->FlowName, FlowConfig->FlowName);

                            InsertTailList(p, &Flow->OnInterfaceLE);
                        }
                    }
                    while (FALSE);
                    
                    if (Status != NO_ERROR)
                    {
                        FreeMemory(FlowInfo);
                    }
                }

                //
                // Move to the next flow in config
                //

                FlowConfig = IPQOS_GET_NEXT_FLOW_ON_IF(FlowConfig);
            }
        }

        //
        // Cleanup all flows that are obsolete
        //

        for (p = Interface->FlowList.Flink;
             p != &Interface->FlowList; 
             p = q)
        {
            Flow = CONTAINING_RECORD(p, QOSMGR_FLOW_ENTRY, OnInterfaceLE);

            q = p->Flink;

            if (Flow->Flags & FLOW_FLAG_DELETE)
            {
                //
                // Delete the flow from the TC API
                //

                Status = TcDeleteFlow(Flow->TciFlowHandle);

                if (Status != NO_ERROR)
                {
                    Flow->Flags &= ~FLOW_FLAG_DELETE;

                    continue;
                }

                //
                // Remove flow from this flow list
                //

                RemoveEntryList(p);

                //
                // Free the flow and its resources
                //

                if (Flow->FlowInfo)
                {
                    FreeMemory(Flow->FlowInfo);
                }

                FreeMemory(Flow);
            }
        }

        //
        // Cleanup old interface information
        //

        if (Interface->InterfaceConfig)
        {
            FreeMemory(Interface->InterfaceConfig);
        }

        Interface->InterfaceConfig = InterfaceConfig;

        Status = NO_ERROR;
    }
    while (FALSE);

    RELEASE_INTERFACE_WRITE_LOCK(Interface);

    return Status;
}


DWORD
GetFlowFromDescription(
    IN      PIPQOS_NAMED_FLOW              FlowDesc,
    OUT     PTC_GEN_FLOW                  *FlowInfo,
    OUT     ULONG                         *FlowSize
    )
{    
    FLOWSPEC       *CurrFlowspec;
    FLOWSPEC        SendFlowspec;
    FLOWSPEC        RecvFlowspec;
    FLOWSPEC       *Flowspec;
    PTC_GEN_FLOW    Flow;
    QOS_OBJECT_HDR *QosObject;
    PWCHAR          FlowspecName;
    PWCHAR          QosObjectName;
    PUCHAR          CopyAtPtr;
    ULONG           ObjectsLength;
    ULONG           i;

#if 1
    //
    // Check for the existence of sending flowspec
    //

    if (FlowDesc->SendingFlowspecName[0] == L'\0')
    {
        return ERROR_INVALID_DATA;
    }
#endif

    //
    // Get the sending and receiving flowspecs
    //

    for (i = 0; i < 2; i++)
    {
        if (i)
        {
            FlowspecName = FlowDesc->RecvingFlowspecName;
            CurrFlowspec = &RecvFlowspec;
        }
        else
        {
            FlowspecName = FlowDesc->SendingFlowspecName;
            CurrFlowspec = &SendFlowspec;
        }

        FillMemory(CurrFlowspec, sizeof(FLOWSPEC), QOS_NOT_SPECIFIED);

        if (FlowspecName[0] != L'\0')
        {
            Flowspec = GetFlowspecFromGlobalConfig(FlowspecName);

            if (Flowspec == NULL)
            {
                return ERROR_INVALID_DATA;
            }

            *CurrFlowspec = *Flowspec;
        }
    }

    //
    // Calculate the size of the TC_GEN_FLOW block
    //

    QosObjectName = IPQOS_GET_FIRST_OBJECT_NAME_ON_NAMED_FLOW(FlowDesc);

    ObjectsLength = 0;

    for (i = 0; i < FlowDesc->NumTcObjects; i++)
    {
        //
        // Get object's description in global info
        //

        QosObject = GetQosObjectFromGlobalConfig(QosObjectName);

        if (QosObject == NULL)
        {
            //
            // Incomplete description
            //

            return ERROR_INVALID_DATA;
        }

        ObjectsLength += QosObject->ObjectLength;

        QosObjectName= IPQOS_GET_NEXT_OBJECT_NAME_ON_NAMED_FLOW(QosObjectName);
    }

    *FlowSize = FIELD_OFFSET(TC_GEN_FLOW, TcObjects) + ObjectsLength;

    *FlowInfo = Flow = AllocMemory(*FlowSize);

    if (Flow == NULL)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    //
    // Fill in the flow information now
    //

    Flow->ReceivingFlowspec = RecvFlowspec;

    Flow->SendingFlowspec = SendFlowspec;

    Flow->TcObjectsLength = ObjectsLength;

    //
    // Repeat the loop above filling info
    //

    QosObjectName = IPQOS_GET_FIRST_OBJECT_NAME_ON_NAMED_FLOW(FlowDesc);

    CopyAtPtr = (PUCHAR) &Flow->TcObjects[0];

    for (i = 0; i < FlowDesc->NumTcObjects; i++)
    {
        //
        // Get object's description in global info
        //

        QosObject = GetQosObjectFromGlobalConfig(QosObjectName);

        // We just checked above for its existence
        ASSERT(QosObject != NULL);

        CopyMemory(CopyAtPtr,
                   QosObject, 
                   QosObject->ObjectLength);

        CopyAtPtr += QosObject->ObjectLength;

        QosObjectName= IPQOS_GET_NEXT_OBJECT_NAME_ON_NAMED_FLOW(QosObjectName);
    }

     return NO_ERROR;
}

FLOWSPEC *
GetFlowspecFromGlobalConfig(
    IN      PWCHAR                         FlowspecName
    )
{
    IPQOS_NAMED_FLOWSPEC *Flowspec;
    UINT                  i;

    Flowspec = IPQOS_GET_FIRST_FLOWSPEC_IN_CONFIG(Globals.GlobalConfig);

    for (i = 0; i < Globals.GlobalConfig->NumFlowspecs; i++)
    {
        if (!_wcsicmp(Flowspec->FlowspecName, FlowspecName))
        {
            break;
        }

        Flowspec = IPQOS_GET_NEXT_FLOWSPEC_IN_CONFIG(Flowspec);
    }

    if (i < Globals.GlobalConfig->NumFlowspecs)
    {
        return &Flowspec->FlowspecDesc;
    }

    return NULL;
}

QOS_OBJECT_HDR *
GetQosObjectFromGlobalConfig(
    IN      PWCHAR                         QosObjectName
    )
{
    IPQOS_NAMED_QOSOBJECT *QosObject;
    UINT                   i;

    QosObject = IPQOS_GET_FIRST_QOSOBJECT_IN_CONFIG(Globals.GlobalConfig);

    for (i = 0; i < Globals.GlobalConfig->NumQosObjects; i++)
    {
        if (!_wcsicmp(QosObject->QosObjectName, QosObjectName))
        {
            break;
        }

        QosObject = IPQOS_GET_NEXT_QOSOBJECT_IN_CONFIG(QosObject);
    }

    if (i < Globals.GlobalConfig->NumFlowspecs)
    {
        return &QosObject->QosObjectHdr;
    }

    return NULL;
}