GNUnet  0.19.4
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 "platform.h"
47 #include "gnunet_private_config.h"
48 #else
49 #define _GNU_SOURCE
50 #endif
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <arpa/inet.h>
54 #include <sys/types.h>
55 #include <unistd.h>
56 #include <stdio.h>
57 #include <string.h>
58 #include <errno.h>
59 #include <stdlib.h>
60 #include <stdint.h>
61 #include <netinet/ip.h>
62 #include <netinet/ip_icmp.h>
63 #include <netinet/in.h>
64 
65 /* The following constant is missing from FreeBSD 9.2 */
66 #ifndef ICMP_TIME_EXCEEDED
67 #define ICMP_TIME_EXCEEDED 11
68 #endif
69 
79 #define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \
80  n); \
81  } } while (0)
82 
86 #define DUMMY_IP "192.0.2.86"
87 
88 #define NAT_TRAV_PORT 22225
89 
93 #define PACKET_ID 256
94 
98 struct ip_header
99 {
103  uint8_t vers_ihl;
104 
108  uint8_t tos;
109 
113  uint16_t pkt_len;
114 
118  uint16_t id;
119 
124 
128  uint8_t ttl;
129 
133  uint8_t proto;
134 
138  uint16_t checksum;
139 
143  uint32_t src_ip;
144 
148  uint32_t dst_ip;
149 };
150 
155 {
156  uint8_t type;
157 
158  uint8_t code;
159 
160  uint16_t checksum;
161 
162  uint32_t unused;
163 
164  /* followed by original payload */
165 };
166 
168 {
169  uint8_t type;
170 
171  uint8_t code;
172 
173  uint16_t checksum;
174 
175  uint32_t reserved;
176 };
177 
182 {
183  uint16_t src_port;
184 
185  uint16_t dst_port;
186 
187  uint16_t length;
188 
189  uint16_t crc;
190 };
191 
195 static int rawsock;
196 
200 static struct in_addr dummy;
201 
205 static uint16_t port;
206 
207 
215 static uint16_t
216 calc_checksum (const uint16_t *data, unsigned int bytes)
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 }
228 
229 
236 static void
237 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
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 }
340 
341 
348 static void
349 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
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 }
454 
455 
456 int
457 main (int argc, char *const *argv)
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 }
545 
546 
547 /* end of gnunet-helper-nat-client.c */
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 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
#define DUMMY_IP
Must match IP given in the server.
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.
int main(int argc, char *const *argv)
static void send_icmp(const struct in_addr *my_ip, const struct in_addr *other)
Send an ICMP message to the target.
#define PACKET_ID
Must match packet ID used by gnunet-helper-nat-server.c.
uint32_t data
The data value.
static int global_ret
Return value from main.
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-uri.c:38
uint16_t flags_frag_offset
Flags (3 bits) + Fragment offset (13 bits)
uint8_t ttl
Time to live.
uint8_t tos
Type of service.
uint32_t src_ip
Source address.
uint8_t proto
Protocol.
uint16_t checksum
Header checksum.
uint8_t vers_ihl
Version (4 bits) + Internet header length (4 bits)
uint16_t id
Identification.
uint32_t dst_ip
Destination address.
uint16_t pkt_len
Total length.
Beginning of UDP packet.