GNUnet  0.10.x
Data Structures | Macros | Functions | Variables
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)   do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
 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):

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

Macro Definition Documentation

◆ _GNU_SOURCE

#define _GNU_SOURCE

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

◆ ICMP_TIME_EXCEEDED

#define ICMP_TIME_EXCEEDED   11

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

Referenced by process_icmp_response().

◆ GNUNET_memcpy

#define GNUNET_memcpy (   dst,
  src,
 
)    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 82 of file gnunet-helper-nat-server.c.

Referenced by process_icmp_response(), and send_icmp_echo().

◆ VERBOSE

#define VERBOSE   0

Should we print some debug output?

Definition at line 87 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 92 of file gnunet-helper-nat-server.c.

Referenced by send_icmp_echo().

◆ DUMMY_IP

#define DUMMY_IP   "192.0.2.86"

Must match IP given in the client.

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

Referenced by main().

◆ NAT_TRAV_PORT

#define NAT_TRAV_PORT   22225

Port for UDP.

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

Referenced by make_udp_socket(), and send_udp().

◆ ICMP_SEND_FREQUENCY_MS

#define ICMP_SEND_FREQUENCY_MS   500

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

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

Referenced by main().

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 237 of file gnunet-helper-nat-server.c.

Referenced by send_icmp_echo().

238 {
239  uint32_t sum;
240  unsigned int i;
241 
242  sum = 0;
243  for (i = 0; i < bytes / 2; i++)
244  sum += data[i];
245  sum = (sum & 0xffff) + (sum >> 16);
246  sum = htons (0xffff - sum);
247  return sum;
248 }
uint32_t data
The data value.
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 257 of file gnunet-helper-nat-server.c.

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, ICMP_ECHO, ip_header::id, IPDEFTTL, 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().

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

References dummy, NAT_TRAV_PORT, and udpsock.

Referenced by main().

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

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().

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

References rawsock.

Referenced by main().

478 {
479  const int one = 1;
480 
481  if (-1 ==
482  setsockopt (rawsock,
483  SOL_SOCKET,
484  SO_BROADCAST,
485  (char *) &one,
486  sizeof (one)))
487  {
488  fprintf (stderr,
489  "setsockopt failed: %s\n",
490  strerror (errno));
491  return -1;
492  }
493  if (-1 ==
494  setsockopt (rawsock,
495  IPPROTO_IP,
496  IP_HDRINCL,
497  (char *) &one,
498  sizeof (one)))
499  {
500  fprintf (stderr,
501  "setsockopt failed: %s\n",
502  strerror (errno));
503  return -1;
504  }
505  return 0;
506 }
static int rawsock
Socket we use to send our ICMP requests.
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 516 of file gnunet-helper-nat-server.c.

References NAT_TRAV_PORT, and ret.

Referenced by main().

517 {
518  int ret;
519  struct sockaddr_in addr;
520 
521  ret = socket (AF_INET, SOCK_DGRAM, 0);
522  if (-1 == ret)
523  {
524  fprintf (stderr,
525  "Error opening UDP socket: %s\n",
526  strerror (errno));
527  return -1;
528  }
529  memset (&addr, 0, sizeof (addr));
530  addr.sin_family = AF_INET;
531 #if HAVE_SOCKADDR_IN_SIN_LEN
532  addr.sin_len = sizeof (struct sockaddr_in);
533 #endif
534  addr.sin_addr = *my_ip;
535  addr.sin_port = htons (NAT_TRAV_PORT);
536 
537  if (0 != bind (ret,
538  (struct sockaddr *) &addr,
539  sizeof (addr)))
540  {
541  fprintf (stderr,
542  "Error binding UDP socket to port %u: %s\n",
544  strerror (errno));
545  (void) close (ret);
546  return -1;
547  }
548  return ret;
549 }
#define NAT_TRAV_PORT
Port for UDP.
static int ret
Final status code.
Definition: gnunet-arm.c:89
Here is the caller graph for this function:

◆ main()

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

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

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

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

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