Commit 868f68b4 authored by Fong's avatar Fong
Browse files

update README

parent 401f92ea
# nat_traversal # nat_traversal
A implementation of this paper: [A New Method for Symmetric NAT Traversal in UDP and TCP](http://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf) An implementation of this paper: [A New Method for Symmetric NAT Traversal in UDP and TCP](http://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf)
According to [RFC 3489](http://tools.ietf.org/html/rfc3478), a symmetric NAT is one where all requests from the same internal IP address and port, to a specific destination IP address and port, are mapped to the same external IP address and
According to [RFC 3489](http://tools.ietf.org/html/rfc3489), a symmetric NAT is one where all requests from the same internal IP address and port, to a specific destination IP address and port, are mapped to the same external IP address and
port. If the same host sends a packet with the same source address and port, but to a different destination, a different mapping is used. Furthermore, only the external host that receives a packet can send a UDP packet back to the internal host. port. If the same host sends a packet with the same source address and port, but to a different destination, a different mapping is used. Furthermore, only the external host that receives a packet can send a UDP packet back to the internal host.
So, if we know the port allocation rule of the Symmetric NAT, we can traverse Symmetric NAT. This paper proposes a new method for traversing Symmetric NAT which is based on port prediction and limited TTL values. So, if we know the port allocation rule of the Symmetric NAT, we can traverse Symmetric NAT. This paper proposes a new method for traversing Symmetric NAT which is based on port prediction and limited TTL values.
This method is based on limited TTL values, port prediction(if it's not predictable, use large number of holes, namely a 1000 connections at once, which will be punched and increase the success rate).
## Usage ## Usage
*** ***
It's just a experience project, I just wanna test whether UDP punching is possible if both nodes are behind symmetric NAT. It works this way: It's just an experimental project, I just wanna test whether UDP punching is possible if both nodes are behind symmetric NAT. It works this way:
A peer got its own NAT info(external ip/port, NAT type ), then registers to server, the server replies to the peer with a unique peer ID. if you specify `-d peer ID` option, the node tries to get the info of that specified peer from the server, then connects to it directly. So when you specify '-d' option, make sure that peer is connected with server. A peer got its own NAT info(external ip/port, NAT type ), then registers to server, the server replies to the peer with a unique peer ID. if you specify `-d peer ID` option, the node tries to get the info of that specified peer from the server, then connects to it directly. So when you specify '-d' option, make sure that peer is connected with server.
To run it, you should run `punch_server.go` in a machine with public IP, so both nodes can connect to it to exchange info(just node info, not to relay payload like [RFC 6062](https://tools.ietf.org/html/rfc6062)), then run `nat_traversal [-s punch server] [-d if you wanna connect to peer]` on clients, you can also specify other arguments, such as, STUN server by `-H` option, source IP by `-i`, source port by `-p`. To run it, you should run `punch_server.go` in a machine with public IP, so that both nodes can connect to it to exchange info(just node info, not to relay payload like [TURN](https://tools.ietf.org/html/rfc6062)), then run `nat_traversal [-s punch server] [-d if you wanna connect to peer]` on clients, you can also specify other arguments, such as, STUN server by `-H` option, source IP by `-i`, source port by `-p`.
This program is not 100% guaranteed to make 2 peers behind symmetric NATs connect This program is not 100% guaranteed to make 2 peers behind symmetric NATs connect
to each other directly, it's theoretically possible. Actually, I just happen to succeed once. Hope more people can test it. to each other, it's possible in theory. Actually, I just happen to succeed once. Hope more people can test it.
\ No newline at end of file \ No newline at end of file
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#define MSG_BUF_SIZE 512 #define MSG_BUF_SIZE 512
// file scope variables // file scope variables
static int nums[MAX_PORT - MIN_PORT]; static int ports[MAX_PORT - MIN_PORT];
static int send_to_punch_server(client* c) { static int send_to_punch_server(client* c) {
int n = send(c->sfd, c->buf, c->msg_buf - c->buf, 0); int n = send(c->sfd, c->buf, c->msg_buf - c->buf, 0);
...@@ -63,18 +63,18 @@ static int send_dummy_udp_packet(int fd, struct sockaddr_in addr) { ...@@ -63,18 +63,18 @@ static int send_dummy_udp_packet(int fd, struct sockaddr_in addr) {
static int punch_hole(struct sockaddr_in peer_addr, int ttl) { static int punch_hole(struct sockaddr_in peer_addr, int ttl) {
int hole = socket(AF_INET, SOCK_DGRAM, 0); int hole = socket(AF_INET, SOCK_DGRAM, 0);
if (hole != -1) { if (hole != -1) {
//struct sockaddr_in local_addr;
// no need to choose local port for now, let OS do that //local_addr.sin_family = AF_INET;
/*struct sockaddr_in local_addr; //local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_family = AF_INET; //local_addr.sin_port = htons(DEFAULT_LOCAL_PORT + 1);
local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //int reuse_addr = 1;
local_addr.sin_port = htons(DEFAULT_LOCAL_PORT + 1); //setsockopt(hole, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr));
if (bind(hole, (struct sockaddr *)&local_addr, sizeof(local_addr))) { //if (bind(hole, (struct sockaddr *)&local_addr, sizeof(local_addr))) {
if (errno == EADDRINUSE) { // if (errno == EADDRINUSE) {
printf("addr in use, try another port\n"); // printf("addr already in use, try another port\n");
return -1; // return -1;
} // }
} */ //}
/* TODO we can use traceroute to get the number of hops to the peer /* TODO we can use traceroute to get the number of hops to the peer
* to make sure this packet woudn't reach the peer but get through the NAT in front of itself * to make sure this packet woudn't reach the peer but get through the NAT in front of itself
...@@ -168,24 +168,24 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf ...@@ -168,24 +168,24 @@ static int connect_to_symmetric_nat(client* c, uint32_t peer_id, struct peer_inf
peer_addr.sin_addr.s_addr = inet_addr(remote_peer.ip); peer_addr.sin_addr.s_addr = inet_addr(remote_peer.ip);
int *holes = malloc(NUM_OF_PORTS * sizeof(int)); int *holes = malloc(NUM_OF_PORTS * sizeof(int));
shuffle(nums, MAX_PORT - MIN_PORT + 1); shuffle(ports, MAX_PORT - MIN_PORT + 1);
int i = 0; int i = 0;
for (; i < NUM_OF_PORTS;) { for (; i < NUM_OF_PORTS;) {
uint16_t port = nums[i]; uint16_t port = ports[i];
if (port != remote_peer.port) { // exclude the used one if (port != remote_peer.port) { // exclude the used one
peer_addr.sin_port = htons(port); peer_addr.sin_port = htons(port);
if ((holes[i] = punch_hole(peer_addr, c->ttl)) < 0) { if ((holes[i] = punch_hole(peer_addr, c->ttl)) < 0) {
// NAT in front of us wound't tolerate too many ports used by one application // NAT in front of us wound't tolerate too many ports used by one application
verbose_log("%s, NAT flooding protection triggered, try %d times\n", strerror(errno), i); verbose_log("failed to punch hole, error: %s\n", strerror(errno));
break; break;
} }
// sleep for a while to avoid flooding protection // sleep for a while to avoid flooding protection
usleep(1000 * 100); usleep(1000 * 100);
++i; ++i;
} else { } else {
nums[i] = nums[1000]; ports[i] = ports[1000];
continue; continue;
} }
} }
...@@ -236,11 +236,11 @@ static void* server_notify_handler(void* data) { ...@@ -236,11 +236,11 @@ static void* server_notify_handler(void* data) {
int sock_array[NUM_OF_PORTS]; int sock_array[NUM_OF_PORTS];
int i = 0; int i = 0;
shuffle(nums, MAX_PORT - MIN_PORT + 1); shuffle(ports, MAX_PORT - MIN_PORT + 1);
// send probe packets, check if connected with peer, if yes, stop probing // send probe packets, check if connected with peer, if yes, stop probing
for (; i < NUM_OF_PORTS;) { for (; i < NUM_OF_PORTS;) {
if (nums[i] == peer.port) { if (ports[i] == peer.port) {
nums[i] = nums[1000]; // TODO ports[i] = ports[1000]; // TODO
continue; continue;
} }
if ((sock_array[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { if ((sock_array[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
...@@ -248,7 +248,7 @@ static void* server_notify_handler(void* data) { ...@@ -248,7 +248,7 @@ static void* server_notify_handler(void* data) {
break; break;
} }
peer_addr.sin_port = htons(nums[i]); peer_addr.sin_port = htons(ports[i]);
// let OS choose available ports // let OS choose available ports
if (send_dummy_udp_packet(sock_array[i], peer_addr) < 0) { if (send_dummy_udp_packet(sock_array[i], peer_addr) < 0) {
...@@ -288,7 +288,7 @@ static void* server_notify_handler(void* data) { ...@@ -288,7 +288,7 @@ static void* server_notify_handler(void* data) {
int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c) { int enroll(struct peer_info self, struct sockaddr_in punch_server, client* c) {
int i, temp; int i, temp;
for (i = 0, temp = MIN_PORT; temp <= MAX_PORT; i++, temp++) { for (i = 0, temp = MIN_PORT; temp <= MAX_PORT; i++, temp++) {
nums[i] = temp; ports[i] = temp;
} }
int server_sock = socket(AF_INET, SOCK_STREAM, 0); int server_sock = socket(AF_INET, SOCK_STREAM, 0);
......
...@@ -204,7 +204,7 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_ ...@@ -204,7 +204,7 @@ static int send_bind_request(int sock, const char* remote_host, uint16_t remote_
} }
break; break;
default: default:
// ignore // ignore other attributes
break; break;
} }
body += attrLen + attrLenPad; body += attrLen + attrLenPad;
...@@ -224,15 +224,14 @@ const char* get_nat_desc(nat_type type) { ...@@ -224,15 +224,14 @@ const char* get_nat_desc(nat_type type) {
nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char* local_ip, uint16_t local_port, char* ext_ip, uint16_t* ext_port) { nat_type detect_nat_type(const char* stun_host, uint16_t stun_port, const char* local_ip, uint16_t local_port, char* ext_ip, uint16_t* ext_port) {
uint32_t mapped_ip = 0; uint32_t mapped_ip = 0;
uint16_t mapped_port = 0; uint16_t mapped_port = 0;
int s; int s = socket(AF_INET, SOCK_DGRAM, 0);
if((s = socket(AF_INET, SOCK_DGRAM, 0)) <= 0) { if (s <= 0) {
return Error; return Error;
} }
nat_type nat_type; nat_type nat_type;
int reuse_addr = 1; int reuse_addr = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr)); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr));
struct sockaddr_in local_addr; struct sockaddr_in local_addr;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment