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;