UPNP Inconsistent Gateway Discovery

Godot Version

v4.2.1.stable.arch_linux

Question

I am making a small project to test basic multiplayer features. As part of this I am using UPnP to make it easier for the user to forward ports to host a game.

Please keep in mind:

  • UPNP is enabled on my router.
  • I am able to forward a port manually and establish a connection outside my local network.

I am able to make a UPNP instance and calling discover() on it returns successfully. I then confirm I have a valid gateway by calling get_gateway().is_valid_gateway(). However, when I attempt to call add_port_mapping there is a FIFTY-FIFTY CHANCE it fails with error code: UPNP_RESULT_INVALID_ARGS. It does not matter what port I use, or whether I use “UDP” or “TCP” as my protocol.

What I have noticed so far is that there are two UPNPDevices being discovered. One of with an IPv4 address and the other with an IPv6 address. I suspect that both IPv4 and IPv6 addresses for my router are being discovered, please let me know if this is possible. When the IPv4 address is found first (hence being used as the device in get_gateway()) then the port mapping succeeds.

Another observation is that when the IPv4 device is found first, then the string in igd_our_addr for both devices (the IPv4 one and the IPv6) correctly contains the local IP address of my machine. However, when the IPv6 device is found first, then the string in igd_our_addr is an incomplete IPv6 address with “presentationURL” appended at the end (example: 111:222:333:4presentationURL) for both the IPv4 and the IPv6 devices. Along with this, the igd_control_url is using the IPv6 device address for both devices as well (example: http//[IPv6Address]:SomePort/ctl/IPConn).

I have a hacky fix for this issue by discarding the IPv6 device and fixing the igd_control_url with the IPv4 device’s address and igd_our_addr with the local network IP for my machine (obtained from IP.get_local_addresses()). However, doing this doesn’t seem very reliable.

Any ideas on why I am seeing these two IP addresses, and why the values of the first one discovered are partly shown in the second one as well? Any advice on how I could make my UPNP setup more robust?

Are you utilizing ipv6? Why not just disable it on the router? Or is this an issue local to the host machine?

I have tested my demo on a separate Windows machine on the same network and the issue still happens, so it is not an issue related to the host machine.

I would try disabling IPv6 on my router, but it is a crappy locked down ISP provided one with limited functionality. This means it doesn’t allow me to disable IPv6.

Anyway, even if disabling IPv6 on the router did work, I still don’t think that would be a good solution. It would be more of a hack really. Shutting down IPv6 just to get an application to work properly is way too intrusive of a solution as it could cause connectivity issues for other applications/services the user has.

1 Like

The documentation says that different outcomes are possible when adding and deleting ports. I think it suggests that routers will update or deny a port setting if it already exists, which can be handled differently depending on the router.

Maybe you could try first deleting a port and immediately attempt adding the same port. Basically to see if for some reason your app or router isn’t cleaning up properly on your app’s exit. If both fail then you should attempt another port.

Also there is a discover ipv6 property in the Godot UPnP class that is set to false by default. Although I don’t know what that really means.

I have an input field on the UI where I can pass any port I want. No matter what port I pass to be forwarded, the result of mapping remains UPNP_RESULT_INVALID_ARGS. Keep in mind, this is when I see the IPv6 gateway being discovered first. In the case where the IPv4 gateway is found first, then I can pretty much use any port I want (except ones I know are already reserved).

I changed the discover_ipv6 flag to true just to see what happens. It made it only discover the IPv6 device and completely ignore the IPv4 one. This made it so the issue always happens instead of half the time. Having functionality the opposite of this would solve my problem I guess lol. Maybe Godot has a bug where setting the IPv6 flag to false does not filter out IPv6 gateways but setting it to true does filter out IPv4?