Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automate port-forwarding e.g. via UPnP #3903

Closed
mxinden opened this issue May 10, 2023 · 8 comments · Fixed by #4156
Closed

feat: automate port-forwarding e.g. via UPnP #3903

mxinden opened this issue May 10, 2023 · 8 comments · Fixed by #4156
Assignees
Labels
difficulty:moderate getting-started Issues that can be tackled if you don't know the internals of libp2p very well help wanted

Comments

@mxinden
Copy link
Member

mxinden commented May 10, 2023

Description

Leverage protocols like UPnP to configure port-forwarding on ones router when behind NAT and/or firewall.

Motivation

Reachable from outside a private network.

Are you planning to do it yourself in a pull request?

No

@mxinden mxinden added difficulty:moderate help wanted getting-started Issues that can be tackled if you don't know the internals of libp2p very well labels May 10, 2023
@mxinden mxinden mentioned this issue May 10, 2023
14 tasks
@dariusc93
Copy link
Member

I guess the questions would be

  1. Do we want this setup as or apart of a Transport or a NetworkBehaviour?
  2. Which protocol would be preferable: IGD or NAT-PMP/PCP? I think in go-libp2p it uses both (would have to double check on that), and in my own small project that i plan on doing a small rewrite on, I am using both protocols with IGD used with PMP being the fallback
  3. Do we care on the actual implementation and if it relies on something written completely in rust or if the project utilizes FFI (eg natpmp utilizes ffi, although it works fine across many platforms ive tested)

Additional thoughts:

  • Current protocols out there may utilize hyper or some http client, so we may want to think about that if we wish to utilize anything like that (eg crates for igd uses such a client)
  • Possibly try to write it purely in rust with dealing with any protocols, assuming there isnt a reliable and up-to-date crate available
  • Once port forwarding can be done, attempt to discover the external address and add it, even possibly make it emit SwarmEvent::NewListenAddr and if something stops port forwarding (eg its unable to be renewed, network device changes, etc) we could possibly emit SwarmEvent::ExpiredListenAddr or SwarmEvent::ListenerClosed?

@thomaseizinger
Copy link
Contributor

Really good questions!

Do we want this setup as or apart of a Transport or a NetworkBehaviour?

From what I understand, Transport makes more sense because it will allow e.g. a tcp::Transport to attempt forwarding of a port the user has just told it to listen_on.

We might have some shared code between libp2p-tcp, libp2p-quic and libp2p-webrtc but that I'd like to keep hidden from their public APIs.

Which protocol would be preferable: IGD or NAT-PMP/PCP? I think in go-libp2p it uses both (would have to double check on that), and in my own small project that i plan on doing a small rewrite on, I am using both protocols with IGD used with PMP being the fallback

Use both if it doesn't result in too much complexity? Not sure if we need to make a call on this here.

Do we care on the actual implementation and if it relies on something written completely in rust or if the project utilizes FFI (eg natpmp utilizes ffi, although it works fine across many platforms ive tested)

My primary concern would be the dependency footprint given that this will be included for pretty much everyone because you need one of those transports for a libp2p deployment. Preferably pure Rust but I don't see FFI as a blocker.

Current protocols out there may utilize hyper or some http client, so we may want to think about that if we wish to utilize anything like that (eg crates for igd uses such a client)

It makes sense to not reinvent the wheel. Esp when it comes to sending HTTP messages. It needs to work across all executors so whatever crate we use shouldn't hardcode tokio or async-std somewhere.

Once port forwarding can be done, attempt to discover the external address and add it, even possibly make it emit SwarmEvent::NewListenAddr and if something stops port forwarding (eg its unable to be renewed, network device changes, etc) we could possibly emit SwarmEvent::ExpiredListenAddr or SwarmEvent::ListenerClosed?

Isn't that orthogonal? With protocols like AutoNAT, I'd expect that because of Upnp, we end up being publicly reachable and thus AutoNAT will report it as such. If the port forwarding is disabled somewhere, we will also learn that again through AutoNAT. I am not sure there needs to be a direct link in the sense of reporting an event that we stopped forwarding.

