void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
{
    /* setup the J2K codec */
    opj_j2k_setup_decoder(jp2->j2k, parameters);

    /* further JP2 initializations go here */
    jp2->color.jp2_has_colr = 0;
    jp2->ignore_pclr_cmap_cdef = parameters->flags &
                                 OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
}


OPJ_BOOL OPJ_CALLCONV opj_set_decoded_resolution_factor(opj_codec_t *p_codec,
        OPJ_UINT32 res_factor)
{
    opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;

    if (!l_codec) {
        return OPJ_FALSE;
    }

    return l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor(
               l_codec->m_codec,
               res_factor,
               &(l_codec->m_event_mgr));
}


static OPJ_SIZE_T opj_read_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
                                     FILE * p_file)
{
    OPJ_SIZE_T l_nb_read = fread(p_buffer, 1, p_nb_bytes, p_file);
    return l_nb_read ? l_nb_read : (OPJ_SIZE_T) - 1;
}


static OPJ_BOOL opj_jpip_write_iptr(opj_jp2_t *jp2,
                                    opj_stream_private_t *cio,
                                    opj_event_mgr_t * p_manager)
{
    OPJ_OFF_T j2k_codestream_exit;
    OPJ_BYTE l_data_header [24];

    /* preconditions */
    assert(jp2 != 00);
    assert(cio != 00);
    assert(p_manager != 00);
    assert(opj_stream_has_seek(cio));

    j2k_codestream_exit = opj_stream_tell(cio);
    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
    opj_write_bytes(l_data_header + 4, JPIP_IPTR,
                    4);                                      /* IPTR */
#if 0
    opj_write_bytes(l_data_header + 4 + 4, 0, 8); /* offset */
    opj_write_bytes(l_data_header + 8 + 8, 0, 8); /* length */
#else
    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
    opj_write_double(l_data_header + 8 + 8, 0); /* length */
#endif

    if (! opj_stream_seek(cio, jp2->jpip_iptr_offset, p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
        return OPJ_FALSE;
    }

    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
        return OPJ_FALSE;
    }

    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


static void write_prxy(int offset_jp2c, int length_jp2c, int offset_idx,
                       int length_idx, opj_stream_private_t *cio,
                       opj_event_mgr_t * p_manager)
{
    OPJ_BYTE l_data_header [8];
    OPJ_OFF_T len, lenp;

    lenp = opj_stream_tell(cio);
    opj_stream_skip(cio, 4, p_manager);         /* L [at the end] */
    opj_write_bytes(l_data_header, JPIP_PRXY, 4); /* IPTR           */
    opj_stream_write_data(cio, l_data_header, 4, p_manager);

    opj_write_bytes(l_data_header, offset_jp2c, 8);  /* OOFF           */
    opj_stream_write_data(cio, l_data_header, 8, p_manager);
    opj_write_bytes(l_data_header, length_jp2c, 4);  /* OBH part 1     */
    opj_write_bytes(l_data_header + 4, JP2_JP2C, 4); /* OBH part 2     */
    opj_stream_write_data(cio, l_data_header, 8, p_manager);

    opj_write_bytes(l_data_header, 1, 1); /* NI             */
    opj_stream_write_data(cio, l_data_header, 1, p_manager);

    opj_write_bytes(l_data_header, offset_idx, 8);   /* IOFF           */
    opj_stream_write_data(cio, l_data_header, 8, p_manager);
    opj_write_bytes(l_data_header, length_idx, 4);   /* IBH part 1     */
    opj_write_bytes(l_data_header + 4, JPIP_CIDX, 4);  /* IBH part 2     */
    opj_stream_write_data(cio, l_data_header, 8, p_manager);

    len = opj_stream_tell(cio) - lenp;
    opj_stream_skip(cio, lenp, p_manager);
    opj_write_bytes(l_data_header, len, 4); /* L              */
    opj_stream_write_data(cio, l_data_header, 4, p_manager);
    opj_stream_seek(cio, lenp + len, p_manager);
}


static OPJ_BOOL opj_jp2_read_jp(opj_jp2_t *jp2,
                                OPJ_BYTE * p_header_data,
                                OPJ_UINT32 p_header_size,
                                opj_event_mgr_t * p_manager
                               )

{
    OPJ_UINT32 l_magic_number;

    /* preconditions */
    assert(p_header_data != 00);
    assert(jp2 != 00);
    assert(p_manager != 00);

    if (jp2->jp2_state != JP2_STATE_NONE) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "The signature box must be the first box in the file.\n");
        return OPJ_FALSE;
    }

    /* assure length of data is correct (4 -> magic number) */
    if (p_header_size != 4) {
        opj_event_msg(p_manager, EVT_ERROR, "Error with JP signature Box size\n");
        return OPJ_FALSE;
    }

    /* rearrange data */
    opj_read_bytes(p_header_data, &l_magic_number, 4);
    if (l_magic_number != 0x0d0a870a) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Error with JP Signature : bad magic number\n");
        return OPJ_FALSE;
    }

    jp2->jp2_state |= JP2_STATE_SIGNATURE;

    return OPJ_TRUE;
}


static OPJ_SIZE_T opj_write_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
                                      FILE * p_file)
{
    return fwrite(p_buffer, 1, p_nb_bytes, p_file);
}


static OPJ_BOOL opj_jp2_check_color(opj_image_t *image, opj_jp2_color_t *color,
                                    opj_event_mgr_t *p_manager)
{
    OPJ_UINT16 i;

    /* testcase 4149.pdf.SIGSEGV.cf7.3501 */
    if (color->jp2_cdef) {
        opj_jp2_cdef_info_t *info = color->jp2_cdef->info;
        OPJ_UINT16 n = color->jp2_cdef->n;
        OPJ_UINT32 nr_channels =
            image->numcomps; /* FIXME image->numcomps == jp2->numcomps before color is applied ??? */

        /* cdef applies to cmap channels if any */
        if (color->jp2_pclr && color->jp2_pclr->cmap) {
            nr_channels = (OPJ_UINT32)color->jp2_pclr->nr_channels;
        }

        for (i = 0; i < n; i++) {
            if (info[i].cn >= nr_channels) {
                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
                              info[i].cn, nr_channels);
                return OPJ_FALSE;
            }
            if (info[i].asoc == 65535U) {
                continue;
            }

            if (info[i].asoc > 0 && (OPJ_UINT32)(info[i].asoc - 1) >= nr_channels) {
                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
                              info[i].asoc - 1, nr_channels);
                return OPJ_FALSE;
            }
        }

        /* issue 397 */
        /* ISO 15444-1 states that if cdef is present, it shall contain a complete list of channel definitions. */
        while (nr_channels > 0) {
            for (i = 0; i < n; ++i) {
                if ((OPJ_UINT32)info[i].cn == (nr_channels - 1U)) {
                    break;
                }
            }
            if (i == n) {
                opj_event_msg(p_manager, EVT_ERROR, "Incomplete channel definitions.\n");
                return OPJ_FALSE;
            }
            --nr_channels;
        }
    }

    /* testcases 451.pdf.SIGSEGV.f4c.3723, 451.pdf.SIGSEGV.5b5.3723 and
       66ea31acbb0f23a2bbc91f64d69a03f5_signal_sigsegv_13937c0_7030_5725.pdf */
    if (color->jp2_pclr && color->jp2_pclr->cmap) {
        OPJ_UINT16 nr_channels = color->jp2_pclr->nr_channels;
        opj_jp2_cmap_comp_t *cmap = color->jp2_pclr->cmap;
        OPJ_BOOL *pcol_usage, is_sane = OPJ_TRUE;

        /* verify that all original components match an existing one */
        for (i = 0; i < nr_channels; i++) {
            if (cmap[i].cmp >= image->numcomps) {
                opj_event_msg(p_manager, EVT_ERROR, "Invalid component index %d (>= %d).\n",
                              cmap[i].cmp, image->numcomps);
                is_sane = OPJ_FALSE;
            }
        }

        pcol_usage = (OPJ_BOOL *) opj_calloc(nr_channels, sizeof(OPJ_BOOL));
        if (!pcol_usage) {
            opj_event_msg(p_manager, EVT_ERROR, "Unexpected OOM.\n");
            return OPJ_FALSE;
        }
        /* verify that no component is targeted more than once */
        for (i = 0; i < nr_channels; i++) {
            OPJ_BYTE mtyp = cmap[i].mtyp;
            OPJ_BYTE pcol = cmap[i].pcol;
            /* See ISO 15444-1 Table I.14 – MTYPi field values */
            if (mtyp != 0 && mtyp != 1) {
                opj_event_msg(p_manager, EVT_ERROR,
                              "Invalid value for cmap[%d].mtyp = %d.\n", i,
                              mtyp);
                is_sane = OPJ_FALSE;
            } else if (pcol >= nr_channels) {
                opj_event_msg(p_manager, EVT_ERROR,
                              "Invalid component/palette index for direct mapping %d.\n", pcol);
                is_sane = OPJ_FALSE;
            } else if (pcol_usage[pcol] && mtyp == 1) {
                opj_event_msg(p_manager, EVT_ERROR, "Component %d is mapped twice.\n", pcol);
                is_sane = OPJ_FALSE;
            } else if (mtyp == 0 && pcol != 0) {
                /* I.5.3.5 PCOL: If the value of the MTYP field for this channel is 0, then
                 * the value of this field shall be 0. */
                opj_event_msg(p_manager, EVT_ERROR, "Direct use at #%d however pcol=%d.\n", i,
                              pcol);
                is_sane = OPJ_FALSE;
            } else if (mtyp == 1 && pcol != i) {
                /* OpenJPEG implementation limitation. See assert(i == pcol); */
                /* in opj_jp2_apply_pclr() */
                opj_event_msg(p_manager, EVT_ERROR,
                              "Implementation limitation: for palette mapping, "
                              "pcol[%d] should be equal to %d, but is equal "
                              "to %d.\n", i, i, pcol);
                is_sane = OPJ_FALSE;
            } else {
                pcol_usage[pcol] = OPJ_TRUE;
            }
        }
        /* verify that all components are targeted at least once */
        for (i = 0; i < nr_channels; i++) {
            if (!pcol_usage[i] && cmap[i].mtyp != 0) {
                opj_event_msg(p_manager, EVT_ERROR, "Component %d doesn't have a mapping.\n",
                              i);
                is_sane = OPJ_FALSE;
            }
        }
        /* Issue 235/447 weird cmap */
        if (1 && is_sane && (image->numcomps == 1U)) {
            for (i = 0; i < nr_channels; i++) {
                if (!pcol_usage[i]) {
                    is_sane = 0U;
                    opj_event_msg(p_manager, EVT_WARNING,
                                  "Component mapping seems wrong. Trying to correct.\n");
                    break;
                }
            }
            if (!is_sane) {
                is_sane = OPJ_TRUE;
                for (i = 0; i < nr_channels; i++) {
                    cmap[i].mtyp = 1U;
                    cmap[i].pcol = (OPJ_BYTE) i;
                }
            }
        }
        opj_free(pcol_usage);
        if (!is_sane) {
            return OPJ_FALSE;
        }
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_jpip_write_cidx(opj_jp2_t *jp2,
                                    opj_stream_private_t *cio,
                                    opj_event_mgr_t * p_manager)
{
    OPJ_OFF_T j2k_codestream_exit;
    OPJ_BYTE l_data_header [24];

    OPJ_UNUSED(jp2);

    /* preconditions */
    assert(jp2 != 00);
    assert(cio != 00);
    assert(p_manager != 00);
    assert(opj_stream_has_seek(cio));

    j2k_codestream_exit = opj_stream_tell(cio);
    opj_write_bytes(l_data_header, 24, 4); /* size of iptr */
    opj_write_bytes(l_data_header + 4, JPIP_CIDX,
                    4);                                      /* IPTR */
#if 0
    opj_write_bytes(l_data_header + 4 + 4, 0, 8); /* offset */
    opj_write_bytes(l_data_header + 8 + 8, 0, 8); /* length */
#else
    opj_write_double(l_data_header + 4 + 4, 0); /* offset */
    opj_write_double(l_data_header + 8 + 8, 0); /* length */
#endif

    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
        return OPJ_FALSE;
    }

    if (opj_stream_write_data(cio, l_data_header, 24, p_manager) != 24) {
        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
        return OPJ_FALSE;
    }

    j2k_codestream_exit = opj_stream_tell(cio);
    if (! opj_stream_seek(cio, j2k_codestream_exit, p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Failed to seek in the stream.\n");
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT p_format)
{
    opj_codec_private_t *l_codec = 00;

    l_codec = (opj_codec_private_t*)opj_calloc(1, sizeof(opj_codec_private_t));
    if (!l_codec) {
        return 00;
    }

    l_codec->is_decompressor = 0;

    switch (p_format) {
    case OPJ_CODEC_J2K:
        l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *,
                struct opj_stream_private *,
                struct opj_event_mgr *)) opj_j2k_encode;

        l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *,
                struct opj_stream_private *,
                struct opj_event_mgr *)) opj_j2k_end_compress;

        l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *,
                struct opj_stream_private *,
                struct opj_image *,
                struct opj_event_mgr *)) opj_j2k_start_compress;

        l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *,
                OPJ_UINT32,
                OPJ_BYTE*,
                OPJ_UINT32,
                struct opj_stream_private *,
                struct opj_event_mgr *)) opj_j2k_write_tile;

        l_codec->m_codec_data.m_compression.opj_destroy = (void (*)(
                    void *)) opj_j2k_destroy;

        l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *,
                opj_cparameters_t *,
                struct opj_image *,
                struct opj_event_mgr *)) opj_j2k_setup_encoder;

        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL(
                    *)(void *,
                       const char* const*,
                       struct opj_event_mgr *)) opj_j2k_encoder_set_extra_options;

        l_codec->m_codec = opj_j2k_create_compress();
        if (! l_codec->m_codec) {
            opj_free(l_codec);
            return 00;
        }

        break;

    case OPJ_CODEC_JP2:
        /* get a JP2 decoder handle */
        l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *,
                struct opj_stream_private *,
                struct opj_event_mgr *)) opj_jp2_encode;

        l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *,
                struct opj_stream_private *,
                struct opj_event_mgr *)) opj_jp2_end_compress;

        l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *,
                struct opj_stream_private *,
                struct opj_image *,
                struct opj_event_mgr *))  opj_jp2_start_compress;

        l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *,
                OPJ_UINT32,
                OPJ_BYTE*,
                OPJ_UINT32,
                struct opj_stream_private *,
                struct opj_event_mgr *)) opj_jp2_write_tile;

        l_codec->m_codec_data.m_compression.opj_destroy = (void (*)(
                    void *)) opj_jp2_destroy;

        l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *,
                opj_cparameters_t *,
                struct opj_image *,
                struct opj_event_mgr *)) opj_jp2_setup_encoder;

        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL(
                    *)(void *,
                       const char* const*,
                       struct opj_event_mgr *)) opj_jp2_encoder_set_extra_options;

        l_codec->m_codec = opj_jp2_create(OPJ_FALSE);
        if (! l_codec->m_codec) {
            opj_free(l_codec);
            return 00;
        }

        break;

    case OPJ_CODEC_UNKNOWN:
    case OPJ_CODEC_JPT:
    default:
        opj_free(l_codec);
        return 00;
    }

    opj_set_default_event_handler(&(l_codec->m_event_mgr));
    return (opj_codec_t*) l_codec;
}


