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

Referenced by send_icmp(), and send_icmp_udp().

210 {
211  uint32_t sum;
212  unsigned int i;
213 
214  sum = 0;
215  for (i = 0; i < bytes / 2; i++)
216  sum += data[i];
217  sum = (sum & 0xffff) + (sum >> 16);
218  sum = htons(0xffff - sum);
219  return sum;
220 }
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 230 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().

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

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

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

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

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