introduced n2n-portfwd tool (#1008)

* moved dev to version 3.1.1

* introduced n2n-portfwd tool

* moved port-forwarding source to tool

* adjusted tiny things, seemingly works with ./configure's  --enable-miniupnp  and  --enable-natpmp

* adjusted for Windows

* wished for better typing skills

* applied some finishing touch

* typo
This commit is contained in:
Logan oos Even 2022-06-05 18:05:40 +02:00 committed by GitHub
parent 99b6b6b66d
commit 51a3a71f95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 398 additions and 228 deletions

View File

@ -125,9 +125,6 @@ if(N2N_OPTION_USE_PORTMAPPING)
add_subdirectory(${THIRD_PARTY_DIR}/libnatpmp libnatpmp) add_subdirectory(${THIRD_PARTY_DIR}/libnatpmp libnatpmp)
link_directories(${PROJECT_BINARY_DIR}/libnatpmp) link_directories(${PROJECT_BINARY_DIR}/libnatpmp)
# Turns on the generic code for supporting either of the above
ADD_DEFINITIONS("-DHAVE_PORT_FORWARDING")
# TODO: # TODO:
# - this is the odd one out, is it needed? # - this is the odd one out, is it needed?
include_directories(${PROJECT_BINARY_DIR}/lib_miniupnpc) include_directories(${PROJECT_BINARY_DIR}/lib_miniupnpc)
@ -264,6 +261,8 @@ add_executable(n2n-keygen tools/n2n-keygen.c)
target_link_libraries(n2n-keygen n2n) target_link_libraries(n2n-keygen n2n)
add_executable(n2n-route tools/n2n-route.c) add_executable(n2n-route tools/n2n-route.c)
target_link_libraries(n2n-route n2n) target_link_libraries(n2n-route n2n)
add_executable(n2n-portfwd tools/n2n-portfwd.c)
target_link_libraries(n2n-portfwd n2n)
add_executable(tests-auth tools/tests-auth.c) add_executable(tests-auth tools/tests-auth.c)
target_link_libraries(tests-auth n2n) target_link_libraries(tests-auth n2n)

View File

@ -62,7 +62,6 @@ AS_IF([test "x$enable_miniupnp" != xno],
[AC_CHECK_LIB([miniupnpc], [upnpDiscover], [AC_CHECK_LIB([miniupnpc], [upnpDiscover],
[ [
AC_DEFINE([HAVE_MINIUPNP], [1], [Have miniupnp library]) AC_DEFINE([HAVE_MINIUPNP], [1], [Have miniupnp library])
AC_DEFINE([HAVE_PORT_FORWARDING],[1],[upnp libs are used])
N2N_LIBS="-lminiupnpc ${N2N_LIBS}" N2N_LIBS="-lminiupnpc ${N2N_LIBS}"
], ],
[AC_MSG_ERROR([miniupnp library not found])] [AC_MSG_ERROR([miniupnp library not found])]
@ -76,7 +75,6 @@ AS_IF([test "x$enable_natpmp" != xno],
[AC_CHECK_LIB([natpmp], [initnatpmp], [AC_CHECK_LIB([natpmp], [initnatpmp],
[ [
AC_DEFINE([HAVE_NATPMP], [1], [Have natpmp library]) AC_DEFINE([HAVE_NATPMP], [1], [Have natpmp library])
AC_DEFINE([HAVE_PORT_FORWARDING],[1],[upnp libs are used])
N2N_LIBS="-lnatpmp ${N2N_LIBS}" N2N_LIBS="-lnatpmp ${N2N_LIBS}"
], ],
[AC_MSG_ERROR([natpmp library not found])] [AC_MSG_ERROR([natpmp library not found])]

5
edge.8
View File

@ -109,11 +109,6 @@ 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,9 +158,7 @@
#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" #include "n2n_port_mapping.h"
#endif // HAVE_MINIUPNP || HAVE_NATPMP
#include "json.h" #include "json.h"

View File

@ -20,13 +20,12 @@
#ifndef _N2N_PORT_MAPPING_H_ #ifndef _N2N_PORT_MAPPING_H_
#define _N2N_PORT_MAPPING_H_ #define _N2N_PORT_MAPPING_H_
#ifdef HAVE_PORT_FORWARDING
#include <stdint.h> #include <stdint.h>
#ifdef HAVE_MINIUPNP #ifdef HAVE_MINIUPNP
#ifdef CMAKE_BUILD #ifdef CMAKE_BUILD
// CMAKE uses static linked lib as submodule which requires different includes than // CMAKE uses statically linked lib as submodule which requires different includes than
// the dynamically linked, intalled library in case of plain make // the dynamically linked, intalled library in case of plain make
#include <miniupnpc.h> #include <miniupnpc.h>
#include <upnpcommands.h> #include <upnpcommands.h>
@ -44,8 +43,8 @@
#endif // HAVE_NATPMP #endif // HAVE_NATPMP
void n2n_chg_port_mapping (struct n2n_edge *eee, const uint16_t port); void n2n_set_port_mapping (const uint16_t port);
void n2n_del_port_mapping (const uint16_t port);
#endif // HAVE_PORT_FORWARDING
#endif // _N2N_PORT_MAPPING_H_ #endif // _N2N_PORT_MAPPING_H_

View File

@ -634,21 +634,6 @@ 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_community_t community_name; /**< The community. 16 full octets. */ n2n_community_t community_name; /**< The community. 16 full octets. */
@ -686,7 +671,6 @@ 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;
@ -747,8 +731,6 @@ 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

@ -234,9 +234,6 @@ 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
@ -298,11 +295,6 @@ 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");
printf (" --------------------------------------------\n\n"); printf (" --------------------------------------------\n\n");
@ -728,12 +720,6 @@ 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;
} }
@ -790,7 +776,6 @@ 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

@ -165,6 +165,7 @@ static void mgmt_edge_info (mgmt_req_t *req, strbuf_t *buf) {
macstr_t mac_buf; macstr_t mac_buf;
struct in_addr ip_addr, ip_addr_mask; struct in_addr ip_addr, ip_addr_mask;
ipstr_t ip_address, ip_address_mask; ipstr_t ip_address, ip_address_mask;
n2n_sock_str_t sockbuf;
ip_addr.s_addr = req->eee->device.ip_addr; ip_addr.s_addr = req->eee->device.ip_addr;
inaddrtoa(ip_address, ip_addr); inaddrtoa(ip_address, ip_addr);
@ -178,11 +179,13 @@ static void mgmt_edge_info (mgmt_req_t *req, strbuf_t *buf) {
"\"version\":\"%s\"," "\"version\":\"%s\","
"\"macaddr\":\"%s\"," "\"macaddr\":\"%s\","
"\"ip4addr\":\"%s\"," "\"ip4addr\":\"%s\","
"\"ip4netmask\":\"%s\"}\n", "\"ip4netmask\":\"%s\","
"\"sockaddr\":\"%s\"}\n",
req->tag, req->tag,
PACKAGE_VERSION, PACKAGE_VERSION,
is_null_mac(req->eee->device.mac_addr) ? "" : macaddr_str(mac_buf, req->eee->device.mac_addr), is_null_mac(req->eee->device.mac_addr) ? "" : macaddr_str(mac_buf, req->eee->device.mac_addr),
ip_address, ip_address_mask); ip_address, ip_address_mask,
sock_to_cstr(sockbuf, &req->eee->conf.preferred_sock));
send_reply(req, buf, msg_len); send_reply(req, buf, msg_len);
} }

View File

@ -27,11 +27,6 @@ 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);
#ifdef HAVE_PORT_FORWARDING
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);
#endif
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,12 +324,9 @@ int supernode_connect (n2n_edge_t *eee) {
eee->cb.sock_opened(eee); eee->cb.sock_opened(eee);
} }
#ifdef HAVE_PORT_FORWARDING // REVISIT: add mgmt port notification to listener for better mgmt port
if(eee->conf.port_forwarding) // subscription support
// 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_PORT_FORWARDING
return 0; return 0;
} }
@ -490,12 +482,7 @@ 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");
} }
#ifdef HAVE_PORT_FORWARDING
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);
@ -2993,10 +2980,7 @@ 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);
#ifdef HAVE_PORT_FORWARDING
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);
@ -3117,10 +3101,6 @@ 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;
} }

