GNUnet  0.11.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, \
79  n); \
80  } } while (0)
81 
85 #define DUMMY_IP "192.0.2.86"
86 
87 #define NAT_TRAV_PORT 22225
88 
92 #define PACKET_ID 256
93 
97 struct ip_header
98 {
102  uint8_t vers_ihl;
103 
107  uint8_t tos;
108 
112  uint16_t pkt_len;
113 
117  uint16_t id;
118 
123 
127  uint8_t ttl;
128 
132  uint8_t proto;
133 
137  uint16_t checksum;
138 
142  uint32_t src_ip;
143 
147  uint32_t dst_ip;
148 };
149 
154 {
155  uint8_t type;
156 
157  uint8_t code;
158 
159  uint16_t checksum;
160 
161  uint32_t unused;
162 
163  /* followed by original payload */
164 };
165 
167 {
168  uint8_t type;
169 
170  uint8_t code;
171 
172  uint16_t checksum;
173 
174  uint32_t reserved;
175 };
176 
181 {
182  uint16_t src_port;
183 
184  uint16_t dst_port;
185 
186  uint16_t length;
187 
188  uint16_t crc;
189 };
190 
194 static int rawsock;
195 
199 static struct in_addr dummy;
200 
204 static uint16_t port;
205 
206 
214 static uint16_t
215 calc_checksum (const uint16_t *data, unsigned int bytes)
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 }
227 
228 
235 static void
236 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
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 }
339 
340 
347 static void
348 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
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 }
453 
454 
455 int
456 main (int argc, char *const *argv)
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 }
544 
545 
546 /* 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 proto
Protocol.
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 uint16_t port
Our "source" port.
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.