@geotro
Copy link

geotro commented May 11, 2023

In natpmp the only part implemented in C is the functionality of getting a user's default gateway. Looks like it would be trivial to convert this part to Rust.

@thomaseizinger
Copy link
Contributor

@jxs I assigned you to this as I believe you are currently working on this :)

@dariusc93
Copy link
Member

Sorry for the late response

Isn't that orthogonal? With protocols like AutoNAT, I'd expect that because of Upnp, we end up being publicly reachable and thus AutoNAT will report it as such. If the port forwarding is disabled somewhere, we will also learn that again through AutoNAT. I am not sure there needs to be a direct link in the sense of reporting an event that we stopped forwarding.

I think my original idea was to make swarm more aware of the external ip directly rather than depending solely on another protocol to notify swarm about it (though the use of it can be more reaffirming), especially in cases of where one may not be using something like AutoNAT, etc. However, I think making use of ToSwarm::NewExternalAddrCandidate, ToSwarm::ExternalAddrConfirmed, and FromSwarm::ExpiredListenAddr in some way might be better since it would notify swarm and the behaviour of the external address if we are able to open that port, if it fails later or for whatever reason disabled (if such a function is provided) on then it could be removed,

@thomaseizinger
Copy link
Contributor

Yes, I had a conversation with @jxs about this and we also concluded that likely the best way to implement this is using NetworkBehaviour and emit ToSwarm::ExternalAddrConfirmed and ToSwarm::ExternalAddrExpired events based on the success of the ITP interactions.

What happens if we are behind more than one NAT? Does ITP guarantee us that we are reachable via the public internet in case of a successful port opening? Or does ITP only care about one NAT device?

@mxinden
Copy link
Member Author

mxinden commented Jul 3, 2023

is using NetworkBehaviour and emit ToSwarm::ExternalAddrConfirmed and ToSwarm::ExternalAddrExpired events based on the success of the ITP interactions.

I would expect the libp2p-upnp NetworkBehaviour to emit ExternalAddrCandidate and not ExternalAddrConfirmed. The former would be picked up by libp2p-autonat which then emits the latter.

@thomaseizinger
Copy link
Contributor

thomaseizinger commented Jul 3, 2023

Yes, I had a conversation with @jxs about this and we also concluded that likely the best way to implement this is using NetworkBehaviour and emit ToSwarm::ExternalAddrConfirmed and ToSwarm::ExternalAddrExpired events based on the success of the ITP interactions.

What happens if we are behind more than one NAT? Does ITP guarantee us that we are reachable via the public internet in case of a successful port opening? Or does ITP only care about one NAT device?

Small correction, I meant IGD, not sure where ITP came from 🙃

is using NetworkBehaviour and emit ToSwarm::ExternalAddrConfirmed and ToSwarm::ExternalAddrExpired events based on the success of the ITP interactions.

I would expect the libp2p-upnp NetworkBehaviour to emit ExternalAddrCandidate and not ExternalAddrConfirmed. The former would be picked up by libp2p-autonat which then emits the latter.

In most deployments, a successful port-forwarding would result in a publicly reachable address and thus AutoNAT would not be needed in this case. It is more correct to rely on AutoNAT but as I stated in a previous discussion, it would be great if we wouldn't create such a strong dependency here. Like, it would be great if we have multiple solutions for external addresses instead of essentially forcing users into AutoNAT.

@mergify mergify bot closed this as completed in #4156 Sep 12, 2023
mergify bot pushed a commit that referenced this issue Sep 12, 2023
Implements UPnP via the IGD protocol. The usage of IGD is an implementation detail and is planned to be extended to support NATpnp.

Resolves: #3903.

Pull-Request: #4156.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty:moderate getting-started Issues that can be tackled if you don't know the internals of libp2p very well help wanted
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants