OVS_ARGUMENT* CreateArgumentStringA_Alloc(OVS_ARGTYPE argType, const char* buffer)
{
    OVS_ARGUMENT* pArg = NULL;
    VOID* newBuffer = NULL;
    UINT16 size = 0;

    pArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pArg)
    {
        return NULL;
    }

    size = (UINT16)strlen(buffer) + 1;

    newBuffer = KZAlloc(size);
    if (!newBuffer)
    {
        return NULL;
    }

    RtlCopyMemory(newBuffer, buffer, size);

    pArg->type = argType;
    pArg->isDisabled = FALSE;
    pArg->isNested = FALSE;
    pArg->freeData = TRUE;

    pArg->length = size;
    pArg->data = newBuffer;

    return pArg;
}
static BOOLEAN _CreateActionsGroupToList(OVS_ARGTYPE groupType, const OVS_ARGUMENT_GROUP* pArgGroup, OVS_ARGUMENT_SLIST_ENTRY** ppArgList)
{
    OVS_ARGUMENT_SLIST_ENTRY* pCurListArg = NULL, *pHeadArg = NULL;
    OVS_ARGUMENT* pGroupArg = NULL;
    BOOLEAN ok = TRUE;

    pHeadArg = KZAlloc(sizeof(OVS_ARGUMENT_SLIST_ENTRY));
    if (!pHeadArg)
    {
        return FALSE;
    }

    pCurListArg = pHeadArg;
    ok = _CreateActionsArgsToList(pArgGroup, &pCurListArg);
    if (!ok)
    {
        return FALSE;
    }

    pGroupArg = CreateGroupArgFromList(groupType, &pHeadArg);
    if (!pGroupArg)
    {
        KFree(pHeadArg);
        return FALSE;
    }

    ok = AppendArgumentToList(pGroupArg, ppArgList);
    if (!ok)
    {
        return FALSE;
    }

    return TRUE;
}
BOOLEAN CopyArgument(_Out_ OVS_ARGUMENT* pDest, _In_ const OVS_ARGUMENT* pSource)
{
    OVS_CHECK(pDest);
    OVS_CHECK(pSource);

    pDest->type = pSource->type;
    pDest->length = pSource->length;
    pDest->isDisabled = pSource->isDisabled;
    pDest->isNested = pSource->isNested;
    pDest->freeData = pSource->freeData;

    if (pDest->length)
    {
        pDest->data = KZAlloc(pDest->length);
        if (!pDest->data)
        {
            return FALSE;
        }
    }

    if (IsArgTypeGroup(pDest->type))
    {
        if (!CopyArgumentGroup(pDest->data, pSource->data, /*args more*/0))
        {
            DestroyArgumentGroup(pDest->data);
        }
    }
    else
    {
        RtlCopyMemory(pDest->data, pSource->data, pDest->length);
    }

    return TRUE;
}
OVS_ARGUMENT* CreateArgFromPacketInfo(const OVS_OFPACKET_INFO* pPacketInfo, const OVS_OFPACKET_INFO* pMask, UINT16 groupType)
{
    OVS_ARGUMENT_SLIST_ENTRY* pList = NULL;
    UINT16 count = 0;
    UINT size = 0;
    OVS_ARGUMENT* args = NULL, *pResult = NULL;
    OVS_ARGUMENT_GROUP* pArgGroup = NULL;

    pList = _CreateArgListFromPacketInfo(pPacketInfo, pMask);
    if (!pList)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " _CreateArgListFromPacketInfo failed\n");
        return NULL;
    }

    args = ArgumentListToArray(pList, &count, &size);
    if (!args)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " could not convert arg list to array\n");
        DestroyOrFreeArgList(&pList, /*destroy*/ TRUE);
        return NULL;
    }

    OVS_CHECK(size <= MAXUINT16);

    pArgGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pArgGroup)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed allocating arg group\n");

        DestroyOrFreeArgList(&pList, /*destroy*/ TRUE);
        KFree(args);
        return NULL;
    }

    pArgGroup->args = args;
    pArgGroup->count = count;
    pArgGroup->groupSize = (UINT16)size;

    pResult = CreateArgumentFromGroup(groupType, pArgGroup);
    if (!pResult)
    {
        DEBUGP(LOG_ERROR, "CreateArgumentFromGroup failed\n");
        DestroyOrFreeArgList(&pList, /*destroy*/ TRUE);
        KFree(args);
        return NULL;
    }

    DBGPRINT_ARG(LOG_INFO, pResult, 0, 0);

    DestroyOrFreeArgList(&pList, /*destroy*/ FALSE);

    VerifyGroup_Size_Recursive(pArgGroup);

    return pResult;
}
OVS_ARGUMENT_GROUP* CreateGroupFromArgArray(OVS_ARGUMENT* argArray, UINT16 countArgs, UINT16 totalSize)
{
    OVS_ARGUMENT_GROUP* pArgGroup = NULL;

    pArgGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    EXPECT(pArgGroup);

    pArgGroup->args = argArray;
    pArgGroup->count = countArgs;
    pArgGroup->groupSize = (UINT16)totalSize;

    return pArgGroup;
}
static BOOLEAN _SampleActionToList(const OVS_ARGUMENT_GROUP* pArgGroup, OVS_ARGUMENT_SLIST_ENTRY** ppArgList)
{
    BOOLEAN ok = TRUE;

    OVS_ARGUMENT_SLIST_ENTRY* pCurListArg = NULL, *pHeadArg = NULL;

    pHeadArg = KZAlloc(sizeof(OVS_ARGUMENT_SLIST_ENTRY));
    if (!pHeadArg)
    {
        return FALSE;
    }

    pCurListArg = pHeadArg;
    OVS_ARGUMENT* pGroupArg = NULL;

    for (UINT i = 0; i < pArgGroup->count; ++i)
    {
        const OVS_ARGUMENT* pArg = pArgGroup->args + i;
        OVS_ARGTYPE argType = pArg->type;

        switch (argType)
        {
        case OVS_ARGTYPE_ACTION_SAMPLE_PROBABILITY:
            if (!CreateArgInList_WithSize(OVS_ARGTYPE_ACTION_SAMPLE_PROBABILITY, pArg->data, pArg->length, &pCurListArg))
            {
                return FALSE;
            }

            break;

        case OVS_ARGTYPE_ACTION_SAMPLE_ACTIONS_GROUP:
            _CreateActionsGroupToList(OVS_ARGTYPE_ACTION_SAMPLE_ACTIONS_GROUP, pArg->data, &pCurListArg);
            break;
        }
    }

    pGroupArg = CreateGroupArgFromList(OVS_ARGTYPE_ACTION_SAMPLE_GROUP, &pHeadArg);
    if (!pGroupArg)
    {
        KFree(pHeadArg);
        return FALSE;
    }

    ok = AppendArgumentToList(pGroupArg, ppArgList);
    if (!ok)
    {
        DestroyArgument(pGroupArg);
    }

    return ok;
}
BOOLEAN SetArgument_Alloc(_Inout_ OVS_ARGUMENT* pArg, OVS_ARGTYPE argType, const VOID* buffer)
{
    UINT expectedSize = 0;
    VOID* newBuffer = NULL;

    OVS_CHECK(pArg);

    if (!GetArgumentExpectedSize(argType, &expectedSize))
    {
        return FALSE;
    }

    if (expectedSize == MAXUINT)
    {
        return FALSE;
    }

    OVS_CHECK(expectedSize <= MAXUINT16);

    if (expectedSize)
    {
        newBuffer = KZAlloc(expectedSize);
        if (!newBuffer)
        {
            return FALSE;
        }

        RtlCopyMemory(newBuffer, buffer, expectedSize);
    }

    DEBUGP_ARG(LOG_INFO, "Set argument: %p; type=%u\n", pArg, argType);
    DBGPRINT_ARGTYPE(LOG_INFO, argType, "", 0);

    pArg->type = argType;
    pArg->isDisabled = FALSE;
    pArg->isNested = FALSE;
    pArg->freeData = TRUE;

    pArg->length = (UINT16)expectedSize;
    pArg->data = (VOID*)newBuffer;

    return TRUE;
}
OVS_ARGUMENT* CreateArgumentStringA(OVS_ARGTYPE argType, const char* buffer)
{
    OVS_ARGUMENT* pArg;

    pArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pArg)
    {
        return NULL;
    }

    pArg->type = argType;
    pArg->isDisabled = FALSE;
    pArg->isNested = FALSE;
    pArg->freeData = FALSE;

    pArg->length = (UINT16)strlen(buffer) + 1;
    pArg->data = (VOID*)buffer;

    return pArg;
}
OVS_ARGUMENT* CreateArgumentFromGroup(OVS_ARGTYPE argType, const OVS_ARGUMENT_GROUP* pData)
{
    OVS_ARGUMENT* pArg = NULL;

    pArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pArg)
    {
        return NULL;
    }

    pArg->type = argType;
    pArg->isDisabled = FALSE;
    pArg->isNested = FALSE;
    pArg->freeData = TRUE;

    pArg->length = pData->groupSize + OVS_ARGUMENT_GROUP_HEADER_SIZE;
    pArg->data = (VOID*)pData;

    return pArg;
}
OVS_ARGUMENT* CreateArgumentWithSize(OVS_ARGTYPE argType, const VOID* buffer, ULONG size)
{
    OVS_ARGUMENT* pArg = NULL;
    UINT expectedSize = 0;

    OVS_CHECK(size > 0);
    OVS_CHECK(size <= MAXUINT);

    pArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pArg)
    {
        return NULL;
    }

    DEBUGP_ARG(LOG_INFO, "Created argument: %p; type=%u\n", pArg, argType);
    DBGPRINT_ARGTYPE(LOG_INFO, argType, "", 0);

    pArg->type = argType;
    pArg->isDisabled = FALSE;
    pArg->isNested = FALSE;
    pArg->freeData = FALSE;

    if (!GetArgumentExpectedSize(argType, &expectedSize))
    {
        return NULL;
    }

    if (expectedSize != MAXUINT)
    {
        DEBUGP_ARG(LOG_ERROR, "Create arg with size should only be used with var-sized args!");
        return NULL;
    }

    pArg->length = (UINT16)size;
    pArg->data = (VOID*)buffer;

    return pArg;
}
static OVS_ARGUMENT* _CreateSetActionArg(const OVS_ARGUMENT* pArgument)
{
    const OVS_ARGUMENT_GROUP* pGroupArg = NULL;
    OVS_ARGTYPE argType = OVS_ARGTYPE_INVALID;

    OVS_CHECK(IsArgTypeGroup(pArgument->type));
    pGroupArg = pArgument->data;

    OVS_CHECK(pGroupArg->count == 1);
    pArgument = pGroupArg->args;
    argType = pArgument->type;

    switch (argType)
    {
    case OVS_ARGTYPE_PI_IPV4_TUNNEL:
    {
        OVS_ARGUMENT* pArg = _CreateIpv4TunnelGroup(pArgument->data);
        return pArg;
    }
        break;

    default:
    {
        OVS_ARGUMENT* pPacketInfoArg = KZAlloc(sizeof(OVS_ARGUMENT));
        if (!pPacketInfoArg)
        {
            DEBUGP(LOG_ERROR, "could not alloc key arg\n");
            return NULL;
        }

        CopyArgument(pPacketInfoArg, pArgument);

        return pPacketInfoArg;
    }
        break;
    }
}
OVS_ARGUMENT* CreateArgument_Alloc(OVS_ARGTYPE argType, const VOID* buffer)
{
    UINT expectedSize = 0;
    VOID* newBuffer = NULL;
    OVS_ARGUMENT* pArg = NULL;

    if (!GetArgumentExpectedSize(argType, &expectedSize))
    {
        return NULL;
    }

    if (expectedSize == MAXUINT)
    {
        return NULL;
    }

    if (expectedSize)
    {
        newBuffer = KZAlloc(expectedSize);
        if (!newBuffer)
        {
            return NULL;
        }

        RtlCopyMemory(newBuffer, buffer, expectedSize);
    }

    pArg = CreateArgument(argType, newBuffer);
    if (pArg)
    {
        pArg->isNested = FALSE;
        pArg->freeData = TRUE;
    }

    return pArg;
}
OVS_ARGUMENT* CreateArgument(OVS_ARGTYPE argType, const VOID* buffer)
{
    OVS_ARGUMENT* pArg = NULL;
    UINT expectedSize = 0;

    pArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pArg)
    {
        return NULL;
    }

    DEBUGP_ARG(LOG_INFO, "Created argument: %p; type=%u\n", pArg, argType);
    DBGPRINT_ARGTYPE(LOG_INFO, argType, "", 0);

    pArg->type = argType;
    pArg->isDisabled = FALSE;
    pArg->isNested = FALSE;
    pArg->freeData = FALSE;

    if (!GetArgumentExpectedSize(argType, &expectedSize))
    {
        return NULL;
    }

    if (expectedSize == MAXUINT)
    {
        return NULL;
    }

    OVS_CHECK(expectedSize <= MAXUINT16);

    pArg->length = (UINT16)expectedSize;
    pArg->data = (VOID*)buffer;

    return pArg;
}
static BOOLEAN _CreateActionsArgsToList(const OVS_ARGUMENT_GROUP* pArgGroup, OVS_ARGUMENT_SLIST_ENTRY** ppArgList)
{
    BOOLEAN ok = TRUE;

    for (UINT i = 0; i < pArgGroup->count; ++i)
    {
        const OVS_ARGUMENT* pArg = pArgGroup->args + i;
        OVS_ARGTYPE argType = pArg->type;

        switch (argType)
        {
        case OVS_ARGTYPE_ACTION_SETINFO_GROUP:
        {
            OVS_ARGUMENT_GROUP* pSetGroup = NULL;
            OVS_ARGUMENT* pPacketInfoArg = NULL, *pSetArg = NULL;

            pPacketInfoArg = _CreateSetActionArg(pArg);
            if (!pPacketInfoArg)
            {
                return FALSE;
            }

            pSetGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
            if (!pSetGroup)
            {
                return FALSE;
            }

            pSetGroup->args = pPacketInfoArg;
            pSetGroup->count = 1;
            pSetGroup->groupSize = pPacketInfoArg->length + OVS_ARGUMENT_HEADER_SIZE;

            pSetArg = KZAlloc(sizeof(OVS_ARGUMENT));
            pSetArg->data = pSetGroup;
            pSetArg->type = OVS_ARGTYPE_ACTION_SETINFO_GROUP;
            pSetArg->length = pSetGroup->groupSize + OVS_ARGUMENT_GROUP_HEADER_SIZE;

            if (!AppendArgumentToList(pSetArg, ppArgList))
            {
                return FALSE;
            }
        }
            break;

        case OVS_ARGTYPE_ACTION_SAMPLE_GROUP:
            ok = _SampleActionToList(pArg->data, ppArgList);
            if (!ok)
            {
                return FALSE;
            }

            break;

        case OVS_ARGTYPE_ACTION_UPCALL_GROUP:
        {
            OVS_ARGUMENT_GROUP* pUpcallGroup = NULL;
            OVS_ARGUMENT* pUpcallArg = NULL;
            BOOLEAN ok = TRUE;

            pUpcallGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
            if (NULL == pUpcallGroup)
            {
                return FALSE;
            }

            ok = CopyArgumentGroup(pUpcallGroup, pArg->data, /*actionsToAdd*/0);
            if (!ok)
            {
                DestroyArgumentGroup(pUpcallGroup);
                return FALSE;
            }

            pUpcallArg = CreateArgumentFromGroup(argType, pUpcallGroup);

            if (!AppendArgumentToList(pUpcallArg, ppArgList))
            {
                return FALSE;
            }
        }
            break;

        default:
        {
            OVS_ARGUMENT* pDestArg = KAlloc(sizeof(OVS_ARGUMENT));
            if (!pDestArg)
            {
                return FALSE;
            }

            CopyArgument(pDestArg, pArg);

            if (!AppendArgumentToList(pDestArg, ppArgList))
            {
                return FALSE;
            }
        }
            break;
        }
    }

    return TRUE;
}
static OVS_ARGUMENT* _CreateIpv4TunnelGroup(const OF_PI_IPV4_TUNNEL* pTunnelInfo)
{
    OVS_ARGUMENT_GROUP* pTunnelGroup = NULL;
    OVS_ARGUMENT_SLIST_ENTRY* pArgListCur = NULL;
    OVS_ARGUMENT_SLIST_ENTRY* pArgHead = NULL;
    OVS_ARGUMENT* argArray = NULL, *pTunnelArg = NULL;
    UINT16 countArgs = 0;
    UINT totalSize = 0;
    BOOLEAN ok = TRUE;

    pArgListCur = KZAlloc(sizeof(OVS_ARGUMENT_SLIST_ENTRY));
    if (!pArgListCur)
    {
        return FALSE;
    }

    pArgHead = pArgListCur;
    pArgHead->pArg = NULL;

    if (pTunnelInfo->tunnelFlags & OVS_TUNNEL_FLAG_KEY)
    {
        if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_ID, &pTunnelInfo->tunnelId, &pArgListCur))
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    if (pTunnelInfo->tunnelFlags & OVS_TUNNEL_FLAG_DONT_FRAGMENT)
    {
        if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_DONT_FRAGMENT, NULL, &pArgListCur))
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    if (pTunnelInfo->tunnelFlags & OVS_TUNNEL_FLAG_CHECKSUM)
    {
        if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_CHECKSUM, NULL, &pArgListCur))
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    //ipv4 addr 0.0.0.0 is invalid
    if (pTunnelInfo->ipv4Source)
    {
        if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_IPV4_SRC, &pTunnelInfo->ipv4Source, &pArgListCur))
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    //ipv4 addr 0.0.0.0 is invalid
    if (pTunnelInfo->ipv4Destination)
    {
        if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_IPV4_DST, &pTunnelInfo->ipv4Destination, &pArgListCur))
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    //ipv4 TOS 0x00 is invalid!
    if (pTunnelInfo->ipv4TypeOfService)
    {
        if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_TOS, &pTunnelInfo->ipv4TypeOfService, &pArgListCur))
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    if (!CreateArgInList(OVS_ARGTYPE_PI_TUNNEL_TTL, &pTunnelInfo->ipv4TimeToLive, &pArgListCur))
    {
        ok = FALSE;
        goto Cleanup;
    }

    //OVS_ARGUMENT-s
    argArray = ArgumentListToArray(pArgHead, &countArgs, &totalSize);

    //OVS_ARGUMENT_GROUP
    pTunnelGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pTunnelGroup)
    {
        return NULL;
    }

    pTunnelGroup->args = argArray;
    pTunnelGroup->count = countArgs;
    pTunnelGroup->groupSize = (UINT16)totalSize;

    //parent OVS_ARGUMENT
    pTunnelArg = KAlloc(sizeof(OVS_ARGUMENT));
    if (!pTunnelArg)
    {
        ok = FALSE;
        goto Cleanup;
    }

    pTunnelArg->data = pTunnelGroup;
    pTunnelArg->length = pTunnelGroup->groupSize + OVS_ARGUMENT_GROUP_HEADER_SIZE;
    pTunnelArg->type = OVS_ARGTYPE_PI_TUNNEL_GROUP;

    VerifyGroup_Size_Recursive(pTunnelArg->data);

