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

Add IPv6 pinhole support using UPnP / NAT-PMP #17012

Open
Sjors opened this issue Oct 1, 2019 · 7 comments · May be fixed by #30043
Open

Add IPv6 pinhole support using UPnP / NAT-PMP #17012

Sjors opened this issue Oct 1, 2019 · 7 comments · May be fixed by #30043
Assignees

Comments

@Sjors
Copy link
Member

Sjors commented Oct 1, 2019

Our current UPnP support works for IPv4 by requesting a port forward. It doesn't work with IPv6 when the user is behind a firewall (which was the default for my modem at least).

More recent versions of libupnp support requesting a IPv6 pinhole.

I haven't checked if the same could be done with NAT-PMP (#11902). That may be preferable to updating libupnp.

UPnP is baked into the GUI release binaries, though it's turned off by default in the settings dialog.

@Sjors
Copy link
Member Author

Sjors commented Sep 6, 2022

Meanwhile NAT-PMP support is there, but it has no IPv6 pinhole support.

@laanwj laanwj self-assigned this Apr 11, 2024
@laanwj
Copy link
Member

laanwj commented Apr 25, 2024

Indeed, UPnP has an explicit command AddPinhole (https://upnp.org/specs/gw/UPnP-gw-WANIPv6FirewallControl-v1-Service.pdf) that's seperate from Add(Any)PortMapping.

The PCP (succssor to NAT-PMP) RFC (https://datatracker.ietf.org/doc/html/rfc6887) doesn't say anything about pinholes (although they do mention "Simple firewall control" but it's unclear to me how that maps to opcodes). Though i would guess the MAP opcode allows that, and more, it just leaves it up to the router how the external to internal mapping should be. Ideally.

It's kind of annoying if the client has to aware of the network state, ideally it doesn't really care how the port gets there, as long as it gets there.

@laanwj
Copy link
Member

laanwj commented Apr 25, 2024

FWIW the version of libUPnP in depends (2.2.2) does have the Pinhole commands:

upnpcommands.h:                         int * inboundPinholeAllowed);
upnpcommands.h:UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
upnpcommands.h:UPNP_AddPinhole(const char * controlURL, const char * servicetype,
upnpcommands.h:UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
upnpcommands.h:UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
upnpcommands.h:UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
upnpcommands.h:UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,

@laanwj
Copy link
Member

laanwj commented Apr 25, 2024

Manually creating an IPv6 pinhole seems to work here:

$ export ROUTER_IPV6_ADDR=...
$ export MY_IPV6_ADDR=...
$ upnpc -6 -u 'http://[${ROUTER_IPV6_ADDR}]:5000/rootDesc.xml' -A  "" 0 $MY_IPV6_ADDR 1234 tcp 30
upnpc : miniupnpc library test client, version 2.2.4.
 (c) 2005-2022 Thomas Bernard.
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
for more information.
Found valid IGD : http://[...]:5000/ctl/IPConn
Local LAN ip address : ...
AddPinhole: ([]:0 -> [...]:1234) / Pinhole ID = 13
$ nc -6l 1234

(but only when connecting to it using IPv6 with the correct globally routable address, when connecting to the IPv4 UPnP address, or a link-scoped IPv6 address, it gives failed with code 606 (Action not authorized). Seems like this problem: miniupnp/miniupnp#600 (comment))

Then connecting to it from another host, and sending some text, appears to work:

$ nc $MY_IPV6_ADDR 1234
.......

I'll try to integrate it into bitcoin. If i can find the right incantation.

@laanwj
Copy link
Member

laanwj commented Apr 28, 2024

The UPnP side of this is progressing, though i've ran into a possible bug/limitaton in miniupnp: miniupnp/miniupnp#731 (comment)

Meanwhile NAT-PMP support is there, but it has no IPv6 pinhole support.

i looked into this too, we'll actually have to implement PCP support ourselves to do this. The good part is that it's just a matter of sending one fixed-size binary UDP packet to the default gateway and parsing the result. Not worth taking on a dependency for.
(see https://github.com/moonlight-stream/GS-IPv6-Forwarder/blob/master/GSv6Fwd/pcp.cpp for an example implementation of IPv6 pinholing)
Can't begin to describe how much simpler than UPnP this is. No XML, no discovery, no HTTP. However, router support is probably less, so we still might want to support both.

@Sjors
Copy link
Member Author

Sjors commented Apr 29, 2024

The good part is that it's just a matter of sending one fixed-size binary UDP packet to the default gateway and parsing the result.

Nice!

@laanwj
Copy link
Member

laanwj commented Apr 29, 2024

Nice!

If you'd like to test, i have a branch here: https://github.com/laanwj/bitcoin/tree/2024-04-pcp-pinhole-test

It is a PoC that adds a ipv6-pinhole-test program that (on Linux only for now):

  • Enumerates local publicly routable IPv6 addresses
  • Gets the default gateway to get the PCP endpoint
  • Requests pinholes for 100 seconds to port 1234 on all addreses, and prints the result

i've tried it on two routers (Turris Omnia and Fritz!Box) and there it worked.

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

Successfully merging a pull request may close this issue.

3 participants