conntrack_flush_tuple(struct conntrack *ct, const struct ct_dpif_tuple *tuple,
                      uint16_t zone)
{
    struct conn_lookup_ctx ctx;
    int error = 0;

    memset(&ctx, 0, sizeof(ctx));
    tuple_to_conn_key(tuple, zone, &ctx.key);
    ctx.hash = conn_key_hash(&ctx.key, ct->hash_basis);
    unsigned bucket = hash_to_bucket(ctx.hash);
    struct conntrack_bucket *ctb = &ct->buckets[bucket];

    ovs_mutex_lock(&ctb->cleanup_mutex);
    ct_lock_lock(&ctb->lock);
    conn_key_lookup(ctb, &ctx, time_msec());
    if (ctx.conn && ctx.conn->conn_type == CT_CONN_TYPE_DEFAULT) {
        conn_clean(ct, ctx.conn, ctb);
    } else {
        VLOG_WARN("Must flush tuple using the original pre-NATed tuple");
        error = ENOENT;
    }
    ct_lock_unlock(&ctb->lock);
    ovs_mutex_unlock(&ctb->cleanup_mutex);
    return error;
}
conntrack_flush_tuple(struct conntrack *ct, const struct ct_dpif_tuple *tuple,
                      uint16_t zone)
{
    struct conn_lookup_ctx ctx;
    int error = 0;

    memset(&ctx, 0, sizeof(ctx));
    tuple_to_conn_key(tuple, zone, &ctx.key);
    ctx.hash = conn_key_hash(&ctx.key, ct->hash_basis);
    unsigned bucket = hash_to_bucket(ctx.hash);
    struct conntrack_bucket *ctb = &ct->buckets[bucket];

    ovs_mutex_lock(&ctb->cleanup_mutex);
    ct_lock_lock(&ctb->lock);
    conn_key_lookup(ctb, &ctx, time_msec());
    if (ctx.conn && ctx.conn->conn_type == CT_CONN_TYPE_DEFAULT) {
        conn_clean(ct, ctx.conn, ctb);
    } else {
        VLOG_WARN("Must flush tuple using the original pre-NATed tuple");
        error = ENOENT;
    }
    ct_lock_unlock(&ctb->lock);
    ovs_mutex_unlock(&ctb->cleanup_mutex);
    return error;
}