Browse Source

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.
pull/115/head
realjiangms 5 years ago
committed by zihao.jiang
parent
commit
9601e82daf
  1. 15
      doc/NEW_FEATURES.txt
  2. 9
      edge.c
  3. 46
      edge_utils.c
  4. 1
      n2n.h

15
doc/NEW_FEATURES.txt

@ -4,3 +4,18 @@ Between 2.0.x and 2.1.x
* Better ming Windows build support. * Better ming Windows build support.
* Added -E flag to allow multicast ethernet traffic. * 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.

9
edge.c

@ -143,7 +143,7 @@ static void help() {
#ifndef __APPLE__ #ifndef __APPLE__
"[-D] " "[-D] "
#endif #endif
"[-r] [-E] [-v] [-i <reg_interval>] [-t <mgmt port>] [-A] [-h]\n\n"); "[-r] [-E] [-v] [-i <reg_interval>] [-L <reg_ttl>] [-t <mgmt port>] [-A] [-h]\n\n");
#if defined(N2N_CAN_NAME_IFACE) #if defined(N2N_CAN_NAME_IFACE)
printf("-d <tun device> | tun device name\n"); printf("-d <tun device> | tun device name\n");
@ -155,6 +155,7 @@ static void help() {
printf("-s <netmask> | Edge interface netmask in dotted decimal notation (255.255.255.0).\n"); printf("-s <netmask> | Edge interface netmask in dotted decimal notation (255.255.255.0).\n");
printf("-l <supernode host:port> | Supernode IP:port\n"); printf("-l <supernode host:port> | Supernode IP:port\n");
printf("-i <reg_interval> | Registration interval, for NAT hole punching (default 20 seconds)\n"); printf("-i <reg_interval> | Registration interval, for NAT hole punching (default 20 seconds)\n");
printf("-L <reg_ttl> | TTL for registration packet when UDP NAT hole punching through supernode (default 0 for not set )\n");
printf("-p <local port> | Fixed local UDP port.\n"); printf("-p <local port> | Fixed local UDP port.\n");
#ifndef WIN32 #ifndef WIN32
printf("-u <UID> | User ID (numeric) to use when privileges are dropped.\n"); printf("-u <UID> | 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); conf->register_interval = atoi(optargument);
break; break;
case 'L': /* supernode registration interval */
conf->register_ttl = atoi(optarg);
break;
#if defined(N2N_CAN_NAME_IFACE) #if defined(N2N_CAN_NAME_IFACE)
case 'd': /* TUNTAP name */ 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; u_char c;
while((c = getopt_long(argc, argv, 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 #ifdef N2N_HAVE_AES
"A" "A"
#endif #endif

46
edge_utils.c

@ -384,9 +384,43 @@ static void register_with_new_peer(n2n_edge_t * eee,
HASH_COUNT(eee->pending_peers)); HASH_COUNT(eee->pending_peers));
/* trace Sending REGISTER */ /* trace Sending REGISTER */
send_register(eee, &(scan->sock), mac);
if(from_supernode) { 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); send_register(eee, &(eee->supernode), mac);
} else {
/* P2P register, send directly */
send_register(eee, &(scan->sock), mac);
} }
register_with_local_peers(eee); 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)) if(is_valid_peer_sock(&pkt.sock))
orig_sender = &(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]", traceEvent(TRACE_INFO, "Rx PACKET from %s (sender=%s) [%u B]",
sock_to_cstr(sockbuf1, &sender), sock_to_cstr(sockbuf1, &sender),
sock_to_cstr(sockbuf2, orig_sender), sock_to_cstr(sockbuf2, orig_sender),

1
n2n.h

@ -217,6 +217,7 @@ typedef struct n2n_edge_conf {
uint8_t tos; /** TOS for sent packets */ uint8_t tos; /** TOS for sent packets */
char *encrypt_key; char *encrypt_key;
int register_interval; /**< Interval for supernode registration, also used for UDP NAT hole punching. */ 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 local_port;
int mgmt_port; int mgmt_port;
} n2n_edge_conf_t; } n2n_edge_conf_t;

Loading…
Cancel
Save