Cleanup:

    if (ok)
    {
        DestroyOrFreeArgList(&pArgHead, /*destroy*/ FALSE);
    }
    else
    {
        DestroyOrFreeArgList(&pArgHead, /*destroy*/ TRUE);
        KFree(argArray);
        KFree(pTunnelGroup);
        KFree(pTunnelArg);

        return NULL;
    }

    return pTunnelArg;
}
static OVS_ARGUMENT* _CreateFlowActionsGroup(const OVS_ARGUMENT_GROUP* pActions)
{
    OVS_ARGUMENT_GROUP* pActionsGroup = NULL;
    OVS_ARGUMENT* argArray = NULL, *pActionsArg = NULL;

    OVS_ARGUMENT_SLIST_ENTRY* pArgListCur = NULL;
    OVS_ARGUMENT_SLIST_ENTRY* pArgHead = NULL;

    BOOLEAN ok = TRUE;
    UINT16 countArgs = 0;
    UINT totalSize = 0;

    pArgListCur = KZAlloc(sizeof(OVS_ARGUMENT_SLIST_ENTRY));
    if (!pArgListCur)
    {
        return FALSE;
    }

    pArgHead = pArgListCur;
    pArgHead->pArg = NULL;
    pArgHead->pNext = NULL;

    if (pActions->count > 0)
    {
        ok = _CreateActionsArgsToList(pActions, &pArgListCur);
        if (!ok)
        {
            goto Cleanup;
        }

        argArray = ArgumentListToArray(pArgHead, &countArgs, &totalSize);
        if (!argArray)
        {
            ok = FALSE;
            goto Cleanup;
        }
    }

    pActionsGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pActionsGroup)
    {
        ok = FALSE;
        goto Cleanup;
    }

    pActionsGroup->args = argArray;
    pActionsGroup->count = countArgs;
    pActionsGroup->groupSize = (UINT16)totalSize;

    pActionsArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pActionsArg)
    {
        ok = FALSE;
        goto Cleanup;
    }

    pActionsArg->data = pActionsGroup;
    pActionsArg->length = pActionsGroup->groupSize + OVS_ARGUMENT_GROUP_HEADER_SIZE;
    pActionsArg->type = OVS_ARGTYPE_FLOW_ACTIONS_GROUP;

