Skip to content

Commit b9416c3

Browse files
committed
Merge bitcoin/bitcoin#25096: [net] Minor improvements to addr caching
292828c [test] Test addr cache for multiple onion binds (dergoegge) 3382905 [net] Seed addr cache randomizer with port from binding address (dergoegge) f10e80b [net] Use ConnectedThroughNetwork() instead of GetNetwork() to seed addr cache randomizer (dergoegge) Pull request description: The addr cache id randomizer is currently supposed to be seeded with the network of the inbound connection and the local socket (only the address is used not the port): https://github.com/bitcoin/bitcoin/blob/a8098f2cef53ec003edae91100afce564e9c6f23/src/net.cpp#L2800-L2804 For inbound onion connections `CNode::addr.GetNetwork()` returns `NET_UNROUTABLE` and `CNode::addrBind` is set to `127.0.0.1:<onion bind port>`. This results in the same addr cache for all inbound connections on 127.0.0.1 binds. To avoid the same addr cache across all onion and other 127.0.0.1 binds, we should seed the addr cache randomizer with the correct network for inbound onion connections (using `CNode::ConnectedThroughNetwork()`) as well as the port of `CNode::addrBind`. ACKs for top commit: sipa: utACK 292828c mzumsande: Code Review ACK 292828c naumenkogs: utACK 292828c Tree-SHA512: d0be13bab6bc121c2926d4b168687f6c2ed4ce0c9dd19be71eb4886adeba8afc3daacdc4e232a0ba3b03a89d69b618abc5595b69abd1ad0c476d825bc6ea1f9f
2 parents bbf2a25 + 292828c commit b9416c3

File tree

2 files changed

+57
-14
lines changed

2 files changed

+57
-14
lines changed

src/net.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2814,8 +2814,11 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
28142814
{
28152815
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
28162816
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
2817-
.Write(requestor.addr.GetNetwork())
2817+
.Write(requestor.ConnectedThroughNetwork())
28182818
.Write(local_socket_bytes.data(), local_socket_bytes.size())
2819+
// For outbound connections, the port of the bound address is randomly
2820+
// assigned by the OS and would therefore not be useful for seeding.
2821+
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
28192822
.Finalize();
28202823
const auto current_time = GetTime<std::chrono::microseconds>();
28212824
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});

test/functional/p2p_getaddr_caching.py

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from test_framework.test_framework import BitcoinTestFramework
1515
from test_framework.util import (
1616
assert_equal,
17+
PORT_MIN,
18+
PORT_RANGE,
1719
)
1820

1921
# As defined in net_processing.
@@ -42,6 +44,13 @@ def addr_received(self):
4244
class AddrTest(BitcoinTestFramework):
4345
def set_test_params(self):
4446
self.num_nodes = 1
47+
# Start onion ports after p2p and rpc ports.
48+
port = PORT_MIN + 2 * PORT_RANGE
49+
self.onion_port1 = port
50+
self.onion_port2 = port + 1
51+
self.extra_args = [
52+
[f"-bind=127.0.0.1:{self.onion_port1}=onion", f"-bind=127.0.0.1:{self.onion_port2}=onion"],
53+
]
4554

4655
def run_test(self):
4756
self.log.info('Fill peer AddrMan with a lot of records')
@@ -55,35 +64,66 @@ def run_test(self):
5564
# only a fraction of all known addresses can be cached and returned.
5665
assert(len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100)))
5766

58-
responses = []
67+
last_response_on_local_bind = None
68+
last_response_on_onion_bind1 = None
69+
last_response_on_onion_bind2 = None
5970
self.log.info('Send many addr requests within short time to receive same response')
6071
N = 5
6172
cur_mock_time = int(time.time())
6273
for i in range(N):
63-
addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
64-
addr_receiver.send_and_ping(msg_getaddr())
74+
addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
75+
addr_receiver_local.send_and_ping(msg_getaddr())
76+
addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
77+
addr_receiver_onion1.send_and_ping(msg_getaddr())
78+
addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
79+
addr_receiver_onion2.send_and_ping(msg_getaddr())
80+
6581
# Trigger response
6682
cur_mock_time += 5 * 60
6783
self.nodes[0].setmocktime(cur_mock_time)
68-
addr_receiver.wait_until(addr_receiver.addr_received)
69-
responses.append(addr_receiver.get_received_addrs())
70-
for response in responses[1:]:
71-
assert_equal(response, responses[0])
72-
assert(len(response) == MAX_ADDR_TO_SEND)
84+
addr_receiver_local.wait_until(addr_receiver_local.addr_received)
85+
addr_receiver_onion1.wait_until(addr_receiver_onion1.addr_received)
86+
addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received)
87+
88+
if i > 0:
89+
# Responses from different binds should be unique
90+
assert(last_response_on_local_bind != addr_receiver_onion1.get_received_addrs())
91+
assert(last_response_on_local_bind != addr_receiver_onion2.get_received_addrs())
92+
assert(last_response_on_onion_bind1 != addr_receiver_onion2.get_received_addrs())
93+
# Responses on from the same bind should be the same
94+
assert_equal(last_response_on_local_bind, addr_receiver_local.get_received_addrs())
95+
assert_equal(last_response_on_onion_bind1, addr_receiver_onion1.get_received_addrs())
96+
assert_equal(last_response_on_onion_bind2, addr_receiver_onion2.get_received_addrs())
97+
98+
last_response_on_local_bind = addr_receiver_local.get_received_addrs()
99+
last_response_on_onion_bind1 = addr_receiver_onion1.get_received_addrs()
100+
last_response_on_onion_bind2 = addr_receiver_onion2.get_received_addrs()
101+
102+
for response in [last_response_on_local_bind, last_response_on_onion_bind1, last_response_on_onion_bind2]:
103+
assert_equal(len(response), MAX_ADDR_TO_SEND)
73104

74105
cur_mock_time += 3 * 24 * 60 * 60
75106
self.nodes[0].setmocktime(cur_mock_time)
76107

77108
self.log.info('After time passed, see a new response to addr request')
78-
last_addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
79-
last_addr_receiver.send_and_ping(msg_getaddr())
109+
addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
110+
addr_receiver_local.send_and_ping(msg_getaddr())
111+
addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
112+
addr_receiver_onion1.send_and_ping(msg_getaddr())
113+
addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
114+
addr_receiver_onion2.send_and_ping(msg_getaddr())
115+
80116
# Trigger response
81117
cur_mock_time += 5 * 60
82118
self.nodes[0].setmocktime(cur_mock_time)
83-
last_addr_receiver.wait_until(last_addr_receiver.addr_received)
84-
# new response is different
85-
assert(set(responses[0]) != set(last_addr_receiver.get_received_addrs()))
119+
addr_receiver_local.wait_until(addr_receiver_local.addr_received)
120+
addr_receiver_onion1.wait_until(addr_receiver_onion1.addr_received)
121+
addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received)
86122

123+
# new response is different
124+
assert(set(last_response_on_local_bind) != set(addr_receiver_local.get_received_addrs()))
125+
assert(set(last_response_on_onion_bind1) != set(addr_receiver_onion1.get_received_addrs()))
126+
assert(set(last_response_on_onion_bind2) != set(addr_receiver_onion2.get_received_addrs()))
87127

88128
if __name__ == '__main__':
89129
AddrTest().main()

0 commit comments

Comments
 (0)