added port forwarding (upnp and natpmp) (#905)

* UPnP port redirection is supported.

* compile fixes

* compile fix

* optimize reconnection code

* prepared upnp threadification to counter main loop stall at supernode change

* NAT-PMP port forwarding support, temporarily merge codes to resolve conflicts.

* make compile fix

* prepared threadification in more detail

* adopted threadification to new file setup

* cleaned up

* renamed functions and data structures

* fixes

* differentiated between miniupnp and natpmp and added corresponding lib support to makefile

* name

* commented unused header includes

* comments

* license

* fixes

* fixes

* fixes

* NAT-PMP is already available.

* added CLI parameter to disable port forwarding if required

* preliminary made use of multithreading

* adjusted log level

* added man page documentation

* def'ed conf

* made pmpnat adjustments

Co-authored-by: fengdaolong <fengdaolong@gmail.com>
This commit is contained in:
Logan oos Even 2021-12-23 12:27:55 +01:00 committed by GitHub
parent a1facf0f3f
commit e6e8cb038a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 776 additions and 11 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ tools/n2n-decode
tools/n2n-keygen tools/n2n-keygen
build build
.idea .idea
.vscode
cmake-build-default cmake-build-default
packages/debian/debian/changelog packages/debian/debian/changelog
packages/debian/debian/control packages/debian/debian/control

8
.gitmodules vendored Normal file
View File

@ -0,0 +1,8 @@
[submodule "thirdparty/miniupnp"]
path = thirdparty/miniupnp
url = https://github.com/miniupnp/miniupnp.git
ignore = dirty
[submodule "thirdparty/libnatpmp"]
path = thirdparty/libnatpmp
url = https://github.com/miniupnp/libnatpmp.git
ignore = dirty

View File

@ -30,6 +30,8 @@ add_definitions(-DCMAKE_BUILD)
add_definitions(-DPACKAGE_OSNAME="${CMAKE_SYSTEM_NAME}") add_definitions(-DPACKAGE_OSNAME="${CMAKE_SYSTEM_NAME}")
add_definitions(-DPACKAGE_VERSION="${PACKAGE_VERSION}") add_definitions(-DPACKAGE_VERSION="${PACKAGE_VERSION}")
# third-party directory
set(THIRD_PARTY_DIR ${CMAKE_SOURCE_DIR}/thirdparty)
# Build information # Build information
OPTION(BUILD_SHARED_LIBS "BUILD Shared Library" OFF) OPTION(BUILD_SHARED_LIBS "BUILD Shared Library" OFF)
@ -39,6 +41,7 @@ OPTION(N2N_OPTION_USE_PTHREAD "USE PTHREAD Library" ON)
OPTION(N2N_OPTION_USE_OPENSSL "USE OPENSSL Library" OFF) OPTION(N2N_OPTION_USE_OPENSSL "USE OPENSSL Library" OFF)
OPTION(N2N_OPTION_USE_PCAPLIB "USE PCAP Library" OFF) OPTION(N2N_OPTION_USE_PCAPLIB "USE PCAP Library" OFF)
OPTION(N2N_OPTION_USE_ZSTD "USE ZSTD Library" OFF) OPTION(N2N_OPTION_USE_ZSTD "USE ZSTD Library" OFF)
OPTION(N2N_OPTION_USE_PORTMAPPING "USE MINIUPNP and NATPMP Libraries" ON)
if(N2N_OPTION_USE_PTHREAD) if(N2N_OPTION_USE_PTHREAD)
@ -75,6 +78,14 @@ if(N2N_OPTION_USE_ZSTD)
add_definitions(-DN2N_HAVE_ZSTD) add_definitions(-DN2N_HAVE_ZSTD)
endif(N2N_OPTION_USE_ZSTD) endif(N2N_OPTION_USE_ZSTD)
if(N2N_OPTION_USE_PORTMAPPING)
ADD_DEFINITIONS("-DHAVE_MINIUPNP")
ADD_DEFINITIONS("-DHAVE_NATPMP")
include_directories(${THIRD_PARTY_DIR}/miniupnp/miniupnpc/include)
include_directories(${PROJECT_BINARY_DIR}/lib_miniupnpc)
include_directories(${THIRD_PARTY_DIR}/libnatpmp)
endif(N2N_OPTION_USE_PORTMAPPING)
if(NOT DEFINED CMAKE_BUILD_TYPE) if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE None) set(CMAKE_BUILD_TYPE None)
endif(NOT DEFINED CMAKE_BUILD_TYPE) endif(NOT DEFINED CMAKE_BUILD_TYPE)
@ -139,7 +150,8 @@ add_library(n2n STATIC
src/network_traffic_filter.c src/network_traffic_filter.c
src/sn_selection.c src/sn_selection.c
src/auth.c src/auth.c
src/curve25519.c) src/curve25519.c
src/n2n_port_mapping.c)
if(N2N_OPTION_USE_PTHREAD) if(N2N_OPTION_USE_PTHREAD)
@ -155,6 +167,14 @@ if(N2N_OPTION_USE_ZSTD)
target_link_libraries(n2n zstd) target_link_libraries(n2n zstd)
endif(N2N_OPTION_USE_ZSTD) endif(N2N_OPTION_USE_ZSTD)
if(N2N_OPTION_USE_PORTMAPPING)
add_subdirectory(${THIRD_PARTY_DIR}/miniupnp/miniupnpc lib_miniupnpc)
link_directories(${PROJECT_BINARY_DIR}/lib_miniupnpc)
add_subdirectory(${THIRD_PARTY_DIR}/libnatpmp libnatpmp)
link_directories(${PROJECT_BINARY_DIR}/libnatpmp)
target_link_libraries(n2n libminiupnpc-static natpmp)
endif(N2N_OPTION_USE_PORTMAPPING)
if(DEFINED WIN32) if(DEFINED WIN32)
add_library(edge_utils_win32 src/edge_utils_win32.c) add_library(edge_utils_win32 src/edge_utils_win32.c)
add_subdirectory(win32) add_subdirectory(win32)

View File

@ -58,6 +58,20 @@ if test "x$with_openssl" != xno; then
fi fi
fi fi
AC_CHECK_LIB([miniupnpc], [upnpDiscover], miniupnp=true)
if test x$miniupnp != x; then
AC_DEFINE([HAVE_MINIUPNP], [], [Have miniupnp library])
N2N_LIBS="-lminiupnpc ${N2N_LIBS}"
fi
AC_CHECK_LIB([natpmp], [initnatpmp], natpmp=true)
if test x$natpmp != x; then
AC_DEFINE([HAVE_NATPMP], [], [Have natpmp library])
N2N_LIBS="-lnatpmp ${N2N_LIBS}"
fi
AC_CHECK_LIB([pcap], [pcap_open_live], pcap=true) AC_CHECK_LIB([pcap], [pcap_open_live], pcap=true)
if test x$pcap != x; then if test x$pcap != x; then

5
edge.8
View File

@ -109,6 +109,11 @@ defaults to load-based selection strategy if not provided.
\fB\-\-select-mac\fR \fB\-\-select-mac\fR
select supernode by MAC address if several to choose from (federation), select supernode by MAC address if several to choose from (federation),
lowest MAC address first. lowest MAC address first.
.TP
\fB\-\-no-port-forwarding\fR
disables the default behavior of trying to have the edge's port forwarded
through a router eventually supporting it (only if compiled with miniupnp
and/or natpmp library support).
.SH TAP DEVICE AND OVERLAY NETWORK CONFIGURATION .SH TAP DEVICE AND OVERLAY NETWORK CONFIGURATION
.TP .TP
\fB\-a \fR[\fImode\fR]<\fIip\fR>[\fI/n\fR] \fB\-a \fR[\fImode\fR]<\fIip\fR>[\fI/n\fR]

View File

@ -158,6 +158,10 @@
#include "network_traffic_filter.h" #include "network_traffic_filter.h"
#include "auth.h" #include "auth.h"
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
#include "n2n_port_mapping.h"
#endif // HAVE_MINIUPNP || HAVE_NATPMP
/* ************************************** */ /* ************************************** */
#include "header_encryption.h" #include "header_encryption.h"

View File

@ -0,0 +1,21 @@
#ifndef _N2N_PORT_MAPPING_H_
#define _N2N_PORT_MAPPING_H_
#include <stdint.h>
#ifdef HAVE_MINIUPNP
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
#endif // HAVE_MINIUPNP
#ifdef HAVE_NATPMP
#include "natpmp.h"
#endif // HAVE_NATPMP
void n2n_chg_port_mapping (struct n2n_edge *eee, const uint16_t port);
#endif // _N2N_PORT_MAPPING_H_

View File

