static BOOLEAN _CreateInPortArgInList(const OVS_OFPACKET_INFO* pPacketInfo, const OVS_OFPACKET_INFO* pMask, OVS_ARGUMENT_SLIST_ENTRY** ppArgList)
{
    UINT32 inputPortValue = 0;

    if (pPacketInfo->physical.ovsInPort == OVS_INVALID_PORT_NUMBER)
    {
        if (pMask)
        {
            if (pMask->physical.ovsInPort == OVS_PI_MASK_MATCH_EXACT(UINT16))
            {
                inputPortValue = OVS_PI_MASK_MATCH_EXACT(UINT32);
            }
            else
            {
                OVS_CHECK(__UNEXPECTED__);
                return TRUE;
            }
        }
        else
        {
            OVS_CHECK(__UNEXPECTED__);
            return TRUE;
        }
    }
    else
    {
        UINT16 highBits = 0;
        UINT16 ovsPortNumber = 0;

        if (pMask)
        {
            highBits = OVS_PI_MASK_MATCH_EXACT(UINT16);
            ovsPortNumber = pMask->physical.ovsInPort;
        }
        else
        {
            highBits = 0;
            ovsPortNumber = pPacketInfo->physical.ovsInPort;
        }

        inputPortValue = ovsPortNumber | (highBits << 16);
    }

    if (!CreateArgInList(OVS_ARGTYPE_PI_DP_INPUT_PORT, &inputPortValue, ppArgList))
    {
        return FALSE;
    }

    return TRUE;
}
static BOOLEAN _MasksFromArgs_HandleEncap(_In_ const OVS_ARGUMENT_GROUP* pMaskGroup, _Inout_ OVS_ARGUMENT* pEncapArg, _Inout_ OVS_ARGUMENT* pEtherTypeArg)
{
    BE16 ethType = 0;
    BE16 vlanTci = 0;
    BOOLEAN ok = TRUE;
    OVS_ARGUMENT* pVlanTciArg = NULL;

    OVS_CHECK(pEncapArg);

    pEncapArg->isDisabled = TRUE;

    if (pEtherTypeArg)
    {
        ethType = GET_ARG_DATA(pEtherTypeArg, BE16);
    }
    else
    {
        DEBUGP(LOG_ERROR, "The eth type argument was not found\n");
        return FALSE;
    }

    if (ethType == OVS_PI_MASK_MATCH_EXACT(UINT16))
    {
        pEtherTypeArg->isDisabled = TRUE;
    }
    else
    {
        DEBUGP(LOG_ERROR, "The vlan frame must have an exact match for ethType. Mask value: %x.\n", RtlUshortByteSwap(ethType));
        return FALSE;
    }

    pVlanTciArg = FindArgument(pMaskGroup, OVS_ARGTYPE_PI_VLAN_TCI);

    if (pVlanTciArg)
    {
        vlanTci = GET_ARG_DATA(pVlanTciArg, BE16);
    }
    else
    {
        DEBUGP(LOG_ERROR, "vlan tci arg not given");
        return FALSE;
    }

    if (!(vlanTci & RtlUshortByteSwap(OVS_VLAN_TAG_PRESENT)))
    {
        DEBUGP(LOG_ERROR, "The vlan field 'tag present' bit must be exact match! Mask value: %x.\n", RtlUshortByteSwap(vlanTci));
        return FALSE;
    }

    return ok;
}
static BOOLEAN _GetPIFromArg_EthType(_Inout_ OVS_OFPACKET_INFO* pPacketInfo, _Inout_ OVS_PI_RANGE* pPiRange, _In_ const OVS_ARGUMENT* pEthTypeArg, _In_ BOOLEAN isMask)
{
    BE16 ethType = GET_ARG_DATA(pEthTypeArg, BE16);

    if (isMask)
    {
        ethType = OVS_PI_MASK_MATCH_EXACT(UINT16);
    }

    if (!isMask)
    {
        EXPECT(RtlUshortByteSwap(ethType) >= OVS_ETHERTYPE_802_3_MIN);
    }

    OVS_PI_UPDATE_ETHINFO_FIELD(pPacketInfo, pPiRange, pEthTypeArg, BE16, type);

    return TRUE;
}
static BOOLEAN _PIFromArg_DatapathInPort(_Inout_ OVS_OFPACKET_INFO* pPacketInfo, _Inout_ OVS_PI_RANGE* pPiRange, _In_ const OVS_ARGUMENT* pArg, BOOLEAN isMask)
{
    UINT16 inPort = (UINT16)GET_ARG_DATA(pArg, UINT32);

    if (isMask)
    {
        inPort = OVS_PI_MASK_MATCH_EXACT(UINT16);
    }

    if (!isMask)
    {
        EXPECT(inPort < OVS_MAX_PORTS);
    }

    OVS_PI_UPDATE_PHYSICAL_FIELD_VALUE(pPacketInfo, pPiRange, ofInPort, inPort);

    return TRUE;
}
BOOLEAN GetFlowMatchFromArguments(_Inout_ OVS_FLOW_MATCH* pFlowMatch, _In_ const OVS_ARGUMENT_GROUP* pPIGroup, const OVS_ARGUMENT_GROUP* pPIMaskGroup)
{
    BOOLEAN encapIsValid = FALSE;
    BOOLEAN ok = TRUE;
    OVS_ARGUMENT* pEthTypeArg = NULL, *pEthAddrArg = NULL;
    OVS_PI_RANGE* pPiRange = NULL;
    OVS_OFPACKET_INFO* pPacketInfo = NULL;

    OVS_CHECK(pFlowMatch);

    if (!pFlowMatch)
    {
        return FALSE;
    }

    pEthTypeArg = FindArgument(pPIGroup, OVS_ARGTYPE_PI_ETH_TYPE);
#if OVS_VERSION == OVS_VERSION_1_11
    EXPECT(pEthAddrArg);
#endif

    pEthAddrArg = FindArgument(pPIGroup, OVS_ARGTYPE_PI_ETH_ADDRESS);
    EXPECT(pEthAddrArg);

    if (pEthTypeArg && RtlUshortByteSwap(OVS_ETHERTYPE_QTAG) == GET_ARG_DATA(pEthTypeArg, BE16))
    {
        if (!_PIFromArgs_HandleEncap(pPIGroup, pEthAddrArg, &encapIsValid))
        {
            return FALSE;
        }
    }

    pPiRange = &pFlowMatch->piRange;
    pPacketInfo = pFlowMatch->pPacketInfo;

    ok = GetPacketInfoFromArguments(pPacketInfo, pPiRange, pPIGroup, /*isMask*/ FALSE);
    if (!ok)
    {
        return FALSE;
    }

    if (!pPIMaskGroup)
    {
        if (pFlowMatch->pFlowMask)
        {
            UINT8* pStart = (UINT8*)&pFlowMatch->pFlowMask->packetInfo + pPiRange->startRange;
            UINT16 range = (UINT16)(pPiRange->endRange - pPiRange->startRange);

            pFlowMatch->pFlowMask->piRange = *pPiRange;
            memset(pStart, OVS_PI_MASK_MATCH_EXACT(UINT8), range);
        }
    }
    else
    {
        OVS_ARGUMENT* pEncapArg = NULL;

        pEncapArg = FindArgument(pPIMaskGroup, OVS_ARGTYPE_PI_ENCAP_GROUP);

        if (pEncapArg)
        {
            if (!encapIsValid)
            {
                DEBUGP(LOG_ERROR, "Tryed to set encapsulation to non-vlan frame!\n");
                return FALSE;
            }

            if (!pEthTypeArg || !_MasksFromArgs_HandleEncap(pPIMaskGroup, pEncapArg, pEthTypeArg))
            {
                return FALSE;
            }
        }

        OVS_CHECK(pFlowMatch->pFlowMask);
        pPiRange = &pFlowMatch->pFlowMask->piRange;
        pPacketInfo = &pFlowMatch->pFlowMask->packetInfo;

        ok = GetPacketInfoFromArguments(pPacketInfo, pPiRange, pPIMaskGroup, /*is mask*/TRUE);
        if (!ok)
        {
            return FALSE;
        }
    }

    //if the userspace gives us bad / unexpected args, we cannot simply deny the flow:
    //a) this might not be a bug (i.e. the userspace intends to set flows like this)
    //b) if it is a bug, we can do little in the kernel to help it.
#if __VERIFY_MASKS
    if (!_VerifyMasks(pFlowMatch, pPIGroup, pPIMaskGroup))
    {
        return FALSE;
    }
#endif

    return TRUE;
}
static BOOLEAN _CreateEthernetArgsInList(const OVS_OFPACKET_INFO* pPacketInfo, const OVS_OFPACKET_INFO* pMask, OVS_ARGUMENT_SLIST_ENTRY** ppArgList, BOOLEAN* pEncapsulated)
{
    OVS_PI_ETH_ADDRESS ethAddrPI = { 0 };
    OVS_ETH_INFO ethInfo = { 0 };

    OVS_CHECK(pEncapsulated);
    *pEncapsulated = FALSE;

    ethInfo = (pMask ? pMask->ethInfo : pPacketInfo->ethInfo);

    RtlCopyMemory(ethAddrPI.source, ethInfo.source, OVS_ETHERNET_ADDRESS_LENGTH);
    RtlCopyMemory(ethAddrPI.destination, ethInfo.destination, OVS_ETHERNET_ADDRESS_LENGTH);

    //ETH ADDRESS
    if (!CreateArgInList(OVS_ARGTYPE_PI_ETH_ADDRESS, &ethAddrPI, ppArgList))
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending eth addr key\n");
        return FALSE;
    }

    if (pPacketInfo->ethInfo.type == RtlUshortByteSwap(OVS_ETHERTYPE_QTAG) ||
        pPacketInfo->ethInfo.tci != 0)
    {
        BE16 ethType = 0;

        DEBUGP(LOG_INFO, "using encapsulation!\n");
        *pEncapsulated = TRUE;

        if (!pMask)
        {
            ethType = RtlUshortByteSwap(OVS_ETHERTYPE_QTAG);
        }
        else
        {
            ethType = OVS_PI_MASK_MATCH_EXACT(UINT16);
        }

        //ETH TYPE
        if (!CreateArgInList(OVS_ARGTYPE_PI_ETH_TYPE, &ethType, ppArgList)) //BE16

        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending vlan eth type\n");
            return FALSE;
        }

        //VLAN TCI
        if (!CreateArgInList(OVS_ARGTYPE_PI_VLAN_TCI, &ethInfo.tci, ppArgList)) //UINT16

        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending vlan tci\n");
            return FALSE;
        }

        if (!pPacketInfo->ethInfo.tci)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " -- have vlan tci but it is 0\n");
            return FALSE;
        }

        if (!_CreateEncapsulationGroupToList(pPacketInfo, pMask, ppArgList))
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending encapsulation group\n");
            return FALSE;
        }

        return TRUE;
    }

    OVS_CHECK(pPacketInfo->ethInfo.type != RtlUshortByteSwap(OVS_ETHERTYPE_802_2));

    if (!CreateArgInList(OVS_ARGTYPE_PI_ETH_TYPE, &ethInfo.type, ppArgList)) //UINT16

    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " failed appending non-encaps eth type\n");
        return FALSE;
    }

    return TRUE;
}
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;
}
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;
}