PeerManager: move the check for banned peers from connection_established (#4569)

## Issue Addressed
https://github.com/sigp/lighthouse/issues/4543
## Proposed Changes
- Removes `NotBanned` from `BanResult`, implements `Display` and `std::error::Error` for `BanResult` and changes `ban_result` return type to `Option<BanResult>` which helps returning `BanResult` on `handle_established_inbound_connection`  
- moves the check from for banned peers from `on_connection_established` to `handle_established_inbound_connection` to start addressing #4543.
- Removes `allow_block_list` as it's now redundant? Not sure about this one but if `PeerManager` keeps track of the banned peers, no need to send a `Swarm` event for `alow_block_list` to also keep that list right? 
 
## Questions

-  #4543 refers:
>  More specifically, implement the connection limit behaviour inside the peer manager.

@AgeManning do you mean copying `libp2p::connection_limits::Behaviour`'s code into `PeerManager`/ having it as an inner `NetworkBehaviour` of `PeerManager`/other? If it's the first two, I think it probably makes more sense to have it as it is as it's less code to maintain.

> Also implement the banning of peers inside the behaviour, rather than passing messages back up to the swarm.

I tried to achieve this, but we still need to pass the `PeerManagerEvent::Banned` swarm event as `DiscV5` handles it's node and ip management internally and I did not find a method to query if a peer is banned. Is there anything else we can do from here?

3397612160/beacon_node/lighthouse_network/src/discovery/mod.rs (L931-L940)

Same as the question above, I did not find a way to check if `DiscV5` has the peer banned, so that we could check here and avoid sending `Swarm` events

3397612160/beacon_node/lighthouse_network/src/peer_manager/network_behaviour.rs (L168-L178)

Is there a chance we try to dial a peer that has been banned previously? 

Thanks!
This commit is contained in:
João Oliveira
2023-10-03 23:59:32 +00:00
parent 7605494791
commit 0dc95a1d37
6 changed files with 107 additions and 113 deletions

View File

@@ -3,10 +3,13 @@ use peer_info::{ConnectionDirection, PeerConnectionStatus, PeerInfo};
use rand::seq::SliceRandom;
use score::{PeerAction, ReportSource, Score, ScoreState};
use slog::{crit, debug, error, trace, warn};
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::net::IpAddr;
use std::time::Instant;
use std::{cmp::Ordering, fmt::Display};
use std::{
collections::{HashMap, HashSet},
fmt::Formatter,
};
use sync_status::SyncStatus;
use types::EthSpec;
@@ -136,26 +139,18 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
}
}
/// Returns the current [`BanResult`] of the peer. This doesn't check the connection state, rather the
/// Returns the current [`BanResult`] of the peer if banned. This doesn't check the connection state, rather the
/// underlying score of the peer. A peer may be banned but still in the connected state
/// temporarily.
///
/// This is used to determine if we should accept incoming connections or not.
pub fn ban_status(&self, peer_id: &PeerId) -> BanResult {
if let Some(peer) = self.peers.get(peer_id) {
match peer.score_state() {
ScoreState::Banned => BanResult::BadScore,
_ => {
if let Some(ip) = self.ip_is_banned(peer) {
BanResult::BannedIp(ip)
} else {
BanResult::NotBanned
}
}
}
} else {
BanResult::NotBanned
}
pub fn ban_status(&self, peer_id: &PeerId) -> Option<BanResult> {
self.peers
.get(peer_id)
.and_then(|peer| match peer.score_state() {
ScoreState::Banned => Some(BanResult::BadScore),
_ => self.ip_is_banned(peer).map(BanResult::BannedIp),
})
}
/// Checks if the peer's known addresses are currently banned.
@@ -1183,23 +1178,25 @@ pub enum BanOperation {
}
/// When checking if a peer is banned, it can be banned for multiple reasons.
#[derive(Copy, Clone, Debug)]
pub enum BanResult {
/// The peer's score is too low causing it to be banned.
BadScore,
/// The peer should be banned because it is connecting from a banned IP address.
BannedIp(IpAddr),
/// The peer is not banned.
NotBanned,
}
// Helper function for unit tests
#[cfg(test)]
impl BanResult {
pub fn is_banned(&self) -> bool {
!matches!(self, BanResult::NotBanned)
impl Display for BanResult {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BanResult::BadScore => write!(f, "Peer has a bad score"),
BanResult::BannedIp(addr) => write!(f, "Peer address: {} is banned", addr),
}
}
}
impl std::error::Error for BanResult {}
#[derive(Default)]
pub struct BannedPeersCount {
/// The number of banned peers in the database.
@@ -1852,11 +1849,11 @@ mod tests {
}
//check that ip1 and ip2 are banned but ip3-5 not
assert!(pdb.ban_status(&p1).is_banned());
assert!(pdb.ban_status(&p2).is_banned());
assert!(!pdb.ban_status(&p3).is_banned());
assert!(!pdb.ban_status(&p4).is_banned());
assert!(!pdb.ban_status(&p5).is_banned());
assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_some());
assert!(pdb.ban_status(&p3).is_none());
assert!(pdb.ban_status(&p4).is_none());
assert!(pdb.ban_status(&p5).is_none());
//ban also the last peer in peers
let _ = pdb.report_peer(
@@ -1868,11 +1865,11 @@ mod tests {
pdb.inject_disconnect(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]);
//check that ip1-ip4 are banned but ip5 not
assert!(pdb.ban_status(&p1).is_banned());
assert!(pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p3).is_banned());
assert!(pdb.ban_status(&p4).is_banned());
assert!(!pdb.ban_status(&p5).is_banned());
assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_some());
assert!(pdb.ban_status(&p3).is_some());
assert!(pdb.ban_status(&p4).is_some());
assert!(pdb.ban_status(&p5).is_none());
//peers[0] gets unbanned
reset_score(&mut pdb, &peers[0]);
@@ -1880,11 +1877,11 @@ mod tests {
let _ = pdb.shrink_to_fit();
//nothing changed
assert!(pdb.ban_status(&p1).is_banned());
assert!(pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p3).is_banned());
assert!(pdb.ban_status(&p4).is_banned());
assert!(!pdb.ban_status(&p5).is_banned());
assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_some());
assert!(pdb.ban_status(&p3).is_some());
assert!(pdb.ban_status(&p4).is_some());
assert!(pdb.ban_status(&p5).is_none());
//peers[1] gets unbanned
reset_score(&mut pdb, &peers[1]);
@@ -1892,11 +1889,11 @@ mod tests {
let _ = pdb.shrink_to_fit();
//all ips are unbanned
assert!(!pdb.ban_status(&p1).is_banned());
assert!(!pdb.ban_status(&p2).is_banned());
assert!(!pdb.ban_status(&p3).is_banned());
assert!(!pdb.ban_status(&p4).is_banned());
assert!(!pdb.ban_status(&p5).is_banned());
assert!(pdb.ban_status(&p1).is_none());
assert!(pdb.ban_status(&p2).is_none());
assert!(pdb.ban_status(&p3).is_none());
assert!(pdb.ban_status(&p4).is_none());
assert!(pdb.ban_status(&p5).is_none());
}
#[test]
@@ -1921,8 +1918,8 @@ mod tests {
}
// check ip is banned
assert!(pdb.ban_status(&p1).is_banned());
assert!(!pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_none());
// unban a peer
reset_score(&mut pdb, &peers[0]);
@@ -1930,8 +1927,8 @@ mod tests {
let _ = pdb.shrink_to_fit();
// check not banned anymore
assert!(!pdb.ban_status(&p1).is_banned());
assert!(!pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p1).is_none());
assert!(pdb.ban_status(&p2).is_none());
// unban all peers
for p in &peers {
@@ -1950,8 +1947,8 @@ mod tests {
}
// both IP's are now banned
assert!(pdb.ban_status(&p1).is_banned());
assert!(pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_some());
// unban all peers
for p in &peers {
@@ -1967,16 +1964,16 @@ mod tests {
}
// nothing is banned
assert!(!pdb.ban_status(&p1).is_banned());
assert!(!pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p1).is_none());
assert!(pdb.ban_status(&p2).is_none());
// reban last peer
let _ = pdb.report_peer(&peers[0], PeerAction::Fatal, ReportSource::PeerManager, "");
pdb.inject_disconnect(&peers[0]);
//Ip's are banned again
assert!(pdb.ban_status(&p1).is_banned());
assert!(pdb.ban_status(&p2).is_banned());
assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_some());
}
#[test]