Cleanup:
    if (ok)
    {
        DestroyOrFreeArgList(&pArgHead, /*destroy*/ FALSE);
    }
    else
    {
        DestroyOrFreeArgList(&pArgHead, /*destroy*/ TRUE);
        KFree(argArray);
        KFree(pActionsGroup);
        KFree(pActionsArg);

        return NULL;
    }

    return pActionsArg;
}
//TODO: should we put OVS_MESSAGE_FLAG_MULTIPART for Flow_Dump?
BOOLEAN CreateMsgFromFlow(_In_ const OVS_FLOW* pFlow, UINT8 command, _Inout_ OVS_MESSAGE* pMsg, UINT32 sequence, UINT32 dpIfIndex, UINT32 portId)
{
    OVS_ARGUMENT_GROUP* pFlowGroup = NULL;
    OVS_ARGUMENT* pPIArg, *pMasksArg, *pTimeUsedArg, *pFlowStats, *pTcpFlags, *pActionsArg;
    BOOLEAN ok = TRUE;
    UINT16 flowArgCount = 0;
    UINT16 curArg = 0;
    OVS_WINL_FLOW_STATS winlStats = { 0 };
    OVS_FLOW_STATS stats = { 0 };
    UINT64 tickCount = 0;
    UINT8 tcpFlags = 0;
    UINT16 argsDataSize = 0;
    LOCK_STATE_EX lockState = { 0 };

    OVS_OFPACKET_INFO unmaskedPacketInfo = { 0 };
    OVS_OFPACKET_INFO maskedPacketInfo = { 0 };
    OVS_OFPACKET_INFO packetInfoMask = { 0 };

    OVS_CHECK(pMsg);

    pPIArg = pMasksArg = pTimeUsedArg = pFlowStats = pTcpFlags = pActionsArg = NULL;

    FLOW_LOCK_READ(pFlow, &lockState);

    unmaskedPacketInfo = pFlow->unmaskedPacketInfo;
    maskedPacketInfo = pFlow->maskedPacketInfo;
    packetInfoMask = pFlow->pMask->packetInfo;

#if OVS_VERSION == OVS_VERSION_1_11
    tickCount = pFlow->stats.lastUsedTime;
    stats.noOfMatchedPackets = pFlow->stats.packetsMached;
    stats.noOfMatchedBytes = pFlow->stats.bytesMatched;
    tcpFlags = pFlow->stats.tcpFlags;
#elif OVS_VERSION == OVS_VERSION_2_3
    //TODO: Flow_GetStats()
    Flow_GetStats_Unsafe(pFlow, &stats);
    winlStats.noOfMatchedBytes = stats.bytesMatched;
    winlStats.noOfMatchedPackets = stats.packetsMached;
#endif

    FLOW_UNLOCK(pFlow, &lockState);

    //2. INIT OVS_MESSAGE
    pMsg->length = sizeof(OVS_MESSAGE);
    pMsg->type = OVS_MESSAGE_TARGET_FLOW;
    pMsg->flags = 0;
    pMsg->sequence = sequence;
    pMsg->pid = portId;

    pMsg->command = command;
    pMsg->version = 1;
    pMsg->reserved = 0;

    pMsg->dpIfIndex = dpIfIndex;

    //3. OVS_ARGUMENT_GROUP
    pFlowGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pFlowGroup)
    {
        return FALSE;
    }

    //3.1. Packet Info
    pPIArg = CreateArgFromPacketInfo(&unmaskedPacketInfo, NULL, OVS_ARGTYPE_FLOW_PI_GROUP);
    if (!pPIArg)
    {
        ok = FALSE;
        goto Cleanup;
    }

    argsDataSize += pPIArg->length;
    ++curArg;

    //3.2. Packet Info Mask
    pMasksArg = CreateArgFromPacketInfo(&maskedPacketInfo, &packetInfoMask, OVS_ARGTYPE_FLOW_MASK_GROUP);
    if (!pMasksArg)
    {
        ok = FALSE;
        goto Cleanup;
    }

    argsDataSize += pMasksArg->length;
    ++curArg;

    //3.3. Flow Time Used
    if (tickCount > 0)
    {
        UINT64 usedTimeInMs = 0, curTimeInMs = 0;

        usedTimeInMs = _TicksToMiliseconds(tickCount);
        curTimeInMs = _TicksToMiliseconds(KeQueryPerformanceCounter(NULL).QuadPart);

        pTimeUsedArg = CreateArgument_Alloc(OVS_ARGTYPE_FLOW_TIME_USED, &usedTimeInMs);
        if (!pTimeUsedArg)
        {
            ok = FALSE;
            goto Cleanup;
        }

        argsDataSize += pTimeUsedArg->length;
        ++curArg;
    }

    //3.4. Flow Stats
    if (winlStats.noOfMatchedPackets > 0)
    {
        pFlowStats = CreateArgument_Alloc(OVS_ARGTYPE_FLOW_STATS, &stats);
        if (!pFlowStats)
        {
            ok = FALSE;
            goto Cleanup;
        }

        argsDataSize += pFlowStats->length;
        ++curArg;
    }

    //3.5. Flow Tcp Flags
    if (tcpFlags)
    {
        pTcpFlags = CreateArgument_Alloc(OVS_ARGTYPE_FLOW_TCP_FLAGS, &tcpFlags);
        if (!pTcpFlags)
        {
            ok = FALSE;
            goto Cleanup;
        }

        argsDataSize += pTcpFlags->length;
        ++curArg;
    }

    FLOW_LOCK_READ(pFlow, &lockState);
    //NOTE: we don't need to use OVS_REFERENCE for pFlow->pActions here
    //because the actions cannot be deleted while under the lock of pFlow
    //pFlow is here referenced, so it and its Actions cannot be deleted
    pActionsArg = _CreateFlowActionsGroup(pFlow->pActions->pActionGroup);
    FLOW_UNLOCK(pFlow, &lockState);

    if (!pActionsArg)
    {
        return FALSE;
    }

    DBGPRINT_ARG(LOG_INFO, pActionsArg, 0, 0);

    argsDataSize += pActionsArg->length;
    ++curArg;

    flowArgCount = curArg;
    if (!AllocateArgumentsToGroup(flowArgCount, pFlowGroup))
    {
        ok = FALSE;
        goto Cleanup;
    }

    pFlowGroup->args[0] = *pPIArg;
    pFlowGroup->args[1] = *pMasksArg;

    curArg = 2;

    if (pTimeUsedArg)
    {
        pFlowGroup->args[curArg] = *pTimeUsedArg;
        curArg++;
    }

    if (pFlowStats)
    {
        pFlowGroup->args[curArg] = *pFlowStats;
        curArg++;
    }

    if (pTcpFlags)
    {
        pFlowGroup->args[curArg] = *pTcpFlags;
        curArg++;
    }

    pFlowGroup->args[curArg] = *pActionsArg;
    ++curArg;

    pFlowGroup->groupSize += argsDataSize;
    pMsg->pArgGroup = pFlowGroup;

