nat_select_range_tuple(struct conntrack *ct, const struct conn *conn,
                       struct conn *nat_conn)
{
    enum { MIN_NAT_EPHEMERAL_PORT = 1024,
           MAX_NAT_EPHEMERAL_PORT = 65535 };

    uint16_t min_port;
    uint16_t max_port;
    uint16_t first_port;
    uint32_t hash = nat_range_hash(conn, ct->hash_basis);

    if ((conn->nat_info->nat_action & NAT_ACTION_SRC) &&
        (!(conn->nat_info->nat_action & NAT_ACTION_SRC_PORT))) {
        min_port = ntohs(conn->key.src.port);
        max_port = ntohs(conn->key.src.port);
        first_port = min_port;
    } else if ((conn->nat_info->nat_action & NAT_ACTION_DST) &&
               (!(conn->nat_info->nat_action & NAT_ACTION_DST_PORT))) {
        min_port = ntohs(conn->key.dst.port);
        max_port = ntohs(conn->key.dst.port);
        first_port = min_port;
    } else {
        uint16_t deltap = conn->nat_info->max_port - conn->nat_info->min_port;
        uint32_t port_index = hash % (deltap + 1);
        first_port = conn->nat_info->min_port + port_index;
        min_port = conn->nat_info->min_port;
        max_port = conn->nat_info->max_port;
    }

    uint32_t deltaa = 0;
    uint32_t address_index;
    union ct_addr ct_addr;
    memset(&ct_addr, 0, sizeof ct_addr);
    union ct_addr max_ct_addr;
    memset(&max_ct_addr, 0, sizeof max_ct_addr);
    max_ct_addr = conn->nat_info->max_addr;

    if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
        deltaa = ntohl(conn->nat_info->max_addr.ipv4) -
                 ntohl(conn->nat_info->min_addr.ipv4);
        address_index = hash % (deltaa + 1);
        ct_addr.ipv4 = htonl(
            ntohl(conn->nat_info->min_addr.ipv4) + address_index);
    } else {
        deltaa = nat_ipv6_addrs_delta(&conn->nat_info->min_addr.ipv6,
                                      &conn->nat_info->max_addr.ipv6);
        /* deltaa must be within 32 bits for full hash coverage. A 64 or
         * 128 bit hash is unnecessary and hence not used here. Most code
         * is kept common with V4; nat_ipv6_addrs_delta() will do the
         * enforcement via max_ct_addr. */
        max_ct_addr = conn->nat_info->min_addr;
        nat_ipv6_addr_increment(&max_ct_addr.ipv6, deltaa);
        address_index = hash % (deltaa + 1);
        ct_addr.ipv6 = conn->nat_info->min_addr.ipv6;
        nat_ipv6_addr_increment(&ct_addr.ipv6, address_index);
    }

    uint16_t port = first_port;
    bool all_ports_tried = false;
    /* For DNAT or for specified port ranges, we don't use ephemeral ports. */
    bool ephemeral_ports_tried
        = conn->nat_info->nat_action & NAT_ACTION_DST ||
              conn->nat_info->nat_action & NAT_ACTION_SRC_PORT
          ? true : false;
    union ct_addr first_addr = ct_addr;
    bool pat_enabled = conn->key.nw_proto != IPPROTO_ICMP &&
                       conn->key.nw_proto != IPPROTO_ICMPV6;

    while (true) {
        if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
            nat_conn->rev_key.dst.addr = ct_addr;
            if (pat_enabled) {
                nat_conn->rev_key.dst.port = htons(port);
            }
        } else {
            nat_conn->rev_key.src.addr = ct_addr;
            if (pat_enabled) {
                nat_conn->rev_key.src.port = htons(port);
            }
        }

        bool found = conn_lookup(ct, &nat_conn->rev_key, time_msec(), NULL,
                                 NULL);
        if (!found) {
            return true;
        } else if (pat_enabled && !all_ports_tried) {
            if (min_port == max_port) {
                all_ports_tried = true;
            } else if (port == max_port) {
                port = min_port;
            } else {
                port++;
            }
            if (port == first_port) {
                all_ports_tried = true;
            }
        } else {
            if (memcmp(&ct_addr, &max_ct_addr, sizeof ct_addr)) {
                if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
                    ct_addr.ipv4 = htonl(ntohl(ct_addr.ipv4) + 1);
                } else {
                    nat_ipv6_addr_increment(&ct_addr.ipv6, 1);
                }
            } else {
                ct_addr = conn->nat_info->min_addr;
            }
            if (!memcmp(&ct_addr, &first_addr, sizeof ct_addr)) {
                if (pat_enabled && !ephemeral_ports_tried) {
                    ephemeral_ports_tried = true;
                    ct_addr = conn->nat_info->min_addr;
                    first_addr = ct_addr;
                    min_port = MIN_NAT_EPHEMERAL_PORT;
                    max_port = MAX_NAT_EPHEMERAL_PORT;
                } else {
                    break;
                }
            }
            first_port = min_port;
            port = first_port;
            all_ports_tried = false;
        }
    }
    return false;
}
nat_select_range_tuple(struct conntrack *ct, const struct conn *conn,
                       struct conn *nat_conn)
{
    enum { MIN_NAT_EPHEMERAL_PORT = 1024,
           MAX_NAT_EPHEMERAL_PORT = 65535 };

