GNUnet  0.19.4
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):

  • Christian Grothoff
  • Nathan Evans
  • Benjamin Kuperman (22 Aug 2010)

Definition in file gnunet-helper-nat-client.c.

Macro Definition Documentation

◆ _GNU_SOURCE

#define _GNU_SOURCE

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

◆ ICMP_TIME_EXCEEDED

#define ICMP_TIME_EXCEEDED   11

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

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

◆ DUMMY_IP

#define DUMMY_IP   "192.0.2.86"

Must match IP given in the server.

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

◆ NAT_TRAV_PORT

#define NAT_TRAV_PORT   22225

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

◆ PACKET_ID

#define PACKET_ID   256

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

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

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

217 {
218  uint32_t sum;
219  unsigned int i;
220 
221  sum = 0;
222  for (i = 0; i < bytes / 2; i++)
223  sum += data[i];
224  sum = (sum & 0xffff) + (sum >> 16);
225  sum = htons (0xffff - sum);
226  return sum;
227 }
uint32_t data
The data value.

References data, and consensus-simulation::sum.

Referenced by send_icmp(), and send_icmp_udp().

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

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

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().

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

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

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().

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

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

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

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

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

◆ dummy

◆ port

uint16_t port
static

Our "source" port.

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

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