From 02ccf69dd6b772423acb343d16ef2bdbb3e3da03 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Thu, 6 Feb 2020 19:00:26 +0200 Subject: [PATCH 01/13] refactor: Move port mapping code to its own module This commit does not change behavior. --- src/Makefile.am | 2 + src/init.cpp | 1 + src/mapport.cpp | 137 ++++++++++++++++++++++++++++++++++++++++ src/mapport.h | 19 ++++++ src/net.cpp | 124 ------------------------------------ src/net.h | 9 --- src/node/interfaces.cpp | 1 + src/qt/optionsmodel.cpp | 5 +- 8 files changed, 163 insertions(+), 135 deletions(-) create mode 100644 src/mapport.cpp create mode 100644 src/mapport.h diff --git a/src/Makefile.am b/src/Makefile.am index 23d790d552..ec697515d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,6 +152,7 @@ BITCOIN_CORE_H = \ key_io.h \ logging.h \ logging/timer.h \ + mapport.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -299,6 +300,7 @@ libbitcoin_server_a_SOURCES = \ index/blockfilterindex.cpp \ index/txindex.cpp \ init.cpp \ + mapport.cpp \ miner.cpp \ net.cpp \ net_processing.cpp \ diff --git a/src/init.cpp b/src/init.cpp index 42e9925f98..5c24490215 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mapport.cpp b/src/mapport.cpp new file mode 100644 index 0000000000..a45d4d9060 --- /dev/null +++ b/src/mapport.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2011-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_UPNP +#include +#include +#include +// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility +// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. +static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); +#endif + +#include +#include +#include +#include +#include + +#ifdef USE_UPNP +static CThreadInterrupt g_upnp_interrupt; +static std::thread g_upnp_thread; +static void ThreadMapPort() +{ + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = nullptr; + const char * minissdpdpath = nullptr; + struct UPNPDev * devlist = nullptr; + char lanaddr[64]; + + int error = 0; +#if MINIUPNPC_API_VERSION < 14 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#else + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if (r != UPNPCOMMAND_SUCCESS) { + LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); + } else { + if (externalIPAddress[0]) { + CNetAddr resolved; + if (LookupHost(externalIPAddress, resolved, false)) { + LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); + AddLocal(resolved, LOCAL_UPNP); + } + } else { + LogPrintf("UPnP: GetExternalIPAddress failed.\n"); + } + } + } + + std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); + + do { + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + + if (r != UPNPCOMMAND_SUCCESS) { + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); + } else { + LogPrintf("UPnP Port Mapping successful.\n"); + } + } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); + + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); + freeUPNPDevlist(devlist); devlist = nullptr; + FreeUPNPUrls(&urls); + } else { + LogPrintf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = nullptr; + if (r != 0) + FreeUPNPUrls(&urls); + } +} + +void StartMapPort() +{ + if (!g_upnp_thread.joinable()) { + assert(!g_upnp_interrupt); + g_upnp_thread = std::thread((std::bind(&TraceThread, "upnp", &ThreadMapPort))); + } +} + +void InterruptMapPort() +{ + if(g_upnp_thread.joinable()) { + g_upnp_interrupt(); + } +} + +void StopMapPort() +{ + if(g_upnp_thread.joinable()) { + g_upnp_thread.join(); + g_upnp_interrupt.reset(); + } +} + +#else +void StartMapPort() +{ + // Intentionally left blank. +} +void InterruptMapPort() +{ + // Intentionally left blank. +} +void StopMapPort() +{ + // Intentionally left blank. +} +#endif diff --git a/src/mapport.h b/src/mapport.h new file mode 100644 index 0000000000..25ff2fd1eb --- /dev/null +++ b/src/mapport.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_MAPPORT_H +#define BITCOIN_MAPPORT_H + +/** -upnp default */ +#ifdef USE_UPNP +static const bool DEFAULT_UPNP = USE_UPNP; +#else +static const bool DEFAULT_UPNP = false; +#endif + +void StartMapPort(); +void InterruptMapPort(); +void StopMapPort(); + +#endif // BITCOIN_MAPPORT_H diff --git a/src/net.cpp b/src/net.cpp index ad4af4abb2..6e5a271945 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -33,15 +33,6 @@ #include #endif -#ifdef USE_UPNP -#include -#include -#include -// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility -// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. -static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); -#endif - #include #include #include @@ -1539,121 +1530,6 @@ void CConnman::WakeMessageHandler() condMsgProc.notify_one(); } - - - - - -#ifdef USE_UPNP -static CThreadInterrupt g_upnp_interrupt; -static std::thread g_upnp_thread; -static void ThreadMapPort() -{ - std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = nullptr; - const char * minissdpdpath = nullptr; - struct UPNPDev * devlist = nullptr; - char lanaddr[64]; - - int error = 0; -#if MINIUPNPC_API_VERSION < 14 - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); -#else - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); -#endif - - struct UPNPUrls urls; - struct IGDdatas data; - int r; - - r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - if (r == 1) - { - if (fDiscover) { - char externalIPAddress[40]; - r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); - } else { - if (externalIPAddress[0]) { - CNetAddr resolved; - if (LookupHost(externalIPAddress, resolved, false)) { - LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); - AddLocal(resolved, LOCAL_UPNP); - } - } else { - LogPrintf("UPnP: GetExternalIPAddress failed.\n"); - } - } - } - - std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); - - do { - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); - - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); - } else { - LogPrintf("UPnP Port Mapping successful.\n"); - } - } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); - - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = nullptr; - FreeUPNPUrls(&urls); - } else { - LogPrintf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = nullptr; - if (r != 0) - FreeUPNPUrls(&urls); - } -} - -void StartMapPort() -{ - if (!g_upnp_thread.joinable()) { - assert(!g_upnp_interrupt); - g_upnp_thread = std::thread((std::bind(&TraceThread, "upnp", &ThreadMapPort))); - } -} - -void InterruptMapPort() -{ - if(g_upnp_thread.joinable()) { - g_upnp_interrupt(); - } -} - -void StopMapPort() -{ - if(g_upnp_thread.joinable()) { - g_upnp_thread.join(); - g_upnp_interrupt.reset(); - } -} - -#else -void StartMapPort() -{ - // Intentionally left blank. -} -void InterruptMapPort() -{ - // Intentionally left blank. -} -void StopMapPort() -{ - // Intentionally left blank. -} -#endif - - - - - - void CConnman::ThreadDNSAddressSeed() { FastRandomContext rng; diff --git a/src/net.h b/src/net.h index acc1da49a5..6827c6897f 100644 --- a/src/net.h +++ b/src/net.h @@ -67,12 +67,6 @@ static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2; static const int MAX_FEELER_CONNECTIONS = 1; /** -listen default */ static const bool DEFAULT_LISTEN = true; -/** -upnp default */ -#ifdef USE_UPNP -static const bool DEFAULT_UPNP = USE_UPNP; -#else -static const bool DEFAULT_UPNP = false; -#endif /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ @@ -181,9 +175,6 @@ enum class ConnectionType { }; void Discover(); -void StartMapPort(); -void InterruptMapPort(); -void StopMapPort(); uint16_t GetListenPort(); enum diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 317a5c7cbe..ff6c9bf6b3 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 152de6decb..c1b8a06be8 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -13,11 +13,12 @@ #include #include -#include // For DEFAULT_SCRIPTCHECK_THREADS +#include #include #include -#include // for -dbcache defaults +#include // for -dbcache defaults #include +#include // For DEFAULT_SCRIPTCHECK_THREADS #include #include From 28e2961fd6a2a9101fc08fb748430989291aaf7e Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 16 Feb 2020 16:37:46 +0200 Subject: [PATCH 02/13] refactor: Replace magic number with named constant --- src/mapport.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mapport.cpp b/src/mapport.cpp index a45d4d9060..1f24b75708 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -34,6 +34,10 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed" #ifdef USE_UPNP static CThreadInterrupt g_upnp_interrupt; static std::thread g_upnp_thread; + +using namespace std::chrono_literals; +static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; + static void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); @@ -84,7 +88,7 @@ static void ThreadMapPort() } else { LogPrintf("UPnP Port Mapping successful.\n"); } - } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); + } while (g_upnp_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); From 8b50d1b5bb29b7d1ea0245ba75a8df3144e312dc Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Tue, 18 Feb 2020 23:23:30 +0200 Subject: [PATCH 03/13] net: Keep trying to use UPnP when -upnp=1 --- src/mapport.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/mapport.cpp b/src/mapport.cpp index 1f24b75708..ced2b773f3 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -37,9 +37,11 @@ static std::thread g_upnp_thread; using namespace std::chrono_literals; static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; +static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min}; -static void ThreadMapPort() +static bool ProcessUpnp() { + bool ret = false; std::string port = strprintf("%u", GetListenPort()); const char * multicastif = nullptr; const char * minissdpdpath = nullptr; @@ -84,11 +86,15 @@ static void ThreadMapPort() r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); if (r != UPNPCOMMAND_SUCCESS) { + ret = false; LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); + break; } else { + ret = true; LogPrintf("UPnP Port Mapping successful.\n"); } } while (g_upnp_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_upnp_interrupt.reset(); r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); @@ -100,6 +106,15 @@ static void ThreadMapPort() if (r != 0) FreeUPNPUrls(&urls); } + + return ret; +} + +static void ThreadMapPort() +{ + do { + if (ProcessUpnp()) return; + } while (g_upnp_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); } void StartMapPort() From 4e91b1e24d96e0cdccdd2a3ed034413f3ba6bae6 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 00:10:48 +0200 Subject: [PATCH 04/13] net: Add flags for port mapping protocols --- src/init.cpp | 4 +--- src/mapport.cpp | 31 +++++++++++++++++++++++++++++-- src/mapport.h | 7 ++++++- src/node/interfaces.cpp | 10 +--------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 5c24490215..8a260a3df2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1900,9 +1900,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA Discover(); // Map ports with UPnP - if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) { - StartMapPort(); - } + StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/mapport.cpp b/src/mapport.cpp index ced2b773f3..5091e9f44d 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -25,6 +25,7 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); #endif +#include #include #include #include @@ -34,6 +35,7 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed" #ifdef USE_UPNP static CThreadInterrupt g_upnp_interrupt; static std::thread g_upnp_thread; +static std::atomic_uint g_mapport_target_proto{MapPortProtoFlag::NONE}; using namespace std::chrono_literals; static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; @@ -117,7 +119,7 @@ static void ThreadMapPort() } while (g_upnp_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); } -void StartMapPort() +void StartThreadMapPort() { if (!g_upnp_thread.joinable()) { assert(!g_upnp_interrupt); @@ -125,6 +127,31 @@ void StartMapPort() } } +static void DispatchMapPort() +{ + if (g_mapport_target_proto == MapPortProtoFlag::UPNP) { + StartThreadMapPort(); + } else { + InterruptMapPort(); + StopMapPort(); + } +} + +static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) +{ + if (enabled) { + g_mapport_target_proto |= proto; + } else { + g_mapport_target_proto &= ~proto; + } +} + +void StartMapPort(bool use_upnp) +{ + MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp); + DispatchMapPort(); +} + void InterruptMapPort() { if(g_upnp_thread.joinable()) { @@ -141,7 +168,7 @@ void StopMapPort() } #else -void StartMapPort() +void StartMapPort(bool use_upnp) { // Intentionally left blank. } diff --git a/src/mapport.h b/src/mapport.h index 25ff2fd1eb..6e33b6ef74 100644 --- a/src/mapport.h +++ b/src/mapport.h @@ -12,7 +12,12 @@ static const bool DEFAULT_UPNP = USE_UPNP; static const bool DEFAULT_UPNP = false; #endif -void StartMapPort(); +enum MapPortProtoFlag : unsigned int { + NONE = 0x00, + UPNP = 0x01, +}; + +void StartMapPort(bool use_upnp); void InterruptMapPort(); void StopMapPort(); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index ff6c9bf6b3..6b84cb0f05 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -94,15 +94,7 @@ public: } } bool shutdownRequested() override { return ShutdownRequested(); } - void mapPort(bool use_upnp) override - { - if (use_upnp) { - StartMapPort(); - } else { - InterruptMapPort(); - StopMapPort(); - } - } + void mapPort(bool use_upnp) override { StartMapPort(use_upnp); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { From cf151cc68c95a8943e43e3fa4061e176262779e7 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 00:57:47 +0200 Subject: [PATCH 05/13] scripted-diff: Rename UPnP stuff -BEGIN VERIFY SCRIPT- sed -i 's/g_upnp_interrupt/g_mapport_interrupt/' src/mapport.cpp sed -i 's/if(g_upnp_thread/if (g_mapport_thread/' src/mapport.cpp sed -i 's/g_upnp_thread/g_mapport_thread/' src/mapport.cpp sed -i 's/LOCAL_UPNP/LOCAL_MAPPED/' src/mapport.cpp sed -i 's/\bupnp\b/mapport/' src/mapport.cpp sed -i 's/LOCAL_UPNP, /LOCAL_MAPPED,/' src/net.h -END VERIFY SCRIPT- --- src/mapport.cpp | 28 ++++++++++++++-------------- src/net.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/mapport.cpp b/src/mapport.cpp index 5091e9f44d..5f0aeb5e40 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -33,8 +33,8 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed" #include #ifdef USE_UPNP -static CThreadInterrupt g_upnp_interrupt; -static std::thread g_upnp_thread; +static CThreadInterrupt g_mapport_interrupt; +static std::thread g_mapport_thread; static std::atomic_uint g_mapport_target_proto{MapPortProtoFlag::NONE}; using namespace std::chrono_literals; @@ -74,7 +74,7 @@ static bool ProcessUpnp() CNetAddr resolved; if (LookupHost(externalIPAddress, resolved, false)) { LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); - AddLocal(resolved, LOCAL_UPNP); + AddLocal(resolved, LOCAL_MAPPED); } } else { LogPrintf("UPnP: GetExternalIPAddress failed.\n"); @@ -95,8 +95,8 @@ static bool ProcessUpnp() ret = true; LogPrintf("UPnP Port Mapping successful.\n"); } - } while (g_upnp_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); - g_upnp_interrupt.reset(); + } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_mapport_interrupt.reset(); r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); @@ -116,14 +116,14 @@ static void ThreadMapPort() { do { if (ProcessUpnp()) return; - } while (g_upnp_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); + } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); } void StartThreadMapPort() { - if (!g_upnp_thread.joinable()) { - assert(!g_upnp_interrupt); - g_upnp_thread = std::thread((std::bind(&TraceThread, "upnp", &ThreadMapPort))); + if (!g_mapport_thread.joinable()) { + assert(!g_mapport_interrupt); + g_mapport_thread = std::thread((std::bind(&TraceThread, "mapport", &ThreadMapPort))); } } @@ -154,16 +154,16 @@ void StartMapPort(bool use_upnp) void InterruptMapPort() { - if(g_upnp_thread.joinable()) { - g_upnp_interrupt(); + if (g_mapport_thread.joinable()) { + g_mapport_interrupt(); } } void StopMapPort() { - if(g_upnp_thread.joinable()) { - g_upnp_thread.join(); - g_upnp_interrupt.reset(); + if (g_mapport_thread.joinable()) { + g_mapport_thread.join(); + g_mapport_interrupt.reset(); } } diff --git a/src/net.h b/src/net.h index 6827c6897f..2f4576016d 100644 --- a/src/net.h +++ b/src/net.h @@ -182,7 +182,7 @@ enum LOCAL_NONE, // unknown LOCAL_IF, // address a local interface listens on LOCAL_BIND, // address explicit bound to - LOCAL_UPNP, // address reported by UPnP + LOCAL_MAPPED, // address reported by UPnP LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX From 58e8364dcdc4e57b0caac09f8402e6535301de9b Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 01:24:36 +0200 Subject: [PATCH 06/13] gui: Apply port mapping changes on dialog exit This commit does not change behavior. It is a prerequisite for NAT-PMP support adding. --- src/qt/optionsdialog.cpp | 5 +++++ src/qt/optionsmodel.cpp | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 468008693a..d3677f0773 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,10 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : #ifndef USE_UPNP ui->mapPortUpnp->setEnabled(false); #endif + connect(this, &QDialog::accepted, [this](){ + QSettings settings; + model->node().mapPort(settings.value("fUseUPnP").toBool()); + }); ui->proxyIp->setEnabled(false); ui->proxyPort->setEnabled(false); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index c1b8a06be8..81ebe3b810 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -355,7 +355,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); - node().mapPort(value.toBool()); break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); From a8d9f275d0ca64797cc89627f8003b48b3efef63 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 01:34:13 +0200 Subject: [PATCH 07/13] net: Add libnatpmp support --- ci/test/00_setup_env_native_qt5.sh | 2 +- configure.ac | 47 ++++++++++++ depends/Makefile | 6 +- depends/config.site.in | 4 + depends/packages/libnatpmp.mk | 19 +++++ depends/packages/packages.mk | 1 + src/Makefile.am | 2 +- src/Makefile.bench.include | 2 +- src/Makefile.qt.include | 2 +- src/Makefile.qttest.include | 2 +- src/Makefile.test.include | 2 +- src/mapport.cpp | 116 +++++++++++++++++++++++++++-- src/mapport.h | 1 + src/net.h | 2 +- 14 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 depends/packages/libnatpmp.mk diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh index dc6b2aecb5..567145fe47 100644 --- a/ci/test/00_setup_env_native_qt5.sh +++ b/ci/test/00_setup_env_native_qt5.sh @@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_qt5 export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic gcc-7 can compile our c++17 and run our functional tests in python3, see doc/dependencies.md export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev" -export DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" +export DEP_OPTS="NO_QT=1 NO_UPNP=1 NO_NATPMP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" export TEST_RUNNER_EXTRA="--previous-releases --coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash export RUN_SECURITY_TESTS="true" export RUN_UNIT_TESTS_SEQUENTIAL="true" diff --git a/configure.ac b/configure.ac index c69b793d6e..352e7aaee7 100644 --- a/configure.ac +++ b/configure.ac @@ -143,6 +143,18 @@ AC_ARG_ENABLE([upnp-default], [use_upnp_default=$enableval], [use_upnp_default=no]) +AC_ARG_WITH([natpmp], + [AS_HELP_STRING([--with-natpmp], + [enable NAT-PMP (default is yes if libnatpmp is found)])], + [use_natpmp=$withval], + [use_natpmp=auto]) + +AC_ARG_ENABLE([natpmp-default], + [AS_HELP_STRING([--enable-natpmp-default], + [if NAT-PMP is enabled, turn it on at startup (default is no)])], + [use_natpmp_default=$enableval], + [use_natpmp_default=no]) + AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), [use_tests=$enableval], @@ -1203,6 +1215,7 @@ if test "x$enable_fuzz" = "xyes"; then enable_wallet=no use_bench=no use_upnp=no + use_natpmp=no use_zmq=no AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"]],,[[$CXXFLAG_WERROR]]) @@ -1300,6 +1313,13 @@ if test x$have_miniupnpc != xno; then fi fi +dnl Check for libnatpmp (optional). +if test "x$use_natpmp" != xno; then + AC_CHECK_HEADERS([natpmp.h], + [AC_CHECK_LIB([natpmp], [initnatpmp], [NATPMP_LIBS=-lnatpmp], [have_natpmp=no])], + [have_natpmp=no]) +fi + if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then use_boost=no else @@ -1558,6 +1578,31 @@ else fi fi +dnl Enable NAT-PMP support. +AC_MSG_CHECKING([whether to build with support for NAT-PMP]) +if test "x$have_natpmp" = xno; then + if test "x$use_natpmp" = xyes; then + AC_MSG_ERROR([NAT-PMP requested but cannot be built. Use --without-natpmp]) + fi + AC_MSG_RESULT([no]) + use_natpmp=no +else + if test "x$use_natpmp" != xno; then + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([whether to build with NAT-PMP enabled by default]) + use_natpmp=yes + natpmp_setting=0 + if test "x$use_natpmp_default" != xno; then + use_natpmp_default=yes + natpmp_setting=1 + fi + AC_MSG_RESULT($use_natpmp_default) + AC_DEFINE_UNQUOTED([USE_NATPMP], [$natpmp_setting], [NAT-PMP support not compiled if undefined, otherwise value (0 or 1) determines default state]) + else + AC_MSG_RESULT([no]) + fi +fi + dnl these are only used when qt is enabled BUILD_TEST_QT="" if test x$bitcoin_enable_qt != xno; then @@ -1704,6 +1749,7 @@ AC_SUBST(SQLITE_LIBS) AC_SUBST(TESTDEFS) AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_LIBS) +AC_SUBST(NATPMP_LIBS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) @@ -1791,6 +1837,7 @@ else fi echo " with bench = $use_bench" echo " with upnp = $use_upnp" +echo " with natpmp = $use_natpmp" echo " use asm = $use_asm" echo " sanitizers = $use_sanitizers" echo " debug enabled = $enable_debug" diff --git a/depends/Makefile b/depends/Makefile index 1ad21f6821..0de98f8e3c 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -37,6 +37,7 @@ NO_QR ?= NO_WALLET ?= NO_ZMQ ?= NO_UPNP ?= +NO_NATPMP ?= MULTIPROCESS ?= FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources @@ -139,10 +140,12 @@ sqlite_packages_$(NO_SQLITE) = $(sqlite_packages) wallet_packages_$(NO_WALLET) = $(bdb_packages_) $(sqlite_packages_) upnp_packages_$(NO_UPNP) = $(upnp_packages) +natpmp_packages_$(NO_NATPMP) = $(natpmp_packages) + zmq_packages_$(NO_ZMQ) = $(zmq_packages) multiprocess_packages_$(MULTIPROCESS) = $(multiprocess_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) $(natpmp_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) ifneq ($(zmq_packages_),) @@ -200,6 +203,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@no_zmq@|$(NO_ZMQ)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ -e 's|@no_upnp@|$(NO_UPNP)|' \ + -e 's|@no_natpmp@|$(NO_NATPMP)|' \ -e 's|@multiprocess@|$(MULTIPROCESS)|' \ -e 's|@debug@|$(DEBUG)|' \ $< > $@ diff --git a/depends/config.site.in b/depends/config.site.in index f4531830c8..bbfbd42460 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -46,6 +46,10 @@ if test -z $with_miniupnpc && test -n "@no_upnp@"; then with_miniupnpc=no fi +if test -z $with_natpmp && test -n "@no_natpmp@"; then + with_natpmp=no +fi + if test -z $with_gui && test -n "@no_qt@"; then with_gui=no fi diff --git a/depends/packages/libnatpmp.mk b/depends/packages/libnatpmp.mk new file mode 100644 index 0000000000..a24f201859 --- /dev/null +++ b/depends/packages/libnatpmp.mk @@ -0,0 +1,19 @@ +package=libnatpmp +$(package)_version=20150609 +$(package)_download_path=https://miniupnp.tuxfamily.org/files/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=e1aa9c4c4219bc06943d6b2130f664daee213fb262fcb94dd355815b8f4536b0 + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" +endef + +define $(package)_build_cmds + $(MAKE) libnatpmp.a $($(package)_build_opts) +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_staging_prefix_dir)/include $($(package)_staging_prefix_dir)/lib &&\ + install *.h $($(package)_staging_prefix_dir)/include &&\ + install libnatpmp.a $($(package)_staging_prefix_dir)/lib +endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index d4fd23a47b..0f35ca0d2d 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -16,6 +16,7 @@ sqlite_packages=sqlite zmq_packages=zeromq upnp_packages=miniupnpc +natpmp_packages=libnatpmp multiprocess_packages = libmultiprocess capnp multiprocess_native_packages = native_libmultiprocess native_capnp diff --git a/src/Makefile.am b/src/Makefile.am index ec697515d9..d5975ccc79 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -598,7 +598,7 @@ bitcoin_bin_ldadd = \ $(LIBMEMENV) \ $(LIBSECP256K1) -bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) +bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) bitcoind_SOURCES = $(bitcoin_daemon_sources) bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags) diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index beb3f8dfd2..56b8ca8ce6 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -74,7 +74,7 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp endif -bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) +bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f46310a603..3d41d203d3 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -320,7 +320,7 @@ if ENABLE_ZMQ bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \ - $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS) bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index c05dd38737..a6a857d952 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -55,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ - $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS) qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 3faa5ac968..4abf388bcc 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -167,7 +167,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) +test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static if ENABLE_ZMQ diff --git a/src/mapport.cpp b/src/mapport.cpp index 5f0aeb5e40..f3ede623a3 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -16,6 +16,11 @@ #include #include +#ifdef USE_NATPMP +#include +#include +#endif // USE_NATPMP + #ifdef USE_UPNP #include #include @@ -23,7 +28,7 @@ // The minimum supported miniUPnPc API version is set to 10. This keeps compatibility // with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); -#endif +#endif // USE_UPNP #include #include @@ -32,7 +37,7 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed" #include #include -#ifdef USE_UPNP +#if defined(USE_NATPMP) || defined(USE_UPNP) static CThreadInterrupt g_mapport_interrupt; static std::thread g_mapport_thread; static std::atomic_uint g_mapport_target_proto{MapPortProtoFlag::NONE}; @@ -41,6 +46,106 @@ using namespace std::chrono_literals; static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min}; +#ifdef USE_NATPMP +static uint16_t g_mapport_external_port = 0; +static bool NatpmpInit(natpmp_t* natpmp) +{ + const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0); + if (r_init == 0) return true; + LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init); + return false; +} + +static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr) +{ + const int r_send = sendpublicaddressrequest(natpmp); + if (r_send == 2 /* OK */) { + int r_read; + natpmpresp_t response; + do { + r_read = readnatpmpresponseorretry(natpmp, &response); + } while (r_read == NATPMP_TRYAGAIN); + + if (r_read == 0) { + external_ipv4_addr = response.pnu.publicaddress.addr; + return true; + } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) { + LogPrintf("natpmp: The gateway does not support NAT-PMP.\n"); + } else { + LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read); + } + } else { + LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send); + } + + return false; +} + +static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered) +{ + const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port; + const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/); + if (r_send == 12 /* OK */) { + int r_read; + natpmpresp_t response; + do { + r_read = readnatpmpresponseorretry(natpmp, &response); + } while (r_read == NATPMP_TRYAGAIN); + + if (r_read == 0) { + auto pm = response.pnu.newportmapping; + if (private_port == pm.privateport && pm.lifetime > 0) { + g_mapport_external_port = pm.mappedpublicport; + const CService external{external_ipv4_addr, pm.mappedpublicport}; + if (!external_ip_discovered && fDiscover) { + AddLocal(external, LOCAL_MAPPED); + external_ip_discovered = true; + } + LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString()); + return true; + } else { + LogPrintf("natpmp: Port mapping failed.\n"); + } + } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) { + LogPrintf("natpmp: The gateway does not support NAT-PMP.\n"); + } else { + LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read); + } + } else { + LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send); + } + + return false; +} + +static bool ProcessNatpmp() +{ + bool ret = false; + natpmp_t natpmp; + struct in_addr external_ipv4_addr; + if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) { + bool external_ip_discovered = false; + const uint16_t private_port = GetListenPort(); + do { + ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered); + } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_mapport_interrupt.reset(); + + const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0); + g_mapport_external_port = 0; + if (r_send == 12 /* OK */) { + LogPrintf("natpmp: Port mapping removed successfully.\n"); + } else { + LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send); + } + } + + closenatpmp(&natpmp); + return ret; +} +#endif // USE_NATPMP + +#ifdef USE_UPNP static bool ProcessUpnp() { bool ret = false; @@ -111,6 +216,7 @@ static bool ProcessUpnp() return ret; } +#endif // USE_UPNP static void ThreadMapPort() { @@ -123,7 +229,7 @@ void StartThreadMapPort() { if (!g_mapport_thread.joinable()) { assert(!g_mapport_interrupt); - g_mapport_thread = std::thread((std::bind(&TraceThread, "mapport", &ThreadMapPort))); + g_mapport_thread = std::thread(std::bind(&TraceThread, "mapport", &ThreadMapPort)); } } @@ -167,7 +273,7 @@ void StopMapPort() } } -#else +#else // #if defined(USE_NATPMP) || defined(USE_UPNP) void StartMapPort(bool use_upnp) { // Intentionally left blank. @@ -180,4 +286,4 @@ void StopMapPort() { // Intentionally left blank. } -#endif +#endif // #if defined(USE_NATPMP) || defined(USE_UPNP) diff --git a/src/mapport.h b/src/mapport.h index 6e33b6ef74..2cdde775d3 100644 --- a/src/mapport.h +++ b/src/mapport.h @@ -15,6 +15,7 @@ static const bool DEFAULT_UPNP = false; enum MapPortProtoFlag : unsigned int { NONE = 0x00, UPNP = 0x01, + NAT_PMP = 0x02, }; void StartMapPort(bool use_upnp); diff --git a/src/net.h b/src/net.h index 2f4576016d..6c167954df 100644 --- a/src/net.h +++ b/src/net.h @@ -182,7 +182,7 @@ enum LOCAL_NONE, // unknown LOCAL_IF, // address a local interface listens on LOCAL_BIND, // address explicit bound to - LOCAL_MAPPED, // address reported by UPnP + LOCAL_MAPPED, // address reported by UPnP or NAT-PMP LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX From 28acffd9d53ec437e908abb8c84497a4f41b91ed Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 01:51:45 +0200 Subject: [PATCH 08/13] net: Add NAT-PMP to port mapping loop --- src/mapport.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/mapport.cpp b/src/mapport.cpp index f3ede623a3..bb5cc47797 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -40,7 +40,8 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed" #if defined(USE_NATPMP) || defined(USE_UPNP) static CThreadInterrupt g_mapport_interrupt; static std::thread g_mapport_thread; -static std::atomic_uint g_mapport_target_proto{MapPortProtoFlag::NONE}; +static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE}; +static std::atomic g_mapport_current_proto{MapPortProtoFlag::NONE}; using namespace std::chrono_literals; static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; @@ -220,9 +221,34 @@ static bool ProcessUpnp() static void ThreadMapPort() { + bool ok; do { - if (ProcessUpnp()) return; - } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); + ok = false; + +#ifdef USE_UPNP + // High priority protocol. + if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) { + g_mapport_current_proto = MapPortProtoFlag::UPNP; + ok = ProcessUpnp(); + if (ok) continue; + } +#endif // USE_UPNP + +#ifdef USE_NATPMP + // Low priority protocol. + if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) { + g_mapport_current_proto = MapPortProtoFlag::NAT_PMP; + ok = ProcessNatpmp(); + if (ok) continue; + } +#endif // USE_NATPMP + + g_mapport_current_proto = MapPortProtoFlag::NONE; + if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + return; + } + + } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); } void StartThreadMapPort() @@ -235,20 +261,39 @@ void StartThreadMapPort() static void DispatchMapPort() { - if (g_mapport_target_proto == MapPortProtoFlag::UPNP) { + if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + return; + } + + if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) { StartThreadMapPort(); - } else { + return; + } + + if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) { InterruptMapPort(); StopMapPort(); + return; } + + if (g_mapport_enabled_protos & g_mapport_current_proto) { + // Enabling another protocol does not cause switching from the currently used one. + return; + } + + assert(g_mapport_thread.joinable()); + assert(!g_mapport_interrupt); + // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp() + // to force trying the next protocol in the ThreadMapPort() loop. + g_mapport_interrupt(); } static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) { if (enabled) { - g_mapport_target_proto |= proto; + g_mapport_enabled_protos |= proto; } else { - g_mapport_target_proto &= ~proto; + g_mapport_enabled_protos &= ~proto; } } @@ -260,6 +305,7 @@ void StartMapPort(bool use_upnp) void InterruptMapPort() { + g_mapport_enabled_protos = MapPortProtoFlag::NONE; if (g_mapport_thread.joinable()) { g_mapport_interrupt(); } From a39f7336a3b493d46a4486c4c94fdca1b3151370 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 02:12:19 +0200 Subject: [PATCH 09/13] net: Add -natpmp command line option --- src/init.cpp | 17 ++++++++++++++--- src/interfaces/node.h | 2 +- src/mapport.cpp | 5 +++-- src/mapport.h | 15 ++++++++++----- src/node/interfaces.cpp | 2 +- src/qt/optionsdialog.cpp | 2 +- src/qt/optionsmodel.cpp | 18 +++++++++++++++++- src/qt/optionsmodel.h | 1 + test/functional/test_framework/util.py | 1 + 9 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 8a260a3df2..9a63fe0e03 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -469,6 +469,11 @@ void SetupServerArgs(NodeContext& node) #else hidden_args.emplace_back("-upnp"); #endif +#ifdef USE_NATPMP + argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION); +#else + hidden_args.emplace_back("-natpmp"); +#endif // USE_NATPMP argsman.AddArg("-whitebind=<[permissions@]addr>", "Bind to the given address and add permission flags to the peers connecting to it. " "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". " "Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -813,10 +818,13 @@ void InitParameterInteraction(ArgsManager& args) // to protect privacy, do not listen by default if a default proxy server is specified if (args.SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); - // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 + // to protect privacy, do not map ports when a proxy is set. The user may still specify -listen=1 // to listen locally, so don't rely on this happening through -listen below. if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -proxy set -> setting -natpmp=0\n", __func__); + } // to protect privacy, do not discover addresses by default if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); @@ -826,6 +834,9 @@ void InitParameterInteraction(ArgsManager& args) // do not map ports or try to retrieve public IP when not listening (pointless) if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -listen=0 -> setting -natpmp=0\n", __func__); + } if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); if (args.SoftSetBoolArg("-listenonion", false)) @@ -1899,8 +1910,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA Discover(); - // Map ports with UPnP - StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP)); + // Map ports with UPnP or NAT-PMP. + StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 97549aeb0e..15f7ef6256 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -82,7 +82,7 @@ public: virtual bool shutdownRequested() = 0; //! Map port. - virtual void mapPort(bool use_upnp) = 0; + virtual void mapPort(bool use_upnp, bool use_natpmp) = 0; //! Get proxy. virtual bool getProxy(Network net, proxyType& proxy_info) = 0; diff --git a/src/mapport.cpp b/src/mapport.cpp index bb5cc47797..2df4ce45d2 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -297,9 +297,10 @@ static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) } } -void StartMapPort(bool use_upnp) +void StartMapPort(bool use_upnp, bool use_natpmp) { MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp); + MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp); DispatchMapPort(); } @@ -320,7 +321,7 @@ void StopMapPort() } #else // #if defined(USE_NATPMP) || defined(USE_UPNP) -void StartMapPort(bool use_upnp) +void StartMapPort(bool use_upnp, bool use_natpmp) { // Intentionally left blank. } diff --git a/src/mapport.h b/src/mapport.h index 2cdde775d3..279d65167f 100644 --- a/src/mapport.h +++ b/src/mapport.h @@ -5,12 +5,17 @@ #ifndef BITCOIN_MAPPORT_H #define BITCOIN_MAPPORT_H -/** -upnp default */ #ifdef USE_UPNP -static const bool DEFAULT_UPNP = USE_UPNP; +static constexpr bool DEFAULT_UPNP = USE_UPNP; #else -static const bool DEFAULT_UPNP = false; -#endif +static constexpr bool DEFAULT_UPNP = false; +#endif // USE_UPNP + +#ifdef USE_NATPMP +static constexpr bool DEFAULT_NATPMP = USE_NATPMP; +#else +static constexpr bool DEFAULT_NATPMP = false; +#endif // USE_NATPMP enum MapPortProtoFlag : unsigned int { NONE = 0x00, @@ -18,7 +23,7 @@ enum MapPortProtoFlag : unsigned int { NAT_PMP = 0x02, }; -void StartMapPort(bool use_upnp); +void StartMapPort(bool use_upnp, bool use_natpmp); void InterruptMapPort(); void StopMapPort(); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 6b84cb0f05..e07eaa33d8 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -94,7 +94,7 @@ public: } } bool shutdownRequested() override { return ShutdownRequested(); } - void mapPort(bool use_upnp) override { StartMapPort(use_upnp); } + void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d3677f0773..4057712a41 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -53,7 +53,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : #endif connect(this, &QDialog::accepted, [this](){ QSettings settings; - model->node().mapPort(settings.value("fUseUPnP").toBool()); + model->node().mapPort(settings.value("fUseUPnP").toBool(), settings.value("fUseNatpmp").toBool()); }); ui->proxyIp->setEnabled(false); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 81ebe3b810..1e0391a35c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -124,6 +124,13 @@ void OptionsModel::Init(bool resetSettings) if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) addOverriddenOption("-upnp"); + if (!settings.contains("fUseNatpmp")) { + settings.setValue("fUseNatpmp", DEFAULT_NATPMP); + } + if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool())) { + addOverriddenOption("-natpmp"); + } + if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) @@ -283,7 +290,13 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fUseUPnP"); #else return false; -#endif +#endif // USE_UPNP + case MapPortNatpmp: +#ifdef USE_NATPMP + return settings.value("fUseNatpmp"); +#else + return false; +#endif // USE_NATPMP case MinimizeOnClose: return fMinimizeOnClose; @@ -356,6 +369,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); break; + case MapPortNatpmp: // core option - can be changed on-the-fly + settings.setValue("fUseNatpmp", value.toBool()); + break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); settings.setValue("fMinimizeOnClose", fMinimizeOnClose); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f1929a5e06..f7171951a1 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -48,6 +48,7 @@ public: ShowTrayIcon, // bool MinimizeToTray, // bool MapPortUPnP, // bool + MapPortNatpmp, // bool MinimizeOnClose, // bool ProxyUse, // bool ProxyIP, // QString diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 62ff5c6e33..b3eb2d61a7 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -362,6 +362,7 @@ def initialize_datadir(dirname, n, chain): f.write("listenonion=0\n") f.write("printtoconsole=0\n") f.write("upnp=0\n") + f.write("natpmp=0\n") f.write("shrinkdebugfile=0\n") os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) From 5a0185b6c9c838290103314916190a0330ed9a82 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 02:25:21 +0200 Subject: [PATCH 10/13] gui: Add NAT-PMP network option --- src/qt/forms/optionsdialog.ui | 10 ++++++++++ src/qt/optionsdialog.cpp | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 88944a58a6..8181cc47e2 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -259,6 +259,16 @@ + + + + Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random. + + + Map port using NA&T-PMP + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 4057712a41..416b9c83c9 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -50,6 +50,9 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : /* Network elements init */ #ifndef USE_UPNP ui->mapPortUpnp->setEnabled(false); +#endif +#ifndef USE_NATPMP + ui->mapPortNatpmp->setEnabled(false); #endif connect(this, &QDialog::accepted, [this](){ QSettings settings; @@ -219,6 +222,7 @@ void OptionsDialog::setMapper() /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); + mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); From e28f9be87a0f3c59a9184d602fe7947526df6a97 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 02:33:35 +0200 Subject: [PATCH 11/13] ci: Add libnatpmp-dev package to some builds --- .cirrus.yml | 2 +- ci/test/00_setup_env_native_asan.sh | 2 +- ci/test/00_setup_env_native_valgrind.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index e7a50d931a..b915cfabc7 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -170,7 +170,7 @@ task: task: name: 'macOS 10.15 native [gui] [no depends]' macos_brew_addon_script: - - brew install boost libevent berkeley-db4 qt miniupnpc ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt + - brew install boost libevent berkeley-db4 qt miniupnpc libnatpmp ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt << : *GLOBAL_TASK_TEMPLATE osx_instance: # Use latest image, but hardcode version to avoid silent upgrades (and breaks) diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh index 191b8049b0..f682486088 100644 --- a/ci/test/00_setup_env_native_asan.sh +++ b/ci/test/00_setup_env_native_asan.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_asan -export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev libsqlite3-dev" +export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev" export DOCKER_NAME_TAG=ubuntu:20.04 export NO_DEPENDS=1 export GOAL="install" diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh index bfaea13a25..f0c153158b 100644 --- a/ci/test/00_setup_env_native_valgrind.sh +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_valgrind -export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libsqlite3-dev" +export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libsqlite3-dev" export USE_VALGRIND=1 export NO_DEPENDS=1 export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 From ae749d12ddbaf592fbdb65d98ca35a0ff5566992 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 02:35:10 +0200 Subject: [PATCH 12/13] doc: Add libnatpmp stuff --- depends/README.md | 1 + doc/build-osx.md | 2 +- doc/build-unix.md | 24 +++++++++++++++++------- doc/dependencies.md | 4 +++- doc/productivity.md | 1 + doc/tor.md | 2 +- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/depends/README.md b/depends/README.md index 085e0e6054..9bc9b6714b 100644 --- a/depends/README.md +++ b/depends/README.md @@ -94,6 +94,7 @@ The following can be set when running make: `make FOO=bar` - `NO_BDB`: Don't download/build/cache BerkeleyDB - `NO_SQLITE`: Don't download/build/cache SQLite - `NO_UPNP`: Don't download/build/cache packages needed for enabling UPnP +- `NO_NATPMP`: Don't download/build/cache packages needed for enabling NAT-PMP - `ALLOW_HOST_PACKAGES`: Packages that are missed in dependencies (due to `NO_*` option or build script logic) are searched for among the host system packages using `pkg-config`. It allows building with packages of other (newer) versions diff --git a/doc/build-osx.md b/doc/build-osx.md index c1d101fde1..b46bf61c4b 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh). ## Dependencies ```shell -brew install automake libtool boost miniupnpc pkg-config python qt libevent qrencode +brew install automake libtool boost miniupnpc libnatpmp pkg-config python qt libevent qrencode ``` If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting). diff --git a/doc/build-unix.md b/doc/build-unix.md index cfe3328b45..5c24886dbf 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -41,6 +41,7 @@ Optional dependencies: Library | Purpose | Description ------------|------------------|---------------------- miniupnpc | UPnP Support | Firewall-jumping support + libnatpmp | NAT-PMP Support | Firewall-jumping support libdb4.8 | Berkeley DB | Optional, wallet storage (only needed when wallet enabled) qt | GUI | GUI toolkit (only needed when GUI enabled) libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled) @@ -99,9 +100,9 @@ SQLite is required for the wallet: To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode) -Optional (see `--with-miniupnpc` and `--enable-upnp-default`): +Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`): - sudo apt-get install libminiupnpc-dev + sudo apt install libminiupnpc-dev libnatpmp-dev ZMQ dependencies (provides ZMQ API): @@ -133,9 +134,9 @@ Build requirements: sudo dnf install gcc-c++ libtool make autoconf automake libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3 -Optional (see `--with-miniupnpc` and `--enable-upnp-default`): +Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`): - sudo dnf install miniupnpc-devel + sudo dnf install miniupnpc-devel libnatpmp-devel ZMQ dependencies (provides ZMQ API): @@ -158,18 +159,27 @@ Notes The release is built with GCC and then "strip bitcoind" to strip the debug symbols, which reduces the executable size by about 90%. - miniupnpc --------- [miniupnpc](https://miniupnp.tuxfamily.org) may be used for UPnP port mapping. It can be downloaded from [here]( https://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and -turned off by default. See the configure options for upnp behavior desired: +turned off by default. See the configure options for UPnP behavior desired: - --without-miniupnpc No UPnP support miniupnp not required + --without-miniupnpc No UPnP support, miniupnp not required --disable-upnp-default (the default) UPnP support turned off by default at runtime --enable-upnp-default UPnP support turned on by default at runtime +libnatpmp +--------- + +[libnatpmp](https://miniupnp.tuxfamily.org/libnatpmp.html) may be used for NAT-PMP port mapping. It can be downloaded +from [here](https://miniupnp.tuxfamily.org/files/). NAT-PMP support is compiled in and +turned off by default. See the configure options for NAT-PMP behavior desired: + + --without-natpmp No NAT-PMP support, libnatpmp not required + --disable-natpmp-default (the default) NAT-PMP support turned off by default at runtime + --enable-natpmp-default NAT-PMP support turned on by default at runtime Berkeley DB ----------- diff --git a/doc/dependencies.md b/doc/dependencies.md index 76e8910871..6b2b904632 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -14,6 +14,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | GCC | | [7+](https://gcc.gnu.org/) (C++17 support) | | | | | HarfBuzz-NG | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | libevent | [2.1.11-stable](https://github.com/libevent/libevent/releases) | [2.0.21](https://github.com/bitcoin/bitcoin/pull/18676) | No | | | +| libnatpmp | [20150609](https://miniupnp.tuxfamily.org/files) | | No | | | | libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | librsvg | | | | | | | MiniUPnPc | [2.0.20180203](https://miniupnp.tuxfamily.org/files) | | No | | | @@ -32,7 +33,8 @@ Controlling dependencies Some dependencies are not needed in all configurations. The following are some factors that affect the dependency list. #### Options passed to `./configure` -* MiniUPnPc is not needed with `--with-miniupnpc=no`. +* MiniUPnPc is not needed with `--without-miniupnpc`. +* libnatpmp is not needed with `--without-natpmp`. * Berkeley DB is not needed with `--disable-wallet` or `--without-bdb`. * SQLite is not needed with `--disable-wallet` or `--without-sqlite`. * Qt is not needed with `--without-gui`. diff --git a/doc/productivity.md b/doc/productivity.md index 555f0afe3c..a01c6f545d 100644 --- a/doc/productivity.md +++ b/doc/productivity.md @@ -51,6 +51,7 @@ After running `./autogen.sh`, which generates the `./configure` file, use `./con ```sh --without-miniupnpc +--without-natpmp --disable-bench --disable-wallet --without-gui diff --git a/doc/tor.md b/doc/tor.md index 86e5d9ddf3..1ba7137b8e 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -99,7 +99,7 @@ as well, use `discover` instead: ./bitcoind ... -discover -and open port 8333 on your firewall (or use -upnp). +and open port 8333 on your firewall (or use port mapping, i.e., `-upnp` or `-natpmp`). If you only want to use Tor to reach .onion addresses, but not use it as a proxy for normal IPv4/IPv6 communication, use: From a191e23b8e7f0e19fc0359825eb7ca0d47966fa9 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 23 Feb 2020 02:36:20 +0200 Subject: [PATCH 13/13] doc: Add release notes --- doc/release-notes-18077.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 doc/release-notes-18077.md diff --git a/doc/release-notes-18077.md b/doc/release-notes-18077.md new file mode 100644 index 0000000000..4034a4c19c --- /dev/null +++ b/doc/release-notes-18077.md @@ -0,0 +1,10 @@ +P2P and network changes +----------------------- + +- Added NAT-PMP port mapping support via [`libnatpmp`](https://miniupnp.tuxfamily.org/libnatpmp.html) + +Command-line options +-------------------- + +- The `-natpmp` option has been added to use NAT-PMP to map the listening port. If both UPnP +and NAT-PMP are enabled, a successful allocation from UPnP prevails over one from NAT-PMP.