From 9601e82daf03efb9e5f8ad73a1f05cff5b8dd69d Mon Sep 17 00:00:00 2001 From: realjiangms Date: Sun, 26 May 2019 09:49:42 +0800 Subject: [PATCH] More reliable punching: 1. Some nat router blocks the port if incoming traffic arrives before outcoming traffic being sent. Give edge ability to set proper TTL so that the registration packet is dropped before it arrives peer. 2. Support Symmetric NAT by predicting 15 more ports when sending registration packet 3. Purge pending mac also on P2P normal packet. This is actually more usual condition. 4. Add doc for new flag. --- doc/NEW_FEATURES.txt | 15 +++++++++++++++ edge.c | 9 +++++++-- edge_utils.c | 46 +++++++++++++++++++++++++++++++++++++++++++- n2n.h | 1 + 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/doc/NEW_FEATURES.txt b/doc/NEW_FEATURES.txt index 9efc587..8748517 100644 --- a/doc/NEW_FEATURES.txt +++ b/doc/NEW_FEATURES.txt @@ -4,3 +4,18 @@ Between 2.0.x and 2.1.x * Better ming Windows build support. * Added -E flag to allow multicast ethernet traffic. +* Added -L flag to allow set TTL for registration packet. +This is an advanced flag to make sure that the registration packet is dropped immediately when it goes out of local nat +so that it will not trigger some firewall behavior on target peer. Actually, the registration packet is only expected to +make local nat UDP hole and is not expected to reach the target peer, see https://tools.ietf.org/html/rfc5389. +To achieve this, the flag should be set as nat level + 1. For example, if we have 2 layer nat in local, we should set +-L 3. +Usually we know exactly how much nat layers in local. +If we are not sure how much nat layers in local, we can use traceroute on Linux to check. Following example shows a local +single layer nat because on second jump it shows a public ip address. In this case it should set -L 2. +$ /usr/sbin/traceroute -w1 8.8.8.8 +traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets + 1 192.168.3.1 (192.168.3.1) 0.464 ms 0.587 ms 0.719 ms + 2 112.65.17.217 (112.65.17.217) 5.269 ms 7.031 ms 8.666 ms + +But this method is not always work due to various local network device policy. diff --git a/edge.c b/edge.c index c4889c8..0567f8f 100644 --- a/edge.c +++ b/edge.c @@ -143,7 +143,7 @@ static void help() { #ifndef __APPLE__ "[-D] " #endif - "[-r] [-E] [-v] [-i ] [-t ] [-A] [-h]\n\n"); + "[-r] [-E] [-v] [-i ] [-L ] [-t ] [-A] [-h]\n\n"); #if defined(N2N_CAN_NAME_IFACE) printf("-d | tun device name\n"); @@ -155,6 +155,7 @@ static void help() { printf("-s | Edge interface netmask in dotted decimal notation (255.255.255.0).\n"); printf("-l | Supernode IP:port\n"); printf("-i | Registration interval, for NAT hole punching (default 20 seconds)\n"); + printf("-L | TTL for registration packet when UDP NAT hole punching through supernode (default 0 for not set )\n"); printf("-p | Fixed local UDP port.\n"); #ifndef WIN32 printf("-u | User ID (numeric) to use when privileges are dropped.\n"); @@ -303,6 +304,10 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e conf->register_interval = atoi(optargument); break; + case 'L': /* supernode registration interval */ + conf->register_ttl = atoi(optarg); + break; + #if defined(N2N_CAN_NAME_IFACE) case 'd': /* TUNTAP name */ { @@ -393,7 +398,7 @@ static int loadFromCLI(int argc, char *argv[], n2n_edge_conf_t *conf, n2n_priv_c u_char c; while((c = getopt_long(argc, argv, - "k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SD" + "k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SDL:" #ifdef N2N_HAVE_AES "A" #endif diff --git a/edge_utils.c b/edge_utils.c index 23aebee..6d7d081 100644 --- a/edge_utils.c +++ b/edge_utils.c @@ -384,9 +384,43 @@ static void register_with_new_peer(n2n_edge_t * eee, HASH_COUNT(eee->pending_peers)); /* trace Sending REGISTER */ - send_register(eee, &(scan->sock), mac); if(from_supernode) { + /* UDP NAT hole punching through supernode. Send to peer first(punch local UDP hole) + * and then ask supernode to forward. Supernode then ask peer to ack. Some nat device + * drop and block ports with incoming UDP packet if out-come traffic does not exist. + * So we can alternatively set TTL so that the packet sent to peer never really reaches + * The register_ttl is basically nat level + 1. Set it to 1 means host like DMZ. + */ + if (eee->conf.register_ttl == 1) { + /* We are DMZ host or port is directly accessible. Just let peer to send back the ack */ +#ifndef WIN32 + } else if(eee->conf.register_ttl > 1) { + /* Setting register_ttl usually implies that the edge knows the internal net topology + * clearly, we can apply aggressive port prediction to support incoming Symmetric NAT + */ + int curTTL = 0; + socklen_t lenTTL = sizeof(int); + 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, + (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)); +#endif + } else { /* eee->conf.register_ttl <= 0 */ + /* Normal STUN */ + send_register(eee, &(scan->sock), mac); + } send_register(eee, &(eee->supernode), mac); + } else { + /* P2P register, send directly */ + send_register(eee, &(scan->sock), mac); } register_with_local_peers(eee); @@ -1461,6 +1495,16 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { if(is_valid_peer_sock(&pkt.sock)) orig_sender = &(pkt.sock); + if(!from_supernode) { + /* This is a P2P packet from the peer. We purge a pending + * registration towards the possibly nat-ted peer address as we now have + * a valid channel. We still use check_peer_registration_needed in + * handle_PACKET to double check this. + */ + traceEvent(TRACE_DEBUG, "Got P2P packet"); + find_and_remove_peer(&eee->pending_peers, pkt.srcMac); + } + traceEvent(TRACE_INFO, "Rx PACKET from %s (sender=%s) [%u B]", sock_to_cstr(sockbuf1, &sender), sock_to_cstr(sockbuf2, orig_sender), diff --git a/n2n.h b/n2n.h index a8dff99..e5018db 100644 --- a/n2n.h +++ b/n2n.h @@ -217,6 +217,7 @@ typedef struct n2n_edge_conf { uint8_t tos; /** TOS for sent packets */ char *encrypt_key; int register_interval; /**< Interval for supernode registration, also used for UDP NAT hole punching. */ + int register_ttl; /**< TTL for registration packet when UDP NAT hole punching through supernode. */ int local_port; int mgmt_port; } n2n_edge_conf_t;