flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
{
    int i;

    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);

    match_init_catchall(flow_metadata);
    if (flow->tunnel.tun_id != htonll(0)) {
        match_set_tun_id(flow_metadata, flow->tunnel.tun_id);
    }
    if (flow->tunnel.flags & FLOW_TNL_PUB_F_MASK) {
        match_set_tun_flags(flow_metadata,
                            flow->tunnel.flags & FLOW_TNL_PUB_F_MASK);
    }
    if (flow->tunnel.ip_src) {
        match_set_tun_src(flow_metadata, flow->tunnel.ip_src);
    }
    if (flow->tunnel.ip_dst) {
        match_set_tun_dst(flow_metadata, flow->tunnel.ip_dst);
    }
    if (ipv6_addr_is_set(&flow->tunnel.ipv6_src)) {
        match_set_tun_ipv6_src(flow_metadata, &flow->tunnel.ipv6_src);
    }
    if (ipv6_addr_is_set(&flow->tunnel.ipv6_dst)) {
        match_set_tun_ipv6_dst(flow_metadata, &flow->tunnel.ipv6_dst);
    }
    if (flow->tunnel.gbp_id != htons(0)) {
        match_set_tun_gbp_id(flow_metadata, flow->tunnel.gbp_id);
    }
    if (flow->tunnel.gbp_flags) {
        match_set_tun_gbp_flags(flow_metadata, flow->tunnel.gbp_flags);
    }
    if (flow->tunnel.erspan_ver) {
        match_set_tun_erspan_ver(flow_metadata, flow->tunnel.erspan_ver);
    }
    if (flow->tunnel.erspan_idx) {
        match_set_tun_erspan_idx(flow_metadata, flow->tunnel.erspan_idx);
    }
    if (flow->tunnel.erspan_dir) {
        match_set_tun_erspan_dir(flow_metadata, flow->tunnel.erspan_dir);
    }
    if (flow->tunnel.erspan_hwid) {
        match_set_tun_erspan_hwid(flow_metadata, flow->tunnel.erspan_hwid);
    }
    if (flow->tunnel.gtpu_flags) {
        match_set_tun_gtpu_flags(flow_metadata, flow->tunnel.gtpu_flags);
    }
    if (flow->tunnel.gtpu_msgtype) {
        match_set_tun_gtpu_msgtype(flow_metadata, flow->tunnel.gtpu_msgtype);
    }
    tun_metadata_get_fmd(&flow->tunnel, flow_metadata);
    if (flow->metadata != htonll(0)) {
        match_set_metadata(flow_metadata, flow->metadata);
    }

    for (i = 0; i < FLOW_N_REGS; i++) {
        if (flow->regs[i]) {
            match_set_reg(flow_metadata, i, flow->regs[i]);
        }
    }

    if (flow->pkt_mark != 0) {
        match_set_pkt_mark(flow_metadata, flow->pkt_mark);
    }

    match_set_in_port(flow_metadata, flow->in_port.ofp_port);
    if (flow->packet_type != htonl(PT_ETH)) {
        match_set_packet_type(flow_metadata, flow->packet_type);
    }

    if (flow->ct_state != 0) {
        match_set_ct_state(flow_metadata, flow->ct_state);
        /* Match dl_type since it is required for the later interpretation of
         * the conntrack metadata. */
        match_set_dl_type(flow_metadata, flow->dl_type);
        if (is_ct_valid(flow, NULL, NULL) && flow->ct_nw_proto != 0) {
            if (flow->dl_type == htons(ETH_TYPE_IP)) {
                match_set_ct_nw_src(flow_metadata, flow->ct_nw_src);
                match_set_ct_nw_dst(flow_metadata, flow->ct_nw_dst);
                match_set_ct_nw_proto(flow_metadata, flow->ct_nw_proto);
                match_set_ct_tp_src(flow_metadata, flow->ct_tp_src);
                match_set_ct_tp_dst(flow_metadata, flow->ct_tp_dst);
            } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
                match_set_ct_ipv6_src(flow_metadata, &flow->ct_ipv6_src);
                match_set_ct_ipv6_dst(flow_metadata, &flow->ct_ipv6_dst);
                match_set_ct_nw_proto(flow_metadata, flow->ct_nw_proto);
                match_set_ct_tp_src(flow_metadata, flow->ct_tp_src);
                match_set_ct_tp_dst(flow_metadata, flow->ct_tp_dst);
            }
        }
    }
    if (flow->ct_zone != 0) {
        match_set_ct_zone(flow_metadata, flow->ct_zone);
    }
    if (flow->ct_mark != 0) {
        match_set_ct_mark(flow_metadata, flow->ct_mark);
    }
    if (!ovs_u128_is_zero(flow->ct_label)) {
        match_set_ct_label(flow_metadata, flow->ct_label);
    }
}