View File

@ -56,7 +56,6 @@
#include "n2n.h" #include "n2n.h"
#ifdef HAVE_PORT_FORWARDING
#ifdef HAVE_MINIUPNP #ifdef HAVE_MINIUPNP
@ -463,9 +462,12 @@ static int n2n_natpmp_del_port_mapping (const uint16_t port) {
#endif // HAVE_NATPMP #endif // HAVE_NATPMP
// static
// ---------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------
// public
static void n2n_set_port_mapping (const uint16_t port) {
void n2n_set_port_mapping (const uint16_t port) {
#ifdef HAVE_NATPMP #ifdef HAVE_NATPMP
// since the NAT-PMP protocol is more concise than UPnP, NAT-PMP is preferred. // since the NAT-PMP protocol is more concise than UPnP, NAT-PMP is preferred.
@ -479,7 +481,7 @@ static void n2n_set_port_mapping (const uint16_t port) {
} }
static void n2n_del_port_mapping (const uint16_t port) { void n2n_del_port_mapping (const uint16_t port) {
#ifdef HAVE_NATPMP #ifdef HAVE_NATPMP
if(n2n_natpmp_del_port_mapping(port)) if(n2n_natpmp_del_port_mapping(port))
@ -490,151 +492,3 @@ static void n2n_del_port_mapping (const uint16_t port) {
#endif // HAVE_MINIUPNP #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);
}
#else
#error "enabling port mapping requires enabling pthread"
#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;
}
return 0;
#else
return -1;
#endif
}
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
}
#endif // HAVE_PORT_FORWARDING

View File

@ -14,7 +14,7 @@ LDFLAGS+=-L..
N2N_LIB=../libn2n.a N2N_LIB=../libn2n.a
TOOLS=n2n-benchmark n2n-keygen n2n-route TOOLS=n2n-benchmark n2n-keygen n2n-route n2n-portfwd
TOOLS+=@ADDITIONAL_TOOLS@ TOOLS+=@ADDITIONAL_TOOLS@
TESTS=tests-compress tests-elliptic tests-hashing tests-transform TESTS=tests-compress tests-elliptic tests-hashing tests-transform
@ -27,6 +27,7 @@ all: $(TOOLS) $(TESTS)
n2n-benchmark.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-benchmark.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile
n2n-keygen.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-keygen.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile
n2n-route.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-route.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile
n2n-portfwd.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile
n2n-decode: n2n-decode.c $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-decode: n2n-decode.c $(N2N_LIB) $(HEADERS) ../Makefile Makefile
$(CC) $(CFLAGS) $< $(LDFLAGS) $(LDLIBS) -lpcap -o $@ $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDLIBS) -lpcap -o $@