OPJ_BOOL OPJ_CALLCONV opj_end_compress(opj_codec_t *p_codec,
                                       opj_stream_t *p_stream)
{
    if (p_codec && p_stream) {
        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
        opj_stream_private_t * l_stream = (opj_stream_private_t *) p_stream;

        if (! l_codec->is_decompressor) {
            return l_codec->m_codec_data.m_compression.opj_end_compress(l_codec->m_codec,
                    l_stream,
                    &(l_codec->m_event_mgr));
        }
    }
    return OPJ_FALSE;

}


void opj_jp2_destroy(opj_jp2_t *jp2)
{
    if (jp2) {
        /* destroy the J2K codec */
        opj_j2k_destroy(jp2->j2k);
        jp2->j2k = 00;

        if (jp2->comps) {
            opj_free(jp2->comps);
            jp2->comps = 00;
        }

        if (jp2->cl) {
            opj_free(jp2->cl);
            jp2->cl = 00;
        }

        if (jp2->color.icc_profile_buf) {
            opj_free(jp2->color.icc_profile_buf);
            jp2->color.icc_profile_buf = 00;
        }

        if (jp2->color.jp2_cdef) {
            if (jp2->color.jp2_cdef->info) {
                opj_free(jp2->color.jp2_cdef->info);
                jp2->color.jp2_cdef->info = NULL;
            }

            opj_free(jp2->color.jp2_cdef);
            jp2->color.jp2_cdef = 00;
        }

        if (jp2->color.jp2_pclr) {
            if (jp2->color.jp2_pclr->cmap) {
                opj_free(jp2->color.jp2_pclr->cmap);
                jp2->color.jp2_pclr->cmap = NULL;
            }
            if (jp2->color.jp2_pclr->channel_sign) {
                opj_free(jp2->color.jp2_pclr->channel_sign);
                jp2->color.jp2_pclr->channel_sign = NULL;
            }
            if (jp2->color.jp2_pclr->channel_size) {
                opj_free(jp2->color.jp2_pclr->channel_size);
                jp2->color.jp2_pclr->channel_size = NULL;
            }
            if (jp2->color.jp2_pclr->entries) {
                opj_free(jp2->color.jp2_pclr->entries);
                jp2->color.jp2_pclr->entries = NULL;
            }

            opj_free(jp2->color.jp2_pclr);
            jp2->color.jp2_pclr = 00;
        }

        if (jp2->m_validation_list) {
            opj_procedure_list_destroy(jp2->m_validation_list);
            jp2->m_validation_list = 00;
        }

        if (jp2->m_procedure_list) {
            opj_procedure_list_destroy(jp2->m_procedure_list);
            jp2->m_procedure_list = 00;
        }

        opj_free(jp2);
    }
}


OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads)
{
    return opj_j2k_set_threads(jp2->j2k, num_threads);
}


OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
        OPJ_UINT32 res_factor,
        opj_event_mgr_t * p_manager)
{
    return opj_j2k_set_decoded_resolution_factor(p_jp2->j2k, res_factor, p_manager);
}


OPJ_BOOL OPJ_CALLCONV opj_encoder_set_extra_options(opj_codec_t *p_codec,
        const char* const* options)
{
    if (p_codec) {
        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;

        if (! l_codec->is_decompressor) {
            return l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options(
                       l_codec->m_codec,
                       options,
                       &(l_codec->m_event_mgr));
        }
    }

    return OPJ_FALSE;
}


static OPJ_BOOL opj_jp2_read_bpcc(opj_jp2_t *jp2,
                                  OPJ_BYTE * p_bpc_header_data,
                                  OPJ_UINT32 p_bpc_header_size,
                                  opj_event_mgr_t * p_manager
                                 )
{
    OPJ_UINT32 i;

    /* preconditions */
    assert(p_bpc_header_data != 00);
    assert(jp2 != 00);
    assert(p_manager != 00);

    if (jp2->bpc != 255) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "A BPCC header box is available although BPC given by the IHDR box (%d) indicate components bit depth is constant\n",
                      jp2->bpc);
    }

    /* and length is relevant */
    if (p_bpc_header_size != jp2->numcomps) {
        opj_event_msg(p_manager, EVT_ERROR, "Bad BPCC header box (bad size)\n");
        return OPJ_FALSE;
    }

    /* read info for each component */
    for (i = 0; i < jp2->numcomps; ++i) {
        opj_read_bytes(p_bpc_header_data, &jp2->comps[i].bpcc,
                       1);  /* read each BPCC component */
        ++p_bpc_header_data;
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_jp2_read_boxhdr(opj_jp2_box_t *box,
                                    OPJ_UINT32 * p_number_bytes_read,
                                    opj_stream_private_t *cio,
                                    opj_event_mgr_t * p_manager)
{
    /* read header from file */
    OPJ_BYTE l_data_header [8];

    /* preconditions */
    assert(cio != 00);
    assert(box != 00);
    assert(p_number_bytes_read != 00);
    assert(p_manager != 00);

    *p_number_bytes_read = (OPJ_UINT32)opj_stream_read_data(cio, l_data_header, 8,
                           p_manager);
    if (*p_number_bytes_read != 8) {
        return OPJ_FALSE;
    }

    /* process read data */
    opj_read_bytes(l_data_header, &(box->length), 4);
    opj_read_bytes(l_data_header + 4, &(box->type), 4);

    if (box->length == 0) { /* last box */
        const OPJ_OFF_T bleft = opj_stream_get_number_byte_left(cio);
        if (bleft > (OPJ_OFF_T)(0xFFFFFFFFU - 8U)) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Cannot handle box sizes higher than 2^32\n");
            return OPJ_FALSE;
        }
        box->length = (OPJ_UINT32)bleft + 8U;
        assert((OPJ_OFF_T)box->length == bleft + 8);
        return OPJ_TRUE;
    }

    /* do we have a "special very large box ?" */
    /* read then the XLBox */
    if (box->length == 1) {
        OPJ_UINT32 l_xl_part_size;

        OPJ_UINT32 l_nb_bytes_read = (OPJ_UINT32)opj_stream_read_data(cio,
                                     l_data_header, 8, p_manager);
        if (l_nb_bytes_read != 8) {
            if (l_nb_bytes_read > 0) {
                *p_number_bytes_read += l_nb_bytes_read;
            }

            return OPJ_FALSE;
        }

        *p_number_bytes_read = 16;
        opj_read_bytes(l_data_header, &l_xl_part_size, 4);
        if (l_xl_part_size != 0) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Cannot handle box sizes higher than 2^32\n");
            return OPJ_FALSE;
        }
        opj_read_bytes(l_data_header + 4, &(box->length), 4);
    }
    return OPJ_TRUE;
}


OPJ_FLOAT64 opj_clock(void)
{
#ifdef _WIN32
    /* _WIN32: use QueryPerformance (very accurate) */
    LARGE_INTEGER freq, t ;
    /* freq is the clock speed of the CPU */
    QueryPerformanceFrequency(&freq) ;
    /* cout << "freq = " << ((double) freq.QuadPart) << endl; */
    /* t is the high resolution performance counter (see MSDN) */
    QueryPerformanceCounter(& t) ;
    return freq.QuadPart ? ((OPJ_FLOAT64) t.QuadPart / (OPJ_FLOAT64) freq.QuadPart)
           : 0 ;
#else
    /* Unix or Linux: use resource usage */
    struct rusage t;
    OPJ_FLOAT64 procTime;
    /* (1) Get the rusage data structure at this moment (man getrusage) */
    getrusage(0, &t);
    /* (2) What is the elapsed time ? - CPU time = User time + System time */
    /* (2a) Get the seconds */
    procTime = (OPJ_FLOAT64)(t.ru_utime.tv_sec + t.ru_stime.tv_sec);
    /* (2b) More precisely! Get the microseconds part ! */
    return (procTime + (OPJ_FLOAT64)(t.ru_utime.tv_usec + t.ru_stime.tv_usec) *
            1e-6) ;
#endif
}


dp_packet_reset_offsets(struct dp_packet *b)
{
    b->l2_pad_size = 0;
    b->l2_5_ofs = UINT16_MAX;
    b->l3_ofs = UINT16_MAX;
    b->l4_ofs = UINT16_MAX;
}


conntrack_flush(struct conntrack *ct, const uint16_t *zone)
{
    struct conn *conn;

    ovs_mutex_lock(&ct->ct_lock);
    CMAP_FOR_EACH (conn, cm_node, &ct->conns) {
        if (!zone || *zone == conn->key.zone) {
            conn_clean_one(ct, conn);
        }
    }
    ovs_mutex_unlock(&ct->ct_lock);

    return 0;
}


miniflow_clone(struct miniflow *dst, const struct miniflow *src,
               size_t n_values)
{
    *dst = *src;   /* Copy maps. */
    memcpy(miniflow_values(dst), miniflow_get_values(src),
           MINIFLOW_VALUES_SIZE(n_values));
}


repl_bytes(char *str, char c1, char c2)
{
    while (*str) {
        if (*str == c1) {
            *str = c2;
        }
        str++;
    }
}


conntrack_ipf_ctx(struct conntrack *ct)
{
    return ct->ipf;
}


flow_count_common_mpls_labels(const struct flow *a, int an,
                              const struct flow *b, int bn,
                              struct flow_wildcards *wc)
{
    int min_n = MIN(an, bn);
    if (min_n == 0) {
        return 0;
    } else {
        int common_n = 0;
        int a_last = an - 1;
        int b_last = bn - 1;
        int i;

        for (i = 0; i < min_n; i++) {
            if (wc) {
                wc->masks.mpls_lse[a_last - i] = OVS_BE32_MAX;
                wc->masks.mpls_lse[b_last - i] = OVS_BE32_MAX;
            }
            if (a->mpls_lse[a_last - i] != b->mpls_lse[b_last - i]) {
                break;
            } else {
                common_n++;
            }
        }

        return common_n;
    }
}


