From a482fe112d18d10075be283b7c8fcd41c762b9f5 Mon Sep 17 00:00:00 2001 From: Logan oos Even <46396513+Logan007@users.noreply.github.com> Date: Mon, 22 Feb 2021 22:37:47 +0545 Subject: [PATCH] added TCP support (#627) (disabled on Windows) --- include/n2n.h | 18 +- include/n2n_define.h | 4 +- include/n2n_typedefs.h | 20 +- include/sn_selection.h | 1 + src/edge.c | 71 +++-- src/edge_utils.c | 682 +++++++++++++++++++++++++++-------------- src/example_sn_embed.c | 4 +- src/n2n.c | 29 +- src/sn.c | 29 +- src/sn_selection.c | 17 +- src/sn_utils.c | 467 ++++++++++++++++++++++++---- 11 files changed, 1014 insertions(+), 328 deletions(-) diff --git a/include/n2n.h b/include/n2n.h index 462937c..8c3e4e5 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -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 #include #include +#include #include #include #include @@ -132,6 +135,9 @@ #include "n2n_typedefs.h" #ifdef WIN32 +#include /* for tcp */ +#define SHUT_RDWR SD_BOTH /* for tcp */ +#define SOL_TCP IPPROTO_TCP /* for tcp */ #include "win32/wintap.h" #include #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); diff --git a/include/n2n_define.h b/include/n2n_define.h index c5e2599..f9f707a 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -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}; diff --git a/include/n2n_typedefs.h b/include/n2n_typedefs.h index 2113e0f..2e7162a 100644 --- a/include/n2n_typedefs.h +++ b/include/n2n_typedefs.h @@ -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 diff --git a/include/sn_selection.h b/include/sn_selection.h index 54789ab..333e969 100644 --- a/include/sn_selection.h +++ b/include/sn_selection.h @@ -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 */ diff --git a/src/edge.c b/src/edge.c index 1c61e1c..577225c 100644 --- a/src/edge.c +++ b/src/edge.c @@ -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 ]" "[-L ]" @@ -168,6 +170,8 @@ static void help (int level) { "[-A] " "[-H] " "[-z]" + "\n " + "[-S]" "\n\n tap device and " "[-a [static:|dhcp:][/]] " "\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 | registration interval, for NAT hole punching (default\n" " | 20 seconds)\n"); printf(" -L | 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 diff --git a/src/edge_utils.c b/src/edge_utils.c index 821150d..7142bc8 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -36,7 +36,7 @@ static void check_peer_registration_needed (n2n_edge_t *eee, const n2n_desc_t *dev_desc, const n2n_sock_t *peer); -static int edge_init_sockets (n2n_edge_t *eee, int udp_local_port, int mgmt_port, uint8_t tos); +static int edge_init_sockets (n2n_edge_t *eee); static int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes); static void edge_cleanup_routes (n2n_edge_t *eee); @@ -98,7 +98,7 @@ void* edge_get_userdata (n2n_edge_t *eee) { int edge_get_n2n_socket (n2n_edge_t *eee) { - return(eee->udp_sock); + return(eee->sock); } /* ************************************** */ @@ -180,6 +180,99 @@ static int is_ip6_discovery (const void * buf, size_t bufsize) { return retval; } + +/* ************************************** */ + + +// reset number of supernode connection attempts: try only once for already more realiable tcp connections +void reset_sup_attempts (n2n_edge_t *eee) { + + eee->sup_attempts = (eee->conf.connect_tcp) ? 1 : N2N_EDGE_SUP_ATTEMPTS; +} + + + +// open socket, close it before if TCP +// in case of TCP, 'connect()' is required +int supernode_connect(n2n_edge_t *eee) { + + int sockopt; + + if((eee->conf.connect_tcp) && (eee->sock >= 0)) { + closesocket(eee->sock); + eee->sock = -1; + } + + if(eee->sock < 0) { + + if(eee->conf.local_port > 0) + traceEvent(TRACE_NORMAL, "Binding to local port %d", + (eee->conf.connect_tcp) ? 0 : eee->conf.local_port); + + eee->sock = open_socket((eee->conf.connect_tcp) ? 0 : eee->conf.local_port, + 1 /* bind ANY */, eee->conf.connect_tcp); + + if(eee->sock < 0) { + traceEvent(TRACE_ERROR, "Failed to bind main UDP port %u", + (eee->conf.connect_tcp) ? 0 : eee->conf.local_port); + return -1; + } + + struct sockaddr_in sock; + sock.sin_family = AF_INET; + sock.sin_port = htons(eee->curr_sn->sock.port); + memcpy(&(sock.sin_addr.s_addr), &(eee->curr_sn->sock.addr.v4), IPV4_SIZE); + + // set tcp socket to O_NONBLOCK so connect does not hang + // requires checking the socket for readiness before sending and receving + if(eee->conf.connect_tcp) { +#ifdef WIN32 + u_long value = 1; + ioctlsocket(eee->sock, FIONBIO, &value); +#else + fcntl(eee->sock, F_SETFL, O_NONBLOCK); +#endif + if((connect(eee->sock, (struct sockaddr*)&(sock), sizeof(struct sockaddr)) < 0) + && (errno != EINPROGRESS)) { + eee->sock = -1; + return -1; + } + } + + if(eee->conf.tos) { + /* https://www.tucny.com/Home/dscp-tos */ + sockopt = eee->conf.tos; + + if(setsockopt(eee->sock, IPPROTO_IP, IP_TOS, (char *)&sockopt, sizeof(sockopt)) == 0) + traceEvent(TRACE_NORMAL, "TOS set to 0x%x", eee->conf.tos); + else + traceEvent(TRACE_ERROR, "Could not set TOS 0x%x[%d]: %s", eee->conf.tos, errno, strerror(errno)); + } +#ifdef IP_PMTUDISC_DO + sockopt = (eee->conf.disable_pmtu_discovery) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO; + + if(setsockopt(eee->sock, IPPROTO_IP, IP_MTU_DISCOVER, &sockopt, sizeof(sockopt)) < 0) + traceEvent(TRACE_WARNING, "Could not %s PMTU discovery[%d]: %s", + (eee->conf.disable_pmtu_discovery) ? "disable" : "enable", errno, strerror(errno)); + else + traceEvent(TRACE_DEBUG, "PMTU discovery %s", (eee->conf.disable_pmtu_discovery) ? "disabled" : "enabled"); +#endif + } + + return 0; +} + + +// always closes the socket +void supernode_disconnect(n2n_edge_t *eee) { + + if(eee->sock >= 0) { + closesocket(eee->sock); + eee->sock = -1; + } +} + + /* ************************************** */ /** Initialise an edge to defaults. @@ -211,7 +304,8 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) { eee->known_peers = NULL; eee->pending_peers = NULL; - eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; + reset_sup_attempts(eee); + sn_selection_criterion_common_data_default(eee); pearson_hash_init(); @@ -277,12 +371,12 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) { // first time calling edge_init_sockets needs -1 in the sockets for it does throw an error // on trying to close them (open_sockets does so for also being able to RE-open the sockets // if called in-between, see "Supernode not responding" in update_supernode_reg(...) - eee->udp_sock = -1; + eee->sock = -1; eee->udp_mgmt_sock = -1; #ifndef SKIP_MULTICAST_PEERS_DISCOVERY eee->udp_multicast_sock = -1; #endif - if(edge_init_sockets(eee, eee->conf.local_port, eee->conf.mgmt_port, eee->conf.tos) < 0) { + if(edge_init_sockets(eee) < 0) { traceEvent(TRACE_ERROR, "socket setup failed"); goto edge_init_error; } @@ -487,14 +581,14 @@ static void register_with_new_peer (n2n_edge_t *eee, n2n_sock_t sock = scan->sock; int alter = 16; /* TODO: set by command line or more reliable prediction method */ - getsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, (void *) (char *) &curTTL, &lenTTL); - setsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, + getsockopt(eee->sock, IPPROTO_IP, IP_TTL, (void *) (char *) &curTTL, &lenTTL); + setsockopt(eee->sock, IPPROTO_IP, IP_TTL, (void *) (char *) &eee->conf.register_ttl, sizeof(eee->conf.register_ttl)); for(; alter > 0; alter--, sock.port++) { send_register(eee, &sock, mac); } - setsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, (void *) (char *) &curTTL, sizeof(curTTL)); + setsockopt(eee->sock, IPPROTO_IP, IP_TTL, (void *) (char *) &curTTL, sizeof(curTTL)); #endif } else { /* eee->conf.register_ttl <= 0 */ /* Normal STUN */ @@ -505,7 +599,6 @@ static void register_with_new_peer (n2n_edge_t *eee, /* P2P register, send directly */ send_register(eee, &(scan->sock), mac); } - register_with_local_peers(eee); } else{ scan->sock = *peer; @@ -714,65 +807,140 @@ static void check_known_peer_sock_change (n2n_edge_t *eee, /* ************************************** */ +/** Send a datagram to a socket file descriptor */ +static ssize_t sendto_fd (n2n_edge_t *eee, const void *buf, + size_t len, struct sockaddr_in *dest) { + + ssize_t sent = 0; + int rc = 1; + + // if required (tcp), wait until writeable as soket is set to O_NONBLOCK, could require + // some wait time directly after re-opening + if(eee->conf.connect_tcp) { + fd_set socket_mask; + struct timeval wait_time; + + FD_ZERO(&socket_mask); + FD_SET(eee->sock, &socket_mask); + wait_time.tv_sec = 0; + wait_time.tv_usec = 500000; + rc = select(eee->sock + 1, NULL, &socket_mask, NULL, &wait_time); + } + + if (rc > 0) { + + sent = sendto(eee->sock, buf, len, 0 /*flags*/, + (struct sockaddr *)dest, sizeof(struct sockaddr_in)); + + if(sent <= 0) { + char * c = strerror(errno); + traceEvent(TRACE_ERROR, "sendto_fd sendto failed (%d) %s", errno, c); +#ifdef WIN32 + traceEvent(TRACE_ERROR, "sendto_fd WSAGetLastError(): %u", WSAGetLastError()); +#endif + if(eee->conf.connect_tcp) { + supernode_disconnect(eee); + eee->sn_wait = 1; + traceEvent(TRACE_DEBUG, "sendto_fd disconnected supernode due to sendto() error"); + return -1; + } + } else { + traceEvent(TRACE_DEBUG, "sendto_fd sent=%d to ", (signed int)sent); + } + } else { + supernode_disconnect(eee); + eee->sn_wait = 1; + traceEvent(TRACE_DEBUG, "sendto_fd disconnected supernode due to select() timeout"); + return -1; + } + return sent; +} + + /** Send a datagram to a socket defined by a n2n_sock_t */ -static ssize_t sendto_sock (int fd, const void * buf, +static ssize_t sendto_sock (n2n_edge_t *eee, const void * buf, size_t len, const n2n_sock_t * dest) { struct sockaddr_in peer_addr; ssize_t sent; + int rc; + int value = 0; if(!dest->family) - // Invalid socket + // invalid socket return 0; - fill_sockaddr((struct sockaddr *) &peer_addr, - sizeof(peer_addr), - dest); + if(eee->sock < 0) + // invalid socket file descriptor, e.g. TCP unconnected has fd of '-1' + return 0; - sent = sendto(fd, buf, len, 0/*flags*/, - (struct sockaddr *)&peer_addr, sizeof(struct sockaddr_in)); + // network order socket + fill_sockaddr((struct sockaddr *) &peer_addr, sizeof(peer_addr), dest); - if(sent < 0) { - char * c = strerror(errno); - traceEvent(TRACE_ERROR, "sendto failed (%d) %s", errno, c); -#ifdef WIN32 - traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); + // if the connection is tcp, i.e. not the regular sock... + if(eee->conf.connect_tcp) { + + setsockopt(eee->sock, SOL_TCP, TCP_NODELAY, &value, sizeof(value)); + value = 1; +#ifndef WIN32 + setsockopt(eee->sock, SOL_TCP, TCP_CORK, &value, sizeof(value)); +#endif + + // prepend packet length... + uint16_t pktsize16 = htobe16(len); + sent = sendto_fd(eee, (uint8_t*)&pktsize16, sizeof(pktsize16), &peer_addr); + + if(sent <= 0) + return -1; + // ...before sending the actual data + } + sent = sendto_fd(eee, buf, len, &peer_addr); + + // if the connection is tcp, i.e. not the regular sock... + if(eee->conf.connect_tcp) { + value = 1; /* value should still be set to 1 */ + setsockopt(eee->sock, SOL_TCP, TCP_NODELAY, &value, sizeof(value)); +#ifndef WIN32 + value = 0; + setsockopt(eee->sock, SOL_TCP, TCP_CORK, &value, sizeof(value)); #endif - } else { - traceEvent(TRACE_DEBUG, "sendto sent=%d to ", (signed int)sent); } return sent; } + /* ************************************** */ + /* Bind eee->udp_multicast_sock to multicast group */ static void check_join_multicast_group (n2n_edge_t *eee) { #ifndef SKIP_MULTICAST_PEERS_DISCOVERY - if(!eee->multicast_joined) { - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = inet_addr(N2N_MULTICAST_GROUP); + if(eee->conf.allow_p2p) { + if(!eee->multicast_joined) { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(N2N_MULTICAST_GROUP); #ifdef WIN32 - dec_ip_str_t ip_addr; - get_best_interface_ip(eee, ip_addr); - mreq.imr_interface.s_addr = inet_addr(ip_addr); + dec_ip_str_t ip_addr; + get_best_interface_ip(eee, ip_addr); + mreq.imr_interface.s_addr = inet_addr(ip_addr); #else - mreq.imr_interface.s_addr = htonl(INADDR_ANY); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); #endif - if(setsockopt(eee->udp_multicast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { - traceEvent(TRACE_WARNING, "Failed to bind to local multicast group %s:%u [errno %u]", - N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT, errno); + if(setsockopt(eee->udp_multicast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { + traceEvent(TRACE_WARNING, "Failed to bind to local multicast group %s:%u [errno %u]", + N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT, errno); #ifdef WIN32 - traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); + traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); #endif - } else { - traceEvent(TRACE_NORMAL, "Successfully joined multicast group %s:%u", - N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT); - eee->multicast_joined = 1; + } else { + traceEvent(TRACE_NORMAL, "Successfully joined multicast group %s:%u", + N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT); + eee->multicast_joined = 1; + } } } #endif @@ -782,7 +950,7 @@ static void check_join_multicast_group (n2n_edge_t *eee) { /** Send a QUERY_PEER packet to the current supernode. */ void send_query_peer (n2n_edge_t * eee, - const n2n_mac_t dst_mac) { + const n2n_mac_t dst_mac) { uint8_t pktbuf[N2N_PKT_BUF_SIZE]; size_t idx; @@ -819,7 +987,7 @@ void send_query_peer (n2n_edge_t * eee, time_stamp ()); } - sendto_sock(eee->udp_sock, pktbuf, idx, &(eee->curr_sn->sock)); + sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); } else { traceEvent(TRACE_DEBUG, "send PING to supernodes"); @@ -855,7 +1023,7 @@ void send_query_peer (n2n_edge_t * eee, // done with the remaining (do not send anymore) break; } - sendto_sock(eee->udp_sock, pktbuf, idx, &(peer->sock)); + sendto_sock(eee, pktbuf, idx, &(peer->sock)); } } } @@ -904,7 +1072,7 @@ void send_register_super (n2n_edge_t *eee) { eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, time_stamp()); - /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, &(eee->curr_sn->sock)); + /* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); } @@ -940,7 +1108,7 @@ static void send_unregister_super (n2n_edge_t *eee) { eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, time_stamp()); - /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, &(eee->curr_sn->sock)); + /* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); } @@ -949,34 +1117,36 @@ static int sort_supernodes (n2n_edge_t *eee, time_t now) { struct peer_info *scan, *tmp; - if(eee->curr_sn != eee->conf.supernodes) { - // have not been connected to the best/top one - send_unregister_super(eee); - - eee->curr_sn = eee->conf.supernodes; - eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; - - traceEvent(TRACE_INFO, "Registering with supernode [%s][number of supernodes %d][attempts left %u]", - supernode_ip(eee), HASH_COUNT(eee->conf.supernodes), (unsigned int)eee->sup_attempts); - - send_register_super(eee); - eee->sn_wait = 1; - } - if(now - eee->last_sweep > SWEEP_TIME) { + // this routine gets periodically called + if(eee->sn_wait == 0) { - // this routine gets periodically called - // it sorts supernodes in ascending order of their selection_criterion fields + // sort supernodes in ascending order of their selection_criterion fields sn_selection_sort(&(eee->conf.supernodes)); } + if(eee->curr_sn != eee->conf.supernodes) { + // we have not been connected to the best/top one + send_unregister_super(eee); + eee->curr_sn = eee->conf.supernodes; + reset_sup_attempts(eee); + supernode_connect(eee); + + traceEvent(TRACE_INFO, "Registering with supernode [%s][number of supernodes %d][attempts left %u]", + supernode_ip(eee), HASH_COUNT(eee->conf.supernodes), (unsigned int)eee->sup_attempts); + + send_register_super(eee); + eee->sn_wait = 1; + } + HASH_ITER(hh, eee->conf.supernodes, scan, tmp) { sn_selection_criterion_default(&(scan->selection_criterion)); } sn_selection_criterion_common_data_default(eee); // send PING to all the supernodes - send_query_peer(eee, null_mac); + if(!eee->conf.connect_tcp) + send_query_peer(eee, null_mac); eee->last_sweep = now; // no answer yet (so far, unused in regular edge code; mainly used during bootstrap loading) @@ -1016,7 +1186,7 @@ static void send_register (n2n_edge_t * eee, encode_mac(reg.srcMac, &idx, eee->device.mac_addr); if(peer_mac) { - /* Can be NULL for multicast registrations */ + // can be NULL for multicast registrations idx = 0; encode_mac(reg.dstMac, &idx, peer_mac); } @@ -1036,7 +1206,7 @@ static void send_register (n2n_edge_t * eee, eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, time_stamp()); - /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); + /* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer); } /* ************************************** */ @@ -1081,7 +1251,7 @@ static void send_register_ack (n2n_edge_t * eee, eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, time_stamp()); - /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); + /* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer); } /* ************************************** */ @@ -1135,37 +1305,39 @@ static void send_grat_arps (n2n_edge_t * eee) { * * This is frequently called by the main loop. */ -void update_supernode_reg (n2n_edge_t * eee, time_t nowTime) { +void update_supernode_reg (n2n_edge_t * eee, time_t now) { struct peer_info *scan, *tmp; - if(eee->sn_wait && (nowTime > (eee->last_register_req + (eee->conf.register_interval/10)))) { + if(eee->sn_wait && (now > (eee->last_register_req + (eee->conf.register_interval/10)))) { /* fall through */ traceEvent(TRACE_DEBUG, "update_supernode_reg: doing fast retry."); - } else if(nowTime < (eee->last_register_req + eee->conf.register_interval)) + } else if(now < (eee->last_register_req + eee->conf.register_interval)) return; /* Too early */ check_join_multicast_group(eee); if(0 == eee->sup_attempts) { /* Give up on that supernode and try the next one. */ - sn_selection_criterion_default(&(eee->curr_sn->selection_criterion)); + sn_selection_criterion_bad(&(eee->curr_sn->selection_criterion)); sn_selection_sort(&(eee->conf.supernodes)); eee->curr_sn = eee->conf.supernodes; - traceEvent(TRACE_WARNING, "Supernode not responding, now trying %s", supernode_ip(eee)); - - eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; + supernode_connect(eee); + reset_sup_attempts(eee); // in some multi-NATed scenarios communication gets stuck on losing connection to supernode // closing and re-opening the socket(s) allows for re-establishing communication - // this can only be done, if working on som eunprivileged port and/or having sufficent + // this can only be done, if working on some unprivileged port and/or having sufficent // privileges. as we are not able to check for sufficent privileges here, we only do it // if port is sufficently high or unset. uncovered: privileged port and sufficent privileges if((eee->conf.local_port == 0) || (eee->conf.local_port > 1024)) { - if(edge_init_sockets(eee, eee->conf.local_port, eee->conf.mgmt_port, eee->conf.tos) < 0) { - traceEvent(TRACE_ERROR, "socket re-initiliaization failed"); + if(edge_init_sockets(eee) < 0) { + traceEvent(TRACE_ERROR, "update_supernode_reg failed while trying socket re-initiliaization"); } + supernode_disconnect(eee); + supernode_connect(eee); + traceEvent(TRACE_DEBUG, "update_supernode_reg disconnected and reconnected supernode "); } } else { @@ -1178,13 +1350,11 @@ void update_supernode_reg (n2n_edge_t * eee, time_t nowTime) { send_register_super(eee); } - - register_with_local_peers(eee); eee->sn_wait = 1; - eee->last_register_req = nowTime; + eee->last_register_req = now; } /* ************************************** */ @@ -1505,7 +1675,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) { "%4u | %-15s | %-17s | %-21s | %-15s | %9s\n", ++num, (peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net), - 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->dev_desc, (peer->last_seen) ? time_buf : ""); @@ -1529,7 +1699,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) { "%4u | %-15s | %-17s | %-21s | %-15s | %9s\n", ++num, (peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net), - 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->dev_desc, (peer->last_seen) ? time_buf : ""); @@ -1550,9 +1720,10 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) { net = htonl(peer->dev_addr.net_addr); sprintf (time_buf, "%9u", now - peer->last_seen); msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), - "%4u | %-15s | %-17s | %-21s | %-15s | %9s\n", + "%4u | %-3s %-11s | %-17s | %-21s | %-15s | %9s\n", ++num, (peer->purgeable == SN_UNPURGEABLE) ? "-l" : "", + (peer == eee->curr_sn) ? (eee->sn_wait ? ">>..." : ">>>>>" ) : "", is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr), sock_to_cstr(sockbuf, &(peer->sock)), sn_selection_criterion_str(sel_buf, peer), @@ -1658,9 +1829,8 @@ static int find_peer_destination (n2n_edge_t * eee, return(0); } - traceEvent(TRACE_DEBUG, "Searching destination peer for MAC %02X:%02X:%02X:%02X:%02X:%02X", - mac_address[0] & 0xFF, mac_address[1] & 0xFF, mac_address[2] & 0xFF, - mac_address[3] & 0xFF, mac_address[4] & 0xFF, mac_address[5] & 0xFF); + traceEvent(TRACE_DEBUG, "Searching destination peer for MAC %s", + macaddr_str(mac_buf, mac_address)); HASH_FIND_PEER(eee->known_peers, mac_address, scan); @@ -1681,9 +1851,8 @@ static int find_peer_destination (n2n_edge_t * eee, if(retval == 0) { memcpy(destination, &(eee->curr_sn->sock), sizeof(struct sockaddr_in)); - traceEvent(TRACE_DEBUG, "P2P Peer [MAC=%02X:%02X:%02X:%02X:%02X:%02X] not found, using supernode", - mac_address[0] & 0xFF, mac_address[1] & 0xFF, mac_address[2] & 0xFF, - mac_address[3] & 0xFF, mac_address[4] & 0xFF, mac_address[5] & 0xFF); + traceEvent(TRACE_DEBUG, "P2P Peer [MAC=%s] not found, using supernode", + macaddr_str(mac_buf, mac_address)); check_query_peer_info(eee, now, mac_address); } @@ -1727,7 +1896,7 @@ static int send_packet (n2n_edge_t * eee, sock_to_cstr(sockbuf, &destination), macaddr_str(mac_buf, dstMac), pktlen); - /* s = */ sendto_sock(eee->udp_sock, pktbuf, pktlen, &destination); + /* s = */ sendto_sock(eee, pktbuf, pktlen, &destination); return 0; } @@ -1904,97 +2073,80 @@ void edge_read_from_tap (n2n_edge_t * eee) { is_ethMulticast(eth_pkt, len))) { traceEvent(TRACE_INFO, "Dropping TX multicast"); } else { - if(eee->network_traffic_filter) { - if(eee->network_traffic_filter->filter_packet_from_tap(eee->network_traffic_filter, eee, eth_pkt, + if(!eee->last_sup) { + // drop packets before first registration with supernode + traceEvent(TRACE_DEBUG, "DROP packet before first registration with supernode"); + return; + } + + if(eee->network_traffic_filter) { + if(eee->network_traffic_filter->filter_packet_from_tap(eee->network_traffic_filter, eee, eth_pkt, len) == N2N_DROP) { - traceEvent(TRACE_DEBUG, "Filtered packet %u", (unsigned int)len); - return; - } - } - - if(eee->cb.packet_from_tap) { - uint16_t tmp_len = len; - if(eee->cb.packet_from_tap(eee, eth_pkt, &tmp_len) == N2N_DROP) { - traceEvent(TRACE_DEBUG, "DROP packet %u", (unsigned int)len); - - return; - } - len = tmp_len; - } - - if(!eee->last_sup) { - // drop packets before first registration with supernode - traceEvent(TRACE_DEBUG, "DROP packet before first registration with supernode"); + traceEvent(TRACE_DEBUG, "Filtered packet %u", (unsigned int)len); return; } - - edge_send_packet2net(eee, eth_pkt, len); } + + if(eee->cb.packet_from_tap) { + uint16_t tmp_len = len; + if(eee->cb.packet_from_tap(eee, eth_pkt, &tmp_len) == N2N_DROP) { + traceEvent(TRACE_DEBUG, "DROP packet %u", (unsigned int)len); + return; + } + len = tmp_len; + } + + edge_send_packet2net(eee, eth_pkt, len); } + } } + /* ************************************** */ -/** Read a datagram from the main UDP socket to the internet. */ -void readFromIPSocket (n2n_edge_t * eee, int in_sock) { + +/** handle a datagram from the main UDP socket to the internet. */ +void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const SOCKET in_sock, + uint8_t *udp_buf, size_t udp_size, time_t now) { n2n_common_t cmn; /* common fields in the packet header */ n2n_sock_str_t sockbuf1; n2n_sock_str_t sockbuf2; /* don't clobber sockbuf1 if writing two addresses to trace */ macstr_t mac_buf1; macstr_t mac_buf2; - uint8_t udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */ - ssize_t recvlen; size_t rem; size_t idx; size_t msg_type; uint8_t from_supernode; peer_info_t *sn = NULL; - struct sockaddr_in sender_sock; n2n_sock_t sender; n2n_sock_t * orig_sender = NULL; - time_t now = 0; uint64_t stamp = 0; - size_t i; int skip_add = 0; - i = sizeof(sender_sock); - recvlen = recvfrom(in_sock, udp_buf, N2N_PKT_BUF_SIZE, 0/*flags*/, - (struct sockaddr *)&sender_sock, (socklen_t*)&i); - - if(recvlen < 0) { -#ifdef WIN32 - if(WSAGetLastError() != WSAECONNRESET) -#endif - { - traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", recvlen, errno, strerror(errno)); -#ifdef WIN32 - traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); -#endif - } - - return; /* failed to receive data from UDP */ - } - /* REVISIT: when UDP/IPv6 is supported we will need a flag to indicate which * IP transport version the packet arrived on. May need to UDP sockets. */ memset(&sender, 0, sizeof(n2n_sock_t)); - sender.family = AF_INET; /* UDP socket was opened PF_INET v4 */ - sender.port = ntohs(sender_sock.sin_port); - memcpy(&(sender.addr.v4), &(sender_sock.sin_addr.s_addr), IPV4_SIZE); - + if(eee->conf.connect_tcp) + // TCP expects that we know our comm partner and does not deliver the sender + memcpy(&sender, &(eee->curr_sn->sock), sizeof(struct sockaddr_in)); + else { + sender.family = AF_INET; /* UDP socket was opened PF_INET v4 */ + sender.port = ntohs(sender_sock->sin_port); + memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE); + } /* The packet may not have an orig_sender socket spec. So default to last * hop as sender. */ orig_sender = &sender; traceEvent(TRACE_DEBUG, "### Rx N2N UDP (%d) from %s", - (signed int)recvlen, sock_to_cstr(sockbuf1, &sender)); + (signed int)udp_size, sock_to_cstr(sockbuf1, &sender)); if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { uint16_t checksum = 0; - if(packet_header_decrypt(udp_buf, recvlen, + if(packet_header_decrypt(udp_buf, udp_size, (char *)eee->conf.community_name, eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, &stamp) == 0) { @@ -2007,15 +2159,13 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) { // on packet type, path taken (via supernode) and packet structure (MAC is not always in the same place) } - rem = recvlen; /* Counts down bytes of packet to protect against buffer overruns. */ + rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */ idx = 0; /* marches through packet header as parts are decoded. */ if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) { traceEvent(TRACE_ERROR, "Failed to decode common section in N2N_UDP"); return; /* failed to decode packet */ } - now = time(NULL); - msg_type = cmn.pc; /* packet code */ // check if packet is from supernode and find the corresponding supernode in list @@ -2062,18 +2212,18 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) { * handle_PACKET to double check this. */ traceEvent(TRACE_DEBUG, "Got P2P packet"); - traceEvent(TRACE_DEBUG, "[P2P] Rx data from %s [%u B]", sock_to_cstr(sockbuf1, &sender), recvlen); + traceEvent(TRACE_DEBUG, "[P2P] Rx data from %s [%u B]", sock_to_cstr(sockbuf1, &sender), udp_size); find_and_remove_peer(&eee->pending_peers, pkt.srcMac); } else { /* [PsP] : edge Peer->Supernode->edge Peer */ traceEvent(TRACE_DEBUG, "[PsP] Rx data from %s (Via=%s) [%u B]", - sock_to_cstr(sockbuf2, orig_sender), sock_to_cstr(sockbuf1, &sender), recvlen); + sock_to_cstr(sockbuf2, orig_sender), sock_to_cstr(sockbuf1, &sender), udp_size); } /* Update the sender in peer table entry */ check_peer_registration_needed(eee, from_supernode, pkt.srcMac, NULL, NULL, orig_sender); - handle_PACKET(eee, from_supernode, &pkt, orig_sender, udp_buf + idx, recvlen - idx); + handle_PACKET(eee, from_supernode, &pkt, orig_sender, udp_buf + idx, udp_size - idx); break; } @@ -2246,7 +2396,7 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) { } eee->sn_wait = 0; - eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because we got a response */ + reset_sup_attempts(eee); /* refresh because we got a response */ /* NOTE: the register_interval should be chosen by the edge node * based on its NAT configuration. */ @@ -2324,6 +2474,9 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) { scan->last_seen = now; /* The data type depends on the actual selection strategy that has been chosen. */ sn_selection_criterion_calculate(eee, scan, &pi.data); + traceEvent(TRACE_INFO, "Rx PONG from supernode '%s'", + macaddr_str(mac_buf1, pi.srcMac)); + break; } } else { @@ -2357,8 +2510,93 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) { traceEvent(TRACE_INFO, "Ignoring packet with unknown community"); } + /* ************************************** */ + +int fetch_and_eventually_process_data (n2n_edge_t *eee, SOCKET sock, + uint8_t *pktbuf, uint16_t *expected, uint16_t *position, + time_t now) { + + size_t bread = 0; + + if((!eee->conf.connect_tcp) || (sock == eee->udp_multicast_sock)) { + // udp + struct sockaddr_in sender_sock; + socklen_t i; + + i = sizeof(sender_sock); + bread = recvfrom(sock, pktbuf, N2N_PKT_BUF_SIZE, 0 /*flags*/, + (struct sockaddr *)&sender_sock, (socklen_t *)&i); + + if((bread < 0) +#ifdef WIN32 + && (WSAGetLastError() != WSAECONNRESET) +#endif + ) { + /* For UDP bread of zero just means no data (unlike TCP). */ + /* The fd is no good now. Maybe we lost our interface. */ + traceEvent(TRACE_ERROR, "fetch_and_eventually_process_data's recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno)); +#ifdef WIN32 + traceEvent(TRACE_ERROR, "fetch_and_eventually_process_data's WSAGetLastError(): %u", WSAGetLastError()); +#endif + return -1; + } + + // we have a datagram to process... + if(bread > 0) { + // ...and the datagram has data (not just a header) + process_udp(eee, &sender_sock, sock, pktbuf, bread, now); + } + + } else { + // tcp + struct sockaddr_in sender_sock; + socklen_t i; + + i = sizeof(sender_sock); + bread = recvfrom(sock, + pktbuf + *position, *expected - *position, 0 /*flags*/, + (struct sockaddr *)&sender_sock, (socklen_t *)&i); + if(bread <= 0) { + traceEvent(TRACE_ERROR, "fetch_and_eventually_process_data's recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno)); +#ifdef WIN32 + traceEvent(TRACE_ERROR, "fetch_and_eventually_process_data's WSAGetLastError(): %u", WSAGetLastError()); +#endif + supernode_disconnect(eee); + eee->sn_wait = 1; + traceEvent(TRACE_DEBUG, "fetch_and_eventually_process_data disconnected supernode due to connection error"); + goto tcp_done; + } + *position = *position + bread; + + if(*position == *expected) { + if(*position == sizeof(uint16_t)) { + // the prepended length has been read, preparing for the packet + *expected = *expected + be16toh(*(uint16_t*)(pktbuf)); + if(*expected > N2N_PKT_BUF_SIZE) { + supernode_disconnect(eee); + eee->sn_wait = 1; + traceEvent(TRACE_DEBUG, "run_edge_loop disconnected supernode due to too many bytes expected"); + goto tcp_done; + } + } else { + // full packet read, handle it + process_udp(eee, (struct sockaddr_in*)&sender_sock, sock, + pktbuf + sizeof(uint16_t), *position - sizeof(uint16_t), now); + // reset, await new prepended length + *expected = sizeof(uint16_t); + *position = 0; + } + } + } + tcp_done: + ; + + return 0; +} + + void print_edge_stats (const n2n_edge_t *eee) { const struct n2n_edge_stats *s = &eee->stats; @@ -2372,9 +2610,11 @@ void print_edge_stats (const n2n_edge_t *eee) { traceEvent(TRACE_NORMAL, "**********************************"); } + /* ************************************** */ -int run_edge_loop (n2n_edge_t * eee, int *keep_running) { + +int run_edge_loop (n2n_edge_t *eee, int *keep_running) { size_t numPurged; time_t lastIfaceCheck = 0; @@ -2382,6 +2622,10 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { time_t last_purge_known = 0; time_t last_purge_pending = 0; + uint16_t expected = sizeof(uint16_t); + uint16_t position = 0; + uint8_t pktbuf[N2N_PKT_BUF_SIZE + sizeof(uint16_t)]; /* buffer + prepended buffer length in case of tcp */ + #ifdef WIN32 struct tunread_arg arg; arg.eee = eee; @@ -2400,19 +2644,27 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { */ while(*keep_running) { + int rc, max_sock = 0; fd_set socket_mask; struct timeval wait_time; - time_t nowTime; + time_t now; + size_t bread; FD_ZERO(&socket_mask); - FD_SET(eee->udp_sock, &socket_mask); - FD_SET(eee->udp_mgmt_sock, &socket_mask); - max_sock = max(eee->udp_sock, eee->udp_mgmt_sock); + FD_SET(eee->udp_mgmt_sock, &socket_mask); + max_sock = eee->udp_mgmt_sock; + + if(eee->sock >= 0) { + FD_SET(eee->sock, &socket_mask); + max_sock = max(eee->sock, eee->udp_mgmt_sock); + } #ifndef SKIP_MULTICAST_PEERS_DISCOVERY - FD_SET(eee->udp_multicast_sock, &socket_mask); - max_sock = max(eee->udp_sock, eee->udp_multicast_sock); + if(eee->conf.allow_p2p) { + FD_SET(eee->udp_multicast_sock, &socket_mask); + max_sock = max(eee->sock, eee->udp_multicast_sock); + } #endif #ifndef WIN32 @@ -2420,41 +2672,56 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { max_sock = max(max_sock, eee->device.fd); #endif - wait_time.tv_sec = (eee->sn_wait)?(SOCKET_TIMEOUT_INTERVAL_SECS / 10 + 1):(SOCKET_TIMEOUT_INTERVAL_SECS); + wait_time.tv_sec = (eee->sn_wait) ? (SOCKET_TIMEOUT_INTERVAL_SECS / 10 + 1) : (SOCKET_TIMEOUT_INTERVAL_SECS); wait_time.tv_usec = 0; - rc = select(max_sock + 1, &socket_mask, NULL, NULL, &wait_time); - nowTime = time(NULL); + now = time(NULL); - /* Make sure ciphers are updated before the packet is treated. */ - if((nowTime - lastTransop) > TRANSOP_TICK_INTERVAL) { - lastTransop = nowTime; + // make sure ciphers are updated before the packet is treated + if((now - lastTransop) > TRANSOP_TICK_INTERVAL) { + lastTransop = now; - eee->transop.tick(&eee->transop, nowTime); + eee->transop.tick(&eee->transop, now); } if(rc > 0) { - /* Any or all of the FDs could have input; check them all. */ + // any or all of the FDs could have input; check them all - if(FD_ISSET(eee->udp_sock, &socket_mask)) { - /* Read a cooked socket from the internet socket (unicast). Writes on the TAP - * socket. */ - readFromIPSocket(eee, eee->udp_sock); + // external + if(FD_ISSET(eee->sock, &socket_mask)) { + if (0 != fetch_and_eventually_process_data (eee, eee->sock, + pktbuf, &expected, &position, + now)) { + *keep_running = 0; + break; + } + if(eee->conf.connect_tcp) { + if((expected >= N2N_PKT_BUF_SIZE) || (position >= N2N_PKT_BUF_SIZE)) { + // something went wrong, possibly even before + // e.g. connection failure/closure in the middle of transmission (between len & data) + supernode_disconnect(eee); + eee->sn_wait = 1; + + expected = sizeof(uint16_t); + position = 0; + } + } } - #ifndef SKIP_MULTICAST_PEERS_DISCOVERY + if(FD_ISSET(eee->udp_multicast_sock, &socket_mask)) { - /* Read a cooked socket from the internet socket (multicast). Writes on the TAP - * socket. */ - traceEvent(TRACE_DEBUG, "Received packet from multicast socket"); - readFromIPSocket(eee, eee->udp_multicast_sock); + if (0 != fetch_and_eventually_process_data (eee, eee->udp_multicast_sock, + pktbuf, &expected, &position, + now)) { + *keep_running = 0; + break; + } } #endif if(FD_ISSET(eee->udp_mgmt_sock, &socket_mask)) { - /* Read a cooked socket from the internet socket. Writes on the TAP - * socket. */ + // read from the management port socket readFromMgmtSocket(eee, keep_running); if(!(*keep_running)) @@ -2463,19 +2730,22 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { #ifndef WIN32 if(FD_ISSET(eee->device.fd, &socket_mask)) { - /* Read an ethernet frame from the TAP socket. Write on the IP - * socket. */ + // read an ethernet frame from the TAP socket; write on the IP socket edge_read_from_tap(eee); } #endif } - /* Finished processing select data. */ - update_supernode_reg(eee, nowTime); + // finished processing select data + update_supernode_reg(eee, now); - numPurged = purge_expired_nodes(&eee->known_peers, &last_purge_known, + numPurged = purge_expired_nodes(&eee->known_peers, + eee->sock, NULL, + &last_purge_known, PURGE_REGISTRATION_FREQUENCY, REGISTRATION_TIMEOUT); - numPurged += purge_expired_nodes(&eee->pending_peers, &last_purge_pending, + numPurged += purge_expired_nodes(&eee->pending_peers, + eee->sock, NULL, + &last_purge_pending, PURGE_REGISTRATION_FREQUENCY, REGISTRATION_TIMEOUT); if(numPurged > 0) { @@ -2486,21 +2756,21 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { } if((eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_DHCP) && - ((nowTime - lastIfaceCheck) > IFACE_UPDATE_INTERVAL)) { + ((now - lastIfaceCheck) > IFACE_UPDATE_INTERVAL)) { uint32_t old_ip = eee->device.ip_addr; traceEvent(TRACE_NORMAL, "Re-checking dynamic IP address."); tuntap_get_address(&(eee->device)); - lastIfaceCheck = nowTime; + lastIfaceCheck = now; if((old_ip != eee->device.ip_addr) && eee->cb.ip_address_changed) eee->cb.ip_address_changed(eee, old_ip, eee->device.ip_addr); } - sort_supernodes(eee, nowTime); + sort_supernodes(eee, now); if(eee->cb.main_loop_period) - eee->cb.main_loop_period(eee, nowTime); + eee->cb.main_loop_period(eee, now); } /* while */ @@ -2510,7 +2780,7 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { send_unregister_super(eee); - closesocket(eee->udp_sock); + closesocket(eee->sock); return(0); } @@ -2520,8 +2790,8 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) { /** Deinitialise the edge and deallocate any owned memory. */ void edge_term (n2n_edge_t * eee) { - if(eee->udp_sock >= 0) - closesocket(eee->udp_sock); + if(eee->sock >= 0) + closesocket(eee->sock); if(eee->udp_mgmt_sock >= 0) closesocket(eee->udp_mgmt_sock); @@ -2547,13 +2817,11 @@ void edge_term (n2n_edge_t * eee) { /* ************************************** */ -static int edge_init_sockets (n2n_edge_t *eee, int udp_local_port, int mgmt_port, uint8_t tos) { + +static int edge_init_sockets (n2n_edge_t *eee) { int sockopt; - if(eee->udp_sock >= 0) - closesocket(eee->udp_sock); - if(eee->udp_mgmt_sock >= 0) closesocket(eee->udp_mgmt_sock); @@ -2562,38 +2830,9 @@ static int edge_init_sockets (n2n_edge_t *eee, int udp_local_port, int mgmt_port closesocket(eee->udp_multicast_sock); #endif - if(udp_local_port > 0) - traceEvent(TRACE_NORMAL, "Binding to local port %d", udp_local_port); - - eee->udp_sock = open_socket(udp_local_port, 1 /* bind ANY */); - if(eee->udp_sock < 0) { - traceEvent(TRACE_ERROR, "Failed to bind main UDP port %u", udp_local_port); - return(-1); - } - - if(tos) { - /* https://www.tucny.com/Home/dscp-tos */ - sockopt = tos; - - if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_TOS, (char *)&sockopt, sizeof(sockopt)) == 0) - traceEvent(TRACE_NORMAL, "TOS set to 0x%x", tos); - else - traceEvent(TRACE_ERROR, "Could not set TOS 0x%x[%d]: %s", tos, errno, strerror(errno)); - } - -#ifdef IP_PMTUDISC_DO - sockopt = (eee->conf.disable_pmtu_discovery) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO; - - if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_MTU_DISCOVER, &sockopt, sizeof(sockopt)) < 0) - traceEvent(TRACE_WARNING, "Could not %s PMTU discovery[%d]: %s", - (eee->conf.disable_pmtu_discovery) ? "disable" : "enable", errno, strerror(errno)); - else - traceEvent(TRACE_DEBUG, "PMTU discovery %s", (eee->conf.disable_pmtu_discovery) ? "disabled" : "enabled"); -#endif - - eee->udp_mgmt_sock = open_socket(mgmt_port, 0 /* bind LOOPBACK */); + eee->udp_mgmt_sock = open_socket(eee->conf.mgmt_port, 0 /* bind LOOPBACK */, 0 /* UDP */); if(eee->udp_mgmt_sock < 0) { - traceEvent(TRACE_ERROR, "Failed to bind management UDP port %u", mgmt_port); + traceEvent(TRACE_ERROR, "Failed to bind management UDP port %u", eee->conf.mgmt_port); return(-2); } @@ -2606,7 +2845,7 @@ static int edge_init_sockets (n2n_edge_t *eee, int udp_local_port, int mgmt_port eee->multicast_peer.addr.v4[2] = 0; eee->multicast_peer.addr.v4[3] = 68; - eee->udp_multicast_sock = open_socket(N2N_MULTICAST_PORT, 1 /* bind ANY */); + eee->udp_multicast_sock = open_socket(N2N_MULTICAST_PORT, 1 /* bind ANY */, 0 /* UDP */); if(eee->udp_multicast_sock < 0) return(-3); else { @@ -3033,7 +3272,6 @@ void edge_init_conf_defaults (n2n_edge_conf_t *conf) { conf->encrypt_key = strdup(getenv("N2N_KEY")); conf->transop_id = N2N_TRANSFORM_ID_AES; } - conf->metric = 0; } diff --git a/src/example_sn_embed.c b/src/example_sn_embed.c index dd570c7..5c31741 100644 --- a/src/example_sn_embed.c +++ b/src/example_sn_embed.c @@ -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); } diff --git a/src/n2n.c b/src/n2n.c index f47b979..01f5530 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -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); diff --git a/src/sn.c b/src/sn.c index 8aa3f45..f52d1cb 100644 --- a/src/sn.c +++ b/src/sn.c @@ -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); diff --git a/src/sn_selection.c b/src/sn_selection.c index 0d156a8..2efc176 100644 --- a/src/sn_selection.c +++ b/src/sn_selection.c @@ -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; diff --git a/src/sn_utils.c b/src/sn_utils.c index e341e6a..adfe9fa 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -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, ®, comm, &(ack.sock), &(ack.auth), SN_ADD_SKIP, now); + ret_value = update_edge(sss, ®, comm, &(ack.sock), socket_fd, &(ack.auth), SN_ADD_SKIP, now); } else { - ret_value = update_edge(sss, ®, comm, &(ack.sock), &(ack.auth), SN_ADD, now); + ret_value = update_edge(sss, ®, 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, ®.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);