Remove banned peers from DHT and track IPs (#1656)

## Issue Addressed

#629 

## Proposed Changes

This removes banned peers from the DHT and informs discovery to block the node_id and the known source IP's associated with this node. It has the capabilities of un banning this peer after a period of time. 

This also corrects the logic about banning specific IP addresses. We now use seen_ip addresses from libp2p rather than those sent to us via identify (which also include local addresses).
This commit is contained in:
Age Manning 2020-09-25 01:52:39 +00:00
parent 15638d1448
commit 28b6d921c6
6 changed files with 264 additions and 182 deletions

26
Cargo.lock generated
View File

@ -1240,9 +1240,9 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]] [[package]]
name = "discv5" name = "discv5"
version = "0.1.0-alpha.10" version = "0.1.0-alpha.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4cba1b485c16864edc11ccbf3abf5fbf1c26ce759ab36c32ee8e12638d50b0d" checksum = "c68cb1b942aadd3bb3a13620c4d831c0aa49eda988cf8bcccfdfdc7ef69504a7"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"arrayvec", "arrayvec",
@ -1253,17 +1253,17 @@ dependencies = [
"hex 0.4.2", "hex 0.4.2",
"hkdf", "hkdf",
"lazy_static", "lazy_static",
"libp2p-core 0.20.1", "libp2p-core 0.22.1",
"libsecp256k1", "libsecp256k1",
"log 0.4.11", "log 0.4.11",
"lru_time_cache", "lru_time_cache",
"multihash", "multihash",
"net2",
"parking_lot 0.11.0", "parking_lot 0.11.0",
"rand 0.7.3", "rand 0.7.3",
"rlp", "rlp",
"sha2 0.8.2", "sha2 0.8.2",
"smallvec 1.4.2", "smallvec 1.4.2",
"socket2",
"tokio 0.2.22", "tokio 0.2.22",
"uint", "uint",
"zeroize", "zeroize",
@ -2673,9 +2673,8 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-core" name = "libp2p-core"
version = "0.20.1" version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/sigp/rust-libp2p?rev=03f998022ce2f566a6c6e6c4206bc0ce4d45109f#03f998022ce2f566a6c6e6c4206bc0ce4d45109f"
checksum = "6a694fd76d7c33a45a0e6e1525e9b9b5d11127c9c94e560ac0f8abba54ed80af"
dependencies = [ dependencies = [
"asn1_der", "asn1_der",
"bs58", "bs58",
@ -2688,8 +2687,8 @@ dependencies = [
"libsecp256k1", "libsecp256k1",
"log 0.4.11", "log 0.4.11",
"multihash", "multihash",
"multistream-select 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "multistream-select 0.8.2 (git+https://github.com/sigp/rust-libp2p?rev=03f998022ce2f566a6c6e6c4206bc0ce4d45109f)",
"parity-multiaddr 0.9.2", "parity-multiaddr 0.9.1",
"parking_lot 0.10.2", "parking_lot 0.10.2",
"pin-project", "pin-project",
"prost", "prost",
@ -2707,8 +2706,9 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-core" name = "libp2p-core"
version = "0.21.0" version = "0.22.1"
source = "git+https://github.com/sigp/rust-libp2p?rev=03f998022ce2f566a6c6e6c4206bc0ce4d45109f#03f998022ce2f566a6c6e6c4206bc0ce4d45109f" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f13ba8c7df0768af2eb391696d562c7de88cc3a35122531aaa6a7d77754d25"
dependencies = [ dependencies = [
"asn1_der", "asn1_der",
"bs58", "bs58",
@ -2721,8 +2721,8 @@ dependencies = [
"libsecp256k1", "libsecp256k1",
"log 0.4.11", "log 0.4.11",
"multihash", "multihash",
"multistream-select 0.8.2 (git+https://github.com/sigp/rust-libp2p?rev=03f998022ce2f566a6c6e6c4206bc0ce4d45109f)", "multistream-select 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multiaddr 0.9.1", "parity-multiaddr 0.9.2",
"parking_lot 0.10.2", "parking_lot 0.10.2",
"pin-project", "pin-project",
"prost", "prost",

View File

@ -40,6 +40,59 @@ mod handler;
const MAX_IDENTIFY_ADDRESSES: usize = 10; const MAX_IDENTIFY_ADDRESSES: usize = 10;
/// Identifier of requests sent by a peer.
pub type PeerRequestId = (ConnectionId, SubstreamId);
/// The types of events than can be obtained from polling the behaviour.
#[derive(Debug)]
pub enum BehaviourEvent<TSpec: EthSpec> {
/// We have successfully dialed and connected to a peer.
PeerDialed(PeerId),
/// A peer has successfully dialed and connected to us.
PeerConnected(PeerId),
/// A peer has disconnected.
PeerDisconnected(PeerId),
/// An RPC Request that was sent failed.
RPCFailed {
/// The id of the failed request.
id: RequestId,
/// The peer to which this request was sent.
peer_id: PeerId,
/// The error that occurred.
error: RPCError,
},
RequestReceived {
/// The peer that sent the request.
peer_id: PeerId,
/// Identifier of the request. All responses to this request must use this id.
id: PeerRequestId,
/// Request the peer sent.
request: Request,
},
ResponseReceived {
/// Peer that sent the response.
peer_id: PeerId,
/// Id of the request to which the peer is responding.
id: RequestId,
/// Response the peer sent.
response: Response<TSpec>,
},
PubsubMessage {
/// The gossipsub message id. Used when propagating blocks after validation.
id: MessageId,
/// The peer from which we received this message, not the peer that published it.
source: PeerId,
/// The topics that this message was sent on.
topics: Vec<TopicHash>,
/// The message itself.
message: PubsubMessage<TSpec>,
},
/// Subscribed to peer for given topic
PeerSubscribed(PeerId, TopicHash),
/// Inform the network to send a Status to this peer.
StatusPeer(PeerId),
}
/// Builds the network behaviour that manages the core protocols of eth2. /// Builds the network behaviour that manages the core protocols of eth2.
/// This core behaviour is managed by `Behaviour` which adds peer management to all core /// This core behaviour is managed by `Behaviour` which adds peer management to all core
/// behaviours. /// behaviours.
@ -837,13 +890,15 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
// notify the peer manager of a successful connection // notify the peer manager of a successful connection
match endpoint { match endpoint {
ConnectedPoint::Listener { .. } => { ConnectedPoint::Listener { send_back_addr, .. } => {
self.peer_manager.connect_ingoing(&peer_id); self.peer_manager
.connect_ingoing(&peer_id, send_back_addr.clone());
self.add_event(BehaviourEvent::PeerConnected(peer_id.clone())); self.add_event(BehaviourEvent::PeerConnected(peer_id.clone()));
debug!(self.log, "Connection established"; "peer_id" => peer_id.to_string(), "connection" => "Incoming"); debug!(self.log, "Connection established"; "peer_id" => peer_id.to_string(), "connection" => "Incoming");
} }
ConnectedPoint::Dialer { .. } => { ConnectedPoint::Dialer { address } => {
self.peer_manager.connect_outgoing(&peer_id); self.peer_manager
.connect_outgoing(&peer_id, address.clone());
self.add_event(BehaviourEvent::PeerDialed(peer_id.clone())); self.add_event(BehaviourEvent::PeerDialed(peer_id.clone()));
debug!(self.log, "Connection established"; "peer_id" => peer_id.to_string(), "connection" => "Dialed"); debug!(self.log, "Connection established"; "peer_id" => peer_id.to_string(), "connection" => "Dialed");
} }
@ -1061,59 +1116,6 @@ impl<TSpec: EthSpec> std::convert::From<Response<TSpec>> for RPCCodedResponse<TS
} }
} }
/// Identifier of requests sent by a peer.
pub type PeerRequestId = (ConnectionId, SubstreamId);
/// The types of events than can be obtained from polling the behaviour.
#[derive(Debug)]
pub enum BehaviourEvent<TSpec: EthSpec> {
/// We have successfully dialed and connected to a peer.
PeerDialed(PeerId),
/// A peer has successfully dialed and connected to us.
PeerConnected(PeerId),
/// A peer has disconnected.
PeerDisconnected(PeerId),
/// An RPC Request that was sent failed.
RPCFailed {
/// The id of the failed request.
id: RequestId,
/// The peer to which this request was sent.
peer_id: PeerId,
/// The error that occurred.
error: RPCError,
},
RequestReceived {
/// The peer that sent the request.
peer_id: PeerId,
/// Identifier of the request. All responses to this request must use this id.
id: PeerRequestId,
/// Request the peer sent.
request: Request,
},
ResponseReceived {
/// Peer that sent the response.
peer_id: PeerId,
/// Id of the request to which the peer is responding.
id: RequestId,
/// Response the peer sent.
response: Response<TSpec>,
},
PubsubMessage {
/// The gossipsub message id. Used when propagating blocks after validation.
id: MessageId,
/// The peer from which we received this message, not the peer that published it.
source: PeerId,
/// The topics that this message was sent on.
topics: Vec<TopicHash>,
/// The message itself.
message: PubsubMessage<TSpec>,
},
/// Subscribed to peer for given topic
PeerSubscribed(PeerId, TopicHash),
/// Inform the network to send a Status to this peer.
StatusPeer(PeerId),
}
/// Persist metadata to disk /// Persist metadata to disk
pub fn save_metadata_to_disk<E: EthSpec>(dir: &PathBuf, metadata: MetaData<E>, log: &slog::Logger) { pub fn save_metadata_to_disk<E: EthSpec>(dir: &PathBuf, metadata: MetaData<E>, log: &slog::Logger) {
let _ = std::fs::create_dir_all(&dir); let _ = std::fs::create_dir_all(&dir);

View File

@ -4,7 +4,7 @@ pub mod enr_ext;
// Allow external use of the lighthouse ENR builder // Allow external use of the lighthouse ENR builder
pub use enr::{build_enr, create_enr_builder_from_config, use_or_load_enr, CombinedKey, Eth2Enr}; pub use enr::{build_enr, create_enr_builder_from_config, use_or_load_enr, CombinedKey, Eth2Enr};
pub use enr_ext::{CombinedKeyExt, EnrExt}; pub use enr_ext::{peer_id_to_node_id, CombinedKeyExt, EnrExt};
pub use libp2p::core::identity::Keypair; pub use libp2p::core::identity::Keypair;
use crate::metrics; use crate::metrics;
@ -20,7 +20,7 @@ use ssz::{Decode, Encode};
use ssz_types::BitVector; use ssz_types::BitVector;
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
net::SocketAddr, net::{IpAddr, SocketAddr},
path::Path, path::Path,
pin::Pin, pin::Pin,
sync::Arc, sync::Arc,
@ -436,6 +436,33 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
enr::save_enr_to_disk(Path::new(&self.enr_dir), &self.local_enr(), &self.log); enr::save_enr_to_disk(Path::new(&self.enr_dir), &self.local_enr(), &self.log);
} }
// Bans a peer and it's associated seen IP addresses.
pub fn ban_peer(&mut self, peer_id: &PeerId, ip_addresses: Vec<IpAddr>) {
// first try and convert the peer_id to a node_id.
if let Ok(node_id) = peer_id_to_node_id(peer_id) {
// If we could convert this peer id, remove it from the DHT and ban it from discovery.
self.discv5.ban_node(&node_id);
// Remove the node from the routing table.
self.discv5.remove_node(&node_id);
}
for ip_address in ip_addresses {
self.discv5.ban_ip(ip_address);
}
}
pub fn unban_peer(&mut self, peer_id: &PeerId, ip_addresses: Vec<IpAddr>) {
// first try and convert the peer_id to a node_id.
if let Ok(node_id) = peer_id_to_node_id(peer_id) {
// If we could convert this peer id, remove it from the DHT and ban it from discovery.
self.discv5.permit_node(&node_id);
}
for ip_address in ip_addresses {
self.discv5.permit_ip(ip_address);
}
}
/* Internal Functions */ /* Internal Functions */
/// Adds a subnet query if one doesn't exist. If a subnet query already exists, this /// Adds a subnet query if one doesn't exist. If a subnet query already exists, this

View File

@ -194,9 +194,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
// Update the PeerDB state. // Update the PeerDB state.
if let Some(peer_id) = ban_peer.take() { if let Some(peer_id) = ban_peer.take() {
self.network_globals.peers.write().ban(&peer_id); self.ban_peer(&peer_id);
} else if let Some(peer_id) = unban_peer.take() { } else if let Some(peer_id) = unban_peer.take() {
self.network_globals.peers.write().unban(&peer_id); self.unban_peer(&peer_id);
} }
} }
@ -312,19 +312,22 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// Sets a peer as connected as long as their reputation allows it /// Sets a peer as connected as long as their reputation allows it
/// Informs if the peer was accepted /// Informs if the peer was accepted
pub fn connect_ingoing(&mut self, peer_id: &PeerId) -> bool { pub fn connect_ingoing(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) -> bool {
self.connect_peer(peer_id, ConnectingType::IngoingConnected) self.connect_peer(peer_id, ConnectingType::IngoingConnected { multiaddr })
} }
/// Sets a peer as connected as long as their reputation allows it /// Sets a peer as connected as long as their reputation allows it
/// Informs if the peer was accepted /// Informs if the peer was accepted
pub fn connect_outgoing(&mut self, peer_id: &PeerId) -> bool { pub fn connect_outgoing(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) -> bool {
self.connect_peer(peer_id, ConnectingType::OutgoingConnected) self.connect_peer(peer_id, ConnectingType::OutgoingConnected { multiaddr })
} }
/// Updates the database informing that a peer is being disconnected. /// Updates the database informing that a peer is being disconnected.
pub fn _disconnecting_peer(&mut self, _peer_id: &PeerId) -> bool { pub fn _disconnecting_peer(&mut self, _peer_id: &PeerId) -> bool {
// TODO: implement // TODO: implement
// This informs the database that we are in the process of disconnecting the
// peer. Currently this state only exists for a short period of time before we force the
// disconnection.
true true
} }
@ -644,8 +647,12 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
peerdb.dialing_peer(peer_id); peerdb.dialing_peer(peer_id);
return true; return true;
} }
ConnectingType::IngoingConnected => peerdb.connect_outgoing(peer_id), ConnectingType::IngoingConnected { multiaddr } => {
ConnectingType::OutgoingConnected => peerdb.connect_ingoing(peer_id), peerdb.connect_outgoing(peer_id, multiaddr)
}
ConnectingType::OutgoingConnected { multiaddr } => {
peerdb.connect_ingoing(peer_id, multiaddr)
}
} }
} }
@ -683,12 +690,11 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// NOTE: This is experimental and will likely be adjusted /// NOTE: This is experimental and will likely be adjusted
fn update_peer_scores(&mut self) { fn update_peer_scores(&mut self) {
/* Check how long have peers been in this state and update their reputations if needed */ /* Check how long have peers been in this state and update their reputations if needed */
let mut pdb = self.network_globals.peers.write();
let mut to_ban_peers = Vec::new(); let mut to_ban_peers = Vec::new();
let mut to_unban_peers = Vec::new(); let mut to_unban_peers = Vec::new();
for (peer_id, info) in pdb.peers_mut() { for (peer_id, info) in self.network_globals.peers.write().peers_mut() {
let previous_state = info.score_state(); let previous_state = info.score_state();
// Update scores // Update scores
info.score_update(); info.score_update();
@ -780,14 +786,51 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
} }
// process banning peers // process banning peers
for peer_id in to_ban_peers { for peer_id in to_ban_peers {
pdb.ban(&peer_id); self.ban_peer(&peer_id);
} }
// process unbanning peers // process unbanning peers
for peer_id in to_unban_peers { for peer_id in to_unban_peers {
pdb.unban(&peer_id); self.unban_peer(&peer_id);
} }
} }
/// Bans a peer.
///
/// Records updates the peers connection status and updates the peer db as well as blocks the
/// peer from participating in discovery and removes them from the routing table.
fn ban_peer(&mut self, peer_id: &PeerId) {
let mut peer_db = self.network_globals.peers.write();
peer_db.ban(peer_id);
let banned_ip_addresses = peer_db
.peer_info(peer_id)
.map(|info| {
info.seen_addresses
.iter()
.filter(|ip| peer_db.is_ip_banned(ip))
.cloned()
.collect::<Vec<_>>()
})
.unwrap_or_default();
self.discovery.ban_peer(&peer_id, banned_ip_addresses);
}
/// Unbans a peer.
///
/// Records updates the peers connection status and updates the peer db as well as removes
/// previous bans from discovery.
fn unban_peer(&mut self, peer_id: &PeerId) {
let mut peer_db = self.network_globals.peers.write();
peer_db.unban(&peer_id);
let seen_ip_addresses = peer_db
.peer_info(peer_id)
.map(|info| info.seen_addresses.iter().cloned().collect::<Vec<_>>())
.unwrap_or_default();
self.discovery.unban_peer(&peer_id, seen_ip_addresses);
}
/// The Peer manager's heartbeat maintains the peer count and maintains peer reputations. /// The Peer manager's heartbeat maintains the peer count and maintains peer reputations.
/// ///
/// It will request discovery queries if the peer count has not reached the desired number of /// It will request discovery queries if the peer count has not reached the desired number of
@ -894,7 +937,13 @@ enum ConnectingType {
/// We are in the process of dialing this peer. /// We are in the process of dialing this peer.
Dialing, Dialing,
/// A peer has dialed us. /// A peer has dialed us.
IngoingConnected, IngoingConnected {
// The multiaddr the peer connected to us on.
multiaddr: Multiaddr,
},
/// We have successfully dialed a peer. /// We have successfully dialed a peer.
OutgoingConnected, OutgoingConnected {
/// The multiaddr we dialed to reach the peer.
multiaddr: Multiaddr,
},
} }