@ -637,6 +637,21 @@ typedef struct n2n_resolve_parameter {
/* *************************************************** */ /* *************************************************** */
// structure to hold port mapping thread's parameters
typedef struct n2n_port_map_parameter {
#ifdef HAVE_PTHREAD
pthread_t id; /* thread id */
pthread_mutex_t access; /* mutex for shared access */
#endif
uint16_t mgmt_port;
uint16_t mapped_port;
uint16_t new_port; /* REVISIT: remove with management port subscriptions */
} n2n_port_map_parameter_t;
/* *************************************************** */
typedef struct n2n_edge_conf { typedef struct n2n_edge_conf {
struct peer_info *supernodes; /**< List of supernodes */ struct peer_info *supernodes; /**< List of supernodes */
n2n_route_t *routes; /**< Networks to route through n2n */ n2n_route_t *routes; /**< Networks to route through n2n */
@ -676,6 +691,7 @@ typedef struct n2n_edge_conf {
uint8_t sn_selection_strategy; /**< encodes currently chosen supernode selection strategy. */ uint8_t sn_selection_strategy; /**< encodes currently chosen supernode selection strategy. */
uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */ uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */
uint64_t mgmt_password_hash; /**< contains hash of managament port password. */ uint64_t mgmt_password_hash; /**< contains hash of managament port password. */
uint8_t port_forwarding; /**< indicates if port forwarding UPNP/PMP is enabled */
} n2n_edge_conf_t; } n2n_edge_conf_t;
@ -733,6 +749,8 @@ struct n2n_edge {
n2n_resolve_parameter_t *resolve_parameter; /**< Pointer to name resolver's parameter block */ n2n_resolve_parameter_t *resolve_parameter; /**< Pointer to name resolver's parameter block */
uint8_t resolution_request; /**< Flag an immediate DNS resolution request */ uint8_t resolution_request; /**< Flag an immediate DNS resolution request */
n2n_port_map_parameter_t *port_map_parameter; /**< Pointer to port mapping thread's parameter block */
n2n_tuntap_priv_config_t tuntap_priv_conf; /**< Tuntap config */ n2n_tuntap_priv_config_t tuntap_priv_conf; /**< Tuntap config */
network_traffic_filter_t *network_traffic_filter; network_traffic_filter_t *network_traffic_filter;

View File

@ -180,7 +180,10 @@ static void help (int level) {
"\n " "\n "
"[-e <preferred local IP address>] [-S<level of solitude>]" "[-e <preferred local IP address>] [-S<level of solitude>]"
"\n " "\n "
"[--select-rtt]" "[--select-rtt] "
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
"[--no-port-forwarding] "
#endif // HAVE_MINIUPNP || HAVE_NATPMP
"\n\n tap device and " "\n\n tap device and "
"[-a [static:|dhcp:]<tap IP address>[/<cidr suffix>]] " "[-a [static:|dhcp:]<tap IP address>[/<cidr suffix>]] "
"\n overlay network " "\n overlay network "
@ -233,6 +236,9 @@ static void help (int level) {
"\n [-E] accept multicast MAC addresses" "\n [-E] accept multicast MAC addresses"
"\n [--select-rtt] select supernode by round trip time" "\n [--select-rtt] select supernode by round trip time"
"\n [--select-mac] select supernode by MAC address" "\n [--select-mac] select supernode by MAC address"
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
"\n [--no-port-forwarding] disable UPnP/PMP port forwarding"
#endif // HAVE_MINIUPNP || HAVE_NATPMP
#ifndef WIN32 #ifndef WIN32
"\n [-f] do not fork but run in foreground" "\n [-f] do not fork but run in foreground"
#endif #endif
@ -294,6 +300,10 @@ static void help (int level) {
printf("--select-rtt | supernode selection based on round trip time\n" printf("--select-rtt | supernode selection based on round trip time\n"
"--select-mac | supernode selection based on MAC address (default:\n" "--select-mac | supernode selection based on MAC address (default:\n"
" | by load)\n"); " | by load)\n");
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
printf("--no-port-... | disable UPnP/PMP port forwarding\n"
"...forwarding | \n");
#endif // HAVE_MINIUPNP || HAVE_NATPMP
printf ("\n"); printf ("\n");
printf (" TAP DEVICE AND OVERLAY NETWORK CONFIGURATION\n"); printf (" TAP DEVICE AND OVERLAY NETWORK CONFIGURATION\n");
@ -752,6 +762,12 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e
break; break;
} }
case '}': /* disable port forwarding */ {
conf->port_forwarding = 0;
break;
}
case 'h': /* quick reference */ { case 'h': /* quick reference */ {
return 2; return 2;
} }
@ -808,6 +824,7 @@ static const struct option long_options[] =
{ "select-rtt", no_argument, NULL, '[' }, /* '[' rtt selection strategy */ { "select-rtt", no_argument, NULL, '[' }, /* '[' rtt selection strategy */
{ "select-mac", no_argument, NULL, ']' }, /* ']' mac selection strategy */ { "select-mac", no_argument, NULL, ']' }, /* ']' mac selection strategy */
{ "management-password", required_argument, NULL, '{' }, /* '{' management port password */ { "management-password", required_argument, NULL, '{' }, /* '{' management port password */
{ "no-port-forwarding", no_argument, NULL, '}' }, /* '}' disable port forwarding */
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };

View File

@ -30,6 +30,9 @@ int resolve_create_thread (n2n_resolve_parameter_t **param, struct peer_info *sn
int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, time_t now); int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, time_t now);
int resolve_cancel_thread (n2n_resolve_parameter_t *param); int resolve_cancel_thread (n2n_resolve_parameter_t *param);
int port_map_create_thread (n2n_port_map_parameter_t **param, uint16_t mgmt_port);
int port_map_cancel_thread (n2n_port_map_parameter_t *param);
static const char * supernode_ip (const n2n_edge_t * eee); static const char * supernode_ip (const n2n_edge_t * eee);
static void send_register (n2n_edge_t *eee, const n2n_sock_t *remote_peer, const n2n_mac_t peer_mac, n2n_cookie_t cookie); static void send_register (n2n_edge_t *eee, const n2n_sock_t *remote_peer, const n2n_mac_t peer_mac, n2n_cookie_t cookie);
@ -329,6 +332,12 @@ int supernode_connect (n2n_edge_t *eee) {
eee->cb.sock_opened(eee); eee->cb.sock_opened(eee);
} }
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
if(eee->conf.port_forwarding)
// REVISIT: replace with mgmt port notification to listener for mgmt port
// subscription support
n2n_chg_port_mapping(eee, eee->conf.preferred_sock.port);
#endif // HAVE_MINIUPNP || HAVE_NATPMP
return 0; return 0;
} }
@ -482,7 +491,12 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) {
if(resolve_create_thread(&(eee->resolve_parameter), eee->conf.supernodes) == 0) { if(resolve_create_thread(&(eee->resolve_parameter), eee->conf.supernodes) == 0) {
traceEvent(TRACE_NORMAL, "successfully created resolver thread"); traceEvent(TRACE_NORMAL, "successfully created resolver thread");
} }
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
if(eee->conf.port_forwarding)
if(port_map_create_thread(&eee->port_map_parameter, eee->conf.mgmt_port) == 0) {
traceEvent(TRACE_NORMAL, "successfully created port mapping thread");
}
#endif // HAVE_MINIUPNP || HAVE_NATPMP
eee->network_traffic_filter = create_network_traffic_filter(); eee->network_traffic_filter = create_network_traffic_filter();
network_traffic_filter_add_rule(eee->network_traffic_filter, eee->conf.network_traffic_filter_rules); network_traffic_filter_add_rule(eee->network_traffic_filter, eee->conf.network_traffic_filter_rules);
@ -1539,7 +1553,6 @@ void update_supernode_reg (n2n_edge_t * eee, time_t now) {
sn_selection_sort(&(eee->conf.supernodes)); sn_selection_sort(&(eee->conf.supernodes));
eee->curr_sn = eee->conf.supernodes; eee->curr_sn = eee->conf.supernodes;
traceEvent(TRACE_WARNING, "supernode not responding, now trying [%s]", supernode_ip(eee)); traceEvent(TRACE_WARNING, "supernode not responding, now trying [%s]", supernode_ip(eee));
supernode_connect(eee);
reset_sup_attempts(eee); reset_sup_attempts(eee);
// trigger out-of-schedule DNS resolution // trigger out-of-schedule DNS resolution
eee->resolution_request = 1; eee->resolution_request = 1;
@ -1567,9 +1580,9 @@ void update_supernode_reg (n2n_edge_t * eee, time_t now) {
} }
} }
supernode_connect(eee);
traceEvent(TRACE_DEBUG, "reconnected to supernode"); traceEvent(TRACE_DEBUG, "reconnected to supernode");
} }
supernode_connect(eee);
} else { } else {
--(eee->sup_attempts); --(eee->sup_attempts);
@ -3182,9 +3195,9 @@ int run_edge_loop (n2n_edge_t *eee) {
WaitForSingleObject(tun_read_thread, INFINITE); WaitForSingleObject(tun_read_thread, INFINITE);
#endif #endif
closesocket(eee->sock); supernode_disconnect(eee);
return(0); return 0;
} }
/* ************************************** */ /* ************************************** */
@ -3193,7 +3206,10 @@ int run_edge_loop (n2n_edge_t *eee) {
void edge_term (n2n_edge_t * eee) { void edge_term (n2n_edge_t * eee) {
resolve_cancel_thread(eee->resolve_parameter); resolve_cancel_thread(eee->resolve_parameter);
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
if(eee->conf.port_forwarding)
port_map_cancel_thread(eee->port_map_parameter);
#endif // HAVE_MINIUPNP || HAVE_NATPMP
if(eee->sock >= 0) if(eee->sock >= 0)
closesocket(eee->sock); closesocket(eee->sock);
@ -3712,6 +3728,10 @@ void edge_init_conf_defaults (n2n_edge_conf_t *conf) {
free(tmp_string); free(tmp_string);
} }
#if defined(HAVE_MINIUPNP) || defined(HAVE_NATPMP)
conf->port_forwarding = 1;
#endif // HAVE_MINIUPNP || HAVE_NATPMP
conf->sn_selection_strategy = SN_SELECTION_STRATEGY_LOAD; conf->sn_selection_strategy = SN_SELECTION_STRATEGY_LOAD;
conf->metric = 0; conf->metric = 0;
} }

