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

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

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

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