net: disconnect peers by address without using a subnet#20849
net: disconnect peers by address without using a subnet#20849vasild wants to merge 2 commits intobitcoin:masterfrom
Conversation
Previously, when disconnecting a peer with a given address we would create a dummy subnet that contains just 1 address (/32 for IPv4 and /128 for IPv6) and would disconnect all peers that belong to this subnet (there may be more than one connection, to a different port). The problem is that for any non-IPv4 and non-IPv6 address we would create an invalid subnet which would later not match any addresses. Thus, don't use a one-host-subnet, but compare the addresses directly. This works for any type of addresses.
|
The problem was discovered by @MarcoFalke. |
|
tested-only ACK 5fd2cee did not review the code 🔮 Show signature and timestampSignature: Timestamp of file with hash |
|
#20852 contains a fix for this issue + it also fixes a similar issue in |
|
Any tips how to test this manually? Disconnecting outbound onion peers with |
|
@jonatack yes, the
|
This comment has been minimized.
This comment has been minimized.
|
Adding logging to the code path and verified that Logging added to master and to this patch: @@ -2805,6 +2805,7 @@ bool CConnman::DisconnectNode(const CSubNet& subnet)
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (subnet.Match(pnode->addr)) {
+ LogPrintf("Disconnecting %s via subnet peer=%d address=%s network=%s\n",
+ pnode->ConnectionTypeAsString(), pnode->GetId(),
+ pnode->addr.ToString(), GetNetworkName(pnode->ConnectedThroughNetwork()));
pnode->fDisconnect = true;
disconnected = true;
}Logging also added to this patch: @@ -2818,6 +2819,7 @@ bool CConnman::DisconnectNode(const CNetAddr& addr)
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (addr == pnode->addr) {
+ LogPrintf("Disconnecting %s via addr peer=%d address=%s network=%s\n",
+ pnode->ConnectionTypeAsString(), pnode->GetId(),
+ pnode->addr.ToString(), GetNetworkName(pnode->ConnectedThroughNetwork()));
pnode->fDisconnect = true;
disconnected = true;
}On master, disconnection fails and nothing is logged. With this patch, the peer is immediately removed from the -netinfo 4 peers dashboard and the log shows: Upgrading to tested ACK 5fd2cee |
Change the `peer_discouragement` test to use `CNode` pointers so that the nodes it uses can be added to `CConnman::vNodes` and cleaned up properly. Make it use `CConnmanTest` instead of `CConnman`. This is needed because we want to check `CNode::fDisconnect` and for this flag to be flipped by `CConnman::DisconnectNode()` the node must be in `CConnman::vNodes`. Extend the test with one torv3 peer and check that it is discouraged and disconnected as expected.
|
Appended an extra commit with unit test that fails on If it does not get re-ACKs or if it turns out to be problematic I will remove it, restoring the already ACK'ed commit id. |
|
Nice, will review asap. |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. 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. |
| BOOST_CHECK(banman->IsDiscouraged(addr2)); | ||
| BOOST_CHECK(banman->IsDiscouraged(addr3)); | ||
| for (CNode* node : nodes) { | ||
| BOOST_CHECK(node->fDisconnect); |
There was a problem hiding this comment.
The new test code fails at the right place on master
test/denialofservice_tests.cpp(292): info: check banman->IsDiscouraged(addr1) has passed
test/denialofservice_tests.cpp(293): info: check banman->IsDiscouraged(addr2) has passed
test/denialofservice_tests.cpp(294): info: check banman->IsDiscouraged(addr3) has passed
test/denialofservice_tests.cpp(296): info: check node->fDisconnect has passed
test/denialofservice_tests.cpp(296): info: check node->fDisconnect has passed
test/denialofservice_tests.cpp(296): error: in "denialofservice_tests/peer_discouragement": check node->fDisconnect has failed
test/denialofservice_tests.cpp(222): Leaving test case "peer_discouragement"; testing time: 32905us
nit, maybe not worth it for tests, could use nodes.at() notation for bounds checking instead of nodes[]
| bool CConnman::DisconnectNode(const CNetAddr& addr) | ||
| { | ||
| return DisconnectNode(CSubNet(addr)); | ||
| bool disconnected = false; |
There was a problem hiding this comment.
Is this change still needed with #20852? It seems to me that makes the old, much more compact, construction work?
There was a problem hiding this comment.
No, if #20852 is merged, then this PR is not needed, see #20849 (comment).
I first opened this PR to fix DisconnectNode() and then realized that the same problem exists in BanMan, thus opened #20852 which fixes both by modifying CSubNet.
Maybe this PR could be preferred as less risky compared to #20852. It is ok if this is merged first and later #20852 is also merged - they do not conflict and both go in master.
|
Just to elaborate on the relationship between #20849 and #20852: The problem in #20849 fixes #20852 changes About the release, maybe the following makes sense: Include #20849 in the next rc and in the Later, if #20852 is merged (ever) there is no need to revert #20849. |
|
It might be better to wait for #20852 (0.21.1), after which this is not needed. |
maflcko
left a comment
There was a problem hiding this comment.
Concept ACK on adding the test. Mind to open a new pull for this?
|
The test is included in #20904. |
Previously, when disconnecting a peer with a given address we would
create a dummy subnet that contains just 1 address (/32 for IPv4 and
/128 for IPv6) and would disconnect all peers that belong to this
subnet (there may be more than one connection, to a different port).
The problem is that for any non-IPv4 and non-IPv6 address we would
create an invalid subnet which would later not match any addresses.
Thus, don't use a one-host-subnet, but compare the addresses directly.
This works for any type of addresses.