635
src/n2n_port_mapping.c Normal file
View File

@ -0,0 +1,635 @@
/**
* (C) 2007-21 - ntop.org and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not see see <http://www.gnu.org/licenses/>
*
*/
// This file contains code taken from MiniUPnPc and natpmp found at
// https://github.com/miniupnp/miniupnp/ or
// https://github.com/miniupnp/natpmp/ respectively
// both as of October 2021
/**
* MiniUPnPc
* Copyright (c) 2005-2021, Thomas BERNARD
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "n2n.h"
#ifdef HAVE_MINIUPNP
#if 0 /* unused code */
/* protofix() checks if protocol is "UDP" or "TCP"
* returns NULL if not */
static const char *protofix (const char *proto) {
int i, b;
const char proto_tcp[4] = {'T', 'C', 'P', 0};
const char proto_udp[4] = {'U', 'D', 'P', 0};
for(i = 0, b = 1; i < 4; i++)
b = b && ((proto[i] == proto_tcp[i]) || (proto[i] == (proto_tcp[i] | 32)));
if(b)
return proto;
for(i = 0, b = 1; i < 4; i++)
b = b && ((proto[i] == proto_udp[i]) || (proto[i] == (proto_udp[i] | 32)));
if(b)
return proto;
return NULL;
}
#endif // unused code
static int n2n_UPNP_GetValidIGD (struct UPNPUrls *urls, struct IGDdatas *data, char *lanaddr, char *externaladdr) {
struct UPNPDev *devlist = NULL;
struct UPNPDev *device = NULL;
int delay = 2000;
const char *multicastif = NULL;
const char *minissdpdpath = NULL;
int localport = UPNP_LOCAL_PORT_ANY;
int ipv6 = 0;
unsigned char ttl = 2; /* defaulting to 2 */
int error = 0;
int ret = 0;
devlist = upnpDiscover(delay, multicastif, minissdpdpath, localport, ipv6, ttl, &error);
if((error != UPNPDISCOVER_SUCCESS) || (devlist == NULL) ) {
traceEvent(TRACE_WARNING, "no IGD UPnP device found on the network");
return -1;
}
traceEvent(TRACE_INFO, "list of UPnP devices found on the network:");
for(device = devlist; device; device = device->pNext) {
traceEvent(TRACE_INFO, " desc: %s", device->descURL);
traceEvent(TRACE_INFO, " st: %s", device->st);
traceEvent(TRACE_INFO, " usn: %s", device->usn);
}
ret = UPNP_GetValidIGD(devlist, urls, data, lanaddr, N2N_NETMASK_STR_SIZE);
if(ret == 0) {
traceEvent(TRACE_WARNING, "UPnP get valid IGD failed, code %d (%s)", ret, strupnperror(ret));
freeUPNPDevlist(devlist);
devlist = NULL;
return -1;
}
freeUPNPDevlist(devlist);
devlist = NULL;
traceEvent(TRACE_INFO, "UPnP found valid IGD: %s", urls->controlURL);
ret = UPNP_GetExternalIPAddress(urls->controlURL,
data->first.servicetype,
externaladdr);
if(ret != UPNPCOMMAND_SUCCESS) {
traceEvent(TRACE_WARNING, "UPnP get external ip address failed, code %d (%s)", ret, strupnperror(ret));
}
return 0;
}
#if 0 /* unused code */
static int n2n_upnp_get_port_mapping (struct UPNPUrls *urls, const struct IGDdatas *data, const uint16_t port, const char *proto,
char *lanaddr, char *lanport, char *description, char *enabled, char *duration) {
int errorcode = 0;
// struct UPNPUrls urls;
// struct IGDdatas data;
// char lanaddr[N2N_NETMASK_STR_SIZE] = {'\0'};
// char lanport[6] = {'\0'};
// char externaladdr[N2N_NETMASK_STR_SIZE] = {'\0'};
char externalport[6] = {'\0'};
// char description[64] = {'\0'};
// char enabled[16] = {'\0'};
// char duration[16] = {'\0'};
int ret = 0;
proto = protofix(proto);
if(!proto) {
traceEvent(TRACE_ERROR, "invalid protocol");
errorcode = -1;
goto end;
}
snprintf(externalport, sizeof(externalport), "%d", port);
ret = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
data->first.servicetype,
externalport, proto, NULL,
lanaddr, lanport, description,
enabled, duration);
if(ret != UPNPCOMMAND_SUCCESS) {
traceEvent(TRACE_WARNING, "UPNP_GetSpecificPortMappingEntry() failed, code %d (%s)", ret, strupnperror(ret));
errorcode = -1;
goto end;
}
end:
FreeUPNPUrls(urls);
return errorcode;
}
#endif // unused code
static int n2n_upnp_set_port_mapping (const uint16_t port) {
int errorcode = 0;
struct UPNPUrls urls;
struct IGDdatas data;
char lanaddr[N2N_NETMASK_STR_SIZE] = {'\0'};
char lanport[6] = {'\0'};
char externaladdr[N2N_NETMASK_STR_SIZE] = {'\0'};
char externalport[6] = {'\0'};
int ret = 0;
if(port == 0) {
traceEvent(TRACE_ERROR, "invalid port");
errorcode = -1;
return errorcode;
}
snprintf(lanport, sizeof(lanport), "%d", port);
memcpy(externalport, lanport, sizeof(externalport));
ret = n2n_UPNP_GetValidIGD(&urls, &data, lanaddr, externaladdr);
if(ret != 0) {
errorcode = -1;
return errorcode;
}
// TCP port
ret = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
externalport, lanport, lanaddr, "n2n-vpn",
"TCP", NULL, "0");
if(ret != UPNPCOMMAND_SUCCESS) {
traceEvent(TRACE_WARNING, "UPnP local TCP port %s mapping failed, code %d (%s)", lanport, ret, strupnperror(ret));
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "UPnP added TCP port mapping: %s:%s -> %s:%s", externaladdr, externalport, lanaddr, lanport);
// UDP port
ret = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
externalport, lanport, lanaddr, "n2n-vpn",
"UDP", NULL, "0");
if(ret != UPNPCOMMAND_SUCCESS) {
traceEvent(TRACE_WARNING, "UPnP local UDP port %s mapping failed, code %d (%s)", lanport, ret, strupnperror(ret));
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "UPnP added UDP port mapping: %s:%s -> %s:%s", externaladdr, externalport, lanaddr, lanport);
FreeUPNPUrls(&urls);
return errorcode;
}
static int n2n_upnp_del_port_mapping (const uint16_t port) {
int errorcode = 0;
struct UPNPUrls urls;
struct IGDdatas data;
char lanaddr[N2N_NETMASK_STR_SIZE] = {'\0'};
// char lanport[6] = {'\0'};
char externaladdr[N2N_NETMASK_STR_SIZE] = {'\0'};
char externalport[6] = {'\0'};
int ret = 0;
if(port == 0) {
traceEvent(TRACE_ERROR, "invalid port");
errorcode = -1;
return errorcode;
}
snprintf(externalport, sizeof(externalport), "%d", port);
ret = n2n_UPNP_GetValidIGD(&urls, &data, lanaddr, externaladdr);
if(ret != 0) {
errorcode = -1;
return errorcode;
}
// TCP port
ret = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, externalport, "TCP", NULL);
if(ret != UPNPCOMMAND_SUCCESS) {
traceEvent(TRACE_WARNING, "UPnP failed to delete TCP port mapping for %s:%s, code %d (%s)", externaladdr, externalport, ret, strupnperror(ret));
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "UPnP deleted TCP port mapping for %s:%s", externaladdr, externalport);
// UDP port
ret = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, externalport, "UDP", NULL);
if(ret != UPNPCOMMAND_SUCCESS) {
traceEvent(TRACE_WARNING, "UPnP failed to delete UDP port mapping for %s:%s, code %d (%s)", externaladdr, externalport, ret, strupnperror(ret));
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "UPnP deleted UDP port mapping for %s:%s", externaladdr, externalport);
FreeUPNPUrls(&urls);
return errorcode;
}
#endif // HAVE_MINIUPNP
// ----------------------------------------------------------------------------------------------------
#ifdef HAVE_NATPMP
static int n2n_natpmp_initialization (natpmp_t *natpmp, char *lanaddr, char *externaladdr) {
int errorcode = 0;
natpmpresp_t response;
int ret = 0;
int forcegw = 0;
in_addr_t gateway = 0;
struct in_addr gateway_in_use;
struct timeval timeout;
fd_set fds;
ret = initnatpmp(natpmp, forcegw, gateway);
if(ret != 0) {
traceEvent(TRACE_WARNING, "NAT-PMP failed to initialize, code %d", ret);
errorcode = -1;
return errorcode;
}
gateway_in_use.s_addr = natpmp->gateway;
traceEvent(TRACE_INFO, "NAT-PMP using gateway: %s", inet_ntoa(gateway_in_use));
ret = sendpublicaddressrequest(natpmp);
if(ret != 2) {
traceEvent(TRACE_WARNING, "NAT-PMP get external ip address failed, code %d", ret);
closenatpmp(natpmp);
errorcode = -1;
return errorcode;
}
do
{
FD_ZERO(&fds);
FD_SET(natpmp->s, &fds);
getnatpmprequesttimeout(natpmp, &timeout);
select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
ret = readnatpmpresponseorretry(natpmp, &response);
traceEvent(TRACE_INFO, "NAT-PMP read response returned %d (%s)", ret, ret == 0 ? "OK" : (ret == NATPMP_TRYAGAIN ? "TRY AGAIN" : "FAILED"));
} while (ret == NATPMP_TRYAGAIN);
if(response.type != NATPMP_RESPTYPE_PUBLICADDRESS) {
traceEvent(TRACE_WARNING, "NAT-PMP invalid response type %u", response.type);
closenatpmp(natpmp);
errorcode = -1;
return errorcode;
}
snprintf(externaladdr, N2N_NETMASK_STR_SIZE, "%s", inet_ntoa(response.pnu.publicaddress.addr));
snprintf(lanaddr, N2N_NETMASK_STR_SIZE, "localhost");
return errorcode;
}
static int n2n_natpmp_port_mapping_request (natpmp_t *natpmp,
const uint16_t port,
const int protocol /* NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP */,
const int method /* set:1 del:0 */) {
int errorcode = 0;
natpmpresp_t response;
int ret = 0;
uint16_t lanport = 0;
uint16_t externalport = 0;
struct timeval timeout;
fd_set fds;
if(port == 0) {
traceEvent(TRACE_ERROR, "invalid port");
errorcode = -1;
return errorcode;
}
lanport = port;
externalport = port;
ret = sendnewportmappingrequest(natpmp, protocol, lanport, externalport, (method ? 31104000 /* lifetime 360 days*/ : 0));
if(ret != 12) {
traceEvent(TRACE_WARNING, "NAT-PMP new port mapping request failed, code %d", ret);
errorcode = -1;
return errorcode;
}
do
{
FD_ZERO(&fds);
FD_SET(natpmp->s, &fds);
getnatpmprequesttimeout(natpmp, &timeout);
select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
ret = readnatpmpresponseorretry(natpmp, &response);
traceEvent(TRACE_INFO, "NAT-PMP read response returned %d (%s)", ret, ret == 0 ? "OK" : (ret == NATPMP_TRYAGAIN ? "TRY AGAIN" : "FAILED"));
} while (ret == NATPMP_TRYAGAIN);
if(!((response.type == NATPMP_RESPTYPE_TCPPORTMAPPING) || (response.type == NATPMP_RESPTYPE_UDPPORTMAPPING))) {
traceEvent(TRACE_WARNING, "NAT-PMP invalid response type %u", response.type);
errorcode = -1;
return errorcode;
}
return errorcode;
}
static int n2n_natpmp_set_port_mapping (const uint16_t port) {
int errorcode = 0;
natpmp_t natpmp;
int ret = 0;
char lanaddr[N2N_NETMASK_STR_SIZE] = {'\0'};
uint16_t lanport = 0;
char externaladdr[N2N_NETMASK_STR_SIZE] = {'\0'};
uint16_t externalport = 0;
lanport = port;
externalport = port;
ret = n2n_natpmp_initialization(&natpmp, lanaddr, externaladdr);
if(ret != 0) {
errorcode = -1;
return errorcode;
}
// TCP port
ret = n2n_natpmp_port_mapping_request(&natpmp, port, NATPMP_PROTOCOL_TCP, 1);
if(ret != 0) {
traceEvent(TRACE_WARNING, "NAT-PMP local TCP port %hu mapping failed", lanport);
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "NAT-PMP added TCP port mapping: %s:%hu -> %s:%hu", externaladdr, externalport, lanaddr, lanport);
// UDP port
ret = n2n_natpmp_port_mapping_request(&natpmp, port, NATPMP_PROTOCOL_UDP, 1);
if(ret != 0) {
traceEvent(TRACE_WARNING, "NAT-PMP local UDP port %hu mapping failed", lanport);
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "NAT-PMP added UDP port mapping: %s:%hu -> %s:%hu", externaladdr, externalport, lanaddr, lanport);
closenatpmp(&natpmp);
return errorcode;
}
static int n2n_natpmp_del_port_mapping (const uint16_t port) {
int errorcode = 0;
natpmp_t natpmp;
int ret = 0;
char lanaddr[N2N_NETMASK_STR_SIZE] = {'\0'};
// uint16_t lanport = 0;
char externaladdr[N2N_NETMASK_STR_SIZE] = {'\0'};
uint16_t externalport = 0;
// lanport = port;
externalport = port;
ret = n2n_natpmp_initialization(&natpmp, lanaddr, externaladdr);
if(ret != 0) {
errorcode = -1;
return errorcode;
}
// TCP port
ret = n2n_natpmp_port_mapping_request(&natpmp, port, NATPMP_PROTOCOL_TCP, 0);
if(ret != 0) {
traceEvent(TRACE_WARNING, "NAT-PMP failed to delete TCP port mapping for %s:%hu", externaladdr, externalport);
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "NAT-PMP deleted TCP port mapping for %s:%hu", externaladdr, externalport);
// UDP port
ret = n2n_natpmp_port_mapping_request(&natpmp, port, NATPMP_PROTOCOL_UDP, 0);
if(ret != 0) {
traceEvent(TRACE_WARNING, "NAT-PMP failed to delete UDP port mapping for %s:%hu", externaladdr, externalport);
errorcode = -1;
} else
traceEvent(TRACE_NORMAL, "NAT-PMP deleted UDP port mapping for %s:%hu", externaladdr, externalport);
closenatpmp(&natpmp);
return errorcode;
}
#endif // HAVE_NATPMP
// ----------------------------------------------------------------------------------------------------
static void n2n_set_port_mapping (const uint16_t port) {
#ifdef HAVE_NATPMP
// since the NAT-PMP protocol is more concise than UPnP, NAT-PMP is preferred.
if(n2n_natpmp_set_port_mapping(port))
#endif // HAVE_NATPMP
{
#ifdef HAVE_MINIUPNP
n2n_upnp_set_port_mapping(port);
#endif // HAVE_MINIUPNP
}
}
static void n2n_del_port_mapping (const uint16_t port) {
#ifdef HAVE_NATPMP
if(n2n_natpmp_del_port_mapping(port))
#endif // HAVE_NATPMP
{
#ifdef HAVE_MINIUPNP
n2n_upnp_del_port_mapping(port);
#endif // HAVE_MINIUPNP
}
}
// static
// ----------------------------------------------------------------------------------------------------
// public
#ifdef HAVE_PTHREAD /* future management port subscriptions will deprecate the following temporary code */
void n2n_chg_port_mapping (struct n2n_edge *eee, uint16_t port) {
// write a port change request to param struct, it will be handled in the thread
pthread_mutex_lock(&eee->port_map_parameter->access);
eee->port_map_parameter->new_port = port;
pthread_mutex_unlock(&eee->port_map_parameter->access);
}
#endif
N2N_THREAD_RETURN_DATATYPE port_map_thread(N2N_THREAD_PARAMETER_DATATYPE p) {
#ifdef HAVE_PTHREAD /* future management port subscriptions will deprecate the following temporary code */
n2n_port_map_parameter_t *param = (n2n_port_map_parameter_t*)p;
while(1) {
sleep(2);
pthread_mutex_lock(&param->access);
if(param->mapped_port != param->new_port) {
if(param->mapped_port)
n2n_del_port_mapping(param->mapped_port);
if(param->new_port)
n2n_set_port_mapping(param->new_port);
param->mapped_port = param->new_port;
}
pthread_mutex_unlock(&param->access);
}
#endif
#if 0 /* to be used with future management port subscriptions */
n2n_port_map_parameter_t *param = (n2n_port_map_parameter_t*)p;
SOCKET socket_fd;
fd_set socket_mask;
struct timeval wait_time;
int ret = 0;
char udp_buf[N2N_PKT_BUF_SIZE];
ssize_t msg_len;
uint32_t addr_tmp = htonl(INADDR_LOOPBACK);
n2n_sock_t sock_tmp;
struct sockaddr_in sock;
socklen_t sock_len;
// open a new socket ...
socket_fd = open_socket(0 /* no specific port */, INADDR_LOOPBACK, 0 /* UDP */);
if(socket_fd < 0) {
traceEvent(TRACE_ERROR, "port_map_thread failed to open a socket to management port");
return 0;
}
// ... and connect to local mgmt port
sock_tmp.family = AF_INET;
memcpy(&sock_tmp.addr.v4, &addr_tmp, IPV4_SIZE);
sock_tmp.port = param->mgmt_port;
sock_len = sizeof(sock);
fill_sockaddr((struct sockaddr*)&sock, sock_len, &sock_tmp);
connect(socket_fd, (struct sockaddr*)&sock, sock_len);
// prepare a subscription request in 'udp_buf' of length 'msg_len'
// !!! dummy
udp_buf[0] = '\n';
msg_len = 1;
send(socket_fd, udp_buf, msg_len, 0 /*flags*/);
// note: 'msg_len' and 'sock' get re-used hereafter
while(1) {
FD_ZERO(&socket_mask);
FD_SET(socket_fd, &socket_mask);
wait_time.tv_sec = SOCKET_TIMEOUT_INTERVAL_SECS;
wait_time.tv_usec = 0;
ret = select(socket_fd + 1, &socket_mask, NULL, NULL, &wait_time);
if(ret > 0) {
if(FD_ISSET(socket_fd, &socket_mask)) {
// get the data
sock_len = sizeof(sock);
msg_len = recv(socket_fd, udp_buf, N2N_PKT_BUF_SIZE, 0 /*flags*/);
// check message format, first message could be the still buffered answer to the subscription request
// !!!
if(1 /* !!! correct message format */) {
// delete an eventually previous port mapping
if(param->mapped_port)
n2n_del_port_mapping(param->mapped_port);
// extract port from message and set accordingly if valid
param->mapped_port = 0; // !!!
if(param->mapped_port)
n2n_set_port_mapping(param->mapped_port);
}
}
}
}
#endif
return 0; /* should never happen */
}
int port_map_create_thread (n2n_port_map_parameter_t **param, uint16_t mgmt_port) {
#ifdef HAVE_PTHREAD
int ret;
// create parameter structure
*param = (n2n_port_map_parameter_t*)calloc(1, sizeof(n2n_port_map_parameter_t));
if(*param) {
// initialize
(*param)->mgmt_port = mgmt_port;
} else {
traceEvent(TRACE_WARNING, "port_map_create_thread was unable to create parameter structure");
return -1;
}
// create thread
ret = pthread_create(&((*param)->id), NULL, port_map_thread, (void *)*param);
if(ret) {
traceEvent(TRACE_WARNING, "port_map_create_thread failed to create port mapping thread with error number %d", ret);
return -1;
}
#endif
return 0;
}
void port_map_cancel_thread (n2n_port_map_parameter_t *param) {
#ifdef HAVE_PTHREAD
pthread_cancel(param->id);
if(param->mapped_port)
n2n_del_port_mapping(param->mapped_port);
free(param);
#endif
}

1
thirdparty/libnatpmp vendored Submodule

@ -0,0 +1 @@
Subproject commit 4536032ae32268a45c073a4d5e91bbab4534773a

1
thirdparty/miniupnp vendored Submodule

@ -0,0 +1 @@
Subproject commit 77876aea5fd926ef25dce9af6b264c7b53d2b134