diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -684,6 +684,13 @@ "(minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + gArgs.AddArg( + "-peertimeout=", + strprintf("Specify p2p connection timeout in seconds. This option " + "determines the amount of time a peer may be inactive before " + "the connection to it is dropped. (minimum: 1, default: %d)", + DEFAULT_PEER_CONNECT_TIMEOUT), + true, OptionsCategory::CONNECTION); gArgs.AddArg( "-torcontrol=:", strprintf( @@ -1510,6 +1517,8 @@ int nUserMaxConnections; int nFD; ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED); +int64_t peer_connect_timeout; + } // namespace [[noreturn]] static void new_handler_terminate() { @@ -1797,6 +1806,13 @@ nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; } + peer_connect_timeout = + gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT); + if (peer_connect_timeout <= 0) { + return InitError( + "peertimeout cannot be configured with a negative value."); + } + // Obtain the amount to charge excess UTXO if (gArgs.IsArgSet("-excessutxocharge")) { Amount n = Amount::zero(); @@ -2560,6 +2576,7 @@ connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; + connOptions.m_peer_connect_timeout = peer_connect_timeout; for (const std::string &strBind : gArgs.GetArgs("-bind")) { CService addrBind; diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -83,6 +83,8 @@ static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24; /** Default for blocks only*/ static const bool DEFAULT_BLOCKSONLY = false; +/** -peertimeout default */ +static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60; static const bool DEFAULT_FORCEDNSSEED = false; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; @@ -136,6 +138,7 @@ unsigned int nReceiveFloodSize = 0; uint64_t nMaxOutboundTimeframe = 0; uint64_t nMaxOutboundLimit = 0; + int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT; std::vector vSeedNodes; std::vector vWhitelistedRange; std::vector vBinds, vWhiteBinds; @@ -157,6 +160,7 @@ m_msgproc = connOptions.m_msgproc; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; nReceiveFloodSize = connOptions.nReceiveFloodSize; + m_peer_connect_timeout = connOptions.m_peer_connect_timeout; { LOCK(cs_totalBytesSent); nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; @@ -385,6 +389,9 @@ uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent); + // P2P timeout in seconds + int64_t m_peer_connect_timeout; + // Whitelisted ranges. Any node connecting from these is automatically // whitelisted (as well as those connecting to whitelisted binds). std::vector vWhitelistedRange; diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -1137,12 +1137,12 @@ void CConnman::InactivityCheck(CNode *pnode) { int64_t nTime = GetSystemTimeInSeconds(); - if (nTime - pnode->nTimeConnected > 60) { + if (nTime - pnode->nTimeConnected > m_peer_connect_timeout) { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { LogPrint(BCLog::NET, - "socket no message in first 60 seconds, %d %d from %d\n", - pnode->nLastRecv != 0, pnode->nLastSend != 0, - pnode->GetId()); + "socket no message in first %i seconds, %d %d from %d\n", + m_peer_connect_timeout, pnode->nLastRecv != 0, + pnode->nLastSend != 0, pnode->GetId()); pnode->fDisconnect = true; } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { LogPrintf("socket sending timeout: %is\n", diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -14,11 +14,11 @@ - Wait 1 second - Assert that we're connected - Send a ping to no_verack_node and no_version_node -- Wait 30 seconds +- Wait 1 second - Assert that we're still connected - Send a ping to no_verack_node and no_version_node -- Wait 31 seconds -- Assert that we're no longer connected (timeout to receive version/verack is 60 seconds) +- Wait 2 seconds +- Assert that we're no longer connected (timeout to receive version/verack is 3 seconds) """ from time import sleep @@ -38,6 +38,8 @@ def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + # set timeout to receive version/verack to 3 seconds + self.extra_args = [["-peertimeout=3"]] def run_test(self): # Setup the p2p connections @@ -56,7 +58,7 @@ no_verack_node.send_message(msg_ping()) no_version_node.send_message(msg_ping()) - sleep(30) + sleep(1) assert "version" in no_verack_node.last_message @@ -67,11 +69,17 @@ no_verack_node.send_message(msg_ping()) no_version_node.send_message(msg_ping()) - sleep(31) - - assert not no_verack_node.is_connected - assert not no_version_node.is_connected - assert not no_send_node.is_connected + expected_timeout_logs = [ + "version handshake timeout from 0", + "socket no message in first 3 seconds, 1 0 from 1", + "socket no message in first 3 seconds, 0 0 from 2", + ] + + with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs): + sleep(2) + assert not no_verack_node.is_connected + assert not no_version_node.is_connected + assert not no_send_node.is_connected if __name__ == '__main__':