flow_format(struct ds *ds,
            const struct flow *flow, const struct ofputil_port_map *port_map)
{
    struct match match;
    struct flow_wildcards *wc = &match.wc;

    match_wc_init(&match, flow);

    /* As this function is most often used for formatting a packet in a
     * packet-in message, skip formatting the packet context fields that are
     * all-zeroes to make the print-out easier on the eyes.  This means that a
     * missing context field implies a zero value for that field.  This is
     * similar to OpenFlow encoding of these fields, as the specification
     * states that all-zeroes context fields should not be encoded in the
     * packet-in messages. */
    if (!flow->in_port.ofp_port) {
        WC_UNMASK_FIELD(wc, in_port);
    }
    if (!flow->skb_priority) {
        WC_UNMASK_FIELD(wc, skb_priority);
    }
    if (!flow->pkt_mark) {
        WC_UNMASK_FIELD(wc, pkt_mark);
    }
    if (!flow->recirc_id) {
        WC_UNMASK_FIELD(wc, recirc_id);
    }
    if (!flow->dp_hash) {
        WC_UNMASK_FIELD(wc, dp_hash);
    }
    if (!flow->ct_state) {
        WC_UNMASK_FIELD(wc, ct_state);
    }
    if (!flow->ct_zone) {
        WC_UNMASK_FIELD(wc, ct_zone);
    }
    if (!flow->ct_mark) {
        WC_UNMASK_FIELD(wc, ct_mark);
    }
    if (ovs_u128_is_zero(flow->ct_label)) {
        WC_UNMASK_FIELD(wc, ct_label);
    }
    if (!is_ct_valid(flow, &match.wc, NULL) || !flow->ct_nw_proto) {
        WC_UNMASK_FIELD(wc, ct_nw_proto);
        WC_UNMASK_FIELD(wc, ct_tp_src);
        WC_UNMASK_FIELD(wc, ct_tp_dst);
        if (flow->dl_type == htons(ETH_TYPE_IP)) {
            WC_UNMASK_FIELD(wc, ct_nw_src);
            WC_UNMASK_FIELD(wc, ct_nw_dst);
        } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
            WC_UNMASK_FIELD(wc, ct_ipv6_src);
            WC_UNMASK_FIELD(wc, ct_ipv6_dst);
        }
    }
    for (int i = 0; i < FLOW_N_REGS; i++) {
        if (!flow->regs[i]) {
            WC_UNMASK_FIELD(wc, regs[i]);
        }
    }
    if (!flow->metadata) {
        WC_UNMASK_FIELD(wc, metadata);
    }

    match_format(&match, port_map, ds, OFP_DEFAULT_PRIORITY);
}


flow_clear_conntrack(struct flow *flow)
{
    flow->ct_state = 0;
    flow->ct_zone = 0;
    flow->ct_mark = 0;
    flow->ct_label = OVS_U128_ZERO;

    flow->ct_nw_proto = 0;
    flow->ct_tp_src = 0;
    flow->ct_tp_dst = 0;
    if (flow->dl_type == htons(ETH_TYPE_IP)) {
        flow->ct_nw_src = 0;
        flow->ct_nw_dst = 0;
    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
        memset(&flow->ct_ipv6_src, 0, sizeof flow->ct_ipv6_src);
        memset(&flow->ct_ipv6_dst, 0, sizeof flow->ct_ipv6_dst);
    }
}


reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
{
    char *tail = dp_packet_tail(pkt);
    uint16_t pad = dp_packet_l2_pad_size(pkt);
    struct conn_key inner_key;
    const char *inner_l4 = NULL;
    uint16_t orig_l3_ofs = pkt->l3_ofs;
    uint16_t orig_l4_ofs = pkt->l4_ofs;

    if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
        struct ip_header *nh = dp_packet_l3(pkt);
        struct icmp_header *icmp = dp_packet_l4(pkt);
        struct ip_header *inner_l3 = (struct ip_header *) (icmp + 1);
        /* This call is already verified to succeed during the code path from
         * 'conn_key_extract()' which calls 'extract_l4_icmp()'. */
        extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *)inner_l3) - pad,
                        &inner_l4, false);
        pkt->l3_ofs += (char *) inner_l3 - (char *) nh;
        pkt->l4_ofs += inner_l4 - (char *) icmp;

        if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
            packet_set_ipv4_addr(pkt, &inner_l3->ip_src,
                                 conn->key.src.addr.ipv4);
        } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
            packet_set_ipv4_addr(pkt, &inner_l3->ip_dst,
                                 conn->key.dst.addr.ipv4);
        }

        reverse_pat_packet(pkt, conn);
        icmp->icmp_csum = 0;
        icmp->icmp_csum = csum(icmp, tail - (char *) icmp - pad);
    } else {
        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
        struct icmp6_data_header *icmp6 = dp_packet_l4(pkt);
        struct ovs_16aligned_ip6_hdr *inner_l3_6 =
            (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1);
        /* This call is already verified to succeed during the code path from
         * 'conn_key_extract()' which calls 'extract_l4_icmp6()'. */
        extract_l3_ipv6(&inner_key, inner_l3_6,
                        tail - ((char *)inner_l3_6) - pad,
                        &inner_l4);
        pkt->l3_ofs += (char *) inner_l3_6 - (char *) nh6;
        pkt->l4_ofs += inner_l4 - (char *) icmp6;

        if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
            packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                 inner_l3_6->ip6_src.be32,
                                 &conn->key.src.addr.ipv6, true);
        } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
            packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                 inner_l3_6->ip6_dst.be32,
                                 &conn->key.dst.addr.ipv6, true);
        }
        reverse_pat_packet(pkt, conn);
        icmp6->icmp6_base.icmp6_cksum = 0;
        icmp6->icmp6_base.icmp6_cksum = packet_csum_upperlayer6(nh6, icmp6,
            IPPROTO_ICMPV6, tail - (char *) icmp6 - pad);
    }
    pkt->l3_ofs = orig_l3_ofs;
    pkt->l4_ofs = orig_l4_ofs;
}


flow_wildcards_init_for_packet(struct flow_wildcards *wc,
                               const struct flow *flow)
{
    ovs_be16 dl_type = OVS_BE16_MAX;

    memset(&wc->masks, 0x0, sizeof wc->masks);

    /* Update this function whenever struct flow changes. */
    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);

    if (flow_tnl_dst_is_set(&flow->tunnel)) {
        if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
            WC_MASK_FIELD(wc, tunnel.tun_id);
        }
        WC_MASK_FIELD(wc, tunnel.ip_src);
        WC_MASK_FIELD(wc, tunnel.ip_dst);
        WC_MASK_FIELD(wc, tunnel.ipv6_src);
        WC_MASK_FIELD(wc, tunnel.ipv6_dst);
        WC_MASK_FIELD(wc, tunnel.flags);
        WC_MASK_FIELD(wc, tunnel.ip_tos);
        WC_MASK_FIELD(wc, tunnel.ip_ttl);
        WC_MASK_FIELD(wc, tunnel.tp_src);
        WC_MASK_FIELD(wc, tunnel.tp_dst);
        WC_MASK_FIELD(wc, tunnel.gbp_id);
        WC_MASK_FIELD(wc, tunnel.gbp_flags);
        WC_MASK_FIELD(wc, tunnel.erspan_ver);
        WC_MASK_FIELD(wc, tunnel.erspan_idx);
        WC_MASK_FIELD(wc, tunnel.erspan_dir);
        WC_MASK_FIELD(wc, tunnel.erspan_hwid);
        WC_MASK_FIELD(wc, tunnel.gtpu_flags);
        WC_MASK_FIELD(wc, tunnel.gtpu_msgtype);

        if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
            if (flow->tunnel.metadata.present.map) {
                wc->masks.tunnel.metadata.present.map =
                                              flow->tunnel.metadata.present.map;
                WC_MASK_FIELD(wc, tunnel.metadata.opts.u8);
                WC_MASK_FIELD(wc, tunnel.metadata.tab);
            }
        } else {
            WC_MASK_FIELD(wc, tunnel.metadata.present.len);
            memset(wc->masks.tunnel.metadata.opts.gnv, 0xff,
                   flow->tunnel.metadata.present.len);
        }
    } else if (flow->tunnel.tun_id) {
        WC_MASK_FIELD(wc, tunnel.tun_id);
    }

    /* metadata, regs, and conj_id wildcarded. */

    WC_MASK_FIELD(wc, skb_priority);
    WC_MASK_FIELD(wc, pkt_mark);
    WC_MASK_FIELD(wc, ct_state);
    WC_MASK_FIELD(wc, ct_zone);
    WC_MASK_FIELD(wc, ct_mark);
    WC_MASK_FIELD(wc, ct_label);
    WC_MASK_FIELD(wc, recirc_id);
    WC_MASK_FIELD(wc, dp_hash);
    WC_MASK_FIELD(wc, in_port);

    /* actset_output wildcarded. */

    WC_MASK_FIELD(wc, packet_type);
    if (flow->packet_type == htonl(PT_ETH)) {
        WC_MASK_FIELD(wc, dl_dst);
        WC_MASK_FIELD(wc, dl_src);
        WC_MASK_FIELD(wc, dl_type);
        /* No need to set mask of inner VLANs that don't exist. */
        for (int i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
            /* Always show the first zero VLAN. */
            WC_MASK_FIELD(wc, vlans[i]);
            if (flow->vlans[i].tci == htons(0)) {
                break;
            }
        }
        dl_type = flow->dl_type;
    } else {
        dl_type = pt_ns_type_be(flow->packet_type);
    }

    if (dl_type == htons(ETH_TYPE_IP)) {
        WC_MASK_FIELD(wc, nw_src);
        WC_MASK_FIELD(wc, nw_dst);
        WC_MASK_FIELD(wc, ct_nw_src);
        WC_MASK_FIELD(wc, ct_nw_dst);
    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
        WC_MASK_FIELD(wc, ipv6_src);
        WC_MASK_FIELD(wc, ipv6_dst);
        WC_MASK_FIELD(wc, ipv6_label);
        if (is_nd(flow, wc)) {
            WC_MASK_FIELD(wc, arp_sha);
            WC_MASK_FIELD(wc, arp_tha);
            WC_MASK_FIELD(wc, nd_target);
        } else {
            WC_MASK_FIELD(wc, ct_ipv6_src);
            WC_MASK_FIELD(wc, ct_ipv6_dst);
        }
    } else if (dl_type == htons(ETH_TYPE_ARP) ||
               dl_type == htons(ETH_TYPE_RARP)) {
        WC_MASK_FIELD(wc, nw_src);
        WC_MASK_FIELD(wc, nw_dst);
        WC_MASK_FIELD(wc, nw_proto);
        WC_MASK_FIELD(wc, arp_sha);
        WC_MASK_FIELD(wc, arp_tha);
        return;
    } else if (eth_type_mpls(dl_type)) {
        for (int i = 0; i < FLOW_MAX_MPLS_LABELS; i++) {
            WC_MASK_FIELD(wc, mpls_lse[i]);
            if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
                break;
            }
        }
        return;
    } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
        WC_MASK_FIELD(wc, nsh.flags);
        WC_MASK_FIELD(wc, nsh.ttl);
        WC_MASK_FIELD(wc, nsh.mdtype);
        WC_MASK_FIELD(wc, nsh.np);
        WC_MASK_FIELD(wc, nsh.path_hdr);
        WC_MASK_FIELD(wc, nsh.context);
    } else {
        return; /* Unknown ethertype. */
    }

    /* IPv4 or IPv6. */
    WC_MASK_FIELD(wc, nw_frag);
    WC_MASK_FIELD(wc, nw_tos);
    WC_MASK_FIELD(wc, nw_ttl);
    WC_MASK_FIELD(wc, nw_proto);
    WC_MASK_FIELD(wc, ct_nw_proto);
    WC_MASK_FIELD(wc, ct_tp_src);
    WC_MASK_FIELD(wc, ct_tp_dst);

    /* No transport layer header in later fragments. */
    if (!(flow->nw_frag & FLOW_NW_FRAG_LATER) &&
        (flow->nw_proto == IPPROTO_ICMP ||
         flow->nw_proto == IPPROTO_ICMPV6 ||
         flow->nw_proto == IPPROTO_TCP ||
         flow->nw_proto == IPPROTO_UDP ||
         flow->nw_proto == IPPROTO_SCTP ||
         flow->nw_proto == IPPROTO_IGMP)) {
        WC_MASK_FIELD(wc, tp_src);
        WC_MASK_FIELD(wc, tp_dst);

        if (flow->nw_proto == IPPROTO_TCP) {
            WC_MASK_FIELD(wc, tcp_flags);
        } else if (flow->nw_proto == IPPROTO_IGMP) {
            WC_MASK_FIELD(wc, igmp_group_ip4);
        }
    }
}


parse_ethertype(const void **datap, size_t *sizep)
{
    const struct llc_snap_header *llc;
    ovs_be16 proto;

    proto = *(ovs_be16 *) data_pull(datap, sizep, sizeof proto);
    if (OVS_LIKELY(ntohs(proto) >= ETH_TYPE_MIN)) {
        return proto;
    }

    if (OVS_UNLIKELY(*sizep < sizeof *llc)) {
        return htons(FLOW_DL_TYPE_NONE);
    }

    llc = *datap;
    if (OVS_UNLIKELY(llc->llc.llc_dsap != LLC_DSAP_SNAP
                     || llc->llc.llc_ssap != LLC_SSAP_SNAP
                     || llc->llc.llc_cntl != LLC_CNTL_SNAP
                     || memcmp(llc->snap.snap_org, SNAP_ORG_ETHERNET,
                               sizeof llc->snap.snap_org))) {
        return htons(FLOW_DL_TYPE_NONE);
    }

    data_pull(datap, sizep, sizeof *llc);

    if (OVS_LIKELY(ntohs(llc->snap.snap_type) >= ETH_TYPE_MIN)) {
        return llc->snap.snap_type;
    }

    return htons(FLOW_DL_TYPE_NONE);
}


