aboutsummaryrefslogtreecommitdiff
path: root/src/transport/gnunet-nat-client.c
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/transport/gnunet-nat-client.c
parent0e66068912b62ffc503f4c6073e3a0317344559f (diff)
downloadgnunet-94229794b52dd866fe7d27ffbed20da18934087d.tar.gz
gnunet-94229794b52dd866fe7d27ffbed20da18934087d.zip
stuff
Diffstat (limited to 'src/transport/gnunet-nat-client.c')
-rw-r--r--src/transport/gnunet-nat-client.c468
1 files changed, 78 insertions, 390 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 */