8000 Btcd disconnects upon receiving sendaddrv2 · Issue #1661 · btcsuite/btcd · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Btcd disconnects upon receiving sendaddrv2 #1661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Sjors opened this issue Nov 12, 2020 · 19 comments · Fixed by #1670
Closed

Btcd disconnects upon receiving sendaddrv2 #1661

Sjors opened this issue Nov 12, 2020 · 19 comments · Fixed by #1670

Comments

@Sjors
Copy link
Sjors commented Nov 12, 2020

Bitcoin Core master, soon to be released, introduces the new sendaddrv2 message (BIP 155). When such a Bitcoin Core node connects to a btcd node (tag v0.21.0-beta) it immediately disconnects:

2020-11-12 18:31:13.935 [ERR] PEER: Can't read message from x.x.x.x:42192 (inbound): ReadMessage: unhandled command [sendaddrv2]
2020-11-12 18:31:13.933 [INF] SYNC: New valid peer x.x.x.x:42192 (inbound) (/Satoshi:0.20.99/)
2020-11-12 18:31:13.946 [INF] SYNC: Syncing to block height 656631 from peer x.x.x.x:42192
2020-11-12 18:31:13.946 [INF] SYNC: Lost peer x.x.x.x:42192 (inbound)

On bitcoind side:

2020-11-12T18:31:13Z Added connection peer=6606
2020-11-12T18:31:13Z sending version (103 bytes) peer=6606
2020-11-12T18:31:13Z send version message: version 70016, blocks=656631, us=[::]:0, peer=6606
2020-11-12T18:31:13Z received: version (113 bytes) peer=6606
2020-11-12T18:31:13Z sending verack (0 bytes) peer=6606
2020-11-12T18:31:13Z sending sendaddrv2 (0 bytes) peer=6606
2020-11-12T18:31:13Z sending getaddr (0 bytes) peer=6606
2020-11-12T18:31:13Z receive version message: /btcwire:0.5.0/btcd:0.21.0/: version 70013, blocks=656631, us=x.x.x.x:42192, peer=6606
2020-11-12T18:31:13Z received: verack (0 bytes) peer=6606
2020-11-12T18:31:13Z New outbound peer connected: version: 70013, blocks=656631, peer=6606 (full-relay)
2020-11-12T18:31:13Z sending sendheaders (0 bytes) peer=6606
2020-11-12T18:31:13Z sending ping (8 bytes) peer=6606
2020-11-12T18:31:13Z sending addr (31 bytes) peer=6606
2020-11-12T18:31:13Z initial getheaders (656630) to peer=6606 (startheight:656631)
2020-11-12T18:31:13Z sending getheaders (1029 bytes) peer=6606
2020-11-12T18:31:13Z sending feefilter (8 bytes) peer=6606
2020-11-12T18:31:13Z received: reject (110 bytes) peer=6606
2020-11-12T18:31:13Z Unknown command "reject" from peer=6606
2020-11-12T18:31:13Z socket recv error for peer=6606: Connection reset by peer (104)
2020-11-12T18:31:13Z disconnecting peer=6606
2020-11-12T18:31:13Z Cleared nodestate for peer=6606

I tried with and without the new peerblockfilters feature, but that doesn't seem to matter.

This may lead to node loneliness. It also happens to Lnd nodes, see breez/breezmobile#408

This issue did not occur as of a slightly older Bitcoin Core commit, e.g. bitcoin/bitcoin@1d3ec2a

@Roasbeef
Copy link
Member
Roasbeef commented Dec 2, 2020

Thanks for reporting this! The easiest way to resolve this is to permit unknown messages rather than disconnecting as we do now. However, what's puzzling to me is why a new message was introduced (sendaddrv2) in order to introduce a new message (addrv2), vs just bumping the p2p protocol version and only sending this new message to peers that understand it. In the past, we adopted this defensive measure (to D/C if we get an unknown message) as accepting unknown messages can possibly open up DoS vectors.

@TheBlueMatt
Copy link

However, what's puzzling to me is why a new message was introduced...vs just bumping the p2p protocol version

