GNUnet  0.19.4
gnunet-helper-nat-server.c File Reference

Tool to help bypass NATs using ICMP method; must run as root (SUID will do) This code will work under GNU/Linux only (or maybe BSDs, but never W32) More...

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>
Include dependency graph for gnunet-helper-nat-server.c:

Go to the source code of this file.

Data Structures

struct  ip_header
 IPv4 header. More...
 
struct  icmp_ttl_exceeded_header
 Format of ICMP packet. More...
 
struct  icmp_echo_header
 
struct  udp_header
 Beginning of UDP packet. More...
 

Macros

#define _GNU_SOURCE
 
#define ICMP_TIME_EXCEEDED   11
 
#define GNUNET_memcpy(dst, src, n)
 Call memcpy() but check for n being 0 first. More...
 
#define VERBOSE   0
 Should we print some debug output? More...
 
#define PACKET_ID   256
 Must match packet ID used by gnunet-helper-nat-client.c. More...
 
#define DUMMY_IP   "192.0.2.86"
 Must match IP given in the client. More...
 
#define NAT_TRAV_PORT   22225
 Port for UDP. More...
 
#define ICMP_SEND_FREQUENCY_MS   500
 How often do we send our ICMP messages to receive replies? More...
 

Functions

static uint16_t calc_checksum (const uint16_t *data, unsigned int bytes)
 CRC-16 for IP/ICMP headers. More...
 
static void send_icmp_echo (const struct in_addr *my_ip)
 Send an ICMP message to the dummy IP. More...
 
static void send_udp ()
 Send a UDP message to the dummy IP. More...
 
static void process_icmp_response ()
 We've received an ICMP response. More...
 
static int setup_raw_socket ()
 Fully initialize the raw socket. More...
 
static int make_udp_socket (const struct in_addr *my_ip)
 Create a UDP socket for writing. More...
 
int main (int argc, char *const *argv)
 

Variables

static int icmpsock
 Socket we use to receive "fake" ICMP replies. More...
 
static int rawsock
 Socket we use to send our ICMP requests. More...
 
static int udpsock
 Socket we use to send our UDP requests. More...
 
static struct in_addr dummy
 Target "dummy" address. More...
 

Detailed Description

Tool to help bypass NATs using ICMP method; must run as root (SUID will do) This code will work under GNU/Linux only (or maybe BSDs, but never W32)

Author
Christian Grothoff

This program will send ONE ICMP message every 500 ms RAW sockets to a DUMMY IP address and also listens for ICMP replies. Since it uses RAW sockets, it must be installed SUID or run as 'root'. In order to keep the security risk of the resulting SUID binary minimal, the program ONLY opens the two RAW sockets with root privileges, then drops them and only then starts to process command line arguments. The code also does not link against any shared libraries (except libc) and is strictly minimal (except for checking for errors). The following list of people have reviewed this code and considered it safe since the last modification (if you reviewed it, please have your name added to the list):

  • Christian Grothoff
  • Nathan Evans
  • Benjamin Kuperman (22 Aug 2010)
  • Jacob Appelbaum (19 Dec 2011)

Definition in file gnunet-helper-nat-server.c.

Macro Definition Documentation

◆ _GNU_SOURCE

#define _GNU_SOURCE

Definition at line 50 of file gnunet-helper-nat-server.c.

◆ ICMP_TIME_EXCEEDED

#define ICMP_TIME_EXCEEDED   11

Definition at line 71 of file gnunet-helper-nat-server.c.

◆ GNUNET_memcpy

#define GNUNET_memcpy (   dst,
  src,
 
)
Value:
do { if (0 != n) { (void) memcpy (dst, src, \
n); \
} } while (0)

Call memcpy() but check for n being 0 first.

In the latter case, it is now safe to pass NULL for src or dst. Unlike traditional memcpy(), returns nothing.

Parameters
dstdestination of the copy, may be NULL if n is zero
srcsource of the copy, may be NULL if n is zero
nnumber of bytes to copy

Definition at line 83 of file gnunet-helper-nat-server.c.

◆ VERBOSE

#define VERBOSE   0

Should we print some debug output?

Definition at line 90 of file gnunet-helper-nat-server.c.

◆ PACKET_ID

#define PACKET_ID   256

Must match packet ID used by gnunet-helper-nat-client.c.

Definition at line 95 of file gnunet-helper-nat-server.c.

◆ DUMMY_IP

#define DUMMY_IP   "192.0.2.86"

Must match IP given in the client.

Definition at line 100 of file gnunet-helper-nat-server.c.

◆ NAT_TRAV_PORT

