parse_vlan(const void **datap, size_t *sizep, union flow_vlan_hdr *vlan_hdrs)
{
    const ovs_be16 *eth_type;

    memset(vlan_hdrs, 0, sizeof(union flow_vlan_hdr) * FLOW_MAX_VLAN_HEADERS);
    data_pull(datap, sizep, ETH_ADDR_LEN * 2);

    eth_type = *datap;

    size_t n;
    for (n = 0; eth_type_vlan(*eth_type) && n < flow_vlan_limit; n++) {
        if (OVS_UNLIKELY(*sizep < sizeof(ovs_be32) + sizeof(ovs_be16))) {
            break;
        }

        const ovs_16aligned_be32 *qp = data_pull(datap, sizep, sizeof *qp);
        vlan_hdrs[n].qtag = get_16aligned_be32(qp);
        vlan_hdrs[n].tci |= htons(VLAN_CFI);
        eth_type = *datap;
    }
    return n;
}
parse_vlan(const void **datap, size_t *sizep, union flow_vlan_hdr *vlan_hdrs)
{
    const ovs_be16 *eth_type;

    memset(vlan_hdrs, 0, sizeof(union flow_vlan_hdr) * FLOW_MAX_VLAN_HEADERS);
    data_pull(datap, sizep, ETH_ADDR_LEN * 2);

    eth_type = *datap;

    size_t n;
    for (n = 0; eth_type_vlan(*eth_type) && n < flow_vlan_limit; n++) {
        if (OVS_UNLIKELY(*sizep < sizeof(ovs_be32) + sizeof(ovs_be16))) {
            break;
        }

        const ovs_16aligned_be32 *qp = data_pull(datap, sizep, sizeof *qp);
        vlan_hdrs[n].qtag = get_16aligned_be32(qp);
        vlan_hdrs[n].tci |= htons(VLAN_CFI);
        eth_type = *datap;
    }
    return n;
}