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 {
99  uint8_t vers_ihl;
100 
104  uint8_t tos;
105 
109  uint16_t pkt_len;
110 
114  uint16_t id;
115 
120 
124  uint8_t ttl;
125 
129  uint8_t proto;
130 
134  uint16_t checksum;
135 
139  uint32_t src_ip;
140 
144  uint32_t dst_ip;
145 };
146 
151  uint8_t type;
152 
153  uint8_t code;
154 
155  uint16_t checksum;
156 
157  uint32_t unused;
158 
159  /* followed by original payload */
160 };
161 
163  uint8_t type;
164 
165  uint8_t code;
166 
167  uint16_t checksum;
168 
169  uint32_t reserved;
170 };
171 
175 struct udp_header {
176  uint16_t src_port;
177 
178  uint16_t dst_port;
179 
180  uint16_t length;
181 
182  uint16_t crc;
183 };
184 
188 static int rawsock;
189 
193 static struct in_addr dummy;
194 
198 static uint16_t port;
199 
200 
208 static uint16_t
209 calc_checksum(const uint16_t * data, unsigned int bytes)
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 }
221 
222 
229 static void
230 send_icmp_udp(const struct in_addr *my_ip, const struct in_addr *other)
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 }
331 
332 
339 static void
340 send_icmp(const struct in_addr *my_ip, const struct in_addr *other)
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 }
445 
446 
447 int
448 main(int argc, char *const *argv)
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 }
535 
536 /* 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.