Skip to content

Conversation

mzumsande
Copy link
Contributor

@mzumsande mzumsande commented Jul 22, 2022

Currently, -onlynet does not work well in connection with initial peer discovery, because DNS seeds only resolve to IPv6 and IPv4 adresses:
With -onlynet=i2p, we would load clearnet addresses from DNS seeds into addrman, be content our addrman isn't empty so we don't try to query hardcoded seeds (although these exist for i2p!), and never attempt to make an automatic outbound connection.
With -onlynet=onion and -proxy set, we wouldn't load addresses via DNS, but will make AddrFetch connections (through a tor exit node) to a random clearnet peer the DNS seed resolves to (see #6808 (comment)), thus breaching the -onlynet preference of the user - this has been reported in the two issues listed below.

This PR proposes two changes:
1.) Don't load addresses that are unreachable (so that we wouldn't connect to them) into addrman. This is already the case for addresses received via p2p addr messages, this PR implements the same for addresses received from DNS seeds and fixed seeds. This means that in the case of -onlynet=onion, we wouldn't load fixed seed IPv4 addresses into addrman, only the onion ones.
2.) Skip trying the DNS seeds if neither IPv4 nor IPv6 are reachable and move directly to adding the hardcoded seeds from networks we can connect to. This is done by soft-setting -dnsseed to 0 in this case, unless -dnsseed=1 was explicitly specified, in which case we abort with an InitError.

Fixes #6808
Fixes #12344

@fanquake fanquake added the P2P label Jul 22, 2022
@mzumsande mzumsande force-pushed the 202207_onlynet_dns branch from 45ed3f7 to 09384fc Compare July 22, 2022 21:40
@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 23, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #23531 (Add Yggdrasil support by prusnak)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

Copy link
Contributor

@brunoerg brunoerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong concept ACK

@michaelfolkson
Copy link

michaelfolkson commented Aug 8, 2022

Concept ACK, Approach ACK.

This was asked about in this StackExchange question.

@sipa
Copy link
Member

sipa commented Aug 10, 2022

Concept ACK on (at least by default) not using DNS seed lookups (incl. addrfetch based ones) with -onlynet=onion. This is really a contradiction. Onlynet=onion means only connections to hidden services, and DNS seeds fundamentally requires making a connection to the IPv4IPv6 world (either by asking DNS servers, or by asking a Tor exit node to make a connection to what a DNS result resolves to).

Orthogonal idea (not for this PR): would it make sense, when using the hardcoded seeds (for whatever reason, onlynot or not), to only make addrfetch connections to them? Their point is getting us an entrypoint to the network, and using them directly as fully-featured nodes probably places an undue burden on them. This would get worse with a change like this PR.

@fanquake fanquake added this to the 24.0 milestone Aug 10, 2022
@mzumsande
Copy link
Contributor Author

09384fc to 1e039d1: Addressed feedback, some refactoring/rewording.

The current PR is not specific to -onlynet=onion but affects all alternative networks. While I discussed Tor and I2P in the OP, is there anything special to CJDNS that would require another approach (ping @vasild)?

Orthogonal idea (not for this PR): would it make sense, when using the hardcoded seeds (for whatever reason, onlynot or not), to only make addrfetch connections to them? Their point is getting us an entrypoint to the network, and using them directly as fully-featured nodes probably places an undue burden on them. This would get worse with a change like this PR.

Yes, I agree that this could makes sense. While in principle, the maximum inbound limit together with the inbound eviction logic should keep the number of connections under control, hardcoded peers will receive connections from a lot of new nodes which require IBD, so it would be nice to reduce their traffic burden.

@vasild
Copy link
Contributor

vasild commented Aug 12, 2022

Concept ACK

is there anything special to CJDNS that would require another approach

Well, unlike Tor and I2P addresses, CJDNS addresses could be returned by the DNS seeds, e.g. this could happen:

