GNUnet  0.10.x
Data Structures | Macros | Functions | Variables
gnunet-helper-nat-client.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. More...

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>
Include dependency graph for gnunet-helper-nat-client.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 DUMMY_IP   "192.0.2.86"
 Must match IP given in the server. More...
 
#define NAT_TRAV_PORT   22225
 
#define PACKET_ID   256
 Must match packet ID used by gnunet-helper-nat-server.c. 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_udp (const struct in_addr *my_ip, const struct in_addr *other)
 Send an ICMP message to the target. More...
 
static void send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
 Send an ICMP message to the target. More...
 
int main (int argc, char *const *argv)
 

Variables

static int rawsock
 Socket we use to send our fake ICMP replies. More...
 
static struct in_addr dummy
 Target "dummy" address of the packet we pretend to respond to. More...
 
static uint16_t port
 Our "source" port. 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.

Author
Christian Grothoff

This program will send ONE ICMP message using RAW sockets to the IP address specified as the second argument. 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 RAW socket 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-client.c.

Macro Definition Documentation

◆ _GNU_SOURCE

#define _GNU_SOURCE

Definition at line 48 of file gnunet-helper-nat-client.c.

◆ ICMP_TIME_EXCEEDED

#define ICMP_TIME_EXCEEDED   11

Definition at line 66 of file gnunet-helper-nat-client.c.

Referenced by send_icmp(), and send_icmp_udp().

◆ 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 78 of file gnunet-helper-nat-client.c.

Referenced by send_icmp(), and send_icmp_udp().

◆ DUMMY_IP

#define DUMMY_IP   "192.0.2.86"

Must match IP given in the server.

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

Referenced by main().

◆ NAT_TRAV_PORT

#define NAT_TRAV_PORT   22225

Definition at line 85 of file gnunet-helper-nat-client.c.

Referenced by send_icmp_udp().

◆ PACKET_ID

#define PACKET_ID   256

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

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

Referenced by send_icmp(), and send_icmp_udp().

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 214 of file gnunet-helper-nat-client.c.

Referenced by send_icmp(), and send_icmp_udp().

215 {
216  uint32_t sum;
217  unsigned int i;
218 
219  sum = 0;
220  for (i = 0; i < bytes / 2; i++)
221  sum += data[i];
222  sum = (sum & 0xffff) + (sum >> 16);
223  sum = htons (0xffff - sum);
224  return sum;
225 }
uint32_t data
The data value.
Here is the caller graph for this function:

◆ send_icmp_udp()

static void send_icmp_udp ( const struct in_addr *  my_ip,
const struct in_addr *  other 
)
static

Send an ICMP message to the target.

Parameters
my_ipsource address
othertarget address

Definition at line 235 of file gnunet-helper-nat-client.c.

References calc_checksum(), ip_header::checksum, icmp_ttl_exceeded_header::checksum, icmp_ttl_exceeded_header::code, udp_header::crc, ip_header::dst_ip, udp_header::dst_port, dummy, ip_header::flags_frag_offset, GNUNET_memcpy, ICMP_TIME_EXCEEDED, ip_header::id, udp_header::length, NAT_TRAV_PORT, PACKET_ID, ip_header::pkt_len, port, ip_header::proto, rawsock, ip_header::src_ip, udp_header::src_port, ip_header::tos, ip_header::ttl, icmp_ttl_exceeded_header::type, icmp_ttl_exceeded_header::unused, and ip_header::vers_ihl.

Referenced by main().

