GNUnet 0.21.0
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
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
195static int rawsock;
196
200static struct in_addr dummy;
201
205static uint16_t port;
206
207
215static uint16_t
216calc_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
236static void
237send_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
348static void
349send_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
456int
457main (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;
540cleanup:
541 if (-1 != rawsock)
542 (void) close (rawsock);
543 return global_ret;
544}
545
546
547/* end of gnunet-helper-nat-client.c */
static int global_ret
Global status value.
static char * data
The data to insert into the dht.
static void cleanup(void *cls)
Disconnect and shutdown.
Definition: gnunet-did.c:131
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.
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.