@@ -129,8 +129,6 @@ static constexpr double BLOCK_DOWNLOAD_TIMEOUT_BASE = 1;
129129static constexpr double BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 0.5 ;
130130/* * Maximum number of headers to announce when relaying blocks with headers message.*/
131131static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8 ;
132- /* * Maximum number of unconnecting headers announcements before DoS score */
133- static const int MAX_NUM_UNCONNECTING_HEADERS_MSGS = 10 ;
134132/* * Minimum blocks required to signal NODE_NETWORK_LIMITED */
135133static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288 ;
136134/* * Window, in blocks, for connecting to NODE_NETWORK_LIMITED peers */
@@ -379,8 +377,8 @@ struct Peer {
379377 /* * Whether we've sent our peer a sendheaders message. **/
380378 std::atomic<bool > m_sent_sendheaders{false };
381379
382- /* * Length of current-streak of unconnecting headers announcements */
383- int m_num_unconnecting_headers_msgs GUARDED_BY (NetEventsInterface::g_msgproc_mutex){0 };
380+ /* * Large reorgs are rare, so only permit one such announcement. */
381+ bool m_chicken_little GUARDED_BY (NetEventsInterface::g_msgproc_mutex){0 };
384382
385383 /* * When to potentially disconnect peer for stalling headers download */
386384 std::chrono::microseconds m_headers_sync_timeout GUARDED_BY (NetEventsInterface::g_msgproc_mutex){0us};
@@ -2544,35 +2542,51 @@ arith_uint256 PeerManagerImpl::GetAntiDoSWorkThreshold()
25442542 *
25452543 * We'll send a getheaders message in response to try to connect the chain.
25462544 *
2547- * The peer can send up to MAX_NUM_UNCONNECTING_HEADERS_MSGS in a row that
2548- * don't connect before given DoS points.
2545+ * Allow this only once, except when there may be a moderate clock difference.
25492546 *
25502547 * Once a headers message is received that is valid and does connect,
2551- * m_num_unconnecting_headers_msgs gets reset back to 0 .
2548+ * m_chicken_little gets reset back to false .
25522549 */
25532550void PeerManagerImpl::HandleFewUnconnectingHeaders (CNode& pfrom, Peer& peer,
25542551 const std::vector<CBlockHeader>& headers)
25552552{
2556- peer.m_num_unconnecting_headers_msgs ++;
25572553 // Try to fill in the missing headers.
25582554 const CBlockIndex* best_header{WITH_LOCK (cs_main, return m_chainman.m_best_header )};
25592555 if (MaybeSendGetHeaders (pfrom, GetLocator (best_header), peer)) {
2560- LogPrint (BCLog::NET, " received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, m_num_unconnecting_headers_msgs =%d)\n " ,
2556+ LogPrint (BCLog::NET, " received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, m_chicken_little =%d)\n " ,
25612557 headers[0 ].GetHash ().ToString (),
25622558 headers[0 ].hashPrevBlock .ToString (),
25632559 best_header->nHeight ,
2564- pfrom.GetId (), peer.m_num_unconnecting_headers_msgs );
2560+ pfrom.GetId (), peer.m_chicken_little );
25652561 }
25662562
25672563 // Set hashLastUnknownBlock for this peer, so that if we
25682564 // eventually get the headers - even from a different peer -
25692565 // we can use this peer to download.
25702566 WITH_LOCK (cs_main, UpdateBlockAvailability (pfrom.GetId (), headers.back ().GetHash ()));
25712567
2572- // The peer may just be broken, so periodically assign DoS points if this
2573- // condition persists.
2574- if (peer.m_num_unconnecting_headers_msgs % MAX_NUM_UNCONNECTING_HEADERS_MSGS == 0 ) {
2575- Misbehaving (peer, strprintf (" %d non-connecting headers" , peer.m_num_unconnecting_headers_msgs ));
2568+ // If the peer has a slightly different clock, it may send us headers more
2569+ // than 2 hours in the future. This is eventually caught in ContextualCheckBlock(),
2570+ // but because we might have fetched the intermediate headers from a different
2571+ // peer, there's no guarantee we'll disconnect this peer.
2572+ //
2573+ // If the block is too far in the future, we immedidately disconnect.
2574+ // This means a malicious peer could get away with a timestamp in between
2575+ // MAX_FUTURE_BLOCK_TIME and the limit here, but such an attack would be
2576+ // very expensive and because of the upper limit, not useful.
2577+ for (auto header : headers) {
2578+ if (header.Time () > NodeClock::now () + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME * 10 }) {
2579+ Misbehaving (peer, " time-too-new" );
2580+ }
2581+ }
2582+
2583+ // Finally, if the peer told us about a (extremely rare) long reorg before,
2584+ // we assume it's broken and just disconnect.
2585+ if (peer.m_chicken_little ) {
2586+ Misbehaving (peer, " non-connecting headers" );
2587+ } else {
2588+ // The peer told us about a long reorg. We'll consider that once.
2589+ peer.m_chicken_little = true ;
25762590 }
25772591}
25782592
@@ -2819,10 +2833,10 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
28192833void PeerManagerImpl::UpdatePeerStateForReceivedHeaders (CNode& pfrom, Peer& peer,
28202834 const CBlockIndex& last_header, bool received_new_header, bool may_have_more_headers)
28212835{
2822- if (peer.m_num_unconnecting_headers_msgs > 0 ) {
2823- LogPrint (BCLog::NET, " peer=%d: resetting m_num_unconnecting_headers_msgs (%d -> 0)\n " , pfrom.GetId (), peer.m_num_unconnecting_headers_msgs );
2836+ if (peer.m_chicken_little > 0 ) {
2837+ LogPrint (BCLog::NET, " peer=%d: resetting m_chicken_little (%d -> 0)\n " , pfrom.GetId (), peer.m_chicken_little );
28242838 }
2825- peer.m_num_unconnecting_headers_msgs = 0 ;
2839+ peer.m_chicken_little = 0 ;
28262840
28272841 LOCK (cs_main);
28282842 CNodeState *nodestate = State (pfrom.GetId ());
0 commit comments