GNUnet  0.10.x
gnunet-helper-nat-client.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2010 GNUnet e.V.
4 
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Affero General Public License for more details.
14 
15  You should have received a copy of the GNU Affero General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19 */
20 
44 #if HAVE_CONFIG_H
45 /* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46 #include "gnunet_config.h"
47 #else
48 #define _GNU_SOURCE
49 #endif
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <arpa/inet.h>
53 #include <sys/types.h>
54 #include <unistd.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <stdint.h>
60 #include <netinet/ip.h>
61 #include <netinet/ip_icmp.h>
62 #include <netinet/in.h>
63 
64 /* The following constant is missing from FreeBSD 9.2 */
65 #ifndef ICMP_TIME_EXCEEDED
66 #define ICMP_TIME_EXCEEDED 11
67 #endif
68 
78 #define GNUNET_memcpy(dst,src,n) do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
79 
83 #define DUMMY_IP "192.0.2.86"
84 
85 #define NAT_TRAV_PORT 22225
86 
90 #define PACKET_ID 256
91 
95 struct ip_header
96 {
97 
101  uint8_t vers_ihl;
102 
106  uint8_t tos;
107 
111  uint16_t pkt_len;
112 
116  uint16_t id;
117 
121  uint16_t flags_frag_offset;
122 
126  uint8_t ttl;
127 
131  uint8_t proto;
132 
136  uint16_t checksum;
137 
141  uint32_t src_ip;
142 
146  uint32_t dst_ip;
147 };
148 
153 {
154  uint8_t type;
155 
156  uint8_t code;
157 
158  uint16_t checksum;
159 
160  uint32_t unused;
161 
162  /* followed by original payload */
163 };
164 
165 struct icmp_echo_header
166 {
167  uint8_t type;
168 
169  uint8_t code;
170 
171  uint16_t checksum;
172 
173  uint32_t reserved;
174 };
175 
179 struct udp_header
180 {
181  uint16_t src_port;
182 
183  uint16_t dst_port;
184 
185  uint16_t length;
186 
187  uint16_t crc;
188 };
189 
193 static int rawsock;
194 
198 static struct in_addr dummy;
199 
203 static uint16_t port;
204 
205 
213 static uint16_t
214 calc_checksum (const uint16_t * data, unsigned int bytes)
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 }
226 
227 
234 static void
235 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
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 }
336 
337 
344 static void
345 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
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 }
450 
451 
452 int
453 main (int argc, char *const *argv)
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 }
540 
541 /* end of gnunet-helper-nat-client.c */
#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.
static void send_icmp_udp(const struct in_addr *my_ip, const struct in_addr *other)
Send an ICMP message to the target.
uint32_t src_ip
Source address.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
uint16_t checksum
Header checksum.
uint16_t id
Identification.
int main(int argc, char *const *argv)
uint32_t dst_ip
Destination address.
uint8_t vers_ihl
Version (4 bits) + Internet header length (4 bits)
uint8_t tos
Type of service.
uint16_t pkt_len
Total length.
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
uint16_t flags_frag_offset
Flags (3 bits) + Fragment offset (13 bits)
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.
uint8_t ttl
Time to live.
#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 int inet_pton(int af, const char *cp, struct in_addr *buf)
Convert IPv4 address from text to binary form.
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model
static uint16_t port
Our "source" port.
static unsigned long long reserved
How much space have we currently reserved?
Beginning of UDP packet.
#define DUMMY_IP
Must match IP given in the server.
uint32_t data
The data value.
static int global_ret
Return value from main.