Skip to content

Commit b8aa7eb

Browse files
fanquakeTom Trevethan
authored andcommitted
Merge bitcoin/bitcoin#30085: p2p: detect addnode cjdns peers in GetAddedNodeInfo()
d0b0474 test: add GetAddedNodeInfo() CJDNS regression unit test (Jon Atack) 684da97 p2p, bugfix: detect addnode cjdns peers in GetAddedNodeInfo() (Jon Atack) Pull request description: Addnode peers connected to us via the cjdns network are currently not detected by `CConnman::GetAddedNodeInfo()`, i.e. `fConnected` is always false. This causes the following issues: - RPC `getaddednodeinfo` incorrectly shows them as not connected - `CConnman::ThreadOpenAddedConnections()` continually retries to connect them Fix the issue and add a unit regression test. Extracted from #28248. Suggest running the test with: `./src/test/test_bitcoin -t net_peer_connection_tests -l test_suite` ACKs for top commit: mzumsande: utACK d0b0474 brunoerg: crACK d0b0474 pinheadmz: ACK d0b0474 Tree-SHA512: a4d81425f79558f5792585611f3fe8ab999b82144daeed5c3ec619861c69add934c2b2afdad24c8488a0ade94f5ce8112f5555d60a1ce913d4f5a1cf5dbba55a
1 parent cb5c59e commit b8aa7eb

File tree

2 files changed

+164
-1
lines changed

2 files changed

+164
-1
lines changed

src/net.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2221,7 +2221,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
22212221
}
22222222

