Skip to content

Commit

Permalink
net: Add libnatpmp support
Browse files Browse the repository at this point in the history
  • Loading branch information
hebasto committed Jan 7, 2021
1 parent 58e8364 commit a8d9f27
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 13 deletions.
2 changes: 1 addition & 1 deletion ci/test/00_setup_env_native_qt5.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
47 changes: 47 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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]])
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"
Expand Down
6 changes: 5 additions & 1 deletion depends/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ NO_QR ?=
NO_WALLET ?=
NO_ZMQ ?=
NO_UPNP ?=
NO_NATPMP ?=
MULTIPROCESS ?=
FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources

Expand Down Expand Up @@ -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_),)
Expand Down Expand Up @@ -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)|' \
$< > $@
Expand Down
4 changes: 4 additions & 0 deletions depends/config.site.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions depends/packages/libnatpmp.mk
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions depends/packages/packages.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.bench.include
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.qttest.include
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
116 changes: 111 additions & 5 deletions src/mapport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@
#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
#endif // USE_UPNP

#include <atomic>
#include <cassert>
Expand All @@ -32,7 +37,7 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"
#include <string>
#include <thread>

#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};
Expand All @@ -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;
Expand Down Expand Up @@ -111,6 +216,7 @@ static bool ProcessUpnp()

return ret;
}
#endif // USE_UPNP

static void ThreadMapPort()
{
Expand All @@ -123,7 +229,7 @@ void StartThreadMapPort()
{
if (!g_mapport_thread.joinable()) {
assert(!g_mapport_interrupt);
g_mapport_thread = std::thread((std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort)));
g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort));
}
}

Expand Down Expand Up @@ -167,7 +273,7 @@ void StopMapPort()
}
}

#else
#else // #if defined(USE_NATPMP) || defined(USE_UPNP)
void StartMapPort(bool use_upnp)
{
// Intentionally left blank.
Expand All @@ -180,4 +286,4 @@ void StopMapPort()
{
// Intentionally left blank.
}
#endif
#endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
1 change: 1 addition & 0 deletions src/mapport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a8d9f27

Please sign in to comment.