236 {
237  char packet[sizeof (struct ip_header) * 2 +
238  sizeof (struct icmp_ttl_exceeded_header) +
239  sizeof (struct udp_header)];
240  struct ip_header ip_pkt;
241  struct icmp_ttl_exceeded_header icmp_pkt;
242  struct udp_header udp_pkt;
243  struct sockaddr_in dst;
244  size_t off;
245  int err;
246 
247  /* ip header: send to (known) ip address */
248  off = 0;
249  ip_pkt.vers_ihl = 0x45;
250  ip_pkt.tos = 0;
251 #ifdef FREEBSD
252  ip_pkt.pkt_len = sizeof (packet); /* Workaround PR kern/21737 */
253 #else
254  ip_pkt.pkt_len = htons (sizeof (packet));
255 #endif
256  ip_pkt.id = htons (PACKET_ID);
257  ip_pkt.flags_frag_offset = 0;
258  ip_pkt.ttl = 128;
259  ip_pkt.proto = IPPROTO_ICMP;
260  ip_pkt.checksum = 0;
261  ip_pkt.src_ip = my_ip->s_addr;
262  ip_pkt.dst_ip = other->s_addr;
263  ip_pkt.checksum =
264  htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
265  GNUNET_memcpy (&packet[off],
266  &ip_pkt,
267  sizeof (struct ip_header));
268  off += sizeof (struct ip_header);
269 
270  icmp_pkt.type = ICMP_TIME_EXCEEDED;
271  icmp_pkt.code = 0;
272  icmp_pkt.checksum = 0;
273  icmp_pkt.unused = 0;
274  GNUNET_memcpy (&packet[off],
275  &icmp_pkt,
276  sizeof (struct icmp_ttl_exceeded_header));
277  off += sizeof (struct icmp_ttl_exceeded_header);
278 
279  /* ip header of the presumably 'lost' udp packet */
280  ip_pkt.vers_ihl = 0x45;
281  ip_pkt.tos = 0;
282  ip_pkt.pkt_len =
283  htons (sizeof (struct ip_header) + sizeof (struct udp_header));
284  ip_pkt.id = htons (0);
285  ip_pkt.flags_frag_offset = 0;
286  ip_pkt.ttl = 128;
287  ip_pkt.proto = IPPROTO_UDP;
288  ip_pkt.checksum = 0;
289  ip_pkt.src_ip = other->s_addr;
290  ip_pkt.dst_ip = dummy.s_addr;
291  ip_pkt.checksum =
292  htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
293  GNUNET_memcpy (&packet[off],
294  &ip_pkt,
295  sizeof (struct ip_header));
296  off += sizeof (struct ip_header);
297 
298  /* build UDP header */
299  udp_pkt.src_port = htons (NAT_TRAV_PORT);
300  udp_pkt.dst_port = htons (NAT_TRAV_PORT);
301  udp_pkt.length = htons (port);
302  udp_pkt.crc = 0;
303  GNUNET_memcpy (&packet[off],
304  &udp_pkt,
305  sizeof (struct udp_header));
306  off += sizeof (struct udp_header);
307 
308  /* set ICMP checksum */
309  icmp_pkt.checksum =
310  htons (calc_checksum
311  ((uint16_t *) & packet[sizeof (struct ip_header)],
312  sizeof (struct icmp_ttl_exceeded_header) +
313  sizeof (struct ip_header) + sizeof (struct udp_header)));
314  GNUNET_memcpy (&packet[sizeof (struct ip_header)],
315  &icmp_pkt,
316  sizeof (struct icmp_ttl_exceeded_header));
317 
318  memset (&dst, 0, sizeof (dst));
319  dst.sin_family = AF_INET;
320 #if HAVE_SOCKADDR_IN_SIN_LEN
321  dst.sin_len = sizeof (struct sockaddr_in);
322 #endif
323  dst.sin_addr = *other;
324  err =
325  sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
326  sizeof (dst));
327  if (err < 0)
328  {
329  fprintf (stderr, "sendto failed: %s\n", strerror (errno));
330  }
331  else if (sizeof (packet) != (size_t) err)
332  {
333  fprintf (stderr, "Error: partial send of ICMP message with size %lu\n", (unsigned long) off);
334  }
335 }
#define PACKET_ID
Must match packet ID used by gnunet-helper-nat-server.c.
static uint16_t calc_checksum(const uint16_t *data, unsigned int bytes)
CRC-16 for IP/ICMP headers.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
static int rawsock
Socket we use to send our fake ICMP replies.
#define ICMP_TIME_EXCEEDED
#define NAT_TRAV_PORT
static struct in_addr dummy
Target "dummy" address of the packet we pretend to respond to.
static uint16_t port
Our "source" port.
Beginning of UDP packet.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_icmp()

static void send_icmp ( const struct in_addr *  my_ip,
const struct in_addr *  other 
)
static

Send an ICMP message to the target.

Parameters
my_ipsource address
othertarget address