    uint16_t min_port;
    uint16_t max_port;
    uint16_t first_port;
    uint32_t hash = nat_range_hash(conn, ct->hash_basis);

    if ((conn->nat_info->nat_action & NAT_ACTION_SRC) &&
        (!(conn->nat_info->nat_action & NAT_ACTION_SRC_PORT))) {
        min_port = ntohs(conn->key.src.port);
        max_port = ntohs(conn->key.src.port);
        first_port = min_port;
    } else if ((conn->nat_info->nat_action & NAT_ACTION_DST) &&
               (!(conn->nat_info->nat_action & NAT_ACTION_DST_PORT))) {
        min_port = ntohs(conn->key.dst.port);
        max_port = ntohs(conn->key.dst.port);
        first_port = min_port;
    } else {
        uint16_t deltap = conn->nat_info->max_port - conn->nat_info->min_port;
        uint32_t port_index = hash % (deltap + 1);
        first_port = conn->nat_info->min_port + port_index;
        min_port = conn->nat_info->min_port;
        max_port = conn->nat_info->max_port;
    }

    uint32_t deltaa = 0;
    uint32_t address_index;
    union ct_addr ct_addr;
    memset(&ct_addr, 0, sizeof ct_addr);
    union ct_addr max_ct_addr;
    memset(&max_ct_addr, 0, sizeof max_ct_addr);
    max_ct_addr = conn->nat_info->max_addr;

    if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
        deltaa = ntohl(conn->nat_info->max_addr.ipv4) -
                 ntohl(conn->nat_info->min_addr.ipv4);
        address_index = hash % (deltaa + 1);
        ct_addr.ipv4 = htonl(
            ntohl(conn->nat_info->min_addr.ipv4) + address_index);
    } else {
        deltaa = nat_ipv6_addrs_delta(&conn->nat_info->min_addr.ipv6,
                                      &conn->nat_info->max_addr.ipv6);
         
        max_ct_addr = conn->nat_info->min_addr;
        nat_ipv6_addr_increment(&max_ct_addr.ipv6, deltaa);
        address_index = hash % (deltaa + 1);
        ct_addr.ipv6 = conn->nat_info->min_addr.ipv6;
        nat_ipv6_addr_increment(&ct_addr.ipv6, address_index);
    }

    uint16_t port = first_port;
    bool all_ports_tried = false;
     
    bool ephemeral_ports_tried
        = conn->nat_info->nat_action & NAT_ACTION_DST ||
              conn->nat_info->nat_action & NAT_ACTION_SRC_PORT
          ? true : false;
    union ct_addr first_addr = ct_addr;
    bool pat_enabled = conn->key.nw_proto != IPPROTO_ICMP &&
                       conn->key.nw_proto != IPPROTO_ICMPV6;

    while (true) {
        if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
            nat_conn->rev_key.dst.addr = ct_addr;
            if (pat_enabled) {
                nat_conn->rev_key.dst.port = htons(port);
            }
        } else {
            nat_conn->rev_key.src.addr = ct_addr;
            if (pat_enabled) {
                nat_conn->rev_key.src.port = htons(port);
            }
        }

        bool found = conn_lookup(ct, &nat_conn->rev_key, time_msec(), NULL,
                                 NULL);
        if (!found) {
            return true;
        } else if (pat_enabled && !all_ports_tried) {
            if (min_port == max_port) {
                all_ports_tried = true;
            } else if (port == max_port) {
                port = min_port;
            } else {
                port++;
            }
            if (port == first_port) {
                all_ports_tried = true;
            }
        } else {
            if (memcmp(&ct_addr, &max_ct_addr, sizeof ct_addr)) {
                if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
                    ct_addr.ipv4 = htonl(ntohl(ct_addr.ipv4) + 1);
                } else {
                    nat_ipv6_addr_increment(&ct_addr.ipv6, 1);
                }
            } else {
                ct_addr = conn->nat_info->min_addr;
            }
            if (!memcmp(&ct_addr, &first_addr, sizeof ct_addr)) {
                if (pat_enabled && !ephemeral_ports_tried) {
                    ephemeral_ports_tried = true;
                    ct_addr = conn->nat_info->min_addr;
                    first_addr = ct_addr;
                    min_port = MIN_NAT_EPHEMERAL_PORT;
                    max_port = MAX_NAT_EPHEMERAL_PORT;
                } else {
                    break;
                }
            }
            first_port = min_port;
            port = first_port;
            all_ports_tried = false;
        }
    }
    return false;
}