Skip to content

Conversation

petarjuki7
Copy link
Contributor

Issue Addressed

Addresses issue #407

Proposed Changes

Added libp2p::upnp functionality.
Changed the CLI arguments so the user can opt-out of upnp with --disable-upnp.
Listening to upnp events.

Additional Info

This PR currently doesn't do anything on upnp events regarding updating ENR ports.
Relates to #255.

@petarjuki7 petarjuki7 changed the title add_upnp_behaviour Implement support for UPnP Jul 20, 2025
@petarjuki7 petarjuki7 changed the title Implement support for UPnP feat(networking): Implement support for UPnP Jul 20, 2025
@petarjuki7 petarjuki7 marked this pull request as draft July 20, 2025 18:00
@petarjuki7
Copy link
Contributor Author

Still in draft because not sure how to respond to events.
cc: @dknopik

@dknopik
Copy link
Member

dknopik commented Jul 21, 2025

Nice start! Regarding the events, I think we have to do #255 first (now that I think about it again). After all, it makes no sense to get external ports via UPnP if we can not advertise them.

Let me know if you want to take a look at #255 as well (no pressure though!). Else, I can tackle it in the next few days and let you know when it's ready.

@petarjuki7
Copy link
Contributor Author

Let me know if you want to take a look at #255 as well (no pressure though!). Else, I can tackle it in the next few days and let you know when it's ready.

Hi, sorry for not responding sooner, had something urgent to take care off. Sure, I can take a look, I will write questions / ideas in that issue then.

@petarjuki7
Copy link
Contributor Author

Lighthouse also has this part:
https://github.com/sigp/lighthouse/blob/stable/beacon_node/network/src/nat.rs

Where they "manually" construct port mappings for addresses they know are listening to (provided from the config).
(https://github.com/sigp/lighthouse/blob/cfb1f7331064b758c6786e4e1dc15507af5ff5d1/beacon_node/network/src/service.rs#L229-#L244).

Not sure if we also need it or it will be caught by the "regular" libp2p behaviour

@petarjuki7 petarjuki7 marked this pull request as ready for review August 21, 2025 16:12
@diegomrsantos
Copy link
Contributor

@dknopik @petarjuki7 how about we continue this?

@dknopik
Copy link
Member

dknopik commented Sep 10, 2025

@diegomrsantos sure, feel free to continue on this already, I'll focus on testing the release for now

@diegomrsantos
Copy link
Contributor

@diegomrsantos sure, feel free to continue on this already, I'll focus on testing the release for now

I meant @petarjuki7 :)

@petarjuki7 petarjuki7 self-assigned this Sep 15, 2025
@petarjuki7 petarjuki7 force-pushed the implement_upnp_support branch from 5cb9b63 to 588f715 Compare September 15, 2025 13:33
@petarjuki7 petarjuki7 changed the base branch from unstable to release-v0.3.0 September 15, 2025 13:33
@dknopik
Copy link
Member

dknopik commented Sep 16, 2025

@petarjuki7 As this is not a fix and will not end up in the first mainnet release, unstable was in fact the adequate target branch

@petarjuki7 petarjuki7 force-pushed the implement_upnp_support branch from 588f715 to 93c84d7 Compare September 16, 2025 10:41
@petarjuki7 petarjuki7 changed the base branch from release-v0.3.0 to unstable September 16, 2025 10:42
@petarjuki7 petarjuki7 force-pushed the implement_upnp_support branch from 93c84d7 to 588f715 Compare September 16, 2025 10:49
@diegomrsantos
Copy link
Contributor

@petarjuki7 is this ready for review?

@petarjuki7
Copy link
Contributor Author

@petarjuki7 is this ready for review?

Sorry, hadn't seen this sooner, yes it is.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements UPnP support to automatically establish external port mappings for the Anchor network client. Users can opt-out of UPnP with the --disable-upnp CLI flag.

  • Added UPnP behavior to the network stack with event handling for port mapping
  • Introduced CLI option to disable UPnP functionality
  • Configured UPnP to update ENR ports when external addresses are mapped

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
anchor/network/src/network.rs Implements UPnP event handling and ENR port updates
anchor/network/src/config.rs Adds upnp_enabled configuration field
anchor/network/src/behaviour.rs Integrates UPnP behavior into the network behavior stack
anchor/network/Cargo.toml Adds upnp feature to libp2p dependency
anchor/client/src/config.rs Maps CLI disable_upnp flag to network configuration
anchor/client/src/cli.rs Adds --disable-upnp CLI argument

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

}

},
libp2p::upnp::Event::ExpiredExternalAddr(_) => {},
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ExpiredExternalAddr event is ignored but should likely update the ENR to remove the expired port mapping. Consider implementing proper cleanup logic to maintain accurate ENR records.

Suggested change
libp2p::upnp::Event::ExpiredExternalAddr(_) => {},
libp2p::upnp::Event::ExpiredExternalAddr(addr) => {
info!(%addr, "UPnP route expired");
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, 0) {
warn!(error = e, "Failed to remove UDP port from ENR");
}
}
_ => {
trace!(%addr, "UPnP expired multiaddr from unknown transport");
}
},
Some(Protocol::Tcp(_tcp_port)) => {
if let Err(e) = self.discovery().try_update_port(true, is_ipv6, 0) {
warn!(error = e, "Failed to remove TCP port from ENR");
}
}
_ => {
trace!(%addr, "UPnP expired multiaddr from unknown transport");
}
}
},

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(in reply to copilot)

This does not really make sense. There is no advantage of setting the port to 0 instead of just leaving it as is.

If we are still publicly reachable, discv5 will notify us of our new external address and if we are not, our ENR will not be propagated anyway.

Comment on lines +223 to +224
let addr = iter.next();
matches!(addr, Some(Protocol::Ip6(_)))
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name 'addr' is used for both the full multiaddr and the first protocol component, which is confusing. Consider renaming the inner variable to 'first_protocol' or similar for clarity.

Suggested change
let addr = iter.next();
matches!(addr, Some(Protocol::Ip6(_)))
let first_protocol = iter.next();
matches!(first_protocol, Some(Protocol::Ip6(_)))

Copilot uses AI. Check for mistakes.

Comment on lines +218 to +253
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"),
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this into it's own function?

}

},
libp2p::upnp::Event::ExpiredExternalAddr(_) => {},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(in reply to copilot)

This does not really make sense. There is no advantage of setting the port to 0 instead of just leaving it as is.

If we are still publicly reachable, discv5 will notify us of our new external address and if we are not, our ENR will not be propagated anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants