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

issue Sjors openend this issue on October 1, 2019
  1. Sjors commented at 5:00 pm on October 1, 2019: member

    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.

  2. Sjors added the label Feature on Oct 1, 2019
  3. fanquake added the label P2P on Oct 1, 2019
  4. Sjors commented at 1:00 pm on September 6, 2022: member
    Meanwhile NAT-PMP support is there, but it has no IPv6 pinhole support.
  5. laanwj assigned laanwj on Apr 11, 2024
  6. laanwj commented at 1:41 pm on April 25, 2024: member

    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.

  7. laanwj commented at 1:45 pm on April 25, 2024: member

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

    0upnpcommands.h:                         int * inboundPinholeAllowed);
    1upnpcommands.h:UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
    2upnpcommands.h:UPNP_AddPinhole(const char * controlURL, const char * servicetype,
    3upnpcommands.h:UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
    4upnpcommands.h:UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
    5upnpcommands.h:UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
    6upnpcommands.h:UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
    
  8. laanwj commented at 5:08 pm on April 25, 2024: member

    Manually creating an IPv6 pinhole seems to work here:

     0$ export ROUTER_IPV6_ADDR=...
     1$ export MY_IPV6_ADDR=...
     2$ upnpc -6 -u 'http://[${ROUTER_IPV6_ADDR}]:5000/rootDesc.xml' -A  "" 0 $MY_IPV6_ADDR 1234 tcp 30
     3upnpc : miniupnpc library test client, version 2.2.4.
     4 (c) 2005-2022 Thomas Bernard.
     5Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
     6for more information.
     7Found valid IGD : http://[...]:5000/ctl/IPConn
     8Local LAN ip address : ...
     9AddPinhole: ([]:0 -> [...]:1234) / Pinhole ID = 13
    10$ 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: https://github.com/miniupnp/miniupnp/issues/600#issuecomment-1133602620)

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

    0$ nc $MY_IPV6_ADDR 1234
    1.......
    

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

  9. laanwj commented at 10:21 am on April 28, 2024: member

    The UPnP side of this is progressing, though i’ve ran into a possible bug/limitaton in miniupnp: https://github.com/miniupnp/miniupnp/issues/731#issuecomment-2081257515

    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.

  10. Sjors commented at 8:36 am on April 29, 2024: member

    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!

  11. laanwj commented at 9:05 am on April 29, 2024: member

    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.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-03 10:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me