aboutsummaryrefslogtreecommitdiff
path: root/src/nat/gnunet-helper-nat-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nat/gnunet-helper-nat-server.c')
-rw-r--r--src/nat/gnunet-helper-nat-server.c651
1 files changed, 651 insertions, 0 deletions
diff --git a/src/nat/gnunet-helper-nat-server.c b/src/nat/gnunet-helper-nat-server.c
new file mode 100644
index 000000000..945c98735
--- /dev/null
+++ b/src/nat/gnunet-helper-nat-server.c
@@ -0,0 +1,651 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/nat/gnunet-helper-nat-server.c
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)
25 * @author Christian Grothoff
26 *
27 * This program will send ONE ICMP message every 500 ms RAW sockets
28 * to a DUMMY IP address and also listens for ICMP replies. Since
29 * it uses RAW sockets, it must be installed SUID or run as 'root'.
30 * In order to keep the security risk of the resulting SUID binary
31 * minimal, the program ONLY opens the two RAW sockets with root
32 * privileges, then drops them and only then starts to process
33 * command line arguments. The code also does not link against
34 * any shared libraries (except libc) and is strictly minimal
35 * (except for checking for errors). The following list of people
36 * have reviewed this code and considered it safe since the last
37 * modification (if you reviewed it, please have your name added
38 * to the list):
39 *
40 * - Christian Grothoff
41 * - Nathan Evans
42 * - Benjamin Kuperman (22 Aug 2010)
43 */
44#if HAVE_CONFIG_H
45/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46#include "gnunet_config.h"
47#else
48#define _GNU_SOURCE
49#endif
50#include <sys/types.h>
51#include <sys/socket.h>
52#include <arpa/inet.h>
53#include <sys/select.h>
54#include <sys/time.h>
55#include <sys/types.h>
56#include <unistd.h>
57#include <stdio.h>
58#include <string.h>
59#include <errno.h>
60#include <stdlib.h>
61#include <stdint.h>
62#include <time.h>
63#include <netinet/ip.h>
64#include <netinet/ip_icmp.h>
65#include <netinet/in.h>
66
67/**
68 * Should we print some debug output?
69 */
70#define VERBOSE 0
71
72/**
73 * Must match IP given in the client.
74 */
75#define DUMMY_IP "192.0.2.86"
76
77/**
78 * Port for UDP
79 */
80#define NAT_TRAV_PORT 22225
81
82/**
83 * How often do we send our ICMP messages to receive replies?
84 */
85#define ICMP_SEND_FREQUENCY_MS 500
86
87/**
88 * IPv4 header.
89 */
90struct ip_header
91{
92
93 /**
94 * Version (4 bits) + Internet header length (4 bits)
95 */
96 uint8_t vers_ihl;
97
98 /**
99 * Type of service
100 */
101 uint8_t tos;
102
103 /**
104 * Total length
105 */
106 uint16_t pkt_len;
107
108 /**
109 * Identification
110 */
111 uint16_t id;
112
113 /**
114 * Flags (3 bits) + Fragment offset (13 bits)
115 */
116 uint16_t flags_frag_offset;
117
118 /**
119 * Time to live
120 */
121 uint8_t ttl;
122
123 /**
124 * Protocol
125 */
126 uint8_t proto;
127
128 /**
129 * Header checksum
130 */
131 uint16_t checksum;
132
133 /**
134 * Source address
135 */
136 uint32_t src_ip;
137
138 /**
139 * Destination address
140 */
141 uint32_t dst_ip;
142};
143
144/**
145 * Format of ICMP packet.
146 */
147struct icmp_ttl_exceeded_header
148{
149 uint8_t type;
150
151 uint8_t code;
152
153 uint16_t checksum;
154
155 uint32_t unused;
156
157 /* followed by original payload */
158};
159
160struct icmp_echo_header
161{
162 uint8_t type;
163
164 uint8_t code;
165
166 uint16_t checksum;
167
168 uint32_t reserved;
169};
170
171
172/**
173 * Beginning of UDP packet.
174 */
175struct udp_header
176{
177 uint16_t src_port;
178
179 uint16_t dst_port;
180
181 uint16_t length;
182
183 uint16_t crc;
184};
185
186/**
187 * Socket we use to receive "fake" ICMP replies.
188 */
189static int icmpsock;
190
191/**
192 * Socket we use to send our ICMP requests.
193 */
194static int rawsock;
195
196/**
197 * Socket we use to send our UDP requests.
198 */
199static int udpsock;
200
201/**
202 * Target "dummy" address.
203 */
204static struct in_addr dummy;
205
206
207/**
208 * CRC-16 for IP/ICMP headers.
209 *
210 * @param data what to calculate the CRC over
211 * @param bytes number of bytes in data (must be multiple of 2)
212 * @return the CRC 16.
213 */
214static uint16_t
215calc_checksum(const uint16_t *data,
216 unsigned int bytes)
217{
218 uint32_t sum;
219 unsigned int i;
220
221 sum = 0;
222 for (i=0;i<bytes/2;i++)
223 sum += data[i];
224 sum = (sum & 0xffff) + (sum >> 16);
225 sum = htons(0xffff - sum);
226 return sum;
227}
228
229
230/**
231 * Send an ICMP message to the dummy IP.
232 *
233 * @param my_ip source address (our ip address)
234 */
235static void
236send_icmp_echo (const struct in_addr *my_ip)
237{
238 char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)];
239 struct icmp_echo_header icmp_echo;
240 struct ip_header ip_pkt;
241 struct sockaddr_in dst;
242 size_t off;
243 int err;
244
245 off = 0;
246 ip_pkt.vers_ihl = 0x45;
247 ip_pkt.tos = 0;
248 ip_pkt.pkt_len = htons (sizeof (packet));
249 ip_pkt.id = htons (256);
250 ip_pkt.flags_frag_offset = 0;
251 ip_pkt.ttl = IPDEFTTL;
252 ip_pkt.proto = IPPROTO_ICMP;
253 ip_pkt.checksum = 0;
254 ip_pkt.src_ip = my_ip->s_addr;
255 ip_pkt.dst_ip = dummy.s_addr;
256 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
257 sizeof (struct ip_header)));
258 memcpy (&packet[off],
259 &ip_pkt,
260 sizeof (struct ip_header));
261 off += sizeof (struct ip_header);
262
263 icmp_echo.type = ICMP_ECHO;
264 icmp_echo.code = 0;
265 icmp_echo.checksum = 0;
266 icmp_echo.reserved = 0;
267 icmp_echo.checksum = htons(calc_checksum((uint16_t*)&icmp_echo,
268 sizeof (struct icmp_echo_header)));
269 memcpy (&packet[off],
270 &icmp_echo,
271 sizeof (struct icmp_echo_header));
272 off += sizeof (struct icmp_echo_header);
273
274 memset (&dst, 0, sizeof (dst));
275 dst.sin_family = AF_INET;
276#if HAVE_SOCKADDR_IN_SIN_LEN
277 dst.sin_len = sizeof (struct sockaddr_in);
278#endif
279 dst.sin_addr = dummy;
280 err = sendto(rawsock,
281 packet, off, 0,
282 (struct sockaddr*)&dst,
283 sizeof(dst));
284 if (err < 0)
285 {
286#if VERBOSE
287 fprintf(stderr,
288 "sendto failed: %s\n", strerror(errno));
289#endif
290 }
291 else if (sizeof (packet) != err)
292 {
293 fprintf(stderr,
294 "Error: partial send of ICMP message\n");
295 }
296}
297
298
299/**
300 * Send a UDP message to the dummy IP.
301 */
302static void
303send_udp ()
304{
305 struct sockaddr_in dst;
306 ssize_t err;
307
308 memset (&dst, 0, sizeof (dst));
309 dst.sin_family = AF_INET;
310#if HAVE_SOCKADDR_IN_SIN_LEN
311 dst.sin_len = sizeof (struct sockaddr_in);
312#endif
313 dst.sin_addr = dummy;
314 dst.sin_port = htons (NAT_TRAV_PORT);
315 err = sendto(udpsock,
316 NULL, 0, 0,
317 (struct sockaddr*)&dst,
318 sizeof(dst));
319 if (err < 0)
320 {
321#if VERBOSE
322 fprintf(stderr,
323 "sendto failed: %s\n", strerror(errno));
324#endif
325 }
326 else if (0 != err)
327 {
328 fprintf(stderr,
329 "Error: partial send of ICMP message\n");
330 }
331}
332
333
334/**
335 * We've received an ICMP response. Process it.
336 */
337static void
338process_icmp_response ()
339{
340 char buf[65536];
341 ssize_t have;
342 struct in_addr source_ip;
343 struct ip_header ip_pkt;
344 struct icmp_ttl_exceeded_header icmp_ttl;
345 struct icmp_echo_header icmp_echo;
346 struct udp_header udp_pkt;
347 size_t off;
348 uint16_t port;
349
350 have = read (icmpsock, buf, sizeof (buf));
351 if (-1 == have)
352 {
353 fprintf (stderr,
354 "Error reading raw socket: %s\n",
355 strerror (errno));
356 return;
357 }
358#if VERBOSE
359 fprintf (stderr,
360 "Received message of %u bytes\n",
361 (unsigned int) have);
362#endif
363 if (have < (ssize_t) (sizeof (struct ip_header) + sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct ip_header)))
364 {
365 /* malformed */
366 return;
367 }
368 off = 0;
369 memcpy (&ip_pkt,
370 &buf[off],
371 sizeof (struct ip_header));
372 off += sizeof (struct ip_header);
373 memcpy(&source_ip,
374 &ip_pkt.src_ip,
375 sizeof (source_ip));
376 memcpy (&icmp_ttl,
377 &buf[off],
378 sizeof (struct icmp_ttl_exceeded_header));
379 off += sizeof (struct icmp_ttl_exceeded_header);
380 if ( (ICMP_TIME_EXCEEDED != icmp_ttl.type) ||
381 (0 != icmp_ttl.code) )
382 {
383 /* different type than what we want */
384 return;
385 }
386 /* skip 2nd IP header */
387 memcpy (&ip_pkt,
388 &buf[off],
389 sizeof (struct ip_header));
390 off += sizeof (struct ip_header);
391
392 switch (ip_pkt.proto)
393 {
394 case IPPROTO_ICMP:
395 if (have != (sizeof (struct ip_header) * 2 +
396 sizeof (struct icmp_ttl_exceeded_header) +
397 sizeof (struct icmp_echo_header)) )
398 {
399 /* malformed */
400 return;
401 }
402 /* grab ICMP ECHO content */
403 memcpy (&icmp_echo,
404 &buf[off],
405 sizeof (struct icmp_echo_header));
406 port = (uint16_t) ntohl (icmp_echo.reserved);
407 break;
408 case IPPROTO_UDP:
409 if (have != (sizeof (struct ip_header) * 2 +
410 sizeof (struct icmp_ttl_exceeded_header) +
411 sizeof (struct udp_header)) )
412 {
413 /* malformed */
414 return;
415 }
416 /* grab UDP content */
417 memcpy (&udp_pkt,
418 &buf[off],
419 sizeof (struct udp_header));
420 port = ntohs (udp_pkt.length);
421 break;
422 default:
423 /* different type than what we want */
424 return;
425 }
426
427 if (port == 0)
428 fprintf (stdout,
429 "%s\n",
430 inet_ntop (AF_INET,
431 &source_ip,
432 buf,
433 sizeof (buf)));
434 else
435 fprintf (stdout,
436 "%s:%u\n",
437 inet_ntop (AF_INET,
438 &source_ip,
439 buf,
440 sizeof (buf)),
441 (unsigned int) port);
442 fflush (stdout);
443}
444
445
446/**
447 * Create an ICMP raw socket for reading.
448 *
449 * @return -1 on error
450 */
451static int
452make_icmp_socket ()
453{
454 int ret;
455
456 ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
457 if (-1 == ret)
458 {
459 fprintf (stderr,
460 "Error opening RAW socket: %s\n",
461 strerror (errno));
462 return -1;
463 }
464 if (ret >= FD_SETSIZE)
465 {
466 fprintf (stderr,
467 "Socket number too large (%d > %u)\n",
468 ret,
469 (unsigned int) FD_SETSIZE);
470 close (ret);
471 return -1;
472 }
473 return ret;
474}
475
476
477/**
478 * Create an ICMP raw socket for writing.
479 *
480 * @return -1 on error
481 */
482static int
483make_raw_socket ()
484{
485 const int one = 1;
486 int ret;
487
488 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
489 if (-1 == ret)
490 {
491 fprintf (stderr,
492 "Error opening RAW socket: %s\n",
493 strerror (errno));
494 return -1;
495 }
496 if (-1 == setsockopt(ret,
497 SOL_SOCKET,
498 SO_BROADCAST,
499 (char *)&one, sizeof(one)))
500 {
501 fprintf(stderr,
502 "setsockopt failed: %s\n",
503 strerror (errno));
504 close (ret);
505 return -1;
506 }
507 if (-1 == setsockopt(ret,
508 IPPROTO_IP,
509 IP_HDRINCL,
510 (char *)&one, sizeof(one)))
511 {
512 fprintf(stderr,
513 "setsockopt failed: %s\n",
514 strerror (errno));
515 close (ret);
516 return -1;
517 }
518 return ret;
519}
520
521
522/**
523 * Create a UDP socket for writinging.
524 *
525 * @param my_ip source address (our ip address)
526 * @return -1 on error
527 */
528static int
529make_udp_socket (const struct in_addr *my_ip)
530{
531 int ret;
532 struct sockaddr_in addr;
533
534 ret = socket (AF_INET, SOCK_DGRAM, 0);
535 if (-1 == ret)
536 {
537 fprintf (stderr,
538 "Error opening UDP socket: %s\n",
539 strerror (errno));
540 return -1;
541 }
542 memset (&addr,
543 0,
544 sizeof (addr));
545 addr.sin_family = AF_INET;
546#if HAVE_SOCKADDR_IN_SIN_LEN
547 addr.sin_len = sizeof (struct sockaddr_in);
548#endif
549 addr.sin_addr = *my_ip;
550 addr.sin_port = htons (NAT_TRAV_PORT);
551
552 if (0 != bind (ret,
553 &addr,
554 sizeof(addr)))
555 {
556 fprintf (stderr,
557 "Error binding UDP socket to port %u: %s\n",
558 NAT_TRAV_PORT,
559 strerror (errno));
560 /* likely problematic, but not certain, try to continue */
561 }
562 return ret;
563}
564
565
566int
567main (int argc,
568 char *const *argv)
569{
570 struct in_addr external;
571 fd_set rs;
572 struct timeval tv;
573 uid_t uid;
574 unsigned int alt;
575
576 if (2 != argc)
577 {
578 fprintf (stderr,
579 "This program must be started with our (internal NAT) IP as the only argument.\n");
580 return 1;
581 }
582 if (1 != inet_pton (AF_INET, argv[1], &external))
583 {
584 fprintf (stderr,
585 "Error parsing IPv4 address: %s\n",
586 strerror (errno));
587 return 1;
588 }
589 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
590 {
591 fprintf (stderr,
592 "Internal error converting dummy IP to binary.\n");
593 return 2;
594 }
595 if (-1 == (icmpsock = make_icmp_socket()))
596 {
597 return 3;
598 }
599 if (-1 == (rawsock = make_raw_socket()))
600 {
601 close (icmpsock);
602 return 3;
603 }
604 uid = getuid ();
605 if (0 != setresuid (uid, uid, uid))
606 {
607 fprintf (stderr,
608 "Failed to setresuid: %s\n",
609 strerror (errno));
610 /* not critical, continue anyway */
611 }
612 if (-1 == (udpsock = make_udp_socket(&external)))
613 {
614 close (icmpsock);
615 close (rawsock);
616 return 3;
617 }
618 alt = 0;
619 while (1)
620 {
621 FD_ZERO (&rs);
622 FD_SET (icmpsock, &rs);
623 tv.tv_sec = 0;
624 tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
625 if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
626 {
627 if (errno == EINTR)
628 continue;
629 fprintf (stderr,
630 "select failed: %s\n",
631 strerror (errno));
632 break;
633 }
634 if (1 == getppid()) /* Check the parent process id, if 1 the parent has died, so we should die too */
635 break;
636 if (FD_ISSET (icmpsock, &rs))
637 process_icmp_response ();
638 if (0 == (++alt % 2))
639 send_icmp_echo (&external);
640 else
641 send_udp ();
642 }
643 /* select failed (internal error or OS out of resources) */
644 close (icmpsock);
645 close (rawsock);
646 close (udpsock);
647 return 4;
648}
649
650
651/* end of gnunet-helper-nat-server.c */