Skip to content

Commit a614f1a

Browse files
AgeManningthomaseizinger
authored andcommitted
fix(connection-limits): correct connection tracking
The connection limit behaviour was not taking into account connection errors. Also, it was using the behaviour events to indicate established connections which is not always going to be the case because other behaviours can deny the connections (thanks @divagant-martian). Closes #4249 Pull-Request: #4250.
1 parent ed47e93 commit a614f1a

File tree

5 files changed

+155
-21
lines changed

5 files changed

+155
-21
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ rust-version = "1.65.0"
6565
[workspace.dependencies]
6666
libp2p-allow-block-list = { version = "0.2.0", path = "misc/allow-block-list" }
6767
libp2p-autonat = { version = "0.11.0", path = "protocols/autonat" }
68-
libp2p-connection-limits = { version = "0.2.0", path = "misc/connection-limits" }
68+
libp2p-connection-limits = { version = "0.2.1", path = "misc/connection-limits" }
6969
libp2p-core = { version = "0.40.0", path = "core" }
7070
libp2p-dcutr = { version = "0.10.0", path = "protocols/dcutr" }
7171
libp2p-deflate = { version = "0.40.0", path = "transports/deflate" }

misc/connection-limits/CHANGELOG.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
## 0.2.0
1+
## 0.2.1
2+
3+
- Do not count a connection as established when it is denied by another sibling `NetworkBehaviour`.
4+
In other words, do not increase established connection counter in `handle_established_outbound_connection` or `handle_established_inbound_connection`, but in `FromSwarm::ConnectionEstablished` instead.
5+
6+
See [PR 4250].
7+
8+
- Decrease `pending_inbound_connections` on `FromSwarm::ListenFailure` and `pending_outbound_connections` on `FromSwarm::DialFailure`.
9+
10+
See [PR 4250].
11+
12+
[PR 4250]: https://github.com/libp2p/rust-libp2p/pull/4250
13+
14+
## 0.2.0
15+
216

317
- Raise MSRV to 1.65.
418
See [PR 3715].

misc/connection-limits/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "libp2p-connection-limits"
33
edition = "2021"
44
rust-version = { workspace = true }
55
description = "Connection limits for libp2p."
6-
version = "0.2.0"
6+
version = "0.2.1"
77
license = "MIT"
88
repository = "https://github.com/libp2p/rust-libp2p"
99
keywords = ["peer-to-peer", "libp2p", "networking"]

misc/connection-limits/src/lib.rs

+137-17
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1919
// DEALINGS IN THE SOFTWARE.
2020

21-
use libp2p_core::{Endpoint, Multiaddr};
21+
use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr};
2222
use libp2p_identity::PeerId;
2323
use libp2p_swarm::{
24+
behaviour::{ConnectionEstablished, DialFailure, ListenFailure},
2425
dummy, ConnectionClosed, ConnectionDenied, ConnectionId, FromSwarm, NetworkBehaviour,
2526
PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm,
2627
};
@@ -249,12 +250,6 @@ impl NetworkBehaviour for Behaviour {
249250
Kind::EstablishedTotal,
250251
)?;
251252

252-
self.established_inbound_connections.insert(connection_id);
253-
self.established_per_peer
254-
.entry(peer)
255-
.or_default()
256-
.insert(connection_id);
257-
258253
Ok(dummy::ConnectionHandler)
259254
}
260255

@@ -305,12 +300,6 @@ impl NetworkBehaviour for Behaviour {
305300
Kind::EstablishedTotal,
306301
)?;
307302

308-
self.established_outbound_connections.insert(connection_id);
309-
self.established_per_peer
310-
.entry(peer)
311-
.or_default()
312-
.insert(connection_id);
313-
314303
Ok(dummy::ConnectionHandler)
315304
}
316305

