Passive peers: configured, and left to connect

A session could only be configured for a peer we would then dial, every
few seconds to two minutes, for as long as it was not up. A new peer
whose side was not ready yet filled the log with failed attempts, and a
peer that opens its sessions itself was dialled for nothing.

A peer with `passive: true` is not dialled. A connection from its address
is taken as for any configured peer, and the session comes up with all
that is configured for it: role, import policy, export limits,
hold-downs. Until then the status page and /api/peers show it as
`Passive`, waiting for the peer to connect; after a session, with how
that session ended, which nothing else would say since nobody dials it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Rayhaan JaufeerallyandClaude Opus 5.5 committed 2026-10-06 00:05:54 +02:00
1 parent 910dd258fb
commit c9751883c4
22 files changed
+178 -8

No files matched your search

@@ -43,6 +43,7 @@ async fn server_offering(
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "route-server-client".into(),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1).into(),
@@ -33,6 +33,7 @@ fn peer_at(endpoint: SocketAddr) -> PeerConfig {
require_role: false,
export: Default::default(),
export_limits: None,
passive: false,
}
}
+1
View File
@@ -60,6 +60,7 @@ async fn server_with(
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -39,6 +39,7 @@ async fn server_dialing(listener: &TcpListener) -> Result<TestServer> {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -170,6 +170,7 @@ async fn test_refused_routes_can_be_seen_and_counted() -> Result<()> {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "refused-demo".into(),
endpoint: endpoint.into(),
+69
View File
@@ -23,13 +23,25 @@ const TIMEOUT: Duration = Duration::from_secs(5);
/// Starts a server configured to dial `listener`'s address as peer "r1".
async fn server_dialing(listener: &TcpListener, remote_asn: u32) -> Result<TestServer> {
server_with_peer(listener, remote_asn, false).await
}
/// Starts a server with peer "r1" at `listener`'s address, which it dials
/// unless the peer is `passive`.
async fn server_with_peer(
listener: &TcpListener,
remote_asn: u32,
passive: bool,
) -> Result<TestServer> {
let endpoint: SocketAddr = listener.local_addr()?;
TestServer::start_with(LOCAL_ASN, |c| {
c.connect_retry = Duration::from_millis(200);
c.accept_unconfigured_peers = !passive;
c.peers = vec![PeerConfig {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -192,6 +204,7 @@ async fn test_inbound_from_configured_address_uses_config() -> Result<()> {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1).into(),
@@ -248,6 +261,7 @@ async fn test_local_address_is_used_as_the_source() -> Result<()> {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -273,6 +287,7 @@ async fn test_local_address_is_used_as_the_source() -> Result<()> {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -350,3 +365,57 @@ async fn test_a_peer_that_refuses_the_session_is_dialled_ever_more_slowly() -> R
);
Ok(())
}
/// A passive peer is not dialled: its session comes up when it connects,
/// and until then, and after, it is shown as waiting, with how its last
/// session ended.
#[tokio::test]
async fn test_a_passive_peer_is_not_dialled_and_comes_up_when_it_connects() -> Result<()> {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await?;
let server = server_with_peer(&listener, PEER_ASN, true).await?;
let state = || server.registry.configured_peers()[0].1.clone();
// Many retry intervals pass, and nothing connects.
assert!(
tokio::time::timeout(Duration::from_secs(1), listener.accept())
.await
.is_err(),
"a passive peer was dialled"
);
assert_eq!(state(), DialState::Passive { last_error: None });
let mut peer = TestPeer::connect(server.addr).await?;
peer.handshake(PEER_ASN, Ipv4Addr::new(10, 0, 0, 2), 90)
.await?;
let listed = tokio::time::timeout(TIMEOUT, async {
loop {
if let Some(session) = server.registry.list().first().cloned() {
return session;
}
tokio::time::sleep(Duration::from_millis(5)).await;
}
})
.await?;
assert_eq!(listed.name.as_deref(), Some("r1"));
drop(peer);
tokio::time::timeout(TIMEOUT, async {
while !matches!(
state(),
DialState::Passive {
last_error: Some(_)
}
) {
tokio::time::sleep(Duration::from_millis(5)).await;
}
})
.await?;
assert!(server.registry.list().is_empty());
assert!(
tokio::time::timeout(Duration::from_millis(600), listener.accept())
.await
.is_err(),
"a passive peer was dialled after its session ended"
);
Ok(())
}
@@ -54,6 +54,7 @@ async fn server_with(originated: &[&str], limits: ExportLimits) -> Result<Setup>
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: Some(Arc::new(limits)),
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -45,6 +45,7 @@ async fn server_with(import: ImportPolicy) -> Result<(TestServer, TcpListener)>
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
@@ -186,6 +186,7 @@ async fn test_no_family_in_common_refuses_the_session() -> Result<()> {
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "v6-only".into(),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1).into(),
+1
View File
@@ -51,6 +51,7 @@ async fn server_with(role: Option<PeerRole>, require_role: bool) -> Result<Setup
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),
+1
View File
@@ -70,6 +70,7 @@ async fn server_with(
graceful_restart: Default::default(),
groups: Vec::new(),
export_limits: None,
passive: false,
reflector_client: false,
name: "r1".into(),
endpoint: endpoint.into(),