-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(connection-limit): set bypass rules for connections #5720
Draft
drHuangMHT
wants to merge
13
commits into
libp2p:master
Choose a base branch
from
drHuangMHT:connection-limit-whitelist
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+35
−1
Draft
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
d78a671
set bypass rules
drHuangMHT 05c757f
Merge branch 'master' into connection-limit-whitelist
dariusc93 cbf009c
Merge branch 'master' into connection-limit-whitelist
dariusc93 399fd34
Merge branch 'master' into connection-limit-whitelist
drHuangMHT 639c3d2
changelog and documentation
drHuangMHT 227b062
replace multiaddr bypass with filter
drHuangMHT d9901ee
Merge branch 'master' into connection-limit-whitelist
drHuangMHT efa83b1
reduce diff
drHuangMHT 47b81c3
fix incorrect condition
drHuangMHT aa01b26
bypass by PeerId only
drHuangMHT 092f313
move bypass list into Behaviour
drHuangMHT 2836212
minor version bump
drHuangMHT 368313c
Merge branch 'master' into connection-limit-whitelist
dariusc93 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,6 +67,7 @@ use libp2p_swarm::{ | |
/// ``` | ||
pub struct Behaviour { | ||
limits: ConnectionLimits, | ||
bypass_rules: BypassRules, | ||
|
||
pending_inbound_connections: HashSet<ConnectionId>, | ||
pending_outbound_connections: HashSet<ConnectionId>, | ||
|
@@ -76,9 +77,10 @@ pub struct Behaviour { | |
} | ||
|
||
impl Behaviour { | ||
pub fn new(limits: ConnectionLimits) -> Self { | ||
pub fn new(limits: ConnectionLimits, bypass_rules: BypassRules) -> Self { | ||
Self { | ||
limits, | ||
bypass_rules, | ||
pending_inbound_connections: Default::default(), | ||
pending_outbound_connections: Default::default(), | ||
established_inbound_connections: Default::default(), | ||
|
@@ -92,6 +94,10 @@ impl Behaviour { | |
pub fn limits_mut(&mut self) -> &mut ConnectionLimits { | ||
&mut self.limits | ||
} | ||
|
||
pub fn bypass_rules_mut(&mut self) -> &mut BypassRules { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the advantage of having an external structure ( |
||
&mut self.bypass_rules | ||
} | ||
} | ||
|
||
fn check_limit(limit: Option<u32>, current: usize, kind: Kind) -> Result<(), ConnectionDenied> { | ||
|
@@ -208,22 +214,57 @@ impl ConnectionLimits { | |
} | ||
} | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub struct BypassRules { | ||
by_peer_id: HashSet<PeerId>, | ||
by_multiaddr: HashSet<Multiaddr>, | ||
} | ||
impl BypassRules { | ||
pub fn new(peer_ids: HashSet<PeerId>, remote_multiaddrs: HashSet<Multiaddr>) -> Self { | ||
Self { | ||
by_peer_id: peer_ids, | ||
by_multiaddr: remote_multiaddrs, | ||
} | ||
} | ||
pub fn bypass_peer_id(&mut self, peer_id: &PeerId) { | ||
self.by_peer_id.insert(*peer_id); | ||
} | ||
pub fn remove_peer_id(&mut self, peer_id: &PeerId) { | ||
self.by_peer_id.remove(peer_id); | ||
} | ||
pub fn bypass_multiaddr(&mut self, multiaddr: Multiaddr) { | ||
self.by_multiaddr.insert(multiaddr); | ||
} | ||
pub fn remove_multiaddr(&mut self, multiaddr: &Multiaddr) { | ||
self.by_multiaddr.remove(multiaddr); | ||
} | ||
pub fn is_peer_bypassed(&self, peer: &PeerId) -> bool { | ||
self.by_peer_id.contains(peer) | ||
} | ||
pub fn is_addr_bypassed(&self, addr: &Multiaddr) -> bool { | ||
self.by_multiaddr.contains(addr) | ||
} | ||
} | ||
|
||
impl NetworkBehaviour for Behaviour { | ||
type ConnectionHandler = dummy::ConnectionHandler; | ||
type ToSwarm = Infallible; | ||
|
||
fn handle_pending_inbound_connection( | ||
&mut self, | ||
connection_id: ConnectionId, | ||
_: &Multiaddr, | ||
_: &Multiaddr, | ||
local_addr: &Multiaddr, | ||
remote_addr: &Multiaddr, | ||
) -> Result<(), ConnectionDenied> { | ||
check_limit( | ||
self.limits.max_pending_incoming, | ||
self.pending_inbound_connections.len(), | ||
Kind::PendingIncoming, | ||
)?; | ||
|
||
if !(self.bypass_rules.is_addr_bypassed(local_addr) | ||
|| self.bypass_rules.is_addr_bypassed(remote_addr)) | ||
{ | ||
check_limit( | ||
self.limits.max_pending_incoming, | ||
self.pending_inbound_connections.len(), | ||
Kind::PendingIncoming, | ||
)?; | ||
} | ||
self.pending_inbound_connections.insert(connection_id); | ||
|
||
Ok(()) | ||
|
@@ -233,46 +274,60 @@ impl NetworkBehaviour for Behaviour { | |
&mut self, | ||
connection_id: ConnectionId, | ||
peer: PeerId, | ||
_: &Multiaddr, | ||
_: &Multiaddr, | ||
local_addr: &Multiaddr, | ||
remote_addr: &Multiaddr, | ||
) -> Result<THandler<Self>, ConnectionDenied> { | ||
self.pending_inbound_connections.remove(&connection_id); | ||
|
||
check_limit( | ||
self.limits.max_established_incoming, | ||
self.established_inbound_connections.len(), | ||
Kind::EstablishedIncoming, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_per_peer, | ||
self.established_per_peer | ||
.get(&peer) | ||
.map(|connections| connections.len()) | ||
.unwrap_or(0), | ||
Kind::EstablishedPerPeer, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_total, | ||
self.established_inbound_connections.len() | ||
+ self.established_outbound_connections.len(), | ||
Kind::EstablishedTotal, | ||
)?; | ||
|
||
if !(self.bypass_rules.is_addr_bypassed(local_addr) | ||
drHuangMHT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|| self.bypass_rules.is_addr_bypassed(remote_addr) | ||
|| self.bypass_rules.is_peer_bypassed(&peer)) | ||
{ | ||
check_limit( | ||
self.limits.max_established_incoming, | ||
self.established_inbound_connections.len(), | ||
Kind::EstablishedIncoming, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_per_peer, | ||
self.established_per_peer | ||
.get(&peer) | ||
.map(|connections| connections.len()) | ||
.unwrap_or(0), | ||
Kind::EstablishedPerPeer, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_total, | ||
self.established_inbound_connections.len() | ||
+ self.established_outbound_connections.len(), | ||
Kind::EstablishedTotal, | ||
)?; | ||
} | ||
Ok(dummy::ConnectionHandler) | ||
} | ||
|
||
fn handle_pending_outbound_connection( | ||
&mut self, | ||
connection_id: ConnectionId, | ||
_: Option<PeerId>, | ||
_: &[Multiaddr], | ||
maybe_peer: Option<PeerId>, | ||
addresses: &[Multiaddr], | ||
_: Endpoint, | ||
) -> Result<Vec<Multiaddr>, ConnectionDenied> { | ||
check_limit( | ||
self.limits.max_pending_outgoing, | ||
self.pending_outbound_connections.len(), | ||
Kind::PendingOutgoing, | ||
)?; | ||
let mut is_bypassed = false; | ||
if let Some(peer) = maybe_peer { | ||
is_bypassed = self.bypass_rules.is_peer_bypassed(&peer) | ||
} | ||
is_bypassed = is_bypassed | ||
|| addresses | ||
.iter() | ||
.any(|addr| self.bypass_rules.is_addr_bypassed(addr)); | ||
drHuangMHT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if !is_bypassed { | ||
check_limit( | ||
self.limits.max_pending_outgoing, | ||
self.pending_outbound_connections.len(), | ||
Kind::PendingOutgoing, | ||
)?; | ||
} | ||
|
||
self.pending_outbound_connections.insert(connection_id); | ||
|
||
|
@@ -283,31 +338,33 @@ impl NetworkBehaviour for Behaviour { | |
&mut self, | ||
connection_id: ConnectionId, | ||
peer: PeerId, | ||
_: &Multiaddr, | ||
addr: &Multiaddr, | ||
_: Endpoint, | ||
_: PortUse, | ||
) -> Result<THandler<Self>, ConnectionDenied> { | ||
self.pending_outbound_connections.remove(&connection_id); | ||
|
||
check_limit( | ||
self.limits.max_established_outgoing, | ||
self.established_outbound_connections.len(), | ||
Kind::EstablishedOutgoing, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_per_peer, | ||
self.established_per_peer | ||
.get(&peer) | ||
.map(|connections| connections.len()) | ||
.unwrap_or(0), | ||
Kind::EstablishedPerPeer, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_total, | ||
self.established_inbound_connections.len() | ||
+ self.established_outbound_connections.len(), | ||
Kind::EstablishedTotal, | ||
)?; | ||
if !(self.bypass_rules.is_peer_bypassed(&peer) || self.bypass_rules.is_addr_bypassed(addr)) | ||
{ | ||
check_limit( | ||
self.limits.max_established_outgoing, | ||
self.established_outbound_connections.len(), | ||
Kind::EstablishedOutgoing, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_per_peer, | ||
self.established_per_peer | ||
.get(&peer) | ||
.map(|connections| connections.len()) | ||
.unwrap_or(0), | ||
Kind::EstablishedPerPeer, | ||
)?; | ||
check_limit( | ||
self.limits.max_established_total, | ||
self.established_inbound_connections.len() | ||
+ self.established_outbound_connections.len(), | ||
Kind::EstablishedTotal, | ||
)?; | ||
} | ||
|
||
Ok(dummy::ConnectionHandler) | ||
} | ||
|
@@ -544,13 +601,13 @@ mod tests { | |
impl Behaviour { | ||
fn new(limits: ConnectionLimits) -> Self { | ||
Self { | ||
limits: super::Behaviour::new(limits), | ||
limits: super::Behaviour::new(limits, Default::default()), | ||
connection_denier: None.into(), | ||
} | ||
} | ||
fn new_with_connection_denier(limits: ConnectionLimits) -> Self { | ||
Self { | ||
limits: super::Behaviour::new(limits), | ||
limits: super::Behaviour::new(limits, Default::default()), | ||
connection_denier: Some(ConnectionDenier {}).into(), | ||
} | ||
} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should remove
bypass_rules
from here and have it be empty internally by default, or have a separate function to supply it when constructing the behaviour. This would then allow us to keep this as a patch release instead of bumping the minor version of the crate due to it being a breaking change. Thoughts?