It depends on who you ask, but I think philosophically there are many folks (many of whom work in Core) who argue a p2p protocol with many implementations should never be dependent on a single version number to describe all the features supported. This is why Core has used empty messages to communicate protocol support for years.

accepting unknown messages can possibly open up DoS vectors.

Can you elaborate more on this? Throwing away bytes received should never be worse than receiving a lot of pings/other useless data, so it seems strange that there would be DoS issues here.

@vasild
Copy link
vasild commented Dec 3, 2020

However, what's puzzling to me is why a new message was introduced (sendaddrv2) in order to introduce a new message (addrv2)

addrv2 is a replacement of the existent addr. Some method is needed to coordinate whether to send addr or addrv2 to a given peer. So, by sendaddrv2 a peer asks "Please send me addrv2 instead of addr".

@laanwj
Copy link
laanwj commented Dec 3, 2020

However, what's puzzling to me is why a new message was introduced...vs just bumping the p2p protocol version

FWIW bumping the protocol version was my initial proposal, but everyone preferred the solution to send a message for signalling instead for the sake of other implementations (e.g. having only part of the feature set but still wanting to use TorV3). For the record I agree with @TheBlueMatt's reasoning about permissionless in introducing features, but I am extremely disappointed that it turns out to be the method that is most problematic for other implementations.

@maflcko
Copy link
maflcko commented Dec 4, 2020

If bitcoin/bitcoin#20564 is merged, it will act as a temporary workaround to give btcd a bit of time to write and deploy a fix. It looks like there are already plans to implement a solution:

Thanks for reporting this! The easiest way to resolve this is to permit unknown messages rather than disconnecting as we do now.

vasild added a commit to vasild/btcd that referenced this issue Dec 4, 2020
This would make it possible for old versions (that contain this change)
to talk with newer versions without requiring a protocol version
increment.

Fixes btcsuite#1661
@jakesylvestre
Copy link
Collaborator

Can you elaborate more on this? Throwing away bytes received should never be worse than receiving a lot of pings/other useless data, so it seems strange that there would be DoS issues here.

@Roasbeef feel free to correct me here, but I think the concern is you could have peer(s) that keep sending garbage commands that would otherwise be dropped

@sipa
Copy link
sipa commented Dec 6, 2020

@jakesyl Of course, peers could be sending garbage. But if that's a problem, then peers spamming you with ping commands or tx messages would be even more of a problem. For that reason disconnecting on unknown messages doesn't protect against attackers - they'd just use an easier mechanism anyway - but it does make it harder to use send* style messages for feature negotiation.

@jakesylvestre
Copy link
Collaborator

got it- thanks for the clarification @sipa!

@Roasbeef
Copy link
Member
Roasbeef commented Dec 8, 2020

It depends on who you ask, but I think philosophically there are many folks (many of whom work in Core) who argue a p2p protocol with many implementations should never be dependent on a single version number to describe all the features supported.