#define NAT_TRAV_PORT   22225

Port for UDP.

Definition at line 105 of file gnunet-helper-nat-server.c.

◆ ICMP_SEND_FREQUENCY_MS

#define ICMP_SEND_FREQUENCY_MS   500

How often do we send our ICMP messages to receive replies?

Definition at line 110 of file gnunet-helper-nat-server.c.

Function Documentation

◆ calc_checksum()

static uint16_t calc_checksum ( const uint16_t *  data,
unsigned int  bytes 
)
static

CRC-16 for IP/ICMP headers.

Parameters
datawhat to calculate the CRC over
bytesnumber of bytes in data (must be multiple of 2)
Returns
the CRC 16.

Definition at line 239 of file gnunet-helper-nat-server.c.

240 {
241  uint32_t sum;
242  unsigned int i;
243 
244  sum = 0;
245  for (i = 0; i < bytes / 2; i++)
246  sum += data[i];
247  sum = (sum & 0xffff) + (sum >> 16);
248  sum = htons (0xffff - sum);
249  return sum;
250 }
uint32_t data
The data value.

References data, and consensus-simulation::sum.

Referenced by send_icmp_echo().

Here is the caller graph for this function:

◆ send_icmp_echo()

static void send_icmp_echo ( const struct in_addr *  my_ip)
static

Send an ICMP message to the dummy IP.

Parameters
my_ipsource address (our ip address)

Definition at line 259 of file gnunet-helper-nat-server.c.

260 {
261  char packet[sizeof(struct ip_header) + sizeof(struct icmp_echo_header)];
262  struct icmp_echo_header icmp_echo;
263  struct ip_header ip_pkt;
264  struct sockaddr_in dst;
265  size_t off;
266  int err;
267 
268  off = 0;
269  ip_pkt.vers_ihl = 0x45;
270  ip_pkt.tos = 0;
271  ip_pkt.pkt_len = htons (sizeof(packet));
272  ip_pkt.id = htons (PACKET_ID);
273  ip_pkt.flags_frag_offset = 0;
274  ip_pkt.ttl = IPDEFTTL;
275  ip_pkt.proto = IPPROTO_ICMP;
276  ip_pkt.checksum = 0;
277  ip_pkt.src_ip = my_ip->s_addr;
278  ip_pkt.dst_ip = dummy.s_addr;
279  ip_pkt.checksum =
280  htons (calc_checksum ((uint16_t *) &ip_pkt,
281  sizeof(struct ip_header)));
282  GNUNET_memcpy (&packet[off],
283  &ip_pkt,
284  sizeof(struct ip_header));
285  off += sizeof(struct ip_header);
286 
287  icmp_echo.type = ICMP_ECHO;
288  icmp_echo.code = 0;
289  icmp_echo.checksum = 0;
290  icmp_echo.reserved = 0;
291  icmp_echo.checksum =
292  htons (calc_checksum
293  ((uint16_t *) &icmp_echo,
294  sizeof(struct icmp_echo_header)));
295  GNUNET_memcpy (&packet[off],
296  &icmp_echo,
297  sizeof(struct icmp_echo_header));
298  off += sizeof(struct icmp_echo_header);
299 
300  memset (&dst, 0, sizeof(dst));
301  dst.sin_family = AF_INET;
302 #if HAVE_SOCKADDR_IN_SIN_LEN
303  dst.sin_len = sizeof(struct sockaddr_in);
304 #endif
305  dst.sin_addr = dummy;
306  err = sendto (rawsock,
307  packet,
308  off,
309  0,
310  (struct sockaddr *) &dst,
311  sizeof(dst));
312  if (err < 0)
313  {
314 #if VERBOSE
315  fprintf (stderr,
316  "sendto failed: %s\n",
317  strerror (errno));
318 #endif
319  }
320  else if (sizeof(packet) != err)
321  {
322  fprintf (stderr,
323  "Error: partial send of ICMP message\n");
324  }
325 }
static uint16_t calc_checksum(const uint16_t *data, unsigned int bytes)
CRC-16 for IP/ICMP headers.
static int rawsock
Socket we use to send our ICMP requests.
static struct in_addr dummy
Target "dummy" address.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
#define PACKET_ID
Must match packet ID used by gnunet-helper-nat-client.c.
uint8_t vers_ihl
Version (4 bits) + Internet header length (4 bits)

References calc_checksum(), ip_header::checksum, icmp_echo_header::checksum, icmp_echo_header::code, ip_header::dst_ip, dummy, ip_header::flags_frag_offset, GNUNET_memcpy, ip_header::id, PACKET_ID, ip_header::pkt_len, ip_header::proto, rawsock, icmp_echo_header::reserved, ip_header::src_ip, ip_header::tos, ip_header::ttl, icmp_echo_header::type, and ip_header::vers_ihl.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_udp()