conntrack_dump_next(struct conntrack_dump *dump, struct ct_dpif_entry *entry)
{
    struct conntrack *ct = dump->ct;
    long long now = time_msec();

    for (;;) {
        struct cmap_node *cm_node = cmap_next_position(&ct->conns,
                                                       &dump->cm_pos);
        if (!cm_node) {
            break;
        }
        struct conn *conn;
        INIT_CONTAINER(conn, cm_node, cm_node);
        if ((!dump->filter_zone || conn->key.zone == dump->zone) &&
            (conn->conn_type != CT_CONN_TYPE_UN_NAT)) {
            conn_to_ct_dpif_entry(conn, entry, now);
            return 0;
        }
    }

    return EOF;
}


conntrack_dump_done(struct conntrack_dump *dump OVS_UNUSED)
{
    return 0;
}


dp_packet_base(const struct dp_packet *b)
{
    return b->base_;
}


conn_key_reverse(struct conn_key *key)
{
    struct ct_endpoint tmp = key->src;
    key->src = key->dst;
    key->dst = tmp;
}


ct_endpoint_to_ct_dpif_inet_addr(const union ct_addr *a,
                                 union ct_dpif_inet_addr *b,
                                 ovs_be16 dl_type)
{
    if (dl_type == htons(ETH_TYPE_IP)) {
        b->ip = a->ipv4;
    } else if (dl_type == htons(ETH_TYPE_IPV6)){
        b->in6 = a->ipv6;
    }
}


expectation_create(struct conntrack *ct, ovs_be16 dst_port,
                   const struct conn *parent_conn, bool reply, bool src_ip_wc,
                   bool skip_nat)
{
    union ct_addr src_addr;
    union ct_addr dst_addr;
    union ct_addr alg_nat_repl_addr;
    struct alg_exp_node *alg_exp_node = xzalloc(sizeof *alg_exp_node);

    if (reply) {
        src_addr = parent_conn->key.src.addr;
        dst_addr = parent_conn->key.dst.addr;
        alg_exp_node->nat_rpl_dst = true;
        if (skip_nat) {
            alg_nat_repl_addr = dst_addr;
        } else if (parent_conn->nat_info &&
                   parent_conn->nat_info->nat_action & NAT_ACTION_DST) {
            alg_nat_repl_addr = parent_conn->rev_key.src.addr;
            alg_exp_node->nat_rpl_dst = false;
        } else {
            alg_nat_repl_addr = parent_conn->rev_key.dst.addr;
        }
    } else {
        src_addr = parent_conn->rev_key.src.addr;
        dst_addr = parent_conn->rev_key.dst.addr;
        alg_exp_node->nat_rpl_dst = false;
        if (skip_nat) {
            alg_nat_repl_addr = src_addr;
        } else if (parent_conn->nat_info &&
                   parent_conn->nat_info->nat_action & NAT_ACTION_DST) {
            alg_nat_repl_addr = parent_conn->key.dst.addr;
            alg_exp_node->nat_rpl_dst = true;
        } else {
            alg_nat_repl_addr = parent_conn->key.src.addr;
        }
    }
    if (src_ip_wc) {
        memset(&src_addr, 0, sizeof src_addr);
    }

    alg_exp_node->key.dl_type = parent_conn->key.dl_type;
    alg_exp_node->key.nw_proto = parent_conn->key.nw_proto;
    alg_exp_node->key.zone = parent_conn->key.zone;
    alg_exp_node->key.src.addr = src_addr;
    alg_exp_node->key.dst.addr = dst_addr;
    alg_exp_node->key.src.port = ALG_WC_SRC_PORT;
    alg_exp_node->key.dst.port = dst_port;
    alg_exp_node->parent_mark = parent_conn->mark;
    alg_exp_node->parent_label = parent_conn->label;
    memcpy(&alg_exp_node->parent_key, &parent_conn->key,
           sizeof alg_exp_node->parent_key);
    /* Take the write lock here because it is almost 100%
     * likely that the lookup will fail and
     * expectation_create() will be called below. */
    ovs_rwlock_wrlock(&ct->resources_lock);
    struct alg_exp_node *alg_exp = expectation_lookup(
        &ct->alg_expectations, &alg_exp_node->key, ct->hash_basis, src_ip_wc);
    if (alg_exp) {
        free(alg_exp_node);
        ovs_rwlock_unlock(&ct->resources_lock);
        return;
    }

    alg_exp_node->alg_nat_repl_addr = alg_nat_repl_addr;
    hmap_insert(&ct->alg_expectations, &alg_exp_node->node,
                conn_key_hash(&alg_exp_node->key, ct->hash_basis));
    expectation_ref_create(&ct->alg_expectation_refs, alg_exp_node,
                           ct->hash_basis);
    ovs_rwlock_unlock(&ct->resources_lock);
}


expectation_ref_create(struct hindex *alg_expectation_refs,
                       struct alg_exp_node *alg_exp_node,
                       uint32_t basis)
{
    if (!expectation_ref_lookup_unique(alg_expectation_refs,
                                       &alg_exp_node->parent_key,
                                       &alg_exp_node->key, basis)) {
        hindex_insert(alg_expectation_refs, &alg_exp_node->node_ref,
                      conn_key_hash(&alg_exp_node->parent_key, basis));
    }
}


alg_src_ip_wc(enum ct_alg_ctl_type alg_ctl_type)
{
    if (alg_ctl_type == CT_ALG_CTL_SIP) {
        return true;
    }
    return false;
}


flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
{
    wc->masks.regs[idx] = mask;
}


dp_packet_set_size(struct dp_packet *b, uint32_t v)
{
    /* netdev-dpdk does not currently support segmentation; consequently, for
     * all intents and purposes, 'data_len' (16 bit) and 'pkt_len' (32 bit) may
     * be used interchangably.
     *
     * On the datapath, it is expected that the size of packets
     * (and thus 'v') will always be <= UINT16_MAX; this means that there is no
     * loss of accuracy in assigning 'v' to 'data_len'.
     */
    b->mbuf.data_len = (uint16_t)v;  /* Current seg length. */
    b->mbuf.pkt_len = v;             /* Total length of all segments linked to
                                      * this segment. */
}


flow_wildcards_set_xreg_mask(struct flow_wildcards *wc, int idx, uint64_t mask)
{
    flow_set_xreg(&wc->masks, idx, mask);
}


dp_packet_clear(struct dp_packet *b)
{
    dp_packet_set_data(b, dp_packet_base(b));
    dp_packet_set_size(b, 0);
}


conntrack_init(void)
{
    struct conntrack *ct = xzalloc(sizeof *ct);

    ovs_rwlock_init(&ct->resources_lock);
    ovs_rwlock_wrlock(&ct->resources_lock);
    hmap_init(&ct->alg_expectations);
    hindex_init(&ct->alg_expectation_refs);
    ovs_rwlock_unlock(&ct->resources_lock);

    ovs_mutex_init_adaptive(&ct->ct_lock);
    ovs_mutex_lock(&ct->ct_lock);
    cmap_init(&ct->conns);
    for (unsigned i = 0; i < ARRAY_SIZE(ct->exp_lists); i++) {
        ovs_list_init(&ct->exp_lists[i]);
    }
    hmap_init(&ct->zone_limits);
    ct->zone_limit_seq = 0;
    timeout_policy_init(ct);
    ovs_mutex_unlock(&ct->ct_lock);

    ct->hash_basis = random_uint32();
    atomic_count_init(&ct->n_conn, 0);
    atomic_init(&ct->n_conn_limit, DEFAULT_N_CONN_LIMIT);
    atomic_init(&ct->tcp_seq_chk, true);
    latch_init(&ct->clean_thread_exit);
    ct->clean_thread = ovs_thread_create("ct_clean", clean_thread_main, ct);
    ct->ipf = ipf_init();

    return ct;
}


dp_packet_l4_checksum_valid(const struct dp_packet *p OVS_UNUSED)
{
    return false;
}


flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
{
    /* Update this function whenever struct flow changes. */
    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);

    memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
    memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
    wc->masks.actset_output = 0;
    wc->masks.conj_id = 0;
}


dp_packet_hwol_set_csum_tcp(struct dp_packet *b OVS_UNUSED)
{
}


new_conn(struct conntrack *ct, struct dp_packet *pkt, struct conn_key *key,
         long long now)
{
    return l4_protos[key->nw_proto]->new_conn(ct, pkt, now);
}


dp_packet_rss_valid(const struct dp_packet *p)
{
    return p->ol_flags & DP_PACKET_OL_RSS_HASH_MASK;
}


dp_packet_hwol_set_tx_ipv4(struct dp_packet *b OVS_UNUSED)
{
}


conntrack_init(void)
{
    struct conntrack *ct = xzalloc(sizeof *ct);

    ovs_rwlock_init(&ct->resources_lock);
    ovs_rwlock_wrlock(&ct->resources_lock);
    hmap_init(&ct->alg_expectations);
    hindex_init(&ct->alg_expectation_refs);
    ovs_rwlock_unlock(&ct->resources_lock);

    ovs_mutex_init_adaptive(&ct->ct_lock);
    ovs_mutex_lock(&ct->ct_lock);
    cmap_init(&ct->conns);
    for (unsigned i = 0; i < ARRAY_SIZE(ct->exp_lists); i++) {
        ovs_list_init(&ct->exp_lists[i]);
    }
    ovs_mutex_unlock(&ct->ct_lock);

    ct->hash_basis = random_uint32();
    atomic_count_init(&ct->n_conn, 0);
    atomic_init(&ct->n_conn_limit, DEFAULT_N_CONN_LIMIT);
    latch_init(&ct->clean_thread_exit);
    ct->clean_thread = ovs_thread_create("ct_clean", clean_thread_main, ct);
    ct->ipf = ipf_init();

    return ct;
}


process_one(struct conntrack *ct, struct dp_packet *pkt,
            struct conn_lookup_ctx *ctx, uint16_t zone,
            bool force, bool commit, long long now, const uint32_t *setmark,
            const struct ovs_key_ct_labels *setlabel,
            const struct nat_action_info_t *nat_action_info,
            ovs_be16 tp_src, ovs_be16 tp_dst, const char *helper)
{
    /* Reset ct_state whenever entering a new zone. */
    if (pkt->md.ct_state && pkt->md.ct_zone != zone) {
        pkt->md.ct_state = 0;
    }

    bool create_new_conn = false;
    conn_key_lookup(ct, &ctx->key, ctx->hash, now, &ctx->conn, &ctx->reply);
    struct conn *conn = ctx->conn;

    /* Delete found entry if in wrong direction. 'force' implies commit. */
    if (OVS_UNLIKELY(force && ctx->reply && conn)) {
        ovs_mutex_lock(&ct->ct_lock);
        if (conn_lookup(ct, &conn->key, now, NULL, NULL)) {
            conn_clean(ct, conn);
        }
        ovs_mutex_unlock(&ct->ct_lock);
        conn = NULL;
    }

    if (OVS_LIKELY(conn)) {
        if (conn->conn_type == CT_CONN_TYPE_UN_NAT) {

            ctx->reply = true;
            struct conn *rev_conn = conn;  /* Save for debugging. */
            uint32_t hash = conn_key_hash(&conn->rev_key, ct->hash_basis);
            conn_key_lookup(ct, &ctx->key, hash, now, &conn, &ctx->reply);

            if (!conn) {
                pkt->md.ct_state |= CS_INVALID;
                write_ct_md(pkt, zone, NULL, NULL, NULL);
                char *log_msg = xasprintf("Missing master conn %p", rev_conn);
                ct_print_conn_info(rev_conn, log_msg, VLL_INFO, true, true);
                free(log_msg);
                return;
            }
        }
    }

    enum ct_alg_ctl_type ct_alg_ctl = get_alg_ctl_type(pkt, tp_src, tp_dst,
                                                       helper);

    if (OVS_LIKELY(conn)) {
        if (OVS_LIKELY(!conn_update_state_alg(ct, pkt, ctx, conn,
                                              nat_action_info,
                                              ct_alg_ctl, now,
                                              &create_new_conn))) {
            create_new_conn = conn_update_state(ct, pkt, ctx, conn, now);
        }
        if (nat_action_info && !create_new_conn) {
            handle_nat(pkt, conn, zone, ctx->reply, ctx->icmp_related);
        }

    } else if (check_orig_tuple(ct, pkt, ctx, now, &conn, nat_action_info)) {
        create_new_conn = conn_update_state(ct, pkt, ctx, conn, now);
    } else {
        if (ctx->icmp_related) {
            /* An icmp related conn should always be found; no new
               connection is created based on an icmp related packet. */
            pkt->md.ct_state = CS_INVALID;
        } else {
            create_new_conn = true;
        }
    }

    const struct alg_exp_node *alg_exp = NULL;
    struct alg_exp_node alg_exp_entry;

    if (OVS_UNLIKELY(create_new_conn)) {

        ovs_rwlock_rdlock(&ct->resources_lock);
        alg_exp = expectation_lookup(&ct->alg_expectations, &ctx->key,
                                     ct->hash_basis,
                                     alg_src_ip_wc(ct_alg_ctl));
        if (alg_exp) {
            memcpy(&alg_exp_entry, alg_exp, sizeof alg_exp_entry);
            alg_exp = &alg_exp_entry;
        }
        ovs_rwlock_unlock(&ct->resources_lock);

        ovs_mutex_lock(&ct->ct_lock);
        if (!conn_lookup(ct, &ctx->key, now, NULL, NULL)) {
            conn = conn_not_found(ct, pkt, ctx, commit, now, nat_action_info,
                                  helper, alg_exp, ct_alg_ctl);
        }
        ovs_mutex_unlock(&ct->ct_lock);
    }

    write_ct_md(pkt, zone, conn, &ctx->key, alg_exp);

    if (conn && setmark) {
        set_mark(pkt, conn, setmark[0], setmark[1]);
    }

    if (conn && setlabel) {
        set_label(pkt, conn, &setlabel[0], &setlabel[1]);
    }

    handle_alg_ctl(ct, ctx, pkt, ct_alg_ctl, conn, now, !!nat_action_info);
}


    OVS_REQUIRES(ct->ct_lock)
{
    if (conn->alg) {
        expectation_clean(ct, &conn->key);
    }

    uint32_t hash = conn_key_hash(&conn->key, ct->hash_basis);
    cmap_remove(&ct->conns, &conn->cm_node, hash);
}