Definition at line 345 of file gnunet-helper-nat-client.c.

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

Referenced by main().

346 {
347  struct ip_header ip_pkt;
348  struct icmp_ttl_exceeded_header icmp_ttl;
349  struct icmp_echo_header icmp_echo;
350  struct sockaddr_in dst;
351  char packet[sizeof (struct ip_header) * 2 +
352  sizeof (struct icmp_ttl_exceeded_header) +
353  sizeof (struct icmp_echo_header)];
354  size_t off;
355  int err;
356 
357  /* ip header: send to (known) ip address */
358  off = 0;
359  ip_pkt.vers_ihl = 0x45;
360  ip_pkt.tos = 0;
361 #ifdef FREEBSD
362  ip_pkt.pkt_len = sizeof (packet); /* Workaround PR kern/21737 */
363 #else
364  ip_pkt.pkt_len = htons (sizeof (packet));
365 #endif
366  ip_pkt.id = htons (PACKET_ID);
367  ip_pkt.flags_frag_offset = 0;
368  ip_pkt.ttl = IPDEFTTL;
369  ip_pkt.proto = IPPROTO_ICMP;
370  ip_pkt.checksum = 0;
371  ip_pkt.src_ip = my_ip->s_addr;
372  ip_pkt.dst_ip = other->s_addr;
373  ip_pkt.checksum =
374  htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
375  GNUNET_memcpy (&packet[off],
376  &ip_pkt,
377  sizeof (struct ip_header));
378  off = sizeof (ip_pkt);
379 
380  /* icmp reply: time exceeded */
381  icmp_ttl.type = ICMP_TIME_EXCEEDED;
382  icmp_ttl.code = 0;
383  icmp_ttl.checksum = 0;
384  icmp_ttl.unused = 0;
385  GNUNET_memcpy (&packet[off],
386  &icmp_ttl,
387  sizeof (struct icmp_ttl_exceeded_header));
388  off += sizeof (struct icmp_ttl_exceeded_header);
389 
390  /* ip header of the presumably 'lost' udp packet */
391  ip_pkt.vers_ihl = 0x45;
392  ip_pkt.tos = 0;
393  ip_pkt.pkt_len =
394  htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
395  ip_pkt.id = htons (PACKET_ID);
396  ip_pkt.flags_frag_offset = 0;
397  ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
398  ip_pkt.proto = IPPROTO_ICMP;
399  ip_pkt.src_ip = other->s_addr;
400  ip_pkt.dst_ip = dummy.s_addr;
401  ip_pkt.checksum = 0;
402  ip_pkt.checksum =
403  htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
404  GNUNET_memcpy (&packet[off],
405  &ip_pkt,
406  sizeof (struct ip_header));
407  off += sizeof (struct ip_header);
408 
409  icmp_echo.type = ICMP_ECHO;
410  icmp_echo.code = 0;
411  icmp_echo.reserved = htonl (port);
412  icmp_echo.checksum = 0;
413  icmp_echo.checksum =
414  htons (calc_checksum
415  ((uint16_t *) &icmp_echo, sizeof (struct icmp_echo_header)));
416  GNUNET_memcpy (&packet[off],
417  &icmp_echo,
418  sizeof (struct icmp_echo_header));
419 
420  /* no go back to calculate ICMP packet checksum */
421  off = sizeof (struct ip_header);
422  icmp_ttl.checksum =
423  htons (calc_checksum
424  ((uint16_t *) & packet[off],
425  sizeof (struct icmp_ttl_exceeded_header) +
426  sizeof (struct ip_header) + sizeof (struct icmp_echo_header)));
427  GNUNET_memcpy (&packet[off],
428  &icmp_ttl,
429  sizeof (struct icmp_ttl_exceeded_header));
430 
431  /* prepare for transmission */
432  memset (&dst, 0, sizeof (dst));
433  dst.sin_family = AF_INET;
434 #if HAVE_SOCKADDR_IN_SIN_LEN
435  dst.sin_len = sizeof (struct sockaddr_in);
436 #endif
437  dst.sin_addr = *other;
438  err =
439  sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
440  sizeof (dst));
441  if (err < 0)
442  {
443  fprintf (stderr, "sendto failed: %s\n", strerror (errno));
444  }
445  else if (sizeof (packet) != (size_t) err)
446  {
447  fprintf (stderr, "Error: partial send of ICMP message\n");
448  }
449 }
#define PACKET_ID
Must match packet ID used by gnunet-helper-nat-server.c.
static uint16_t calc_checksum(const uint16_t *data, unsigned int bytes)
CRC-16 for IP/ICMP headers.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
static int rawsock
Socket we use to send our fake ICMP replies.
#define ICMP_TIME_EXCEEDED
static struct in_addr dummy
Target "dummy" address of the packet we pretend to respond to.
static uint16_t port
Our "source" port.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

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