22232223
for (const std::string& strAddNode : lAddresses) {
2224-
CService service(LookupNumeric(strAddNode, Params().GetDefaultPort(strAddNode)));
2224+
CService service{MaybeFlipIPv6toCJDNS(LookupNumeric(strAddNode, Params().GetDefaultPort(strAddNode)))};
22252225
AddedNodeInfo addedNode{strAddNode, CService(), false, false};
22262226
if (service.IsValid()) {
22272227
// strAddNode is an IP:port
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright (c) 2023-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <chainparams.h>
6+
#include <compat/compat.h>
7+
#include <net.h>
8+
#include <net_processing.h>
9+
#include <netaddress.h>
10+
#include <netbase.h>
11+
#include <netgroup.h>
12+
#include <node/connection_types.h>
13+
#include <node/protocol_version.h>
14+
#include <protocol.h>
15+
#include <random.h>
16+
#include <test/util/logging.h>
17+
#include <test/util/net.h>
18+
#include <test/util/random.h>
19+
#include <test/util/setup_common.h>
20+
#include <tinyformat.h>
21+
#include <util/chaintype.h>
22+
23+
#include <algorithm>
24+
#include <cstdint>
25+
#include <memory>
26+
#include <optional>
27+
#include <string>
28+
#include <vector>
29+
30+
#include <boost/test/unit_test.hpp>
31+
32+
struct LogIPsTestingSetup : public TestingSetup {
33+
LogIPsTestingSetup()
34+
: TestingSetup{ChainType::MAIN, /*extra_args=*/{"-logips"}} {}
35+
};
36+
37+
BOOST_FIXTURE_TEST_SUITE(net_peer_connection_tests, LogIPsTestingSetup)
38+
39+
static CService ip(uint32_t i)
40+
{
41+
struct in_addr s;
42+
s.s_addr = i;
43+
return CService{CNetAddr{s}, Params().GetDefaultPort()};
44+
}
45+
46+
/** Create a peer and connect to it. If the optional `address` (IP/CJDNS only) isn't passed, a random address is created. */
47+
static void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, ConnmanTestMsg& connman, ConnectionType conn_type, bool onion_peer = false, std::optional<std::string> address = std::nullopt)
48+
{
49+
CAddress addr{};
50+
51+
if (address.has_value()) {
52+
addr = CAddress{MaybeFlipIPv6toCJDNS(LookupNumeric(address.value(), Params().GetDefaultPort())), NODE_NONE};
53+
} else if (onion_peer) {
54+
auto tor_addr{g_insecure_rand_ctx.randbytes(ADDR_TORV3_SIZE)};
55+
BOOST_REQUIRE(addr.SetSpecial(OnionToString(tor_addr)));
56+
}
57+
58+
while (!addr.IsLocal() && !addr.IsRoutable()) {
59+
addr = CAddress{ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE};
60+
}
61+
62+
BOOST_REQUIRE(addr.IsValid());
63+
64+
const bool inbound_onion{onion_peer && conn_type == ConnectionType::INBOUND};
65+
66+
nodes.emplace_back(new CNode{++id,
67+
/*sock=*/nullptr,
68+
addr,
69+
/*nKeyedNetGroupIn=*/0,
70+
/*nLocalHostNonceIn=*/0,
71+
CAddress{},
72+
/*addrNameIn=*/"",
73+
conn_type,
74+
/*inbound_onion=*/inbound_onion});
75+
CNode& node = *nodes.back();
76+
node.SetCommonVersion(PROTOCOL_VERSION);
77+
78+
peerman.InitializeNode(node, ServiceFlags(NODE_NETWORK | NODE_WITNESS));
79+
node.fSuccessfullyConnected = true;
80+
81+
connman.AddTestNode(node);
82+
}
83+
84+
BOOST_AUTO_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection)
85+
{
86+
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params());
87+
auto peerman = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, {});
88+
NodeId id{0};
89+
std::vector<CNode*> nodes;
90+
91+
// Connect a localhost peer.
92+
{
93+
ASSERT_DEBUG_LOG("Added connection to 127.0.0.1:8333 peer=1");
94+
AddPeer(id, nodes, *peerman, *connman, ConnectionType::MANUAL, /*onion_peer=*/false, /*address=*/"127.0.0.1");
95+
BOOST_REQUIRE(nodes.back() != nullptr);
96+
}
97+
98+
// Call ConnectNode(), which is also called by RPC addnode onetry, for a localhost
99+
// address that resolves to multiple IPs, including that of the connected peer.
100+
// The connection attempt should consistently fail due to the check in ConnectNode().
101+
for (int i = 0; i < 10; ++i) {
102+
ASSERT_DEBUG_LOG("Not opening a connection to localhost, already connected to 127.0.0.1:8333");
103+
BOOST_CHECK(!connman->ConnectNodePublic(*peerman, "localhost", ConnectionType::MANUAL));
104+
}
105+
106+
// Add 3 more peer connections.
107+
AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY);
108+
AddPeer(id, nodes, *peerman, *connman, ConnectionType::BLOCK_RELAY, /*onion_peer=*/true);
109+
AddPeer(id, nodes, *peerman, *connman, ConnectionType::INBOUND);
110+
111+
// Add a CJDNS peer connection.
112+
AddPeer(id, nodes, *peerman, *connman, ConnectionType::INBOUND, /*onion_peer=*/false,
113+
/*address=*/"[fc00:3344:5566:7788:9900:aabb:ccdd:eeff]:1234");
114+
BOOST_CHECK(nodes.back()->IsInboundConn());
115+
BOOST_CHECK_EQUAL(nodes.back()->ConnectedThroughNetwork(), Network::NET_CJDNS);
116+
117+
BOOST_TEST_MESSAGE("Call AddNode() for all the peers");
118+
for (auto node : connman->TestNodes()) {
119+
BOOST_CHECK(connman->AddNode({/*m_added_node=*/node->addr.ToStringAddrPort(), /*m_use_v2transport=*/true}));
120+
BOOST_TEST_MESSAGE(strprintf("peer id=%s addr=%s", node->GetId(), node->addr.ToStringAddrPort()));
121+
}
122+
123+
BOOST_TEST_MESSAGE("\nCall AddNode() with 2 addrs resolving to existing localhost addnode entry; neither should be added");
124+
BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.0.0.1", /*m_use_v2transport=*/true}));
125+
// OpenBSD doesn't support the IPv4 shorthand notation with omitted zero-bytes.
126+
#if !defined(__OpenBSD__)
127+
BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.1", /*m_use_v2transport=*/true}));
128+
#endif
129+
130+
BOOST_TEST_MESSAGE("\nExpect GetAddedNodeInfo to return expected number of peers with `include_connected` true/false");
131+
BOOST_CHECK_EQUAL(connman->GetAddedNodeInfo(/*include_connected=*/true).size(), nodes.size());
132+
BOOST_CHECK(connman->GetAddedNodeInfo(/*include_connected=*/false).empty());
133+
134+
// Test AddedNodesContain()
135+
for (auto node : connman->TestNodes()) {
136+
BOOST_CHECK(connman->AddedNodesContain(node->addr));
137+
}
138+
AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY);
139+
BOOST_CHECK(!connman->AddedNodesContain(nodes.back()->addr));
140+
141+
BOOST_TEST_MESSAGE("\nPrint GetAddedNodeInfo contents:");
142+
for (const auto& info : connman->GetAddedNodeInfo(/*include_connected=*/true)) {
143+
BOOST_TEST_MESSAGE(strprintf("\nadded node: %s", info.m_params.m_added_node));
144+
BOOST_TEST_MESSAGE(strprintf("connected: %s", info.fConnected));
145+
if (info.fConnected) {
146+
BOOST_TEST_MESSAGE(strprintf("IP address: %s", info.resolvedAddress.ToStringAddrPort()));
147+
BOOST_TEST_MESSAGE(strprintf("direction: %s", info.fInbound ? "inbound" : "outbound"));
148+
}
149+
}
150+
151+
BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected");
152+
for (auto node : connman->TestNodes()) {
153+
BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr));
154+
}
155+
156+
// Clean up
157+
for (auto node : connman->TestNodes()) {
158+
peerman->FinalizeNode(*node);
159+
}
160+
connman->ClearTestNodes();
161+
}
162+
163+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)