mirror of
https://github.com/monero-project/monero.git
synced 2024-12-23 12:09:54 +00:00
p2p: do not send last_seen timestamp to peers
This can be used for fingerprinting and working out the network topology. Instead of sending the first N (which are sorted by last seen time), we sent a random subset of the first N+N/5, which ensures reasonably recent peers are used, while preventing repeated calls from deducing new entries are peers the target node just connected to. The list is also randomly shuffled so the original set of timestamps cannot be approximated.
This commit is contained in:
parent
475481949a
commit
28a7d31565
3 changed files with 30 additions and 12 deletions
|
@ -1955,7 +1955,7 @@ namespace nodetool
|
|||
const epee::net_utils::zone zone_type = context.m_remote_address.get_zone();
|
||||
network_zone& zone = m_network_zones.at(zone_type);
|
||||
|
||||
zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new);
|
||||
zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true);
|
||||
m_payload_handler.get_payload_sync_data(rsp.payload_data);
|
||||
|
||||
/* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon)
|
||||
|
@ -2058,7 +2058,7 @@ namespace nodetool
|
|||
});
|
||||
|
||||
//fill response
|
||||
zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new);
|
||||
zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true);
|
||||
get_local_node_data(rsp.node_data, zone);
|
||||
m_payload_handler.get_payload_sync_data(rsp.payload_data);
|
||||
LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE");
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace nodetool
|
|||
size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();}
|
||||
size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();}
|
||||
bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs);
|
||||
bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
|
||||
bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
|
||||
void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
|
||||
void get_peerlist(peerlist_types& peers);
|
||||
bool get_white_peer_by_index(peerlist_entry& p, size_t i);
|
||||
|
@ -263,23 +263,40 @@ namespace nodetool
|
|||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
inline
|
||||
bool peerlist_manager::get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth)
|
||||
bool peerlist_manager::get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth)
|
||||
{
|
||||
|
||||
CRITICAL_REGION_LOCAL(m_peerlist_lock);
|
||||
peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>();
|
||||
uint32_t cnt = 0;
|
||||
bs_head.reserve(depth);
|
||||
|
||||
// picks a random set of peers within the first 120%, rather than a set of the first 100%.
|
||||
// The intent is that if someone asks twice, they can't easily tell:
|
||||
// - this address was not in the first list, but is in the second, so the only way this can be
|
||||
// is if its last_seen was recently reset, so this means the target node recently had a new
|
||||
// connection to that address
|
||||
// - this address was in the first list, and not in the second, which means either the address
|
||||
// was moved to the gray list (if it's not accessibe, which the attacker can check if
|
||||
// the address accepts incoming connections) or it was the oldest to still fit in the 250 items,
|
||||
// so its last_seen is old.
|
||||
const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth;
|
||||
bs_head.reserve(pick_depth);
|
||||
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
|
||||
{
|
||||
if(!vl.last_seen)
|
||||
continue;
|
||||
|
||||
if(cnt++ >= depth)
|
||||
if(cnt++ >= pick_depth)
|
||||
break;
|
||||
|
||||
bs_head.push_back(vl);
|
||||
}
|
||||
|
||||
if (anonymize)
|
||||
{
|
||||
std::random_shuffle(bs_head.begin(), bs_head.end());
|
||||
if (bs_head.size() > depth)
|
||||
bs_head.resize(depth);
|
||||
for (auto &e: bs_head)
|
||||
e.last_seen = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -81,7 +81,8 @@ namespace nodetool
|
|||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(adr)
|
||||
KV_SERIALIZE(id)
|
||||
KV_SERIALIZE(last_seen)
|
||||
if (!is_store || this_ref.last_seen != 0)
|
||||
KV_SERIALIZE_OPT(last_seen, (int64_t)0)
|
||||
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
|
||||
KV_SERIALIZE_OPT(rpc_port, (uint16_t)0)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
@ -132,7 +133,7 @@ namespace nodetool
|
|||
ss << pe.id << "\t" << pe.adr.str()
|
||||
<< " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-")
|
||||
<< " \tpruning seed " << pe.pruning_seed
|
||||
<< " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen)
|
||||
<< " \tlast_seen: " << (pe.last_seen == 0 ? std::string("never") : epee::misc_utils::get_time_interval_string(now_time - pe.last_seen))
|
||||
<< std::endl;
|
||||
}
|
||||
return ss.str();
|
||||
|
|
Loading…
Reference in a new issue