static void send_udp ( )
static

Send a UDP message to the dummy IP.

Definition at line 332 of file gnunet-helper-nat-server.c.

333 {
334  struct sockaddr_in dst;
335  ssize_t err;
336 
337  memset (&dst, 0, sizeof(dst));
338  dst.sin_family = AF_INET;
339 #if HAVE_SOCKADDR_IN_SIN_LEN
340  dst.sin_len = sizeof(struct sockaddr_in);
341 #endif
342  dst.sin_addr = dummy;
343  dst.sin_port = htons (NAT_TRAV_PORT);
344  err = sendto (udpsock,
345  NULL,
346  0,
347  0,
348  (struct sockaddr *) &dst,
349  sizeof(dst));
350  if (err < 0)
351  {
352 #if VERBOSE
353  fprintf (stderr,
354  "sendto failed: %s\n",
355  strerror (errno));
356 #endif
357  }
358  else if (0 != err)
359  {
360  fprintf (stderr,
361  "Error: partial send of ICMP message\n");
362  }
363 }
#define NAT_TRAV_PORT
Port for UDP.
static int udpsock
Socket we use to send our UDP requests.

References dummy, NAT_TRAV_PORT, and udpsock.

Referenced by main().

Here is the caller graph for this function:

◆ process_icmp_response()

static void process_icmp_response ( )
static

We've received an ICMP response.

Process it.

Definition at line 370 of file gnunet-helper-nat-server.c.

371 {
372  char buf[65536];
373  ssize_t have;
374  struct in_addr source_ip;
375  struct ip_header ip_pkt;
376  struct icmp_ttl_exceeded_header icmp_ttl;
377  struct icmp_echo_header icmp_echo;
378  struct udp_header udp_pkt;
379  size_t off;
380  uint16_t port;
381 
382  have = read (icmpsock, buf, sizeof(buf));
383  if (-1 == have)
384  {
385  fprintf (stderr,
386  "Error reading raw socket: %s\n",
387  strerror (errno));
388  return;
389  }
390 #if VERBOSE
391  fprintf (stderr,
392  "Received message of %u bytes\n",
393  (unsigned int) have);
394 #endif
395  if (have <
396  (ssize_t) (sizeof(struct ip_header)
397  + sizeof(struct icmp_ttl_exceeded_header)
398  + sizeof(struct ip_header)))
399  {
400  /* malformed */
401  return;
402  }
403  off = 0;
404  GNUNET_memcpy (&ip_pkt,
405  &buf[off],
406  sizeof(struct ip_header));
407  off += sizeof(struct ip_header);
408  GNUNET_memcpy (&icmp_ttl,
409  &buf[off],
410  sizeof(struct icmp_ttl_exceeded_header));
411  off += sizeof(struct icmp_ttl_exceeded_header);
412  if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
413  {
414  /* different type than what we want */
415  return;
416  }
417  /* grab source IP of 1st IP header */
418  source_ip.s_addr = ip_pkt.src_ip;
419 
420  /* skip 2nd IP header */
421  GNUNET_memcpy (&ip_pkt,
422  &buf[off],
423  sizeof(struct ip_header));
424  off += sizeof(struct ip_header);
425 
426  switch (ip_pkt.proto)
427  {
428  case IPPROTO_ICMP:
429  if (have !=
430  (sizeof(struct ip_header) * 2
431  + sizeof(struct icmp_ttl_exceeded_header)
432  + sizeof(struct icmp_echo_header)))
433  {
434  /* malformed */
435  return;
436  }
437  /* grab ICMP ECHO content */
438  GNUNET_memcpy (&icmp_echo,
439  &buf[off],
440  sizeof(struct icmp_echo_header));
441  port = (uint16_t) ntohl (icmp_echo.reserved);
442  break;
443 
444  case IPPROTO_UDP:
445  if (have !=
446  (sizeof(struct ip_header) * 2
447  + sizeof(struct icmp_ttl_exceeded_header) + sizeof(struct udp_header)))
448  {
449  /* malformed */
450  return;
451  }
452  /* grab UDP content */
453  GNUNET_memcpy (&udp_pkt,
454  &buf[off],
455  sizeof(struct udp_header));
456  port = ntohs (udp_pkt.length);
457  break;
458 
459  default:
460  /* different type than what we want */
461  return;
462  }
463 
464  if (port == 0)
465  fprintf (stdout, "%s\n",
466  inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)));
467  else
468  fprintf (stdout, "%s:%u\n",
469  inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)),
470  (unsigned int) port);
471  fflush (stdout);
472 }
static uint16_t port
Port number.
Definition: gnunet-bcd.c:147
#define ICMP_TIME_EXCEEDED
static int icmpsock
Socket we use to receive "fake" ICMP replies.
static char buf[2048]
Beginning of UDP packet.