Cleanup:
    VerifyGroup_Size_Recursive(pMsg->pArgGroup);

    if (ok)
    {
        KFree(pPIArg);
        KFree(pMasksArg);
        KFree(pTimeUsedArg);
        KFree(pFlowStats);
        KFree(pTcpFlags);
        KFree(pActionsArg);
    }
    else
    {
        FreeGroupWithArgs(pFlowGroup);

        DestroyArgument(pPIArg);
        DestroyArgument(pMasksArg);
        DestroyArgument(pTimeUsedArg);
        DestroyArgument(pFlowStats);
        DestroyArgument(pTcpFlags);
        DestroyArgument(pActionsArg);
    }

    return ok;
}
static OVS_ARGUMENT* _CreateEncapsulationArg(const OVS_OFPACKET_INFO* pPacketInfo, const OVS_OFPACKET_INFO* pMask)
{
    OVS_ARGUMENT_GROUP* pEncapsGroup = NULL;
    OVS_ARGUMENT* argArray = NULL, *pEncapsArg = NULL;

    OVS_ARGUMENT_SLIST_ENTRY* pArgListCur = NULL;
    OVS_ARGUMENT_SLIST_ENTRY* pArgHead = NULL;

    BOOLEAN ok = TRUE;
    UINT16 countArgs = 0;
    UINT totalSize = 0;
    BE16 ethType = 0;

    ethType = (pMask ? pMask->ethInfo.type : pPacketInfo->ethInfo.type);

    pEncapsGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pEncapsGroup)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed allocating group\n");
        return FALSE;
    }

    pEncapsArg = KZAlloc(sizeof(OVS_ARGUMENT));
    if (!pEncapsArg)
    {
        ok = FALSE;
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed allocating encaps arg\n");
        goto Cleanup;
    }

    pArgListCur = KZAlloc(sizeof(OVS_ARGUMENT_SLIST_ENTRY));
    if (!pArgListCur)
    {
        ok = FALSE;
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed allocating arg list item\n");
        goto Cleanup;
    }

    pArgHead = pArgListCur;
    pArgHead->pArg = NULL;

    //NOTE: 802.2 frames are represented in ovs messages as:
    //packet info eth type = missing (=> filled by us)
    //mask eth info = exact match 
    if (pPacketInfo->ethInfo.type == RtlUshortByteSwap(OVS_ETHERTYPE_802_2) &&
        pMask && pMask->ethInfo.type)
    {
        if (pMask->ethInfo.type != OVS_PI_MASK_MATCH_EXACT(BE16))
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " expected 802.2 mask to be exact!\n");
            ok = FALSE;
            goto Cleanup;
        }
    }

    if (!CreateArgInList(OVS_ARGTYPE_PI_ETH_TYPE, &ethType, &pArgListCur)) //UINT16
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending enc eth type\n");

        ok = FALSE;
        goto Cleanup;
    }

    if (!_CreateArgsFromLayer3And4InList(pPacketInfo, pMask, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending layer 4 / 4 to list\n");
        ok = FALSE;
        goto Cleanup;
    }

    argArray = ArgumentListToArray(pArgHead, &countArgs, &totalSize);
    if (!argArray)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed converting list to array\n");
        ok = FALSE;
        goto Cleanup;
    }

    pEncapsGroup->args = argArray;
    pEncapsGroup->count = countArgs;
    pEncapsGroup->groupSize = (UINT16)totalSize;

    pEncapsArg->data = pEncapsGroup;
    pEncapsArg->length = (UINT16)totalSize + OVS_ARGUMENT_GROUP_HEADER_SIZE;
    pEncapsArg->type = OVS_ARGTYPE_PI_ENCAP_GROUP;

    VerifyGroup_Size_Recursive(pEncapsArg->data);

