Merge #18077: net: Add NAT-PMP port forwarding support

a191e23b8e doc: Add release notes (Hennadii Stepanov)
ae749d12dd doc: Add libnatpmp stuff (Hennadii Stepanov)
e28f9be87a ci: Add libnatpmp-dev package to some builds (Hennadii Stepanov)
5a0185b6c9 gui: Add NAT-PMP network option (Hennadii Stepanov)
a39f7336a3 net: Add -natpmp command line option (Hennadii Stepanov)
28acffd9d5 net: Add NAT-PMP to port mapping loop (Hennadii Stepanov)
a8d9f275d0 net: Add libnatpmp support (Hennadii Stepanov)
58e8364dcd gui: Apply port mapping changes on dialog exit (Hennadii Stepanov)
cf151cc68c scripted-diff: Rename UPnP stuff (Hennadii Stepanov)
4e91b1e24d net: Add flags for port mapping protocols (Hennadii Stepanov)
8b50d1b5bb net: Keep trying to use UPnP when -upnp=1 (Hennadii Stepanov)
28e2961fd6 refactor: Replace magic number with named constant (Hennadii Stepanov)
02ccf69dd6 refactor: Move port mapping code to its own module (Hennadii Stepanov)

Pull request description:

  Close #11902
  This PR is an alternative to:
  - #12288
  - #15717

  To compile with NAT-PMP support on Ubuntu [`libnatpmp-dev`](https://packages.ubuntu.com/source/bionic/libnatpmp) should be available.

  Log excerpt:
  ```
  2020-02-05T20:12:28Z [mapport] NAT-PMP: public address = 95.164.65.194
  2020-02-05T20:12:28Z [mapport] AddLocal(95.164.65.194:18333,3)
  2020-02-05T20:12:28Z [mapport] NAT-PMP: port mapping successful.
  ```

  See: [`libnatpmp`](https://miniupnp.tuxfamily.org/libnatpmp.html)

  ---

  Some follow-ups are out of this PR's scope:
  - mention NAT-PMP library in the version message
  - ~integrate NAT-PMP into the GUI~ (already [added](https://github.com/bitcoin/bitcoin/pull/18077#issuecomment-589405068))

ACKs for top commit:
  laanwj:
    Tested and code review ACK a191e23b8e

Tree-SHA512: 10e19267c21bf30f20ff1abfc882d526049f0e790b95e12f109dc2bed7c0aef45de03eaf967f4e667e7509be04f1873a5c508087393d947205f3aab2ad6d7cf1
This commit is contained in:
Wladimir J. van der Laan 2021-01-07 19:33:14 +01:00
commit d7e2401c62
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
33 changed files with 547 additions and 173 deletions

View file

@ -170,7 +170,7 @@ task:
task: task:
name: 'macOS 10.15 native [gui] [no depends]' name: 'macOS 10.15 native [gui] [no depends]'
macos_brew_addon_script: 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 << : *GLOBAL_TASK_TEMPLATE
osx_instance: osx_instance:
# Use latest image, but hardcode version to avoid silent upgrades (and breaks) # Use latest image, but hardcode version to avoid silent upgrades (and breaks)

View file

@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_asan 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 DOCKER_NAME_TAG=ubuntu:20.04
export NO_DEPENDS=1 export NO_DEPENDS=1
export GOAL="install" export GOAL="install"

View file

@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_qt5 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 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 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 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_SECURITY_TESTS="true"
export RUN_UNIT_TESTS_SEQUENTIAL="true" export RUN_UNIT_TESTS_SEQUENTIAL="true"

View file

@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_valgrind 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 USE_VALGRIND=1
export NO_DEPENDS=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 export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547

View file

@ -143,6 +143,18 @@ AC_ARG_ENABLE([upnp-default],
[use_upnp_default=$enableval], [use_upnp_default=$enableval],
[use_upnp_default=no]) [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, AC_ARG_ENABLE(tests,
AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]),
[use_tests=$enableval], [use_tests=$enableval],
@ -1206,6 +1218,7 @@ if test "x$enable_fuzz" = "xyes"; then
enable_wallet=no enable_wallet=no
use_bench=no use_bench=no
use_upnp=no use_upnp=no
use_natpmp=no
use_zmq=no use_zmq=no
AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"]],,[[$CXXFLAG_WERROR]]) AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"]],,[[$CXXFLAG_WERROR]])
@ -1303,6 +1316,13 @@ if test x$have_miniupnpc != xno; then
fi fi
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 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 use_boost=no
else else
@ -1561,6 +1581,31 @@ else
fi fi
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 dnl these are only used when qt is enabled
BUILD_TEST_QT="" BUILD_TEST_QT=""
if test x$bitcoin_enable_qt != xno; then if test x$bitcoin_enable_qt != xno; then
@ -1707,6 +1752,7 @@ AC_SUBST(SQLITE_LIBS)
AC_SUBST(TESTDEFS) AC_SUBST(TESTDEFS)
AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_CPPFLAGS)
AC_SUBST(MINIUPNPC_LIBS) AC_SUBST(MINIUPNPC_LIBS)
AC_SUBST(NATPMP_LIBS)
AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_LIBS)
AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(ZMQ_LIBS) AC_SUBST(ZMQ_LIBS)
@ -1794,6 +1840,7 @@ else
fi fi
echo " with bench = $use_bench" echo " with bench = $use_bench"
echo " with upnp = $use_upnp" echo " with upnp = $use_upnp"
echo " with natpmp = $use_natpmp"
echo " use asm = $use_asm" echo " use asm = $use_asm"
echo " sanitizers = $use_sanitizers" echo " sanitizers = $use_sanitizers"
echo " debug enabled = $enable_debug" echo " debug enabled = $enable_debug"

