parse_ipv6_ext_hdrs__(const void **datap, size_t *sizep, uint8_t *nw_proto,
                      uint8_t *nw_frag,
                      const struct ovs_16aligned_ip6_frag **frag_hdr)
{
    *frag_hdr = NULL;
    while (1) {
        if (OVS_LIKELY((*nw_proto != IPPROTO_HOPOPTS)
                       && (*nw_proto != IPPROTO_ROUTING)
                       && (*nw_proto != IPPROTO_DSTOPTS)
                       && (*nw_proto != IPPROTO_AH)
                       && (*nw_proto != IPPROTO_FRAGMENT))) {
            /* It's either a terminal header (e.g., TCP, UDP) or one we
             * don't understand.  In either case, we're done with the
             * packet, so use it to fill in 'nw_proto'. */
            return true;
        }

        /* We only verify that at least 8 bytes of the next header are
         * available, but many of these headers are longer.  Ensure that
         * accesses within the extension header are within those first 8
         * bytes. All extension headers are required to be at least 8
         * bytes. */
        if (OVS_UNLIKELY(*sizep < 8)) {
            return false;
        }

        if ((*nw_proto == IPPROTO_HOPOPTS)
            || (*nw_proto == IPPROTO_ROUTING)
            || (*nw_proto == IPPROTO_DSTOPTS)) {
            /* These headers, while different, have the fields we care
             * about in the same location and with the same
             * interpretation. */
            const struct ip6_ext *ext_hdr = *datap;
            *nw_proto = ext_hdr->ip6e_nxt;
            if (OVS_UNLIKELY(!data_try_pull(datap, sizep,
                                            (ext_hdr->ip6e_len + 1) * 8))) {
                return false;
            }
        } else if (*nw_proto == IPPROTO_AH) {
            /* A standard AH definition isn't available, but the fields
             * we care about are in the same location as the generic
             * option header--only the header length is calculated
             * differently. */
            const struct ip6_ext *ext_hdr = *datap;
            *nw_proto = ext_hdr->ip6e_nxt;
            if (OVS_UNLIKELY(!data_try_pull(datap, sizep,
                                            (ext_hdr->ip6e_len + 2) * 4))) {
                return false;
            }
        } else if (*nw_proto == IPPROTO_FRAGMENT) {
            *frag_hdr = *datap;

            *nw_proto = (*frag_hdr)->ip6f_nxt;
            if (!data_try_pull(datap, sizep, sizeof **frag_hdr)) {
                return false;
            }

            /* We only process the first fragment. */
            if ((*frag_hdr)->ip6f_offlg != htons(0)) {
                *nw_frag = FLOW_NW_FRAG_ANY;
                if (((*frag_hdr)->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) {
                    *nw_frag |= FLOW_NW_FRAG_LATER;
                    *nw_proto = IPPROTO_FRAGMENT;
                    return true;
                }
            }
        }
    }
}
parse_ipv6_ext_hdrs__(const void **datap, size_t *sizep, uint8_t *nw_proto,
                      uint8_t *nw_frag,
                      const struct ovs_16aligned_ip6_frag **frag_hdr)
{
    *frag_hdr = NULL;
    while (1) {
        if (OVS_LIKELY((*nw_proto != IPPROTO_HOPOPTS)
                       && (*nw_proto != IPPROTO_ROUTING)
                       && (*nw_proto != IPPROTO_DSTOPTS)
                       && (*nw_proto != IPPROTO_AH)
                       && (*nw_proto != IPPROTO_FRAGMENT))) {
             
            return true;
        }

         
        if (OVS_UNLIKELY(*sizep < 8)) {
            return false;
        }

        if ((*nw_proto == IPPROTO_HOPOPTS)
            || (*nw_proto == IPPROTO_ROUTING)
            || (*nw_proto == IPPROTO_DSTOPTS)) {
             
            const struct ip6_ext *ext_hdr = *datap;
            *nw_proto = ext_hdr->ip6e_nxt;
            if (OVS_UNLIKELY(!data_try_pull(datap, sizep,
                                            (ext_hdr->ip6e_len + 1) * 8))) {
                return false;
            }
        } else if (*nw_proto == IPPROTO_AH) {
             
            const struct ip6_ext *ext_hdr = *datap;
            *nw_proto = ext_hdr->ip6e_nxt;
            if (OVS_UNLIKELY(!data_try_pull(datap, sizep,
                                            (ext_hdr->ip6e_len + 2) * 4))) {
                return false;
            }
        } else if (*nw_proto == IPPROTO_FRAGMENT) {
            *frag_hdr = *datap;

            *nw_proto = (*frag_hdr)->ip6f_nxt;
            if (!data_try_pull(datap, sizep, sizeof **frag_hdr)) {
                return false;
            }

             
            if ((*frag_hdr)->ip6f_offlg != htons(0)) {
                *nw_frag = FLOW_NW_FRAG_ANY;
                if (((*frag_hdr)->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) {
                    *nw_frag |= FLOW_NW_FRAG_LATER;
                    *nw_proto = IPPROTO_FRAGMENT;
                    return true;
                }
            }
        }
    }
}