View File

@ -7,6 +7,7 @@ use serde::{
ser::{SerializeStruct, Serializer}, ser::{SerializeStruct, Serializer},
Serialize, Serialize,
}; };
use std::collections::HashSet;
use std::net::IpAddr; use std::net::IpAddr;
use std::time::Instant; use std::time::Instant;
use types::{EthSpec, SubnetId}; use types::{EthSpec, SubnetId};
@ -24,8 +25,12 @@ pub struct PeerInfo<T: EthSpec> {
pub client: Client, pub client: Client,
/// Connection status of this peer /// Connection status of this peer
pub connection_status: PeerConnectionStatus, pub connection_status: PeerConnectionStatus,
/// The known listening addresses of this peer. /// The known listening addresses of this peer. This is given by identify and can be arbitrary
/// (including local IPs).
pub listening_addresses: Vec<Multiaddr>, pub listening_addresses: Vec<Multiaddr>,
/// This is addresses we have physically seen and this is what we use for banning/un-banning
/// peers.
pub seen_addresses: HashSet<IpAddr>,
/// The current syncing state of the peer. The state may be determined after it's initial /// The current syncing state of the peer. The state may be determined after it's initial
/// connection. /// connection.
pub sync_status: PeerSyncStatus, pub sync_status: PeerSyncStatus,
@ -47,7 +52,8 @@ impl<TSpec: EthSpec> Default for PeerInfo<TSpec> {
score: Score::default(), score: Score::default(),
client: Client::default(), client: Client::default(),
connection_status: Default::default(), connection_status: Default::default(),
listening_addresses: vec![], listening_addresses: Vec::new(),
seen_addresses: HashSet::new(),
sync_status: PeerSyncStatus::Unknown, sync_status: PeerSyncStatus::Unknown,
meta_data: None, meta_data: None,
min_ttl: None, min_ttl: None,

View File

@ -1,7 +1,7 @@
use super::peer_info::{PeerConnectionStatus, PeerInfo}; use super::peer_info::{PeerConnectionStatus, PeerInfo};
use super::peer_sync_status::PeerSyncStatus; use super::peer_sync_status::PeerSyncStatus;
use super::score::{Score, ScoreState}; use super::score::{Score, ScoreState};
use crate::multiaddr::Protocol; use crate::multiaddr::{Multiaddr, Protocol};
use crate::rpc::methods::MetaData; use crate::rpc::methods::MetaData;
use crate::PeerId; use crate::PeerId;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
@ -174,13 +174,14 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
} }
fn ip_is_banned(&self, peer: &PeerInfo<TSpec>) -> bool { fn ip_is_banned(&self, peer: &PeerInfo<TSpec>) -> bool {
peer.listening_addresses.iter().any(|addr| { peer.seen_addresses
addr.iter().any(|p| match p { .iter()
Protocol::Ip4(ip) => self.banned_peers_count.ip_is_banned(&ip.into()), .any(|addr| self.banned_peers_count.ip_is_banned(addr))
Protocol::Ip6(ip) => self.banned_peers_count.ip_is_banned(&ip.into()), }
_ => false,
}) /// Returns true if the IP is banned.
}) pub fn is_ip_banned(&self, ip: &IpAddr) -> bool {
self.banned_peers_count.ip_is_banned(ip)
} }
/// Returns true if the Peer is either banned or in the disconnected state. /// Returns true if the Peer is either banned or in the disconnected state.
@ -361,7 +362,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
} }
/// Sets a peer as connected with an ingoing connection. /// Sets a peer as connected with an ingoing connection.
pub fn connect_ingoing(&mut self, peer_id: &PeerId) { pub fn connect_ingoing(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) {
let info = self.peers.entry(peer_id.clone()).or_default(); let info = self.peers.entry(peer_id.clone()).or_default();
if info.connection_status.is_disconnected() { if info.connection_status.is_disconnected() {
@ -370,10 +371,19 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
self.banned_peers_count self.banned_peers_count
.remove_banned_peer(&info.connection_status); .remove_banned_peer(&info.connection_status);
info.connection_status.connect_ingoing(); info.connection_status.connect_ingoing();
// Add the seen ip address to the peer's info
if let Some(ip_addr) = multiaddr.iter().find_map(|p| match p {
Protocol::Ip4(ip) => Some(ip.into()),
Protocol::Ip6(ip) => Some(ip.into()),
_ => None,
}) {
info.seen_addresses.insert(ip_addr);
}
} }
/// Sets a peer as connected with an outgoing connection. /// Sets a peer as connected with an outgoing connection.
pub fn connect_outgoing(&mut self, peer_id: &PeerId) { pub fn connect_outgoing(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) {
let info = self.peers.entry(peer_id.clone()).or_default(); let info = self.peers.entry(peer_id.clone()).or_default();
if info.connection_status.is_disconnected() { if info.connection_status.is_disconnected() {
@ -382,6 +392,15 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
self.banned_peers_count self.banned_peers_count
.remove_banned_peer(&info.connection_status); .remove_banned_peer(&info.connection_status);
info.connection_status.connect_outgoing(); info.connection_status.connect_outgoing();
// Add the seen ip address to the peer's info
if let Some(ip_addr) = multiaddr.iter().find_map(|p| match p {
Protocol::Ip4(ip) => Some(ip.into()),
Protocol::Ip6(ip) => Some(ip.into()),
_ => None,
}) {
info.seen_addresses.insert(ip_addr);
}
} }
/// Sets the peer as disconnected. A banned peer remains banned /// Sets the peer as disconnected. A banned peer remains banned
@ -411,20 +430,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
} }
if !info.connection_status.is_banned() { if !info.connection_status.is_banned() {
info.connection_status info.connection_status
.ban( .ban(info.seen_addresses.iter().cloned().collect());
info.listening_addresses
.iter()
.fold(Vec::new(), |mut v, a| {
for p in a {
match p {
Protocol::Ip4(ip) => v.push(ip.into()),
Protocol::Ip6(ip) => v.push(ip.into()),
_ => (),
}
}
v
}),
);
self.banned_peers_count self.banned_peers_count
.add_banned_peer(&info.connection_status); .add_banned_peer(&info.connection_status);
} }
@ -564,10 +570,10 @@ mod tests {
let (n_in, n_out) = (10, 20); let (n_in, n_out) = (10, 20);
for _ in 0..n_in { for _ in 0..n_in {
pdb.connect_ingoing(&random_peer); pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
} }
for _ in 0..n_out { for _ in 0..n_out {
pdb.connect_outgoing(&random_peer); pdb.connect_outgoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
} }
// the peer is known // the peer is known
@ -592,7 +598,7 @@ mod tests {
for _ in 0..MAX_DC_PEERS + 1 { for _ in 0..MAX_DC_PEERS + 1 {
let p = PeerId::random(); let p = PeerId::random();
pdb.connect_ingoing(&p); pdb.connect_ingoing(&p, "/ip4/0.0.0.0".parse().unwrap());
} }
assert_eq!(pdb.disconnected_peers, 0); assert_eq!(pdb.disconnected_peers, 0);
@ -609,7 +615,7 @@ mod tests {
for _ in 0..MAX_BANNED_PEERS + 1 { for _ in 0..MAX_BANNED_PEERS + 1 {
let p = PeerId::random(); let p = PeerId::random();
pdb.connect_ingoing(&p); pdb.connect_ingoing(&p, "/ip4/0.0.0.0".parse().unwrap());
} }
assert_eq!(pdb.banned_peers_count.banned_peers(), 0); assert_eq!(pdb.banned_peers_count.banned_peers(), 0);
@ -627,9 +633,9 @@ mod tests {
let p0 = PeerId::random(); let p0 = PeerId::random();
let p1 = PeerId::random(); let p1 = PeerId::random();
let p2 = PeerId::random(); let p2 = PeerId::random();
pdb.connect_ingoing(&p0); pdb.connect_ingoing(&p0, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&p1); pdb.connect_ingoing(&p1, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&p2); pdb.connect_ingoing(&p2, "/ip4/0.0.0.0".parse().unwrap());
add_score(&mut pdb, &p0, 70.0); add_score(&mut pdb, &p0, 70.0);
add_score(&mut pdb, &p1, 100.0); add_score(&mut pdb, &p1, 100.0);
add_score(&mut pdb, &p2, 50.0); add_score(&mut pdb, &p2, 50.0);
@ -649,9 +655,9 @@ mod tests {
let p0 = PeerId::random(); let p0 = PeerId::random();
let p1 = PeerId::random(); let p1 = PeerId::random();
let p2 = PeerId::random(); let p2 = PeerId::random();
pdb.connect_ingoing(&p0); pdb.connect_ingoing(&p0, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&p1); pdb.connect_ingoing(&p1, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&p2); pdb.connect_ingoing(&p2, "/ip4/0.0.0.0".parse().unwrap());
add_score(&mut pdb, &p0, 70.0); add_score(&mut pdb, &p0, 70.0);
add_score(&mut pdb, &p1, 100.0); add_score(&mut pdb, &p1, 100.0);
add_score(&mut pdb, &p2, 50.0); add_score(&mut pdb, &p2, 50.0);
@ -669,18 +675,18 @@ mod tests {
let random_peer = PeerId::random(); let random_peer = PeerId::random();
pdb.connect_ingoing(&random_peer); pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
dbg!("1"); dbg!("1");
pdb.connect_ingoing(&random_peer); pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
dbg!("1"); dbg!("1");
pdb.disconnect(&random_peer); pdb.disconnect(&random_peer);
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
dbg!("1"); dbg!("1");
pdb.connect_outgoing(&random_peer); pdb.connect_outgoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
dbg!("1"); dbg!("1");
pdb.disconnect(&random_peer); pdb.disconnect(&random_peer);
@ -711,20 +717,20 @@ mod tests {
let random_peer2 = PeerId::random(); let random_peer2 = PeerId::random();
let random_peer3 = PeerId::random(); let random_peer3 = PeerId::random();
pdb.connect_ingoing(&random_peer); pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&random_peer1); pdb.connect_ingoing(&random_peer1, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&random_peer2); pdb.connect_ingoing(&random_peer2, "/ip4/0.0.0.0".parse().unwrap());
pdb.connect_ingoing(&random_peer3); pdb.connect_ingoing(&random_peer3, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
assert_eq!( assert_eq!(
pdb.banned_peers_count.banned_peers(), pdb.banned_peers_count.banned_peers(),
pdb.banned_peers().count() pdb.banned_peers().count()
); );
pdb.connect_ingoing(&random_peer); pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
pdb.disconnect(&random_peer1); pdb.disconnect(&random_peer1);
pdb.ban(&random_peer2); pdb.ban(&random_peer2);
pdb.connect_ingoing(&random_peer3); pdb.connect_ingoing(&random_peer3, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
assert_eq!( assert_eq!(
pdb.banned_peers_count.banned_peers(), pdb.banned_peers_count.banned_peers(),
@ -737,7 +743,7 @@ mod tests {
pdb.banned_peers().count() pdb.banned_peers().count()
); );
pdb.connect_outgoing(&random_peer2); pdb.connect_outgoing(&random_peer2, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
assert_eq!( assert_eq!(
pdb.banned_peers_count.banned_peers(), pdb.banned_peers_count.banned_peers(),
@ -751,10 +757,10 @@ mod tests {
); );
pdb.ban(&random_peer3); pdb.ban(&random_peer3);
pdb.connect_ingoing(&random_peer1); pdb.connect_ingoing(&random_peer1, "/ip4/0.0.0.0".parse().unwrap());
pdb.disconnect(&random_peer2); pdb.disconnect(&random_peer2);
pdb.ban(&random_peer3); pdb.ban(&random_peer3);
pdb.connect_ingoing(&random_peer); pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap());
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
assert_eq!( assert_eq!(
pdb.banned_peers_count.banned_peers(), pdb.banned_peers_count.banned_peers(),
@ -777,19 +783,14 @@ mod tests {
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
} }
fn connect_peer_with_ips(pdb: &mut PeerDB<M>, ips: Vec<Vec<IpAddr>>) -> PeerId { fn connect_peer_with_ips(pdb: &mut PeerDB<M>, ips: Vec<IpAddr>) -> PeerId {
let p = PeerId::random(); let p = PeerId::random();
pdb.connect_ingoing(&p);
pdb.peers.get_mut(&p).unwrap().listening_addresses = ips for ip in ips {
.into_iter() let mut addr = Multiaddr::empty();
.map(|ip_addresses| { addr.push(Protocol::from(ip));
let mut addr = Multiaddr::empty(); pdb.connect_ingoing(&p, addr);
for ip_address in ip_addresses { }
addr.push(Protocol::from(ip_address));
}
addr
})
.collect();
p p
} }
@ -797,29 +798,29 @@ mod tests {
fn test_ban_address() { fn test_ban_address() {
let mut pdb = get_db(); let mut pdb = get_db();
let ip1: IpAddr = Ipv4Addr::new(1, 2, 3, 4).into(); let ip1 = Ipv4Addr::new(1, 2, 3, 4).into();
let ip2: IpAddr = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8).into(); let ip2 = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8).into();
let ip3: IpAddr = Ipv4Addr::new(1, 2, 3, 5).into(); let ip3 = Ipv4Addr::new(1, 2, 3, 5).into();
let ip4: IpAddr = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 9).into(); let ip4 = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 9).into();
let ip5: IpAddr = Ipv4Addr::new(2, 2, 3, 4).into(); let ip5 = Ipv4Addr::new(2, 2, 3, 4).into();
let mut peers = Vec::new(); let mut peers = Vec::new();
for i in 0..BANNED_PEERS_PER_IP_THRESHOLD + 2 { for i in 0..BANNED_PEERS_PER_IP_THRESHOLD + 2 {
peers.push(connect_peer_with_ips( peers.push(connect_peer_with_ips(
&mut pdb, &mut pdb,
if i == 0 { if i == 0 {
vec![vec![ip1], vec![ip2]] vec![ip1, ip2]
} else { } else {
vec![vec![ip1, ip2], vec![ip3, ip4]] vec![ip1, ip2, ip3, ip4]
}, },
)); ));
} }
let p1 = connect_peer_with_ips(&mut pdb, vec![vec![ip1]]); let p1 = connect_peer_with_ips(&mut pdb, vec![ip1]);
let p2 = connect_peer_with_ips(&mut pdb, vec![vec![ip2, ip5]]); let p2 = connect_peer_with_ips(&mut pdb, vec![ip2, ip5]);
let p3 = connect_peer_with_ips(&mut pdb, vec![vec![ip3], vec![ip5]]); let p3 = connect_peer_with_ips(&mut pdb, vec![ip3, ip5]);
let p4 = connect_peer_with_ips(&mut pdb, vec![vec![ip5, ip4]]); let p4 = connect_peer_with_ips(&mut pdb, vec![ip5, ip4]);
let p5 = connect_peer_with_ips(&mut pdb, vec![vec![ip5]]); let p5 = connect_peer_with_ips(&mut pdb, vec![ip5]);
for p in &peers[..BANNED_PEERS_PER_IP_THRESHOLD + 1] { for p in &peers[..BANNED_PEERS_PER_IP_THRESHOLD + 1] {
pdb.ban(p); pdb.ban(p);
@ -872,66 +873,63 @@ mod tests {
let mut peers = Vec::new(); let mut peers = Vec::new();
for _ in 0..BANNED_PEERS_PER_IP_THRESHOLD + 1 { for _ in 0..BANNED_PEERS_PER_IP_THRESHOLD + 1 {
peers.push(connect_peer_with_ips(&mut pdb, vec![vec![ip1]])); peers.push(connect_peer_with_ips(&mut pdb, vec![ip1]));
} }
let p1 = connect_peer_with_ips(&mut pdb, vec![vec![ip1]]); let p1 = connect_peer_with_ips(&mut pdb, vec![ip1]);
let p2 = connect_peer_with_ips(&mut pdb, vec![vec![ip2]]); let p2 = connect_peer_with_ips(&mut pdb, vec![ip2]);
//ban all peers // ban all peers
for p in &peers { for p in &peers {
pdb.ban(p); pdb.ban(p);
} }
//check ip is banned // check ip is banned
assert!(pdb.is_banned(&p1)); assert!(pdb.is_banned(&p1));
assert!(!pdb.is_banned(&p2)); assert!(!pdb.is_banned(&p2));
//change addresses of banned peers // change addresses of banned peers
for p in &peers { for p in &peers {
pdb.peers.get_mut(p).unwrap().listening_addresses = let seen_addresses = &mut pdb.peers.get_mut(p).unwrap().seen_addresses;
vec![Multiaddr::empty().with(Protocol::from(ip2))]; seen_addresses.clear();
seen_addresses.insert(ip2);
} }
//check still the same ip is banned // check still the same ip is banned
assert!(pdb.is_banned(&p1)); assert!(pdb.is_banned(&p1));
assert!(!pdb.is_banned(&p2)); assert!(!pdb.is_banned(&p2));
//unban a peer // unban a peer
pdb.unban(&peers[0]); pdb.unban(&peers[0]);
//check not banned anymore // check not banned anymore
assert!(!pdb.is_banned(&p1)); assert!(!pdb.is_banned(&p1));
assert!(!pdb.is_banned(&p2)); assert!(!pdb.is_banned(&p2));
//check still not banned after new ban // unban and reban all peers
pdb.ban(&peers[0]);
assert!(!pdb.is_banned(&p1));
assert!(!pdb.is_banned(&p2));
//unban and reban all peers
for p in &peers { for p in &peers {
pdb.unban(p); pdb.unban(p);
pdb.ban(p); pdb.ban(p);
} }
//ip2 is now banned // ip2 is now banned
assert!(!pdb.is_banned(&p1)); assert!(!pdb.is_banned(&p1));
assert!(pdb.is_banned(&p2)); assert!(pdb.is_banned(&p2));
//change ips back again // change ips back again
for p in &peers { for p in &peers {
pdb.peers.get_mut(p).unwrap().listening_addresses = let seen_addresses = &mut pdb.peers.get_mut(p).unwrap().seen_addresses;
vec![Multiaddr::empty().with(Protocol::from(ip1))]; seen_addresses.clear();
seen_addresses.insert(ip1);
} }
//reban every peer except one // reban every peer except one
for p in &peers[1..] { for p in &peers[1..] {
pdb.unban(p); pdb.unban(p);
pdb.ban(p); pdb.ban(p);
} }
//nothing is banned // nothing is banned
assert!(!pdb.is_banned(&p1)); assert!(!pdb.is_banned(&p1));
assert!(!pdb.is_banned(&p2)); assert!(!pdb.is_banned(&p2));
@ -950,7 +948,7 @@ mod tests {
let log = build_log(slog::Level::Debug, false); let log = build_log(slog::Level::Debug, false);
let mut pdb: PeerDB<M> = PeerDB::new(vec![trusted_peer.clone()], &log); let mut pdb: PeerDB<M> = PeerDB::new(vec![trusted_peer.clone()], &log);
pdb.connect_ingoing(&trusted_peer); pdb.connect_ingoing(&trusted_peer, "/ip4/0.0.0.0".parse().unwrap());
// Check trusted status and score // Check trusted status and score
assert!(pdb.peer_info(&trusted_peer).unwrap().is_trusted); assert!(pdb.peer_info(&trusted_peer).unwrap().is_trusted);