376
tools/n2n-portfwd.c Normal file
View File

@ -0,0 +1,376 @@
/**
* (C) 2007-22 - 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/>
*
*/
#include "n2n.h"
#define WITH_PORT 1
#define CORRECT_TAG 2
#define SOCKET_TIMEOUT 2
#define INFO_INTERVAL 5
#ifdef WIN32
#define STDIN_FILENO _fileno(stdin)
#endif
typedef struct n2n_portfwd_conf {
uint16_t port; /* management port */
} n2n_portfwd_conf_t;
static int keep_running = 1; /* for main loop, handled by signals */
// -------------------------------------------------------------------------------------------------------
// PLATFORM-DEPENDANT CODE (FOR NON-MANDATORY FEATURE)
void set_term_handler(const void *handler) {
#ifdef __linux__
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, handler);
signal(SIGINT, handler);
#endif
#ifdef WIN32
SetConsoleCtrlHandler(handler, TRUE);
#endif
}
#ifdef WIN32
BOOL WINAPI term_handler (DWORD sig) {
#else
static void term_handler (int sig) {
#endif
static int called = 0;
if(called) {
traceEvent(TRACE_NORMAL, "ok, leaving now");
_exit(0);
} else {
traceEvent(TRACE_NORMAL, "shutting down...");
called = 1;
}
keep_running = 0;
#ifdef WIN32
return TRUE;
#endif
}
// -------------------------------------------------------------------------------------------------------
SOCKET connect_to_management_port (n2n_portfwd_conf_t *ppp) {
SOCKET ret;
struct sockaddr_in sock_addr;
ret = socket (PF_INET, SOCK_DGRAM, 0);
if((int)ret < 0)
return -1;
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sock_addr.sin_port = htons(ppp->port);
if(0 != connect(ret, (struct sockaddr *)&sock_addr, sizeof(sock_addr)))
return -1;
return ret;
}
// -------------------------------------------------------------------------------------------------------
int get_port_from_json (uint16_t *port, json_object_t *json, char *key, int tag, int flags) {
int i;
char *colon = NULL;
if(NULL == json)
return 0;
for(i = 0; i < json->count; i++) {
if(json->pairs[i].type == JSON_STRING) {
if(!strcmp(json->pairs[i].key, key)) {
// cut off port from IP address
if((colon = strchr(json->pairs[i].value->string_value, ':'))) {
if(*colon != '\0') {
*port = atoi(colon + 1);
flags |= WITH_PORT;
}
}
}
if(!strcmp(json->pairs[i].key, "_tag" )) {
if(atoi(json->pairs[i].value->string_value) == tag) {
flags |= CORRECT_TAG;
}
}
} else if(json->pairs[i].type == JSON_OBJECT) {
flags |= get_port_from_json(port, json, key, tag, flags);
}
}
return flags;
}
// -------------------------------------------------------------------------------------------------------
// taken from https://web.archive.org/web/20170407122137/http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/
int kbhit () {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
// -------------------------------------------------------------------------------------------------------
static void help (int level) {
if(level == 0) return; /* no help required */
printf(" n2n-portfwd [-t <manangement_port>] [-V] [-v]"
"\n"
"\n This tool tries to find a router in local network and asks it to"
"\n forward the edge's port(UDP and TCP) by sending corresponding"
"\n UPnP and PMP requests."
"\n\n Adapt port (default: %d) to match your edge's management port"
"\n configuration."
"\n\n Verbosity can be increased or decreased with -V or -v , repeat as"
"\n as needed."
"\n\n",
N2N_EDGE_MGMT_PORT);
exit(0);
}
static int set_option (n2n_portfwd_conf_t *ppp, int optkey, char *optargument) {
switch(optkey) {
case 't': /* management port */ {
uint16_t port = atoi(optargument);
if(port) {
ppp->port = port;
} else {
traceEvent(TRACE_WARNING, "invalid management port provided with '-t'");
}
break;
}
case 'V': /* more verbose */ {
setTraceLevel(getTraceLevel() + 1);
break;
}
case 'v': /* less verbose */ {
setTraceLevel(getTraceLevel() - 1);
break;
}
default: /* unknown option */ {
return 1; /* for help */
}
}
return 0;
}
// -------------------------------------------------------------------------------------------------------
int main (int argc, char* argv[]) {
n2n_portfwd_conf_t ppp;
uint8_t c;
SOCKET sock;
size_t msg_len;
char udp_buf[N2N_PKT_BUF_SIZE];
fd_set socket_mask;
struct timeval wait_time;
time_t now = 0;
time_t last_info_req = 0;
json_object_t *json;
int ret;
int tag_info;
uint16_t port = 0, current_port = 0;
// version
print_n2n_version();
// ensure prerequisites
#ifndef HAVE_MINIUPNP
traceEvent(TRACE_WARNING, "no miniupnp support compiled");
#else
traceEvent(TRACE_NORMAL, "compiled with miniupnp support");
#endif
#ifndef HAVE_NATPMP
traceEvent(TRACE_WARNING, "no natpmp support compiled");
#else
traceEvent(TRACE_NORMAL, "compiled with natpmp support");
#endif
#if !defined(HAVE_MINIUPNP) && !defined(HAVE_NATPMP)
traceEvent(TRACE_WARNING, "no port forwarding method found");
return -1;
#endif
// handle signals to properly end the tool
set_term_handler(term_handler);
// init data structure
ppp.port = N2N_EDGE_MGMT_PORT;
setTraceLevel(2); /* NORMAL, should already be default */
n2n_srand(n2n_seed());
// get command line options and eventually overwrite initialized conf
while((c = getopt_long(argc, argv, "t:vV", NULL, NULL)) != '?') {
if(c == 255) break;
help(set_option(&ppp, c, optarg));
}
// verify conf and react with output to conf-related changes
// (nothing to do)
// additional checks
// (nothing to do)
// connect to mamagement port
traceEvent(TRACE_NORMAL, "connecting to edge management port %d", ppp.port);
sock = connect_to_management_port(&ppp);
if(sock == -1) {
traceEvent(TRACE_ERROR, "unable to open socket for management port connection");
goto end_route_tool;
}
// output status
traceEvent(TRACE_NORMAL, "press ENTER to end the program");
reset_main_loop:
wait_time.tv_sec = SOCKET_TIMEOUT;
wait_time.tv_usec = 0;
port = 0;
current_port = 0;
tag_info = 0;
// main loop
// read answer packet by packet which are only accepted if a corresponding request was sent before
// of which we know about by having set the related tag, tag_info
// a valid sock address indicates that we have seen a valid answer to the info request
while(keep_running && !kbhit()) {
// current time
now = time(NULL);
// check if we need to send info request again
if(now > last_info_req + INFO_INTERVAL) {
// send info read request
while(!(tag_info = ((uint32_t)n2n_rand()) >> 23));
msg_len = 0;
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"r %u info\n", tag_info);
ret = send(sock, udp_buf, msg_len, 0);
last_info_req = now;
}
// wait for any answer to info or read request
FD_ZERO(&socket_mask);
FD_SET(sock, &socket_mask);
ret = select(sock + 1, &socket_mask, NULL, NULL, &wait_time);
// refresh current time after having waited
now = time(NULL);
if(ret > 0) {
if(FD_ISSET(sock, &socket_mask)) {
msg_len = recv(sock, udp_buf, sizeof(udp_buf), 0);
if((msg_len > 0) && (msg_len < sizeof(udp_buf))) {
// make sure it is a string and replace all newlines with spaces
udp_buf[msg_len] = '\0';
for (char *p = udp_buf; (p = strchr(p, '\n')) != NULL; p++) *p = ' ';
traceEvent(TRACE_DEBUG, "received '%s' from management port", udp_buf);
// handle the answer, json needs to be freed later
json = json_parse(udp_buf);
// look for local edge information
if(tag_info) {
// local IP address port
port = 0;
ret = get_port_from_json(&port, json, "sockaddr", tag_info, 0);
if(ret == (WITH_PORT | CORRECT_TAG)) {
traceEvent(TRACE_DEBUG, "received information about edge port %d", port);
// evaluate current situation and take appropriate action
if(port != current_port) {
traceEvent(TRACE_NORMAL, "found edge port %d", port);
if(current_port)
n2n_del_port_mapping(current_port);
if(port)
n2n_set_port_mapping(port);
current_port = port;
}
}
}
// no need for current json object anymore
json_free(json);
}
} else {
// can this happen? reset the loop
traceEvent(TRACE_ERROR, "loop reset");
goto reset_main_loop;
}
} else if(ret == 0) {
// select() timeout
// action required?
} else {
// select() error
// action required?
}
}
end_route_tool:
// delete port forwarding if any
if(current_port)
n2n_del_port_mapping(current_port);
// close connection
closesocket(sock);
return 0;
}