Consider that before this "add a message to intro another message" approach, it was possible to ascertain the capabilities of a node purely based on the advertised node version + services. IMO this property is useful for implementations that prefer to maintain a stricter set of peers for functionality and discovery purposes. With this new approach, an implementation is now only able to ascertain the capabilities of a given node by parsing a combination of its version+services along with the user agent string (assuming we don't want to require the node to first attempt to connect to any other potential node to obtain this information).

User agent strings themselves re finicky since users can change them to arbitrary values for most implementations, so node implementations are now forced to always connect out to a node, wait for w/e meta-extension message, before deciding to hang up or keep the connection or actually add the address to their local address manager.

As a result, the protocol version + services no longer uniquely describe what set of messages a node is able to send and understand. Interactive probing is required to obtain the same information which was previously obtainable given only an addr message and/or initial version handshake.

@Roasbeef
Copy link
Member
Roasbeef commented Dec 8, 2020

addrv2 is a replacement of the existent addr. Some method is needed to coordinate whether to send addr or addrv2 to a given peer. So, by sendaddrv2 a peer asks "Please send me addrv2 instead of addr".

That mechanism could've been the services bit field available in the legacy addr message and the version handshake. Consider that if I want to make a new implementation that only prefers addrv2 message, but can only bootstrap using say the DNS seeds (which only allow filtering based on the service bit field and not the user agent or w/e), I need to manually connect out to each of these nodes, wait for their messages, then decide if they can allow me to start populating my new pristine addrv2 peer table.

Alternatively, I could've just only connected out to nodes that advertised in their addrv1 messages that they're able to provide me with addrv2 peers/addrs.

@sipa
Copy link
sipa commented Dec 8, 2020

which was previously obtainable given only 8000 an addr message and/or initial version handshake.

With the proposed change in bitcoin/bips#1043 that's actually still possible, as the sendaddrv2 will be sent before verack, so after the initial handshake you know what the peer supports. The same is done in BIP339 (wtxid relay).

That mechanism could've been the services bit field available in the legacy addr message and the version handshake.

That's a separate discussion. You're arguing here that there may be reasons why addrv2 support should be discoverable. If that's the case, it certainly should have been a service bit. But I believe the BIPs' authors intentionally chose not to make the feature discoverable as service bits are a very limited resource - addrv2 is simply an extension of addrv1.

@Roasbeef
Copy link
Member
Roasbeef commented Dec 8, 2020

It looks like this draft BIP attempts to codify bitcoind's new approach going forward which is great so other implementations can know how to interpret/handle its new choice of mechanism to extend the protocol.

Re ignoring messages or not: IMO it really depends on if it's reasonable (and IMO it is) for an implementation to expect that the protocol version+service define a subset of the existing Bitcoin P2P protocol. Why should I maintain a connection to a peer which is sending random messages I don't know the significance
of and may be unable to parse fully?

Stepping back this also begs the question: is sending "unknown" messages to another implementation compliant with the "Bitcoin P2P Protocol"? AFAICT, it's unclear since the "defacto" specification of the p2p protocol appears to be either the Bitcoin wiki or Bitcoin developer site. So it seems this behavior itself undefined, though I may be missing something.

@TheBlueMatt
Copy link
TheBlueMatt commented Dec 8, 2020

Consider that before this "add a message to intro another message" approach, it was possible to ascertain the capabilities of a node purely based on the advertised node version + services.

This is...dubious. I know I've written P2P clients that will happily send a high version number so that they get certain feature support, but don't implement other, older-version-number features. This has always worked as intended and is precisely the reason for the "empty message as feature negotiation" stuff - sure, it may not be the most elegant way to negotiate protocol features, but it works. Just sending a version number has never been enough to turn on certain feature in the P2P protocol (including in Bitcoin Core), many features have required, since day one, an additional negotiation step, and happily functioned without completing it.

Re ignoring messages or not: IMO it really depends on if it's reasonable (and IMO it is) for an implementation to expect that the protocol version+service define a subset of the existing Bitcoin P2P protocol.

As sipa indicated, this, sadly, just isn't very workable - service bits is a rather limited resource (there's only 32 of them), and as a result many protocol features do not have a feature bit assigned to them. As noted above, given that, there is no way to build a "only implement some of the features implied by protocol version X" client without building an additional version negotiation protocol, hence the use of dummy protocol negotiation messages.

@sipa
Copy link
sipa commented Dec 8, 2020

Why should I maintain a connection to a peer which is sending random messages I don't know the significance of and may be unable to parse fully?

I think the answer depends on your perspective. Assuming the sender is not malicious, they are presumably using a protocol extension you don't know about - and the extension should have been chosen in such a way that it doesn't affect anyone who doesn't understand it. That has been the case for all protocol changes that I know of since 2014 (including sendheaders, feefilter, compact blocks, and wtxid relay). Those happened to always be accompanied with a version bump, but it really serves no purpose - the protocols are effectively negotiated using new messages, and the bump just determines whether that negotiation is even tried. I believe people felt this also doesn't belong in a protocol that should be permissionlessly extensible.

Stepping back this also begs the question: is sending "unknown" messages to another implementation compliant with the "Bitcoin P2P Protocol"? AFAICT, it's unclear since the "defacto" specification of the p2p protocol appears to be either the Bitcoin wiki or Bitcoin developer site. So it seems this behavior itself undefined, though I may be missing something.

