GNUnet  0.11.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)
 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,
 
)
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 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 89 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 94 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 99 of file gnunet-helper-nat-server.c.

Referenced by main().

◆ NAT_TRAV_PORT

#define NAT_TRAV_PORT   22225

Port for UDP.

Definition at line 104 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 109 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 238 of file gnunet-helper-nat-server.c.

Referenced by send_icmp_echo().

239 {
240  uint32_t sum;
241  unsigned int i;
242 
243  sum = 0;
244  for (i = 0; i < bytes / 2; i++)
245  sum += data[i];
246  sum = (sum & 0xffff) + (sum >> 16);
247  sum = htons (0xffff - sum);
248  return sum;
249 }
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 258 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, 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().

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

References dummy, NAT_TRAV_PORT, and udpsock.

Referenced by main().

332 {
333  struct sockaddr_in dst;
334  ssize_t err;
335 
336  memset (&dst, 0, sizeof(dst));
337  dst.sin_family = AF_INET;
338 #if HAVE_SOCKADDR_IN_SIN_LEN
339  dst.sin_len = sizeof(struct sockaddr_in);
340 #endif
341  dst.sin_addr = dummy;
342  dst.sin_port = htons (NAT_TRAV_PORT);
343  err = sendto (udpsock,
344  NULL,
345  0,
346  0,
347  (struct sockaddr *) &dst,
348  sizeof(dst));
349  if (err < 0)
350  {
351 #if VERBOSE
352  fprintf (stderr,
353  "sendto failed: %s\n",
354  strerror (errno));
355 #endif
356  }
357  else if (0 != err)
358  {
359  fprintf (stderr,
360  "Error: partial send of ICMP message\n");
361  }
362 }
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 369 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().

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

References rawsock.

Referenced by main().

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

References NAT_TRAV_PORT, and ret.

Referenced by main().

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

◆ main()

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

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

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.

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

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