From 583ad977b1f809cc6871e0f6fb4ff3df3599e388 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Sun, 20 Jul 2025 19:43:19 +0200 Subject: [PATCH 1/3] add_upnp_behaviour --- anchor/client/src/cli.rs | 8 ++++++++ anchor/client/src/config.rs | 3 +++ anchor/network/Cargo.toml | 1 + anchor/network/src/behaviour.rs | 15 ++++++++++++++- anchor/network/src/config.rs | 4 ++++ anchor/network/src/network.rs | 8 ++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/anchor/client/src/cli.rs b/anchor/client/src/cli.rs index fec7a719c..c9ff42f5c 100644 --- a/anchor/client/src/cli.rs +++ b/anchor/client/src/cli.rs @@ -281,6 +281,14 @@ pub struct Node { )] pub use_zero_ports: bool, + #[clap( + long, + help = "Disables UPnP support. Setting this will prevent Anchor \ + from attempting to automatically establish external port mappings.", + default_value = "false" + )] + pub disable_upnp: bool, + // Prometheus metrics HTTP server related arguments #[clap( long, diff --git a/anchor/client/src/config.rs b/anchor/client/src/config.rs index a909f389f..e3a69fe67 100644 --- a/anchor/client/src/config.rs +++ b/anchor/client/src/config.rs @@ -182,6 +182,9 @@ pub fn from_cli(cli_args: &Node, global_config: GlobalConfig) -> Result, pub handshake: handshake::Behaviour, } @@ -136,6 +142,12 @@ impl AnchorBehaviour { let handshake = handshake::create_behaviour(local_keypair); + let upnp = Toggle::from( + network_config + .upnp_enabled + .then(libp2p::upnp::tokio::Behaviour::default), + ); + Ok(AnchorBehaviour { identify, ping: ping::Behaviour::default(), @@ -143,6 +155,7 @@ impl AnchorBehaviour { discovery, peer_manager, handshake, + upnp, }) } } diff --git a/anchor/network/src/config.rs b/anchor/network/src/config.rs index c444e3f5f..3dbc55b28 100644 --- a/anchor/network/src/config.rs +++ b/anchor/network/src/config.rs @@ -72,6 +72,9 @@ pub struct Config { /// Target number of connected peers. pub target_peers: usize, + /// Attempt to construct external port mappings with UPnP. + pub upnp_enabled: bool, + pub domain_type: DomainType, } @@ -103,6 +106,7 @@ impl Config { disable_quic_support: false, subscribe_all_subnets: false, domain_type: DomainType::default(), + upnp_enabled: true, } } } diff --git a/anchor/network/src/network.rs b/anchor/network/src/network.rs index d7a060adc..61806922e 100644 --- a/anchor/network/src/network.rs +++ b/anchor/network/src/network.rs @@ -214,6 +214,14 @@ impl Network { self.handle_handshake_result(result); } } + AnchorBehaviourEvent::Upnp(upnp_event) => { + match upnp_event { + libp2p::upnp::Event::NewExternalAddr(_) => todo!(), + libp2p::upnp::Event::ExpiredExternalAddr(_) => todo!(), + libp2p::upnp::Event::GatewayNotFound => info!("UPnP not available."), + libp2p::upnp::Event::NonRoutableGateway => info!("UPnP is available but gateway is not exposed to public network"), + } + } AnchorBehaviourEvent::PeerManager(peer_manager::Event::Heartbeat(heartbeat)) => { if let Some(actions) = heartbeat.connect_actions { self.handle_connect_actions(actions); From 588f7153296e93fd18e8dc11d26c9c4272740888 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Fri, 8 Aug 2025 00:49:19 +0200 Subject: [PATCH 2/3] implement NewExternalAddr event handler --- anchor/network/src/network.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/anchor/network/src/network.rs b/anchor/network/src/network.rs index 61806922e..c1b5d5eab 100644 --- a/anchor/network/src/network.rs +++ b/anchor/network/src/network.rs @@ -216,8 +216,38 @@ impl Network { } AnchorBehaviourEvent::Upnp(upnp_event) => { match upnp_event { - libp2p::upnp::Event::NewExternalAddr(_) => todo!(), - libp2p::upnp::Event::ExpiredExternalAddr(_) => todo!(), + libp2p::upnp::Event::NewExternalAddr(addr) => { + info!(%addr, "UPnP route established"); + let mut iter = addr.iter(); + let is_ipv6 = { + let addr = iter.next(); + matches!(addr, Some(Protocol::Ip6(_))) + }; + match iter.next() { + Some(Protocol::Udp(udp_port)) => match iter.next() { + Some(Protocol::QuicV1) => { + if let Err(e) = + self.discovery().try_update_port(false, is_ipv6, udp_port) + { + warn!(error = e, "Failed to update ENR"); + } + } + _ => { + trace!(%addr, "UPnP address mapped multiaddr from unknown transport"); + } + }, + Some(Protocol::Tcp(tcp_port)) => { + if let Err(e) = self.discovery().try_update_port(true, is_ipv6, tcp_port) { + warn!(error = e, "Failed to update ENR"); + } + } + _ => { + trace!(%addr, "UPnP address mapped multiaddr from unknown transport"); + } + } + + }, + libp2p::upnp::Event::ExpiredExternalAddr(_) => {}, libp2p::upnp::Event::GatewayNotFound => info!("UPnP not available."), libp2p::upnp::Event::NonRoutableGateway => info!("UPnP is available but gateway is not exposed to public network"), } From 5d2cfea7011cb303f563a5accf7a99de81895d41 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 27 Oct 2025 15:36:52 +0100 Subject: [PATCH 3/3] refactoring upnp behaviour --- anchor/network/src/network.rs | 80 +++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/anchor/network/src/network.rs b/anchor/network/src/network.rs index c60bdae74..a0bf7d07c 100644 --- a/anchor/network/src/network.rs +++ b/anchor/network/src/network.rs @@ -18,6 +18,7 @@ use libp2p::{ identity::Keypair, multiaddr::Protocol, swarm::{SwarmEvent, dial_opts::DialOpts}, + upnp::Event, }; use message_receiver::{MessageReceiver, Outcome}; use prometheus_client::registry::Registry; @@ -236,42 +237,7 @@ impl Network { } } AnchorBehaviourEvent::Upnp(upnp_event) => { - match upnp_event { - libp2p::upnp::Event::NewExternalAddr(addr) => { - info!(%addr, "UPnP route established"); - let mut iter = addr.iter(); - let is_ipv6 = { - let addr = iter.next(); - matches!(addr, Some(Protocol::Ip6(_))) - }; - match iter.next() { - Some(Protocol::Udp(udp_port)) => match iter.next() { - Some(Protocol::QuicV1) => { - if let Err(e) = - self.discovery().try_update_port(false, is_ipv6, udp_port) - { - warn!(error = e, "Failed to update ENR"); - } - } - _ => { - trace!(%addr, "UPnP address mapped multiaddr from unknown transport"); - } - }, - Some(Protocol::Tcp(tcp_port)) => { - if let Err(e) = self.discovery().try_update_port(true, is_ipv6, tcp_port) { - warn!(error = e, "Failed to update ENR"); - } - } - _ => { - trace!(%addr, "UPnP address mapped multiaddr from unknown transport"); - } - } - - }, - libp2p::upnp::Event::ExpiredExternalAddr(_) => {}, - libp2p::upnp::Event::GatewayNotFound => info!("UPnP not available."), - libp2p::upnp::Event::NonRoutableGateway => info!("UPnP is available but gateway is not exposed to public network"), - } + self.on_upnp_event(upnp_event); } AnchorBehaviourEvent::PeerManager(peer_manager::Event::Heartbeat(heartbeat)) => { if let Some(actions) = heartbeat.connect_actions { @@ -580,6 +546,48 @@ impl Network { } } + fn on_upnp_event(&mut self, event: Event) { + match event { + libp2p::upnp::Event::NewExternalAddr(addr) => { + info!(%addr, "UPnP route established"); + let mut iter = addr.iter(); + let is_ipv6 = { + let addr = iter.next(); + matches!(addr, Some(Protocol::Ip6(_))) + }; + match iter.next() { + Some(Protocol::Udp(udp_port)) => match iter.next() { + Some(Protocol::QuicV1) => { + if let Err(e) = + self.discovery().try_update_port(false, is_ipv6, udp_port) + { + warn!(error = e, "Failed to update ENR"); + } + } + _ => { + trace!(%addr, "UPnP address mapped multiaddr from unknown transport"); + } + }, + Some(Protocol::Tcp(tcp_port)) => { + if let Err(e) = self.discovery().try_update_port(true, is_ipv6, tcp_port) { + warn!(error = e, "Failed to update ENR"); + } + } + _ => { + trace!(%addr, "UPnP address mapped multiaddr from unknown transport"); + } + } + } + libp2p::upnp::Event::ExpiredExternalAddr(addr) => { + info!(%addr, "UPnP route expired"); + } + libp2p::upnp::Event::GatewayNotFound => info!("UPnP not available."), + libp2p::upnp::Event::NonRoutableGateway => { + info!("UPnP is available but gateway is not exposed to public network") + } + } + } + fn peer_manager(&mut self) -> &mut PeerManager { &mut self.swarm.behaviour_mut().peer_manager }