Cleanup:

    if (ok)
    {
        DestroyOrFreeArgList(&pArgHead, /*destroy*/ FALSE);
        return pEncapsArg;
    }
    
    DestroyOrFreeArgList(&pArgHead, /*destroy*/ TRUE);
    KFree(argArray);
    KFree(pEncapsGroup);
    KFree(pEncapsArg);

    return NULL;
}
static OVS_ARGUMENT_SLIST_ENTRY* _CreateArgListFromPacketInfo(const OVS_OFPACKET_INFO* pPacketInfo, const OVS_OFPACKET_INFO* pMask)
{
    OVS_ARGUMENT_SLIST_ENTRY* pArgListCur = NULL;
    OVS_ARGUMENT_SLIST_ENTRY* pArgHead = NULL;
    BOOLEAN ok = TRUE;
    BOOLEAN encapsulated = FALSE;
    UINT32 packetPriority = 0, packetMark = 0, datapathHash = 0, recircId = 0;

    pArgListCur = KZAlloc(sizeof(OVS_ARGUMENT_SLIST_ENTRY));
    if (!pArgListCur)
    {
        return NULL;
    }

    pArgHead = pArgListCur;
    pArgHead->pArg = NULL;

    packetPriority = (pMask ? pMask->physical.packetPriority : pPacketInfo->physical.packetPriority);
    packetMark = (pMask ? pMask->physical.packetMark : pPacketInfo->physical.packetMark);
    datapathHash = (pMask ? pMask->flowHash : pPacketInfo->flowHash);
    recircId = (pMask ? pMask->recirculationId : pPacketInfo->recirculationId);

    if (!CreateArgInList(OVS_ARGTYPE_PI_DATAPATH_HASH, &datapathHash, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending datapath hash\n");
        return NULL;
    }

    if (!CreateArgInList(OVS_ARGTYPE_PI_DATAPATH_RECIRCULATION_ID, &recircId, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending datapath recirculation id\n");
        return NULL;
    }

    if (!CreateArgInList(OVS_ARGTYPE_PI_PACKET_PRIORITY, &packetPriority, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending packet priority\n");
        return NULL;
    }

    if (!CreateArgInList(OVS_ARGTYPE_PI_PACKET_MARK, &packetMark, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending packet mark\n");
        ok = FALSE;
        goto Cleanup;
    }

    //TUNNEL
    if (!_CreateTunnelArgInList(pPacketInfo, pMask, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending tunnel key\n");
        ok = FALSE;
        goto Cleanup;
    }

    //INPUT OF PORT
    if (!_CreateInPortArgInList(pPacketInfo, pMask, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending in port\n");
        ok = FALSE;
        goto Cleanup;
    }

    if (!_CreateEthernetArgsInList(pPacketInfo, pMask, &pArgListCur, &encapsulated))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending eth args\n");
        ok = FALSE;
        goto Cleanup;
    }

    if (encapsulated)
    {
        goto Cleanup;
    }

    if (!_CreateArgsFromLayer3And4InList(pPacketInfo, pMask, &pArgListCur))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending layer3 / 4 args\n");
        ok = FALSE;
        goto Cleanup;
    }

Cleanup:
    if (!ok)
    {
        DestroyOrFreeArgList(&pArgHead, /*destroy*/ TRUE);
        return NULL;
    }

    return pArgHead;
}