seed.bitcoin.sipa.be has IPv6 address fc:fb1:4:387e:9248:9aff:febe:1c39

But we would still need to open a connection to the DNS server itself. If that is from the CJDNS network, then we are perfectly good (e.g. /etc/resolv.conf contains something like nameserver fc:123...). But if the DNS server is IPv4 or non-cjdns-IPv6, then opening a connection to it violates onlynet=cjdns, right? My understanding is that there is no easy and reliable way to check the address(es) of the DNS server(s) in use by the machine. They could also be changed after we have checked.

I guess, given the above, it is best and easiest to treat CJDNS like Tor and I2P and have onlynet=cjdns incompatible with dnsseed=1.

@achow101
Copy link
Member

ACK 1e039d1

@fanquake fanquake requested a review from dergoegge August 15, 2022 20:51
@DrahtBot DrahtBot mentioned this pull request Aug 21, 2022
@naumenkogs
Copy link
Member

utACK 1e039d1

Copy link
Contributor

@vasild vasild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 1e039d1

This would leave gArgs.GetBoolArg("-dnsseed") to true but would effectively disable DNS seeds if e.g. -onlynet=onion is given. The existent code is already capable of disabling DNS seeds - it does so if dnsseed=0.

Given that we want to disable DNS seeds, would it be simpler to flip (soft set) -dnsseed to false in InitParameterInteraction() and not touch any of the code in net.{cpp.h} (no need to add new member to CConnman)?

Would require something like this in InitParameterInteraction().
bool clearnet_reachable = true;
if (args.IsArgSet("-onlynet")) {
    const auto onlynets = args.GetArgs("-onlynet");
    clearnet_reachable = std::any_of(onlynets.begin(), onlynets.end(), [](const auto& net) {
        const auto n = ParseNetwork(net);
        return n == NET_IPV4 || n == NET_IPV6;
    });
}

src/init.cpp Outdated
Comment on lines 1328 to 1340
if (args.GetBoolArg("-dnsseed") == true && !IsReachable(NET_IPV4) && !IsReachable(NET_IPV6)) {
return InitError(strprintf(_("Incompatible options: -dnsseed was explicitly specified, but -onlynet forbids connections to IPv4/IPv6")));
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also cover -forcednsseed? Its help reads: "Always query for peer addresses via DNS lookup", seems to contradict with -onlynet=onion, just like -dnsseed does. In other words, should this give an init error too: bitcoind -onlynet=onion -forcednsseed=1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already an interaction between -forcednsseed and -dnsseed which gives an InitError if -dnsseed is not set.
With the latest push (taking your suggestion to use InitParameterInteraction()) that should hit.

@mzumsande
Copy link
Contributor Author

mzumsande commented Sep 1, 2022

Given that we want to disable DNS seeds, would it be simpler to flip (soft set) -dnsseed to false in InitParameterInteraction() and not touch any of the code in net.{cpp.h} (no need to add new member to CConnman)?

Thanks - I agree that it would be simpler and more similar to other parameter interactions to do that and took your suggestion with the latest push (added you as coauthor).

@naumenkogs
Copy link
Member

ACK 7dab719

Copy link
Contributor

@vasild vasild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 7dab719

@naumenkogs's suggestions above make sense, would be happy to re-ACK.

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach ACK, agree with @naumenkogs' suggestions as well

@@ -1630,15 +1633,20 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
LOCK2(m_addr_fetches_mutex, m_added_nodes_mutex);
if (m_addr_fetches.empty() && m_added_nodes.empty()) {
add_fixed_seeds_now = true;
LogPrintf("Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n");
LogPrintf("Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet), -addnode is not provided and all -seednode(s) attempted\n");
Copy link
Member

@jonatack jonatack Sep 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while touching this, could use severity-based logging (info is always logged, like LogPrintf), feel free to ignore

Suggested change
LogPrintf("Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet), -addnode is not provided and all -seednode(s) attempted\n");
LogPrintLevel(BCLog::ADDRMAN, BCLog::Level::Info, "Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet), -addnode is not provided and all -seednode(s) attempted\n");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to have this done in a dedicated PR that switches it everywhere per module (for 25.0 - no idea whether this qualifies as a bugfix and should still go into 24.0, at least it still has the label), so that hopefully there wouldn't be too much of a mix for an extended time.

Also, how about a short heads-up in the weekly IRC meeting or so summarizing the recent changes? While logging is used by / affects everyone, I think most contributors who didn't follow the recent developments have internalized something like "With LogPrintf be careful about disk-filling attacks - with category-based logging no need to think about that much" which is going to change.

Copy link
Member

@jonatack jonatack Sep 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes agree, was about to update my comment to say never mind. (Am about to propose renaming the LogPrintLevel macro to simply Log or something before converting to more of them, as it is likely to subsume most of the other logging.)

@mzumsande
Copy link
Contributor Author

mzumsande commented Sep 2, 2022

7dab719 to 37b2b5b: Addressed review feedback.

Copy link
Contributor

@vasild vasild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 37b2b5b

@Sjors
Copy link
Member

Sjors commented Sep 6, 2022

Concept ACK

37b2b5b ("don't query seeds...") makes sense to me, but e91e3ef ("add only reachable addresses to addrman") less so.

This commit seems to assume that the node is always started with the same -onlynet parameter, but what if the user decides to enable IPv4 / IPv6 later? peers.dat would be full of onion nodes in that case. Then again, eventually we'd query the DNS seeds again, so maybe it's not a big deal.

IIUC we only ever load the fixed seed nodes if addrman.size() == 0.

src/net.cpp Outdated
@@ -1475,6 +1475,9 @@ void CConnman::ThreadDNSAddressSeed()
vAdd.push_back(addr);
found++;
}
vAdd.erase(std::remove_if(vAdd.begin(), vAdd.end(),
[](const CAddress& addr) { return !IsReachable(addr); }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e91e3ef: why not just check this condition before calling vAdd.push_back(addr)? We should probably not increment found either when we don't use an address.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. On second thought, the filtering seems unnecessary in the DNS seed case, because with the second commit, we will skip querying the DNS seeds if IPv4 and IPv6 are unreachable, while DNS seeds only return IPv4 and IPv6 addresses. So I removed it.

@mzumsande
Copy link
Contributor Author

IIUC we only ever load the fixed seed nodes if addrman.size() == 0.

Yes, so we will never load them more than once for a given peers.dat.

This commit seems to assume that the node is always started with the same -onlynet parameter, but what if the user decides to enable IPv4 / IPv6 later? peers.dat would be full of onion nodes in that case. Then again, eventually we'd query the DNS seeds again, so maybe it's not a big deal.

The general idea is to make the behavior consistent with the behavior towards peers (don't accept addrs into our addrman we can't or don't want to connect to).

I think if a user decides to just more additional networks (at least for a transitional period), there shouldn't be a problem: Both GetAddr answers and rumoured addresses from existing peer will likely contain a few entries from other networks over time, and once these are no longer unreachable due to -onlynet parameters, they will be accepted to addrman.

It's more complicated if the user makes an abrupt change from -onlynet=X to -onlynet=Y:
As you mentioned, switching to -onlynet=IPv4/IPv6 should not be a problem, because we'll ask the DNS seeds even if addrman.size() != 0 after 5 minutes.

I think that switching from -onlynet=IPv4/IPv6 to e.g. -onlynet=onion or another network will probably not work as a general rule (irrespective of this PR): Since we likely used the DNS seeds to bootstrap, we have no IPs from previously unreachable networks in AddrMan, and we won't attempt the hardcoded seeds because addrman isn't empty.

What this PR may make more difficult is a situation where we switch between privacy networks, e.g. from -onlynet=onion to -onlynet=i2p because we may have queried the fixed seed and still have some random i2p fixed seeds in our addrman if we are lucky (and didn't overwrite these with others, which would likely happen after a while). This seems like a rather exotic situation to me, less likely than the previous one.

Maybe it would be better to change the criterion for querying hardcoded seeds from addrman.size() == 0 to # of reachable addresses == 0, so that we would query the hardcoded seeds again if the existing entries all became unreachable due to a sudden change in -onlynet parameters?

mzumsande and others added 2 commits September 6, 2022 15:16
We will not make outgoing connection to peers that are unreachable
(e.g. because of -onlynet configuration).
Therefore, it makes no sense to add them to addrman in the first place.
While this is already the case for addresses received via p2p addr
messages, this commit does the same for addresses received
from fixed seeds.
This happens, for example, if the user specified -onlynet=onion or
-onlynet=i2p. DNS seeds only resolve to IPv4 / IPv6 addresses,
making their answers useless to us, since we don't want to make
connections to these.
If, within the DNS seed thread, we'd instead do fallback AddrFetch
connections to one of the clearnet addresses the DNS seed resolves to,
we might get usable addresses from other networks
if lucky, but would be violating our -onlynet user preference
in doing so.

Therefore, in this case it is better to rely on fixed seeds for networks we
want to connect to.

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
@Sjors
Copy link
Member

Sjors commented Sep 7, 2022

addrman.size() == 0 || reachable addresses == 0 sounds good to me

Copy link
Member

@Sjors Sjors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 91f0a7f modulo using hardcoded seeds when we have nothing reachable

@naumenkogs
Copy link
Member

utACK 385f5a4

Copy link
Contributor

@vasild vasild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 385f5a4

I experienced the problem discussed above (finding peers after onlynet changes) but forgot to fix or even report it back then. This discussion prompted me to report it at #26035 so that it does not get forgotten again.

Thanks!

@fanquake fanquake merged commit 37095c7 into bitcoin:master Sep 7, 2022
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Sep 7, 2022
@Sjors
Copy link
Member

Sjors commented Sep 8, 2022

Ok, so now the "modulo" part of my review is at least documented in #26035.

maflcko pushed a commit to bitcoin-core/gui that referenced this pull request Sep 9, 2022
…es to addrman"

ce42570 doc: comment "add only reachable addresses to addrman" (Kristaps Kaupe)

Pull request description:

  Proposed by Sjors during review of #25678, was likely just missed, as it also for me looks a code where comment will not hurt.

  bitcoin/bitcoin#25678 (comment)

ACKs for top commit:
  mzumsande:
    ACK ce42570
  vasild:
    ACK ce42570
  Zero-1729:
    re-ACK ce42570

Tree-SHA512: ef085d527349de07c1b43ed39e55e34b29cb0137c9509bd14a1af88206f7d4aa7dfec1dca53a9deaed67a2d0f32fa21e0b1a04d4d5d7f8a265dfab3b62bf8c54
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Sep 11, 2022
…drman"

ce42570 doc: comment "add only reachable addresses to addrman" (Kristaps Kaupe)

Pull request description:

  Proposed by Sjors during review of bitcoin#25678, was likely just missed, as it also for me looks a code where comment will not hurt.

  bitcoin#25678 (comment)

ACKs for top commit:
  mzumsande:
    ACK ce42570
  vasild:
    ACK ce42570
  Zero-1729:
    re-ACK ce42570

Tree-SHA512: ef085d527349de07c1b43ed39e55e34b29cb0137c9509bd14a1af88206f7d4aa7dfec1dca53a9deaed67a2d0f32fa21e0b1a04d4d5d7f8a265dfab3b62bf8c54
@mzumsande mzumsande deleted the 202207_onlynet_dns branch November 3, 2022 01:10
@bitcoin bitcoin locked and limited conversation to collaborators Nov 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Setting onlynet=onion still makes some IPv4 connections. onlynet=tor still using exits for seeds is confusing