Conversation
The flag was intended to track whether we sent a GETADDR to a peer, but it became redundant after the initial self-announcement sets it to false, even though we still expect the actual getaddr response. Co-authored-by: Martin Zumsande <mzumsande@gmail.com>
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please copy-paste ConflictsReviewers, this pull request conflicts with the following ones:
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. |
|
Sharing this here in case it’s helpful for review.
msg # 1 message one is the self announcement msg # 2 getaddr response msg # 3 addresses to relay |
|
Interesting. Concept ACK. |
|
concept ack. |
stratospher
left a comment
There was a problem hiding this comment.
ACK 9e34acb. nice find!
m_getaddr_sent logic is broken after #34146 and doesn’t do what it’s supposed to do anymore.
i think removing it is a logical bug fix which would be nice to have in v31. (not sure if it’s too late for that)
there would be a few behaviour changes with this PR:
- (might be debatable change) we would relay ADDR messages to peers just based on size ≤10. feel it's ok because:
- is it ok if someone send 5 ADDR as the GETADDR response (instead of the usual 1000 ADDR) and we relay it?
- we do GETADDR requests only to outbound peers ( so unlikely for it to be attacker controlled).
- in some remote scenario (where we got eclipsed) , suppose an attacker is chosen as our outbound peer - and it only sends us 5 ADDR as GETADDR response. we will relay it to our other peers once during the start of node’s lifecycle (wouldn’t have relayed these 5 ADDR before this PR). we also will have bigger problems to worry about than if address got relayed.
- there are much more effective ways for an attacker to force certain ADDR to be relayed than focus on the response to GETADDR - a normal node only sends GETADDR once during the version handshake.
- is it ok if someone send 5 ADDR as the GETADDR response (instead of the usual 1000 ADDR) and we relay it?
- (good change -fixes an inconsistent behaviour on master) currently we don’t relay the 1st ADDR message received from an outbound peer with size ≤ 10. now we relay it.
- (good change) initial self announcement always gets relayed.
| assert_equal(inbound_peer.num_ipv4_received, 0) | ||
|
|
||
| # Send an empty ADDR message to initialize address relay on this connection. | ||
|
|
There was a problem hiding this comment.
9e34acb: nit: I think keeping this comment "# Send an empty ADDR message to initialize address relay on this connection." is useful.
There was a problem hiding this comment.
I agree, it took me a minute to figure out what was going on here.
Also, unrelated to this PR, I noticed the outbound p2p connections are sending GETADDR that will get ignored.
There was a problem hiding this comment.
Also, unrelated to this PR, I noticed the outbound p2p connections are sending GETADDR that will get ignored.
I think its because -> https://github.com/bitcoin/bitcoin/blob/master/src/net_processing.cpp#L4911
|
cc @mzumsande |
| assert_equal(inbound_peer.num_ipv4_received, 0) | ||
|
|
||
| # Send an empty ADDR message to initialize address relay on this connection. | ||
|
|
There was a problem hiding this comment.
I agree, it took me a minute to figure out what was going on here.
Also, unrelated to this PR, I noticed the outbound p2p connections are sending GETADDR that will get ignored.
|
cc @0xB10C |
The assertion was passing because inbound_peer had m_addr_relay_enabled set to false, not for the intended reason of m_getaddr_sent being true.
9e34acb to
b591499
Compare
Thanks for reviewing |
For the first regular ADDR after the GETADDR response, this condition was false because bitcoin/src/net_processing.cpp Line 4074 in 0165132 |
mzumsande
left a comment
There was a problem hiding this comment.
Concept ACK
Just making clear that while #34146 had the effect that m_get_addr_sent doesn't match the GETADDR answer anymore, it didn't make anything worse: the flag was already unnecessary before (due to the <10 criterion), and the first self-announcement (when it was mixed into the 1000 size GETADDR answer) did not get relayed to peers before either.
So I'd say this PR doesn't correct a bug introduced by #34146, but improves logic that was already broken before.
|
I think the intended logic is (was) "we sent a getaddr, therefore the next addr message we receive will probably be old information which we shouldn't relay further". So #34146 did introduce a "bug" in that it became no longer true that next next addr message contained old information -- instead it would contain a self-announcement. That would then clear the flag, and we'd risk relaying the old information in the following addr message, that was in response to the getaddr request. The I think this change is fine though -- the 10min and <10 check seem already sufficient: if we get a small response to our getaddr, that's full of (honestly) recent information, then relaying that is either good or at worst harmless; and if an attacker were trying to exploit that ability, they could simply have sent a dummy addr message, and the real attack data later, avoiding the I'm not sure that improved relay of self-announcements of nodes that only just connected to you is necessarily a good thing. Currently (and prior to #34146) your node's IP would likely only be relayed reliably once you've been connected to a peer for a couple of hours or so, which serves as an automatic way of avoiding cluttering up everyone's address book with (honest) nodes that start up, sync, and shut down. So Concept ~0. Code looks fine if the behaviour is desirable, though. |
Does this have the direction mixed up? This is about improved relay of self-announcements originating from peers that we connected to - we, the initiating node, issue the GETADDR request, set The reverse direction (we, a possibly new/unreliable node, connect to a peer, and our own self-advertisment gets relayed by that peer or not) is not affected by this PR at all I think because this would involve |
|
reACK b591499 |
Yeah, it does have the direction mixed up. Still not sure how much benefit it makes to relay the initial self-announcement though. If they're already reachable/reliable nodes, then their address is already well known, and only getting re-announced by long-term connections is probably not terrible? I guess if it weren't useful to relay it, the peer shouldn't bother sending it to us -- we've already connected successfully, so we already have a working address for the peer and don't need to be told; so why else would they send it unless they wanted it relayed so others could more easily connect to them? Delaying the self-announcement until the connection's been alive a random amount of time would obviously be possible on the sender's side; (eg suggested here) so why treat it specially on the receiver side? I guess this doesn't have privacy implications -- I don't think you can distinguish a relayed self-announcement between being one of our outbounds or one of our inbounds; and distinguishing an addr relay for a direct connection vs a random forwarded addr seems like it requires a long-term connection and probably a lot of probability analysis? From #19794 (comment) :
I suppose that's something we could consider. I suppose we could also consider limiting our GETADDR responses to addr entries that are more than 10min old, or artificially inflating the ages of the addresses we send to ensure they're more than 10min old (and perhaps documenting that in a BIP so other node software that cares about the privacy concerns raised in that comment can implement compatible logic). |
|
Concept NACK I do not agree with this solution for removing
For these reasons, instead of removing |
Can't this be done independent of this patch? Also, the GETADDR is only sent for outbound connections so its impact is limited? |
I think this doesn't make sense -- if we want to treat addresses received as a response to "GETADDR" differently, then bool relay{true};
if (peer.m_addr_token_bucket < 1.0) {
if (rate_limited) {
if (m_getaddr_bucket == 0) {
++num_rate_limit; continue;
} else {
--m_getaddr_bucket;
relay = false;
}
}
} else {
peer.m_addr_token_bucket -= 1.0;
}
...
if (relay && addr.nTime > current_a_time - 10min && vAddr.size() <= 10 && addr.IsRoutable()) {
// Relay to a limited number of other nodes
RelayAddress(pfrom.GetId(), addr, reachable);
} |
|
This doesn't need to be part of v31. As mentioned above, even though it is possible for us to relay a GETADDR response currently, the probability is low because the default node behaviour will almost always have around 1000 addresses (> 10 addresses, hence we don't relay), and GETADDR responses are usually older. |
This attack is still possible with or without this flag in normal address relay, not just in response to GETADDR. |
|
reACK b591499. agree regarding it not being important for v31 - it's just an extra address relay anyways. I thought of this as a logical couple to #34150 - we're changing initial self announcement sending logic in that PR - so makes sense that the receiving logic code is also updated which is why I initially suggested it for v31. but it's just an inconsistency in our code and won't matter to people running the software. I guess the initial self announcement being relayed + this not being done before is what's causing concern. There's the alternative (naiyoma#14) which naiyoma proposed of making also liked aj's alternative suggestion of making sure GETADDR responses are actually old when selecting from addrman. I collected some address relay stats of my node last month. don't quote me on this since I haven't cross checked with other GETADDR responses. this is the address staleness of the GETADDR response of 1 particular peer (GPT plotted a histogram from the data):
also worth noting - the previous version of PR in #19794 was made at a time when ADDR token logic wasn't there + possibility of someone spamming us without any rate limiting. |
good idea to separate the logic of the response from normal forwarding , could i also suggest the following: that during the first X seconds we are expecting that we will receive a self-announcement and a response to our GETADDR message, for self-announcement we could distinguish it by checking if the sender is the same as the address in the buffer + the time it will be fresh, so we will relay it. and for the other addresses we will use |
…provements 57bfa86 test: use static methods and clarify comment in addr_relay (stratospher) 7ee8c0a test: protect outbound connection from eviction in getaddr_test (stratospher) ecb5ce6 test: fix addr relay test silent pass and wrong peerinfo index (stratospher) Pull request description: couple of improvements in the addr relay test: - fixes the silent test pass discovered in #34717 (comment) (will remove this if that PR gets merged - the test doesn't fail even though #34717 changes the behaviour) - correct wrong peerinfo index - prevent intermittent disconnection warnings like the one shown below by protecting outbound peer from `ConsiderEviction` ``` TestFramework (INFO): Check that we answer getaddr messages only once per connection TestFramework.p2p (WARNING): Connection lost to 127.0.0.1:58829 due to [Errno 54] Connection reset by peer ``` - remove a no longer applicable test comment since we don't need to send initial GETADDR for intial self announcement anymore ACKs for top commit: Bortlesboat: ACK 57bfa86. Ran both `p2p_addr_relay.py` and `p2p_addr_selfannouncement.py` locally, both pass. Good catch on the stale `peerinfo` reference in `inbound_blackhole_tests` — that would silently check the wrong peer. naiyoma: ACK 57bfa86 mzumsande: Code Review ACK 57bfa86 Tree-SHA512: 22e4f87f66569bfa629a68a8b440cd21b5285c6dad6eb7926514f2d74e16fe3711525b264f82765c83020be976a0438b8d2ab1e48e7c0b7d85437ee672d52324
|
🐙 This pull request conflicts with the target branch and needs rebase. |

This PR removes
m_getaddr_sent, as it no longer behaves as originally intended. Initially, this flag was meant to track when a getaddr message was sent to a peer.Now that self-announcements are sent separately from getaddr responses, the self-announcement sets
m_getaddr_senttofalseeven though we are still waiting for the getaddr response (1000 addresses).When the getaddr response does arrive, we rely on the size of the addr message, not the flag, to decide whether it should be relayed. This makes the flag redundant.
This is the current behavior:
Removing this flag ensures that:
I had initially considered an alternative approach naiyoma#14, where the self-announcement would not affect this flag and would therefore retain the initial behaviour, but i decided to reattempt its removal, as this was previously attempted, see #19794. Given the changes since then, I believe revisiting it is now more appropriate.