aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2010-02-06 23:13:26 +0000
committerChristian Grothoff <christian@grothoff.org>2010-02-06 23:13:26 +0000
commit94229794b52dd866fe7d27ffbed20da18934087d (patch)
treefbd0ea504a32b1eece19cbc8b5270a4536538195 /src
parent0e66068912b62ffc503f4c6073e3a0317344559f (diff)
downloadgnunet-94229794b52dd866fe7d27ffbed20da18934087d.tar.gz
gnunet-94229794b52dd866fe7d27ffbed20da18934087d.zip
stuff
Diffstat (limited to 'src')
-rw-r--r--src/transport/gnunet-nat-client.c468
-rw-r--r--src/transport/gnunet-nat-server.c399
2 files changed, 157 insertions, 710 deletions
diff --git a/src/transport/gnunet-nat-client.c b/src/transport/gnunet-nat-client.c
index 0fce09e71..ba99c8816 100644
--- a/src/transport/gnunet-nat-client.c
+++ b/src/transport/gnunet-nat-client.c
@@ -20,16 +20,14 @@
20 20
21/** 21/**
22 * @file src/transport/gnunet-nat-client.c 22 * @file src/transport/gnunet-nat-client.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as root (for now, later SUID will do) 23 * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24 * This code will work under GNU/Linux only (or maybe BSDs, but never W32) 24 * This code will work under GNU/Linux only.
25 * @author Christian Grothoff 25 * @author Christian Grothoff
26 */ 26 */
27 27#define _GNU_SOURCE
28#include <sys/types.h> 28#include <sys/types.h>
29#include <sys/socket.h> 29#include <sys/socket.h>
30#include <arpa/inet.h> 30#include <arpa/inet.h>
31#include <sys/select.h>
32#include <sys/time.h>
33#include <sys/types.h> 31#include <sys/types.h>
34#include <unistd.h> 32#include <unistd.h>
35#include <stdio.h> 33#include <stdio.h>
@@ -37,37 +35,14 @@
37#include <errno.h> 35#include <errno.h>
38#include <stdlib.h> 36#include <stdlib.h>
39#include <stdint.h> 37#include <stdint.h>
40#include <time.h>
41#include <netinet/ip.h> 38#include <netinet/ip.h>
42#include <netinet/ip_icmp.h> 39#include <netinet/ip_icmp.h>
43#include <netinet/in.h> 40#include <netinet/in.h>
44 41
45#define DEBUG 1
46
47/**
48 * Number of UDP ports to keep open (typically >= 256)
49 */
50#define NUM_UDP_PORTS 1024
51
52/**
53 * Number of ICMP replies to send per message received (typically >= 1024)
54 */
55#define NUM_ICMP_REPLIES 10240
56
57/** 42/**
58 * How often do we send our UDP messages to keep ports open? (typically < 100ms) 43 * Must match IP given in the server.
59 */ 44 */
60#define UDP_SEND_FREQUENCY_MS 10 45#define DUMMY_IP "1.2.3.4"
61
62/**
63 * Port we use for the dummy target.
64 */
65#define NAT_TRAV_PORT 22223
66
67/**
68 * How often do we retry to open and bind a UDP socket before giving up?
69 */
70#define MAX_TRIES 10
71 46
72struct ip_packet 47struct ip_packet
73{ 48{
@@ -83,31 +58,14 @@ struct ip_packet
83 uint32_t dst_ip; 58 uint32_t dst_ip;
84}; 59};
85 60
86struct udp_packet
87{
88 uint16_t source_port;
89 uint16_t dst_port;
90 uint16_t mlen_aka_reply_port_magic;
91 uint16_t checksum_aka_my_magic;
92};
93
94struct icmp_packet 61struct icmp_packet
95{ 62{
96 uint8_t type; 63 uint8_t type;
97 uint8_t code; 64 uint8_t code;
98 uint16_t checksum; 65 uint16_t checksum;
99 uint32_t reserved; 66 uint32_t reserved;
100 struct ip_packet ip;
101 struct udp_packet udp;
102}; 67};
103
104
105static int udpsocks[NUM_UDP_PORTS];
106
107static uint16_t udpports[NUM_UDP_PORTS];
108 68
109static int icmpsock;
110
111static int rawsock; 69static int rawsock;
112 70
113static struct in_addr dummy; 71static struct in_addr dummy;
@@ -115,63 +73,6 @@ static struct in_addr dummy;
115static struct in_addr target; 73static struct in_addr target;
116 74
117 75
118/**
119 * create a random port number that is not totally
120 * unlikely to be chosen by the nat box.
121 */
122static uint16_t make_port ()
123{
124 return 1024 + ( (unsigned int)rand ()) % (63 * 1024 - 2);
125}
126
127
128/**
129 * create a fresh udp socket bound to a random local port.
130 */
131static int
132make_udp_socket (uint16_t *port)
133{
134 int ret;
135 int tries;
136 struct sockaddr_in src;
137
138 for (tries=0;tries<MAX_TRIES;tries++)
139 {
140 ret = socket (AF_INET, SOCK_DGRAM, 0);
141 if (-1 == ret)
142 {
143 fprintf (stderr,
144 "Error opening udp socket: %s\n",
145 strerror (errno));
146 return -1;
147 }
148 if (ret >= FD_SETSIZE)
149 {
150 fprintf (stderr,
151 "Socket number too large (%d > %u)\n",
152 ret,
153 (unsigned int) FD_SETSIZE);
154 close (ret);
155 return -1;
156 }
157 memset (&src, 0, sizeof (src));
158 src.sin_family = AF_INET;
159 src.sin_port = htons (make_port ());
160 if (0 != bind (ret, (struct sockaddr*) &src, sizeof (src)))
161 {
162 close (ret);
163 continue;
164 }
165 *port = ntohs (src.sin_port);
166 return ret;
167 }
168 fprintf (stderr,
169 "Error binding udp socket: %s\n",
170 strerror (errno));
171 return -1;
172}
173
174
175static uint16_t 76static uint16_t
176calc_checksum(const uint16_t *data, 77calc_checksum(const uint16_t *data,
177 unsigned int bytes) 78 unsigned int bytes)
@@ -188,76 +89,83 @@ calc_checksum(const uint16_t *data,
188} 89}
189 90
190 91
92static void
93make_echo (const struct in_addr *src_ip,
94 struct icmp_packet *echo)
95{
96 memset(echo, 0, sizeof(struct icmp_packet));
97 echo->type = ICMP_ECHO;
98 echo->code = 0;
99 echo->reserved = 0;
100 echo->checksum = 0;
101 echo->checksum = htons(calc_checksum((uint16_t*)echo,
102 sizeof (struct icmp_packet)));
103}
104
105
191/** 106/**
192 * send an icmp message to the target. 107 * Send an ICMP message to the target.
193 * 108 *
194 * @param my_ip source address (our ip address) 109 * @param my_ip source address
195 * @param other target address 110 * @param other target address
196 * @param target_port_number fake port number to put into icmp response
197 * as well as the icmpextradata as 'my_magic'
198 * @param source_port_number magic_number that enables the other peer to
199 * identify our port number ('reply in response to') to
200 * put in the data portion; 0 if we are initiating;
201 * goes into 'reply_port_magic' of the icmpextradata
202 */ 111 */
203static void 112static void
204send_icmp (const struct in_addr *my_ip, 113send_icmp (const struct in_addr *my_ip,
205 const struct in_addr *other, 114 const struct in_addr *other)
206 uint16_t target_port_number,
207 uint16_t source_port_number)
208{ 115{
209 struct ip_packet ip_pkt; 116 struct ip_packet ip_pkt;
210 struct icmp_packet icmp_pkt; 117 struct icmp_packet *icmp_pkt;
118 struct icmp_packet icmp_echo;
211 struct sockaddr_in dst; 119 struct sockaddr_in dst;
212 char packet[sizeof (ip_pkt) + sizeof (icmp_pkt)]; 120 char packet[sizeof (struct ip_packet)*2 + sizeof (struct icmp_packet)*2];
213 size_t off; 121 size_t off;
214 int err; 122 int err;
215 123
216 /* ip header: send to (known) ip address */ 124 /* ip header: send to (known) ip address */
217 off = 0; 125 off = 0;
218 memset(&ip_pkt, 0, sizeof(ip_pkt)); 126 memset(&ip_pkt, 0, sizeof(ip_pkt));
219 ip_pkt.vers_ihl = 0x45;//|(pkt_len>>2);//5;//(ipversion << 4) | (iphdr_size >> 2); 127 ip_pkt.vers_ihl = 0x45;
220 ip_pkt.tos = 0; 128 ip_pkt.tos = 0;
221 ip_pkt.pkt_len = sizeof (packet); /* huh? */ 129 ip_pkt.pkt_len = sizeof (packet); /* huh? */
222 ip_pkt.id = 1; /* kernel will change anyway!? */ 130 ip_pkt.id = 1;
223 ip_pkt.flags_frag_offset = 0; 131 ip_pkt.flags_frag_offset = 0;
224 ip_pkt.ttl = IPDEFTTL; 132 ip_pkt.ttl = IPDEFTTL;
225 ip_pkt.proto = IPPROTO_ICMP; 133 ip_pkt.proto = IPPROTO_ICMP;
226 ip_pkt.checksum = 0; 134 ip_pkt.checksum = 0;
227 ip_pkt.src_ip = my_ip->s_addr; 135 ip_pkt.src_ip = my_ip->s_addr;
228 ip_pkt.dst_ip = other->s_addr; 136 ip_pkt.dst_ip = other->s_addr;
229 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt))); 137 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (struct ip_packet)));
230 memcpy (packet, &ip_pkt, sizeof (ip_pkt)); 138 memcpy (packet, &ip_pkt, sizeof (struct ip_packet));
231 off += sizeof (ip_pkt); 139 off += sizeof (ip_pkt);
232
233 /* icmp reply: time exceeded */ 140 /* icmp reply: time exceeded */
234 memset(&icmp_pkt, 0, sizeof(icmp_pkt)); 141 icmp_pkt = (struct icmp_packet*) &packet[off];
235 icmp_pkt.type = ICMP_TIME_EXCEEDED; 142 memset(icmp_pkt, 0, sizeof(struct icmp_packet));
236 icmp_pkt.code = ICMP_HOST_UNREACH; 143 icmp_pkt->type = ICMP_TIME_EXCEEDED;
237 icmp_pkt.reserved = 0; 144 icmp_pkt->code = 0;
238 icmp_pkt.checksum = 0; 145 icmp_pkt->reserved = 0;
146 icmp_pkt->checksum = 0;
147 off += sizeof (struct icmp_packet);
239 148
240 /* ip header of the presumably 'lost' udp packet */ 149 /* ip header of the presumably 'lost' udp packet */
241 icmp_pkt.ip.vers_ihl = 0x45; 150 ip_pkt.vers_ihl = 0x45;
242 icmp_pkt.ip.tos = 0; 151 ip_pkt.tos = 0;
243 /* no idea why i need to shift the bits here, but not on ip_pkt->pkt_len... */ 152 ip_pkt.pkt_len = (sizeof (struct ip_packet) + sizeof (struct icmp_packet));
244 icmp_pkt.ip.pkt_len = (sizeof (ip_pkt) + sizeof (icmp_pkt)) << 8; 153 ip_pkt.id = 1;
245 icmp_pkt.ip.id = 1; /* kernel sets proper value htons(ip_id_counter); */ 154 ip_pkt.flags_frag_offset = 0;
246 icmp_pkt.ip.flags_frag_offset = 0; 155 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
247 icmp_pkt.ip.ttl = 1; /* real TTL would be 1 on a time exceeded packet */ 156 ip_pkt.proto = IPPROTO_ICMP;
248 icmp_pkt.ip.proto = IPPROTO_UDP; 157 ip_pkt.src_ip = other->s_addr;
249 icmp_pkt.ip.src_ip = other->s_addr; 158 ip_pkt.dst_ip = dummy.s_addr;
250 icmp_pkt.ip.dst_ip = dummy.s_addr; 159 ip_pkt.checksum = 0;
251 icmp_pkt.ip.checksum = 0; 160 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (struct ip_packet)));
252 icmp_pkt.ip.checksum = htons(calc_checksum((uint16_t*)&icmp_pkt.ip, sizeof (icmp_pkt.ip))); 161 memcpy (&packet[off], &ip_pkt, sizeof (struct ip_packet));
253 icmp_pkt.udp.source_port = htons (target_port_number); 162 off += sizeof (struct ip_packet);
254 icmp_pkt.udp.dst_port = htons (NAT_TRAV_PORT); 163 make_echo (other, &icmp_echo);
255 icmp_pkt.udp.mlen_aka_reply_port_magic = htons (source_port_number); 164 memcpy (&packet[off], &icmp_echo, sizeof(struct icmp_packet));
256 icmp_pkt.udp.checksum_aka_my_magic = htons (target_port_number); 165 off += sizeof (struct icmp_packet);
257 icmp_pkt.checksum = htons(calc_checksum((uint16_t*)&icmp_pkt, sizeof (icmp_pkt))); 166 icmp_pkt->checksum = htons(calc_checksum((uint16_t*)icmp_pkt,
258 memcpy (&packet[off], &icmp_pkt, sizeof (icmp_pkt)); 167 sizeof (struct icmp_packet)*2 + sizeof(struct ip_packet)));
259 off += sizeof (icmp_pkt); 168
260
261 memset (&dst, 0, sizeof (dst)); 169 memset (&dst, 0, sizeof (dst));
262 dst.sin_family = AF_INET; 170 dst.sin_family = AF_INET;
263 dst.sin_addr = *other; 171 dst.sin_addr = *other;
@@ -266,177 +174,20 @@ send_icmp (const struct in_addr *my_ip,
266 off, 0, 174 off, 0,
267 (struct sockaddr*)&dst, 175 (struct sockaddr*)&dst,
268 sizeof(dst)); /* or sizeof 'struct sockaddr'? */ 176 sizeof(dst)); /* or sizeof 'struct sockaddr'? */
269 if (err < 0) { 177 if (err < 0)
270 fprintf(stderr,
271 "sendto failed: %s\n", strerror(errno));
272 } else if (err != off)
273 fprintf(stderr,
274 "Error: partial send of ICMP message\n");
275}
276
277
278/**
279 * We discovered the IP address of the other peer.
280 * Try to connect back to it.
281 */
282static void
283try_connect (const struct in_addr *my_ip,
284 const struct in_addr *other,
285 uint16_t port_magic)
286{
287 unsigned int i;
288 char sbuf [INET_ADDRSTRLEN];
289
290 fprintf (stderr,
291 "Sending %u ICMPs to `%s' with reply magic %u\n",
292 NUM_ICMP_REPLIES,
293 inet_ntop (AF_INET,
294 other,
295 sbuf,
296 sizeof (sbuf)),
297 port_magic);
298 for (i=0;i<NUM_ICMP_REPLIES;i++)
299 send_icmp (my_ip, other, make_port(), port_magic);
300}
301
302
303static void
304process_icmp_response (const struct in_addr *my_ip,
305 int s)
306{
307 char buf[65536];
308 ssize_t have;
309 struct in_addr sip;
310 uint16_t my_magic;
311 uint16_t reply_magic;
312 uint16_t local_port;
313 struct ip_packet ip_pkt;
314 struct icmp_packet icmp_pkt;
315 size_t off;
316
317 have = read (s, buf, sizeof (buf));
318 if (have == -1)
319 {
320 fprintf (stderr,
321 "Error reading raw socket: %s\n",
322 strerror (errno));
323 /* What now? */
324 return;
325 }
326 if (have != sizeof (struct ip_packet) + sizeof (struct icmp_packet))
327 {
328 fprintf (stderr,
329 "Received ICMP message of unexpected size: %u bytes\n",
330 (unsigned int) have);
331 return;
332 }
333 off = 0;
334 memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt));
335 off += sizeof (ip_pkt);
336 memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt));
337 off += sizeof (icmp_pkt);
338
339 if ( (ip_pkt.proto == IPPROTO_ICMP) &&
340 (icmp_pkt.type == ICMP_DEST_UNREACH) &&
341 (icmp_pkt.code == ICMP_HOST_UNREACH) )
342 { 178 {
343 /* this is what is normal due to our UDP traffic */ 179 fprintf(stderr,
344 return; 180 "sendto failed: %s\n", strerror(errno));
345 } 181 }
346 if ( (ip_pkt.proto == IPPROTO_ICMP) && 182 else if (err != off)
347 (icmp_pkt.type == ICMP_TIME_EXCEEDED) &&
348 (icmp_pkt.code == ICMP_HOST_UNREACH) )
349 { 183 {
350 /* this is what we might see on loopback: this is the format 184 fprintf(stderr,
351 we as the client send out (the host uses 'ICMP_NET_UNREACH'); 185 "Error: partial send of ICMP message\n");
352 Ignore! */
353 return;
354 }
355 if ( (ip_pkt.proto != IPPROTO_ICMP) ||
356 (icmp_pkt.type != ICMP_TIME_EXCEEDED) ||
357 (icmp_pkt.code != ICMP_NET_UNREACH) )
358 {
359 /* Note the expected client response and not the normal network response */
360#if DEBUG
361 fprintf (stderr,
362 "Received unexpected ICMP message contents (%u, %u, %u), ignoring\n",
363 ip_pkt.proto,
364 icmp_pkt.type,
365 icmp_pkt.code);
366#endif
367 return;
368 }
369 memcpy(&sip, &ip_pkt.src_ip, sizeof (sip));
370 reply_magic = ntohs (icmp_pkt.udp.checksum_aka_my_magic);
371 my_magic = ntohs (icmp_pkt.udp.mlen_aka_reply_port_magic);
372 local_port = ntohs (icmp_pkt.udp.source_port);
373 if (my_magic == 0)
374 {
375#if DEBUG
376 /* we get these a lot during loopback testing... */
377 fprintf (stderr,
378 "Received ICMP without hint as to which port worked, dropping\n");
379#endif
380 return;
381 }
382#if DEBUG
383 fprintf (stderr,
384 "Received ICMP from `%s' with outgoing port %u, listen port %u and incoming for other peer %u\n",
385 inet_ntop (AF_INET,
386 &sip,
387 buf,
388 sizeof (buf)),
389 my_magic,
390 local_port,
391 reply_magic);
392#endif
393 if (my_magic == 0)
394 {
395 try_connect (my_ip, &sip, reply_magic);
396 }
397 else
398 {
399 send_icmp (my_ip, &target, my_magic, reply_magic);
400 printf ("%s:%u listen on %u\n",
401 inet_ntop (AF_INET,
402 &sip,
403 buf,
404 sizeof(buf)),
405 my_magic,
406 local_port);
407 /* technically, we're done here! */
408 exit (0);
409 } 186 }
410} 187}
411 188
412 189
413static int 190static int
414make_icmp_socket ()
415{
416 int ret;
417
418 ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
419 if (-1 == ret)
420 {
421 fprintf (stderr,
422 "Error opening RAW socket: %s\n",
423 strerror (errno));
424 return -1;
425 }
426 if (ret >= FD_SETSIZE)
427 {
428 fprintf (stderr,
429 "Socket number too large (%d > %u)\n",
430 ret,
431 (unsigned int) FD_SETSIZE);
432 close (ret);
433 return -1;
434 }
435 return ret;
436}
437
438
439static int
440make_raw_socket () 191make_raw_socket ()
441{ 192{
442 const int one = 1; 193 const int one = 1;
@@ -450,15 +201,6 @@ make_raw_socket ()
450 strerror (errno)); 201 strerror (errno));
451 return -1; 202 return -1;
452 } 203 }
453 if (ret >= FD_SETSIZE)
454 {
455 fprintf (stderr,
456 "Socket number too large (%d > %u)\n",
457 ret,
458 (unsigned int) FD_SETSIZE);
459 close (ret);
460 return -1;
461 }
462 if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST, 204 if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST,
463 (char *)&one, sizeof(one)) == -1) 205 (char *)&one, sizeof(one)) == -1)
464 fprintf(stderr, 206 fprintf(stderr,
@@ -477,88 +219,34 @@ int
477main (int argc, char *const *argv) 219main (int argc, char *const *argv)
478{ 220{
479 struct in_addr external; 221 struct in_addr external;
480 unsigned int i; 222 uid_t uid;
481 unsigned int pos; 223
482 fd_set rs; 224 if (-1 == (rawsock = make_raw_socket()))
483 struct timeval tv; 225 return 1;
484 struct sockaddr_in dst; 226 uid = getuid ();
485 uint16_t p; 227 if (0 != setresuid (uid, uid, uid))
486 228 fprintf (stderr,
487 if (argc != 4) 229 "Failed to setresuid: %s\n",
230 strerror (errno));
231 if (argc != 3)
488 { 232 {
489 fprintf (stderr, 233 fprintf (stderr,
490 "This program must be started with our IP, the targets external IP and the dummy IP address as arguments.\n"); 234 "This program must be started with our IP and the targets external IP as arguments.\n");
491 return 1; 235 return 1;
492 } 236 }
493 if ( (1 != inet_pton (AF_INET, argv[1], &external)) || 237 if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
494 (1 != inet_pton (AF_INET, argv[2], &target)) || 238 (1 != inet_pton (AF_INET, argv[2], &target)) )
495 (1 != inet_pton (AF_INET, argv[3], &dummy)) )
496 { 239 {
497 fprintf (stderr, 240 fprintf (stderr,
498 "Error parsing IPv4 address: %s\n", 241 "Error parsing IPv4 address: %s\n",
499 strerror (errno)); 242 strerror (errno));
500 return 1; 243 return 1;
501 } 244 }
502 srand (time(NULL)); 245 inet_pton (AF_INET, DUMMY_IP, &dummy);
503 memset (&dst, 0, sizeof (dst)); 246 send_icmp (&external,
504 dst.sin_family = AF_INET; 247 &target);
505 dst.sin_port = htons (NAT_TRAV_PORT); 248 close (rawsock);
506 dst.sin_addr = dummy;
507
508 if (-1 == (icmpsock = make_icmp_socket()))
509 return 1;
510 if (-1 == (rawsock = make_raw_socket()))
511 {
512 close (icmpsock);
513 return 1;
514 }
515 for (i=0;i<NUM_UDP_PORTS;i++)
516 udpsocks[i] = make_udp_socket (&udpports[i]);
517 pos = 0;
518 while (1)
519 {
520 FD_ZERO (&rs);
521 FD_SET (icmpsock, &rs);
522 tv.tv_sec = 0;
523 tv.tv_usec = UDP_SEND_FREQUENCY_MS * 1000;
524 select (icmpsock + 1, &rs, NULL, NULL, &tv);
525 /* FIXME: do I need my external IP here? */
526 if (FD_ISSET (icmpsock, &rs))
527 {
528 process_icmp_response (&external, icmpsock);
529 continue;
530 }
531#if DEBUG
532 fprintf (stderr,
533 "Sending UDP message to %s:%u\n",
534 argv[3],
535 NAT_TRAV_PORT);
536#endif
537 if (-1 == sendto (udpsocks[pos],
538 NULL, 0, 0,
539 (struct sockaddr*) &dst, sizeof (dst)))
540 {
541 fprintf (stderr,
542 "sendto failed: %s\n",
543 strerror (errno));
544 close (udpsocks[pos]);
545 udpsocks[pos] = make_udp_socket (&udpports[pos]);
546 }
547 p = make_port ();
548#if DEBUG
549 fprintf (stderr,
550 "Sending fake ICMP message to %s:%u\n",
551 argv[1],
552 p);
553#endif
554 send_icmp (&external,
555 &target,
556 p,
557 0);
558 pos = (pos+1) % NUM_UDP_PORTS;
559 }
560 return 0; 249 return 0;
561} 250}
562 251
563
564/* end of gnunet-nat-client.c */ 252/* end of gnunet-nat-client.c */
diff --git a/src/transport/gnunet-nat-server.c b/src/transport/gnunet-nat-server.c
index 936726fb3..225c0af5f 100644
--- a/src/transport/gnunet-nat-server.c
+++ b/src/transport/gnunet-nat-server.c
@@ -20,11 +20,11 @@
20 20
21/** 21/**
22 * @file src/transport/gnunet-nat-server.c 22 * @file src/transport/gnunet-nat-server.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as root (for now, later SUID will do) 23 * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24 * This code will work under GNU/Linux only (or maybe BSDs, but never W32) 24 * This code will work under GNU/Linux only (or maybe BSDs, but never W32)
25 * @author Christian Grothoff 25 * @author Christian Grothoff
26 */ 26 */
27 27#define _GNU_SOURCE
28#include <sys/types.h> 28#include <sys/types.h>
29#include <sys/socket.h> 29#include <sys/socket.h>
30#include <arpa/inet.h> 30#include <arpa/inet.h>
@@ -42,37 +42,18 @@
42#include <netinet/ip_icmp.h> 42#include <netinet/ip_icmp.h>
43#include <netinet/in.h> 43#include <netinet/in.h>
44 44
45#define DEBUG 0
46
47/**
48 * Number of UDP ports to keep open (typically >= 256).
49 */
50#define NUM_UDP_PORTS 256
51
52/**
53 * Number of ICMP replies to send per message received (typically >= 1024)
54 */
55#define NUM_ICMP_REPLIES 1024
56
57/** 45/**
58 * How often do we send our UDP messages to keep ports open? (typically < 100ms) 46 * Must match IP given in the client.
59 */ 47 */
60#define UDP_SEND_FREQUENCY_MS 50 48#define DUMMY_IP "1.2.3.4"
61 49
62/** 50/**
63 * Port we use for the dummy target. 51 * How often do we send our ICMP messages to receive replies?
64 */ 52 */
65#define NAT_TRAV_PORT 2222 53#define ICMP_SEND_FREQUENCY_MS 500
66
67/**
68 * How often do we retry to open and bind a UDP socket before giving up?
69 */
70#define MAX_TRIES 10
71
72 54
73struct ip_packet 55struct ip_packet
74{ 56{
75
76 uint8_t vers_ihl; 57 uint8_t vers_ihl;
77 uint8_t tos; 58 uint8_t tos;
78 uint16_t pkt_len; 59 uint16_t pkt_len;
@@ -85,112 +66,20 @@ struct ip_packet
85 uint32_t dst_ip; 66 uint32_t dst_ip;
86}; 67};
87 68
88
89struct udp_packet
90{
91 uint16_t source_port;
92 uint16_t dst_port;
93 uint16_t mlen_aka_reply_port_magic;
94 uint16_t checksum_aka_my_magic;
95};
96
97
98struct icmp_packet 69struct icmp_packet
99{ 70{
100 uint8_t type; 71 uint8_t type;
101 uint8_t code; 72 uint8_t code;
102 uint16_t checksum; 73 uint16_t checksum;
103 uint32_t reserved; 74 uint32_t reserved;
104 struct ip_packet ip;
105 struct udp_packet udp;
106};
107
108
109/**
110 * Structure of the data we tack on to the fake ICMP reply
111 * (last 4 bytes of the 64 bytes).
112 */
113struct extra_packet
114{
115 /**
116 * if this is a reply to an icmp, what was the 'my_magic'
117 * value from the original icmp?
118 */
119 uint16_t reply_port_magic;
120
121 /**
122 * magic value of the sender of this icmp message.
123 */
124 uint16_t my_magic;
125}; 75};
126 76
127static int udpsocks[NUM_UDP_PORTS];
128 77
129static uint16_t udpports[NUM_UDP_PORTS];
130
131static int icmpsock; 78static int icmpsock;
132 79
133static int rawsock; 80static int rawsock;
134 81
135static struct in_addr dummy; 82static struct in_addr dummy;
136
137
138/**
139 * create a random port number that is not totally
140 * unlikely to be chosen by the nat box.
141 */
142static uint16_t make_port ()
143{
144 return 1024 + ( (unsigned int)rand ()) % (63 * 1024 - 2);
145}
146
147
148/**
149 * create a fresh udp socket bound to a random local port.
150 */
151static int
152make_udp_socket (uint16_t *port)
153{
154 int ret;
155 int tries;
156 struct sockaddr_in src;
157
158 for (tries=0;tries<MAX_TRIES;tries++)
159 {
160 ret = socket (AF_INET, SOCK_DGRAM, 0);
161 if (-1 == ret)
162 {
163 fprintf (stderr,
164 "Error opening udp socket: %s\n",
165 strerror (errno));
166 return -1;
167 }
168 if (ret >= FD_SETSIZE)
169 {
170 fprintf (stderr,
171 "Socket number too large (%d > %u)\n",
172 ret,
173 (unsigned int) FD_SETSIZE);
174 close (ret);
175 return -1;
176 }
177 memset (&src, 0, sizeof (src));
178 src.sin_family = AF_INET;
179 src.sin_port = htons (make_port ());
180 if (0 != bind (ret, (struct sockaddr*) &src, sizeof (src)))
181 {
182 close (ret);
183 continue;
184 }
185 *port = ntohs (src.sin_port);
186 return ret;
187 }
188 fprintf (stderr,
189 "Error binding udp socket: %s\n",
190 strerror (errno));
191 return -1;
192}
193
194 83
195static uint16_t 84static uint16_t
196calc_checksum(const uint16_t *data, 85calc_checksum(const uint16_t *data,
@@ -208,144 +97,93 @@ calc_checksum(const uint16_t *data,
208} 97}
209 98
210 99
100static void
101make_echo (const struct in_addr *src_ip,
102 struct icmp_packet *echo)
103{
104 memset(echo, 0, sizeof(struct icmp_packet));
105 echo->type = ICMP_ECHO;
106 echo->code = 0;
107 echo->reserved = 0;
108 echo->checksum = 0;
109 echo->checksum = htons(calc_checksum((uint16_t*)echo, sizeof (struct icmp_packet)));
110}
111
112
211/** 113/**
212 * send an icmp message to the target. 114 * Send an ICMP message to the dummy IP.
213 * 115 *
214 * @param my_ip source address (our ip address) 116 * @param my_ip source address (our ip address)
215 * @param other target address
216 * @param target_port_number fake port number to put into icmp response
217 * as well as the icmpextradata as 'my_magic'
218 * @param source_port_number magic_number that enables the other peer to
219 * identify our port number ('reply in response to') to
220 * put in the data portion; 0 if we are initiating;
221 * goes into 'reply_port_magic' of the icmpextradata
222 */ 117 */
223static void 118static void
224send_icmp (const struct in_addr *my_ip, 119send_icmp_echo (const struct in_addr *my_ip)
225 const struct in_addr *other,
226 uint16_t target_port_number,
227 uint16_t source_port_number)
228{ 120{
229 struct ip_packet ip_pkt; 121 struct icmp_packet icmp_echo;
230 struct icmp_packet icmp_pkt;
231 struct sockaddr_in dst; 122 struct sockaddr_in dst;
232 char packet[sizeof (ip_pkt) + sizeof (icmp_pkt)];
233 size_t off; 123 size_t off;
234 int err; 124 int err;
125 struct ip_packet ip_pkt;
126 struct icmp_packet icmp_pkt;
127 char packet[sizeof (ip_pkt) + sizeof (icmp_pkt)];
235 128
236 /* ip header: send to (known) ip address */
237 off = 0; 129 off = 0;
238 memset(&ip_pkt, 0, sizeof(ip_pkt)); 130 memset(&ip_pkt, 0, sizeof(ip_pkt));
239 ip_pkt.vers_ihl = 0x45;//|(pkt_len>>2);//5;//(ipversion << 4) | (iphdr_size >> 2); 131 ip_pkt.vers_ihl = 0x45;
240 ip_pkt.tos = 0; 132 ip_pkt.tos = 0;
241 ip_pkt.pkt_len = sizeof (packet); /* huh? */ 133 ip_pkt.pkt_len = sizeof (packet);
242 ip_pkt.id = 1; /* kernel will change anyway!? */ 134 ip_pkt.id = 1;
243 ip_pkt.flags_frag_offset = 0; 135 ip_pkt.flags_frag_offset = 0;
244 ip_pkt.ttl = IPDEFTTL; 136 ip_pkt.ttl = IPDEFTTL;
245 ip_pkt.proto = IPPROTO_ICMP; 137 ip_pkt.proto = IPPROTO_ICMP;
246 ip_pkt.checksum = 0; /* maybe the kernel helps us out..? */ 138 ip_pkt.checksum = 0;
247 ip_pkt.src_ip = my_ip->s_addr; 139 ip_pkt.src_ip = my_ip->s_addr;
248 ip_pkt.dst_ip = other->s_addr; 140 ip_pkt.dst_ip = dummy.s_addr;
249 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt))); 141 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt)));
250 memcpy (packet, &ip_pkt, sizeof (ip_pkt)); 142 memcpy (packet, &ip_pkt, sizeof (ip_pkt));
251 off += sizeof (ip_pkt); 143 off += sizeof (ip_pkt);
252 144 make_echo (my_ip, &icmp_echo);
253 /* icmp reply: time exceeded */ 145 memcpy (&packet[off], &icmp_echo, sizeof (icmp_echo));
254 memset(&icmp_pkt, 0, sizeof(icmp_pkt)); 146 off += sizeof (icmp_echo);
255 icmp_pkt.type = ICMP_TIME_EXCEEDED;
256 icmp_pkt.code = ICMP_NET_UNREACH;
257 icmp_pkt.reserved = 0;
258 icmp_pkt.checksum = 0;
259
260 /* ip header of the presumably 'lost' udp packet */
261 icmp_pkt.ip.vers_ihl = 0x45;
262 icmp_pkt.ip.tos = 0;
263 /* no idea why i need to shift the bits here, but not on ip_pkt->pkt_len... */
264 icmp_pkt.ip.pkt_len = (sizeof (ip_pkt) + sizeof (icmp_pkt)) << 8;
265 icmp_pkt.ip.id = 1; /* kernel sets proper value htons(ip_id_counter); */
266 icmp_pkt.ip.flags_frag_offset = 0;
267 icmp_pkt.ip.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
268 icmp_pkt.ip.proto = IPPROTO_UDP;
269 icmp_pkt.ip.src_ip = other->s_addr;
270 icmp_pkt.ip.dst_ip = dummy.s_addr;
271 icmp_pkt.ip.checksum = 0;
272 icmp_pkt.ip.checksum = htons(calc_checksum((uint16_t*)&icmp_pkt.ip, sizeof (icmp_pkt.ip)));
273 icmp_pkt.udp.source_port = htons (target_port_number);
274 icmp_pkt.udp.dst_port = htons (NAT_TRAV_PORT);
275 icmp_pkt.udp.mlen_aka_reply_port_magic = htons (source_port_number);
276 icmp_pkt.udp.checksum_aka_my_magic = htons (target_port_number);
277 icmp_pkt.checksum = htons(calc_checksum((uint16_t*)&icmp_pkt, sizeof (icmp_pkt)));
278 memcpy (&packet[off], &icmp_pkt, sizeof (icmp_pkt));
279 off += sizeof (icmp_pkt);
280 147
281 memset (&dst, 0, sizeof (dst)); 148 memset (&dst, 0, sizeof (dst));
282 dst.sin_family = AF_INET; 149 dst.sin_family = AF_INET;
283 dst.sin_addr = *other; 150 dst.sin_addr = dummy;
284 err = sendto(rawsock, 151 err = sendto(rawsock,
285 packet, 152 packet, off, 0,
286 off, 0,
287 (struct sockaddr*)&dst, 153 (struct sockaddr*)&dst,
288 sizeof(dst)); /* or sizeof 'struct sockaddr'? */ 154 sizeof(dst));
289 if (err < 0) { 155 if (err < 0)
290 fprintf(stderr, 156 {
291 "sendto failed: %s\n", strerror(errno)); 157 fprintf(stderr,
292 } else if (err != off) 158 "sendto failed: %s\n", strerror(errno));
293 fprintf(stderr, 159 }
294 "Error: partial send of ICMP message\n"); 160 else if (err != off)
295} 161 {
296 162 fprintf(stderr,
297 163 "Error: partial send of ICMP message\n");
298/** 164 }
299 * We discovered the IP address of the other peer.
300 * Try to connect back to it.
301 */
302static void
303try_connect (const struct in_addr *my_ip,
304 const struct in_addr *other,
305 uint16_t port_magic)
306{
307 unsigned int i;
308#if DEBUG
309 char sbuf [INET_ADDRSTRLEN];
310
311 fprintf (stderr,
312 "Sending %u ICMPs to `%s' with reply magic %u\n",
313 NUM_ICMP_REPLIES,
314 inet_ntop (AF_INET,
315 other,
316 sbuf,
317 sizeof (sbuf)),
318 port_magic);
319#endif
320 for (i=0;i<NUM_ICMP_REPLIES;i++)
321 send_icmp (my_ip, other, make_port(), port_magic);
322} 165}
323 166
324 167
325static void 168static void
326process_icmp_response (const struct in_addr *my_ip, 169process_icmp_response ()
327 int s)
328{ 170{
329 char buf[65536]; 171 char buf[65536];
330 ssize_t have; 172 ssize_t have;
331 struct in_addr sip; 173 struct in_addr sip;
332 uint16_t my_magic;
333 uint16_t reply_magic;
334 uint16_t local_port;
335 struct ip_packet ip_pkt; 174 struct ip_packet ip_pkt;
336 struct icmp_packet icmp_pkt; 175 struct icmp_packet icmp_pkt;
337 size_t off; 176 size_t off;
338 177
339 have = read (s, buf, sizeof (buf)); 178 have = read (icmpsock, buf, sizeof (buf));
340 if (have == -1) 179 if (have == -1)
341 { 180 {
342 fprintf (stderr, 181 fprintf (stderr,
343 "Error reading raw socket: %s\n", 182 "Error reading raw socket: %s\n",
344 strerror (errno)); 183 strerror (errno));
345 /* What now? */
346 return; 184 return;
347 } 185 }
348 if (have != sizeof (struct ip_packet) + sizeof (struct icmp_packet)) 186 if (have != sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2)
349 { 187 {
350 fprintf (stderr, 188 fprintf (stderr,
351 "Received ICMP message of unexpected size: %u bytes\n", 189 "Received ICMP message of unexpected size: %u bytes\n",
@@ -357,66 +195,22 @@ process_icmp_response (const struct in_addr *my_ip,
357 off += sizeof (ip_pkt); 195 off += sizeof (ip_pkt);
358 memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt)); 196 memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt));
359 off += sizeof (icmp_pkt); 197 off += sizeof (icmp_pkt);
360
361 if ( (ip_pkt.proto == IPPROTO_ICMP) &&
362 (icmp_pkt.type == ICMP_DEST_UNREACH) &&
363 (icmp_pkt.code == ICMP_HOST_UNREACH) )
364 {
365 /* this is what is normal due to our UDP traffic */
366 return;
367 }
368 if ( (ip_pkt.proto == IPPROTO_ICMP) &&
369 (icmp_pkt.type == ICMP_TIME_EXCEEDED) &&
370 (icmp_pkt.code == ICMP_NET_UNREACH) )
371 {
372 /* this is what we might see on loopback: this is the format
373 we as the server send out (the client uses 'ICMP_HOST_UNREACH');
374 Ignore! */
375 return;
376 }
377
378 if ( (ip_pkt.proto != IPPROTO_ICMP) || 198 if ( (ip_pkt.proto != IPPROTO_ICMP) ||
379 (icmp_pkt.type != ICMP_TIME_EXCEEDED) || 199 (icmp_pkt.type != ICMP_TIME_EXCEEDED) ||
380 (icmp_pkt.code != ICMP_HOST_UNREACH) ) 200 (icmp_pkt.code != 0) )
381 { 201 {
382 /* Note the expected client response and not the normal network response */ 202 /* maybe we got an actual reply back... */
383 fprintf (stderr, 203 return;
384 "Received unexpected ICMP message contents (%u, %u, %u), ignoring\n",
385 ip_pkt.proto,
386 icmp_pkt.type,
387 icmp_pkt.code);
388 return;
389 } 204 }
390 memcpy(&sip, &ip_pkt.src_ip, sizeof (sip)); 205 memcpy(&sip,
391 reply_magic = ntohs (icmp_pkt.udp.checksum_aka_my_magic); 206 &ip_pkt.src_ip,
392 my_magic = ntohs (icmp_pkt.udp.mlen_aka_reply_port_magic); 207 sizeof (sip));
393 local_port = ntohs (icmp_pkt.udp.source_port); 208 fprintf (stdout,
394#if DEBUG 209 "%s\n",
395 fprintf (stderr,
396 "Received ICMP from `%s' with outgoing port %u, listen port %u and incoming port hint for other peer %u\n",
397 inet_ntop (AF_INET, 210 inet_ntop (AF_INET,
398 &sip, 211 &sip,
399 buf, 212 buf,
400 sizeof (buf)), 213 sizeof (buf)));
401 my_magic,
402 local_port,
403 reply_magic);
404#endif
405 if (my_magic == 0)
406 {
407 try_connect (my_ip, &sip, reply_magic);
408 }
409 else
410 {
411 /* FIXME: should close 'local_port' */
412 printf ("%s:%u listen on %u\n",
413 inet_ntop (AF_INET,
414 &sip,
415 buf,
416 sizeof(buf)),
417 my_magic,
418 local_port);
419 }
420} 214}
421 215
422 216
@@ -460,15 +254,6 @@ make_raw_socket ()
460 strerror (errno)); 254 strerror (errno));
461 return -1; 255 return -1;
462 } 256 }
463 if (ret >= FD_SETSIZE)
464 {
465 fprintf (stderr,
466 "Socket number too large (%d > %u)\n",
467 ret,
468 (unsigned int) FD_SETSIZE);
469 close (ret);
470 return -1;
471 }
472 if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST, 257 if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST,
473 (char *)&one, sizeof(one)) == -1) 258 (char *)&one, sizeof(one)) == -1)
474 fprintf(stderr, 259 fprintf(stderr,
@@ -487,72 +272,46 @@ int
487main (int argc, char *const *argv) 272main (int argc, char *const *argv)
488{ 273{
489 struct in_addr external; 274 struct in_addr external;
490 unsigned int i;
491 unsigned int pos;
492 fd_set rs; 275 fd_set rs;
493 struct timeval tv; 276 struct timeval tv;
494 struct sockaddr_in dst; 277 uid_t uid;
495 278
496 if (argc != 3) 279 if (-1 == (icmpsock = make_icmp_socket()))
280 return 1;
281 if (-1 == (rawsock = make_raw_socket()))
282 {
283 close (icmpsock);
284 return 1;
285 }
286 uid = getuid ();
287 if (0 != setresuid (uid, uid, uid))
288 fprintf (stderr,
289 "Failed to setresuid: %s\n",
290 strerror (errno));
291 if (argc != 2)
497 { 292 {
498 fprintf (stderr, 293 fprintf (stderr,
499 "This program must be started with our external IP and the dummy IP address as arguments.\n"); 294 "This program must be started with our external IP as the only argument.\n");
500 return 1; 295 return 1;
501 } 296 }
502 if ( (1 != inet_pton (AF_INET, argv[1], &external)) || 297 if (1 != inet_pton (AF_INET, argv[1], &external))
503 (1 != inet_pton (AF_INET, argv[2], &dummy)) )
504 { 298 {
505 fprintf (stderr, 299 fprintf (stderr,
506 "Error parsing IPv4 address: %s\n", 300 "Error parsing IPv4 address: %s\n",
507 strerror (errno)); 301 strerror (errno));
508 return 1; 302 return 1;
509 } 303 }
510 srand (time(NULL)); 304 inet_pton (AF_INET, DUMMY_IP, &dummy);
511 memset (&dst, 0, sizeof (dst));
512 dst.sin_family = AF_INET;
513 dst.sin_port = htons (NAT_TRAV_PORT);
514 dst.sin_addr = dummy;
515
516 if (-1 == (icmpsock = make_icmp_socket()))
517 return 1;
518 if (-1 == (rawsock = make_raw_socket()))
519 {
520 close (icmpsock);
521 return 1;
522 }
523 for (i=0;i<NUM_UDP_PORTS;i++)
524 udpsocks[i] = make_udp_socket (&udpports[i]);
525 pos = 0;
526 while (1) 305 while (1)
527 { 306 {
528 FD_ZERO (&rs); 307 FD_ZERO (&rs);
529 FD_SET (icmpsock, &rs); 308 FD_SET (icmpsock, &rs);
530 tv.tv_sec = 0; 309 tv.tv_sec = 0;
531 tv.tv_usec = UDP_SEND_FREQUENCY_MS * 1000; 310 tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
532 select (icmpsock + 1, &rs, NULL, NULL, &tv); 311 select (icmpsock + 1, &rs, NULL, NULL, &tv);
533 /* FIXME: do I need my external IP here? */
534 if (FD_ISSET (icmpsock, &rs)) 312 if (FD_ISSET (icmpsock, &rs))
535 { 313 process_icmp_response ();
536 process_icmp_response (&external, icmpsock); 314 send_icmp_echo (&external);
537 continue;
538 }
539#if DEBUG
540 fprintf (stderr,
541 "Sending UDP message to %s:%u\n",
542 argv[2],
543 NAT_TRAV_PORT);
544#endif
545 if (-1 == sendto (udpsocks[pos],
546 NULL, 0, 0,
547 (struct sockaddr*) &dst, sizeof (dst)))
548 {
549 fprintf (stderr,
550 "sendto failed: %s\n",
551 strerror (errno));
552 close (udpsocks[pos]);
553 udpsocks[pos] = make_udp_socket (&udpports[pos]);
554 }
555 pos = (pos+1) % NUM_UDP_PORTS;
556 } 315 }
557 return 0; 316 return 0;
558} 317}