aboutsummaryrefslogtreecommitdiff
path: root/src/transport/gnunet-nat-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/gnunet-nat-server.c')
-rw-r--r--src/transport/gnunet-nat-server.c399
1 files changed, 79 insertions, 320 deletions
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}