GNUnet  0.11.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)
 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,
 
)
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 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 85 of file gnunet-helper-nat-client.c.

Referenced by main().

◆ NAT_TRAV_PORT

#define NAT_TRAV_PORT   22225

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

Referenced by send_icmp(), and send_icmp_udp().

216 {
217  uint32_t sum;
218  unsigned int i;
219 
220  sum = 0;
221  for (i = 0; i < bytes / 2; i++)
222  sum += data[i];
223  sum = (sum & 0xffff) + (sum >> 16);
224  sum = htons (0xffff - sum);
225  return sum;
226 }
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 236 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().

237 {
238  char packet[sizeof(struct ip_header) * 2
239  + sizeof(struct icmp_ttl_exceeded_header)
240  + sizeof(struct udp_header)];
241  struct ip_header ip_pkt;
242  struct icmp_ttl_exceeded_header icmp_pkt;
243  struct udp_header udp_pkt;
244  struct sockaddr_in dst;
245  size_t off;
246  int err;
247 
248  /* ip header: send to (known) ip address */
249  off = 0;
250  ip_pkt.vers_ihl = 0x45;
251  ip_pkt.tos = 0;
252 #ifdef FREEBSD
253  ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
254 #else
255  ip_pkt.pkt_len = htons (sizeof(packet));
256 #endif
257  ip_pkt.id = htons (PACKET_ID);
258  ip_pkt.flags_frag_offset = 0;
259  ip_pkt.ttl = 128;
260  ip_pkt.proto = IPPROTO_ICMP;
261  ip_pkt.checksum = 0;
262  ip_pkt.src_ip = my_ip->s_addr;
263  ip_pkt.dst_ip = other->s_addr;
264  ip_pkt.checksum =
265  htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
266  GNUNET_memcpy (&packet[off],
267  &ip_pkt,
268  sizeof(struct ip_header));
269  off += sizeof(struct ip_header);
270 
271  icmp_pkt.type = ICMP_TIME_EXCEEDED;
272  icmp_pkt.code = 0;
273  icmp_pkt.checksum = 0;
274  icmp_pkt.unused = 0;
275  GNUNET_memcpy (&packet[off],
276  &icmp_pkt,
277  sizeof(struct icmp_ttl_exceeded_header));
278  off += sizeof(struct icmp_ttl_exceeded_header);
279 
280  /* ip header of the presumably 'lost' udp packet */
281  ip_pkt.vers_ihl = 0x45;
282  ip_pkt.tos = 0;
283  ip_pkt.pkt_len =
284  htons (sizeof(struct ip_header) + sizeof(struct udp_header));
285  ip_pkt.id = htons (0);
286  ip_pkt.flags_frag_offset = 0;
287  ip_pkt.ttl = 128;
288  ip_pkt.proto = IPPROTO_UDP;
289  ip_pkt.checksum = 0;
290  ip_pkt.src_ip = other->s_addr;
291  ip_pkt.dst_ip = dummy.s_addr;
292  ip_pkt.checksum =
293  htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
294  GNUNET_memcpy (&packet[off],
295  &ip_pkt,
296  sizeof(struct ip_header));
297  off += sizeof(struct ip_header);
298 
299  /* build UDP header */
300  udp_pkt.src_port = htons (NAT_TRAV_PORT);
301  udp_pkt.dst_port = htons (NAT_TRAV_PORT);
302  udp_pkt.length = htons (port);
303  udp_pkt.crc = 0;
304  GNUNET_memcpy (&packet[off],
305  &udp_pkt,
306  sizeof(struct udp_header));
307  off += sizeof(struct udp_header);
308 
309  /* set ICMP checksum */
310  icmp_pkt.checksum =
311  htons (calc_checksum
312  ((uint16_t *) &packet[sizeof(struct ip_header)],
313  sizeof(struct icmp_ttl_exceeded_header)
314  + sizeof(struct ip_header) + sizeof(struct udp_header)));
315  GNUNET_memcpy (&packet[sizeof(struct ip_header)],
316  &icmp_pkt,
317  sizeof(struct icmp_ttl_exceeded_header));
318 
319  memset (&dst, 0, sizeof(dst));
320  dst.sin_family = AF_INET;
321 #if HAVE_SOCKADDR_IN_SIN_LEN
322  dst.sin_len = sizeof(struct sockaddr_in);
323 #endif
324  dst.sin_addr = *other;
325  err =
326  sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
327  sizeof(dst));
328  if (err < 0)
329  {
330  fprintf (stderr, "sendto failed: %s\n", strerror (errno));
331  }
332  else if (sizeof(packet) != (size_t) err)
333  {
334  fprintf (stderr, "Error: partial send of ICMP message with size %lu\n",
335  (unsigned long) off);
336  }
337 }
#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 347 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_TIME_EXCEEDED, ip_header::id, 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().

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

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

456 {
457  const int one = 1;
458  struct in_addr external;
459  struct in_addr target;
460  uid_t uid;
461  unsigned int p;
462  int raw_eno;
463  int global_ret;
464 
465  /* Create an ICMP raw socket for writing (only operation that requires root) */
466  rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
467  raw_eno = errno; /* for later error checking */
468 
469  /* now drop root privileges */
470  uid = getuid ();
471 #ifdef HAVE_SETRESUID
472  if (0 != setresuid (uid, uid, uid))
473  {
474  fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
475  global_ret = 1;
476  goto cleanup;
477  }
478 #else
479  if (0 != (setuid (uid) | seteuid (uid)))
480  {
481  fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
482  global_ret = 2;
483  goto cleanup;
484  }
485 #endif
486  if (-1 == rawsock)
487  {
488  fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno));
489  global_ret = 3;
490  goto cleanup;
491  }
492  if (0 !=
493  setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one,
494  sizeof(one)))
495  {
496  fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
497  global_ret = 4;
498  goto cleanup;
499  }
500  if (0 !=
501  setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof(one)))
502  {
503  fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
504  global_ret = 5;
505  goto cleanup;
506  }
507 
508  if (4 != argc)
509  {
510  fprintf (stderr,
511  "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
512  global_ret = 6;
513  goto cleanup;
514  }
515  if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
516  (1 != inet_pton (AF_INET, argv[2], &target)))
517  {
518  fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
519  global_ret = 7;
520  goto cleanup;
521  }
522  if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
523  {
524  fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
525  global_ret = 8;
526  goto cleanup;
527  }
528  port = (uint16_t) p;
529  if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
530  {
531  fprintf (stderr, "Internal error converting dummy IP to binary.\n");
532  global_ret = 9;
533  goto cleanup;
534  }
535  send_icmp (&external, &target);
536  send_icmp_udp (&external, &target);
537  global_ret = 0;
538 cleanup:
539  if (-1 != rawsock)
540  (void) close (rawsock);
541  return global_ret;
542 }
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 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 194 of file gnunet-helper-nat-client.c.

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

◆ dummy

struct in_addr dummy
static

◆ port

uint16_t port
static

Our "source" port.

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

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