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 #ifdef FREEBSD
253  ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
254 #else
255  ip_pkt.pkt_len = htons (sizeof(packet));
256 #endif
257  ip_pkt.id = htons (PACKET_ID);
258  ip_pkt.flags_frag_offset = 0;
259  ip_pkt.ttl = 128;
260  ip_pkt.proto = IPPROTO_ICMP;
261  ip_pkt.checksum = 0;
262  ip_pkt.src_ip = my_ip->s_addr;
263  ip_pkt.dst_ip = other->s_addr;
264  ip_pkt.checksum =
265  htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
266  GNUNET_memcpy (&packet[off],
267  &ip_pkt,
268  sizeof(struct ip_header));
269  off += sizeof(struct ip_header);
270 
271  icmp_pkt.type = ICMP_TIME_EXCEEDED;
272  icmp_pkt.code = 0;
273  icmp_pkt.checksum = 0;
274  icmp_pkt.unused = 0;
275  GNUNET_memcpy (&packet[off],
276  &icmp_pkt,
277  sizeof(struct icmp_ttl_exceeded_header));
278  off += sizeof(struct icmp_ttl_exceeded_header);
279 
280  /* ip header of the presumably 'lost' udp packet */
281  ip_pkt.vers_ihl = 0x45;
282  ip_pkt.tos = 0;
283  ip_pkt.pkt_len =
284  htons (sizeof(struct ip_header) + sizeof(struct udp_header));
285  ip_pkt.id = htons (0);
286  ip_pkt.flags_frag_offset = 0;
287  ip_pkt.ttl = 128;
288  ip_pkt.proto = IPPROTO_UDP;
289  ip_pkt.checksum = 0;
290  ip_pkt.src_ip = other->s_addr;
291  ip_pkt.dst_ip = dummy.s_addr;
292  ip_pkt.checksum =
293  htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
294  GNUNET_memcpy (&packet[off],
295  &ip_pkt,
296  sizeof(struct ip_header));
297  off += sizeof(struct ip_header);
298 
299  /* build UDP header */
300  udp_pkt.src_port = htons (NAT_TRAV_PORT);
301  udp_pkt.dst_port = htons (NAT_TRAV_PORT);
302  udp_pkt.length = htons (port);
303  udp_pkt.crc = 0;
304  GNUNET_memcpy (&packet[off],
305  &udp_pkt,
306  sizeof(struct udp_header));
307  off += sizeof(struct udp_header);
308 
309  /* set ICMP checksum */
310  icmp_pkt.checksum =
311  htons (calc_checksum
312  ((uint16_t *) &packet[sizeof(struct ip_header)],
313  sizeof(struct icmp_ttl_exceeded_header)
314  + sizeof(struct ip_header) + sizeof(struct udp_header)));
315  GNUNET_memcpy (&packet[sizeof(struct ip_header)],
316  &icmp_pkt,
317  sizeof(struct icmp_ttl_exceeded_header));
318 
319  memset (&dst, 0, sizeof(dst));
320  dst.sin_family = AF_INET;
321 #if HAVE_SOCKADDR_IN_SIN_LEN
322  dst.sin_len = sizeof(struct sockaddr_in);
323 #endif
324  dst.sin_addr = *other;
325  err =
326  sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
327  sizeof(dst));
328  if (err < 0)
329  {
330  fprintf (stderr, "sendto failed: %s\n", strerror (errno));
331  }
332  else if (sizeof(packet) != (size_t) err)
333  {
334  fprintf (stderr, "Error: partial send of ICMP message with size %lu\n",
335  (unsigned long) off);
336  }
337 }
338 
339 
346 static void
347 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
348 {
349  struct ip_header ip_pkt;
350  struct icmp_ttl_exceeded_header icmp_ttl;
351  struct icmp_echo_header icmp_echo;
352  struct sockaddr_in dst;
353  char packet[sizeof(struct ip_header) * 2
354  + sizeof(struct icmp_ttl_exceeded_header)
355  + sizeof(struct icmp_echo_header)];
356  size_t off;
357  int err;
358 
359  /* ip header: send to (known) ip address */
360  off = 0;
361  ip_pkt.vers_ihl = 0x45;
362  ip_pkt.tos = 0;
363 #ifdef FREEBSD
364  ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
365 #else
366  ip_pkt.pkt_len = htons (sizeof(packet));
367 #endif
368  ip_pkt.id = htons (PACKET_ID);
369  ip_pkt.flags_frag_offset = 0;
370  ip_pkt.ttl = IPDEFTTL;
371  ip_pkt.proto = IPPROTO_ICMP;
372  ip_pkt.checksum = 0;
373  ip_pkt.src_ip = my_ip->s_addr;
374  ip_pkt.dst_ip = other->s_addr;
375  ip_pkt.checksum =
376  htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
377  GNUNET_memcpy (&packet[off],
378  &ip_pkt,
379  sizeof(struct ip_header));
380  off = sizeof(ip_pkt);
381 
382  /* icmp reply: time exceeded */
383  icmp_ttl.type = ICMP_TIME_EXCEEDED;
384  icmp_ttl.code = 0;
385  icmp_ttl.checksum = 0;
386  icmp_ttl.unused = 0;
387  GNUNET_memcpy (&packet[off],
388  &icmp_ttl,
389  sizeof(struct icmp_ttl_exceeded_header));
390  off += sizeof(struct icmp_ttl_exceeded_header);
391 
392  /* ip header of the presumably 'lost' udp packet */
393  ip_pkt.vers_ihl = 0x45;
394  ip_pkt.tos = 0;
395  ip_pkt.pkt_len =
396  htons (sizeof(struct ip_header) + sizeof(struct icmp_echo_header));
397  ip_pkt.id = htons (PACKET_ID);
398  ip_pkt.flags_frag_offset = 0;
399  ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
400  ip_pkt.proto = IPPROTO_ICMP;
401  ip_pkt.src_ip = other->s_addr;
402  ip_pkt.dst_ip = dummy.s_addr;
403  ip_pkt.checksum = 0;
404  ip_pkt.checksum =
405  htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
406  GNUNET_memcpy (&packet[off],
407  &ip_pkt,
408  sizeof(struct ip_header));
409  off += sizeof(struct ip_header);
410 
411  icmp_echo.type = ICMP_ECHO;
412  icmp_echo.code = 0;
413  icmp_echo.reserved = htonl (port);
414  icmp_echo.checksum = 0;
415  icmp_echo.checksum =
416  htons (calc_checksum
417  ((uint16_t *) &icmp_echo, sizeof(struct icmp_echo_header)));
418  GNUNET_memcpy (&packet[off],
419  &icmp_echo,
420  sizeof(struct icmp_echo_header));
421 
422  /* no go back to calculate ICMP packet checksum */
423  off = sizeof(struct ip_header);
424  icmp_ttl.checksum =
425  htons (calc_checksum
426  ((uint16_t *) &packet[off],
427  sizeof(struct icmp_ttl_exceeded_header)
428  + sizeof(struct ip_header) + sizeof(struct icmp_echo_header)));
429  GNUNET_memcpy (&packet[off],
430  &icmp_ttl,
431  sizeof(struct icmp_ttl_exceeded_header));
432 
433  /* prepare for transmission */
434  memset (&dst, 0, sizeof(dst));
435  dst.sin_family = AF_INET;
436 #if HAVE_SOCKADDR_IN_SIN_LEN
437  dst.sin_len = sizeof(struct sockaddr_in);
438 #endif
439  dst.sin_addr = *other;
440  err =
441  sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
442  sizeof(dst));
443  if (err < 0)
444  {
445  fprintf (stderr, "sendto failed: %s\n", strerror (errno));
446  }
447  else if (sizeof(packet) != (size_t) err)
448  {
449  fprintf (stderr, "Error: partial send of ICMP message\n");
450  }
451 }
452 
453 
454 int
455 main (int argc, char *const *argv)
456 {
457  const int one = 1;
458  struct in_addr external;
459  struct in_addr target;
460  uid_t uid;
461  unsigned int p;
462  int raw_eno;
463  int global_ret;
464 
465  /* Create an ICMP raw socket for writing (only operation that requires root) */
466  rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
467  raw_eno = errno; /* for later error checking */
468 
469  /* now drop root privileges */
470  uid = getuid ();
471 #ifdef HAVE_SETRESUID
472  if (0 != setresuid (uid, uid, uid))
473  {
474  fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
475  global_ret = 1;
476  goto cleanup;
477  }
478 #else
479  if (0 != (setuid (uid) | seteuid (uid)))
480  {
481  fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
482  global_ret = 2;
483  goto cleanup;
484  }
485 #endif
486  if (-1 == rawsock)
487  {
488  fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno));
489  global_ret = 3;
490  goto cleanup;
491  }
492  if (0 !=
493  setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one,
494  sizeof(one)))
495  {
496  fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
497  global_ret = 4;
498  goto cleanup;
499  }
500  if (0 !=
501  setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof(one)))
502  {
503  fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
504  global_ret = 5;
505  goto cleanup;
506  }
507 
508  if (4 != argc)
509  {
510  fprintf (stderr,
511  "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
512  global_ret = 6;
513  goto cleanup;
514  }
515  if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
516  (1 != inet_pton (AF_INET, argv[2], &target)))
517  {
518  fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
519  global_ret = 7;
520  goto cleanup;
521  }
522  if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
523  {
524  fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
525  global_ret = 8;
526  goto cleanup;
527  }
528  port = (uint16_t) p;
529  if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
530  {
531  fprintf (stderr, "Internal error converting dummy IP to binary.\n");
532  global_ret = 9;
533  goto cleanup;
534  }
535  send_icmp (&external, &target);
536  send_icmp_udp (&external, &target);
537  global_ret = 0;
538 cleanup:
539  if (-1 != rawsock)
540  (void) close (rawsock);
541  return global_ret;
542 }
543 
544 
545 /* 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.