VOID DbgPrintArg(ULONG logLevel, _In_ OVS_ARGUMENT* pArg, int depth, int index)
{
    char* padding = NULL;

    OVS_CHECK(pArg);
    OVS_CHECK(depth >= 0);

    padding = KAlloc(depth + 1);
    if (!padding)
    {
        return;
    }

    memset(padding, '\t', depth);
    padding[depth] = 0;

    DbgPrintArgType(logLevel, pArg->type, padding, index);
    DEBUGP_ARG(logLevel, "%ssize: 0x%x\n", padding, pArg->length);

    if (IsArgTypeGroup(pArg->type))
    {
        ++depth;
        DbgPrintArgGroup(logLevel, pArg->data, depth);
    }

    KFree(padding);
}
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;
}
VOID DestroyArgumentData(_In_ OVS_ARGUMENT* pArg)
{
    OVS_CHECK(pArg);

    if (IsArgTypeGroup(pArg->type))
    {
        OVS_ARGUMENT_GROUP* pGroup = pArg->data;

        DestroyArgumentGroup(pGroup);
    }
    else
    {
        //free arg data
        if (pArg->freeData)
        {
            KFree(pArg->data);
        }
    }
}
OVS_ARGUMENT* FindArgumentGroupAsArg(_In_ OVS_ARGUMENT_GROUP* pArgGroup, OVS_ARGTYPE groupType)
{
    OVS_CHECK(pArgGroup);

    for (UINT32 i = 0; i < pArgGroup->count; ++i)
    {
        OVS_ARGUMENT* pArg = pArgGroup->args + i;

        if (!pArg->isDisabled && IsArgTypeGroup(pArg->type))
        {
            if (pArg->type == (UINT32)groupType)
            {
                return pArg;
            }
        }
    }

    return NULL;
}
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;
    }
}
BOOLEAN GetPacketContextFromPIArgs(_In_ const OVS_ARGUMENT_GROUP* pArgGroup, _Inout_ OVS_OFPACKET_INFO* pPacketInfo)
{
    OF_PI_IPV4_TUNNEL* pTunnelInfo = &pPacketInfo->tunnelInfo;
    OVS_PI_RANGE* pPiRange = NULL;
    OVS_ARGUMENT* pDatapathInPortArg = NULL;
    OVS_FLOW_MATCH flowMatch = { 0 };

    pPacketInfo->physical.ovsInPort = OVS_INVALID_PORT_NUMBER;
    pPacketInfo->physical.packetPriority = 0;
    pPacketInfo->physical.packetMark = 0;

    RtlZeroMemory(pTunnelInfo, sizeof(OF_PI_IPV4_TUNNEL));
    RtlZeroMemory(&flowMatch, sizeof(flowMatch));
    flowMatch.pPacketInfo = pPacketInfo;

    OVS_CHECK(pArgGroup);

    pPiRange = &flowMatch.piRange;
    pPacketInfo = flowMatch.pPacketInfo;

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

        switch (argType)
        {
        case OVS_ARGTYPE_PI_DATAPATH_HASH:
            PIFromArg_DatapathHash(pPacketInfo, pPiRange, pArg);
            break;

        case OVS_ARGTYPE_PI_DATAPATH_RECIRCULATION_ID:
            PIFromArg_DatapathRecirculationId(pPacketInfo, pPiRange, pArg);
            break;

        case OVS_ARGTYPE_PI_PACKET_PRIORITY:
            PIFromArg_PacketPriority(pPacketInfo, pPiRange, pArg);
            break;

        case OVS_ARGTYPE_PI_PACKET_MARK:
            PIFromArg_PacketMark(pPacketInfo, pPiRange, pArg);
            break;

        case OVS_ARGTYPE_PI_DP_INPUT_PORT:
            pDatapathInPortArg = pArg;
            if (!PIFromArg_DatapathInPort(pPacketInfo, pPiRange, pArg, /*is mask*/FALSE))
            {
                return FALSE;
            }

            break;

        case OVS_ARGTYPE_PI_TUNNEL_GROUP:
            OVS_CHECK(IsArgTypeGroup(pArg->type));

            if (!PIFromArg_Tunnel(pArg->data, pPacketInfo, pPiRange, /*is mask*/ FALSE))
            {
                return FALSE;
            }

            break;

        default:
            //nothing to do here: the rest are non-context / non-metadata keys
            break;
        }
    }

    if (!pDatapathInPortArg)
    {
        PIFromArg_SetDefaultDatapathInPort(pPacketInfo, pPiRange, FALSE);
    }

    return TRUE;
}
BOOLEAN GetArgumentExpectedSize(OVS_ARGTYPE argumentType, _Inout_ UINT* pSize)
{
    if (IsArgTypeGroup(argumentType))
    {
        *pSize = MAXUINT;
        return TRUE;
    }

    switch (argumentType)
    {
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_FLOW_STATS, OVS_WINL_FLOW_STATS);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_FLOW_TCP_FLAGS, UINT8);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_FLOW_TIME_USED, UINT64);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_FLOW_CLEAR, 0);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_PACKET_PRIORITY, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_DP_INPUT_PORT, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_ETH_ADDRESS, OVS_PI_ETH_ADDRESS);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_ETH_TYPE, UINT16);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_VLAN_TCI, BE16);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_IPV4, OVS_PI_IPV4);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_IPV6, OVS_PI_IPV6);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TCP, OVS_PI_TCP);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TCP_FLAGS, BE16);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_UDP, OVS_PI_UDP);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_SCTP, OVS_PI_SCTP);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_ICMP, OVS_PI_ICMP);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_ICMP6, OVS_PI_ICMP6);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_ARP, OVS_PI_ARP);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_NEIGHBOR_DISCOVERY, OVS_PI_NEIGHBOR_DISCOVERY);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_PACKET_MARK, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_IPV4_TUNNEL, OF_PI_IPV4_TUNNEL);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_MPLS, OVS_PI_MPLS);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_DATAPATH_HASH, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_DATAPATH_RECIRCULATION_ID, UINT32);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TUNNEL_ID, BE64);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TUNNEL_IPV4_SRC, BE32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TUNNEL_IPV4_DST, BE32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TUNNEL_TOS, UINT8);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_PI_TUNNEL_TTL, UINT8);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_PI_TUNNEL_DONT_FRAGMENT, 0);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_PI_TUNNEL_CHECKSUM, 0);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_PI_TUNNEL_OAM, 0);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_PI_TUNNEL_GENEVE_OPTIONS, MAXUINT);

        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_PACKET_BUFFER, MAXUINT);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_PACKET_USERDATA, MAXUINT);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_OUTPUT_TO_PORT, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_PUSH_VLAN, OVS_ACTION_PUSH_VLAN);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_ACTION_POP_VLAN, 0);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_PUSH_MPLS, OVS_ACTION_PUSH_MPLS);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_POP_MPLS, BE16);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_RECIRCULATION, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_HASH, OVS_ACTION_FLOW_HASH);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_UPCALL_PORT_ID, UINT32);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_ACTION_UPCALL_DATA, MAXUINT);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_ACTION_SAMPLE_PROBABILITY, UINT32);

        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_DATAPATH_NAME, MAXUINT);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_DATAPATH_UPCALL_PORT_ID, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_DATAPATH_STATS, OVS_DATAPATH_STATS);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_DATAPATH_MEGAFLOW_STATS, OVS_DATAPATH_MEGAFLOW_STATS);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_DATAPATH_USER_FEATURES, UINT32);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_OFPORT_NUMBER, UINT32);
        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_OFPORT_TYPE, UINT32);
        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_OFPORT_NAME, MAXUINT);

        __SIZE_CASE_ARGTYPE(OVS_ARGTYPE_OFPORT_UPCALL_PORT_ID, MAXUINT);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_OFPORT_STATS, OVS_OFPORT_STATS);

        __SIZE_CASE_ARGTYPE_TYPE(OVS_ARGTYPE_OFPORT_OPTION_DESTINATION_PORT, UINT16);

    default:
        OVS_CHECK(__UNEXPECTED__);
        return FALSE;
    }
}
BOOLEAN GetPacketInfoFromArguments(_Inout_ OVS_OFPACKET_INFO* pPacketInfo, _Inout_ OVS_PI_RANGE* pPiRange, _In_ const OVS_ARGUMENT_GROUP* pPIGroup, _In_ BOOLEAN isMask)
{
    BOOLEAN haveIpv4 = FALSE;
    OVS_ARGUMENT* pVlanTciArg = NULL, *pEthTypeArg = NULL, *pDatapathInPortArg = NULL;

    OVS_CHECK(pPacketInfo);
    OVS_CHECK(pPiRange);

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

        switch (argType)
        {
        case OVS_ARGTYPE_PI_DATAPATH_HASH:
            OVS_PI_UPDATE_MAIN_FIELD(pPacketInfo, pPiRange, pArg, UINT32, flowHash);
            break;

        case OVS_ARGTYPE_PI_DATAPATH_RECIRCULATION_ID:
            OVS_PI_UPDATE_MAIN_FIELD(pPacketInfo, pPiRange, pArg, UINT32, recirculationId);
            break;

        case OVS_ARGTYPE_PI_PACKET_PRIORITY:
            OVS_PI_UPDATE_PHYSICAL_FIELD(pPacketInfo, pPiRange, pArg, UINT32, packetPriority);
            break;

        case OVS_ARGTYPE_PI_DP_INPUT_PORT:
            pDatapathInPortArg = pArg;
            EXPECT(_PIFromArg_DatapathInPort(pPacketInfo, pPiRange, pArg, isMask));
            break;

        case OVS_ARGTYPE_PI_PACKET_MARK:
            OVS_PI_UPDATE_PHYSICAL_FIELD(pPacketInfo, pPiRange, pArg, UINT32, packetMark);
            break;

        case OVS_ARGTYPE_PI_TUNNEL_GROUP:
            OVS_CHECK(IsArgTypeGroup(pArg->type));
            EXPECT(_PIFromArg_Tunnel(pArg->data, pPacketInfo, pPiRange, isMask));
            break;

        case OVS_ARGTYPE_PI_ETH_ADDRESS:
        {
            const OVS_PI_ETH_ADDRESS* pEthAddressPI = pArg->data;

            OVS_PI_UPDATE_ETHINFO_ADDRESS(pPacketInfo, pPiRange, source, pEthAddressPI->source);
            OVS_PI_UPDATE_ETHINFO_ADDRESS(pPacketInfo, pPiRange, destination, pEthAddressPI->destination);
        }
            break;

        case OVS_ARGTYPE_PI_VLAN_TCI:
            pVlanTciArg = pArg;
            {
                BE16 tci = GET_ARG_DATA(pVlanTciArg, BE16);

                EXPECT(tci & RtlUshortByteSwap(OVS_VLAN_TAG_PRESENT));
                OVS_PI_UPDATE_ETHINFO_FIELD(pPacketInfo, pPiRange, pVlanTciArg, BE16, tci);
            }
            break;

        case OVS_ARGTYPE_PI_ETH_TYPE:
            pEthTypeArg = pArg;
            EXPECT(_GetPIFromArg_EthType(pPacketInfo, pPiRange, pArg, isMask));
            break;

        case OVS_ARGTYPE_PI_IPV4:
            haveIpv4 = TRUE;
            EXPECT(_GetPIFromArg_Ipv4(pPacketInfo, pPiRange, pArg, isMask));
            break;

        case OVS_ARGTYPE_PI_IPV6:
            EXPECT(_GetPIFromArg_Ipv6(pPacketInfo, pPiRange, pArg, isMask));
            break;

        case OVS_ARGTYPE_PI_ARP:
            EXPECT(_GetPIFromArg_Arp(pPacketInfo, pPiRange, pArg, isMask));
            break;

        case OVS_ARGTYPE_PI_MPLS:
        {
            const OVS_PI_MPLS* pMplsPI = pArg->data;

            OVS_PI_UPDATE_NETINFO_FIELD_VALUE(pPacketInfo, pPiRange, mplsTopLabelStackEntry, pMplsPI->mplsLse);
        }
            break;

        case OVS_ARGTYPE_PI_TCP:
        {
            const OVS_PI_TCP* pTcpPI = pArg->data;

            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, sourcePort, pTcpPI->source);
            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, destinationPort, pTcpPI->destination);
        }
            break;

        case OVS_ARGTYPE_PI_TCP_FLAGS:
            OVS_PI_UPDATE_TPINFO_FIELD(pPacketInfo, pPiRange, pArg, BE16, tcpFlags);
            break;

        case OVS_ARGTYPE_PI_UDP:
        {
            const OVS_PI_UDP* pUdpPI = pArg->data;

            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, sourcePort, pUdpPI->source);
            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, destinationPort, pUdpPI->destination);
        }
            break;

        case OVS_ARGTYPE_PI_SCTP:
        {
            const OVS_PI_SCTP* pSctpPI = pArg->data;

            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, sourcePort, pSctpPI->source);
            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, destinationPort, pSctpPI->destination);
        }
            break;

        case OVS_ARGTYPE_PI_ICMP:
        {
            const OVS_PI_ICMP* pIcmpPI = pArg->data;

            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, sourcePort, RtlUshortByteSwap(pIcmpPI->type));
            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, destinationPort, RtlUshortByteSwap(pIcmpPI->code));
        }
            break;

        case OVS_ARGTYPE_PI_ICMP6:
        {
            const OVS_PI_ICMP6* pIcmpv6PI = pArg->data;

            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, sourcePort, RtlUshortByteSwap(pIcmpv6PI->type));
            OVS_PI_UPDATE_TPINFO_FIELD_VALUE(pPacketInfo, pPiRange, destinationPort, RtlUshortByteSwap(pIcmpv6PI->code));
        }
            break;

        case OVS_ARGTYPE_PI_NEIGHBOR_DISCOVERY:
            _GetPIFromArg_NeighborDiscovery(pPacketInfo, pPiRange, pArg);
            break;

        default:
            DEBUGP(LOG_ERROR, __FUNCTION__ " unexpected key / mask arg type: %u\n", pArg->type);
            return FALSE;
        }
    }

    if (!pDatapathInPortArg && !isMask)
    {
        OVS_PI_UPDATE_PHYSICAL_FIELD_VALUE(pPacketInfo, pPiRange, packetMark, OVS_INVALID_PORT_NUMBER);
    }

    if (!pVlanTciArg)
    {
        if (!isMask)
        {
            //TODO: we should normally set vlan tci to 0xFFFF in this case.
            //but it used to work with 0 only
            OVS_PI_UPDATE_ETHINFO_FIELD_VALUE(pPacketInfo, pPiRange, tci, 0);
        }
    }

    if (!pEthTypeArg)
    {
        if (isMask)
        {
            OVS_PI_UPDATE_ETHINFO_FIELD_VALUE(pPacketInfo, pPiRange, type, OVS_PI_MASK_MATCH_EXACT(UINT16));
        }
        else
        {
            OVS_PI_UPDATE_ETHINFO_FIELD_VALUE(pPacketInfo, pPiRange, type, RtlUshortByteSwap(OVS_ETHERTYPE_802_2));
        }
    }

    return TRUE;
}