From 62d73f5370415f910c95a67b3d9f97bc85487bbe Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Tue, 11 Apr 2023 15:09:31 -0400 Subject: [PATCH 1/4] net, refactor: pass CNode instead of CNetAddr to GetLocalAddress Access to CNode will be needed in the following commits. --- src/net.cpp | 10 +++++----- src/net.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index a53835f999..ab0d34fc63 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -153,7 +153,7 @@ uint16_t GetListenPort() } // find 'best' local address for a particular peer -bool GetLocal(CService& addr, const CNetAddr *paddrPeer) +bool GetLocal(CService& addr, const CNode& peer) { if (!fListen) return false; @@ -165,7 +165,7 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) for (const auto& entry : mapLocalHost) { int nScore = entry.second.nScore; - int nReachability = entry.first.GetReachabilityFrom(paddrPeer); + int nReachability = entry.first.GetReachabilityFrom(&peer.addr); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService(entry.first, entry.second.nPort); @@ -203,10 +203,10 @@ static std::vector ConvertSeeds(const std::vector &vSeedsIn) // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. -CService GetLocalAddress(const CNetAddr& addrPeer) +CService GetLocalAddress(const CNode& peer) { CService addr; - if (GetLocal(addr, &addrPeer)) { + if (GetLocal(addr, peer)) { return addr; } return CService{CNetAddr(), GetListenPort()}; @@ -229,7 +229,7 @@ bool IsPeerAddrLocalGood(CNode *pnode) std::optional GetLocalAddrForPeer(CNode& node) { - CService addrLocal{GetLocalAddress(node.addr)}; + CService addrLocal{GetLocalAddress(node)}; if (gArgs.GetBoolArg("-addrmantest", false)) { // use IPv4 loopback during addrmantest addrLocal = CService(LookupNumeric("127.0.0.1", GetListenPort())); diff --git a/src/net.h b/src/net.h index 83fe0427d4..e161671853 100644 --- a/src/net.h +++ b/src/net.h @@ -164,8 +164,8 @@ bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); void RemoveLocal(const CService& addr); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); -bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); -CService GetLocalAddress(const CNetAddr& addrPeer); +bool GetLocal(CService& addr, const CNode& peer); +CService GetLocalAddress(const CNode& peer); CService MaybeFlipIPv6toCJDNS(const CService& service); From e4d541c7cfa65da77e80e6786fdcb197ab50d04b Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Tue, 11 Apr 2023 16:10:28 -0400 Subject: [PATCH 2/4] net, refactor: pass reference for peer address in GetReachabilityFrom The address of the peer always exists (because addr is a member of CNode), so it was not possible to pass a nullptr before. Also remove NET_UNKNOWN, which is unused now. --- src/net.cpp | 2 +- src/netaddress.cpp | 16 ++++++---------- src/netaddress.h | 2 +- src/test/fuzz/netaddress.cpp | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index ab0d34fc63..9379f5be78 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -165,7 +165,7 @@ bool GetLocal(CService& addr, const CNode& peer) for (const auto& entry : mapLocalHost) { int nScore = entry.second.nScore; - int nReachability = entry.first.GetReachabilityFrom(&peer.addr); + int nReachability = entry.first.GetReachabilityFrom(peer.addr); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService(entry.first, entry.second.nPort); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 85ae8fab36..4758f24680 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -723,19 +723,16 @@ std::vector CNetAddr::GetAddrBytes() const // private extensions to enum Network, only returned by GetExtNetwork, // and only used in GetReachabilityFrom -static const int NET_UNKNOWN = NET_MAX + 0; -static const int NET_TEREDO = NET_MAX + 1; -int static GetExtNetwork(const CNetAddr *addr) +static const int NET_TEREDO = NET_MAX; +int static GetExtNetwork(const CNetAddr& addr) { - if (addr == nullptr) - return NET_UNKNOWN; - if (addr->IsRFC4380()) + if (addr.IsRFC4380()) return NET_TEREDO; - return addr->GetNetwork(); + return addr.GetNetwork(); } /** Calculates a metric for how reachable (*this) is from a given partner */ -int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const +int CNetAddr::GetReachabilityFrom(const CNetAddr& paddrPartner) const { enum Reachability { REACH_UNREACHABLE, @@ -750,7 +747,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const if (!IsRoutable() || IsInternal()) return REACH_UNREACHABLE; - int ourNet = GetExtNetwork(this); + int ourNet = GetExtNetwork(*this); int theirNet = GetExtNetwork(paddrPartner); bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); @@ -790,7 +787,6 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; } - case NET_UNKNOWN: case NET_UNROUTABLE: default: switch(ourNet) { diff --git a/src/netaddress.h b/src/netaddress.h index 3d15b0b123..36dc886406 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -203,7 +203,7 @@ public: bool HasLinkedIPv4() const; std::vector GetAddrBytes() const; - int GetReachabilityFrom(const CNetAddr* paddrPartner = nullptr) const; + int GetReachabilityFrom(const CNetAddr& paddrPartner) const; explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); bool GetIn6Addr(struct in6_addr* pipv6Addr) const; diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp index 049ae02f4d..5141d3362d 100644 --- a/src/test/fuzz/netaddress.cpp +++ b/src/test/fuzz/netaddress.cpp @@ -84,7 +84,7 @@ FUZZ_TARGET(netaddress) (void)CServiceHash(0, 0)(service); const CNetAddr other_net_addr = ConsumeNetAddr(fuzzed_data_provider); - (void)net_addr.GetReachabilityFrom(&other_net_addr); + (void)net_addr.GetReachabilityFrom(other_net_addr); (void)sub_net.Match(other_net_addr); const CService other_service{net_addr, fuzzed_data_provider.ConsumeIntegral()}; From f4754b9dfb84859166843fb2a1888fb3cfebf73c Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Tue, 11 Apr 2023 15:27:13 -0400 Subject: [PATCH 3/4] net: restrict self-advertisements with privacy networks Stop advertising 1) our i2p/onion address to peers from other networks 2) Local addresses of non-privacy networks to i2p/onion peers Doing so could lead to fingerprinting ourselves. Co-authored-by: Vasil Dimov --- src/net.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/net.cpp b/src/net.cpp index 9379f5be78..ac94f14fcc 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -164,6 +164,16 @@ bool GetLocal(CService& addr, const CNode& peer) LOCK(g_maplocalhost_mutex); for (const auto& entry : mapLocalHost) { + // For privacy reasons, don't advertise our privacy-network address + // to other networks and don't advertise our other-network address + // to privacy networks. + const Network our_net{entry.first.GetNetwork()}; + const Network peers_net{peer.ConnectedThroughNetwork()}; + if (our_net != peers_net && + (our_net == NET_ONION || our_net == NET_I2P || + peers_net == NET_ONION || peers_net == NET_I2P)) { + continue; + } int nScore = entry.second.nScore; int nReachability = entry.first.GetReachabilityFrom(peer.addr); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) From e7cf8657e1165ea5da3911a9e543837cd8938f97 Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Wed, 12 Apr 2023 11:52:33 -0400 Subject: [PATCH 4/4] test: add unit test for local address advertising --- src/test/net_tests.cpp | 105 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index aa577f7b27..ead604598e 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -904,4 +904,109 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message) TestOnlyResetTimeData(); } + +BOOST_AUTO_TEST_CASE(advertise_local_address) +{ + auto CreatePeer = [](const CAddress& addr) { + return std::make_unique(/*id=*/0, + /*sock=*/nullptr, + addr, + /*nKeyedNetGroupIn=*/0, + /*nLocalHostNonceIn=*/0, + CAddress{}, + /*pszDest=*/std::string{}, + ConnectionType::OUTBOUND_FULL_RELAY, + /*inbound_onion=*/false); + }; + SetReachable(NET_CJDNS, true); + + CAddress addr_ipv4{Lookup("1.2.3.4", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_ipv4.IsValid()); + BOOST_REQUIRE(addr_ipv4.IsIPv4()); + + CAddress addr_ipv6{Lookup("1122:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_ipv6.IsValid()); + BOOST_REQUIRE(addr_ipv6.IsIPv6()); + + CAddress addr_ipv6_tunnel{Lookup("2002:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_ipv6_tunnel.IsValid()); + BOOST_REQUIRE(addr_ipv6_tunnel.IsIPv6()); + BOOST_REQUIRE(addr_ipv6_tunnel.IsRFC3964()); + + CAddress addr_teredo{Lookup("2001:0000:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_teredo.IsValid()); + BOOST_REQUIRE(addr_teredo.IsIPv6()); + BOOST_REQUIRE(addr_teredo.IsRFC4380()); + + CAddress addr_onion; + BOOST_REQUIRE(addr_onion.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")); + BOOST_REQUIRE(addr_onion.IsValid()); + BOOST_REQUIRE(addr_onion.IsTor()); + + CAddress addr_i2p; + BOOST_REQUIRE(addr_i2p.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p")); + BOOST_REQUIRE(addr_i2p.IsValid()); + BOOST_REQUIRE(addr_i2p.IsI2P()); + + CService service_cjdns{Lookup("fc00:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + CAddress addr_cjdns{MaybeFlipIPv6toCJDNS(service_cjdns), NODE_NONE}; + BOOST_REQUIRE(addr_cjdns.IsValid()); + BOOST_REQUIRE(addr_cjdns.IsCJDNS()); + + const auto peer_ipv4{CreatePeer(addr_ipv4)}; + const auto peer_ipv6{CreatePeer(addr_ipv6)}; + const auto peer_ipv6_tunnel{CreatePeer(addr_ipv6_tunnel)}; + const auto peer_teredo{CreatePeer(addr_teredo)}; + const auto peer_onion{CreatePeer(addr_onion)}; + const auto peer_i2p{CreatePeer(addr_i2p)}; + const auto peer_cjdns{CreatePeer(addr_cjdns)}; + + // one local clearnet address - advertise to all but privacy peers + AddLocal(addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv4) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv6) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv6_tunnel) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_teredo) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_cjdns) == addr_ipv4); + BOOST_CHECK(!GetLocalAddress(*peer_onion).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_i2p).IsValid()); + RemoveLocal(addr_ipv4); + + // local privacy addresses - don't advertise to clearnet peers + AddLocal(addr_onion); + AddLocal(addr_i2p); + BOOST_CHECK(!GetLocalAddress(*peer_ipv4).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_ipv6).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_ipv6_tunnel).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_teredo).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_cjdns).IsValid()); + BOOST_CHECK(GetLocalAddress(*peer_onion) == addr_onion); + BOOST_CHECK(GetLocalAddress(*peer_i2p) == addr_i2p); + RemoveLocal(addr_onion); + RemoveLocal(addr_i2p); + + // local addresses from all networks + AddLocal(addr_ipv4); + AddLocal(addr_ipv6); + AddLocal(addr_ipv6_tunnel); + AddLocal(addr_teredo); + AddLocal(addr_onion); + AddLocal(addr_i2p); + AddLocal(addr_cjdns); + BOOST_CHECK(GetLocalAddress(*peer_ipv4) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv6) == addr_ipv6); + BOOST_CHECK(GetLocalAddress(*peer_ipv6_tunnel) == addr_ipv6); + BOOST_CHECK(GetLocalAddress(*peer_teredo) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_onion) == addr_onion); + BOOST_CHECK(GetLocalAddress(*peer_i2p) == addr_i2p); + BOOST_CHECK(GetLocalAddress(*peer_cjdns) == addr_cjdns); + RemoveLocal(addr_ipv4); + RemoveLocal(addr_ipv6); + RemoveLocal(addr_ipv6_tunnel); + RemoveLocal(addr_teredo); + RemoveLocal(addr_onion); + RemoveLocal(addr_i2p); + RemoveLocal(addr_cjdns); +} + BOOST_AUTO_TEST_SUITE_END()