References buf, icmp_ttl_exceeded_header::code, GNUNET_memcpy, ICMP_TIME_EXCEEDED, icmpsock, udp_header::length, port, ip_header::proto, icmp_echo_header::reserved, ip_header::src_ip, and icmp_ttl_exceeded_header::type.

Referenced by main().

Here is the caller graph for this function:

◆ setup_raw_socket()

static int setup_raw_socket ( )
static

Fully initialize the raw socket.

Returns
-1 on error, 0 on success

Definition at line 481 of file gnunet-helper-nat-server.c.

482 {
483  const int one = 1;
484 
485  if (-1 ==
486  setsockopt (rawsock,
487  SOL_SOCKET,
488  SO_BROADCAST,
489  (char *) &one,
490  sizeof(one)))
491  {
492  fprintf (stderr,
493  "setsockopt failed: %s\n",
494  strerror (errno));
495  return -1;
496  }
497  if (-1 ==
498  setsockopt (rawsock,
499  IPPROTO_IP,
500  IP_HDRINCL,
501  (char *) &one,
502  sizeof(one)))
503  {
504  fprintf (stderr,
505  "setsockopt failed: %s\n",
506  strerror (errno));
507  return -1;
508  }
509  return 0;
510 }

References rawsock.

Referenced by main().

Here is the caller graph for this function:

◆ make_udp_socket()

static int make_udp_socket ( const struct in_addr *  my_ip)
static

Create a UDP socket for writing.

Parameters
my_ipsource address (our ip address)
Returns
-1 on error

Definition at line 520 of file gnunet-helper-nat-server.c.

521 {
522  int ret;
523  struct sockaddr_in addr;
524 
525  ret = socket (AF_INET, SOCK_DGRAM, 0);
526  if (-1 == ret)
527  {
528  fprintf (stderr,
529  "Error opening UDP socket: %s\n",
530  strerror (errno));
531  return -1;
532  }
533  memset (&addr, 0, sizeof(addr));
534  addr.sin_family = AF_INET;
535 #if HAVE_SOCKADDR_IN_SIN_LEN
536  addr.sin_len = sizeof(struct sockaddr_in);
537 #endif
538  addr.sin_addr = *my_ip;
539  addr.sin_port = htons (NAT_TRAV_PORT);
540 
541  if (0 != bind (ret,
542  (struct sockaddr *) &addr,
543  sizeof(addr)))
544  {
545  fprintf (stderr,
546  "Error binding UDP socket to port %u: %s\n",
548  strerror (errno));
549  (void) close (ret);
550  return -1;
551  }
552  return ret;
553 }
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81

References NAT_TRAV_PORT, and ret.

Referenced by main().

Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char *const *  argv 
)

Definition at line 557 of file gnunet-helper-nat-server.c.