I think part of this debate is because apparently there was no common understanding about what the protocol was. That's unfortunate, and messy, but it's probably inevitable in any protocol between multiple systems without central authority to fix a specification. Bitcoin Core has always ignored unknown messages, so this issue comes a bit as a surprise.

@Roasbeef
Copy link
Member
Roasbeef commented Dec 8, 2020

Just sending a version number has never been enough to turn on certain feature in the P2P protocol

Other than sendheaders which features does this statement apply to?

Rationale earlier in this thread for this change was "it's easier on other impls" but it doesn't appear that other impls were consulted. I scanned the past year of bitcoin dev mailing list posts (for 2020), and the only tangential mention of this new p2p feature rollout is the mail by suhas that linked to that document, tho the motivation in the email was the upcoming wtxid change. It may have been discussed in the list in 2019, but I would think if a new p2p feature were close to being rolled out, there'd be another mail to give people a heads up.

As sipa indicated, this, sadly, just isn't very workable - service bits is a rather limited resource (there's only 32 of them), and as a result many protocol features do not have a feature bit assigned to them

Yeah I'm familiar with this opinion re service bit scarcity, to which my response usually is: why not just add more? The addrv2 message could've added "more than we'd ever likely use" given it redefined the structure of the addr message itself.

@sipa
Copy link
sipa commented Dec 8, 2020

but I would think if a new p2p feature were close to being rolled out, there'd be another mail to give people a heads up.

You're absolutely right about that. I think people incorrectly assumed that others would have been paying attention to the discussion on BIP155 over the past months, but looking back I don't think any of that made itself to the mailing list.

@TheBlueMatt
Copy link
TheBlueMatt commented Dec 8, 2020

Other than sendheaders which features does this statement apply to?

feefilter (technically, I mean hard which way you really call this one), wtxidrelay, and compact blocks as well, and that's just from sipa's list above.

Rationale earlier in this thread for this change was "it's easier on other impls" but it doesn't appear that other impls were consulted.

Right, I believe that rationale was selected about 10 years ago, maybe more :). Indeed, the issue is that these things largely weren't documented, only decided and then folks moved on. When others actually wrote other implementations, they didn't come up again.

Yeah I'm familiar with this opinion re service bit scarcity, to which my response usually is: why not just add more?

Sure, but that's besides the point - there's a perfectly fine, even if inelegant, way by which features have been negotiated for many years - send a dummy message to turn it on, and only send that message if the protocol version is high.

@maflcko
Copy link
maflcko commented Dec 8, 2020

Other than sendheaders which features does this statement apply to?

It applies to every message type, except version, verack (and maybe ping, pong).

Just to give some examples for illustration, but really it applies to every message type:

For example, reject messages are defined for protocol versions 70002 and higher, but they are not mandatory. Even if reject messages were advertised somehow with a p2p version or service bits (or some other new feature negotiation method), you'd always have to actually open the connection to the remote node to ensure it has the claimed capabilities.

Another example, wtxidrelay messages are defined for protocol version 70016 and higher, but they are not mandatory either.

Another example, inv tx messages to announce transactions. Keeping a data structure to store transactions (e.g. a mempool or wallet) is not madatory and some nodes might simply choose to not send you inv tx messages.

@jnewbery
Copy link
jnewbery commented Dec 9, 2020

Stepping back this also begs the question: is sending "unknown" messages to another implementation compliant with the "Bitcoin P2P Protocol"? AFAICT, it's unclear since the "defacto" specification of the p2p protocol appears to be either the Bitcoin wiki or Bitcoin developer site. So it seems this behavior itself undefined, though I may be missing something.

The de facto specification for the protocol was initially the Satoshi implementation. From the initial v0.1 release, unknown message types have been permitted as a mechanism to extend the p2p protocol: https://github.com/benjyz/bitcoinArchive/blob/master/bitcoin0.1/src/main.cpp#L2035-L2039. This has remained true for the Bitcoin Core implementation since that time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
9 participants
0