conn_key_hash(const struct conn_key *key, uint32_t basis)
{
    uint32_t hsrc, hdst, hash;
    hsrc = hdst = basis;
    hsrc = ct_endpoint_hash_add(hsrc, &key->src);
    hdst = ct_endpoint_hash_add(hdst, &key->dst);

    /* Even if source and destination are swapped the hash will be the same. */
    hash = hsrc ^ hdst;

    /* Hash the rest of the key(L3 and L4 types and zone). */
    hash = hash_words((uint32_t *) (&key->dst + 1),
                      (uint32_t *) (key + 1) - (uint32_t *) (&key->dst + 1),
                      hash);

    return hash_finish(hash, 0);
}


nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
{
    if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
        pkt->md.ct_state |= CS_SRC_NAT;
        if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
            struct ip_header *nh = dp_packet_l3(pkt);
            packet_set_ipv4_addr(pkt, &nh->ip_src,
                                 conn->rev_key.dst.addr.ipv4_aligned);
        } else {
            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
            packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                 nh6->ip6_src.be32,
                                 &conn->rev_key.dst.addr.ipv6_aligned,
                                 true);
        }
        if (!related) {
            pat_packet(pkt, conn);
        }
    } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
        pkt->md.ct_state |= CS_DST_NAT;
        if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
            struct ip_header *nh = dp_packet_l3(pkt);
            packet_set_ipv4_addr(pkt, &nh->ip_dst,
                                 conn->rev_key.src.addr.ipv4_aligned);
        } else {
            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
            packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                 nh6->ip6_dst.be32,
                                 &conn->rev_key.src.addr.ipv6_aligned,
                                 true);
        }
        if (!related) {
            pat_packet(pkt, conn);
        }
    }
}


dp_packet_rss_invalidate(struct dp_packet *p)
{
    p->rss_hash_valid = false;
}


delete_conn(struct conn *conn)
{
    free(conn->nat_info);
    free(conn->alg);
    free(conn);
}


new_conn(struct conntrack_bucket *ctb, struct dp_packet *pkt,
         struct conn_key *key, long long now)
{
    return l4_protos[key->nw_proto]->new_conn(ctb, pkt, now);
}


dp_packet_batch_init(struct dp_packet_batch *batch)
{
    batch->count = 0;
    batch->trunc = false;
}


nat_ipv6_addr_increment(struct in6_addr *ipv6_aligned, uint32_t increment)
{
    uint8_t *ipv6_hi = &ipv6_aligned->s6_addr[0];
    uint8_t *ipv6_lo = &ipv6_aligned->s6_addr[0] + sizeof(ovs_be64);
    ovs_be64 addr6_64_hi;
    ovs_be64 addr6_64_lo;
    memcpy(&addr6_64_hi, ipv6_hi, sizeof addr6_64_hi);
    memcpy(&addr6_64_lo, ipv6_lo, sizeof addr6_64_lo);

    if (UINT64_MAX - increment >= ntohll(addr6_64_lo)) {
        addr6_64_lo = htonll(increment + ntohll(addr6_64_lo));
    } else if (addr6_64_hi != OVS_BE64_MAX) {
        addr6_64_hi = htonll(1 + ntohll(addr6_64_hi));
        addr6_64_lo = htonll(increment - (UINT64_MAX -
                                          ntohll(addr6_64_lo) + 1));
    } else {
        OVS_NOT_REACHED();
    }

    memcpy(ipv6_hi, &addr6_64_hi, sizeof addr6_64_hi);
    memcpy(ipv6_lo, &addr6_64_lo, sizeof addr6_64_lo);
}


process_ftp_ctl_v6(struct conntrack *ct,
                   struct dp_packet *pkt,
                   const struct conn *conn_for_expectation,
                   struct ct_addr *v6_addr_rep,
                   char **ftp_data_start,
                   size_t *addr_offset_from_ftp_data_start,
                   size_t *addr_size, enum ct_alg_mode *mode)
{
    struct tcp_header *th = dp_packet_l4(pkt);
    size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
    char *tcp_hdr = (char *) th;
    char ftp_msg[LARGEST_FTP_MSG_OF_INTEREST + 1] = {0};
    get_ftp_ctl_msg(pkt, ftp_msg);
    *ftp_data_start = tcp_hdr + tcp_hdr_len;
    char *ftp = ftp_msg;
    struct in6_addr ip6_addr;

    if (!strncasecmp(ftp, FTP_EPRT_CMD, strlen(FTP_EPRT_CMD))) {
        ftp = ftp_msg + strlen(FTP_EPRT_CMD);
        ftp = skip_non_digits(ftp);
        if (*ftp != FTP_AF_V6 || isdigit(ftp[1])) {
            return CT_FTP_CTL_INVALID;
        }
        /* Jump over delimiter. */
        ftp += 2;

        memset(&ip6_addr, 0, sizeof ip6_addr);
        char *ip_addr_start = ftp;
        *addr_offset_from_ftp_data_start = ip_addr_start - ftp_msg;
        ftp = skip_ipv6_digits(ftp);
        *ftp = 0;
        *addr_size = ftp - ip_addr_start;
        int rc2 = inet_pton(AF_INET6, ip_addr_start, &ip6_addr);
        if (rc2 != 1) {
            return CT_FTP_CTL_INVALID;
        }
        ftp++;
        *mode = CT_FTP_MODE_ACTIVE;
    } else {
        ftp = ftp_msg + strcspn(ftp_msg, "(");
        ftp = skip_non_digits(ftp);
        if (!isdigit(*ftp)) {
            return CT_FTP_CTL_INVALID;
        }

        /* Not used for passive mode. */
        *addr_offset_from_ftp_data_start = 0;
        *addr_size = 0;

        *mode = CT_FTP_MODE_PASSIVE;
    }

    char *save_ftp = ftp;
    ftp = terminate_number_str(ftp, MAX_EXT_FTP_PORT_DGTS);
    if (!ftp) {
        return CT_FTP_CTL_INVALID;
    }

    int value;
    if (!str_to_int(save_ftp, 10, &value)) {
        return CT_FTP_CTL_INVALID;
    }
    if (value > CT_MAX_L4_PORT) {
        return CT_FTP_CTL_INVALID;
    }

    uint16_t port_hs = value;
    ovs_be16 port = htons(port_hs);

    switch (*mode) {
    case CT_FTP_MODE_ACTIVE:
        *v6_addr_rep = conn_for_expectation->rev_key.dst.addr;
        /* Although most servers will block this exploit, there may be some
         * less well managed. */
        if (memcmp(&ip6_addr, &v6_addr_rep->ipv6_aligned, sizeof ip6_addr) &&
            memcmp(&ip6_addr, &conn_for_expectation->key.src.addr.ipv6_aligned,
                   sizeof ip6_addr)) {
            return CT_FTP_CTL_INVALID;
        }
        break;
    case CT_FTP_MODE_PASSIVE:
        *v6_addr_rep = conn_for_expectation->key.dst.addr;
        break;
    case CT_TFTP_MODE:
    default:
        OVS_NOT_REACHED();
    }

    expectation_create(ct, port, conn_for_expectation,
                       !!(pkt->md.ct_state & CS_REPLY_DIR), false, false);
    return CT_FTP_CTL_INTEREST;
}


process_one(struct conntrack *ct, struct dp_packet *pkt,
            struct conn_lookup_ctx *ctx, uint16_t zone,
            bool force, bool commit, long long now, const uint32_t *setmark,
            const struct ovs_key_ct_labels *setlabel,
            const struct nat_action_info_t *nat_action_info,
            ovs_be16 tp_src, ovs_be16 tp_dst, const char *helper)
{
    struct conn *conn;
    unsigned bucket = hash_to_bucket(ctx->hash);
    ct_lock_lock(&ct->buckets[bucket].lock);
    conn_key_lookup(&ct->buckets[bucket], ctx, now);
    conn = ctx->conn;

    /* Reset ct_state whenever entering a new zone. */
    if (pkt->md.ct_state && pkt->md.ct_zone != zone) {
        pkt->md.ct_state = 0;
    }

    /* Delete found entry if in wrong direction. 'force' implies commit. */
    if (OVS_UNLIKELY(force && ctx->reply && conn)) {
        struct conn lconn;
        memcpy(&lconn, conn, sizeof lconn);
        ct_lock_unlock(&ct->buckets[bucket].lock);
        conn_clean_safe(ct, &lconn, &ct->buckets[bucket], ctx->hash);
        ct_lock_lock(&ct->buckets[bucket].lock);
        conn = NULL;
    }

    if (OVS_LIKELY(conn)) {
        if (conn->conn_type == CT_CONN_TYPE_UN_NAT) {

            ctx->reply = true;

            struct conn_lookup_ctx ctx2;
            ctx2.conn = NULL;
            memcpy(&ctx2.key, &conn->rev_key, sizeof ctx2.key);
            ctx2.hash = conn_key_hash(&conn->rev_key, ct->hash_basis);

            ct_lock_unlock(&ct->buckets[bucket].lock);
            bucket = hash_to_bucket(ctx2.hash);

            ct_lock_lock(&ct->buckets[bucket].lock);
            conn_key_lookup(&ct->buckets[bucket], &ctx2, now);

            if (ctx2.conn) {
                conn = ctx2.conn;
            } else {
                /* It is a race condition where conn has timed out and removed
                 * between unlock of the rev_conn and lock of the forward conn;
                 * nothing to do. */
                pkt->md.ct_state |= CS_INVALID;
                write_ct_md(pkt, zone, NULL, NULL, NULL);
                ct_lock_unlock(&ct->buckets[bucket].lock);
                return;
            }
        }
    }

    bool create_new_conn = false;
    struct conn conn_for_un_nat_copy;
    conn_for_un_nat_copy.conn_type = CT_CONN_TYPE_DEFAULT;

    enum ct_alg_ctl_type ct_alg_ctl = get_alg_ctl_type(pkt, tp_src, tp_dst,
                                                       helper);

    if (OVS_LIKELY(conn)) {
        if (OVS_LIKELY(!conn_update_state_alg(ct, pkt, ctx, conn,
                                              nat_action_info,
                                              ct_alg_ctl, now, bucket,
                                              &create_new_conn))) {
            create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now,
                                                bucket);
        }
        if (nat_action_info && !create_new_conn) {
            handle_nat(pkt, conn, zone, ctx->reply, ctx->icmp_related);
        }

    } else if (check_orig_tuple(ct, pkt, ctx, now, &bucket, &conn,
                               nat_action_info)) {
        create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now, bucket);
    } else {
        if (ctx->icmp_related) {
            /* An icmp related conn should always be found; no new
               connection is created based on an icmp related packet. */
            pkt->md.ct_state = CS_INVALID;
        } else {
            create_new_conn = true;
        }
    }

    const struct alg_exp_node *alg_exp = NULL;
    struct alg_exp_node alg_exp_entry;

    if (OVS_UNLIKELY(create_new_conn)) {

        ct_rwlock_rdlock(&ct->resources_lock);
        alg_exp = expectation_lookup(&ct->alg_expectations, &ctx->key,
                                     ct->hash_basis,
                                     alg_src_ip_wc(ct_alg_ctl));
        if (alg_exp) {
            alg_exp_entry = *alg_exp;
            alg_exp = &alg_exp_entry;
        }
        ct_rwlock_unlock(&ct->resources_lock);

        conn = conn_not_found(ct, pkt, ctx, commit, now, nat_action_info,
                              &conn_for_un_nat_copy, helper, alg_exp,
                              ct_alg_ctl);
    }

    write_ct_md(pkt, zone, conn, &ctx->key, alg_exp);

    if (conn && setmark) {
        set_mark(pkt, conn, setmark[0], setmark[1]);
    }

    if (conn && setlabel) {
        set_label(pkt, conn, &setlabel[0], &setlabel[1]);
    }

    struct conn conn_for_expectation;
    if (OVS_UNLIKELY((ct_alg_ctl != CT_ALG_CTL_NONE) && conn)) {
        memcpy(&conn_for_expectation, conn, sizeof conn_for_expectation);
    }

    ct_lock_unlock(&ct->buckets[bucket].lock);

    if (is_un_nat_conn_valid(&conn_for_un_nat_copy)) {
        create_un_nat_conn(ct, &conn_for_un_nat_copy, now, !!alg_exp);
    }

    handle_alg_ctl(ct, ctx, pkt, ct_alg_ctl, conn, now, !!nat_action_info,
                   &conn_for_expectation);
}


