added TCP support (#627)

(disabled on Windows)
This commit is contained in:
Logan oos Even 2021-02-22 22:37:47 +05:45 committed by GitHub
parent 85894715bd
commit a482fe112d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1014 additions and 328 deletions

View File

@ -32,6 +32,7 @@
*/
#define N2N_HAVE_DAEMON /* needs to be defined before it gets undefined */
#define N2N_HAVE_TCP /* needs to be defined before it gets undefined */
/* #define N2N_CAN_NAME_IFACE */
@ -44,6 +45,7 @@
#endif
#define N2N_CAN_NAME_IFACE 1
#undef N2N_HAVE_DAEMON
#undef N2N_HAVE_TCP /* as explained on https://github.com/ntop/n2n/pull/627#issuecomment-782093706 */
#undef N2N_HAVE_SETUID
#else
#ifndef CMAKE_BUILD
@ -106,6 +108,7 @@
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
@ -132,6 +135,9 @@
#include "n2n_typedefs.h"
#ifdef WIN32
#include <winsock2.h> /* for tcp */
#define SHUT_RDWR SD_BOTH /* for tcp */
#define SOL_TCP IPPROTO_TCP /* for tcp */
#include "win32/wintap.h"
#include <sys/stat.h>
#else
@ -211,7 +217,7 @@ void print_edge_stats (const n2n_edge_t *eee);
char* sock_to_cstr (n2n_sock_str_t out,
const n2n_sock_t * sock);
char * ip_subnet_to_str (dec_ip_bit_str_t buf, const n2n_ip_subnet_t *ipaddr);
SOCKET open_socket (int local_port, int bind_any);
SOCKET open_socket (int local_port, int bind_any, int type);
int sock_equal (const n2n_sock_t * a,
const n2n_sock_t * b);
@ -222,9 +228,17 @@ int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp, int
/* Operations on peer_info lists. */
size_t purge_peer_list (struct peer_info ** peer_list,
SOCKET socket_not_to_close,
n2n_tcp_connection_t *tcp_connections,
time_t purge_before);
size_t clear_peer_list (struct peer_info ** peer_list);
size_t purge_expired_nodes (struct peer_info **peer_list, time_t *p_last_purge, int frequency, int timeout);
size_t purge_expired_nodes (struct peer_info **peer_list,
SOCKET socket_not_to_close,
n2n_tcp_connection_t *tcp_connections,
time_t *p_last_purge,
int frequency, int timeout);
/* Edge conf */
void edge_init_conf_defaults (n2n_edge_conf_t *conf);

View File

@ -108,12 +108,14 @@ enum sn_purge{SN_PURGEABLE = 0, SN_UNPURGEABLE = 1};
#define HASH_FIND_PEER(head,mac,out) \
HASH_FIND(hh,head,mac,sizeof(n2n_mac_t),out)
#define N2N_EDGE_SN_HOST_SIZE 48
#define N2N_EDGE_NUM_SUPERNODES 2
#define N2N_EDGE_SUP_ATTEMPTS 3 /* Number of failed attmpts before moving on to next supernode. */
#define N2N_PATHNAME_MAXLEN 256
#define N2N_EDGE_MGMT_PORT 5644
#define N2N_SN_MGMT_PORT 5645
#define N2N_TCP_BACKLOG_QUEUE_SIZE 3 /* number of concurrently pending connections to be accepted */
/* NOT the number of max. TCP connections */
/* flag used in add_sn_to_list_by_mac_or_sock */
enum skip_add{SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2};

View File

@ -415,6 +415,7 @@ struct peer_info {
n2n_ip_subnet_t dev_addr;
n2n_desc_t dev_desc;
n2n_sock_t sock;
SOCKET socket_fd;
n2n_cookie_t last_cookie;
n2n_auth_t auth;
int timeout;
@ -598,6 +599,7 @@ typedef struct n2n_edge_conf {
int register_ttl; /**< TTL for registration packet when UDP NAT hole punching through supernode. */
int local_port;
int mgmt_port;
uint8_t connect_tcp; /** connection to supernode 0 = UDP; 1 = TCP */
n2n_auth_t auth;
filter_rule_t *network_traffic_filter_rules;
int metric; /**< Network interface metric (Windows only). */
@ -631,7 +633,7 @@ struct n2n_edge {
/* Sockets */
/* supernode socket is in eee->curr_sn->sock (of type n2n_sock_t) */
int udp_sock;
int sock;
int udp_mgmt_sock; /**< socket for status info. */
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY
@ -692,6 +694,18 @@ struct sn_community_regular_expression {
UT_hash_handle hh; /* makes this structure hashable */
};
typedef struct n2n_tcp_connection {
SOCKET socket_fd; /* file descriptor for tcp socket */
struct sockaddr sock; /* network order socket */
uint16_t expected; /* number of bytes expected to be read */
uint16_t position; /* current position in the buffer*/
uint8_t buffer[N2N_PKT_BUF_SIZE + sizeof(uint16_t)]; /* buffer for data collected from tcp socket incl. prepended length */
UT_hash_handle hh; /* makes this structure hashable */
} n2n_tcp_connection_t;
typedef struct n2n_sn {
time_t start_time; /* Used to measure uptime. */
sn_stats_t stats;
@ -700,7 +714,9 @@ typedef struct n2n_sn {
uint16_t lport; /* Local UDP port to bind to. */
uint16_t mport; /* Management UDP port to bind to. */
int sock; /* Main socket for UDP traffic with edges. */
int mgmt_sock; /* management socket. */
int tcp_sock; /* auxiliary socket for optional TCP connections */
n2n_tcp_connection_t *tcp_connections;/* list of established TCP connections */
int mgmt_sock; /* management socket. */
n2n_ip_subnet_t min_auto_ip_net; /* Address range of auto_ip service. */
n2n_ip_subnet_t max_auto_ip_net; /* Address range of auto_ip service. */
#ifndef WIN32

View File

@ -26,6 +26,7 @@ typedef char selection_criterion_str_t[SN_SELECTION_CRITERION_BUF_SIZE];
/* selection criterion's functions */
int sn_selection_criterion_init (peer_info_t *peer);
int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion);
int sn_selection_criterion_bad (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion);
int sn_selection_criterion_calculate (n2n_edge_t *eee, peer_info_t *peer, SN_SELECTION_CRITERION_DATA_TYPE *data);
/* common data's functions */

View File

@ -44,9 +44,12 @@ int num_cap = sizeof(cap_values)/sizeof(cap_value_t);
// forward declaration for use in main()
void send_register_super (n2n_edge_t *eee);
void send_query_peer (n2n_edge_t * eee, const n2n_mac_t dst_mac);
void send_query_peer (n2n_edge_t *eee, const n2n_mac_t dst_mac);
int supernode_connect (n2n_edge_t *eee);
int supernode_disconnect (n2n_edge_t *eee);
int fetch_and_eventually_process_data (n2n_edge_t *eee, SOCKET sock,
uint8_t *pktbuf, uint16_t *expected, uint16_t *position,
time_t now);
/* ***************************************************** */
/** Find the address and IP mode for the tuntap device.
@ -159,7 +162,6 @@ static void help (int level) {
#ifndef __APPLE__
"[-D] "
#endif
"[-S] "
"\n options for under- "
"[-i <registration interval>]"
"[-L <registration ttl>]"
@ -168,6 +170,8 @@ static void help (int level) {
"[-A<cipher>] "
"[-H] "
"[-z<compression>]"
"\n "
"[-S<level of solitude>]"
"\n\n tap device and "
"[-a [static:|dhcp:]<tap IP address>[/<cidr suffix>]] "
"\n overlay network "
@ -206,8 +210,7 @@ static void help (int level) {
#ifndef __APPLE__
"[-D] enable PMTU discovery"
#endif
"\n flag options [-S] do not connect p2p, always use supernode"
"\n [-H] enable header encryption"
"\n flag options [-H] enable header encryption"
"\n [-r] enable packet forwarding through n2n community"
"\n [-E] accept multicast MAC addresses"
#ifndef WIN32
@ -240,7 +243,11 @@ static void help (int level) {
printf(" -D | enable PMTU discovery, it can reduce fragmentation but\n"
" | causes connections to stall if not properly supported\n");
#endif
printf(" -S | do not connect p2p, always use the supernode\n");
printf(" -S1 ... -S2 or -S | -S1 or -S do not connect p2p, always use the supernode\n"
#ifdef N2N_HAVE_TCP
" | -S2 connects through TCP and only to the supernode\n"
#endif
);
printf(" -i <reg_interval> | registration interval, for NAT hole punching (default\n"
" | 20 seconds)\n");
printf(" -L <reg_ttl> | TTL for registration packet for NAT hole punching through\n"
@ -593,7 +600,15 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e
}
case 'S': {
conf->allow_p2p = 0;
int solitude_level = 1;
if(optargument)
solitude_level = atoi(optargument);
if(solitude_level >= 1)
conf->allow_p2p = 0;
#ifdef N2N_HAVE_TCP
if(solitude_level == 2)
conf->connect_tcp = 1;
#endif
break;
}
@ -663,7 +678,7 @@ static int loadFromCLI (int argc, char *argv[], n2n_edge_conf_t *conf, n2n_tunta
u_char c;
while ((c = getopt_long(argc, argv,
"k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:I:SDL:z::A::Hn:R:"
"k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:I:S::DL:z::A::Hn:R:"
#ifdef __linux__
"T:"
#endif
@ -833,11 +848,17 @@ int main (int argc, char* argv[]) {
n2n_tuntap_priv_config_t ec; /* config used for standalone program execution */
uint8_t runlevel = 0; /* bootstrap: runlevel */
uint8_t seek_answer = 1; /* expecting answer from supernode */
time_t now_time, last_action; /* timeout */
time_t now, last_action; /* timeout */
macstr_t mac_buf; /* output mac address */
fd_set socket_mask; /* for supernode answer */
struct timeval wait_time; /* timeout for sn answer */
size_t bread = 0;
uint16_t expected = sizeof(uint16_t);
uint16_t position = 0;
uint8_t pktbuf[N2N_SN_PKTBUF_SIZE + sizeof(uint16_t)]; /* buffer + prepended buffer length in case of tcp */
#ifndef WIN32
struct passwd *pw = NULL;
#endif
@ -944,7 +965,7 @@ int main (int argc, char* argv[]) {
// is found
// if more than one supernode given, find at least one who is alive to faster establish connection
if(HASH_COUNT(eee->conf.supernodes) <= 1) {
if((HASH_COUNT(eee->conf.supernodes) <= 1) || (eee->conf.connect_tcp)) {
// skip the initial supernode ping
traceEvent(TRACE_DEBUG, "Skip PING to supernode.");
runlevel = 2;
@ -952,15 +973,16 @@ int main (int argc, char* argv[]) {
eee->last_sup = 0; /* if it wasn't zero yet */
eee->curr_sn = eee->conf.supernodes;
supernode_connect(eee);
while(runlevel < 5) {
now_time = time(NULL);
now = time(NULL);
// we do not use switch-case because we also check for 'greater than'
if(runlevel == 0) { /* PING to all known supernodes */
last_action = now_time;
last_action = now;
eee->sn_pong = 0;
// (re-)initialize the number of max concurrent pings (decreases by calling send_query_peer)
eee->conf.number_max_sn_pings = NUMBER_SN_PINGS_INITIAL;
@ -975,9 +997,10 @@ int main (int argc, char* argv[]) {
eee->sn_pong = 0;
sn_selection_sort(&(eee->conf.supernodes));
eee->curr_sn = eee->conf.supernodes;
supernode_connect(eee);
traceEvent(TRACE_NORMAL, "Received first PONG from supernode [%s].", eee->curr_sn->ip_addr);
runlevel++;
} else if(last_action <= (now_time - BOOTSTRAP_TIMEOUT)) {
} else if(last_action <= (now - BOOTSTRAP_TIMEOUT)) {
// timeout
runlevel--;
// skip waiting for answer to direcly go to send PING again
@ -1001,7 +1024,7 @@ int main (int argc, char* argv[]) {
if(runlevel == 2) { /* send REGISTER_SUPER to get auto ip address from a supernode */
if(eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_SN_ASSIGN) {
last_action = now_time;
last_action = now;
eee->sn_wait = 1;
send_register_super(eee);
runlevel++;
@ -1018,12 +1041,13 @@ int main (int argc, char* argv[]) {
runlevel++;
traceEvent(TRACE_NORMAL, "Received REGISTER_SUPER_ACK from supernode for IP address asignment.");
// it should be from curr_sn, but we can't determine definitely here, so no details to output
} else if(last_action <= (now_time - BOOTSTRAP_TIMEOUT)) {
} else if(last_action <= (now - BOOTSTRAP_TIMEOUT)) {
// timeout, so try next supernode
if(eee->curr_sn->hh.next)
eee->curr_sn = eee->curr_sn->hh.next;
else
eee->curr_sn = eee->conf.supernodes;
supernode_connect(eee);
runlevel--;
// skip waiting for answer to direcly go to send REGISTER_SUPER again
seek_answer = 0;
@ -1053,23 +1077,27 @@ int main (int argc, char* argv[]) {
// we usually wait for some answer, there however are exceptions when going back to a previous runlevel
if(seek_answer) {
FD_ZERO(&socket_mask);
FD_SET(eee->udp_sock, &socket_mask);
FD_SET(eee->sock, &socket_mask);
wait_time.tv_sec = BOOTSTRAP_TIMEOUT;
wait_time.tv_usec = 0;
if(select(eee->udp_sock + 1, &socket_mask, NULL, NULL, &wait_time) > 0) {
if(FD_ISSET(eee->udp_sock, &socket_mask)) {
readFromIPSocket(eee, eee->udp_sock);
if(select(eee->sock + 1, &socket_mask, NULL, NULL, &wait_time) > 0) {
if(FD_ISSET(eee->sock, &socket_mask)) {
fetch_and_eventually_process_data (eee, eee->sock,
pktbuf, &expected, &position,
now);
}
}
}
seek_answer = 1;
}
// allow a higher number of pings for first regular round of ping
// to quicker get an inital 'supernode selection criterion overview'
eee->conf.number_max_sn_pings = NUMBER_SN_PINGS_INITIAL;
// do not immediately ping again, allow some time
eee->last_sweep = now_time - SWEEP_TIME + 2 * BOOTSTRAP_TIMEOUT;
eee->last_sweep = now - SWEEP_TIME + 2 * BOOTSTRAP_TIMEOUT;
eee->sn_wait = 1;
eee->last_register_req = 0;
@ -1114,6 +1142,7 @@ int main (int argc, char* argv[]) {
#endif
#ifdef __linux__
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, term_handler);
signal(SIGINT, term_handler);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -29,12 +29,12 @@ int main () {
sss_node.daemon = 0; // Whether to daemonize
sss_node.lport = 1234; // Main UDP listen port
sss_node.sock = open_socket(sss_node.lport, 1);
sss_node.sock = open_socket(sss_node.lport, 1, 0);
if(-1 == sss_node.sock) {
exit(-2);
}
sss_node.mgmt_sock = open_socket(5645, 0); // Main UDP management port
sss_node.mgmt_sock = open_socket(5645, 0, 0); // Main UDP management port
if(-1 == sss_node.mgmt_sock) {
exit(-2);
}

View File

@ -28,13 +28,13 @@
/* ************************************** */
SOCKET open_socket (int local_port, int bind_any) {
SOCKET open_socket (int local_port, int bind_any, int type /* 0 = UDP, TCP otherwise */) {
SOCKET sock_fd;
struct sockaddr_in local_address;
int sockopt;
if((sock_fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
if((sock_fd = socket(PF_INET, ((type == 0) ? SOCK_DGRAM : SOCK_STREAM) , 0)) < 0) {
traceEvent(TRACE_ERROR, "Unable to create socket [%s][%d]\n",
strerror(errno), sock_fd);
return(-1);
@ -60,6 +60,7 @@ SOCKET open_socket (int local_port, int bind_any) {
return(sock_fd);
}
static int traceLevel = 2 /* NORMAL */;
static int useSyslog = 0, syslog_opened = 0;
static FILE *traceFile = NULL;
@ -439,7 +440,10 @@ void print_n2n_version () {
/* *********************************************** */
size_t purge_expired_nodes (struct peer_info **peer_list, time_t *p_last_purge,
size_t purge_expired_nodes (struct peer_info **peer_list,
SOCKET socket_not_to_close,
n2n_tcp_connection_t *tcp_connections,
time_t *p_last_purge,
int frequency, int timeout) {
time_t now = time(NULL);
@ -451,7 +455,7 @@ size_t purge_expired_nodes (struct peer_info **peer_list, time_t *p_last_purge,
traceEvent(TRACE_DEBUG, "Purging old registrations");
num_reg = purge_peer_list(peer_list, now - timeout);
num_reg = purge_peer_list(peer_list, socket_not_to_close, tcp_connections, now - timeout);
(*p_last_purge) = now;
traceEvent(TRACE_DEBUG, "Remove %ld registrations", num_reg);
@ -459,15 +463,30 @@ size_t purge_expired_nodes (struct peer_info **peer_list, time_t *p_last_purge,
return num_reg;
}
/** Purge old items from the peer_list and return the number of items that were removed. */
/** Purge old items from the peer_list, eventually close the related socket, and
* return the number of items that were removed. */
size_t purge_peer_list (struct peer_info ** peer_list,
SOCKET socket_not_to_close,
n2n_tcp_connection_t *tcp_connections,
time_t purge_before) {
struct peer_info *scan, *tmp;
n2n_tcp_connection_t *conn;
size_t retval = 0;
HASH_ITER(hh, *peer_list, scan, tmp) {
if((scan->purgeable == SN_PURGEABLE) && (scan->last_seen < purge_before)) {
if((scan->socket_fd >=0) && (scan->socket_fd != socket_not_to_close)) {
if(tcp_connections) {
HASH_FIND_INT(tcp_connections, &scan->socket_fd, conn);
if(conn) {
HASH_DEL(tcp_connections, conn);
free(conn);
}
}
shutdown(scan->socket_fd, SHUT_RDWR);
closesocket(scan->socket_fd);
}
HASH_DEL(*peer_list, scan);
retval++;
free(scan);

View File

@ -355,7 +355,6 @@ static int setOption (int optkey, char *_optarg, n2n_sn_t *sss) {
memcpy(anchor_sn->mac_addr, null_mac, sizeof(n2n_mac_t));
anchor_sn->purgeable = SN_UNPURGEABLE;
anchor_sn->last_valid_time_stamp = initial_time_stamp();
}
}
}
@ -660,10 +659,11 @@ BOOL WINAPI term_handler (DWORD sig)
int main (int argc, char * const argv[]) {
int rc;
#ifndef WIN32
struct passwd *pw = NULL;
#endif
struct peer_info *scan, *tmp;
sn_init(&sss_node);
add_federation_to_communities(&sss_node);
@ -704,7 +704,7 @@ int main (int argc, char * const argv[]) {
traceEvent(TRACE_DEBUG, "traceLevel is %d", getTraceLevel());
sss_node.sock = open_socket(sss_node.lport, 1 /*bind ANY*/);
sss_node.sock = open_socket(sss_node.lport, 1 /*bind ANY*/, 0 /* UDP */);
if(-1 == sss_node.sock) {
traceEvent(TRACE_ERROR, "Failed to open main socket. %s", strerror(errno));
exit(-2);
@ -712,7 +712,24 @@ int main (int argc, char * const argv[]) {
traceEvent(TRACE_NORMAL, "supernode is listening on UDP %u (main)", sss_node.lport);
}
sss_node.mgmt_sock = open_socket(sss_node.mport, 0 /* bind LOOPBACK */);
#ifdef N2N_HAVE_TCP
sss_node.tcp_sock = open_socket(sss_node.lport, 1 /*bind ANY*/, 1 /* TCP */);
if(-1 == sss_node.tcp_sock) {
traceEvent(TRACE_ERROR, "Failed to open auxiliary TCP socket. %s", strerror(errno));
exit(-2);
} else {
traceEvent(TRACE_NORMAL, "supernode opened TCP %u (aux)", sss_node.lport);
}
if(-1 == listen(sss_node.tcp_sock, N2N_TCP_BACKLOG_QUEUE_SIZE)) {
traceEvent(TRACE_ERROR, "Failed to listen on auxiliary TCP socket. %s", strerror(errno));
exit(-2);
} else {
traceEvent(TRACE_NORMAL, "supernode is listening on TCP %u (aux)", sss_node.lport);
}
#endif
sss_node.mgmt_sock = open_socket(sss_node.mport, 0 /* bind LOOPBACK */, 0 /* UDP */);
if(-1 == sss_node.mgmt_sock) {
traceEvent(TRACE_ERROR, "Failed to open management socket. %s", strerror(errno));
exit(-2);
@ -720,6 +737,9 @@ int main (int argc, char * const argv[]) {
traceEvent(TRACE_NORMAL, "supernode is listening on UDP %u (management)", sss_node.mport);
}
HASH_ITER(hh, sss_node.federation->edges, scan, tmp)
scan->socket_fd = sss_node.sock;
#ifndef WIN32
if(((pw = getpwnam ("n2n")) != NULL) || ((pw = getpwnam ("nobody")) != NULL)) {
sss_node.userid = sss_node.userid == 0 ? pw->pw_uid : 0;
@ -745,6 +765,7 @@ int main (int argc, char * const argv[]) {
traceEvent(TRACE_NORMAL, "supernode started");
#ifdef __linux__
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, term_handler);
signal(SIGINT, term_handler);
signal(SIGHUP, dump_registrations);

View File

@ -38,6 +38,15 @@ int sn_selection_criterion_init (peer_info_t *peer) {
/* Set selection_criterion field to default value according to selected strategy. */
int sn_selection_criterion_default (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) {
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) (UINT32_MAX >> 1) - 1;
return 0; /* OK */
}
/* Set selection_criterion field to 'bad' value (worse than default) according to selected strategy. */
int sn_selection_criterion_bad (SN_SELECTION_CRITERION_DATA_TYPE *selection_criterion) {
*selection_criterion = (SN_SELECTION_CRITERION_DATA_TYPE) UINT32_MAX >> 1;
return 0; /* OK */
@ -148,12 +157,12 @@ extern char * sn_selection_criterion_str (selection_criterion_str_t out, peer_in
#ifndef SN_SELECTION_RTT
snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE,
(int16_t)(peer->selection_criterion) != -1 ? "load = %8d" :
"", peer->selection_criterion);
(int16_t)(peer->selection_criterion) >= 0 ? "load = %8d" :
"", peer->selection_criterion);
#else
snprintf(out, SN_SELECTION_CRITERION_BUF_SIZE,
(int16_t)(peer->selection_criterion) != -1 ? "rtt = %6d ms" :
"", peer->selection_criterion);
(int16_t)(peer->selection_criterion) >= 0 ? "rtt = %6d ms" :
"", peer->selection_criterion);
#endif
return out;

View File

@ -28,8 +28,8 @@ static int try_forward (n2n_sn_t * sss,
const uint8_t * pktbuf,
size_t pktsize);
static ssize_t sendto_sock (n2n_sn_t *sss,
const n2n_sock_t *sock,
static ssize_t sendto_peer (n2n_sn_t *sss,
const struct peer_info *peer,
const uint8_t *pktbuf,
size_t pktsize);
@ -52,6 +52,7 @@ static int update_edge (n2n_sn_t *sss,
const n2n_REGISTER_SUPER_t* reg,
struct sn_community *comm,
const n2n_sock_t *sender_sock,
const SOCKET socket_fd,
n2n_auth_t *answer_auth,
int skip_add,
time_t now);
@ -72,6 +73,7 @@ static int process_mgmt (n2n_sn_t *sss,
static int process_udp (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const SOCKET socket_fd,
uint8_t *udp_buf,
size_t udp_size,
time_t now);
@ -95,7 +97,7 @@ static int try_forward (n2n_sn_t * sss,
if(NULL != scan) {
int data_sent_len;
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize);
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize);
if(data_sent_len == pktsize) {
++(sss->stats.fwd);
@ -110,6 +112,7 @@ static int try_forward (n2n_sn_t * sss,
sock_to_cstr(sockbuf, &(scan->sock)),
macaddr_str(mac_buf, scan->mac_addr),
errno, strerror(errno));
return -1;
}
} else {
if(!from_supernode) {
@ -119,37 +122,155 @@ static int try_forward (n2n_sn_t * sss,
} else {
traceEvent(TRACE_DEBUG, "try_forward unknown MAC. Dropping the packet.");
/* Not a known MAC so drop. */
return(-2);
return -2;
}
}
return(0);
return 0;
}
/** Send a datagram to the destination embodied in a n2n_sock_t.
static void close_tcp_connection(n2n_sn_t *sss, n2n_tcp_connection_t *conn) {
struct sn_community *comm, *tmp_comm;
struct peer_info *edge, *tmp_edge;
if(!conn)
return;
// find peer by file descriptor
HASH_ITER(hh, sss->communities, comm, tmp_comm) {
HASH_ITER(hh, comm->edges, edge, tmp_edge) {
if(edge->socket_fd == conn->socket_fd) {
// remove peer
HASH_DEL(comm->edges, edge);
free(edge);
goto close_conn; /* break - level 2 */
}
}
}
close_conn:
// close the connection
shutdown(conn->socket_fd, SHUT_RDWR);
closesocket(conn->socket_fd);
// forget about the connection
HASH_DEL(sss->tcp_connections, conn);
free(conn);
}
/** Send a datagram to a file descriptor socket.
*
* @return -1 on error otherwise number of bytes sent
*/
static ssize_t sendto_sock (n2n_sn_t *sss,
const n2n_sock_t *sock,
static ssize_t sendto_fd (n2n_sn_t *sss,
SOCKET socket_fd,
const struct sockaddr *socket,
const uint8_t *pktbuf,
size_t pktsize) {
ssize_t sent = 0;
struct sn_community *comm, *tmp_comm;
struct peer_info *edge, *tmp_edge;
n2n_tcp_connection_t *conn;
sent = sendto(socket_fd, pktbuf, pktsize, 0 /* flags */,
socket, sizeof(struct sockaddr_in));
if(sent <= 0) {
char * c = strerror(errno);
traceEvent(TRACE_ERROR, "sendto_fd failed (%d) %s", errno, c);
#ifdef WIN32
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError());
#endif
// if the erroneous connection is tcp, i.e. not the regular sock...
if((socket_fd >= 0) && (socket_fd != sss->sock)) {
// ...forget about the corresponding peer and the connection
HASH_FIND_INT(sss->tcp_connections, &socket_fd, conn);
close_tcp_connection(sss, conn);
return -1;
}
} else {
traceEvent(TRACE_DEBUG, "sendto_fd sent=%d to ", (signed int)sent);
}
return sent;
}
/** Send a datagram to a network order socket of type struct sockaddr.
*
* @return -1 on error otherwise number of bytes sent
*/
static ssize_t sendto_sock(n2n_sn_t *sss,
SOCKET socket_fd,
const struct sockaddr *socket,
const uint8_t *pktbuf,
size_t pktsize) {
ssize_t sent = 0;
int value = 0;
// if the connection is tcp, i.e. not the regular sock...
if((socket_fd >= 0) && (socket_fd != sss->sock)) {
setsockopt(socket_fd, SOL_TCP, TCP_NODELAY, &value, sizeof(value));
value = 1;
#ifndef WIN32
setsockopt(socket_fd, SOL_TCP, TCP_CORK, &value, sizeof(value));
#endif
// prepend packet length...
uint16_t pktsize16 = htobe16(pktsize);
sent = sendto_fd(sss, socket_fd, socket, (uint8_t*)&pktsize16, sizeof(pktsize16));
if(sent <= 0)
return -1;
// ...before sending the actual data
}
sent = sendto_fd(sss, socket_fd, socket, pktbuf, pktsize);
// if the connection is tcp, i.e. not the regular sock...
if((socket_fd >= 0) && (socket_fd != sss->sock)) {
value = 1; /* value should still be set to 1 */
setsockopt(socket_fd, SOL_TCP, TCP_NODELAY, &value, sizeof(value));
#ifndef WIN32
value = 0;
setsockopt(socket_fd, SOL_TCP, TCP_CORK, &value, sizeof(value));
#endif
}
return sent;
}
/** Send a datagram to a peer whose destination socket is embodied in its sock field of type n2n_sock_t.
* It calls sendto_sock to do the final send.
*
* @return -1 on error otherwise number of bytes sent
*/
static ssize_t sendto_peer (n2n_sn_t *sss,
const struct peer_info *peer,
const uint8_t *pktbuf,
size_t pktsize) {
n2n_sock_str_t sockbuf;
if(AF_INET == sock->family) {
struct sockaddr_in udpsock;
if(AF_INET == peer->sock.family) {
udpsock.sin_family = AF_INET;
udpsock.sin_port = htons(sock->port);
memcpy(&(udpsock.sin_addr.s_addr), &(sock->addr.v4), IPV4_SIZE);
// network order socket
struct sockaddr_in socket;
fill_sockaddr((struct sockaddr *)&socket, sizeof(socket), &(peer->sock));
traceEvent(TRACE_DEBUG, "sendto_sock %lu to [%s]",
traceEvent(TRACE_DEBUG, "sendto_peer %lu to [%s]",
pktsize,
sock_to_cstr(sockbuf, sock));
sock_to_cstr(sockbuf, &(peer->sock)));
return sendto(sss->sock, pktbuf, pktsize, 0,
(const struct sockaddr *)&udpsock, sizeof(struct sockaddr_in));
return sendto_sock(sss,
(peer->socket_fd >= 0) ? peer->socket_fd : sss->sock,
(const struct sockaddr*)&socket, pktbuf, pktsize);
} else {
/* AF_INET6 not implemented */
errno = EAFNOSUPPORT;
@ -157,6 +278,7 @@ static ssize_t sendto_sock (n2n_sn_t *sss,
}
}
/** Try and broadcast a message to all edges in the community.
*
* This will send the exact same datagram to zero or more edges registered to
@ -185,7 +307,7 @@ static int try_broadcast (n2n_sn_t * sss,
HASH_ITER(hh, sss->federation->edges, scan, tmp) {
int data_sent_len;
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize);
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize);
if(data_sent_len != pktsize) {
++(sss->stats.errors);
@ -210,7 +332,7 @@ static int try_broadcast (n2n_sn_t * sss,
/* REVISIT: exclude if the destination socket is where the packet came from. */
int data_sent_len;
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize);
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize);
if(data_sent_len != pktsize) {
++(sss->stats.errors);
@ -233,6 +355,7 @@ static int try_broadcast (n2n_sn_t * sss,
return 0;
}
/** Initialise some fields of the community structure **/
int comm_init (struct sn_community *comm, char *cmn) {
@ -304,18 +427,34 @@ int sn_init(n2n_sn_t *sss) {
return 0; /* OK */
}
/** Deinitialise the supernode structure and deallocate any memory owned by
* it. */
void sn_term (n2n_sn_t *sss) {
struct sn_community *community, *tmp;
struct sn_community_regular_expression *re, *tmp_re;
n2n_tcp_connection_t *conn, *tmp_conn;
if(sss->sock >= 0) {
closesocket(sss->sock);
}
sss->sock = -1;
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) {
shutdown(conn->socket_fd, SHUT_RDWR);
closesocket(conn->socket_fd);
HASH_DEL(sss->tcp_connections, conn);
free(conn);
}
if(sss->tcp_sock >= 0) {
shutdown(sss->tcp_sock, SHUT_RDWR);
closesocket(sss->tcp_sock);
}
sss->tcp_sock = -1;
if(sss->mgmt_sock >= 0) {
closesocket(sss->mgmt_sock);
}
@ -345,6 +484,7 @@ void sn_term (n2n_sn_t *sss) {
#endif
}
/** Determine the appropriate lifetime for new registrations.
*
* If the supernode has been put into a pre-shutdown phase then this lifetime
@ -409,6 +549,7 @@ static int update_edge (n2n_sn_t *sss,
const n2n_REGISTER_SUPER_t* reg,
struct sn_community *comm,
const n2n_sock_t *sender_sock,
const SOCKET socket_fd,
n2n_auth_t *answer_auth,
int skip_add,
time_t now) {
@ -447,6 +588,7 @@ static int update_edge (n2n_sn_t *sss,
scan->dev_addr.net_bitlen = reg->dev_addr.net_bitlen;
memcpy((char*)scan->dev_desc, reg->dev_desc, N2N_DESC_SIZE);
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
scan->socket_fd = socket_fd;
memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE));
handle_remote_auth(sss, scan, &(reg->auth), answer_auth);
scan->last_valid_time_stamp = initial_time_stamp();
@ -463,6 +605,7 @@ static int update_edge (n2n_sn_t *sss,
if(!sock_equal(sender_sock, &(scan->sock))) {
if((auth = auth_edge(&(scan->auth), &(reg->auth), answer_auth)) == 0) {
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
scan->socket_fd = socket_fd;
memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE));
traceEvent(TRACE_INFO, "update_edge updated %s ==> %s",
@ -697,7 +840,7 @@ static int re_register_and_purge_supernodes (n2n_sn_t *sss, struct sn_community
}
// purge long-time-not-seen supernodes
purge_expired_nodes(&(comm->edges), p_last_re_reg_and_purge,
purge_expired_nodes(&(comm->edges), sss->sock, sss->tcp_connections, p_last_re_reg_and_purge,
RE_REG_AND_PURGE_FREQUENCY, LAST_SEEN_SN_INACTIVE);
if(comm != NULL) {
@ -748,7 +891,7 @@ static int re_register_and_purge_supernodes (n2n_sn_t *sss, struct sn_community
comm->header_encryption_ctx, comm->header_iv_ctx,
time_stamp());
/* sent = */ sendto_sock(sss, &(peer->sock), pktbuf, idx);
/* sent = */ sendto_peer(sss, peer, pktbuf, idx);
}
}
@ -774,7 +917,7 @@ static int purge_expired_communities (n2n_sn_t *sss,
if(comm->is_federation == IS_FEDERATION)
continue;
num_reg += purge_peer_list(&comm->edges, now - REGISTRATION_TIMEOUT);
num_reg += purge_peer_list(&comm->edges, sss->sock, sss->tcp_connections, now - REGISTRATION_TIMEOUT);
if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) {
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community);
if(NULL != comm->header_encryption_ctx) {
@ -800,6 +943,7 @@ static int number_enc_packets_sort (struct sn_community *a, struct sn_community
return (b->number_enc_packets - a->number_enc_packets);
}
static int sort_communities (n2n_sn_t *sss,
time_t* p_last_sort,
time_t now) {
@ -847,13 +991,13 @@ static int process_mgmt (n2n_sn_t *sss,
traceEvent(TRACE_DEBUG, "process_mgmt");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
" ### | TAP | MAC | EDGE | HINT | LAST SEEN\n");
" ### | TAP | MAC | EDGE | HINT | LAST SEEN\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"====================================================================================================\n");
"========================================================================================================\n");
HASH_ITER(hh, sss->communities, community, tmp) {
if(num_comm)
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"----------------------------------------------------------------------------------------------------\n");
"--------------------------------------------------------------------------------------------------------\n");
num_comm++;
num_edges += HASH_COUNT(community->edges);
@ -869,12 +1013,13 @@ static int process_mgmt (n2n_sn_t *sss,
HASH_ITER(hh, community->edges, peer, tmpPeer) {
sprintf (time_buf, "%9u", now - peer->last_seen);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"%4u | %-19s | %-17s | %-21s | %-15s | %9s\n",
"%4u | %-19s | %-17s | %-21s %-3s | %-15s | %9s\n",
++num,
(peer->dev_addr.net_addr == 0) ? ((peer->purgeable == SN_UNPURGEABLE) ? "-l" : "") :
ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
macaddr_str(mac_buf, peer->mac_addr),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
((peer->socket_fd >= 0) && (peer->socket_fd != sss->sock)) ? "TCP" : "",
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
@ -883,7 +1028,7 @@ static int process_mgmt (n2n_sn_t *sss,
}
}
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"====================================================================================================\n");
"========================================================================================================\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"uptime %lu | ", (now - sss->start_time));
@ -933,7 +1078,7 @@ static int sendto_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf,
size_t mgmt_size) {
ssize_t r = sendto(sss->mgmt_sock, mgmt_buf, mgmt_size, 0 /*flags*/,
(struct sockaddr *)sender_sock, sizeof (struct sockaddr_in));
@ -950,7 +1095,8 @@ static int sendto_mgmt (n2n_sn_t *sss,
*
*/
static int process_udp (n2n_sn_t * sss,
const struct sockaddr_in * sender_sock,
const struct sockaddr_in *sender_sock,
const SOCKET socket_fd,
uint8_t * udp_buf,
size_t udp_size,
time_t now) {
@ -1358,6 +1504,8 @@ static int process_udp (n2n_sn_t * sss,
if(comm->is_federation == IS_FEDERATION) {
skip_add = SN_ADD;
p = add_sn_to_list_by_mac_or_sock(&(sss->federation->edges), &(ack.sock), reg.edgeMac, &skip_add);
// communication with other supernodes happens via standard udp port
p->socket_fd = sss->sock;
}
/* Skip random numbers of supernodes before payload assembling, calculating an appropriate random_number.
@ -1390,9 +1538,9 @@ static int process_udp (n2n_sn_t * sss,
if(!is_null_mac(reg.edgeMac)) {
if(cmn.flags & N2N_FLAGS_SOCKET) {
ret_value = update_edge(sss, &reg, comm, &(ack.sock), &(ack.auth), SN_ADD_SKIP, now);
ret_value = update_edge(sss, &reg, comm, &(ack.sock), socket_fd, &(ack.auth), SN_ADD_SKIP, now);
} else {
ret_value = update_edge(sss, &reg, comm, &(ack.sock), &(ack.auth), SN_ADD, now);
ret_value = update_edge(sss, &reg, comm, &(ack.sock), socket_fd, &(ack.auth), SN_ADD, now);
}
}
@ -1408,12 +1556,7 @@ static int process_udp (n2n_sn_t * sss,
comm->header_encryption_ctx, comm->header_iv_ctx,
time_stamp());
}
sendto(sss->sock, ackbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
if(cmn.flags & N2N_FLAGS_SOCKET) {
sendto_sock(sss, &reg.sock, ackbuf, encx);
}
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackbuf, encx);
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_NAK for %s",
macaddr_str(mac_buf, reg.edgeMac));
@ -1453,12 +1596,25 @@ static int process_udp (n2n_sn_t * sss,
time_stamp());
}
sendto(sss->sock, ackbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackbuf, encx);
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]",
macaddr_str(mac_buf, reg.edgeMac),
sock_to_cstr(sockbuf, &(ack.sock)));
} else {
// this is an edge with valid authentication registering with another supernode
// so we can delete it here if present (can happen)
HASH_FIND_PEER(comm->edges, reg.edgeMac, peer);
if(peer != NULL) {
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) {
n2n_tcp_connection_t *conn;
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), conn);
close_tcp_connection(sss, conn); /* also deletes the peer */
} else {
HASH_DEL(comm->edges, peer);
free(peer);
}
}
}
}
} else {
@ -1502,7 +1658,14 @@ static int process_udp (n2n_sn_t * sss,
HASH_FIND_PEER(comm->edges, unreg.srcMac, peer);
if(peer != NULL) {
if((auth = auth_edge(&(peer->auth), &unreg.auth, NULL)) == 0) {
HASH_DEL(comm->edges, peer);
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) {
n2n_tcp_connection_t *conn;
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), conn);
close_tcp_connection(sss, conn); /* also deletes the peer */
} else {
HASH_DEL(comm->edges, peer);
free(peer);
}
}
}
@ -1576,6 +1739,8 @@ static int process_udp (n2n_sn_t * sss,
for(i = 0; i < ack.num_sn; i++) {
skip_add = SN_ADD;
tmp = add_sn_to_list_by_mac_or_sock(&(sss->federation->edges), &(payload->sock), payload->mac, &skip_add);
// other supernodes communicate via standard udp socket
tmp->socket_fd = sss->sock;
if(skip_add == SN_ADD_ADDED) {
tmp->last_seen = now - LAST_SEEN_SN_NEW;
@ -1589,7 +1754,9 @@ static int process_udp (n2n_sn_t * sss,
}
case MSG_TYPE_REGISTER_SUPER_NAK: {
n2n_common_t cmn2;
n2n_REGISTER_SUPER_NAK_t nak;
uint8_t nakbuf[N2N_SN_PKTBUF_SIZE];
size_t encx = 0;
struct peer_info *peer;
n2n_sock_str_t sockbuf;
@ -1626,10 +1793,32 @@ static int process_udp (n2n_sn_t * sss,
HASH_FIND_PEER(comm->edges, nak.srcMac, peer);
if(comm->is_federation == IS_NO_FEDERATION) {
if(peer != NULL) {
HASH_DEL(comm->edges, peer);
// this is a NAK for one of the edges conencted to this supernode, forward,
// i.e. re-assemble (memcpy of udpbuf to nakbuf could be sufficient as well)
// use incoming cmn (with already decreased TTL)
// NAK (cookie and srcMac) remains unchanged
encode_REGISTER_SUPER_NAK(nakbuf, &encx, &cmn, &nak);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(nakbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx,
time_stamp());
}
sendto_peer(sss, peer, nakbuf, encx);
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) {
n2n_tcp_connection_t *conn;
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), conn);
close_tcp_connection(sss, conn); /* also deletes the peer */
} else {
HASH_DEL(comm->edges, peer);
free(peer);
}
}
}
break;
}
@ -1707,8 +1896,7 @@ static int process_udp (n2n_sn_t * sss,
}
}
sendto(sss->sock, encbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, encbuf, encx);
traceEvent(TRACE_DEBUG, "Tx PONG to %s",
macaddr_str(mac_buf, query.srcMac));
@ -1727,6 +1915,7 @@ static int process_udp (n2n_sn_t * sss,
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t));
pi.aflags = 0;
memcpy(pi.srcMac, query.srcMac, sizeof(n2n_mac_t));
memcpy(pi.mac, query.targetMac, sizeof(n2n_mac_t));
pi.sock = scan->sock;
@ -1737,13 +1926,9 @@ static int process_udp (n2n_sn_t * sss,
comm->header_iv_ctx,
time_stamp());
}
// back to sender, be it edge or supernode (which will forward to edge)
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, encbuf, encx);
if(cmn.flags & N2N_FLAGS_SOCKET) {
sendto_sock(sss, &query.sock, encbuf, encx);
} else {
sendto(sss->sock, encbuf, encx, 0,
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in));
}
traceEvent(TRACE_DEBUG, "Tx PEER_INFO to %s",
macaddr_str(mac_buf, query.srcMac));
@ -1757,12 +1942,7 @@ static int process_udp (n2n_sn_t * sss,
macaddr_str(mac_buf, query.srcMac));
memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
/* We are going to add socket even if it was not there before */
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE;
query.sock.family = AF_INET;
query.sock.port = ntohs(sender_sock->sin_port);
memcpy(query.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
cmn2.flags |= N2N_FLAGS_FROM_SUPERNODE;
encode_QUERY_PEER(encbuf, &encx, &cmn2, &query);
@ -1776,7 +1956,53 @@ static int process_udp (n2n_sn_t * sss,
}
}
}
break;
}
case MSG_TYPE_PEER_INFO: {
n2n_PEER_INFO_t pi;
uint8_t encbuf[N2N_SN_PKTBUF_SIZE];
size_t encx = 0;
struct peer_info *peer;
if(!comm) {
traceEvent(TRACE_DEBUG, "process_udp PEER_INFO with unknown community %s", cmn.community);
return -1;
}
decode_PEER_INFO(&pi, &cmn, udp_buf, &rem, &idx);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_edge_time_stamp_and_verify(comm->edges, sn, pi.srcMac, stamp, TIME_STAMP_NO_JITTER)) {
traceEvent(TRACE_DEBUG, "process_udp dropped PEER_INFO due to time stamp error.");
return -1;
}
}
traceEvent(TRACE_INFO, "Rx PEER_INFO from %s [%s]",
macaddr_str(mac_buf, pi.srcMac),
sock_to_cstr(sockbuf, &sender));
HASH_FIND_PEER(comm->edges, pi.srcMac, peer);
if(peer != NULL) {
if((comm->is_federation == IS_NO_FEDERATION) && (!is_null_mac(pi.srcMac))) {
// this is a PEER_INFO for one of the edges conencted to this supernode, forward,
// i.e. re-assemble (memcpy of udpbuf to encbuf could be sufficient as well)
// use incoming cmn (with already decreased TTL)
// PEER_INFO remains unchanged
encode_PEER_INFO(encbuf, &encx, &cmn, &pi);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(encbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx,
time_stamp());
}
sendto_peer(sss, peer, encbuf, encx);
}
}
break;
}
@ -1788,6 +2014,7 @@ static int process_udp (n2n_sn_t * sss,
return 0;
}
/** Long lived processing entry point. Split out from main to simply
* daemonisation on some platforms. */
int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
@ -1804,22 +2031,47 @@ int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
ssize_t bread;
int max_sock;
fd_set socket_mask;
n2n_tcp_connection_t *conn, *tmp_conn;
struct sn_community *comm, *tmp_comm;
struct peer_info *edge, *tmp_edge;
SOCKET tmp_sock;
n2n_sock_str_t sockbuf;
struct timeval wait_time;
time_t now = 0;
time_t before, now = 0;
FD_ZERO(&socket_mask);
max_sock = MAX(sss->sock, sss->mgmt_sock);
FD_SET(sss->sock, &socket_mask);
#ifdef N2N_HAVE_TCP
FD_SET(sss->tcp_sock, &socket_mask);
#endif
FD_SET(sss->mgmt_sock, &socket_mask);
max_sock = MAX(MAX(sss->sock, sss->mgmt_sock), sss->tcp_sock);
#ifdef N2N_HAVE_TCP
// add the tcp connections' sockets
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) {
//socket descriptor
FD_SET(conn->socket_fd, &socket_mask);
if(conn->socket_fd > max_sock)
max_sock = conn->socket_fd;
}
#endif
wait_time.tv_sec = 10;
wait_time.tv_usec = 0;
before = time(NULL);
rc = select(max_sock + 1, &socket_mask, NULL, NULL, &wait_time);
now = time(NULL);
if(rc > 0) {
// external udp
if(FD_ISSET(sss->sock, &socket_mask)) {
struct sockaddr_in sender_sock;
socklen_t i;
@ -1843,13 +2095,90 @@ int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
break;
}
/* We have a datagram to process */
// we have a datagram to process...
if(bread > 0) {
/* And the datagram has data (not just a header) */
process_udp(sss, &sender_sock, pktbuf, bread, now);
// ...and the datagram has data (not just a header)
process_udp(sss, &sender_sock, sss->sock, pktbuf, bread, now);
}
}
#ifdef N2N_HAVE_TCP
// the so far known tcp connections
// do NOT use 'HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) {' to iterate because
// deletion of OTHER connections (that can happen if forwarding to another edge node fails)
// may rsult in seg faults if using HASH ITER which is only safe to use if deleting current item
for(conn = sss->tcp_connections; conn != NULL; conn = conn->hh.next ) {
if(FD_ISSET(conn->socket_fd, &socket_mask)) {
struct sockaddr_in sender_sock;
socklen_t i;
i = sizeof(sender_sock);
bread = recvfrom(conn->socket_fd,
conn->buffer + conn->position, conn->expected - conn->position, 0 /*flags*/,
(struct sockaddr *)&sender_sock, (socklen_t *)&i);
if(bread <= 0) {
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno));
#ifdef WIN32
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError());
#endif
close_tcp_connection(sss, conn);
continue;
}
conn->position += bread;
if(conn->position == conn->expected) {
if(conn->position == sizeof(uint16_t)) {
// the prepended length has been read, preparing for the packet
conn->expected += be16toh(*(uint16_t*)(conn->buffer));
if(conn->expected > N2N_SN_PKTBUF_SIZE) {
traceEvent(TRACE_ERROR, "too many bytes in tcp packet expected");
close_tcp_connection(sss, conn);
continue;
}
} else {
// full packet read, handle it
process_udp(sss, (struct sockaddr_in*)&(conn->sock), conn->socket_fd,
conn->buffer + sizeof(uint16_t), conn->position - sizeof(uint16_t), now);
// reset, await new prepended length
conn->expected = sizeof(uint16_t);
conn->position = 0;
}
}
}
}
// accept new incoming tcp connection
if(FD_ISSET(sss->tcp_sock, &socket_mask)) {
struct sockaddr_in sender_sock;
socklen_t i;
i = sizeof(sender_sock);
if((HASH_COUNT(sss->tcp_connections) + 4) < FD_SETSIZE) {
tmp_sock = accept(sss->tcp_sock, (struct sockaddr *)&sender_sock, (socklen_t *)&i);
if(tmp_sock >= 0) {
conn = (n2n_tcp_connection_t*)malloc(sizeof(n2n_tcp_connection_t));
if(conn) {
conn->socket_fd = tmp_sock;
memcpy(&(conn->sock), &sender_sock, sizeof(struct sockaddr_in));
conn->expected = sizeof(uint16_t);
conn->position = 0;
HASH_ADD_INT(sss->tcp_connections, socket_fd, conn);
traceEvent(TRACE_DEBUG, "run_sn_loop accepted incoming TCP connection from %s",
sock_to_cstr(sockbuf, (n2n_sock_t*)&sender_sock));
}
}
} else {
// no space to store the socket for a new connection, close immediately
traceEvent(TRACE_DEBUG, "run_sn_loop denied incoming TCP connection from %s due to max connections limit hit",
sock_to_cstr(sockbuf, (n2n_sock_t*)&sender_sock));
}
}
#endif /* N2N_HAVE_TCP */
// handle management port input
if(FD_ISSET(sss->mgmt_sock, &socket_mask)) {
struct sockaddr_in sender_sock;
size_t i;
@ -1864,11 +2193,19 @@ int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
break;
}
/* We have a datagram to process */
// we have a datagram to process
process_mgmt(sss, &sender_sock, pktbuf, bread, now);
}
} else {
traceEvent(TRACE_DEBUG, "timeout");
if(((now - before) < wait_time.tv_sec) && (*keep_running)){
// this is no real timeout, something went wrong with one of the tcp connections (probably)
// close them all, edges will re-open if they detect closure
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn)
close_tcp_connection(sss, conn);
traceEvent(TRACE_DEBUG, "falsly claimed timeout, assuming issue with tcp connection, closing them all");
} else
traceEvent(TRACE_DEBUG, "timeout");
}
re_register_and_purge_supernodes(sss, sss->federation, &last_re_reg_and_purge, now);