View file

@ -37,6 +37,7 @@ NO_QR ?=
NO_WALLET ?= NO_WALLET ?=
NO_ZMQ ?= NO_ZMQ ?=
NO_UPNP ?= NO_UPNP ?=
NO_NATPMP ?=
MULTIPROCESS ?= MULTIPROCESS ?=
FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources 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_) wallet_packages_$(NO_WALLET) = $(bdb_packages_) $(sqlite_packages_)
upnp_packages_$(NO_UPNP) = $(upnp_packages) upnp_packages_$(NO_UPNP) = $(upnp_packages)
natpmp_packages_$(NO_NATPMP) = $(natpmp_packages)
zmq_packages_$(NO_ZMQ) = $(zmq_packages) zmq_packages_$(NO_ZMQ) = $(zmq_packages)
multiprocess_packages_$(MULTIPROCESS) = $(multiprocess_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) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages)
ifneq ($(zmq_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_zmq@|$(NO_ZMQ)|' \
-e 's|@no_wallet@|$(NO_WALLET)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \
-e 's|@no_upnp@|$(NO_UPNP)|' \ -e 's|@no_upnp@|$(NO_UPNP)|' \
-e 's|@no_natpmp@|$(NO_NATPMP)|' \
-e 's|@multiprocess@|$(MULTIPROCESS)|' \ -e 's|@multiprocess@|$(MULTIPROCESS)|' \
-e 's|@debug@|$(DEBUG)|' \ -e 's|@debug@|$(DEBUG)|' \
$< > $@ $< > $@

View file

@ -94,6 +94,7 @@ The following can be set when running make: `make FOO=bar`
- `NO_BDB`: Don't download/build/cache BerkeleyDB - `NO_BDB`: Don't download/build/cache BerkeleyDB
- `NO_SQLITE`: Don't download/build/cache SQLite - `NO_SQLITE`: Don't download/build/cache SQLite
- `NO_UPNP`: Don't download/build/cache packages needed for enabling UPnP - `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</dd>
- `ALLOW_HOST_PACKAGES`: Packages that are missed in dependencies (due to `NO_*` option or - `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 build script logic) are searched for among the host system packages using
`pkg-config`. It allows building with packages of other (newer) versions `pkg-config`. It allows building with packages of other (newer) versions

View file

@ -46,6 +46,10 @@ if test -z $with_miniupnpc && test -n "@no_upnp@"; then
with_miniupnpc=no with_miniupnpc=no
fi 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 if test -z $with_gui && test -n "@no_qt@"; then
with_gui=no with_gui=no
fi fi

View file

@ -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

View file

@ -16,6 +16,7 @@ sqlite_packages=sqlite
zmq_packages=zeromq zmq_packages=zeromq
upnp_packages=miniupnpc upnp_packages=miniupnpc
natpmp_packages=libnatpmp
multiprocess_packages = libmultiprocess capnp multiprocess_packages = libmultiprocess capnp
multiprocess_native_packages = native_libmultiprocess native_capnp multiprocess_native_packages = native_libmultiprocess native_capnp

View file

@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh).
## Dependencies ## Dependencies
```shell ```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). If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).

View file

@ -41,6 +41,7 @@ Optional dependencies:
Library | Purpose | Description Library | Purpose | Description
------------|------------------|---------------------- ------------|------------------|----------------------
miniupnpc | UPnP Support | Firewall-jumping support 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) libdb4.8 | Berkeley DB | Optional, wallet storage (only needed when wallet enabled)
qt | GUI | GUI toolkit (only needed when GUI 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) 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) 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): 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 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): ZMQ dependencies (provides ZMQ API):
@ -158,18 +159,27 @@ Notes
The release is built with GCC and then "strip bitcoind" to strip the debug The release is built with GCC and then "strip bitcoind" to strip the debug
symbols, which reduces the executable size by about 90%. symbols, which reduces the executable size by about 90%.
miniupnpc miniupnpc
--------- ---------
[miniupnpc](https://miniupnp.tuxfamily.org) may be used for UPnP port mapping. It can be downloaded from [here]( [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 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 --disable-upnp-default (the default) UPnP support turned off by default at runtime
--enable-upnp-default UPnP support turned on 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 Berkeley DB
----------- -----------

View file

@ -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) | | | | | GCC | | [7+](https://gcc.gnu.org/) (C++17 support) | | | |
| HarfBuzz-NG | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | 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 | | | | 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) | | libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| librsvg | | | | | | | librsvg | | | | | |
| MiniUPnPc | [2.0.20180203](https://miniupnp.tuxfamily.org/files) | | No | | | | 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. Some dependencies are not needed in all configurations. The following are some factors that affect the dependency list.
#### Options passed to `./configure` #### 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`. * Berkeley DB is not needed with `--disable-wallet` or `--without-bdb`.
* SQLite is not needed with `--disable-wallet` or `--without-sqlite`. * SQLite is not needed with `--disable-wallet` or `--without-sqlite`.
* Qt is not needed with `--without-gui`. * Qt is not needed with `--without-gui`.

View file

@ -51,6 +51,7 @@ After running `./autogen.sh`, which generates the `./configure` file, use `./con
```sh ```sh
--without-miniupnpc --without-miniupnpc
--without-natpmp
--disable-bench --disable-bench
--disable-wallet --disable-wallet
--without-gui --without-gui

View file

@ -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.

View file

@ -99,7 +99,7 @@ as well, use `discover` instead:
./bitcoind ... -discover ./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 If you only want to use Tor to reach .onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use: for normal IPv4/IPv6 communication, use:

View file

@ -152,6 +152,7 @@ BITCOIN_CORE_H = \
key_io.h \ key_io.h \
logging.h \ logging.h \
logging/timer.h \ logging/timer.h \
mapport.h \
memusage.h \ memusage.h \
merkleblock.h \ merkleblock.h \
miner.h \ miner.h \
@ -299,6 +300,7 @@ libbitcoin_server_a_SOURCES = \
index/blockfilterindex.cpp \ index/blockfilterindex.cpp \
index/txindex.cpp \ index/txindex.cpp \
init.cpp \ init.cpp \
mapport.cpp \
miner.cpp \ miner.cpp \
net.cpp \ net.cpp \
net_processing.cpp \ net_processing.cpp \
@ -598,7 +600,7 @@ bitcoin_bin_ldadd = \
$(LIBMEMENV) \ $(LIBMEMENV) \
$(LIBSECP256K1) $(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_SOURCES = $(bitcoin_daemon_sources)
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags) bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)

View file

@ -74,7 +74,7 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif 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) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)

View file

@ -320,7 +320,7 @@ if ENABLE_ZMQ
bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif endif
bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \ 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) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX

View file

@ -55,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif endif
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ 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) \ $(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) $(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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)

View file

@ -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) $(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_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 test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static
if ENABLE_ZMQ if ENABLE_ZMQ

View file

@ -26,6 +26,7 @@
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <key.h> #include <key.h>
#include <mapport.h>
#include <miner.h> #include <miner.h>
#include <net.h> #include <net.h>
#include <net_permissions.h> #include <net_permissions.h>
@ -468,6 +469,11 @@ void SetupServerArgs(NodeContext& node)
#else #else
hidden_args.emplace_back("-upnp"); hidden_args.emplace_back("-upnp");
#endif #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. " 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, ", ") + ". " "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); "Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@ -812,10 +818,13 @@ void InitParameterInteraction(ArgsManager& args)
// to protect privacy, do not listen by default if a default proxy server is specified // to protect privacy, do not listen by default if a default proxy server is specified
if (args.SoftSetBoolArg("-listen", false)) if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); 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. // to listen locally, so don't rely on this happening through -listen below.
if (args.SoftSetBoolArg("-upnp", false)) if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); 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 // to protect privacy, do not discover addresses by default
if (args.SoftSetBoolArg("-discover", false)) if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
@ -825,6 +834,9 @@ void InitParameterInteraction(ArgsManager& args)
// do not map ports or try to retrieve public IP when not listening (pointless) // do not map ports or try to retrieve public IP when not listening (pointless)
if (args.SoftSetBoolArg("-upnp", false)) if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); 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)) if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (args.SoftSetBoolArg("-listenonion", false)) if (args.SoftSetBoolArg("-listenonion", false))
@ -1898,10 +1910,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
Discover(); Discover();
// Map ports with UPnP // Map ports with UPnP or NAT-PMP.
if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) { StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP));
StartMapPort();
}
CConnman::Options connOptions; CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices; connOptions.nLocalServices = nLocalServices;

View file

@ -82,7 +82,7 @@ public:
virtual bool shutdownRequested() = 0; virtual bool shutdownRequested() = 0;
//! Map port. //! Map port.
virtual void mapPort(bool use_upnp) = 0; virtual void mapPort(bool use_upnp, bool use_natpmp) = 0;
//! Get proxy. //! Get proxy.
virtual bool getProxy(Network net, proxyType& proxy_info) = 0; virtual bool getProxy(Network net, proxyType& proxy_info) = 0;

336
src/mapport.cpp Normal file
View file

@ -0,0 +1,336 @@
// 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 <config/bitcoin-config.h>
#endif
#include <mapport.h>
#include <clientversion.h>
#include <logging.h>
#include <net.h>
#include <netaddress.h>
#include <netbase.h>
#include <threadinterrupt.h>
#include <util/system.h>
#ifdef USE_NATPMP
#include <compat.h>
#include <natpmp.h>
#endif // USE_NATPMP
#ifdef USE_UPNP
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
// 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 // USE_UPNP
#include <atomic>
#include <cassert>
#include <chrono>
#include <functional>
#include <string>
#include <thread>
#if defined(USE_NATPMP) || defined(USE_UPNP)
static CThreadInterrupt g_mapport_interrupt;
static std::thread g_mapport_thread;
static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
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;
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_MAPPED);
}
} 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) {
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_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);
freeUPNPDevlist(devlist); devlist = nullptr;
FreeUPNPUrls(&urls);
} else {
LogPrintf("No valid UPnP IGDs found\n");
freeUPNPDevlist(devlist); devlist = nullptr;
if (r != 0)
FreeUPNPUrls(&urls);
}
return ret;
}
#endif // USE_UPNP
static void ThreadMapPort()
{
bool ok;
do {
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()
{
if (!g_mapport_thread.joinable()) {
assert(!g_mapport_interrupt);
g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort));
}
}
static void DispatchMapPort()
{
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();
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_enabled_protos |= proto;
} else {
g_mapport_enabled_protos &= ~proto;
}
}
void StartMapPort(bool use_upnp, bool use_natpmp)
{
MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
DispatchMapPort();
}
void InterruptMapPort()
{
g_mapport_enabled_protos = MapPortProtoFlag::NONE;
if (g_mapport_thread.joinable()) {
g_mapport_interrupt();
}
}
void StopMapPort()
{
if (g_mapport_thread.joinable()) {
g_mapport_thread.join();
g_mapport_interrupt.reset();
}
}
#else // #if defined(USE_NATPMP) || defined(USE_UPNP)
void StartMapPort(bool use_upnp, bool use_natpmp)
{
// Intentionally left blank.
}
void InterruptMapPort()
{
// Intentionally left blank.
}
void StopMapPort()
{
// Intentionally left blank.
}
#endif // #if defined(USE_NATPMP) || defined(USE_UPNP)

30
src/mapport.h Normal file
View file

@ -0,0 +1,30 @@
// 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
#ifdef USE_UPNP
static constexpr bool DEFAULT_UPNP = USE_UPNP;
#else
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,
UPNP = 0x01,
NAT_PMP = 0x02,
};
void StartMapPort(bool use_upnp, bool use_natpmp);
void InterruptMapPort();
void StopMapPort();
#endif // BITCOIN_MAPPORT_H

View file

@ -33,15 +33,6 @@
#include <poll.h> #include <poll.h>
#endif #endif
#ifdef USE_UPNP
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
// 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 <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <unordered_map> #include <unordered_map>
@ -1539,121 +1530,6 @@ void CConnman::WakeMessageHandler()
condMsgProc.notify_one(); 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<void (*)()>, "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() void CConnman::ThreadDNSAddressSeed()
{ {
FastRandomContext rng; FastRandomContext rng;

View file

@ -67,12 +67,6 @@ static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2;
static const int MAX_FEELER_CONNECTIONS = 1; static const int MAX_FEELER_CONNECTIONS = 1;
/** -listen default */ /** -listen default */
static const bool DEFAULT_LISTEN = true; 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. */ /** The maximum number of peer connections to maintain. */
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */ /** The default for -maxuploadtarget. 0 = Unlimited */
@ -181,9 +175,6 @@ enum class ConnectionType {
}; };
void Discover(); void Discover();
void StartMapPort();
void InterruptMapPort();
void StopMapPort();
uint16_t GetListenPort(); uint16_t GetListenPort();
enum enum
@ -191,7 +182,7 @@ enum
LOCAL_NONE, // unknown LOCAL_NONE, // unknown
LOCAL_IF, // address a local interface listens on LOCAL_IF, // address a local interface listens on
LOCAL_BIND, // address explicit bound to LOCAL_BIND, // address explicit bound to
LOCAL_UPNP, // address reported by UPnP LOCAL_MAPPED, // address reported by UPnP or NAT-PMP
LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MANUAL, // address explicitly specified (-externalip=)
LOCAL_MAX LOCAL_MAX

View file

@ -12,6 +12,7 @@
#include <interfaces/handler.h> #include <interfaces/handler.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <interfaces/wallet.h> #include <interfaces/wallet.h>
#include <mapport.h>
#include <net.h> #include <net.h>
#include <net_processing.h> #include <net_processing.h>
#include <netaddress.h> #include <netaddress.h>
@ -93,15 +94,7 @@ public:
} }
} }
bool shutdownRequested() override { return ShutdownRequested(); } bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp) override void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
{
if (use_upnp) {
StartMapPort();
} else {
InterruptMapPort();
StopMapPort();
}
}
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(CConnman::NumConnections flags) override size_t getNodeCount(CConnman::NumConnections flags) override
{ {

View file

@ -259,6 +259,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="mapPortNatpmp">
<property name="toolTip">
<string>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.</string>
</property>
<property name="text">
<string>Map port using NA&amp;T-PMP</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="allowIncoming"> <widget class="QCheckBox" name="allowIncoming">
<property name="toolTip"> <property name="toolTip">

View file

@ -24,6 +24,7 @@
#include <QIntValidator> #include <QIntValidator>
#include <QLocale> #include <QLocale>
#include <QMessageBox> #include <QMessageBox>
#include <QSettings>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QTimer> #include <QTimer>
@ -50,6 +51,13 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
#ifndef USE_UPNP #ifndef USE_UPNP
ui->mapPortUpnp->setEnabled(false); ui->mapPortUpnp->setEnabled(false);
#endif #endif
#ifndef USE_NATPMP
ui->mapPortNatpmp->setEnabled(false);
#endif
connect(this, &QDialog::accepted, [this](){
QSettings settings;
model->node().mapPort(settings.value("fUseUPnP").toBool(), settings.value("fUseNatpmp").toBool());
});
ui->proxyIp->setEnabled(false); ui->proxyIp->setEnabled(false);
ui->proxyPort->setEnabled(false); ui->proxyPort->setEnabled(false);
@ -214,6 +222,7 @@ void OptionsDialog::setMapper()
/* Network */ /* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp);
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);

View file

@ -13,11 +13,12 @@
#include <qt/guiutil.h> #include <qt/guiutil.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS #include <mapport.h>
#include <net.h> #include <net.h>
#include <netbase.h> #include <netbase.h>
#include <txdb.h> // for -dbcache defaults #include <txdb.h> // for -dbcache defaults
#include <util/string.h> #include <util/string.h>
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <QDebug> #include <QDebug>
#include <QSettings> #include <QSettings>
@ -123,6 +124,13 @@ void OptionsModel::Init(bool resetSettings)
if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp"); 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")) if (!settings.contains("fListen"))
settings.setValue("fListen", DEFAULT_LISTEN); settings.setValue("fListen", DEFAULT_LISTEN);
if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
@ -282,7 +290,13 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return settings.value("fUseUPnP"); return settings.value("fUseUPnP");
#else #else
return false; return false;
#endif #endif // USE_UPNP
case MapPortNatpmp:
#ifdef USE_NATPMP
return settings.value("fUseNatpmp");
#else
return false;
#endif // USE_NATPMP
case MinimizeOnClose: case MinimizeOnClose:
return fMinimizeOnClose; return fMinimizeOnClose;
@ -354,7 +368,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break; break;
case MapPortUPnP: // core option - can be changed on-the-fly case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool()); settings.setValue("fUseUPnP", value.toBool());
node().mapPort(value.toBool()); break;
case MapPortNatpmp: // core option - can be changed on-the-fly
settings.setValue("fUseNatpmp", value.toBool());
break; break;
case MinimizeOnClose: case MinimizeOnClose:
fMinimizeOnClose = value.toBool(); fMinimizeOnClose = value.toBool();

View file

@ -48,6 +48,7 @@ public:
ShowTrayIcon, // bool ShowTrayIcon, // bool
MinimizeToTray, // bool MinimizeToTray, // bool
MapPortUPnP, // bool MapPortUPnP, // bool
MapPortNatpmp, // bool
MinimizeOnClose, // bool MinimizeOnClose, // bool
ProxyUse, // bool ProxyUse, // bool
ProxyIP, // QString ProxyIP, // QString

View file

@ -362,6 +362,7 @@ def initialize_datadir(dirname, n, chain):
f.write("listenonion=0\n") f.write("listenonion=0\n")
f.write("printtoconsole=0\n") f.write("printtoconsole=0\n")
f.write("upnp=0\n") f.write("upnp=0\n")
f.write("natpmp=0\n")
f.write("shrinkdebugfile=0\n") f.write("shrinkdebugfile=0\n")
os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)