@@ -328,10 +317,33 @@ impl NetworkBehaviour for Behaviour {
328317
.or_default()
329318
.remove(&connection_id);
330319
}
331-
FromSwarm::ConnectionEstablished(_) => {}
320+
FromSwarm::ConnectionEstablished(ConnectionEstablished {
321+
peer_id,
322+
endpoint,
323+
connection_id,
324+
..
325+
}) => {
326+
match endpoint {
327+
ConnectedPoint::Listener { .. } => {
328+
self.established_inbound_connections.insert(connection_id);
329+
}
330+
ConnectedPoint::Dialer { .. } => {
331+
self.established_outbound_connections.insert(connection_id);
332+
}
333+
}
334+
335+
self.established_per_peer
336+
.entry(peer_id)
337+
.or_default()
338+
.insert(connection_id);
339+
}
340+
FromSwarm::DialFailure(DialFailure { connection_id, .. }) => {
341+
self.pending_outbound_connections.remove(&connection_id);
342+
}
332343
FromSwarm::AddressChange(_) => {}
333-
FromSwarm::DialFailure(_) => {}
334-
FromSwarm::ListenFailure(_) => {}
344+
FromSwarm::ListenFailure(ListenFailure { connection_id, .. }) => {
345+
self.pending_inbound_connections.remove(&connection_id);
346+
}
335347
FromSwarm::NewListener(_) => {}
336348
FromSwarm::NewListenAddr(_) => {}
337349
FromSwarm::ExpiredListenAddr(_) => {}
@@ -364,7 +376,9 @@ impl NetworkBehaviour for Behaviour {
364376
#[cfg(test)]
365377
mod tests {
366378
use super::*;
367-
use libp2p_swarm::{dial_opts::DialOpts, DialError, ListenError, Swarm, SwarmEvent};
379+
use libp2p_swarm::{
380+
behaviour::toggle::Toggle, dial_opts::DialOpts, DialError, ListenError, Swarm, SwarmEvent,
381+
};
368382
use libp2p_swarm_test::SwarmExt;
369383
use quickcheck::*;
370384

@@ -466,19 +480,125 @@ mod tests {
466480
quickcheck(prop as fn(_));
467481
}
468482

483+
/// Another sibling [`NetworkBehaviour`] implementation might deny established connections in
484+
/// [`handle_established_outbound_connection`] or [`handle_established_inbound_connection`].
485+
/// [`Behaviour`] must not increase the established counters in
486+
/// [`handle_established_outbound_connection`] or [`handle_established_inbound_connection`], but
487+
/// in [`SwarmEvent::ConnectionEstablished`] as the connection might still be denied by a
488+
/// sibling [`NetworkBehaviour`] in the former case. Only in the latter case
489+
/// ([`SwarmEvent::ConnectionEstablished`]) can the connection be seen as established.
490+
#[test]
491+
fn support_other_behaviour_denying_connection() {
492+
let mut swarm1 = Swarm::new_ephemeral(|_| {
493+
Behaviour::new_with_connection_denier(ConnectionLimits::default())
494+
});
495+
let mut swarm2 = Swarm::new_ephemeral(|_| Behaviour::new(ConnectionLimits::default()));
496+
497+
async_std::task::block_on(async {
498+
// Have swarm2 dial swarm1.
499+
let (listen_addr, _) = swarm1.listen().await;
500+
swarm2.dial(listen_addr).unwrap();
501+
async_std::task::spawn(swarm2.loop_on_next());
502+
503+
// Wait for the ConnectionDenier of swarm1 to deny the established connection.
504+
let cause = swarm1
505+
.wait(|event| match event {
506+
SwarmEvent::IncomingConnectionError {
507+
error: ListenError::Denied { cause },
508+
..
509+
} => Some(cause),
510+
_ => None,
511+
})
512+
.await;
513+
514+
cause.downcast::<std::io::Error>().unwrap();
515+
516+
assert_eq!(
517+
0,
518+
swarm1
519+
.behaviour_mut()
520+
.limits
521+
.established_inbound_connections
522+
.len(),
523+
"swarm1 connection limit behaviour to not count denied established connection as established connection"
524+
)
525+
});
526+
}
527+
469528
#[derive(libp2p_swarm_derive::NetworkBehaviour)]
470529
#[behaviour(prelude = "libp2p_swarm::derive_prelude")]
471530
struct Behaviour {
472531
limits: super::Behaviour,
473532
keep_alive: libp2p_swarm::keep_alive::Behaviour,
533+
connection_denier: Toggle<ConnectionDenier>,
474534
}
475535

476536
impl Behaviour {
477537
fn new(limits: ConnectionLimits) -> Self {
478538
Self {
479539
limits: super::Behaviour::new(limits),
480540
keep_alive: libp2p_swarm::keep_alive::Behaviour,
541+
connection_denier: None.into(),
481542
}
482543
}
544+
fn new_with_connection_denier(limits: ConnectionLimits) -> Self {
545+
Self {
546+
limits: super::Behaviour::new(limits),
547+
keep_alive: libp2p_swarm::keep_alive::Behaviour,
548+
connection_denier: Some(ConnectionDenier {}).into(),
549+
}
550+
}
551+
}
552+
553+
struct ConnectionDenier {}
554+
555+
impl NetworkBehaviour for ConnectionDenier {
556+
type ConnectionHandler = dummy::ConnectionHandler;
557+
type ToSwarm = Void;
558+
559+
fn handle_established_inbound_connection(
560+
&mut self,
561+
_connection_id: ConnectionId,
562+
_peer: PeerId,
563+
_local_addr: &Multiaddr,
564+
_remote_addr: &Multiaddr,
565+
) -> Result<THandler<Self>, ConnectionDenied> {
566+
Err(ConnectionDenied::new(std::io::Error::new(
567+
std::io::ErrorKind::Other,
568+
"ConnectionDenier",
569+
)))
570+
}
571+
572+
fn handle_established_outbound_connection(
573+
&mut self,
574+
_connection_id: ConnectionId,
575+
_peer: PeerId,
576+
_addr: &Multiaddr,
577+
_role_override: Endpoint,
578+
) -> Result<THandler<Self>, ConnectionDenied> {
579+
Err(ConnectionDenied::new(std::io::Error::new(
580+
std::io::ErrorKind::Other,
581+
"ConnectionDenier",
582+
)))
583+
}
584+
585+
fn on_swarm_event(&mut self, _event: FromSwarm<Self::ConnectionHandler>) {}
586+
587+
fn on_connection_handler_event(
588+
&mut self,
589+
_peer_id: PeerId,
590+
_connection_id: ConnectionId,
591+
event: THandlerOutEvent<Self>,
592+
) {
593+
void::unreachable(event)
594+
}
595+
596+
fn poll(
597+
&mut self,
598+
_cx: &mut Context<'_>,
599+
_params: &mut impl PollParameters,
600+
) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
601+
Poll::Pending
602+
}
483603
}
484604
}

0 commit comments

Comments
 (0)