parse_ipv6_ext_hdrs__(const void **datap, size_t *sizep, uint8_t *nw_proto,
                      uint8_t *nw_frag)
{
    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) {
            const struct ovs_16aligned_ip6_frag *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;
                }
            }
        }
    }
}


write_ct_md(struct dp_packet *pkt, uint16_t zone, const struct conn *conn,
            const struct conn_key *key, const struct alg_exp_node *alg_exp)
{
    pkt->md.ct_state |= CS_TRACKED;
    pkt->md.ct_zone = zone;
    pkt->md.ct_mark = conn ? conn->mark : 0;
    pkt->md.ct_label = conn ? conn->label : OVS_U128_ZERO;

    /* Use the original direction tuple if we have it. */
    if (conn) {
        if (conn->alg_related) {
            key = &conn->master_key;
        } else {
            key = &conn->key;
        }
    } else if (alg_exp) {
        pkt->md.ct_mark = alg_exp->master_mark;
        pkt->md.ct_label = alg_exp->master_label;
        key = &alg_exp->master_key;
    }

    pkt->md.ct_orig_tuple_ipv6 = false;

    if (key) {
        if (key->dl_type == htons(ETH_TYPE_IP)) {
            pkt->md.ct_orig_tuple.ipv4 = (struct ovs_key_ct_tuple_ipv4) {
                key->src.addr.ipv4_aligned,
                key->dst.addr.ipv4_aligned,
                key->nw_proto != IPPROTO_ICMP
                ? key->src.port : htons(key->src.icmp_type),
                key->nw_proto != IPPROTO_ICMP
                ? key->dst.port : htons(key->src.icmp_code),
                key->nw_proto,
            };
        } else {
            pkt->md.ct_orig_tuple_ipv6 = true;
            pkt->md.ct_orig_tuple.ipv6 = (struct ovs_key_ct_tuple_ipv6) {
                key->src.addr.ipv6_aligned,
                key->dst.addr.ipv6_aligned,
                key->nw_proto != IPPROTO_ICMPV6
                ? key->src.port : htons(key->src.icmp_type),
                key->nw_proto != IPPROTO_ICMPV6
                ? key->dst.port : htons(key->src.icmp_code),
                key->nw_proto,
            };
        }
    } else {
        memset(&pkt->md.ct_orig_tuple, 0, sizeof pkt->md.ct_orig_tuple);
    }
}


un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
              bool related)
{
    if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
        pkt->md.ct_state |= CS_DST_NAT;
        if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
            struct ip_header *nh = dp_packet_l3(pkt);
            packet_set_ipv4_addr(pkt, &nh->ip_dst,
                                 conn->key.src.addr.ipv4_aligned);
        } else {
            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
            packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                 nh6->ip6_dst.be32,
                                 &conn->key.src.addr.ipv6_aligned, true);
        }

        if (OVS_UNLIKELY(related)) {
            reverse_nat_packet(pkt, conn);
        } else {
            un_pat_packet(pkt, conn);
        }
    } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
        pkt->md.ct_state |= CS_SRC_NAT;
        if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
            struct ip_header *nh = dp_packet_l3(pkt);
            packet_set_ipv4_addr(pkt, &nh->ip_src,
                                 conn->key.dst.addr.ipv4_aligned);
        } else {
            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
            packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                 nh6->ip6_src.be32,
                                 &conn->key.dst.addr.ipv6_aligned, true);
        }

        if (OVS_UNLIKELY(related)) {
            reverse_nat_packet(pkt, conn);
        } else {
            un_pat_packet(pkt, conn);
        }
    }
}


conntrack_init(struct conntrack *ct)
{
    long long now = time_msec();

    ct_rwlock_init(&ct->resources_lock);
    ct_rwlock_wrlock(&ct->resources_lock);
    hmap_init(&ct->nat_conn_keys);
    hmap_init(&ct->alg_expectations);
    hindex_init(&ct->alg_expectation_refs);
    ovs_list_init(&ct->alg_exp_list);
    ct_rwlock_unlock(&ct->resources_lock);

    for (unsigned i = 0; i < CONNTRACK_BUCKETS; i++) {
        struct conntrack_bucket *ctb = &ct->buckets[i];

        ct_lock_init(&ctb->lock);
        ct_lock_lock(&ctb->lock);
        hmap_init(&ctb->connections);
        for (unsigned j = 0; j < ARRAY_SIZE(ctb->exp_lists); j++) {
            ovs_list_init(&ctb->exp_lists[j]);
        }
        ct_lock_unlock(&ctb->lock);
        ovs_mutex_init(&ctb->cleanup_mutex);
        ovs_mutex_lock(&ctb->cleanup_mutex);
        ctb->next_cleanup = now + CT_TM_MIN;
        ovs_mutex_unlock(&ctb->cleanup_mutex);
    }
    ct->hash_basis = random_uint32();
    atomic_count_init(&ct->n_conn, 0);
    atomic_init(&ct->n_conn_limit, DEFAULT_N_CONN_LIMIT);
    latch_init(&ct->clean_thread_exit);
    ct->clean_thread = ovs_thread_create("ct_clean", clean_thread_main, ct);
}


flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
                 uint16_t basis)
{
    switch (fields) {

    case NX_HASH_FIELDS_ETH_SRC:
        return jhash_bytes(&flow->dl_src, sizeof flow->dl_src, basis);

    case NX_HASH_FIELDS_SYMMETRIC_L4:
        return flow_hash_symmetric_l4(flow, basis);

    case NX_HASH_FIELDS_SYMMETRIC_L3L4:
        return flow_hash_symmetric_l3l4(flow, basis, false);

    case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP:
        return flow_hash_symmetric_l3l4(flow, basis, true);

    case NX_HASH_FIELDS_NW_SRC:
        if (flow->dl_type == htons(ETH_TYPE_IP)) {
            return jhash_bytes(&flow->nw_src, sizeof flow->nw_src, basis);
        } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
            return jhash_bytes(&flow->ipv6_src, sizeof flow->ipv6_src, basis);
        } else {
            return basis;
        }

    case NX_HASH_FIELDS_NW_DST:
        if (flow->dl_type == htons(ETH_TYPE_IP)) {
            return jhash_bytes(&flow->nw_dst, sizeof flow->nw_dst, basis);
        } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
            return jhash_bytes(&flow->ipv6_dst, sizeof flow->ipv6_dst, basis);
        } else {
            return basis;
        }

    }

    OVS_NOT_REACHED();
}


flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
{
    /* Update this function whenever struct flow changes. */
    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);

    memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
    memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
    wc->masks.actset_output = 0;
    wc->masks.conj_id = 0;
}


nat_ipv6_addr_increment(struct in6_addr *ipv6_aligned, uint32_t increment)
{
    uint8_t *ipv6_hi = &ipv6_aligned->s6_addr[0];
    uint8_t *ipv6_lo = &ipv6_aligned->s6_addr[0] + sizeof(ovs_be64);
    ovs_be64 addr6_64_hi;
    ovs_be64 addr6_64_lo;
    memcpy(&addr6_64_hi, ipv6_hi, sizeof addr6_64_hi);
    memcpy(&addr6_64_lo, ipv6_lo, sizeof addr6_64_lo);

    if (UINT64_MAX - increment >= ntohll(addr6_64_lo)) {
        addr6_64_lo = htonll(increment + ntohll(addr6_64_lo));
    } else if (addr6_64_hi != OVS_BE64_MAX) {
        addr6_64_hi = htonll(1 + ntohll(addr6_64_hi));
        addr6_64_lo = htonll(increment - (UINT64_MAX -
                                          ntohll(addr6_64_lo) + 1));
    } else {
        OVS_NOT_REACHED();
    }

    memcpy(ipv6_hi, &addr6_64_hi, sizeof addr6_64_hi);
    memcpy(ipv6_lo, &addr6_64_lo, sizeof addr6_64_lo);

    return;
}


extract_l4_icmp6(struct conn_key *key, const void *data, size_t size,
                 bool *related)
{
    const struct icmp6_header *icmp6 = data;

    /* All the messages that we support need at least 4 bytes after
     * the header */
    if (size < sizeof *icmp6 + 4) {
        return false;
    }

    switch (icmp6->icmp6_type) {
    case ICMP6_ECHO_REQUEST:
    case ICMP6_ECHO_REPLY:
        if (icmp6->icmp6_code != 0) {
            return false;
        }
        /* Separate ICMP connection: identified using id */
        key->src.icmp_id = key->dst.icmp_id = *(ovs_be16 *) (icmp6 + 1);
        key->src.icmp_type = icmp6->icmp6_type;
        key->dst.icmp_type = reverse_icmp6_type(icmp6->icmp6_type);
        break;
    case ICMP6_DST_UNREACH:
    case ICMP6_PACKET_TOO_BIG:
    case ICMP6_TIME_EXCEEDED:
    case ICMP6_PARAM_PROB: {
        /* ICMP packet part of another connection. We should
         * extract the key from embedded packet header */
        struct conn_key inner_key;
        const char *l3 = (const char *) icmp6 + 8;
        const char *tail = (const char *) data + size;
        const char *l4 = NULL;

        if (!related) {
            return false;
        }

        memset(&inner_key, 0, sizeof inner_key);
        inner_key.dl_type = htons(ETH_TYPE_IPV6);
        bool ok = extract_l3_ipv6(&inner_key, l3, tail - l3, &l4);
        if (!ok) {
            return false;
        }

        /* pf doesn't do this, but it seems a good idea */
        if (!ipv6_addr_equals(&inner_key.src.addr.ipv6_aligned,
                              &key->dst.addr.ipv6_aligned)) {
            return false;
        }

        key->src = inner_key.src;
        key->dst = inner_key.dst;
        key->nw_proto = inner_key.nw_proto;

        ok = extract_l4(key, l4, tail - l4, NULL, l3, false);
        if (ok) {
            conn_key_reverse(key);
            *related = true;
        }
        return ok;
    }
    default:
        return false;
    }

    return true;
}


extract_l4_tcp(struct conn_key *key, const void *data, size_t size)
{
    if (OVS_UNLIKELY(size < TCP_HEADER_LEN)) {
        return false;
    }

    const struct tcp_header *tcp = data;
    key->src.port = tcp->tcp_src;
    key->dst.port = tcp->tcp_dst;

    /* Port 0 is invalid */
    return key->src.port && key->dst.port;
}