559 {
560  struct in_addr external;
561  fd_set rs;
562  struct timeval tv;
563  uid_t uid;
564  unsigned int alt;
565  int icmp_eno;
566  int raw_eno;
567  int global_ret;
568 
569  /* Create an ICMP raw socket for reading (we'll check errors later) */
570  icmpsock = socket (AF_INET,
571  SOCK_RAW,
572  IPPROTO_ICMP);
573  icmp_eno = errno;
574 
575  /* Create an (ICMP) raw socket for writing (we'll check errors later) */
576  rawsock = socket (AF_INET,
577  SOCK_RAW,
578  IPPROTO_RAW);
579  raw_eno = errno;
580  udpsock = -1;
581 
582  /* drop root rights */
583  uid = getuid ();
584 #ifdef HAVE_SETRESUID
585  if (0 != setresuid (uid, uid, uid))
586  {
587  fprintf (stderr,
588  "Failed to setresuid: %s\n",
589  strerror (errno));
590  global_ret = 1;
591  goto error_exit;
592  }
593 #else
594  if (0 != (setuid (uid) | seteuid (uid)))
595  {
596  fprintf (stderr,
597  "Failed to setuid: %s\n",
598  strerror (errno));
599  global_ret = 2;
600  goto error_exit;
601  }
602 #endif
603 
604  /* Now that we run without root rights, we can do error checking... */
605  if (2 != argc)
606  {
607  fprintf (stderr,
608  "This program must be started with our (internal NAT) IP as the only argument.\n");
609  global_ret = 3;
610  goto error_exit;
611  }
612  if (1 != inet_pton (AF_INET, argv[1], &external))
613  {
614  fprintf (stderr,
615  "Error parsing IPv4 address: %s\n",
616  strerror (errno));
617  global_ret = 4;
618  goto error_exit;
619  }
620  if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
621  {
622  fprintf (stderr,
623  "Internal error converting dummy IP to binary.\n");
624  global_ret = 5;
625  goto error_exit;
626  }
627 
628  /* error checking icmpsock */
629  if (-1 == icmpsock)
630  {
631  fprintf (stderr,
632  "Error opening RAW socket: %s\n",
633  strerror (icmp_eno));
634  global_ret = 6;
635  goto error_exit;
636  }
637  if (icmpsock >= FD_SETSIZE)
638  {
639  /* this could happen if we were started with a large number of already-open
640  file descriptors... */
641  fprintf (stderr,
642  "Socket number too large (%d > %u)\n",
643  icmpsock,
644  (unsigned int) FD_SETSIZE);
645  global_ret = 7;
646  goto error_exit;
647  }
648 
649  /* error checking rawsock */
650  if (-1 == rawsock)
651  {
652  fprintf (stderr,
653  "Error opening RAW socket: %s\n",
654  strerror (raw_eno));
655  global_ret = 8;
656  goto error_exit;
657  }
658  /* no need to check 'rawsock' against FD_SETSIZE as it is never used
659  with 'select' */
660 
661  if (0 != setup_raw_socket ())
662  {
663  global_ret = 9;
664  goto error_exit;
665  }
666 
667  if (-1 == (udpsock = make_udp_socket (&external)))
668  {
669  global_ret = 10;
670  goto error_exit;
671  }
672 
673  alt = 0;
674  while (1)
675  {
676  FD_ZERO (&rs);
677  FD_SET (icmpsock, &rs);
678  tv.tv_sec = 0;
679  tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
680  if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
681  {
682  if (errno == EINTR)
683  continue;
684  fprintf (stderr,
685  "select failed: %s\n",
686  strerror (errno));
687  break;
688  }
689  if (1 == getppid ()) /* Check the parent process id, if 1 the parent has died, so we should die too */
690  break;
691  if (FD_ISSET (icmpsock, &rs))
692  {
694  continue;
695  }
696  if (0 == (++alt % 2))
697  send_icmp_echo (&external);
698  else
699  send_udp ();
700  }
701 
702  /* select failed (internal error or OS out of resources) */
703  global_ret = 11;
704 error_exit:
705  if (-1 != icmpsock)
706  (void) close (icmpsock);
707  if (-1 != rawsock)
708  (void) close (rawsock);
709  if (-1 != udpsock)
710  (void) close (udpsock);
711  return global_ret;
712 }
static void process_icmp_response()
We've received an ICMP response.
static void send_udp()
Send a UDP message to the dummy IP.
#define DUMMY_IP
Must match IP given in the client.
static int setup_raw_socket()
Fully initialize the raw socket.
#define ICMP_SEND_FREQUENCY_MS
How often do we send our ICMP messages to receive replies?
static int make_udp_socket(const struct in_addr *my_ip)
Create a UDP socket for writing.
static void send_icmp_echo(const struct in_addr *my_ip)
Send an ICMP message to the dummy IP.
static int global_ret
Return value from main.

References dummy, DUMMY_IP, global_ret, ICMP_SEND_FREQUENCY_MS, icmpsock, make_udp_socket(), process_icmp_response(), rawsock, send_icmp_echo(), send_udp(), setup_raw_socket(), and udpsock.

Here is the call graph for this function:

Variable Documentation

◆ icmpsock

int icmpsock
static

Socket we use to receive "fake" ICMP replies.

Definition at line 213 of file gnunet-helper-nat-server.c.

Referenced by main(), and process_icmp_response().

◆ rawsock

int rawsock
static

Socket we use to send our ICMP requests.

Definition at line 218 of file gnunet-helper-nat-server.c.

Referenced by main(), send_icmp_echo(), and setup_raw_socket().

◆ udpsock

int udpsock
static

Socket we use to send our UDP requests.

Definition at line 223 of file gnunet-helper-nat-server.c.

Referenced by main(), and send_udp().

◆ dummy

struct in_addr dummy
static

Target "dummy" address.

Definition at line 223 of file gnunet-helper-nat-server.c.

Referenced by main(), send_icmp_echo(), and send_udp().