diff --git a/anchor/client/src/cli.rs b/anchor/client/src/cli.rs index 5ff060322..7fbe0aa0b 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 e11821052..3051b0586 100644 --- a/anchor/client/src/config.rs +++ b/anchor/client/src/config.rs @@ -183,6 +183,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 3fc2ce31b..428349570 100644 --- a/anchor/network/src/config.rs +++ b/anchor/network/src/config.rs @@ -77,6 +77,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, } @@ -109,6 +112,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..c1b5d5eab 100644 --- a/anchor/network/src/network.rs +++ b/anchor/network/src/network.rs @@ -214,6 +214,44 @@ impl Network { self.handle_handshake_result(result); } } + 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"), + } + } AnchorBehaviourEvent::PeerManager(peer_manager::Event::Heartbeat(heartbeat)) => { if let Some(actions) = heartbeat.connect_actions { self.handle_connect_actions(actions);