Definition at line 453 of file gnunet-helper-nat-client.c.

References cleanup(), dummy, DUMMY_IP, global_ret, inet_pton(), p, port, rawsock, send_icmp(), and send_icmp_udp().

454 {
455  const int one = 1;
456  struct in_addr external;
457  struct in_addr target;
458  uid_t uid;
459  unsigned int p;
460  int raw_eno;
461  int global_ret;
462 
463  /* Create an ICMP raw socket for writing (only operation that requires root) */
464  rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
465  raw_eno = errno; /* for later error checking */
466 
467  /* now drop root privileges */
468  uid = getuid ();
469 #ifdef HAVE_SETRESUID
470  if (0 != setresuid (uid, uid, uid))
471  {
472  fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
473  global_ret = 1;
474  goto cleanup;
475  }
476 #else
477  if (0 != (setuid (uid) | seteuid (uid)))
478  {
479  fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
480  global_ret = 2;
481  goto cleanup;
482  }
483 #endif
484  if (-1 == rawsock)
485  {
486  fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno));
487  global_ret = 3;
488  goto cleanup;
489  }
490  if (0 !=
491  setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof (one)))
492  {
493  fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
494  global_ret = 4;
495  goto cleanup;
496  }
497  if (0 !=
498  setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof (one)))
499  {
500  fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
501  global_ret = 5;
502  goto cleanup;
503  }
504 
505  if (4 != argc)
506  {
507  fprintf (stderr,
508  "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
509  global_ret = 6;
510  goto cleanup;
511  }
512  if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
513  (1 != inet_pton (AF_INET, argv[2], &target)))
514  {
515  fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
516  global_ret = 7;
517  goto cleanup;
518  }
519  if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
520  {
521  fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
522  global_ret = 8;
523  goto cleanup;
524  }
525  port = (uint16_t) p;
526  if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
527  {
528  fprintf (stderr, "Internal error converting dummy IP to binary.\n");
529  global_ret = 9;
530  goto cleanup;
531  }
532  send_icmp (&external, &target);
533  send_icmp_udp (&external, &target);
534  global_ret = 0;
535  cleanup:
536  if (-1 != rawsock)
537  (void) close (rawsock);
538  return global_ret;
539 }
static void send_icmp_udp(const struct in_addr *my_ip, const struct in_addr *other)
Send an ICMP message to the target.
static void send_icmp(const struct in_addr *my_ip, const struct in_addr *other)
Send an ICMP message to the target.
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-qr.c:59
static void cleanup(void *cls)
Function scheduled as very last function, cleans up after us.
static int rawsock
Socket we use to send our fake ICMP replies.
static struct in_addr dummy
Target "dummy" address of the packet we pretend to respond to.
static int inet_pton(int af, const char *cp, struct in_addr *buf)
Convert IPv4 address from text to binary form.
static uint16_t port
Our "source" port.
#define DUMMY_IP
Must match IP given in the server.
static int global_ret
Return value from main.
Here is the call graph for this function:

Variable Documentation

◆ rawsock

int rawsock
static

Socket we use to send our fake ICMP replies.

Definition at line 193 of file gnunet-helper-nat-client.c.

Referenced by main(), send_icmp(), and send_icmp_udp().

◆ dummy

struct in_addr dummy
static

Target "dummy" address of the packet we pretend to respond to.

Definition at line 198 of file gnunet-helper-nat-client.c.

Referenced by main(), send_icmp(), and send_icmp_udp().

◆ port

uint16_t port
static

Our "source" port.

Definition at line 203 of file gnunet-helper-nat-client.c.

Referenced by main(), send_icmp(), and send_icmp_udp().