parse_tcp_flags(struct dp_packet *packet)
{
    const void *data = dp_packet_data(packet);
    const char *frame = (const char *)data;
    size_t size = dp_packet_size(packet);
    ovs_be16 dl_type;
    uint8_t nw_frag = 0, nw_proto = 0;

    if (!dp_packet_is_eth(packet)) {
        return 0;
    }

    dp_packet_reset_offsets(packet);

    dl_type = parse_dl_type(&data, &size);
    if (OVS_UNLIKELY(eth_type_mpls(dl_type))) {
        packet->l2_5_ofs = (char *)data - frame;
    }
    if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) {
        const struct ip_header *nh = data;
        int ip_len;
        uint16_t tot_len;

        if (OVS_UNLIKELY(!ipv4_sanity_check(nh, size, &ip_len, &tot_len))) {
            return 0;
        }
        dp_packet_set_l2_pad_size(packet, size - tot_len);
        packet->l3_ofs = (uint16_t)((char *)nh - frame);
        nw_proto = nh->ip_proto;
        nw_frag = ipv4_get_nw_frag(nh);

        size = tot_len;   /* Never pull padding. */
        data_pull(&data, &size, ip_len);
    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
        const struct ovs_16aligned_ip6_hdr *nh = data;
        uint16_t plen;

        if (OVS_UNLIKELY(!ipv6_sanity_check(nh, size))) {
            return 0;
        }
        packet->l3_ofs = (uint16_t)((char *)nh - frame);
        data_pull(&data, &size, sizeof *nh);

        plen = ntohs(nh->ip6_plen); /* Never pull padding. */
        dp_packet_set_l2_pad_size(packet, size - plen);
        size = plen;
        const struct ovs_16aligned_ip6_frag *frag_hdr;
        nw_proto = nh->ip6_nxt;
        if (!parse_ipv6_ext_hdrs__(&data, &size, &nw_proto, &nw_frag,
            &frag_hdr)) {
            return 0;
        }
    } else {
        return 0;
    }

    packet->l4_ofs = (uint16_t)((char *)data - frame);
    if (!(nw_frag & FLOW_NW_FRAG_LATER) && nw_proto == IPPROTO_TCP &&
        size >= TCP_HEADER_LEN) {
        const struct tcp_header *tcp = data;

        return TCP_FLAGS(tcp->tcp_ctl);
    }

    return 0;
}
parse_tcp_flags(struct dp_packet *packet)
{
    const void *data = dp_packet_data(packet);
    const char *frame = (const char *)data;
    size_t size = dp_packet_size(packet);
    ovs_be16 dl_type;
    uint8_t nw_frag = 0, nw_proto = 0;

    if (!dp_packet_is_eth(packet)) {
        return 0;
    }

    dp_packet_reset_offsets(packet);

    dl_type = parse_dl_type(&data, &size);
    if (OVS_UNLIKELY(eth_type_mpls(dl_type))) {
        packet->l2_5_ofs = (char *)data - frame;
    }
    if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) {
        const struct ip_header *nh = data;
        int ip_len;
        uint16_t tot_len;

        if (OVS_UNLIKELY(!ipv4_sanity_check(nh, size, &ip_len, &tot_len))) {
            return 0;
        }
        dp_packet_set_l2_pad_size(packet, size - tot_len);
        packet->l3_ofs = (uint16_t)((char *)nh - frame);
        nw_proto = nh->ip_proto;
        nw_frag = ipv4_get_nw_frag(nh);

        size = tot_len;    
        data_pull(&data, &size, ip_len);
    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
        const struct ovs_16aligned_ip6_hdr *nh = data;
        uint16_t plen;

        if (OVS_UNLIKELY(!ipv6_sanity_check(nh, size))) {
            return 0;
        }
        packet->l3_ofs = (uint16_t)((char *)nh - frame);
        data_pull(&data, &size, sizeof *nh);

        plen = ntohs(nh->ip6_plen);  
        dp_packet_set_l2_pad_size(packet, size - plen);
        size = plen;
        const struct ovs_16aligned_ip6_frag *frag_hdr;
        nw_proto = nh->ip6_nxt;
        if (!parse_ipv6_ext_hdrs__(&data, &size, &nw_proto, &nw_frag,
            &frag_hdr)) {
            return 0;
        }
    } else {
        return 0;
    }

    packet->l4_ofs = (uint16_t)((char *)data - frame);
    if (!(nw_frag & FLOW_NW_FRAG_LATER) && nw_proto == IPPROTO_TCP &&
        size >= TCP_HEADER_LEN) {
        const struct tcp_header *tcp = data;

        return TCP_FLAGS(tcp->tcp_ctl);
    }

    return 0;
}