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

Referenced by send_icmp_echo().

233 {
234  uint32_t sum;
235  unsigned int i;
236 
237  sum = 0;
238  for (i = 0; i < bytes / 2; i++)
239  sum += data[i];
240  sum = (sum & 0xffff) + (sum >> 16);
241  sum = htons(0xffff - sum);
242  return sum;
243 }
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 252 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().

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

References dummy, NAT_TRAV_PORT, and udpsock.

Referenced by main().

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

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

References rawsock.

Referenced by main().

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

References NAT_TRAV_PORT, and ret.

Referenced by main().

514 {
515  int ret;
516  struct sockaddr_in addr;
517 
518  ret = socket(AF_INET, SOCK_DGRAM, 0);
519  if (-1 == ret)
520  {
521  fprintf(stderr,
522  "Error opening UDP socket: %s\n",
523  strerror(errno));
524  return -1;
525  }
526  memset(&addr, 0, sizeof(addr));
527  addr.sin_family = AF_INET;
528 #if HAVE_SOCKADDR_IN_SIN_LEN
529  addr.sin_len = sizeof(struct sockaddr_in);
530 #endif
531  addr.sin_addr = *my_ip;
532  addr.sin_port = htons(NAT_TRAV_PORT);
533 
534  if (0 != bind(ret,
535  (struct sockaddr *)&addr,
536  sizeof(addr)))
537  {
538  fprintf(stderr,
539  "Error binding UDP socket to port %u: %s\n",
541  strerror(errno));
542  (void)close(ret);
543  return -1;
544  }
545  return ret;
546 }
#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 550 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.

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

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