flow_compose_l4(struct dp_packet *p, const struct flow *flow)
{
    size_t l4_len = 0;

    if (!(flow->nw_frag & FLOW_NW_FRAG_ANY)
        || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
        if (flow->nw_proto == IPPROTO_TCP) {
            struct tcp_header *tcp;

            l4_len = sizeof *tcp;
            tcp = dp_packet_put_zeros(p, l4_len);
            tcp->tcp_src = flow->tp_src;
            tcp->tcp_dst = flow->tp_dst;
            tcp->tcp_ctl = TCP_CTL(ntohs(flow->tcp_flags), 5);
        } else if (flow->nw_proto == IPPROTO_UDP) {
            struct udp_header *udp;

            l4_len = sizeof *udp;
            udp = dp_packet_put_zeros(p, l4_len);
            udp->udp_src = flow->tp_src;
            udp->udp_dst = flow->tp_dst;
            udp->udp_len = htons(l4_len);
        } else if (flow->nw_proto == IPPROTO_SCTP) {
            struct sctp_header *sctp;

            l4_len = sizeof *sctp;
            sctp = dp_packet_put_zeros(p, l4_len);
            sctp->sctp_src = flow->tp_src;
            sctp->sctp_dst = flow->tp_dst;
        } else if (flow->nw_proto == IPPROTO_ICMP) {
            struct icmp_header *icmp;

            l4_len = sizeof *icmp;
            icmp = dp_packet_put_zeros(p, l4_len);
            icmp->icmp_type = ntohs(flow->tp_src);
            icmp->icmp_code = ntohs(flow->tp_dst);
        } else if (flow->nw_proto == IPPROTO_IGMP) {
            struct igmp_header *igmp;

            l4_len = sizeof *igmp;
            igmp = dp_packet_put_zeros(p, l4_len);
            igmp->igmp_type = ntohs(flow->tp_src);
            igmp->igmp_code = ntohs(flow->tp_dst);
            put_16aligned_be32(&igmp->group, flow->igmp_group_ip4);
        } else if (flow->nw_proto == IPPROTO_ICMPV6) {
            struct icmp6_hdr *icmp;

            l4_len = sizeof *icmp;
            icmp = dp_packet_put_zeros(p, l4_len);
            icmp->icmp6_type = ntohs(flow->tp_src);
            icmp->icmp6_code = ntohs(flow->tp_dst);

            if (icmp->icmp6_code == 0 &&
                (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
                 icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
                struct in6_addr *nd_target;
                struct ovs_nd_lla_opt *lla_opt;

                l4_len += sizeof *nd_target;
                nd_target = dp_packet_put_zeros(p, sizeof *nd_target);
                *nd_target = flow->nd_target;

                if (!eth_addr_is_zero(flow->arp_sha)) {
                    l4_len += 8;
                    lla_opt = dp_packet_put_zeros(p, 8);
                    lla_opt->len = 1;
                    lla_opt->type = ND_OPT_SOURCE_LINKADDR;
                    lla_opt->mac = flow->arp_sha;
                }
                if (!eth_addr_is_zero(flow->arp_tha)) {
                    l4_len += 8;
                    lla_opt = dp_packet_put_zeros(p, 8);
                    lla_opt->len = 1;
                    lla_opt->type = ND_OPT_TARGET_LINKADDR;
                    lla_opt->mac = flow->arp_tha;
                }
            }
        }
    }
    return l4_len;
}


flow_wc_map(const struct flow *flow, struct flowmap *map)
{
    /* Update this function whenever struct flow changes. */
    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);

    flowmap_init(map);

    if (flow_tnl_dst_is_set(&flow->tunnel)) {
        FLOWMAP_SET__(map, tunnel, offsetof(struct flow_tnl, metadata));
        if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
            if (flow->tunnel.metadata.present.map) {
                FLOWMAP_SET(map, tunnel.metadata);
            }
        } else {
            FLOWMAP_SET(map, tunnel.metadata.present.len);
            FLOWMAP_SET__(map, tunnel.metadata.opts.gnv,
                          flow->tunnel.metadata.present.len);
        }
    }

    /* Metadata fields that can appear on packet input. */
    FLOWMAP_SET(map, skb_priority);
    FLOWMAP_SET(map, pkt_mark);
    FLOWMAP_SET(map, recirc_id);
    FLOWMAP_SET(map, dp_hash);
    FLOWMAP_SET(map, in_port);
    FLOWMAP_SET(map, dl_dst);
    FLOWMAP_SET(map, dl_src);
    FLOWMAP_SET(map, dl_type);
    FLOWMAP_SET(map, vlans);
    FLOWMAP_SET(map, ct_state);
    FLOWMAP_SET(map, ct_zone);
    FLOWMAP_SET(map, ct_mark);
    FLOWMAP_SET(map, ct_label);
    FLOWMAP_SET(map, packet_type);

    /* Ethertype-dependent fields. */
    if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) {
        FLOWMAP_SET(map, nw_src);
        FLOWMAP_SET(map, nw_dst);
        FLOWMAP_SET(map, nw_proto);
        FLOWMAP_SET(map, nw_frag);
        FLOWMAP_SET(map, nw_tos);
        FLOWMAP_SET(map, nw_ttl);
        FLOWMAP_SET(map, tp_src);
        FLOWMAP_SET(map, tp_dst);
        FLOWMAP_SET(map, ct_nw_proto);
        FLOWMAP_SET(map, ct_nw_src);
        FLOWMAP_SET(map, ct_nw_dst);
        FLOWMAP_SET(map, ct_tp_src);
        FLOWMAP_SET(map, ct_tp_dst);

        if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_IGMP)) {
            FLOWMAP_SET(map, igmp_group_ip4);
        } else {
            FLOWMAP_SET(map, tcp_flags);
        }
    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
        FLOWMAP_SET(map, ipv6_src);
        FLOWMAP_SET(map, ipv6_dst);
        FLOWMAP_SET(map, ipv6_label);
        FLOWMAP_SET(map, nw_proto);
        FLOWMAP_SET(map, nw_frag);
        FLOWMAP_SET(map, nw_tos);
        FLOWMAP_SET(map, nw_ttl);
        FLOWMAP_SET(map, tp_src);
        FLOWMAP_SET(map, tp_dst);

        if (OVS_UNLIKELY(is_nd(flow, NULL))) {
            FLOWMAP_SET(map, nd_target);
            FLOWMAP_SET(map, arp_sha);
            FLOWMAP_SET(map, arp_tha);
        } else {
            FLOWMAP_SET(map, ct_nw_proto);
            FLOWMAP_SET(map, ct_ipv6_src);
            FLOWMAP_SET(map, ct_ipv6_dst);
            FLOWMAP_SET(map, ct_tp_src);
            FLOWMAP_SET(map, ct_tp_dst);
            FLOWMAP_SET(map, tcp_flags);
        }
    } else if (eth_type_mpls(flow->dl_type)) {
        FLOWMAP_SET(map, mpls_lse);
    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
               flow->dl_type == htons(ETH_TYPE_RARP)) {
        FLOWMAP_SET(map, nw_src);
        FLOWMAP_SET(map, nw_dst);
        FLOWMAP_SET(map, nw_proto);
        FLOWMAP_SET(map, arp_sha);
        FLOWMAP_SET(map, arp_tha);
    } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
        FLOWMAP_SET(map, nsh.flags);
        FLOWMAP_SET(map, nsh.mdtype);
        FLOWMAP_SET(map, nsh.np);
        FLOWMAP_SET(map, nsh.spi);
        FLOWMAP_SET(map, nsh.si);
        FLOWMAP_SET(map, nsh.c);
    }
}


    OVS_REQUIRES(ctb->lock)
{
    ovs_list_remove(&conn->exp_node);
    hmap_remove(&ctb->connections, &conn->node);
    atomic_count_dec(&ct->n_conn);
    if (conn->nat_info) {
        nat_clean(ct, conn, ctb);
    } else {
        delete_conn(conn);
    }
}


detect_ftp_ctl_type(const struct conn_lookup_ctx *ctx,
                    struct dp_packet *pkt)
{

    char ftp_msg[LARGEST_FTP_MSG_OF_INTEREST + 1] = {0};
    get_ftp_ctl_msg(pkt, ftp_msg);
    if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
        if (strncasecmp(ftp_msg, FTP_EPRT_CMD, strlen(FTP_EPRT_CMD)) &&
            !strcasestr(ftp_msg, FTP_EPSV_REPLY)) {
            return CT_FTP_CTL_OTHER;
        }
    } else {
        if (strncasecmp(ftp_msg, FTP_PORT_CMD, strlen(FTP_PORT_CMD)) &&
            strncasecmp(ftp_msg, FTP_PASV_REPLY_CODE,
                        strlen(FTP_PASV_REPLY_CODE))) {
            return CT_FTP_CTL_OTHER;
        }
    }

    return CT_FTP_CTL_INTEREST;
}


conn_seq_skew_set(struct conntrack *ct, const struct conn_key *key,
                  long long now, int seq_skew, bool seq_skew_dir)
{
    uint32_t hash = conn_key_hash(key, ct->hash_basis);
    unsigned bucket = hash_to_bucket(hash);
    ct_lock_lock(&ct->buckets[bucket].lock);
    struct conn *conn = conn_lookup(ct, key, now);
    if (conn && seq_skew) {
        conn->seq_skew = seq_skew;
        conn->seq_skew_dir = seq_skew_dir;
    }
    ct_lock_unlock(&ct->buckets[bucket].lock);
}


clean_thread_main(void *f_)
{
    struct conntrack *ct = f_;

    while (!latch_is_set(&ct->clean_thread_exit)) {
        long long next_wake;
        long long now = time_msec();

        next_wake = conntrack_clean(ct, now);

        if (next_wake < now) {
            poll_timer_wait_until(now + CT_CLEAN_MIN_INTERVAL);
        } else {
            poll_timer_wait_until(MAX(next_wake, now + CT_CLEAN_INTERVAL));
        }
        latch_wait(&ct->clean_thread_exit);
        poll_block();
    }

    return NULL;
}


conn_key_hash(const struct conn_key *key, uint32_t basis)
{
    uint32_t hsrc, hdst, hash;

    hsrc = hdst = basis;
    hsrc = ct_endpoint_hash_add(hsrc, &key->src);
    hdst = ct_endpoint_hash_add(hdst, &key->dst);

    /* Even if source and destination are swapped the hash will be the same. */
    hash = hsrc ^ hdst;

    /* Hash the rest of the key(L3 and L4 types and zone). */
    hash = hash_words((uint32_t *) (&key->dst + 1),
                      (uint32_t *) (key + 1) - (uint32_t *) (&key->dst + 1),
                      hash);

    return hash_finish(hash, 0);
}


expectation_create(struct conntrack *ct,
                   ovs_be16 dst_port,
                   const long long now,
                   enum ct_alg_mode mode,
                   const struct conn *master_conn)
{
    struct ct_addr src_addr;
    struct ct_addr dst_addr;
    struct ct_addr alg_nat_repl_addr;

    switch (mode) {
    case CT_FTP_MODE_ACTIVE:
    case CT_TFTP_MODE:
        src_addr = master_conn->rev_key.src.addr;
        dst_addr = master_conn->rev_key.dst.addr;
        alg_nat_repl_addr = master_conn->key.src.addr;
        break;
    case CT_FTP_MODE_PASSIVE:
        src_addr = master_conn->key.src.addr;
        dst_addr = master_conn->key.dst.addr;
        alg_nat_repl_addr = master_conn->rev_key.dst.addr;
        break;
    default:
        OVS_NOT_REACHED();
    }

    struct alg_exp_node *alg_exp_node =
        xzalloc(sizeof *alg_exp_node);
    alg_exp_node->key.dl_type = master_conn->key.dl_type;
    alg_exp_node->key.nw_proto = master_conn->key.nw_proto;
    alg_exp_node->key.zone = master_conn->key.zone;
    alg_exp_node->key.src.addr = src_addr;
    alg_exp_node->key.dst.addr = dst_addr;
    alg_exp_node->key.src.port = ALG_WC_SRC_PORT;
    alg_exp_node->key.dst.port = dst_port;
    alg_exp_node->master_mark = master_conn->mark;
    alg_exp_node->master_label = master_conn->label;
    alg_exp_node->master_key = master_conn->key;
    alg_exp_node->passive_mode = mode == CT_FTP_MODE_PASSIVE;
    /* Take the write lock here because it is almost 100%
     * likely that the lookup will fail and
     * expectation_create() will be called below. */
    ct_rwlock_wrlock(&ct->resources_lock);
    struct alg_exp_node *alg_exp = expectation_lookup(
        &ct->alg_expectations, &alg_exp_node->key, ct->hash_basis);
    if (alg_exp) {
        free(alg_exp_node);
        ct_rwlock_unlock(&ct->resources_lock);
        return;
    }

    alg_exp_node->alg_nat_repl_addr = alg_nat_repl_addr;
    uint32_t alg_exp_conn_key_hash =
        conn_key_hash(&alg_exp_node->key,
                      ct->hash_basis);
    hmap_insert(&ct->alg_expectations,
                &alg_exp_node->node,
                alg_exp_conn_key_hash);

    alg_exp_init_expiration(ct, alg_exp_node, now);
    ct_rwlock_unlock(&ct->resources_lock);
}


dp_packet_ip_checksum_valid(struct dp_packet *p)
{
#ifdef DPDK_NETDEV
    return p->mbuf.ol_flags & PKT_RX_IP_CKSUM_GOOD;
#else
    return 0 && p;
#endif
}


dp_packet_batch_apply_cutlen(struct dp_packet_batch *pktb)
{
    int i;

    if (!pktb->trunc)
        return;

    for (i = 0; i < pktb->count; i++) {
        uint32_t cutlen = dp_packet_get_cutlen(pktb->packets[i]);

        dp_packet_set_size(pktb->packets[i],
                    dp_packet_size(pktb->packets[i]) - cutlen);
        dp_packet_reset_cutlen(pktb->packets[i]);
    }
    pktb->trunc = false;
}


flow_random_hash_fields(struct flow *flow)
{
    uint16_t rnd = random_uint16();

    /* Initialize to all zeros. */
    memset(flow, 0, sizeof *flow);

    eth_addr_random(&flow->dl_src);
    eth_addr_random(&flow->dl_dst);

    flow->vlan_tci = (OVS_FORCE ovs_be16) (random_uint16() & VLAN_VID_MASK);

    /* Make most of the random flows IPv4, some IPv6, and rest random. */
    flow->dl_type = rnd < 0x8000 ? htons(ETH_TYPE_IP) :
        rnd < 0xc000 ? htons(ETH_TYPE_IPV6) : (OVS_FORCE ovs_be16)rnd;

    if (dl_type_is_ip_any(flow->dl_type)) {
        if (flow->dl_type == htons(ETH_TYPE_IP)) {
            flow->nw_src = (OVS_FORCE ovs_be32)random_uint32();
            flow->nw_dst = (OVS_FORCE ovs_be32)random_uint32();
        } else {
            random_bytes(&flow->ipv6_src, sizeof flow->ipv6_src);
            random_bytes(&flow->ipv6_dst, sizeof flow->ipv6_dst);
        }
        /* Make most of IP flows TCP, some UDP or SCTP, and rest random. */
        rnd = random_uint16();
        flow->nw_proto = rnd < 0x8000 ? IPPROTO_TCP :
            rnd < 0xc000 ? IPPROTO_UDP :
            rnd < 0xd000 ? IPPROTO_SCTP : (uint8_t)rnd;
        if (flow->nw_proto == IPPROTO_TCP ||
            flow->nw_proto == IPPROTO_UDP ||
            flow->nw_proto == IPPROTO_SCTP) {
            flow->tp_src = (OVS_FORCE ovs_be16)random_uint16();
            flow->tp_dst = (OVS_FORCE ovs_be16)random_uint16();
        }
    }
}


miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
{
    uint32_t hash = basis;

    if (flow) {
        ovs_be16 dl_type = MINIFLOW_GET_BE16(flow, dl_type);

        hash = hash_add(hash, MINIFLOW_GET_U8(flow, nw_proto));

        /* Separate loops for better optimization. */
        if (dl_type == htons(ETH_TYPE_IPV6)) {
            struct flowmap map = FLOWMAP_EMPTY_INITIALIZER;
            uint64_t value;

            FLOWMAP_SET(&map, ipv6_src);
            FLOWMAP_SET(&map, ipv6_dst);

            MINIFLOW_FOR_EACH_IN_FLOWMAP(value, flow, map) {
                hash = hash_add64(hash, value);
            }
        } else {
            hash = hash_add(hash, MINIFLOW_GET_U32(flow, nw_src));
            hash = hash_add(hash, MINIFLOW_GET_U32(flow, nw_dst));
        }
        /* Add both ports at once. */
        hash = hash_add(hash, MINIFLOW_GET_U32(flow, tp_src));
        hash = hash_finish(hash, 42); /* Arbitrary number. */
    }
    return hash;
}


flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
               struct flow_wildcards *wc, bool clear_flow_L3)
{
    ovs_assert(eth_type_mpls(mpls_eth_type));
    ovs_assert(n < FLOW_MAX_MPLS_LABELS);

    if (n) {
        int i;

        if (wc) {
            memset(&wc->masks.mpls_lse, 0xff, sizeof *wc->masks.mpls_lse * n);
        }
        for (i = n; i >= 1; i--) {
            flow->mpls_lse[i] = flow->mpls_lse[i - 1];
        }
        flow->mpls_lse[0] = (flow->mpls_lse[1] & htonl(~MPLS_BOS_MASK));
    } else {
        int label = 0;          /* IPv4 Explicit Null. */
        int tc = 0;
        int ttl = 64;

        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
            label = 2;
        }

        if (is_ip_any(flow)) {
            tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
            if (wc) {
                wc->masks.nw_tos |= IP_DSCP_MASK;
                wc->masks.nw_ttl = 0xff;
            }

            if (flow->nw_ttl) {
                ttl = flow->nw_ttl;
            }
        }

        flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));

        if (clear_flow_L3) {
            /* Clear all L3 and L4 fields and dp_hash. */
            BUILD_ASSERT(FLOW_WC_SEQ == 35);
            memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
                   sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
            flow->dp_hash = 0;
        }
    }
    flow->dl_type = mpls_eth_type;
}


dp_packet_l4_size(const struct dp_packet *b)
{
    return b->l4_ofs != UINT16_MAX
        ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l4(b)
        - dp_packet_l2_pad_size(b)
        : 0;
}


static void handle_satn_stop(ESPState *s)
{
    int32_t cmdlen;

    if (s->dma && !s->dma_enabled) {
        s->dma_cb = handle_satn_stop;
        return;
    }
    s->pdma_cb = satn_stop_pdma_cb;
    cmdlen = get_cmd(s, 1);
    if (cmdlen > 0) {
        trace_esp_handle_satn_stop(fifo8_num_used(&s->cmdfifo));
        s->do_cmd = 1;
        s->cmdfifo_cdb_offset = 1;
        s->rregs[ESP_RSTAT] = STAT_MO;
        s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC;
        s->rregs[ESP_RSEQ] = SEQ_MO;
        esp_raise_irq(s);
    } else if (cmdlen == 0) {
        s->do_cmd = 1;
        /* Target present, switch to message out phase */
        s->rregs[ESP_RSEQ] = SEQ_MO;
        s->rregs[ESP_RSTAT] = STAT_MO;
    }
}


static void esp_init(Object *obj)
{
    ESPState *s = ESP(obj);

    fifo8_create(&s->fifo, ESP_FIFO_SZ);
    fifo8_create(&s->cmdfifo, ESP_CMDFIFO_SZ);
}


static void esp_pdma_write(ESPState *s, uint8_t val)
{
    uint32_t dmalen = esp_get_tc(s);

    if (dmalen == 0) {
        return;
    }

    if (s->do_cmd) {
        esp_fifo_push(&s->cmdfifo, val);
    } else {
        esp_fifo_push(&s->fifo, val);
    }

    dmalen--;
    esp_set_tc(s, dmalen);
}


void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
{
    trace_esp_mem_writeb(saddr, s->wregs[saddr], val);
    switch (saddr) {
    case ESP_TCHI:
        s->tchi_written = true;
        /* fall through */
    case ESP_TCLO:
    case ESP_TCMID:
        s->rregs[ESP_RSTAT] &= ~STAT_TC;
        break;
    case ESP_FIFO:
        if (s->do_cmd) {
            esp_fifo_push(&s->cmdfifo, val);
        } else {
            esp_fifo_push(&s->fifo, val);
        }

        /* Non-DMA transfers raise an interrupt after every byte */
        if (s->rregs[ESP_CMD] == CMD_TI) {
            s->rregs[ESP_RINTR] |= INTR_FC | INTR_BS;
            esp_raise_irq(s);
        }
        break;
    case ESP_CMD:
        s->rregs[saddr] = val;
        if (val & CMD_DMA) {
            s->dma = 1;
            /* Reload DMA counter.  */
            if (esp_get_stc(s) == 0) {
                esp_set_tc(s, 0x10000);
            } else {
                esp_set_tc(s, esp_get_stc(s));
            }
        } else {
            s->dma = 0;
        }
        switch (val & CMD_CMD) {
        case CMD_NOP:
            trace_esp_mem_writeb_cmd_nop(val);
            break;
        case CMD_FLUSH:
            trace_esp_mem_writeb_cmd_flush(val);
            fifo8_reset(&s->fifo);
            break;
        case CMD_RESET:
            trace_esp_mem_writeb_cmd_reset(val);
            esp_soft_reset(s);
            break;
        case CMD_BUSRESET:
            trace_esp_mem_writeb_cmd_bus_reset(val);
            if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) {
                s->rregs[ESP_RINTR] |= INTR_RST;
                esp_raise_irq(s);
            }
            break;
        case CMD_TI:
            trace_esp_mem_writeb_cmd_ti(val);
            handle_ti(s);
            break;
        case CMD_ICCS:
            trace_esp_mem_writeb_cmd_iccs(val);
            write_response(s);
            s->rregs[ESP_RINTR] |= INTR_FC;
            s->rregs[ESP_RSTAT] |= STAT_MI;
            break;
        case CMD_MSGACC:
            trace_esp_mem_writeb_cmd_msgacc(val);
            s->rregs[ESP_RINTR] |= INTR_DC;
            s->rregs[ESP_RSEQ] = 0;
            s->rregs[ESP_RFLAGS] = 0;
            esp_raise_irq(s);
            break;
        case CMD_PAD:
            trace_esp_mem_writeb_cmd_pad(val);
            s->rregs[ESP_RSTAT] = STAT_TC;
            s->rregs[ESP_RINTR] |= INTR_FC;
            s->rregs[ESP_RSEQ] = 0;
            break;
        case CMD_SATN:
            trace_esp_mem_writeb_cmd_satn(val);
            break;
        case CMD_RSTATN:
            trace_esp_mem_writeb_cmd_rstatn(val);
            break;
        case CMD_SEL:
            trace_esp_mem_writeb_cmd_sel(val);
            handle_s_without_atn(s);
            break;
        case CMD_SELATN:
            trace_esp_mem_writeb_cmd_selatn(val);
            handle_satn(s);
            break;
        case CMD_SELATNS:
            trace_esp_mem_writeb_cmd_selatns(val);
            handle_satn_stop(s);
            break;
        case CMD_ENSEL:
            trace_esp_mem_writeb_cmd_ensel(val);
            s->rregs[ESP_RINTR] = 0;
            break;
        case CMD_DISSEL:
            trace_esp_mem_writeb_cmd_dissel(val);
            s->rregs[ESP_RINTR] = 0;
            esp_raise_irq(s);
            break;
        default:
            trace_esp_error_unhandled_command(val);
            break;
        }
        break;
    case ESP_WBUSID ... ESP_WSYNO:
        break;
    case ESP_CFG1:
    case ESP_CFG2: case ESP_CFG3:
    case ESP_RES3: case ESP_RES4:
        s->rregs[saddr] = val;
        break;
    case ESP_WCCF ... ESP_WTEST:
        break;
    default:
        trace_esp_error_invalid_write(val, saddr);
        return;
    }
    s->wregs[saddr] = val;
}


static void do_busid_cmd(ESPState *s, uint8_t busid)
{
    uint32_t cmdlen;
    int32_t datalen;
    int lun;
    SCSIDevice *current_lun;
    uint8_t buf[ESP_CMDFIFO_SZ];

    trace_esp_do_busid_cmd(busid);
    lun = busid & 7;
    cmdlen = fifo8_num_used(&s->cmdfifo);
    if (!cmdlen || !s->current_dev) {
        return;
    }
    esp_fifo_pop_buf(&s->cmdfifo, buf, cmdlen);

    current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun);
    s->current_req = scsi_req_new(current_lun, 0, lun, buf, s);
    datalen = scsi_req_enqueue(s->current_req);
    s->ti_size = datalen;
    fifo8_reset(&s->cmdfifo);
    if (datalen != 0) {
        s->rregs[ESP_RSTAT] = STAT_TC;
        s->rregs[ESP_RSEQ] = SEQ_CD;
        s->ti_cmd = 0;
        esp_set_tc(s, 0);
        if (datalen > 0) {
            /*
             * Switch to DATA IN phase but wait until initial data xfer is
             * complete before raising the command completion interrupt
             */
            s->data_in_ready = false;
            s->rregs[ESP_RSTAT] |= STAT_DI;
        } else {
            s->rregs[ESP_RSTAT] |= STAT_DO;
            s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC;
            esp_raise_irq(s);
            esp_lower_drq(s);
        }
        scsi_req_continue(s->current_req);
        return;
    }
}


static void handle_s_without_atn(ESPState *s)
{
    int32_t cmdlen;

    if (s->dma && !s->dma_enabled) {
        s->dma_cb = handle_s_without_atn;
        return;
    }
    s->pdma_cb = s_without_satn_pdma_cb;
    cmdlen = get_cmd(s, ESP_CMDFIFO_SZ);
    if (cmdlen > 0) {
        s->cmdfifo_cdb_offset = 0;
        s->do_cmd = 0;
        do_busid_cmd(s, 0);
    } else if (cmdlen == 0) {
        s->do_cmd = 1;
        /* Target present, but no cmd yet - switch to command phase */
        s->rregs[ESP_RSEQ] = SEQ_CD;
        s->rregs[ESP_RSTAT] = STAT_CD;
    }
}


	while(1) {
		while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */
			brword b = br->buffer[br->consumed_words] << br->consumed_bits;
			if(b) {
				i = COUNT_ZERO_MSBS(b);
				*val += i;
				i++;
				br->consumed_bits += i;
				if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */
					br->consumed_words++;
					br->consumed_bits = 0;
				}
				return true;
			}
			else {
				*val += FLAC__BITS_PER_WORD - br->consumed_bits;
				br->consumed_words++;
				br->consumed_bits = 0;
				/* didn't find stop bit yet, have to keep going... */
			}
		}
		/* at this point we've eaten up all the whole words; have to try
		 * reading through any tail bytes before calling the read callback.
		 * this is a repeat of the above logic adjusted for the fact we
		 * don't have a whole word.  note though if the client is feeding
		 * us data a byte at a time (unlikely), br->consumed_bits may not
		 * be zero.
		 */
		if(br->bytes*8 > br->consumed_bits) {
			const uint32_t end = br->bytes * 8;
			brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits;
			if(b) {
				i = COUNT_ZERO_MSBS(b);
				*val += i;
				i++;
				br->consumed_bits += i;
				FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD);
				return true;
			}
			else {
				*val += end - br->consumed_bits;
				br->consumed_bits = end;
				FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD);
				/* didn't find stop bit yet, have to keep going... */
			}
		}
		if(!bitreader_read_from_client_(br))
			return false;
	}


void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed)
{
	FLAC__ASSERT(0 != br);
	FLAC__ASSERT(0 != br->buffer);
	FLAC__ASSERT((br->consumed_bits & 7) == 0);

	br->read_crc16 = (uint32_t)seed;
	br->crc16_offset = br->consumed_words;
	br->crc16_align = br->consumed_bits;
}